From 0b127eb201137e993c83b720cf5904d7db235c3f Mon Sep 17 00:00:00 2001 From: Evangelos Ribeiro Tzaras Date: Thu, 26 Aug 2021 17:45:09 +0200 Subject: [PATCH 1/6] ci: No need to use experimental anymore We have a new libhandy in unstable. --- debian/salsa-ci.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/debian/salsa-ci.yml b/debian/salsa-ci.yml index 7ab8237..ac9f25b 100644 --- a/debian/salsa-ci.yml +++ b/debian/salsa-ci.yml @@ -5,9 +5,4 @@ include: variables: SALSA_CI_DISABLE_BUILD_PACKAGE_ANY: 1 - RELEASE: 'experimental' - # Since those pull from unstable - SALSA_CI_DISABLE_REPROTEST: 1 - SALSA_CI_DISABLE_PIUPARTS: 1 - SALSA_CI_DISABLE_AUTOPKGTEST: 1 -- GitLab From 88d8b35a983e0ed3f69d633a8fd2b97353f58550 Mon Sep 17 00:00:00 2001 From: Evangelos Ribeiro Tzaras Date: Thu, 26 Aug 2021 17:47:35 +0200 Subject: [PATCH 2/6] Document and release 0.3.4-2 --- debian/changelog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/debian/changelog b/debian/changelog index 92e8ae7..d07116e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +chatty (0.3.4-2) unstable; urgency=medium + + * ci: No need to use experimental anymore + * Upload to unstable + + -- Evangelos Ribeiro Tzaras Thu, 26 Aug 2021 17:47:00 +0200 + chatty (0.3.4-1) experimental; urgency=medium * New upstream version 0.3.4 -- GitLab From 5f82fe5b78c8ed5ec4e172e716740c83a34887ca Mon Sep 17 00:00:00 2001 From: Evangelos Ribeiro Tzaras Date: Tue, 31 Aug 2021 23:25:39 +0200 Subject: [PATCH 3/6] New upstream version 0.4.0~beta2 --- data/sm.puri.Chatty.gschema.xml | 6 + debian/changelog | 93 ++ debian/control | 2 + meson.build | 2 +- po/pt_BR.po | 853 +++++++++++------- po/ro.po | 322 ++++--- sm.puri.Chatty.json | 63 +- src/chatty-application.c | 13 +- src/chatty-chat-view.c | 156 ++-- src/chatty-chat-view.h | 2 - src/chatty-chat.c | 37 +- src/chatty-chat.h | 2 - src/chatty-contact-provider.c | 21 +- src/chatty-enums.h | 9 +- src/chatty-history.c | 221 +++-- src/chatty-image-item.c | 8 +- src/chatty-list-row.c | 4 +- src/chatty-log.c | 237 ++++- src/chatty-log.h | 78 +- src/chatty-manager.c | 288 +++--- src/chatty-manager.h | 1 + src/chatty-message-row.c | 14 +- src/chatty-message.c | 10 +- src/chatty-mm-chat.c | 685 ++++++++++++++ src/chatty-mm-chat.h | 52 ++ src/chatty-notification.c | 5 +- src/chatty-pp-chat.c | 114 +-- src/chatty-secret-store.c | 35 +- src/chatty-settings.c | 28 + src/chatty-settings.h | 1 + src/chatty-text-item.c | 1 + src/chatty-utils.c | 4 +- src/chatty-window.c | 95 +- src/dialogs/chatty-ma-account-details.c | 67 +- src/dialogs/chatty-new-chat-dialog.c | 63 +- src/dialogs/chatty-new-muc-dialog.c | 6 +- src/dialogs/chatty-pp-account-details.c | 2 +- src/dialogs/chatty-pp-chat-info.c | 9 +- src/dialogs/chatty-settings-dialog.c | 26 +- src/matrix/chatty-ma-account.c | 239 +++-- src/matrix/chatty-ma-account.h | 2 +- src/matrix/chatty-ma-buddy.c | 46 +- src/matrix/chatty-ma-chat.c | 196 ++-- src/matrix/matrix-api.c | 209 ++++- src/matrix/matrix-api.h | 22 + src/matrix/matrix-db.c | 10 +- src/matrix/matrix-enc.c | 6 +- src/matrix/matrix-net.c | 15 +- src/meson.build | 5 + src/ui/chatty-dialog-new-chat.ui | 18 +- src/ui/chatty-ma-account-details.ui | 61 +- src/ui/chatty-ma-chat-info.ui | 2 + src/ui/chatty-pp-chat-info.ui | 2 + src/ui/chatty-settings-dialog.ui | 55 ++ src/users/chatty-account.c | 36 - src/users/chatty-account.h | 6 - src/users/chatty-contact.c | 83 +- src/users/chatty-contact.h | 4 +- src/users/chatty-item.c | 54 +- src/users/chatty-item.h | 7 +- src/users/chatty-mm-account.c | 1100 +++++++++++++++++++++++ src/users/chatty-mm-account.h | 59 ++ src/users/chatty-mm-buddy.c | 202 +++++ src/users/chatty-mm-buddy.h | 33 + src/users/chatty-pp-account.c | 66 +- src/users/chatty-pp-buddy.c | 64 +- src/users/chatty-pp-buddy.h | 1 - src/users/itu-e212-iso.h | 295 ++++++ src/xeps/chatty-xep-0313.c | 19 +- src/xeps/chatty-xep-0352.c | 11 +- src/xeps/xeps.c | 3 +- tests/account.c | 7 +- tests/matrix-db.c | 4 +- tests/utils.c | 16 +- 74 files changed, 5055 insertions(+), 1538 deletions(-) create mode 100644 src/chatty-mm-chat.c create mode 100644 src/chatty-mm-chat.h create mode 100644 src/users/chatty-mm-account.c create mode 100644 src/users/chatty-mm-account.h create mode 100644 src/users/chatty-mm-buddy.c create mode 100644 src/users/chatty-mm-buddy.h create mode 100644 src/users/itu-e212-iso.h diff --git a/data/sm.puri.Chatty.gschema.xml b/data/sm.puri.Chatty.gschema.xml index 47cd0ac..7d4557f 100644 --- a/data/sm.puri.Chatty.gschema.xml +++ b/data/sm.puri.Chatty.gschema.xml @@ -62,6 +62,12 @@ Whether pressing Enter key sends the message + + true + Request SMS delivery reports + Whether to request delivery reports for outgoing SMS + + false Enable experimental features diff --git a/debian/changelog b/debian/changelog index e2af78c..bf5cbf5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,96 @@ +chatty (0.4.0~beta2) byzantium; urgency=medium + + * contact: Add API to test for exact match + * contact-provider: Test for exact match to find matching contact + * contact: Let item_matches() match partial matches + * new-chat-dialog: Place the new contact at top of the list + * new-chat-dialog: Update search list on protocol changes + * mm-account: Use time from the SMS for new messages + * mm-buddy: Prefer locally parsed number as the SMS number + * new-chat-dialog: Update search view when list item changes + * window: Allow adding non existing contacts to GNOME contacts + * message-row: Hide avatar image only if not hidden before + * log: Improve logging + * history: Return not-found error if chat is not found + * manager: Don't warn if chat is not found in db + * settings: Add settings for SMS delivery report request + * mm-account: Request delivery reports only if asked for one + * settings-dialog: Add UI to set SMS delivery report request + + -- Mohammed Sadiq Mon, 30 Aug 2021 16:38:08 +0530 + +chatty (0.4.0~beta) byzantium; urgency=medium + + [ Mohammed Sadiq ] + * matrix-net: Handle mxc file uri + * ma-account: Load user avatars and use it + * matrix-api: Add API to get room encryption state + * ma-chat: Load encryption state on start + * chat-view: Fix message entry focus in non-folded mode + * text-item: Let message text be selectable + * pp-chat-info: Allow selecting chat details + * ma-chat-info: Allow selecting chat details + * debian/control: Depend on mm-glib + * Add native SMS support + * manager: Use the new MmAccount for SMS chat + * window: Handle opening ChattyMmChat + * history: Support SMS accounts + * window: Always show SMS chats + * chat-view: Adapt to ChattyMmAccount changes + * new-chat-dialog: Use MmAccount for SMS chats + * window: Handle deleting SMS chats + * pp-chat-info: Don't allow changing SMS avatars + * pp-chat-info: Show notification switch only for purple account + * ma-chat: Fix a debug format string + * chat-view: Add support for message spell check + * matrix-api: Add API to delete avatar + * ma-account: Implemente deleting user avatar + * ma-account-details: Add a way to delete user avatar + * secret-store: Use simple API to manage secrets + * window: Simplify chat list filter + * various: Avoid an unnecessary check + * pp-account: Don't handle SMS accounts + * pp-chat-info: Don't handle SMS accounts + * pp-chat: Don't handle SMS accounts + * pp-buddy: Don't handle SMS accounts + * enums: Remove no longer used enum + * manager: Don't handle SMS with purple + * window: Remove purple SMS code + * various: Rename SMS enum to MMS_SMS + * manager: Don't add purple sms account to list + * application: Improve presenting window + * chat-view: Reset unread count after message is sent + * matrix-api: Add an API to get room members + * ma-chat: Get members list for sending encrypted messages + * ma-chat: Don't claim keys when a request is in progress + * ma-chat: Handle m.room.member events + * item: Unify API to get/set username + * pp-chat-info: Show the right title for phone numbers + * window: Show delete menu item for SMS chats + * flatpak: Fix build + + [ Guido Günther ] + * notification: Set category (Closes: #514) + + [ Bruno Lopes ] + * Update Brazilian Portuguese translation + + [ Rafael Fontenelle ] + * Update Brazilian Portuguese translation + + [ Chris T ] + * mm-account: Do not delete an SMS if there is no text in it + + [ Daniel Șerbănescu ] + * po: Update Romanian translation + + [ Chris Talbot ] + * image-item: Handle MMS files + * mm-account: set eds after users are loaded + * message-row: Handle MMS Files + + -- Mohammed Sadiq Wed, 25 Aug 2021 14:23:08 +0530 + chatty (0.3.4) byzantium; urgency=medium [ Mohammed Sadiq ] diff --git a/debian/control b/debian/control index 519121c..5fa2598 100644 --- a/debian/control +++ b/debian/control @@ -12,7 +12,9 @@ Build-Depends: libhandy-1-dev (>= 1.1.90), libebook-contacts1.2-dev, libgcrypt20-dev, + libgspell-1-dev, libjson-glib-dev, + libmm-glib-dev (>= 1.12.0), libphonenumber-dev, libpurple-dev, libolm-dev, diff --git a/meson.build b/meson.build index e1a1864..ed8572f 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'chatty', 'c', 'cpp', - version: '0.3.4', + version: '0.4.0.beta2', meson_version: '>= 0.46.0', ) diff --git a/po/pt_BR.po b/po/pt_BR.po index 3790b26..b157376 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -1,29 +1,27 @@ # Brazilian Portuguese translaiton to chatty. -# Copyright (C) 2020 THE chatty'S COPYRIGHT HOLDER +# Copyright (C) 2021 THE chatty'S COPYRIGHT HOLDER # This file is distributed under the same license as the chatty package. -# Luís Fernando Stürmer da Rosa , 2018. #zanata -# Luís Fernando Stürmer da Rosa , 2019. #zanata -# Luís Fernando Stürmer da Rosa , 2020. #zanata -# Rafael Fontenelle , 2020. -# Luís Fernando Stürmer da Rosa , 2020. +# Luís Fernando Stürmer da Rosa , 2018-2020. +# Bruno Lopes , 2020. +# Rafael Fontenelle , 2020-2021. # msgid "" msgstr "" "Project-Id-Version: purism-chatty\n" "Report-Msgid-Bugs-To: https://source.puri.sm/Librem5/chatty/issues\n" -"POT-Creation-Date: 2020-07-30 03:24+0000\n" -"PO-Revision-Date: 2020-07-30 11:11-0300\n" -"Last-Translator: Luís Fernando Stürmer da Rosa \n" +"POT-Creation-Date: 2021-08-06 03:25+0000\n" +"PO-Revision-Date: 2021-08-06 09:04-0300\n" +"Last-Translator: Rafael Fontenelle \n" "Language-Team: Brazilian Portuguese \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 2.3.1\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Gtranslator 40.0\n" +"Plural-Forms: nplurals=2; plural=(n > 1)\n" #: data/sm.puri.Chatty.desktop.in:3 data/sm.puri.Chatty.metainfo.xml.in:6 -#: src/chatty-application.c:273 src/ui/chatty-window.ui:224 +#: src/chatty-application.c:359 src/ui/chatty-window.ui:183 msgid "Chats" msgstr "Conversas" @@ -63,7 +61,7 @@ msgstr "Se é para enviar o status da mensagem lida" msgid "Message carbon copies" msgstr "Cópias de mensagens" -#: data/sm.puri.Chatty.gschema.xml:26 src/ui/chatty-settings-dialog.ui:153 +#: data/sm.puri.Chatty.gschema.xml:26 src/ui/chatty-settings-dialog.ui:177 msgid "Share chat history among devices" msgstr "Sincronizar histórico entre dispositivos" @@ -84,33 +82,37 @@ msgid "Whether to Send typing notifications" msgstr "Se é para enviar notificações de digitação" #: data/sm.puri.Chatty.gschema.xml:43 data/sm.puri.Chatty.gschema.xml:44 -msgid "Mark offline users differently" -msgstr "Diferenciar usuários desconectados" - -#: data/sm.puri.Chatty.gschema.xml:49 data/sm.puri.Chatty.gschema.xml:50 msgid "Mark Idle users differently" msgstr "Diferenciar usuários ociosos" -#: data/sm.puri.Chatty.gschema.xml:55 data/sm.puri.Chatty.gschema.xml:56 +#: data/sm.puri.Chatty.gschema.xml:49 data/sm.puri.Chatty.gschema.xml:50 msgid "Indicate unknown contacts" msgstr "Indicar contatos não conhecidos" -#: data/sm.puri.Chatty.gschema.xml:61 +#: data/sm.puri.Chatty.gschema.xml:55 msgid "Convert text to emoticons" msgstr "Converter textos em emoticons" -#: data/sm.puri.Chatty.gschema.xml:62 +#: data/sm.puri.Chatty.gschema.xml:56 msgid "Convert text matching emoticons as real emoticons" msgstr "Converter emoticons de texto para emoticons verdadeiros" -#: data/sm.puri.Chatty.gschema.xml:67 +#: data/sm.puri.Chatty.gschema.xml:61 msgid "Enter key sends the message" msgstr "ENTER envia a mensagem" -#: data/sm.puri.Chatty.gschema.xml:68 +#: data/sm.puri.Chatty.gschema.xml:62 msgid "Whether pressing Enter key sends the message" msgstr "Se é para enviar ao pressionar ENTER" +#: data/sm.puri.Chatty.gschema.xml:67 +msgid "Enable experimental features" +msgstr "Habilita recursos experimentais" + +#: data/sm.puri.Chatty.gschema.xml:68 +msgid "Whether to enable experimental features" +msgstr "Se deve habilitar recursos experimentais" + #: data/sm.puri.Chatty.gschema.xml:73 msgid "Window maximized" msgstr "Janela maximizada" @@ -139,190 +141,212 @@ msgstr "Conversas é um aplicativo compatível com XMPP e SMS." msgid "Chats message window" msgstr "Janela de mensagens" -#: src/chatty-application.c:68 +#: src/chatty-application.c:79 msgid "Show release version" msgstr "Exibir versão" -#: src/chatty-application.c:69 +#: src/chatty-application.c:80 msgid "Start in daemon mode" msgstr "Inicializar em segundo plano" -#: src/chatty-application.c:70 +#: src/chatty-application.c:81 msgid "Disable all accounts" msgstr "Desabilitar contas" -#: src/chatty-application.c:71 +#: src/chatty-application.c:82 msgid "Enable libpurple debug messages" -msgstr "Habilitar messagens de depuração do libpurple" +msgstr "Habilitar mensagens de depuração do libpurple" -#: src/chatty-application.c:72 +#: src/chatty-application.c:84 msgid "Enable verbose libpurple debug messages" msgstr "Habilitar mensagens de depuração detalhada do libpurple" -#: src/chatty-application.c:108 +#: src/chatty-application.c:134 #, c-format msgid "Authorize %s?" msgstr "Autorizar %s?" -#: src/chatty-application.c:112 +#: src/chatty-application.c:138 src/matrix/matrix-utils.c:507 msgid "Reject" msgstr "Rejeitar" -#: src/chatty-application.c:114 +#: src/chatty-application.c:140 src/matrix/matrix-utils.c:508 msgid "Accept" msgstr "Aceitar" -#: src/chatty-application.c:119 +#: src/chatty-application.c:145 #, c-format msgid "Add %s to contact list" msgstr "Adicionar %s à lista de contatos" -#: src/chatty-application.c:142 +#: src/chatty-application.c:168 msgid "Contact added" msgstr "Contato adicionado" -#: src/chatty-application.c:145 +#: src/chatty-application.c:171 #, c-format msgid "User %s has added %s to the contacts" msgstr "O usuário %s adicionou %s aos contatos" -#: src/chatty-application.c:165 +#: src/chatty-application.c:191 msgid "Login failed" msgstr "O login falhou" -#: src/chatty-application.c:171 +#: src/chatty-application.c:197 msgid "Please check ID and password" msgstr "Verifique o ID e a senha" -#: src/chatty-chat-view.c:70 src/chatty-chat-view.c:75 -msgid "This is an IM conversation." -msgstr "Esta é uma conversa IM." - -#: src/chatty-chat-view.c:71 src/chatty-chat-view.c:81 -msgid "Your messages are not encrypted," -msgstr "Suas mensagens não estão criptografadas," +#: src/chatty-chat-view.c:201 +msgid "This is an SMS conversation" +msgstr "Esta é uma conversa por SMS" -#: src/chatty-chat-view.c:72 -msgid "ask your counterpart to use E2EE." -msgstr "convide seu interlocutor a usar o E2EE." +#: src/chatty-chat-view.c:203 +msgid "Your messages are not encrypted, and carrier rates may apply" +msgstr "" +"Suas mensagens não estão criptografadas e podem ser aplicadas taxas da " +"operadora" -#: src/chatty-chat-view.c:76 -msgid "Your messages are secured" -msgstr "Suas mensagens estão protegidas" +#: src/chatty-chat-view.c:207 +msgid "This is an IM conversation" +msgstr "Esta é uma conversa por IM" -#: src/chatty-chat-view.c:77 -msgid "by end-to-end encryption." -msgstr "por criptografia ponta a ponta." +#: src/chatty-chat-view.c:210 +msgid "Your messages are encrypted" +msgstr "Suas mensagens estão criptografadas" -#: src/chatty-chat-view.c:80 -msgid "This is an SMS conversation." -msgstr "Esta é uma conversa SMS." +#: src/chatty-chat-view.c:213 +msgid "Your messages are not encrypted" +msgstr "Suas mensagens não estão criptografadas" -#: src/chatty-chat-view.c:82 -msgid "and carrier rates may apply." -msgstr "e taxas podem ser cobradas." +#. TRANSLATORS: %s is the Device ID +#: src/chatty-fp-row.c:131 +#, c-format +msgid "Device ID %s fingerprint:" +msgstr "Impressão digital do ID de dispositivo %s:" -#: src/chatty-list-row.c:72 +#. Translators: Timestamp seconds suffix +#: src/chatty-list-row.c:74 +msgctxt "timestamp-suffix-seconds" msgid "s" msgstr "s" -#: src/chatty-list-row.c:73 src/chatty-list-row.c:74 +#. Translators: Timestamp minute suffix +#: src/chatty-list-row.c:76 +msgctxt "timestamp-suffix-minute" msgid "m" msgstr "m" -#: src/chatty-list-row.c:75 src/chatty-list-row.c:76 +#. Translators: Timestamp minutes suffix +#: src/chatty-list-row.c:78 +msgctxt "timestamp-suffix-minutes" +msgid "m" +msgstr "m" + +#. Translators: Timestamp hour suffix +#: src/chatty-list-row.c:80 +msgctxt "timestamp-suffix-hour" msgid "h" msgstr "h" -#: src/chatty-list-row.c:77 src/chatty-list-row.c:78 +#. Translators: Timestamp hours suffix +#: src/chatty-list-row.c:82 +msgctxt "timestamp-suffix-hours" +msgid "h" +msgstr "h" + +#. Translators: Timestamp day suffix +#: src/chatty-list-row.c:84 +msgctxt "timestamp-suffix-day" msgid "d" msgstr "d" -#: src/chatty-list-row.c:79 +#. Translators: Timestamp days suffix +#: src/chatty-list-row.c:86 +msgctxt "timestamp-suffix-days" +msgid "d" +msgstr "d" + +#. Translators: Timestamp month suffix +#: src/chatty-list-row.c:88 +msgctxt "timestamp-suffix-month" msgid "mo" msgstr "m" -#: src/chatty-list-row.c:80 +#. Translators: Timestamp months suffix +#: src/chatty-list-row.c:90 +msgctxt "timestamp-suffix-months" msgid "mos" msgstr "ms" -#: src/chatty-list-row.c:81 src/chatty-list-row.c:82 +#. Translators: Timestamp year suffix +#: src/chatty-list-row.c:92 +msgctxt "timestamp-suffix-year" msgid "y" msgstr "a" -#: src/chatty-list-row.c:183 +#. Translators: Timestamp years suffix +#: src/chatty-list-row.c:94 +msgctxt "timestamp-suffix-years" +msgid "y" +msgstr "a" + +#. Translators: Timestamp prefix (e.g. Over 5h) +#: src/chatty-list-row.c:196 msgid "Over" msgstr "Sobre" -#: src/chatty-list-row.c:187 +#. Translators: Timestamp prefix (e.g. Almost 5h) +#: src/chatty-list-row.c:201 msgid "Almost" msgstr "Quase" -#: src/chatty-list-row.c:204 +#: src/chatty-list-row.c:253 msgid "Owner" msgstr "Dono" -#: src/chatty-list-row.c:207 +#: src/chatty-list-row.c:256 msgid "Moderator" msgstr "Moderador" -#: src/chatty-list-row.c:210 +#: src/chatty-list-row.c:259 msgid "Member" msgstr "Participante" -#: src/chatty-manager.c:991 -#, c-format -msgid "New message from %s" -msgstr "Nova mensagem de %s" - -#: src/chatty-message-row.c:89 +#: src/chatty-message-row.c:79 msgid "Copy" msgstr "Copiar" -#: src/chatty-notify.c:81 +#: src/chatty-notification.c:45 msgid "Open Message" msgstr "Abrir mensagem" -#: src/chatty-notify.c:86 +#: src/chatty-notification.c:185 +#, c-format +msgid "New message from %s" +msgstr "Nova mensagem de %s" + +#: src/chatty-notification.c:187 msgid "Message Received" msgstr "Mensagem recebida" -#: src/chatty-notify.c:92 -msgid "Message Error" -msgstr "Erro na mensagem" - -#: src/chatty-notify.c:98 -msgid "Account Info" -msgstr "Informações da conta" - -#: src/chatty-notify.c:104 -msgid "Account Connected" -msgstr "Conta conectada" - -#: src/chatty-notify.c:111 -msgid "Open Account Settings" -msgstr "Abrir configurações da conta" - -#: src/chatty-notify.c:114 -msgid "Account Disconnected" -msgstr "Conta desconectada" - -#: src/chatty-purple-notify.c:42 +#: src/chatty-purple-notify.c:44 src/matrix/matrix-utils.c:503 msgid "Close" msgstr "Fechar" #: src/chatty-purple-request.c:186 msgid "Save File..." -msgstr "Salvar arquivo..." +msgstr "Salvar arquivo…" #: src/chatty-purple-request.c:187 msgid "Open File..." -msgstr "Abrir arquivo..." - -#: src/chatty-purple-request.c:191 src/chatty-window.c:656 -#: src/dialogs/chatty-settings-dialog.c:447 -#: src/dialogs/chatty-user-info-dialog.c:66 src/ui/chatty-dialog-join-muc.ui:16 -#: src/ui/chatty-dialog-muc-info.ui:56 +msgstr "Abrir arquivo…" + +#: src/chatty-purple-request.c:191 src/chatty-window.c:463 +#: src/dialogs/chatty-ma-account-details.c:87 +#: src/dialogs/chatty-pp-account-details.c:87 +#: src/ui/chatty-dialog-join-muc.ui:16 src/ui/chatty-info-dialog.ui:44 +#: src/ui/chatty-info-dialog.ui:191 src/ui/chatty-ma-chat-info.ui:114 +#: src/ui/chatty-pp-chat-info.ui:380 msgid "Cancel" msgstr "Cancelar" @@ -330,57 +354,43 @@ msgstr "Cancelar" msgid "Save" msgstr "Salvar" -#: src/chatty-purple-request.c:193 src/dialogs/chatty-settings-dialog.c:446 -#: src/dialogs/chatty-user-info-dialog.c:65 +#: src/chatty-purple-request.c:193 src/dialogs/chatty-ma-account-details.c:86 +#: src/dialogs/chatty-pp-account-details.c:86 src/ui/chatty-info-dialog.ui:198 +#: src/ui/chatty-ma-chat-info.ui:121 src/ui/chatty-pp-chat-info.ui:387 msgid "Open" msgstr "Abrir" -#. TRANSLATORS: Time format with time in AM/PM format -#: src/chatty-utils.c:291 -msgid "%I:%M %p" -msgstr "%I:%M %p" - -#. TRANSLATORS: Time format as supported by g_date_time_format() -#: src/chatty-utils.c:296 -msgid "%A %R" -msgstr "%A %R" - -#. TRANSLATORS: Time format with day and time in AM/PM format -#: src/chatty-utils.c:299 -msgid "%A %I:%M %p" +#: src/chatty-secret-store.c:86 +#, c-format +msgid "Chatty password for \"%s\"" +msgstr "Senha do Chatty para “%s”" + +#. TRANSLATORS: Timestamp from the last week with 24 hour time, e.g. “Tuesday 18∶42”. +#. See https://developer.gnome.org/glib/stable/glib-GDateTime.html#g-date-time-format +#. +#: src/chatty-utils.c:379 +msgid "%A %H∶%M" +msgstr "%A %H∶%M" + +#. TRANSLATORS: Timestamp from the last week with 12 hour time, e.g. “Tuesday 06∶42 PM”. +#. See https://developer.gnome.org/glib/stable/glib-GDateTime.html#g-date-time-format +#. +#: src/chatty-utils.c:384 +msgid "%A %I∶%M %p" msgstr "%A %I:%M %p" -#. TRANSLATORS: Year format as supported by g_date_time_format() -#: src/chatty-utils.c:303 +#. TRANSLATORS: Timestamp from more than 7 days ago, e.g. “2020-08-11”. +#. See https://developer.gnome.org/glib/stable/glib-GDateTime.html#g-date-time-format +#. +#: src/chatty-utils.c:391 msgid "%Y-%m-%d" msgstr "%Y-%m-%d" -#: src/chatty-window.c:100 src/chatty-window.c:105 src/chatty-window.c:110 -msgid "Choose a contact" -msgstr "Escolha um contato" - -#: src/chatty-window.c:101 -msgid "" -"Select an SMS or Instant Message contact with the \"+\" " -"button in the titlebar." -msgstr "" -"Escolha por um contato via SMS ou mensagem instantânea com o " -"botão \"+\" na barra de título." +#: src/chatty-window.c:173 +msgid "Select a contact with the “+” button in the titlebar." +msgstr "Escolha um contato com o botão “+” na barra de título." -#: src/chatty-window.c:106 -msgid "" -"Select an Instant Message contact with the \"+\" button in the " -"titlebar." -msgstr "" -"Escolha um contato de mensagem instantânea com o botão \"+\" na barra " -"de título." - -#: src/chatty-window.c:111 -msgid "Start a SMS chat with the \"+\" button in the titlebar." -msgstr "" -"Comece uma conversa via SMS tocando no botão \"+\" na barra de título." - -#: src/chatty-window.c:112 src/chatty-window.c:116 +#: src/chatty-window.c:177 msgid "" "For Instant Messaging add or activate an account in \"preferences" "\"." @@ -388,45 +398,87 @@ msgstr "" "Para mandar mensagens instantâneas crie ou ative uma conta em " "\"preferências\"." -#: src/chatty-window.c:115 -msgid "Start chatting" -msgstr "Comece a conversar" +#: src/chatty-window.c:448 +msgid "Delete chat with" +msgstr "Apagar conversa com" -#: src/chatty-window.c:639 +#: src/chatty-window.c:449 +msgid "This deletes the conversation history" +msgstr "Isto fará com que o histórico de conversas seja apagado" + +#: src/chatty-window.c:451 msgid "Disconnect group chat" msgstr "Sair da conversa em grupo" -#: src/chatty-window.c:640 +#: src/chatty-window.c:452 msgid "This removes chat from chats list" msgstr "Isto removerá a conversa da lista" -#: src/chatty-window.c:644 -msgid "Delete chat with" -msgstr "Apagar conversa com" - -#: src/chatty-window.c:645 -msgid "This deletes the conversation history" -msgstr "Isto fará com que o histórico de conversas seja apagado" - -#: src/chatty-window.c:658 +#: src/chatty-window.c:465 msgid "Delete" msgstr "Apagar" -#: src/chatty-window.c:910 +#: src/chatty-window.c:535 +#, c-format +msgid "Error saving contact: %s" +msgstr "Erro ao salvar o contato: %s" + +#: src/chatty-window.c:618 msgid "An SMS and XMPP messaging client" msgstr "Um aplicativo de conversa via XMPP e SMS" -#: src/chatty-window.c:917 +#: src/chatty-window.c:625 msgid "translator-credits" msgstr "" "Luís Fernando Stürmer da Rosa \n" -"Rafael Fontenelle " +"Rafael Fontenelle \n" +"Bruno Lopes " + +#: src/chatty-window.c:843 +#, c-format +msgid "“%s” is not a valid phone number" +msgstr "“%s” não é um número de telefone válido" + +#: src/dialogs/chatty-pp-chat-info.c:163 +msgid "Encryption is not available" +msgstr "Criptografia não disponível" + +#: src/dialogs/chatty-pp-chat-info.c:167 +msgid "This chat is encrypted" +msgstr "Esta conversa está criptografada" + +#: src/dialogs/chatty-pp-chat-info.c:171 +msgid "This chat is not encrypted" +msgstr "Esta conversa não está criptografada" + +#: src/dialogs/chatty-pp-chat-info.c:195 +msgid "Encryption not available" +msgstr "Criptografia não disponível" + +#: src/dialogs/chatty-pp-chat-info.c:216 +#, c-format +msgid "%u Member" +msgid_plural "%u Members" +msgstr[0] "%u membro" +msgstr[1] "%u membros" + +#: src/dialogs/chatty-pp-chat-info.c:250 +msgid "Phone Number:" +msgstr "Número do telefone:" -#: src/dialogs/chatty-muc-info-dialog.c:324 -msgid "members" -msgstr "participantes" +#: src/dialogs/chatty-pp-chat-info.c:252 +msgid "XMPP ID:" +msgstr "ID XMPP:" -#: src/dialogs/chatty-new-chat-dialog.c:134 +#: src/dialogs/chatty-pp-chat-info.c:257 +msgid "Matrix ID:" +msgstr "ID Matrix:" + +#: src/dialogs/chatty-pp-chat-info.c:261 +msgid "Telegram ID:" +msgstr "ID Telegram:" + +#: src/dialogs/chatty-new-chat-dialog.c:133 msgid "Send To" msgstr "Enviar para" @@ -435,60 +487,100 @@ msgstr "Enviar para" msgid "Error opening GNOME Contacts: %s" msgstr "Erro ao abrir o aplicativo Contatos: %s" -#: src/dialogs/chatty-settings-dialog.c:319 -msgid "Select Protocol" -msgstr "Selecione o protocolo" - -#: src/dialogs/chatty-settings-dialog.c:324 -msgid "Add XMPP account" -msgstr "Adicionar conta XMPP" +#: src/dialogs/chatty-ma-account-details.c:83 +#: src/dialogs/chatty-pp-account-details.c:83 src/ui/chatty-info-dialog.ui:186 +#: src/ui/chatty-ma-chat-info.ui:109 src/ui/chatty-pp-chat-info.ui:375 +msgid "Set Avatar" +msgstr "Configurar avatar" -#: src/dialogs/chatty-settings-dialog.c:355 +#: src/dialogs/chatty-ma-account-details.c:325 +#: src/dialogs/chatty-pp-account-details.c:158 msgid "connected" -msgstr "contectado" +msgstr "conectado" -#: src/dialogs/chatty-settings-dialog.c:357 +#: src/dialogs/chatty-ma-account-details.c:327 +#: src/dialogs/chatty-pp-account-details.c:160 msgid "connecting…" msgstr "conectando…" -#: src/dialogs/chatty-settings-dialog.c:359 +#: src/dialogs/chatty-ma-account-details.c:329 +#: src/dialogs/chatty-pp-account-details.c:162 msgid "disconnected" msgstr "desconectado" -#: src/dialogs/chatty-settings-dialog.c:443 -#: src/dialogs/chatty-user-info-dialog.c:62 -msgid "Set Avatar" -msgstr "Configurar avatar" - -#: src/dialogs/chatty-settings-dialog.c:513 -#: src/ui/chatty-settings-dialog.ui:477 +#: src/dialogs/chatty-settings-dialog.c:378 +#: src/ui/chatty-ma-account-details.ui:156 +#: src/ui/chatty-pp-account-details.ui:150 msgid "Delete Account" msgstr "Apagar conta" -#: src/dialogs/chatty-settings-dialog.c:516 +#: src/dialogs/chatty-settings-dialog.c:381 #, c-format msgid "Delete account %s?" msgstr "Apagar conta %s?" -#: src/dialogs/chatty-user-info-dialog.c:144 -msgid "Encryption not available" -msgstr "Criptografia não disponível" +#: src/dialogs/chatty-settings-dialog.c:483 +msgid "Select Protocol" +msgstr "Selecione o protocolo" -#: src/dialogs/chatty-user-info-dialog.c:182 -msgid "Encryption is not available" -msgstr "Criptografia não disponível" +#: src/matrix/chatty-ma-account.c:291 +msgid "Incorrect password" +msgstr "Senha incorreta" -#: src/dialogs/chatty-user-info-dialog.c:184 -msgid "This chat is encrypted" -msgstr "Esta conversa está criptografada" +#: src/matrix/chatty-ma-account.c:294 +msgid "_OK" +msgstr "_OK" -#: src/dialogs/chatty-user-info-dialog.c:186 -msgid "This chat is not encrypted" -msgstr "Esta conversa não está criptografada" +#: src/matrix/chatty-ma-account.c:295 src/ui/chatty-settings-dialog.ui:41 +#: src/ui/chatty-settings-dialog.ui:509 +msgid "_Cancel" +msgstr "_Cancelar" -#: src/dialogs/chatty-user-info-dialog.c:256 -msgid "Phone Number:" -msgstr "Número do telefone:" +#: src/matrix/chatty-ma-account.c:301 +#, c-format +msgid "Please enter password for “%s”" +msgstr "Por favor, insira a senha para “%s”" + +#: src/matrix/chatty-ma-chat.c:164 +msgid "Empty room" +msgstr "Sala vazia" + +#: src/matrix/chatty-ma-chat.c:168 +#, c-format +msgid "%s and %s" +msgstr "%s e %s" + +#: src/matrix/chatty-ma-chat.c:170 +#, c-format +msgid "%s and %u other" +msgid_plural "%s and %u others" +msgstr[0] "%s e %u outro" +msgstr[1] "%s e %u outros" + +#: src/matrix/matrix-utils.c:472 +#, c-format +msgid "The certificate for ‘%s’ has unknown CA" +msgstr "O certificado para ‘%s’ é de uma CA desconhecida" + +#: src/matrix/matrix-utils.c:474 +#, c-format +msgid "The certificate for ‘%s’ is self-signed" +msgstr "O certificado para ‘%s’ é auto-assinado" + +#: src/matrix/matrix-utils.c:478 +#, c-format +msgid "The certificate for ‘%s’ has expired" +msgstr "O certificado para ‘%s’ expirou" + +#: src/matrix/matrix-utils.c:482 +#, c-format +msgid "The certificate for ‘%s’ has been revoked" +msgstr "O certificado para ‘%s’ foi revogado" + +#: src/matrix/matrix-utils.c:491 +#, c-format +msgid "Error validating certificate for ‘%s’" +msgstr "Erro ao validar o certificado para ‘%s’" #: src/ui/chatty-dialog-join-muc.ui:12 msgid "New Group Chat" @@ -498,58 +590,14 @@ msgstr "Novo grupo de conversa" msgid "Join Chat" msgstr "Participar da conversa" -#: src/ui/chatty-dialog-join-muc.ui:81 src/ui/chatty-dialog-new-chat.ui:237 +#: src/ui/chatty-dialog-join-muc.ui:80 src/ui/chatty-dialog-new-chat.ui:296 msgid "Select chat account" msgstr "Selecionar uma conta" -#: src/ui/chatty-dialog-join-muc.ui:155 +#: src/ui/chatty-dialog-join-muc.ui:150 msgid "Password (optional)" msgstr "Senha (opcional)" -#: src/ui/chatty-dialog-muc-info.ui:26 -msgid "Group Details" -msgstr "Detalhes do grupo" - -#: src/ui/chatty-dialog-muc-info.ui:52 -msgid "Invite Contact" -msgstr "Convidar contato" - -#: src/ui/chatty-dialog-muc-info.ui:69 -msgid "Invite" -msgstr "Convidar" - -#: src/ui/chatty-dialog-muc-info.ui:166 -msgid "Room topic" -msgstr "Assunto da sala" - -#: src/ui/chatty-dialog-muc-info.ui:225 -msgid "Room settings" -msgstr "Configurações da sala" - -#: src/ui/chatty-dialog-muc-info.ui:248 src/ui/chatty-dialog-user-info.ui:209 -msgid "Notifications" -msgstr "Notificações" - -#: src/ui/chatty-dialog-muc-info.ui:249 -msgid "Show notification badge" -msgstr "Exibir barra de notificações" - -#: src/ui/chatty-dialog-muc-info.ui:265 -msgid "Status Messages" -msgstr "Mensagens de status" - -#: src/ui/chatty-dialog-muc-info.ui:266 -msgid "Show status messages in chat" -msgstr "Exibir mensagens de status na conversa" - -#: src/ui/chatty-dialog-muc-info.ui:289 -msgid "0 members" -msgstr "0 participante" - -#: src/ui/chatty-dialog-muc-info.ui:377 -msgid "Invite Message" -msgstr "Mensagem de convite" - #: src/ui/chatty-dialog-new-chat.ui:26 msgid "Start Chat" msgstr "Começar uma conversa" @@ -558,47 +606,126 @@ msgstr "Começar uma conversa" msgid "New Contact" msgstr "Novo contato" -#: src/ui/chatty-dialog-new-chat.ui:83 src/ui/chatty-window.ui:125 +#: src/ui/chatty-dialog-new-chat.ui:83 src/ui/chatty-window.ui:113 msgid "Add Contact" msgstr "Criar contato" -#: src/ui/chatty-dialog-new-chat.ui:149 +#: src/ui/chatty-dialog-new-chat.ui:148 msgid "Send To:" msgstr "Enviar para:" -#: src/ui/chatty-dialog-new-chat.ui:288 +#: src/ui/chatty-dialog-new-chat.ui:224 +msgid "No Search Results" +msgstr "Sem resultados da pesquisa" + +#: src/ui/chatty-dialog-new-chat.ui:237 +msgid "Try different search, or type a valid number to create new chat" +msgstr "" +"Tente uma pesquisa diferente, ou digite um número válido para criar um novo " +"chat" + +#: src/ui/chatty-dialog-new-chat.ui:343 msgid "Name (optional)" msgstr "Nome (opcional)" -#: src/ui/chatty-dialog-new-chat.ui:328 src/ui/chatty-window.ui:151 +#: src/ui/chatty-dialog-new-chat.ui:383 msgid "Add to Contacts" msgstr "Adicionar aos contatos" -#: src/ui/chatty-dialog-user-info.ui:12 src/ui/chatty-window.ui:111 +#: src/ui/chatty-info-dialog.ui:17 src/ui/chatty-window.ui:99 msgid "Chat Details" msgstr "Detalhes da conversa" -#: src/ui/chatty-dialog-user-info.ui:96 +#: src/ui/chatty-info-dialog.ui:56 +msgid "Invite" +msgstr "Convidar" + +#: src/ui/chatty-ma-chat-info.ui:79 src/ui/chatty-ma-account-details.ui:207 +msgid "Matrix ID" +msgstr "ID Matrix" + +#: src/ui/chatty-pp-chat-info.ui:59 +msgid "Room topic" +msgstr "Assunto da sala" + +#: src/ui/chatty-pp-chat-info.ui:162 msgid "XMPP ID" msgstr "ID XMPP" -#: src/ui/chatty-dialog-user-info.ui:111 src/ui/chatty-dialog-user-info.ui:225 +#: src/ui/chatty-pp-chat-info.ui:189 src/ui/chatty-pp-chat-info.ui:295 msgid "Encryption" msgstr "Criptografia" -#: src/ui/chatty-dialog-user-info.ui:126 src/ui/chatty-settings-dialog.ui:390 +#: src/ui/chatty-pp-chat-info.ui:215 src/ui/chatty-ma-account-details.ui:41 +#: src/ui/chatty-pp-account-details.ui:93 msgid "Status" msgstr "Estado" -#: src/ui/chatty-dialog-user-info.ui:226 -msgid "Secure messaging using OMEMO" -msgstr "Proteja suas conversas usando OMEMO" +#: src/ui/chatty-pp-chat-info.ui:240 +msgid "Chat settings" +msgstr "Configurações da conversa" + +#: src/ui/chatty-pp-chat-info.ui:261 +msgid "Notifications" +msgstr "Notificações" + +#: src/ui/chatty-pp-chat-info.ui:278 +msgid "Status Messages" +msgstr "Mensagens de status" + +#: src/ui/chatty-pp-chat-info.ui:279 +msgid "Show status messages in chat" +msgstr "Exibir mensagens de status na conversa" + +#: src/ui/chatty-pp-chat-info.ui:296 +msgid "Encrypt Messages" +msgstr "Criptografar mensagens" -#: src/ui/chatty-dialog-user-info.ui:247 +#: src/ui/chatty-pp-chat-info.ui:318 msgid "Fingerprints" msgstr "Assinaturas digitais" -#: src/ui/chatty-settings-dialog.ui:12 src/ui/chatty-window.ui:18 +#: src/ui/chatty-ma-account-details.ui:67 +msgid "Name" +msgstr "Nome" + +#: src/ui/chatty-ma-account-details.ui:97 +msgid "Email" +msgstr "E-mail" + +#: src/ui/chatty-ma-account-details.ui:126 +msgid "Phone" +msgstr "Telefone" + +#: src/ui/chatty-ma-account-details.ui:170 +msgid "Advanced information" +msgstr "Informações avançadas" + +#: src/ui/chatty-ma-account-details.ui:181 +msgid "Homeserver" +msgstr "Servidor doméstico" + +#: src/ui/chatty-ma-account-details.ui:233 +msgid "Device ID" +msgstr "ID do dispositivo" + +#: src/ui/chatty-pp-account-details.ui:43 +msgid "Account ID" +msgstr "ID da conta" + +#: src/ui/chatty-pp-account-details.ui:67 +msgid "Protocol" +msgstr "Protocolo" + +#: src/ui/chatty-pp-account-details.ui:119 src/ui/chatty-settings-dialog.ui:419 +msgid "Password" +msgstr "Senha" + +#: src/ui/chatty-pp-account-details.ui:164 +msgid "Own Fingerprint" +msgstr "Minha digital" + +#: src/ui/chatty-settings-dialog.ui:12 src/ui/chatty-window.ui:17 msgid "Preferences" msgstr "Preferências" @@ -606,139 +733,119 @@ msgstr "Preferências" msgid "Back" msgstr "Voltar" -#: src/ui/chatty-settings-dialog.ui:41 +#: src/ui/chatty-settings-dialog.ui:54 msgid "_Add" msgstr "_Adicionar" -#: src/ui/chatty-settings-dialog.ui:57 +#: src/ui/chatty-settings-dialog.ui:70 msgid "_Save" msgstr "_Salvar" -#: src/ui/chatty-settings-dialog.ui:91 +#: src/ui/chatty-settings-dialog.ui:114 msgid "Accounts" msgstr "Contas" -#: src/ui/chatty-settings-dialog.ui:105 +#: src/ui/chatty-settings-dialog.ui:128 msgid "Add new account…" msgstr "Adicionar nova conta…" -#: src/ui/chatty-settings-dialog.ui:117 +#: src/ui/chatty-settings-dialog.ui:141 msgid "Privacy" msgstr "Privacidade" -#: src/ui/chatty-settings-dialog.ui:122 +#: src/ui/chatty-settings-dialog.ui:146 msgid "Message Receipts" msgstr "Confirmações de recebimento" -#: src/ui/chatty-settings-dialog.ui:123 +#: src/ui/chatty-settings-dialog.ui:147 msgid "Confirm received messages" msgstr "Confirmar mensagens recebidas" -#: src/ui/chatty-settings-dialog.ui:137 +#: src/ui/chatty-settings-dialog.ui:161 msgid "Message Archive Management" msgstr "Gerenciador de mensagens arquivadas" -#: src/ui/chatty-settings-dialog.ui:138 +#: src/ui/chatty-settings-dialog.ui:162 msgid "Sync conversations with chat server" msgstr "Sincronizar conversas com o servidor" -#: src/ui/chatty-settings-dialog.ui:152 +#: src/ui/chatty-settings-dialog.ui:176 msgid "Message Carbon Copies" msgstr "Copiar as mensagens" -#: src/ui/chatty-settings-dialog.ui:167 +#: src/ui/chatty-settings-dialog.ui:191 msgid "Typing Notification" msgstr "Notificar digitação" -#: src/ui/chatty-settings-dialog.ui:168 +#: src/ui/chatty-settings-dialog.ui:192 msgid "Send typing messages" msgstr "Enviar mensagens ao digitá-las" -#: src/ui/chatty-settings-dialog.ui:185 +#: src/ui/chatty-settings-dialog.ui:209 msgid "Chats List" msgstr "Lista de conversas" -#: src/ui/chatty-settings-dialog.ui:190 -msgid "Indicate Offline Contacts" -msgstr "Indicar contatos desconectados" - -#: src/ui/chatty-settings-dialog.ui:191 -msgid "Grey out avatars from offline contacts" -msgstr "Acinzentar avatares de contatos desconectados" - -#: src/ui/chatty-settings-dialog.ui:205 +#: src/ui/chatty-settings-dialog.ui:214 msgid "Indicate Idle Contacts" msgstr "Indicar contatos inativos" -#: src/ui/chatty-settings-dialog.ui:206 +#: src/ui/chatty-settings-dialog.ui:215 msgid "Blur avatars from idle contacts" msgstr "Desfocar avatares de contatos desconectados" -#: src/ui/chatty-settings-dialog.ui:220 +#: src/ui/chatty-settings-dialog.ui:229 msgid "Indicate Unknown Contacts" msgstr "Indicar contatos desconhecidos" -#: src/ui/chatty-settings-dialog.ui:221 +#: src/ui/chatty-settings-dialog.ui:230 msgid "Color unknown contact ID red" msgstr "Avermelhar contatos desconhecidos" -#: src/ui/chatty-settings-dialog.ui:238 +#: src/ui/chatty-settings-dialog.ui:247 msgid "Editor" msgstr "Editor" -#: src/ui/chatty-settings-dialog.ui:243 +#: src/ui/chatty-settings-dialog.ui:252 msgid "Graphical Emoticons" msgstr "Emoticons gráficos" -#: src/ui/chatty-settings-dialog.ui:244 +#: src/ui/chatty-settings-dialog.ui:253 msgid "Convert ASCII emoticons" msgstr "Converter emoticons ASCII" -#: src/ui/chatty-settings-dialog.ui:258 +#: src/ui/chatty-settings-dialog.ui:267 msgid "Return = Send Message" msgstr "Enter envia a mensagem" -#: src/ui/chatty-settings-dialog.ui:259 +#: src/ui/chatty-settings-dialog.ui:268 msgid "Send message with return key" msgstr "Enviar a mensagem com o botão enter" -#: src/ui/chatty-settings-dialog.ui:328 -msgid "Account ID" -msgstr "ID da conta" - -#: src/ui/chatty-settings-dialog.ui:361 -msgid "Protocol" -msgstr "Protocolo" - -#: src/ui/chatty-settings-dialog.ui:419 src/ui/chatty-settings-dialog.ui:716 -msgid "Password" -msgstr "Senha" - -#: src/ui/chatty-settings-dialog.ui:494 -msgid "Own Fingerprint" -msgstr "Minha digital" - -#: src/ui/chatty-settings-dialog.ui:520 -msgid "Other Devices" -msgstr "Outros dispositivos" - -#: src/ui/chatty-settings-dialog.ui:604 +#: src/ui/chatty-settings-dialog.ui:327 msgid "XMPP" msgstr "XMPP" -#: src/ui/chatty-settings-dialog.ui:618 +#: src/ui/chatty-settings-dialog.ui:341 msgid "Matrix" msgstr "Matrix" -#: src/ui/chatty-settings-dialog.ui:633 +#: src/ui/chatty-settings-dialog.ui:356 msgid "Telegram" msgstr "Telegram" -#: src/ui/chatty-settings-dialog.ui:691 -msgid "Provider" -msgstr "Provedor" +#: src/ui/chatty-settings-dialog.ui:465 +msgid "Matrix Home Server" +msgstr "Servidor matrix doméstico" + +#: src/ui/chatty-settings-dialog.ui:482 +msgid "Enter the matrix home server address." +msgstr "Insere o endereço do servidor matrix doméstico." -#: src/ui/chatty-window.ui:31 +#: src/ui/chatty-settings-dialog.ui:500 +msgid "_Accept" +msgstr "_Aceitar" + +#: src/ui/chatty-window.ui:30 msgid "About Chats" msgstr "Sobre o Conversas" @@ -750,30 +857,120 @@ msgstr "Nova mensagem…" msgid "New Group Message…" msgstr "Nova mensagem de grupo…" -#: src/ui/chatty-window.ui:84 -msgid "New Bulk SMS…" -msgstr "Novo SMS em massa…" - -#: src/ui/chatty-window.ui:176 +#: src/ui/chatty-window.ui:138 msgid "Leave Chat" msgstr "Sair da conversa" -#: src/ui/chatty-window.ui:189 +#: src/ui/chatty-window.ui:151 msgid "Delete Chat" msgstr "Apagar conversa" -#: src/users/chatty-contact.c:313 +#: src/ui/chatty-window.ui:378 +msgid "Start Chatting" +msgstr "Começar a conversar" + +#: src/users/chatty-contact.c:317 msgid "Mobile: " msgstr "No celular: " -#: src/users/chatty-contact.c:315 +#: src/users/chatty-contact.c:319 msgid "Work: " msgstr "No trabalho: " -#: src/users/chatty-contact.c:317 +#: src/users/chatty-contact.c:321 msgid "Other: " msgstr "Outro: " +#~ msgid "Mark offline users differently" +#~ msgstr "Diferenciar usuários desconectados" + +#~ msgid "ask your counterpart to use E2EE." +#~ msgstr "convide seu interlocutor a usar o E2EE." + +#~ msgid "Your messages are secured" +#~ msgstr "Suas mensagens estão protegidas" + +#~ msgid "by end-to-end encryption." +#~ msgstr "por criptografia ponta a ponta." + +#~ msgid "and carrier rates may apply." +#~ msgstr "e taxas podem ser cobradas." + +#~ msgid "Message Error" +#~ msgstr "Erro na mensagem" + +#~ msgid "Account Info" +#~ msgstr "Informações da conta" + +#~ msgid "Account Connected" +#~ msgstr "Conta conectada" + +#~ msgid "Choose a contact" +#~ msgstr "Escolha um contato" + +#~ msgid "" +#~ "Select an SMS or Instant Message contact with the \"+\" button in the titlebar." +#~ msgstr "" +#~ "Escolha por um contato via SMS ou mensagem instantânea com " +#~ "o botão \"+\" na barra de título." + +#~ msgid "Start a SMS chat with the \"+\" button in the titlebar." +#~ msgstr "" +#~ "Comece uma conversa via SMS tocando no botão \"+\" na barra de " +#~ "título." + +#~ msgid "members" +#~ msgstr "participantes" + +#~ msgid "Group Details" +#~ msgstr "Detalhes do grupo" + +#~ msgid "Invite Contact" +#~ msgstr "Convidar contato" + +#~ msgid "Room settings" +#~ msgstr "Configurações da sala" + +#~ msgid "Show notification badge" +#~ msgstr "Exibir barra de notificações" + +#~ msgid "0 members" +#~ msgstr "0 participante" + +#~ msgid "Secure messaging using OMEMO" +#~ msgstr "Proteja suas conversas usando OMEMO" + +#~ msgid "Indicate Offline Contacts" +#~ msgstr "Indicar contatos desconectados" + +#~ msgid "Grey out avatars from offline contacts" +#~ msgstr "Acinzentar avatares de contatos desconectados" + +#~ msgid "New Bulk SMS…" +#~ msgstr "Novo SMS em massa…" + +#~ msgid "Open Account Settings" +#~ msgstr "Abrir configurações da conta" + +#~ msgid "Account Disconnected" +#~ msgstr "Conta desconectada" + +#~ msgid "%I:%M %p" +#~ msgstr "%I:%M %p" + +#~ msgid "%A %R" +#~ msgstr "%A %R" + +#~ msgid "Add XMPP account" +#~ msgstr "Adicionar conta XMPP" + +#~ msgid "Other Devices" +#~ msgstr "Outros dispositivos" + +#~ msgid "Provider" +#~ msgstr "Provedor" + #~ msgid "Initial development release of chatty with support for xmpp and sms." #~ msgstr "" #~ "Versão inicial de desenvolvimento do chatty compatível com xmpp e sms" diff --git a/po/ro.po b/po/ro.po index 4888078..5ba5ca8 100644 --- a/po/ro.po +++ b/po/ro.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: chatty master\n" "Report-Msgid-Bugs-To: https://source.puri.sm/Librem5/chatty/issues\n" -"POT-Creation-Date: 2021-04-18 15:24+0000\n" -"PO-Revision-Date: 2021-04-18 21:35+0300\n" +"POT-Creation-Date: 2021-08-12 15:25+0000\n" +"PO-Revision-Date: 2021-07-06 17:50+0200\n" "Last-Translator: Florentina Mușat \n" "Language-Team: Romanian \n" "Language: ro\n" @@ -17,11 +17,11 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n==0 || (n!=1 && n%100>=1 && n" "%100<=19) ? 1 : 2);\n" -"X-Generator: Poedit 2.4.2\n" +"X-Generator: Gtranslator 3.30.1\n" "X-Poedit-SourceCharset: UTF-8\n" #: data/sm.puri.Chatty.desktop.in:3 data/sm.puri.Chatty.metainfo.xml.in:6 -#: src/chatty-application.c:326 src/ui/chatty-window.ui:183 +#: src/chatty-application.c:359 src/ui/chatty-window.ui:183 msgid "Chats" msgstr "Discuții" @@ -142,78 +142,78 @@ msgstr "Discuții este o aplicație de mesagerie care suportă XMPP și SMS." msgid "Chats message window" msgstr "Fereastra de mesaj pentru Discuții" -#: src/chatty-application.c:73 +#: src/chatty-application.c:79 msgid "Show release version" msgstr "Arată versiunea" -#: src/chatty-application.c:74 +#: src/chatty-application.c:80 msgid "Start in daemon mode" msgstr "Pornește în modul serviciu" -#: src/chatty-application.c:75 +#: src/chatty-application.c:81 msgid "Disable all accounts" msgstr "Dezactivează toate conturile" -#: src/chatty-application.c:76 +#: src/chatty-application.c:82 msgid "Enable libpurple debug messages" msgstr "Activează mesajele de depanare libpurple" -#: src/chatty-application.c:78 +#: src/chatty-application.c:84 msgid "Enable verbose libpurple debug messages" msgstr "Activează mesajele detaliate de depanare libpurple" -#: src/chatty-application.c:128 +#: src/chatty-application.c:134 #, c-format msgid "Authorize %s?" msgstr "Autorizați %s?" -#: src/chatty-application.c:132 src/matrix/matrix-utils.c:508 +#: src/chatty-application.c:138 src/matrix/matrix-utils.c:507 msgid "Reject" msgstr "Respinge" -#: src/chatty-application.c:134 src/matrix/matrix-utils.c:509 +#: src/chatty-application.c:140 src/matrix/matrix-utils.c:508 msgid "Accept" msgstr "Acceptă" -#: src/chatty-application.c:139 +#: src/chatty-application.c:145 #, c-format msgid "Add %s to contact list" msgstr "Adaugă %s la lista de contacte" -#: src/chatty-application.c:162 +#: src/chatty-application.c:168 msgid "Contact added" msgstr "Contact adăugat" -#: src/chatty-application.c:165 +#: src/chatty-application.c:171 #, c-format msgid "User %s has added %s to the contacts" msgstr "Utilizatorul %s a adăugat %s la contacte" -#: src/chatty-application.c:185 +#: src/chatty-application.c:191 msgid "Login failed" msgstr "Autentificarea a eșuat" -#: src/chatty-application.c:191 +#: src/chatty-application.c:197 msgid "Please check ID and password" msgstr "Verifică ID-ul și parola" -#: src/chatty-chat-view.c:224 +#: src/chatty-chat-view.c:202 msgid "This is an SMS conversation" msgstr "Aceasta este o conversație SMS" -#: src/chatty-chat-view.c:226 +#: src/chatty-chat-view.c:204 msgid "Your messages are not encrypted, and carrier rates may apply" msgstr "Mesajele nu sunt codate, și pot fi aplicate tarifele operatorului" -#: src/chatty-chat-view.c:230 +#: src/chatty-chat-view.c:208 msgid "This is an IM conversation" msgstr "Aceasta este o conversație IM" -#: src/chatty-chat-view.c:233 +#: src/chatty-chat-view.c:211 msgid "Your messages are encrypted" msgstr "Mesajele sunt codate" -#: src/chatty-chat-view.c:236 +#: src/chatty-chat-view.c:214 msgid "Your messages are not encrypted" msgstr "Mesajele nu sunt codate" @@ -224,111 +224,111 @@ msgid "Device ID %s fingerprint:" msgstr "Amprenta ID-ului aparatului %s:" #. Translators: Timestamp seconds suffix -#: src/chatty-list-row.c:73 +#: src/chatty-list-row.c:74 msgctxt "timestamp-suffix-seconds" msgid "s" msgstr "s" #. Translators: Timestamp minute suffix -#: src/chatty-list-row.c:75 +#: src/chatty-list-row.c:76 msgctxt "timestamp-suffix-minute" msgid "m" msgstr "m" #. Translators: Timestamp minutes suffix -#: src/chatty-list-row.c:77 +#: src/chatty-list-row.c:78 msgctxt "timestamp-suffix-minutes" msgid "m" msgstr "m" #. Translators: Timestamp hour suffix -#: src/chatty-list-row.c:79 +#: src/chatty-list-row.c:80 msgctxt "timestamp-suffix-hour" msgid "h" msgstr "o" #. Translators: Timestamp hours suffix -#: src/chatty-list-row.c:81 +#: src/chatty-list-row.c:82 msgctxt "timestamp-suffix-hours" msgid "h" msgstr "o" #. Translators: Timestamp day suffix -#: src/chatty-list-row.c:83 +#: src/chatty-list-row.c:84 msgctxt "timestamp-suffix-day" msgid "d" msgstr "z" #. Translators: Timestamp days suffix -#: src/chatty-list-row.c:85 +#: src/chatty-list-row.c:86 msgctxt "timestamp-suffix-days" msgid "d" msgstr "z" #. Translators: Timestamp month suffix -#: src/chatty-list-row.c:87 +#: src/chatty-list-row.c:88 msgctxt "timestamp-suffix-month" msgid "mo" msgstr "lu" #. Translators: Timestamp months suffix -#: src/chatty-list-row.c:89 +#: src/chatty-list-row.c:90 msgctxt "timestamp-suffix-months" msgid "mos" msgstr "luni" #. Translators: Timestamp year suffix -#: src/chatty-list-row.c:91 +#: src/chatty-list-row.c:92 msgctxt "timestamp-suffix-year" msgid "y" msgstr "a" #. Translators: Timestamp years suffix -#: src/chatty-list-row.c:93 +#: src/chatty-list-row.c:94 msgctxt "timestamp-suffix-years" msgid "y" msgstr "a" #. Translators: Timestamp prefix (e.g. Over 5h) -#: src/chatty-list-row.c:195 +#: src/chatty-list-row.c:196 msgid "Over" msgstr "Peste" #. Translators: Timestamp prefix (e.g. Almost 5h) -#: src/chatty-list-row.c:200 +#: src/chatty-list-row.c:201 msgid "Almost" msgstr "Aproape" -#: src/chatty-list-row.c:217 +#: src/chatty-list-row.c:253 msgid "Owner" msgstr "Proprietar" -#: src/chatty-list-row.c:220 +#: src/chatty-list-row.c:256 msgid "Moderator" msgstr "Moderator" -#: src/chatty-list-row.c:223 +#: src/chatty-list-row.c:259 msgid "Member" msgstr "Membru" -#: src/chatty-message-row.c:91 +#: src/chatty-message-row.c:79 msgid "Copy" msgstr "Copiază" -#: src/chatty-notification.c:166 +#: src/chatty-notification.c:45 msgid "Open Message" msgstr "Deschide mesajul" -#: src/chatty-notification.c:201 +#: src/chatty-notification.c:185 #, c-format msgid "New message from %s" msgstr "Mesaj nou de la %s" -#: src/chatty-notification.c:203 +#: src/chatty-notification.c:187 msgid "Message Received" msgstr "Mesaj primit" -#: src/chatty-purple-notify.c:44 src/matrix/matrix-utils.c:504 +#: src/chatty-purple-notify.c:44 src/matrix/matrix-utils.c:503 msgid "Close" msgstr "Închide" @@ -340,9 +340,12 @@ msgstr "Salvează fișierul..." msgid "Open File..." msgstr "Deschide fișierul..." -#: src/chatty-purple-request.c:191 src/chatty-window.c:501 -#: src/dialogs/chatty-settings-dialog.c:578 src/ui/chatty-dialog-join-muc.ui:16 -#: src/ui/chatty-info-dialog.ui:44 src/ui/chatty-info-dialog.ui:551 +#: src/chatty-purple-request.c:191 src/chatty-window.c:463 +#: src/dialogs/chatty-ma-account-details.c:87 +#: src/dialogs/chatty-pp-account-details.c:87 +#: src/ui/chatty-dialog-join-muc.ui:16 src/ui/chatty-info-dialog.ui:44 +#: src/ui/chatty-info-dialog.ui:191 src/ui/chatty-ma-chat-info.ui:114 +#: src/ui/chatty-pp-chat-info.ui:380 msgid "Cancel" msgstr "Anulează" @@ -350,12 +353,13 @@ msgstr "Anulează" msgid "Save" msgstr "Salvează" -#: src/chatty-purple-request.c:193 src/dialogs/chatty-settings-dialog.c:577 -#: src/ui/chatty-info-dialog.ui:558 +#: src/chatty-purple-request.c:193 src/dialogs/chatty-ma-account-details.c:86 +#: src/dialogs/chatty-pp-account-details.c:86 src/ui/chatty-info-dialog.ui:198 +#: src/ui/chatty-ma-chat-info.ui:121 src/ui/chatty-pp-chat-info.ui:387 msgid "Open" msgstr "Deschide" -#: src/chatty-secret-store.c:81 +#: src/chatty-secret-store.c:86 #, c-format msgid "Chatty password for \"%s\"" msgstr "Parola Chatty pentru „%s”" @@ -363,29 +367,29 @@ msgstr "Parola Chatty pentru „%s”" #. TRANSLATORS: Timestamp from the last week with 24 hour time, e.g. “Tuesday 18∶42”. #. See https://developer.gnome.org/glib/stable/glib-GDateTime.html#g-date-time-format #. -#: src/chatty-utils.c:377 +#: src/chatty-utils.c:379 msgid "%A %H∶%M" msgstr "%A %O∶%M" #. TRANSLATORS: Timestamp from the last week with 12 hour time, e.g. “Tuesday 06∶42 PM”. #. See https://developer.gnome.org/glib/stable/glib-GDateTime.html#g-date-time-format #. -#: src/chatty-utils.c:382 +#: src/chatty-utils.c:384 msgid "%A %I∶%M %p" msgstr "%A %I:%M %p" #. TRANSLATORS: Timestamp from more than 7 days ago, e.g. “2020-08-11”. #. See https://developer.gnome.org/glib/stable/glib-GDateTime.html#g-date-time-format #. -#: src/chatty-utils.c:389 +#: src/chatty-utils.c:391 msgid "%Y-%m-%d" msgstr "%A-%l-%z" -#: src/chatty-window.c:190 +#: src/chatty-window.c:173 msgid "Select a contact with the “+” button in the titlebar." msgstr "Selectați un contact cu butonul „+” din bara de titlu." -#: src/chatty-window.c:194 +#: src/chatty-window.c:177 msgid "" "For Instant Messaging add or activate an account in \"preferences" "\"." @@ -393,86 +397,87 @@ msgstr "" "Pentru Mesagerie instantă adaugă sau activează un cont în " "„preferințe”." -#: src/chatty-window.c:486 +#: src/chatty-window.c:448 msgid "Delete chat with" msgstr "Șterge discuția cu" -#: src/chatty-window.c:487 +#: src/chatty-window.c:449 msgid "This deletes the conversation history" msgstr "Aceasta șterge istoricul conversației" -#: src/chatty-window.c:489 +#: src/chatty-window.c:451 msgid "Disconnect group chat" msgstr "Deconectează discuția în grup" -#: src/chatty-window.c:490 +#: src/chatty-window.c:452 msgid "This removes chat from chats list" msgstr "Aceasta elimină discuția din lista discuțiilor" -#: src/chatty-window.c:503 +#: src/chatty-window.c:465 msgid "Delete" msgstr "Șterge" -#: src/chatty-window.c:574 +#: src/chatty-window.c:535 #, c-format msgid "Error saving contact: %s" msgstr "Eroare la salvarea contactului: %s" -#: src/chatty-window.c:672 +#: src/chatty-window.c:618 msgid "An SMS and XMPP messaging client" msgstr "Un client de mesagerie SMS și XMPP" -#: src/chatty-window.c:679 +#: src/chatty-window.c:625 msgid "translator-credits" msgstr "" -"Florentina Mușat , 2020" +"Florentina Mușat , " +"2020-2021" -#: src/chatty-window.c:937 +#: src/chatty-window.c:843 #, c-format msgid "“%s” is not a valid phone number" msgstr "„%s” nu este un număr de telefon valabil" -#: src/dialogs/chatty-info-dialog.c:78 -#, c-format -msgid "%u Member" -msgid_plural "%u Members" -msgstr[0] "%u Membru" -msgstr[1] "%u Membri" -msgstr[2] "%u de Membri" - -#: src/dialogs/chatty-info-dialog.c:106 +#: src/dialogs/chatty-pp-chat-info.c:163 msgid "Encryption is not available" msgstr "Codarea nu este disponibilă" -#: src/dialogs/chatty-info-dialog.c:110 +#: src/dialogs/chatty-pp-chat-info.c:167 msgid "This chat is encrypted" msgstr "Această discuție este codată" -#: src/dialogs/chatty-info-dialog.c:114 +#: src/dialogs/chatty-pp-chat-info.c:171 msgid "This chat is not encrypted" msgstr "Această discuție nu este codată" -#: src/dialogs/chatty-info-dialog.c:138 +#: src/dialogs/chatty-pp-chat-info.c:195 msgid "Encryption not available" msgstr "Codare indisponibilă" -#: src/dialogs/chatty-info-dialog.c:174 +#: src/dialogs/chatty-pp-chat-info.c:216 +#, c-format +msgid "%u Member" +msgid_plural "%u Members" +msgstr[0] "%u Membru" +msgstr[1] "%u Membri" +msgstr[2] "%u de Membri" + +#: src/dialogs/chatty-pp-chat-info.c:250 msgid "Phone Number:" msgstr "Număr de telefon:" -#: src/dialogs/chatty-info-dialog.c:176 +#: src/dialogs/chatty-pp-chat-info.c:252 msgid "XMPP ID:" msgstr "ID XMPP:" -#: src/dialogs/chatty-info-dialog.c:181 +#: src/dialogs/chatty-pp-chat-info.c:257 msgid "Matrix ID:" msgstr "ID Matrix:" -#: src/dialogs/chatty-info-dialog.c:185 +#: src/dialogs/chatty-pp-chat-info.c:261 msgid "Telegram ID:" msgstr "ID Telegram:" -#: src/dialogs/chatty-new-chat-dialog.c:134 +#: src/dialogs/chatty-new-chat-dialog.c:133 msgid "Send To" msgstr "Trimite la" @@ -481,64 +486,70 @@ msgstr "Trimite la" msgid "Error opening GNOME Contacts: %s" msgstr "Eroare la deschiderea Contacte GNOME: %s" -#: src/dialogs/chatty-settings-dialog.c:424 -msgid "Select Protocol" -msgstr "Alege protocolul" +#: src/dialogs/chatty-ma-account-details.c:83 +#: src/dialogs/chatty-pp-account-details.c:83 src/ui/chatty-info-dialog.ui:186 +#: src/ui/chatty-ma-chat-info.ui:109 src/ui/chatty-pp-chat-info.ui:375 +msgid "Set Avatar" +msgstr "Alege poza de profil" -#: src/dialogs/chatty-settings-dialog.c:454 +#: src/dialogs/chatty-ma-account-details.c:325 +#: src/dialogs/chatty-pp-account-details.c:158 msgid "connected" msgstr "conectat" -#: src/dialogs/chatty-settings-dialog.c:456 +#: src/dialogs/chatty-ma-account-details.c:327 +#: src/dialogs/chatty-pp-account-details.c:160 msgid "connecting…" msgstr "se conectează…" -#: src/dialogs/chatty-settings-dialog.c:458 +#: src/dialogs/chatty-ma-account-details.c:329 +#: src/dialogs/chatty-pp-account-details.c:162 msgid "disconnected" msgstr "deconectat" -#: src/dialogs/chatty-settings-dialog.c:574 src/ui/chatty-info-dialog.ui:546 -msgid "Set Avatar" -msgstr "Alege poza de profil" - -#: src/dialogs/chatty-settings-dialog.c:658 -#: src/ui/chatty-settings-dialog.ui:461 +#: src/dialogs/chatty-settings-dialog.c:378 +#: src/ui/chatty-ma-account-details.ui:156 +#: src/ui/chatty-pp-account-details.ui:150 msgid "Delete Account" msgstr "Șterge contul" -#: src/dialogs/chatty-settings-dialog.c:661 +#: src/dialogs/chatty-settings-dialog.c:381 #, c-format msgid "Delete account %s?" msgstr "Ștergi contul %s?" -#: src/matrix/chatty-ma-account.c:250 +#: src/dialogs/chatty-settings-dialog.c:483 +msgid "Select Protocol" +msgstr "Alege protocolul" + +#: src/matrix/chatty-ma-account.c:291 msgid "Incorrect password" msgstr "Parolă greșită" -#: src/matrix/chatty-ma-account.c:253 +#: src/matrix/chatty-ma-account.c:294 msgid "_OK" msgstr "_OK" -#: src/matrix/chatty-ma-account.c:254 src/ui/chatty-settings-dialog.ui:41 -#: src/ui/chatty-settings-dialog.ui:720 +#: src/matrix/chatty-ma-account.c:295 src/ui/chatty-settings-dialog.ui:41 +#: src/ui/chatty-settings-dialog.ui:509 msgid "_Cancel" msgstr "_Anulează" -#: src/matrix/chatty-ma-account.c:260 +#: src/matrix/chatty-ma-account.c:301 #, c-format msgid "Please enter password for “%s”" msgstr "Introdu parola pentru „%s”" -#: src/matrix/chatty-ma-chat.c:158 +#: src/matrix/chatty-ma-chat.c:164 msgid "Empty room" msgstr "Cameră goală" -#: src/matrix/chatty-ma-chat.c:162 +#: src/matrix/chatty-ma-chat.c:168 #, c-format msgid "%s and %s" msgstr "%s și %s" -#: src/matrix/chatty-ma-chat.c:164 +#: src/matrix/chatty-ma-chat.c:170 #, c-format msgid "%s and %u other" msgid_plural "%s and %u others" @@ -546,27 +557,27 @@ msgstr[0] "%s și %u altul" msgstr[1] "%s și %u alții" msgstr[2] "%s și %u alții" -#: src/matrix/matrix-utils.c:473 +#: src/matrix/matrix-utils.c:472 #, c-format msgid "The certificate for ‘%s’ has unknown CA" msgstr "Certificatul pentru „%s” are CA necunoscută" -#: src/matrix/matrix-utils.c:475 +#: src/matrix/matrix-utils.c:474 #, c-format msgid "The certificate for ‘%s’ is self-signed" msgstr "Certificatul pentru „%s” este autosemnat" -#: src/matrix/matrix-utils.c:479 +#: src/matrix/matrix-utils.c:478 #, c-format msgid "The certificate for ‘%s’ has expired" msgstr "Certificatul pentru „%s” a expirat" -#: src/matrix/matrix-utils.c:483 +#: src/matrix/matrix-utils.c:482 #, c-format msgid "The certificate for ‘%s’ has been revoked" msgstr "Certificatul pentru „%s” a fost anulat" -#: src/matrix/matrix-utils.c:492 +#: src/matrix/matrix-utils.c:491 #, c-format msgid "Error validating certificate for ‘%s’" msgstr "Eroare la validarea certificatului pentru „%s”" @@ -627,46 +638,91 @@ msgstr "Detaliile discuției" msgid "Invite" msgstr "Invită" -#: src/ui/chatty-info-dialog.ui:138 +#: src/ui/chatty-ma-chat-info.ui:79 src/ui/chatty-ma-account-details.ui:207 +msgid "Matrix ID" +msgstr "ID Matrix" + +#: src/ui/chatty-pp-chat-info.ui:59 msgid "Room topic" msgstr "Subiectul camerei" -#: src/ui/chatty-info-dialog.ui:242 +#: src/ui/chatty-pp-chat-info.ui:162 msgid "XMPP ID" msgstr "ID XMPP" -#: src/ui/chatty-info-dialog.ui:268 src/ui/chatty-info-dialog.ui:374 +#: src/ui/chatty-pp-chat-info.ui:189 src/ui/chatty-pp-chat-info.ui:295 msgid "Encryption" msgstr "Codare" -#: src/ui/chatty-info-dialog.ui:294 src/ui/chatty-settings-dialog.ui:374 +#: src/ui/chatty-pp-chat-info.ui:215 src/ui/chatty-ma-account-details.ui:41 +#: src/ui/chatty-pp-account-details.ui:93 msgid "Status" msgstr "Stare" -#: src/ui/chatty-info-dialog.ui:319 +#: src/ui/chatty-pp-chat-info.ui:240 msgid "Chat settings" msgstr "Opțiunile discuției" -#: src/ui/chatty-info-dialog.ui:340 +#: src/ui/chatty-pp-chat-info.ui:261 msgid "Notifications" msgstr "Înștiințări" -#: src/ui/chatty-info-dialog.ui:357 +#: src/ui/chatty-pp-chat-info.ui:278 msgid "Status Messages" msgstr "Mesaje de stare" -#: src/ui/chatty-info-dialog.ui:358 +#: src/ui/chatty-pp-chat-info.ui:279 msgid "Show status messages in chat" msgstr "Arată mesajele de stare în discuție" -#: src/ui/chatty-info-dialog.ui:375 +#: src/ui/chatty-pp-chat-info.ui:296 msgid "Encrypt Messages" msgstr "Codare Mesaje" -#: src/ui/chatty-info-dialog.ui:397 +#: src/ui/chatty-pp-chat-info.ui:318 msgid "Fingerprints" msgstr "Amprente" +#: src/ui/chatty-ma-account-details.ui:67 +msgid "Name" +msgstr "Nume" + +#: src/ui/chatty-ma-account-details.ui:97 +msgid "Email" +msgstr "E-mail" + +#: src/ui/chatty-ma-account-details.ui:126 +msgid "Phone" +msgstr "Telefon" + +#: src/ui/chatty-ma-account-details.ui:170 +msgid "Advanced information" +msgstr "Informații avansate" + +#: src/ui/chatty-ma-account-details.ui:181 +msgid "Homeserver" +msgstr "Server de acasă" + +#: src/ui/chatty-ma-account-details.ui:233 +msgid "Device ID" +msgstr "ID dispozitiv" + +#: src/ui/chatty-pp-account-details.ui:43 +msgid "Account ID" +msgstr "ID-ul contului" + +#: src/ui/chatty-pp-account-details.ui:67 +msgid "Protocol" +msgstr "Protocol" + +#: src/ui/chatty-pp-account-details.ui:119 src/ui/chatty-settings-dialog.ui:419 +msgid "Password" +msgstr "Parola" + +#: src/ui/chatty-pp-account-details.ui:164 +msgid "Own Fingerprint" +msgstr "Amprenta proprie" + #: src/ui/chatty-settings-dialog.ui:12 src/ui/chatty-window.ui:17 msgid "Preferences" msgstr "Preferințe" @@ -679,7 +735,7 @@ msgstr "Înapoi" msgid "_Add" msgstr "_Adaugă" -#: src/ui/chatty-settings-dialog.ui:80 +#: src/ui/chatty-settings-dialog.ui:70 msgid "_Save" msgstr "_Salvează" @@ -763,43 +819,27 @@ msgstr "Enter = Trimite mesajul" msgid "Send message with return key" msgstr "Trimite mesajul cu tasta Enter" -#: src/ui/chatty-settings-dialog.ui:319 -msgid "Account ID" -msgstr "ID-ul contului" - -#: src/ui/chatty-settings-dialog.ui:345 -msgid "Protocol" -msgstr "Protocol" - -#: src/ui/chatty-settings-dialog.ui:403 src/ui/chatty-settings-dialog.ui:630 -msgid "Password" -msgstr "Parola" - -#: src/ui/chatty-settings-dialog.ui:479 -msgid "Own Fingerprint" -msgstr "Amprenta proprie" - -#: src/ui/chatty-settings-dialog.ui:538 +#: src/ui/chatty-settings-dialog.ui:327 msgid "XMPP" msgstr "XMPP" -#: src/ui/chatty-settings-dialog.ui:552 +#: src/ui/chatty-settings-dialog.ui:341 msgid "Matrix" msgstr "Matrix" -#: src/ui/chatty-settings-dialog.ui:567 +#: src/ui/chatty-settings-dialog.ui:356 msgid "Telegram" msgstr "Telegram" -#: src/ui/chatty-settings-dialog.ui:676 +#: src/ui/chatty-settings-dialog.ui:465 msgid "Matrix Home Server" msgstr "Serverul Matrix" -#: src/ui/chatty-settings-dialog.ui:693 +#: src/ui/chatty-settings-dialog.ui:482 msgid "Enter the matrix home server address." msgstr "Introdu adresa serverului Matrix." -#: src/ui/chatty-settings-dialog.ui:711 +#: src/ui/chatty-settings-dialog.ui:500 msgid "_Accept" msgstr "_Acceptă" @@ -823,7 +863,7 @@ msgstr "Părăsește discuția" msgid "Delete Chat" msgstr "Șterge discuția" -#: src/ui/chatty-window.ui:396 +#: src/ui/chatty-window.ui:378 msgid "Start Chatting" msgstr "Începe discuția" diff --git a/sm.puri.Chatty.json b/sm.puri.Chatty.json index 78961f0..8c3a1f1 100644 --- a/sm.puri.Chatty.json +++ b/sm.puri.Chatty.json @@ -9,7 +9,12 @@ "--share=ipc", "--socket=fallback-x11", "--socket=wayland", - "--talk-name=org.sigxcpu.Feedback" + "--talk-name=org.sigxcpu.Feedback", + "--system-talk-name=org.freedesktop.ModemManager1", + "--talk-name=org.gnome.evolution.dataserver.AddressBook10", + "--talk-name=org.gnome.evolution.dataserver.Sources5", + "--talk-name=im.pidgin.purple.PurpleService", + "--talk-name=org.gnome.evolution.dataserver.Subprocess.Backend.*" ], "cleanup-commands": [ "rm -rf /app/lib/debug/ /app/include/boost" ], "cleanup": [ @@ -169,9 +174,9 @@ "-DENABLE_UOA=OFF", "-DENABLE_GOOGLE_AUTH=OFF", "-DENABLE_GOOGLE=OFF", - "-DENABLE_WITH_PHONENUMBER=ON", "-DENABLE_VALA_BINDINGS=ON", "-DENABLE_WEATHER=OFF", + "-DWITH_PHONENUMBER=ON", "-DWITH_OPENLDAP=OFF", "-DWITH_LIBDB=OFF", "-DENABLE_INTROSPECTION=ON", @@ -207,10 +212,64 @@ "sources": [ { "type": "git", + "tag" : "3.2.4", "url": "https://gitlab.matrix.org/matrix-org/olm.git" } ] }, + { + "name" : "gspell", + "config-opts" : [ + "--disable-gtk-doc", + "--disable-gtk-doc-html" + ], + "cleanup" : [ + "/bin", + "/include", + "/lib/pkgconfig", + "*.la", + "/share" + ], + "sources" : [ + { + "type" : "archive", + "url" : "https://download.gnome.org/sources/gspell/1.8/gspell-1.8.1.tar.xz", + "sha256" : "819a1d23c7603000e73f5e738bdd284342e0cd345fb0c7650999c31ec741bbe5" + } + ] + }, + { + "name" : "gudev", + "buildsystem" : "meson", + "config-opts" : [ + "-Dtests=disabled", + "-Dintrospection=disabled" + ], + "sources" : [ + { + "type" : "git", + "url" : "https://gitlab.gnome.org/GNOME/libgudev.git" + } + ] + }, + { + "name" : "ModemManager", + "buildsystem" : "autotools", + "config-opts" : [ + "--disable-introspection", + "--disable-vala", + "--with-udev-base-dir=/app/lib", + "--with-systemdsystemunitdir=/app/lib/systemd/system", + "--without-mbim", + "--without-qmi" + ], + "sources" : [ + { + "type" : "git", + "url" : "git://anongit.freedesktop.org/ModemManager/ModemManager" + } + ] + }, { "name": "purism-chatty", "buildsystem": "meson", diff --git a/src/chatty-application.c b/src/chatty-application.c index 871bdde..eaecc16 100644 --- a/src/chatty-application.c +++ b/src/chatty-application.c @@ -193,7 +193,7 @@ application_show_connection_error (ChattyApplication *self, gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog), "%s: %s\n\n%s", message, - chatty_account_get_username (CHATTY_ACCOUNT (account)), + chatty_item_get_username (CHATTY_ITEM (account)), _("Please check ID and password")); run_dialog_and_destroy (GTK_DIALOG (dialog)); @@ -206,13 +206,8 @@ application_open_chat (ChattyApplication *self, g_assert (CHATTY_IS_APPLICATION (self)); g_assert (CHATTY_IS_CHAT (chat)); - if (!self->main_window) { - self->main_window = chatty_window_new (GTK_APPLICATION (self)); - g_object_add_weak_pointer (G_OBJECT (self->main_window), (gpointer *)&self->main_window); - } - + g_application_activate (G_APPLICATION (self)); chatty_window_open_chat (CHATTY_WINDOW (self->main_window), chat); - gtk_window_present (GTK_WINDOW (self->main_window)); } static gboolean @@ -237,8 +232,8 @@ chatty_application_show_window (GSimpleAction *action, g_assert (CHATTY_IS_APPLICATION (self)); + self->show_window = TRUE; g_application_activate (G_APPLICATION (self)); - gtk_window_present (GTK_WINDOW (self->main_window)); } static void @@ -260,6 +255,8 @@ chatty_application_open_chat (GSimpleAction *action, CHATTY_DEBUG_MSG ("Opening chat %s, account: %s", room_id, account_id); + self->show_window = TRUE; + g_application_activate (G_APPLICATION (self)); chatty_window_open_chat (CHATTY_WINDOW (self->main_window), chat); } diff --git a/src/chatty-chat-view.c b/src/chatty-chat-view.c index 564e3cb..dfdfb47 100644 --- a/src/chatty-chat-view.c +++ b/src/chatty-chat-view.c @@ -10,6 +10,7 @@ */ #include +#include #include "chatty-avatar.h" #include "chatty-chat.h" @@ -55,8 +56,6 @@ struct _ChattyChatView gboolean first_scroll_to_bottom; }; -static GHashTable *ht_sms_id = NULL; - #define INDICATOR_WIDTH 60 #define INDICATOR_HEIGHT 40 #define INDICATOR_MARGIN 2 @@ -90,14 +89,6 @@ enum { static guint signals[N_SIGNALS]; -static gboolean -chat_view_hash_table_match_item (gpointer key, - gpointer value, - gpointer user_data) -{ - return value == user_data; -} - static void chatty_draw_typing_indicator (cairo_t *cr) { @@ -206,7 +197,7 @@ chatty_chat_view_update (ChattyChatView *self) gtk_widget_set_visible (self->send_file_button, chatty_chat_has_file_upload (self->chat)); - if (protocol == CHATTY_PROTOCOL_SMS) { + if (protocol == CHATTY_PROTOCOL_MMS_SMS) { gtk_label_set_label (GTK_LABEL (self->empty_label0), _("This is an SMS conversation")); gtk_label_set_label (GTK_LABEL (self->empty_label1), @@ -225,7 +216,7 @@ chatty_chat_view_update (ChattyChatView *self) context = gtk_widget_get_style_context (self->send_message_button); - if (protocol == CHATTY_PROTOCOL_SMS) + if (protocol == CHATTY_PROTOCOL_MMS_SMS) gtk_style_context_add_class (context, "button_send_green"); else if (chatty_chat_is_im (self->chat)) gtk_style_context_add_class (context, "suggested-action"); @@ -267,6 +258,21 @@ chat_view_edge_overshot_cb (ChattyChatView *self, } +static void +chat_account_status_changed_cb (ChattyChatView *self) +{ + ChattyAccount *account; + gboolean enabled; + + account = chatty_chat_get_account (self->chat); + g_return_if_fail (account); + + enabled = chatty_account_get_status (account) == CHATTY_CONNECTED; + gtk_widget_set_sensitive (self->message_input, enabled); + gtk_widget_set_sensitive (self->send_file_button, enabled); + gtk_widget_set_sensitive (self->send_message_button, enabled); +} + static GtkWidget * chat_view_message_row_new (ChattyMessage *message, ChattyChatView *self) @@ -371,6 +377,16 @@ chat_view_send_file_button_clicked_cb (ChattyChatView *self, } } +static void +view_send_message_async_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(ChattyChatView) self = user_data; + + chatty_chat_set_unread_count (self->chat, 0); +} + static void chat_view_send_message_button_clicked_cb (ChattyChatView *self) { @@ -379,8 +395,6 @@ chat_view_send_message_button_clicked_cb (ChattyChatView *self) g_autoptr(ChattyMessage) msg = NULL; g_autofree char *message = NULL; GtkTextIter start, end; - gchar *sms_id_str; - guint sms_id; g_assert (CHATTY_IS_CHAT_VIEW (self)); @@ -413,24 +427,6 @@ chat_view_send_message_button_clicked_cb (ChattyChatView *self) protocol = chatty_item_get_protocols (CHATTY_ITEM (self->chat)); - /* provide a msg-id to the sms-plugin for send-receipts */ - if (conv && chatty_item_get_protocols (CHATTY_ITEM (self->chat)) == CHATTY_PROTOCOL_SMS) { - sms_id = g_random_int (); - - sms_id_str = g_strdup_printf ("%i", sms_id); - - g_hash_table_insert (ht_sms_id, sms_id_str, g_object_ref (self->chat)); - - g_debug ("hash table insert sms_id_str: %s ht_size: %i", - sms_id_str, g_hash_table_size (ht_sms_id)); - - purple_conv_im_send_with_flags (PURPLE_CONV_IM (conv), - sms_id_str, - PURPLE_MESSAGE_NO_LOG | - PURPLE_MESSAGE_NOTIFY | - PURPLE_MESSAGE_INVISIBLE); - } - if (protocol == CHATTY_PROTOCOL_MATRIX || protocol == CHATTY_PROTOCOL_XMPP || protocol == CHATTY_PROTOCOL_TELEGRAM) @@ -440,7 +436,9 @@ chat_view_send_message_button_clicked_cb (ChattyChatView *self) NULL, time (NULL), escaped ? CHATTY_MESSAGE_HTML_ESCAPED : CHATTY_MESSAGE_TEXT, CHATTY_DIRECTION_OUT, 0); - chatty_chat_send_message_async (self->chat, msg, NULL, NULL); + chatty_chat_send_message_async (self->chat, msg, + view_send_message_async_cb, + g_object_ref (self)); gtk_widget_hide (self->send_message_button); } @@ -482,7 +480,7 @@ chat_view_message_input_changed_cb (ChattyChatView *self) chatty_update_typing_status (self); if (chatty_settings_get_convert_emoticons (chatty_settings_get_default ()) && - chatty_item_get_protocols (CHATTY_ITEM (self->chat)) != CHATTY_PROTOCOL_SMS) + chatty_item_get_protocols (CHATTY_ITEM (self->chat)) != CHATTY_PROTOCOL_MMS_SMS) chatty_check_for_emoticon (self); } @@ -595,56 +593,6 @@ chat_view_file_requested_cb (ChattyChatView *self, g_object_ref (self)); } -static void -chat_view_sms_sent_cb (const char *sms_id, - int status) -{ - ChattyChat *chat; - ChattyMessage *message; - GListModel *message_list; - const gchar *message_id; - ChattyMsgStatus sent_status; - time_t time_now; - guint n_items; - - if (sms_id == NULL) - return; - - if (status == CHATTY_SMS_RECEIPT_NONE) - sent_status = CHATTY_STATUS_SENDING_FAILED; - else if (status == CHATTY_SMS_RECEIPT_MM_ACKN) - sent_status = CHATTY_STATUS_SENT; - else if (status == CHATTY_SMS_RECEIPT_SMSC_ACKN) - sent_status = CHATTY_STATUS_DELIVERED; - else - return; - - chat = g_hash_table_lookup (ht_sms_id, sms_id); - - if (!chat) - return; - - message_list = chatty_chat_get_messages (chat); - n_items = g_list_model_get_n_items (message_list); - message = g_list_model_get_item (message_list, n_items - 1); - message_id = chatty_message_get_id (message); - time_now = time (NULL); - - if (message_id == NULL) - chatty_message_set_id (message, sms_id); - - if (g_strcmp0 (message_id, sms_id) == 0) { - chatty_message_set_status (message, sent_status, time_now); - g_object_unref (message); - return; - } - - message = chatty_pp_chat_find_message_with_id (CHATTY_PP_CHAT (chat), sms_id); - - if (message) - chatty_message_set_status (message, sent_status, time_now); -} - static void chatty_chat_view_map (GtkWidget *widget) { @@ -660,9 +608,6 @@ chatty_chat_view_finalize (GObject *object) { ChattyChatView *self = (ChattyChatView *)object; - g_hash_table_foreach_remove (ht_sms_id, - chat_view_hash_table_match_item, - self); g_clear_object (&self->chat); G_OBJECT_CLASS (chatty_chat_view_parent_class)->finalize (object); @@ -722,6 +667,7 @@ chatty_chat_view_class_init (ChattyChatViewClass *klass) static void chatty_chat_view_init (ChattyChatView *self) { + GspellTextView *gspell_view; GtkAdjustment *vadjustment; gtk_widget_init_template (GTK_WIDGET (self)); @@ -736,6 +682,9 @@ chatty_chat_view_init (ChattyChatView *self) gtk_list_box_set_header_func (GTK_LIST_BOX (self->message_list), (GtkListBoxUpdateHeaderFunc)chat_view_update_header_func, NULL, NULL); + + gspell_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (self->message_input)); + gspell_text_view_basic_setup (gspell_view); } GtkWidget * @@ -744,29 +693,11 @@ chatty_chat_view_new (void) return g_object_new (CHATTY_TYPE_CHAT_VIEW, NULL); } - -void -chatty_chat_view_purple_init (void) -{ - ht_sms_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - - purple_signal_connect (purple_conversations_get_handle (), - "sms-sent", ht_sms_id, - PURPLE_CALLBACK (chat_view_sms_sent_cb), NULL); -} - -void -chatty_chat_view_purple_uninit (void) -{ - purple_signals_disconnect_by_handle (ht_sms_id); - - g_hash_table_destroy (ht_sms_id); -} - void chatty_chat_view_set_chat (ChattyChatView *self, ChattyChat *chat) { + ChattyAccount *account; GListModel *messages; g_return_if_fail (CHATTY_IS_CHAT_VIEW (self)); @@ -790,9 +721,20 @@ chatty_chat_view_set_chat (ChattyChatView *self, return; messages = chatty_chat_get_messages (chat); + account = chatty_chat_get_account (chat); + if (g_list_model_get_n_items (messages) <= 3) chatty_chat_load_past_messages (chat, -1); + + if (account) + g_signal_connect_object (account, "notify::status", + G_CALLBACK (chat_account_status_changed_cb), + self, + G_CONNECT_SWAPPED); + + chat_account_status_changed_cb (self); + gtk_list_box_bind_model (GTK_LIST_BOX (self->message_list), chatty_chat_get_messages (self->chat), (GtkListBoxCreateWidgetFunc)chat_view_message_row_new, @@ -811,6 +753,8 @@ chatty_chat_view_set_chat (ChattyChatView *self, chat_buddy_typing_changed_cb (self); chatty_chat_view_update (self); chat_view_adjustment_value_changed_cb (self); + + gtk_widget_grab_focus (self->message_input); } ChattyChat * diff --git a/src/chatty-chat-view.h b/src/chatty-chat-view.h index 63ea043..a91de12 100644 --- a/src/chatty-chat-view.h +++ b/src/chatty-chat-view.h @@ -22,8 +22,6 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (ChattyChatView, chatty_chat_view, CHATTY, CHAT_VIEW, GtkBox) GtkWidget *chatty_chat_view_new (void); -void chatty_chat_view_purple_init (void); -void chatty_chat_view_purple_uninit (void); void chatty_chat_view_set_chat (ChattyChatView *self, ChattyChat *chat); ChattyChat *chatty_chat_view_get_chat (ChattyChatView *self); diff --git a/src/chatty-chat.c b/src/chatty-chat.c index e063898..edecec6 100644 --- a/src/chatty-chat.c +++ b/src/chatty-chat.c @@ -101,19 +101,6 @@ chatty_chat_real_get_chat_name (ChattyChat *self) return ""; } -static const char * -chatty_chat_real_get_username (ChattyChat *self) -{ - ChattyChatPrivate *priv = chatty_chat_get_instance_private (self); - - g_assert (CHATTY_IS_CHAT (self)); - - if (priv->user_name) - return priv->user_name; - - return ""; -} - static ChattyAccount * chatty_chat_real_get_account (ChattyChat *self) { @@ -303,6 +290,20 @@ chatty_chat_real_get_name (ChattyItem *item) return ""; } +static const char * +chatty_chat_real_get_username (ChattyItem *item) +{ + ChattyChat *self = (ChattyChat *)item; + ChattyChatPrivate *priv = chatty_chat_get_instance_private (self); + + g_assert (CHATTY_IS_CHAT (self)); + + if (priv->user_name) + return priv->user_name; + + return ""; +} + static void chatty_chat_get_property (GObject *object, guint prop_id, @@ -376,12 +377,12 @@ chatty_chat_class_init (ChattyChatClass *klass) object_class->finalize = chatty_chat_finalize; item_class->get_name = chatty_chat_real_get_name; + item_class->get_username = chatty_chat_real_get_username; klass->set_data = chatty_chat_real_set_data; klass->is_im = chatty_chat_real_is_im; klass->has_file_upload = chatty_chat_real_has_file_upload; klass->get_chat_name = chatty_chat_real_get_chat_name; - klass->get_username = chatty_chat_real_get_username; klass->get_account = chatty_chat_real_get_account; klass->load_past_messages = chatty_chat_real_load_past_messages; klass->is_loading_history = chatty_chat_real_is_loading_history; @@ -520,14 +521,6 @@ chatty_chat_get_chat_name (ChattyChat *self) return CHATTY_CHAT_GET_CLASS (self)->get_chat_name (self); } -const char * -chatty_chat_get_username (ChattyChat *self) -{ - g_return_val_if_fail (CHATTY_IS_CHAT (self), ""); - - return CHATTY_CHAT_GET_CLASS (self)->get_username (self); -} - ChattyAccount * chatty_chat_get_account (ChattyChat *self) { diff --git a/src/chatty-chat.h b/src/chatty-chat.h index dec6024..02895c5 100644 --- a/src/chatty-chat.h +++ b/src/chatty-chat.h @@ -34,7 +34,6 @@ struct _ChattyChatClass gboolean (*is_im) (ChattyChat *self); gboolean (*has_file_upload) (ChattyChat *self); const char *(*get_chat_name) (ChattyChat *self); - const char *(*get_username) (ChattyChat *self); ChattyAccount *(*get_account) (ChattyChat *self); GListModel *(*get_messages) (ChattyChat *self); GListModel *(*get_users) (ChattyChat *self); @@ -89,7 +88,6 @@ void chatty_chat_set_data (ChattyChat *self, gboolean chatty_chat_is_im (ChattyChat *self); gboolean chatty_chat_has_file_upload (ChattyChat *self); const char *chatty_chat_get_chat_name (ChattyChat *self); -const char *chatty_chat_get_username (ChattyChat *self); ChattyAccount *chatty_chat_get_account (ChattyChat *self); GListModel *chatty_chat_get_messages (ChattyChat *self); void chatty_chat_load_past_messages (ChattyChat *self, diff --git a/src/chatty-contact-provider.c b/src/chatty-contact-provider.c index bef92a2..5923a04 100644 --- a/src/chatty-contact-provider.c +++ b/src/chatty-contact-provider.c @@ -115,10 +115,9 @@ chatty_eds_bus_got (GObject *object, } static ChattyContact * -chatty_contact_provider_matches (ChattyEds *self, - const char *needle, - ChattyProtocol protocols, - gboolean match_name) +chatty_contact_provider_get_match (ChattyEds *self, + const char *value, + ChattyProtocol protocols) { GListModel *model; guint n_items; @@ -129,13 +128,13 @@ chatty_contact_provider_matches (ChattyEds *self, for (guint i = 0; i < n_items; i++) { - g_autoptr(ChattyItem) item = NULL; + g_autoptr(ChattyContact) contact = NULL; - item = g_list_model_get_item (model, i); - match = chatty_item_matches (item, needle, protocols, match_name); + contact = g_list_model_get_item (model, i); + match = chatty_contact_is_exact_match (contact, value, protocols); if (match) - return CHATTY_CONTACT (item); + return contact; } return NULL; @@ -191,7 +190,7 @@ chatty_eds_load_contact (ChattyEds *self, if (self->protocols & CHATTY_PROTOCOL_CALL) protocol = CHATTY_PROTOCOL_CALL; else - protocol = CHATTY_PROTOCOL_SMS; + protocol = CHATTY_PROTOCOL_MMS_SMS; } else if (field_id == E_CONTACT_IM_JABBER) { protocol = CHATTY_PROTOCOL_XMPP; } else { @@ -240,7 +239,7 @@ chatty_eds_objects_added_cb (ChattyEds *self, for (GSList *l = (GSList *)objects; l != NULL; l = l->next) { - if (self->protocols & CHATTY_PROTOCOL_SMS || + if (self->protocols & CHATTY_PROTOCOL_MMS_SMS || self->protocols & CHATTY_PROTOCOL_CALL) chatty_eds_load_contact (self, l->data, E_CONTACT_TEL); @@ -641,7 +640,7 @@ chatty_eds_find_by_number (ChattyEds *self, { g_return_val_if_fail (CHATTY_IS_EDS (self), NULL); - return chatty_contact_provider_matches (self, phone_number, CHATTY_PROTOCOL_ANY, FALSE); + return chatty_contact_provider_get_match (self, phone_number, CHATTY_PROTOCOL_ANY); } diff --git a/src/chatty-enums.h b/src/chatty-enums.h index e484357..3124960 100644 --- a/src/chatty-enums.h +++ b/src/chatty-enums.h @@ -48,7 +48,7 @@ typedef enum typedef enum { CHATTY_PROTOCOL_NONE = 0, - CHATTY_PROTOCOL_SMS = 1 << 0, + CHATTY_PROTOCOL_MMS_SMS = 1 << 0, CHATTY_PROTOCOL_MMS = 1 << 1, CHATTY_PROTOCOL_CALL = 1 << 2, CHATTY_PROTOCOL_XMPP = 1 << 3, @@ -148,10 +148,3 @@ typedef enum CHATTY_ITEM_ARCHIVED, CHATTY_ITEM_BLOCKED, } ChattyItemState; - -typedef enum -{ - CHATTY_SMS_RECEIPT_NONE = -1, - CHATTY_SMS_RECEIPT_MM_ACKN = 0, - CHATTY_SMS_RECEIPT_SMSC_ACKN, -} e_sms_receipt_states; diff --git a/src/chatty-history.c b/src/chatty-history.c index 45b19f2..94b708f 100644 --- a/src/chatty-history.c +++ b/src/chatty-history.c @@ -22,6 +22,9 @@ #include "chatty-utils.h" #include "chatty-settings.h" +#include "users/chatty-mm-account.h" +#include "users/chatty-mm-buddy.h" +#include "chatty-mm-chat.h" #include "chatty-history.h" #define STRING(arg) STRING_VALUE(arg) @@ -30,6 +33,11 @@ /* increment when DB changes */ #define HISTORY_VERSION 3 +/* Shouldn't be modified, new values should be appended */ +#define MESSAGE_DIRECTION_OUT -1 +#define MESSAGE_DIRECTION_SYSTEM 0 +#define MESSAGE_DIRECTION_IN 1 + /* Shouldn't be modified, new values should be appended */ #define CHATTY_ID_UNKNOWN_VALUE 0 #define CHATTY_ID_PHONE_VALUE 1 @@ -41,7 +49,7 @@ /* Shouldn't be modified, new values should be appended */ #define PROTOCOL_UNKNOWN 0 -#define PROTOCOL_SMS 1 +#define PROTOCOL_MMS_SMS 1 #define PROTOCOL_MMS 2 #define PROTOCOL_XMPP 3 #define PROTOCOL_MATRIX 4 @@ -148,8 +156,8 @@ static int history_protocol_to_value (ChattyProtocol protocol) { switch (protocol) { - case CHATTY_PROTOCOL_SMS: - return PROTOCOL_SMS; + case CHATTY_PROTOCOL_MMS_SMS: + return PROTOCOL_MMS_SMS; case CHATTY_PROTOCOL_MMS: return PROTOCOL_MMS; @@ -177,7 +185,7 @@ static int history_protocol_to_type_value (ChattyProtocol protocol) { switch (protocol) { - case CHATTY_PROTOCOL_SMS: + case CHATTY_PROTOCOL_MMS_SMS: case CHATTY_PROTOCOL_MMS: case CHATTY_PROTOCOL_TELEGRAM: return CHATTY_ID_PHONE_VALUE; @@ -530,7 +538,7 @@ chatty_history_create_schema (ChattyHistory *self, "SELECT users.id," "CASE " "WHEN users.username='SMS' " - "THEN " STRING (PROTOCOL_SMS) " " + "THEN " STRING (PROTOCOL_MMS_SMS) " " "ELSE " STRING (PROTOCOL_MMS) " " "END " "FROM users;" @@ -638,7 +646,7 @@ insert_or_ignore_user (ChattyHistory *self, if (!who || !*who) return 0; - if (protocol & (CHATTY_PROTOCOL_SMS | CHATTY_PROTOCOL_MMS | CHATTY_PROTOCOL_TELEGRAM)) { + if (protocol & (CHATTY_PROTOCOL_MMS_SMS | CHATTY_PROTOCOL_MMS | CHATTY_PROTOCOL_TELEGRAM)) { char *country; country = g_object_get_data (G_OBJECT (task), "country-code"); @@ -724,6 +732,54 @@ insert_or_ignore_account (ChattyHistory *self, return id; } +static int +history_add_phone_user (ChattyHistory *self, + GTask *task, + const char *username, + const char *alias) +{ + sqlite3_stmt *stmt; + int status, id = 0; + + sqlite3_prepare_v2 (self->db, "INSERT OR IGNORE INTO users(username,alias,type) " + "VALUES(?,?,"STRING(CHATTY_ID_PHONE_VALUE)");", + -1, &stmt, NULL); + history_bind_text (stmt, 1, username, "binding when adding user"); + history_bind_text (stmt, 2, alias, "binding when adding user"); + status = sqlite3_step (stmt); + sqlite3_finalize (stmt); + + if (status != SQLITE_DONE) { + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Couldn't insert into users. errno: %d, desc: %s", + status, sqlite3_errmsg (self->db)); + return 0; + } + + /* We can't use last_row_id as we may ignore the last insert */ + sqlite3_prepare_v2 (self->db, + "SELECT users.id FROM users " + "WHERE users.username=? AND type="STRING(CHATTY_ID_PHONE_VALUE)";", + -1, &stmt, NULL); + history_bind_text (stmt, 1, username, "binding when getting phone user"); + status = sqlite3_step (stmt); + + if (status == SQLITE_ROW) + id = sqlite3_column_int (stmt, 0); + sqlite3_finalize (stmt); + + if (status != SQLITE_ROW) + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Couldn't get user. errno: %d, desc: %s", + status, sqlite3_errmsg (self->db)); + + return id; +} + static int get_thread_id (ChattyHistory *self, ChattyChat *chat) @@ -739,7 +795,7 @@ get_thread_id (ChattyHistory *self, "ON users.username=? AND accounts.user_id=users.id " "AND threads.name=? AND threads.type=?;", -1, &stmt, NULL); - history_bind_text (stmt, 1, chatty_chat_get_username (chat), "binding when getting thread"); + history_bind_text (stmt, 1, chatty_item_get_username (CHATTY_ITEM (chat)), "binding when getting thread"); history_bind_text (stmt, 2, chatty_chat_get_chat_name (chat), "binding when getting thread"); history_bind_int (stmt, 3, chatty_chat_is_im (chat) ? THREAD_DIRECT_CHAT : THREAD_GROUP_CHAT, "binding when adding phone number"); @@ -769,13 +825,13 @@ insert_or_ignore_thread (ChattyHistory *self, user_id = insert_or_ignore_user (self, chatty_item_get_protocols (CHATTY_ITEM (chat)), - chatty_chat_get_username (chat), + chatty_item_get_username (CHATTY_ITEM (chat)), NULL, task); if (!user_id) { const char *who; - who = chatty_chat_get_username (chat); + who = chatty_item_get_username (CHATTY_ITEM (chat)); if (!who || !*who) g_task_return_new_error (task, @@ -832,6 +888,35 @@ insert_or_ignore_thread (ChattyHistory *self, id = sqlite3_column_int (stmt, 0); sqlite3_finalize (stmt); + if (status == SQLITE_ROW && + CHATTY_IS_MM_CHAT (chat)) { + GListModel *buddies; + guint n_items; + + buddies = chatty_chat_get_users (chat); + n_items = g_list_model_get_n_items (buddies); + + for (guint i = 0; i < n_items; i++) { + g_autoptr(ChattyMmBuddy) buddy = NULL; + const char *number, *name; + + buddy = g_list_model_get_item (buddies, i); + name = chatty_item_get_name (CHATTY_ITEM (buddy)); + number = chatty_mm_buddy_get_number (buddy); + user_id = history_add_phone_user (self, task, number, name); + + sqlite3_prepare_v2 (self->db, + "INSERT OR IGNORE INTO thread_members(thread_id,user_id) " + "VALUES(?,?);", + -1, &stmt, NULL); + history_bind_int (stmt, 1, id, "binding when adding phone number"); + history_bind_int (stmt, 2, user_id, "binding when adding phone number"); + + sqlite3_step (stmt); + sqlite3_finalize (stmt); + } + } + if (status != SQLITE_ROW) g_task_return_new_error (task, G_IO_ERROR, @@ -863,33 +948,6 @@ chatty_history_backup (ChattyHistory *self) g_critical ("Error creating DB backup: %s", error->message); } -static gboolean -history_add_phone_user (ChattyHistory *self, - GTask *task, - const char *username, - const char *alias) -{ - sqlite3_stmt *stmt; - int status; - - sqlite3_prepare_v2 (self->db, "INSERT OR IGNORE INTO users(username,alias,type) " - "VALUES(?,?,"STRING(CHATTY_ID_PHONE_VALUE)");", - -1, &stmt, NULL); - history_bind_text (stmt, 1, username, "binding when adding user"); - history_bind_text (stmt, 2, alias, "binding when adding user"); - status = sqlite3_step (stmt); - sqlite3_finalize (stmt); - - if (status != SQLITE_DONE) - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "Couldn't insert into accounts. errno: %d, desc: %s", - status, sqlite3_errmsg (self->db)); - - return status == SQLITE_DONE; -} - static gboolean history_add_phone_account (ChattyHistory *self, GTask *task, @@ -1047,7 +1105,7 @@ chatty_history_migrate_db_to_v1_to_v3 (ChattyHistory *self, /*** Accounts ***/ /* We have exactly one account for SMS */ "INSERT OR IGNORE INTO accounts(user_id,protocol,enabled) " - "SELECT DISTINCT users.id," STRING(PROTOCOL_SMS) ",1 FROM users " + "SELECT DISTINCT users.id," STRING(PROTOCOL_MMS_SMS) ",1 FROM users " "WHERE users.username='SMS';" /* XMPP IM accounts */ @@ -1274,7 +1332,7 @@ chatty_history_migrate_db_to_v1_to_v3 (ChattyHistory *self, /* Fill in accounts */ if (!history_add_phone_account (self, task, account_number ? account_number : account, - g_strcmp0 (account, "SMS") == 0 ? PROTOCOL_SMS : PROTOCOL_TELEGRAM)) + g_strcmp0 (account, "SMS") == 0 ? PROTOCOL_MMS_SMS : PROTOCOL_TELEGRAM)) return FALSE; if (!history_add_phone_user (self, task, @@ -1360,7 +1418,7 @@ chatty_history_migrate_db_to_v1_to_v3 (ChattyHistory *self, /* Fill in accounts */ if (!history_add_phone_account (self, task, account_number ? account_number : account, - g_strcmp0 (account, "SMS") == 0 ? PROTOCOL_SMS : PROTOCOL_TELEGRAM)) + g_strcmp0 (account, "SMS") == 0 ? PROTOCOL_MMS_SMS : PROTOCOL_TELEGRAM)) return FALSE; if (sender && @@ -2017,7 +2075,7 @@ history_get_messages (ChattyHistory *self, if (!thread_id) { g_task_return_new_error (task, - G_IO_ERROR, G_IO_ERROR_FAILED, + G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Couldn't find chat %s", chatty_chat_get_chat_name (chat)); return; @@ -2167,7 +2225,7 @@ history_add_message (ChattyHistory *self, alias = chatty_message_get_user_alias (message); if (direction == CHATTY_DIRECTION_OUT) - who = chatty_chat_get_username (chat); + who = chatty_item_get_username (CHATTY_ITEM (chat)); if ((!who || !*who) && direction == CHATTY_DIRECTION_IN && chatty_chat_is_im (chat)) who = chatty_chat_get_chat_name (chat); @@ -2244,6 +2302,42 @@ history_add_message (ChattyHistory *self, status, sqlite3_errmsg (self->db)); } +static GPtrArray * +get_sms_thread_members (ChattyHistory *self, + int thread_id) +{ + GPtrArray *members = NULL; + sqlite3_stmt *stmt; + + g_assert (CHATTY_IS_HISTORY (self)); + g_assert (g_thread_self () == self->worker_thread); + + sqlite3_prepare_v2 (self->db, "SELECT username,alias FROM users " + "INNER JOIN thread_members " + "ON thread_id=? AND user_id=users.id " + "WHERE users.username != 'SMS' AND users.username != 'MMS'", + -1, &stmt, NULL); + history_bind_int (stmt, 1, thread_id, "binding when getting thread members"); + + while (sqlite3_step (stmt) == SQLITE_ROW) { + ChattyMmBuddy *buddy; + const char *name, *alias; + + if (!members) + members = g_ptr_array_new_full (30, g_object_unref); + + name = (const char *)sqlite3_column_text (stmt, 0); + alias = (const char *)sqlite3_column_text (stmt, 1); + + buddy = chatty_mm_buddy_new (name, alias); + g_ptr_array_insert (members, 0, buddy); + } + + sqlite3_finalize (stmt); + + return members; +} + static void history_get_chats (ChattyHistory *self, GTask *task) @@ -2267,10 +2361,14 @@ history_get_chats (ChattyHistory *self, account = g_object_get_data (G_OBJECT (task), "account"); /* We currently handle only matrix accounts */ - g_assert (CHATTY_IS_MA_ACCOUNT (account)); + g_assert (CHATTY_IS_MA_ACCOUNT (account) || CHATTY_IS_MM_ACCOUNT (account)); - user_id = chatty_account_get_username (account); - protocol = PROTOCOL_MATRIX; + user_id = chatty_item_get_username (CHATTY_ITEM (account)); + + if (CHATTY_IS_MA_ACCOUNT (account)) + protocol = PROTOCOL_MATRIX; + else + protocol = PROTOCOL_MMS_SMS; sqlite3_prepare_v2 (self->db, "SELECT threads.id,threads.name,threads.alias,threads.encrypted," @@ -2306,12 +2404,28 @@ history_get_chats (ChattyHistory *self, file->path = g_strdup ((const char *)sqlite3_column_text (stmt, 5)); } - chat = (gpointer)chatty_ma_chat_new (name, alias, file); - chatty_chat_set_encryption (CHATTY_CHAT (chat), encrypted); + if (CHATTY_IS_MA_ACCOUNT (account)) { + chat = (gpointer)chatty_ma_chat_new (name, alias, file); + chatty_chat_set_encryption (CHATTY_CHAT (chat), encrypted); + } else { + chat = (gpointer)chatty_mm_chat_new (name, alias, CHATTY_PROTOCOL_MMS_SMS, TRUE); + } + messages = get_messages_before_time (self, chat, NULL, thread_id, INT_MAX, 1); - chatty_ma_chat_add_messages (CHATTY_MA_CHAT (chat), messages); + + if (CHATTY_IS_MA_ACCOUNT (account)) + chatty_ma_chat_add_messages (CHATTY_MA_CHAT (chat), messages); + else + chatty_mm_chat_prepend_messages (CHATTY_MM_CHAT (chat), messages); g_ptr_array_insert (threads, -1, chat); + + if (protocol == PROTOCOL_MMS_SMS) { + g_autoptr(GPtrArray) members = NULL; + + members = get_sms_thread_members (self, thread_id); + chatty_mm_chat_add_users (CHATTY_MM_CHAT (chat), members); + } } sqlite3_finalize (stmt); @@ -2367,7 +2481,7 @@ history_update_user (ChattyHistory *self, account = g_object_get_data (G_OBJECT (task), "account"); g_assert (CHATTY_IS_ACCOUNT (account)); - user_name = chatty_account_get_username (account); + user_name = chatty_item_get_username (CHATTY_ITEM (account)); protocol = chatty_item_get_protocols (CHATTY_ITEM (account)); name = chatty_item_get_name (CHATTY_ITEM (account)); @@ -2435,7 +2549,7 @@ history_delete_chat (ChattyHistory *self, return; } - account = chatty_chat_get_username (chat); + account = chatty_item_get_username (CHATTY_ITEM (chat)); status = sqlite3_prepare_v2 (self->db, "DELETE FROM threads " @@ -2487,7 +2601,7 @@ history_load_account (ChattyHistory *self, account = g_object_get_data (G_OBJECT (task), "account"); g_assert (CHATTY_IS_ACCOUNT (account)); - user_name = chatty_account_get_username (account); + user_name = chatty_item_get_username (CHATTY_ITEM (account)); if (!user_name || !*user_name) { g_task_return_new_error (task, @@ -3018,12 +3132,15 @@ chatty_history_get_chats_async (ChattyHistory *self, gpointer user_data) { GTask *task; + const char *protocol; g_return_if_fail (CHATTY_IS_HISTORY (self)); g_return_if_fail (CHATTY_IS_ACCOUNT (account)); - /* Currently we handle only matrix accounts */ - if (!g_str_equal (chatty_account_get_protocol_name (account), "Matrix")) + /* Currently we handle only matrix and SMS accounts */ + protocol = chatty_account_get_protocol_name (account); + if (!g_str_equal (protocol, "Matrix") && + !g_str_equal (protocol, "SMS")) g_return_if_reached (); task = g_task_new (self, NULL, callback, user_data); diff --git a/src/chatty-image-item.c b/src/chatty-image-item.c index 3d76759..767baa7 100644 --- a/src/chatty-image-item.c +++ b/src/chatty-image-item.c @@ -56,8 +56,12 @@ item_set_image (gpointer user_data) scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self)); sc = gtk_widget_get_style_context (self->image); - if (file->path) - path = g_build_filename (g_get_user_cache_dir (), "chatty", file->path, NULL); + if (file->path) { + if (self->protocol == CHATTY_PROTOCOL_MMS_SMS || self->protocol == CHATTY_PROTOCOL_MMS) + path = g_build_filename (g_get_user_data_dir (), "chatty", file->path, NULL); + else + path = g_build_filename (g_get_user_cache_dir (), "chatty", file->path, NULL); + } if (path) pixbuf = gdk_pixbuf_new_from_file_at_scale (path, 240 * scale_factor, diff --git a/src/chatty-list-row.c b/src/chatty-list-row.c index 378ffce..1e0d5df 100644 --- a/src/chatty-list-row.c +++ b/src/chatty-list-row.c @@ -281,7 +281,7 @@ chatty_list_row_update (ChattyListRow *self) ChattyAccount *account; account = chatty_pp_buddy_get_account (CHATTY_PP_BUDDY (self->item)); - subtitle = chatty_account_get_username (account); + subtitle = chatty_item_get_username (CHATTY_ITEM (account)); } else { /* Buddy in chat list */ g_autofree char *markup = NULL; ChattyUserFlag flag; @@ -295,7 +295,7 @@ chatty_list_row_update (ChattyListRow *self) g_autofree gchar *type = NULL; const gchar *number; - number = chatty_contact_get_value (CHATTY_CONTACT (self->item)); + number = chatty_item_get_username (self->item); if (chatty_contact_is_dummy (CHATTY_CONTACT (self->item))) type = g_strdup (number); diff --git a/src/chatty-log.c b/src/chatty-log.c index bea9d88..b7ad2c8 100644 --- a/src/chatty-log.c +++ b/src/chatty-log.c @@ -10,18 +10,89 @@ */ #include +#include #include #include #include "chatty-log.h" -#define DEFAULT_DOMAIN "chatty" +#define DEFAULT_DOMAIN_PREFIX "chatty" char *domains; static int verbosity; gboolean any_domain; +gboolean no_anonymize; gboolean stderr_is_journal; +static gboolean +should_log (const char *log_domain, + GLogLevelFlags log_level) +{ + /* Ignore custom flags set */ + log_level = log_level & ~CHATTY_LOG_DETAILED; + + /* Don't skip serious logs */ + if (log_level & (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_WARNING)) + return TRUE; + + if (any_domain && domains) { + /* If domain is “all” show logs upto debug regardless of the verbosity */ + if (log_level & ~CHATTY_LOG_LEVEL_TRACE) + return TRUE; + + /* If the log is trace level, log if verbosity >= 4 */ + return verbosity >= 4; + } + + if (!any_domain && domains && log_domain && strstr (domains, log_domain)) + return TRUE; + + switch ((int)log_level) + { + case G_LOG_LEVEL_MESSAGE: + if (verbosity < 1) + return FALSE; + break; + + case G_LOG_LEVEL_INFO: + if (verbosity < 2) + return FALSE; + break; + + case G_LOG_LEVEL_DEBUG: + if (verbosity < 3) + return FALSE; + break; + + case CHATTY_LOG_LEVEL_TRACE: + if (verbosity < 4) + return FALSE; + break; + + default: + break; + } + + if (!log_domain) + log_domain = "**"; + + /* Skip logs from other domains if verbosity level is low */ + if (any_domain && !domains && + verbosity < 5 && + log_level > G_LOG_LEVEL_MESSAGE && + !strstr (log_domain, DEFAULT_DOMAIN_PREFIX)) + return FALSE; + + /* GdkPixbuf logs are too much verbose, skip unless asked not to. */ + if (log_level >= G_LOG_LEVEL_MESSAGE && + verbosity < 7 && + g_strcmp0 (log_domain, "GdkPixbuf") == 0 && + (!domains || !strstr (domains, log_domain))) + return FALSE; + + return TRUE; +} + static void log_str_append_log_domain (GString *log_str, const char *log_domain, @@ -57,6 +128,9 @@ static const char * get_log_level_prefix (GLogLevelFlags log_level, gboolean use_color) { + /* Ignore custom flags set */ + log_level = log_level & ~CHATTY_LOG_DETAILED; + if (use_color) { switch ((int)log_level) /* Same colors as used in GLib */ @@ -132,6 +206,33 @@ chatty_log_write (GLogLevelFlags log_level, g_string_append_printf (log_str, "[%5d]:", getpid ()); g_string_append_printf (log_str, "%s: ", get_log_level_prefix (log_level, can_color)); + + if (log_level & CHATTY_LOG_DETAILED) + { + const char *code_func = NULL, *code_line = NULL; + for (guint i = 0; i < n_fields; i++) + { + const GLogField *field = &fields[i]; + + if (!code_func && g_strcmp0 (field->key, "CODE_FUNC") == 0) + code_func = field->value; + else if (!code_line && g_strcmp0 (field->key, "CODE_LINE") == 0) + code_line = field->value; + + if (code_func && code_line) + break; + } + + if (code_func) + { + g_string_append_printf (log_str, "%s():", code_func); + + if (code_line) + g_string_append_printf (log_str, "%s:", code_line); + g_string_append_c (log_str, ' '); + } + } + g_string_append (log_str, log_message); fprintf (stream, "%s\n", log_str->str); @@ -149,39 +250,6 @@ chatty_log_handler (GLogLevelFlags log_level, const char *log_domain = NULL; const char *log_message = NULL; - /* If domain is “all” show logs upto debug regardless of the verbosity */ - switch ((int)log_level) - { - case G_LOG_LEVEL_MESSAGE: - if (any_domain && domains) - break; - if (verbosity < 1) - return G_LOG_WRITER_HANDLED; - break; - - case G_LOG_LEVEL_INFO: - if (any_domain && domains) - break; - if (verbosity < 2) - return G_LOG_WRITER_HANDLED; - break; - - case G_LOG_LEVEL_DEBUG: - if (any_domain && domains) - break; - if (verbosity < 3) - return G_LOG_WRITER_HANDLED; - break; - - case CHATTY_LOG_LEVEL_TRACE: - if (verbosity < 4) - return G_LOG_WRITER_HANDLED; - break; - - default: - break; - } - for (guint i = 0; (!log_domain || !log_message) && i < n_fields; i++) { const GLogField *field = &fields[i]; @@ -192,22 +260,11 @@ chatty_log_handler (GLogLevelFlags log_level, log_message = field->value; } - if (!log_domain) - log_domain = "**"; - - /* Skip logs from other domains if verbosity level is low */ - if (any_domain && !domains && - verbosity < 5 && - log_level > G_LOG_LEVEL_MESSAGE && - !strstr (log_domain, DEFAULT_DOMAIN)) + if (!should_log (log_domain, log_level)) return G_LOG_WRITER_HANDLED; - /* GdkPixbuf logs are too much verbose, skip unless asked not to. */ - if (log_level >= G_LOG_LEVEL_MESSAGE && - verbosity < 7 && - g_strcmp0 (log_domain, "GdkPixbuf") == 0 && - (!domains || !strstr (domains, log_domain))) - return G_LOG_WRITER_HANDLED; + if (!log_domain) + log_domain = "**"; if (!log_message) log_message = "(NULL) message"; @@ -244,6 +301,13 @@ chatty_log_init (void) if (!domains || g_str_equal (domains, "all")) any_domain = TRUE; + if (domains && g_str_equal (domains, "no-anonymize")) + { + any_domain = TRUE; + no_anonymize = TRUE; + g_clear_pointer (&domains, g_free); + } + stderr_is_journal = g_log_writer_is_journald (fileno (stderr)); g_log_set_writer_func (chatty_log_handler, NULL, NULL); g_once_init_leave (&initialized, 1); @@ -262,3 +326,80 @@ chatty_log_get_verbosity (void) { return verbosity; } + +void +chatty_log (const char *domain, + GLogLevelFlags log_level, + const char *value, + const char *file, + const char *line, + const char *func, + const char *message_format, + ...) +{ + g_autoptr(GString) str = NULL; + va_list args; + + if (!message_format || !*message_format) + return; + + if (!should_log (domain, log_level)) + return; + + str = g_string_new (NULL); + va_start (args, message_format); + g_string_append_vprintf (str, message_format, args); + va_end (args); + + chatty_log_anonymize_value (str, value); + g_log_structured (domain, log_level, + "CODE_FILE", file, + "CODE_LINE", line, + "CODE_FUNC", func, + "MESSAGE", "%s", str->str); +} + +void +chatty_log_anonymize_value (GString *str, + const char *value) +{ + if (!value || !*value) + return; + + g_assert (str); + + if (no_anonymize) + { + g_string_append (str, value); + return; + } + + g_string_append_c (str, ' '); + + if (!isalnum (*value)) + g_string_append_c (str, *value++); + + if (*value) + g_string_append_c (str, *value++); + + if (*value) + g_string_append_c (str, *value++); + + if (!*value) + return; + + /* Replace all but the last two alnum chars with 'x' */ + while (*value) + { + while (isalnum (*value) && value[1] && value[2]) + { + if (value[1] == ':' || value[1] == '@' || value[1] == ' ' || + value[-1] == ':' || value[-1] == '@' || value[-1] == ' ') + g_string_append_c (str, *value); + else + g_string_append_c (str, '#'); + value++; + } + g_string_append_c (str, *value++); + } +} diff --git a/src/chatty-log.h b/src/chatty-log.h index 3340710..76e5ac8 100644 --- a/src/chatty-log.h +++ b/src/chatty-log.h @@ -1,5 +1,5 @@ /* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- */ -/* chatty-log.c +/* chatty-log.h * * Copyright 2021 Purism SPC * @@ -11,53 +11,49 @@ #pragma once +#include + #ifndef CHATTY_LOG_LEVEL_TRACE # define CHATTY_LOG_LEVEL_TRACE ((GLogLevelFlags)(1 << G_LOG_LEVEL_USER_SHIFT)) +# define CHATTY_LOG_DETAILED ((GLogLevelFlags)(1 << (G_LOG_LEVEL_USER_SHIFT + 1))) #endif -/* XXX: Should we use the semi-private g_log_structured_standard() API? */ #define CHATTY_TRACE_MSG(fmt, ...) \ - g_log_structured (G_LOG_DOMAIN, CHATTY_LOG_LEVEL_TRACE, \ - "MESSAGE", "%s():%d: " fmt, \ - G_STRFUNC, __LINE__, ##__VA_ARGS__) + chatty_log (G_LOG_DOMAIN, \ + CHATTY_LOG_LEVEL_TRACE | CHATTY_LOG_DETAILED, \ + NULL, __FILE__, G_STRINGIFY (__LINE__), \ + G_STRFUNC, fmt, ##__VA_ARGS__) +#define CHATTY_TRACE(value, fmt, ...) \ + chatty_log (G_LOG_DOMAIN, \ + CHATTY_LOG_LEVEL_TRACE | CHATTY_LOG_DETAILED, \ + value, __FILE__, G_STRINGIFY (__LINE__), \ + G_STRFUNC, fmt, ##__VA_ARGS__) #define CHATTY_DEBUG_MSG(fmt, ...) \ - g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, \ - "MESSAGE", "%s():%d: " fmt, \ - G_STRFUNC, __LINE__, ##__VA_ARGS__) -#define CHATTY_PROBE \ - g_log_strucutured (G_LOG_DOMAIN, CHATTY_LOG_LEVEL_TRACE, \ - "MESSAGE", "PROBE: %s():%d", \ - G_STRFUNC, __LINE__) -#define CHATTY_TODO(_msg) \ - g_log_structured (G_LOG_DOMAIN, CHATTY_LOG_LEVEL_TRACE, \ - "MESSAGE", " TODO: %s():%d: %s", \ - G_STRFUNC, __LINE__, _msg) -#define CHATTY_ENTRY \ - g_log_structured (G_LOG_DOMAIN, CHATTY_LOG_LEVEL_TRACE, \ - "MESSAGE", "ENTRY: %s():%d", \ - G_STRFUNC, __LINE__) -#define CHATTY_EXIT \ - G_STMT_START { \ - g_log_structured (G_LOG_DOMAIN, CHATTY_LOG_LEVEL_TRACE, \ - "MESSAGE", " EXIT: %s():%d", \ - G_STRFUNC, __LINE__); \ - return; \ - } G_STMT_END -#define CHATTY_GOTO(_l) \ - G_STMT_START { \ - g_log_structured (G_LOG_DOMAIN, CHATTY_LOG_LEVEL_TRACE, \ - "MESSAGE", " GOTO: %s():%d ("#_l ")", \ - G_STRFUNC, __LINE__); \ - goto _l; \ - } G_STMT_END -#define CHATTY_RETURN(_r) \ - G_STMT_START { \ - g_log_structured (G_LOG_DOMAIN, CHATTY_LOG_LEVEL_TRACE, \ - "MESSAGE", " EXIT: %s():%d ", \ - G_STRFUNC, __LINE__); \ - return _r; \ - } G_STMT_END + chatty_log (G_LOG_DOMAIN, \ + G_LOG_LEVEL_DEBUG | CHATTY_LOG_DETAILED, \ + NULL, __FILE__, G_STRINGIFY (__LINE__), \ + G_STRFUNC, fmt, ##__VA_ARGS__) +#define CHATTY_DEBUG(value, fmt, ...) \ + chatty_log (G_LOG_DOMAIN, \ + G_LOG_LEVEL_DEBUG | CHATTY_LOG_DETAILED, \ + value, __FILE__, G_STRINGIFY (__LINE__), \ + G_STRFUNC, fmt, ##__VA_ARGS__) +#define CHATTY_WARNING(value, fmt, ...) \ + chatty_log (G_LOG_DOMAIN, \ + G_LOG_LEVEL_WARNING | CHATTY_LOG_DETAILED, \ + value, __FILE__, G_STRINGIFY (__LINE__), \ + G_STRFUNC, fmt, ##__VA_ARGS__) void chatty_log_init (void); void chatty_log_increase_verbosity (void); int chatty_log_get_verbosity (void); +void chatty_log (const char *domain, + GLogLevelFlags log_level, + const char *value, + const char *file, + const char *line, + const char *func, + const char *message_format, + ...) G_GNUC_PRINTF (7, 8); +void chatty_log_anonymize_value (GString *str, + const char *value); diff --git a/src/chatty-manager.c b/src/chatty-manager.c index 0406fc0..509e761 100644 --- a/src/chatty-manager.c +++ b/src/chatty-manager.c @@ -28,18 +28,21 @@ #include "chatty-application.h" #include "chatty-window.h" #include "chatty-chat-view.h" +#include "users/chatty-mm-account.h" #include "users/chatty-pp-account.h" #include "matrix/chatty-ma-account.h" #include "matrix/chatty-ma-chat.h" #include "matrix/matrix-db.h" #include "chatty-secret-store.h" #include "chatty-chat.h" +#include "chatty-mm-chat.h" #include "chatty-pp-chat.h" #include "chatty-notification.h" #include "chatty-purple-request.h" #include "chatty-purple-notify.h" #include "chatty-history.h" #include "chatty-manager.h" +#include "chatty-log.h" /** * SECTION: chatty-manager @@ -76,11 +79,12 @@ struct _ChattyManager PurplePlugin *file_upload_plugin; MatrixDb *matrix_db; + /* We have exactly one MM account */ + ChattyMmAccount *mm_account; gboolean disable_auto_login; gboolean network_available; - gboolean has_modem; ChattyProtocol active_protocols; }; @@ -200,11 +204,22 @@ manager_load_messages_cb (GObject *object, } } - } else if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + } else if (error && + !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && + !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { g_warning ("Error fetching messages: %s,", error->message); } } +static gboolean +manager_mm_set_eds (gpointer user_data) +{ + g_autoptr(ChattyManager) self = user_data; + + chatty_mm_account_set_eds (self->mm_account, self->chatty_eds); + + return G_SOURCE_REMOVE; +} static void manager_eds_is_ready (ChattyManager *self) @@ -227,7 +242,7 @@ manager_eds_is_ready (ChattyManager *self) account = g_list_model_get_item (accounts, i); protocol = chatty_item_get_protocols (CHATTY_ITEM (account)); - if (protocol != CHATTY_PROTOCOL_SMS) + if (protocol != CHATTY_PROTOCOL_MMS_SMS) continue; model = chatty_account_get_buddies (account); @@ -236,7 +251,7 @@ manager_eds_is_ready (ChattyManager *self) g_autoptr(ChattyPpBuddy) buddy = NULL; buddy = g_list_model_get_item (model, j); - id = chatty_pp_buddy_get_id (buddy); + id = chatty_item_get_username (CHATTY_ITEM (buddy)); if (chatty_pp_buddy_get_contact (buddy)) continue; @@ -246,6 +261,9 @@ manager_eds_is_ready (ChattyManager *self) chatty_pp_buddy_set_contact (buddy, contact); } } + + /* Set eds after some timeout so that most contacts are loaded */ + g_timeout_add (200, manager_mm_set_eds, g_object_ref (self)); } typedef struct _PurpleGLibIOClosure @@ -339,8 +357,6 @@ PurpleEventLoopUiOps eventloop_ui_ops = static void chatty_purple_quit (void) { - chatty_chat_view_purple_uninit (); - purple_conversations_set_ui_ops (NULL); purple_connections_set_ui_ops (NULL); purple_blist_set_ui_ops (NULL); @@ -356,7 +372,6 @@ chatty_purple_quit (void) static void chatty_purple_ui_init (void) { - chatty_chat_view_purple_init (); chatty_manager_purple_init (chatty_manager_get_default ()); } @@ -599,7 +614,6 @@ chatty_conv_new (PurpleConversation *conv) PurpleValue *value; PurpleBlistNode *conv_node; const gchar *conv_name; - const gchar *folks_name; PurpleConversationType conv_type = purple_conversation_get_type (conv); @@ -639,25 +653,6 @@ chatty_conv_new (PurpleConversation *conv) conv_name = purple_conversation_get_name (conv); buddy = purple_find_buddy (account, conv_name); - if (chatty_item_get_protocols (CHATTY_ITEM (chat)) == CHATTY_PROTOCOL_SMS) { - if (buddy == NULL) { - ChattyEds *chatty_eds; - ChattyContact *contact; - - chatty_eds = chatty_manager_get_eds (chatty_manager_get_default ()); - contact = chatty_eds_find_by_number (chatty_eds, conv_name); - - if (contact) { - folks_name = chatty_item_get_name (CHATTY_ITEM (contact)); - - buddy = purple_buddy_new (account, conv_name, folks_name); - purple_blist_node_set_bool (PURPLE_BLIST_NODE (buddy), "chatty-notifications", TRUE); - - purple_blist_add_buddy (buddy, NULL, NULL, NULL); - } - } - } - if (buddy == NULL) { buddy = purple_buddy_new (account, conv_name, NULL); purple_blist_add_buddy (buddy, NULL, NULL, NULL); @@ -737,7 +732,6 @@ chatty_conv_write_conversation (PurpleConversation *conv, conv, NULL}; ChattyProtocol protocol; - ChattyMsgType msg_type; if ((flags & PURPLE_MESSAGE_SYSTEM) && !(flags & PURPLE_MESSAGE_NOTIFY)) { flags &= ~(PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV); @@ -748,11 +742,6 @@ chatty_conv_write_conversation (PurpleConversation *conv, self = chatty_manager_get_default (); protocol = chatty_item_get_protocols (CHATTY_ITEM (chat)); - if (protocol == CHATTY_PROTOCOL_SMS) - msg_type = CHATTY_MESSAGE_TEXT; - else - msg_type = CHATTY_MESSAGE_HTML_ESCAPED; - account = purple_conversation_get_account (conv); g_return_if_fail (account != NULL); gc = purple_account_get_connection (account); @@ -799,8 +788,8 @@ chatty_conv_write_conversation (PurpleConversation *conv, // If anyone wants to suppress archiving - feel free to set NO_LOG flag purple_signal_emit (chatty_manager_get_default (), "conversation-write", conv, &pcm, &uuid, type); - g_debug("Posting message id:%s flags:%d type:%d from:%s", - uuid, pcm.flags, type, pcm.who); + CHATTY_DEBUG (pcm.who, "Posting message id:%s flags:%d type:%d from:", + uuid, pcm.flags, type); if (!uuid) uuid = g_uuid_string_random (); @@ -809,7 +798,8 @@ chatty_conv_write_conversation (PurpleConversation *conv, if (pcm.flags & (PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_ERROR)) { // System is usually also RECV so should be first to catch - chat_message = chatty_message_new (NULL, message, uuid, 0, msg_type, CHATTY_DIRECTION_SYSTEM, 0); + chat_message = chatty_message_new (NULL, message, uuid, 0, CHATTY_MESSAGE_HTML_ESCAPED, + CHATTY_DIRECTION_SYSTEM, 0); chatty_pp_chat_append_message (CHATTY_PP_CHAT (chat), chat_message); } else if (pcm.flags & PURPLE_MESSAGE_RECV) { g_autoptr(ChattyContact) contact = NULL; @@ -820,7 +810,8 @@ chatty_conv_write_conversation (PurpleConversation *conv, chatty_contact_set_name (contact, pcm.who); chatty_contact_set_value (contact, pcm.who); - chat_message = chatty_message_new (CHATTY_ITEM (contact), message, uuid, mtime, msg_type, CHATTY_DIRECTION_IN, 0); + chat_message = chatty_message_new (CHATTY_ITEM (contact), message, uuid, mtime, + CHATTY_MESSAGE_HTML_ESCAPED, CHATTY_DIRECTION_IN, 0); chatty_pp_chat_append_message (CHATTY_PP_CHAT (chat), chat_message); if (buddy && purple_blist_node_get_bool (node, "chatty-notifications") && @@ -831,7 +822,8 @@ chatty_conv_write_conversation (PurpleConversation *conv, } } else if (flags & PURPLE_MESSAGE_SEND && pcm.flags & PURPLE_MESSAGE_SEND) { // normal send - chat_message = chatty_message_new (NULL, message, uuid, 0, msg_type, CHATTY_DIRECTION_OUT, 0); + chat_message = chatty_message_new (NULL, message, uuid, 0, CHATTY_MESSAGE_HTML_ESCAPED, + CHATTY_DIRECTION_OUT, 0); chatty_message_set_status (chat_message, CHATTY_STATUS_SENT, 0); chatty_pp_chat_append_message (CHATTY_PP_CHAT (chat), chat_message); } else if (pcm.flags & PURPLE_MESSAGE_SEND) { @@ -839,7 +831,8 @@ chatty_conv_write_conversation (PurpleConversation *conv, // FIXME: current list_box does not allow ordering rows by timestamp // TODO: Needs proper sort function and timestamp as user_data for rows // FIXME: Alternatively may need to reload history to re-populate rows - chat_message = chatty_message_new (NULL, message, uuid, mtime, msg_type, CHATTY_DIRECTION_OUT, 0); + chat_message = chatty_message_new (NULL, message, uuid, mtime, CHATTY_MESSAGE_HTML_ESCAPED, + CHATTY_DIRECTION_OUT, 0); chatty_message_set_status (chat_message, CHATTY_STATUS_SENT, 0); chatty_pp_chat_append_message (CHATTY_PP_CHAT (chat), chat_message); } @@ -976,18 +969,6 @@ manager_message_carbons_changed (ChattyManager *self, chatty_manager_unload_plugin (self->carbon_plugin); } -static void -chatty_manager_enable_sms_account (ChattyManager *self) -{ - g_autoptr(ChattyPpAccount) account = NULL; - - if (purple_accounts_find ("SMS", "prpl-mm-sms")) - return; - - account = chatty_pp_account_new (CHATTY_PROTOCOL_SMS, "SMS", NULL, FALSE); - chatty_account_save (CHATTY_ACCOUNT (account)); -} - static ChattyPpBuddy * manager_find_buddy (GListModel *model, PurpleBuddy *pp_buddy) @@ -1024,6 +1005,9 @@ manager_buddy_added_cb (PurpleBuddy *pp_buddy, g_assert (CHATTY_IS_MANAGER (self)); pp_account = purple_buddy_get_account (pp_buddy); + if (g_strcmp0 (purple_account_get_protocol_id (pp_account), "prpl-mm-sms") == 0) + return; + account = chatty_pp_account_get_object (pp_account); g_return_if_fail (account); @@ -1033,7 +1017,7 @@ manager_buddy_added_cb (PurpleBuddy *pp_buddy, if (!buddy) buddy = chatty_pp_account_add_purple_buddy (account, pp_buddy); - id = chatty_pp_buddy_get_id (buddy); + id = chatty_item_get_username (CHATTY_ITEM (buddy)); contact = chatty_eds_find_by_number (self->chatty_eds, id); chatty_pp_buddy_set_contact (buddy, contact); @@ -1145,6 +1129,11 @@ manager_account_added_cb (PurpleAccount *pp_account, return; } + if (g_strcmp0 (protocol_id, "prpl-mm-sms") == 0) { + purple_account_set_enabled (pp_account, purple_core_get_ui (), FALSE); + return; + } + account = chatty_pp_account_get_object (pp_account); if (account) @@ -1160,9 +1149,6 @@ manager_account_added_cb (PurpleAccount *pp_account, if (self->disable_auto_login) chatty_account_set_enabled (CHATTY_ACCOUNT (account), FALSE); - - if (chatty_item_is_sms (CHATTY_ITEM (account))) - chatty_account_set_enabled (CHATTY_ACCOUNT (account), TRUE); } static void @@ -1211,8 +1197,7 @@ manager_account_connection_failed_cb (PurpleAccount *pp_account, g_return_if_fail (account); if (error == PURPLE_CONNECTION_ERROR_NETWORK_ERROR && - self->network_available && - chatty_item_get_protocols (CHATTY_ITEM (account)) != CHATTY_PROTOCOL_SMS) + self->network_available) chatty_account_connect (CHATTY_ACCOUNT (account), TRUE); if (purple_connection_error_is_fatal (error)) @@ -1375,9 +1360,6 @@ manager_update_protocols (ChattyManager *self) self->active_protocols |= protocol; } - if (self->has_modem) - self->active_protocols |= CHATTY_PROTOCOL_SMS; - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIVE_PROTOCOLS]); } @@ -1395,13 +1377,6 @@ manager_connection_signed_on_cb (PurpleConnection *gc, account = chatty_pp_account_get_object (pp_account); g_return_if_fail (account); - /* - * SMS plugin emits “signed-on” regardless of the true state - * So it’s handled in “mm-sms-state” callback. - */ - if (chatty_item_is_sms (CHATTY_ITEM (account))) - return; - protocol = chatty_item_get_protocols (CHATTY_ITEM (account)); self->active_protocols |= protocol; @@ -1423,13 +1398,6 @@ manager_connection_signed_off_cb (PurpleConnection *gc, account = chatty_pp_account_get_object (pp_account); g_return_if_fail (account); - /* - * SMS plugin emits “signed-off” regardless of the true state - * So it’s handled in “mm-sms-state” callback. - */ - if (chatty_item_is_sms (CHATTY_ITEM (account))) - return; - manager_update_protocols (self); g_object_notify (G_OBJECT (account), "status"); @@ -1448,62 +1416,14 @@ manager_find_chat (GListModel *model, chat = g_list_model_get_item (model, i); - if (chatty_pp_chat_get_purple_chat (CHATTY_PP_CHAT (chat)) == pp_chat) + if (CHATTY_IS_PP_CHAT (chat) && + chatty_pp_chat_get_purple_chat (CHATTY_PP_CHAT (chat)) == pp_chat) return chat; } return NULL; } -static void -manager_sms_modem_added_cb (gint status) -{ - ChattyPpAccount *account; - PurpleAccount *pp_account; - - pp_account = purple_accounts_find ("SMS", "prpl-mm-sms"); - account = chatty_pp_account_get_object (pp_account); - g_return_if_fail (CHATTY_IS_PP_ACCOUNT (account)); - - chatty_account_connect (CHATTY_ACCOUNT (account), TRUE); -} - - -/* XXX: works only with one modem */ -static void -manager_sms_state_changed_cb (int state, - ChattyManager *self) -{ - ChattyProtocol old_protocols; - - g_assert (CHATTY_IS_MANAGER (self)); - - old_protocols = self->active_protocols; - - if (state == PUR_MM_STATE_READY) { - self->has_modem = TRUE; - self->active_protocols |= CHATTY_PROTOCOL_SMS; - } else if (state != PUR_MM_STATE_MANAGER_FOUND && state != PUR_MM_STATE_MODEM_FOUND) { - self->has_modem = FALSE; - self->active_protocols &= ~CHATTY_PROTOCOL_SMS; - } - - if (old_protocols != self->active_protocols) - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIVE_PROTOCOLS]); -} - -static void -manager_sms_get_country_cb (const char *country_code, - ChattyManager *self) -{ - ChattySettings *settings; - - g_assert (CHATTY_IS_MANAGER (self)); - - settings = chatty_settings_get_default (); - chatty_settings_set_country_iso_code (settings, country_code); -} - static void manager_network_changed_cb (GNetworkMonitor *network_monitor, gboolean network_available, @@ -1698,18 +1618,6 @@ chatty_manager_initialize_libpurple (ChattyManager *self) "signed-off", self, PURPLE_CALLBACK (manager_connection_signed_off_cb), self); - purple_signal_connect (purple_plugins_get_handle (), - "mm-sms-modem-added", self, - PURPLE_CALLBACK (manager_sms_modem_added_cb), NULL); - - purple_signal_connect (purple_plugins_get_handle (), - "mm-sms-state", self, - PURPLE_CALLBACK (manager_sms_state_changed_cb), self); - - purple_signal_connect (purple_plugins_get_handle (), - "mm-sms-country-code", self, - PURPLE_CALLBACK (manager_sms_get_country_cb), self); - g_signal_connect_object (network_monitor, "network-changed", G_CALLBACK (manager_network_changed_cb), self, G_CONNECT_AFTER); @@ -1884,6 +1792,14 @@ chatty_manager_class_init (ChattyManagerClass *klass) 1, CHATTY_TYPE_CHAT); } +static void +manager_mm_account_changed_cb (ChattyManager *self) +{ + g_assert (CHATTY_IS_MANAGER (self)); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIVE_PROTOCOLS]); +} + static void chatty_manager_init (ChattyManager *self) { @@ -1891,8 +1807,13 @@ chatty_manager_init (ChattyManager *self) self->notification = chatty_notification_new (); - self->chatty_eds = chatty_eds_new (CHATTY_PROTOCOL_SMS); + self->chatty_eds = chatty_eds_new (CHATTY_PROTOCOL_MMS_SMS); self->account_list = g_list_store_new (CHATTY_TYPE_ACCOUNT); + self->mm_account = chatty_mm_account_new (); + + g_signal_connect_object (self->mm_account, "notify::status", + G_CALLBACK (manager_mm_account_changed_cb), self, + G_CONNECT_SWAPPED); self->chat_list = g_list_store_new (CHATTY_TYPE_CHAT); self->list_of_chat_list = g_list_store_new (G_TYPE_LIST_MODEL); @@ -2054,6 +1975,20 @@ matrix_db_open_cb (GObject *object, g_warning ("Failed to open Matrix DB: %s", error->message); } +static void +manager_mm_account_load_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(ChattyManager) self = user_data; + GListModel *chat_list; + + g_assert (CHATTY_IS_MANAGER (self)); + + chat_list = chatty_mm_account_get_chat_list (self->mm_account); + g_list_store_append (self->list_of_chat_list, chat_list); +} + void chatty_manager_purple (ChattyManager *self) { @@ -2071,6 +2006,12 @@ chatty_manager_purple (ChattyManager *self) search_path = g_build_filename (purple_user_dir (), "plugins", NULL); purple_plugins_add_search_path (search_path); + chatty_mm_account_set_history_db (self->mm_account, + chatty_manager_get_history (self)); + chatty_mm_account_load_async (self->mm_account, + manager_mm_account_load_cb, + g_object_ref (self)); + if (chatty_settings_get_experimental_features (chatty_settings_get_default ())) { self->matrix_db = matrix_db_new (); db_path = g_build_filename (purple_user_dir(), "chatty", "db", NULL); @@ -2174,7 +2115,6 @@ chatty_manager_load_plugins (ChattyManager *self) purple_plugins_load_saved (CHATTY_PREFS_ROOT "/plugins/loaded"); purple_plugins_probe (G_MODULE_SUFFIX); - self->sms_plugin = purple_plugins_find_with_id ("prpl-mm-sms"); self->lurch_plugin = purple_plugins_find_with_id ("core-riba-lurch"); self->carbon_plugin = purple_plugins_find_with_id ("core-riba-carbons"); self->file_upload_plugin = purple_plugins_find_with_id ("xep-http-file-upload"); @@ -2191,8 +2131,7 @@ chatty_manager_load_plugins (ChattyManager *self) if (chatty_settings_get_experimental_features (chatty_settings_get_default ())) chatty_manager_unload_plugin (purple_plugins_find_with_id ("prpl-matrix")); - if (chatty_manager_load_plugin (self->sms_plugin)) - chatty_manager_enable_sms_account (self); + chatty_manager_unload_plugin (purple_plugins_find_with_id ("prpl-mm-sms")); settings = chatty_settings_get_default (); g_signal_connect_object (settings, "notify::message-carbons", @@ -2278,9 +2217,16 @@ chatty_manager_lurch_plugin_is_loaded (ChattyManager *self) ChattyProtocol chatty_manager_get_active_protocols (ChattyManager *self) { + ChattyProtocol protocols; + g_return_val_if_fail (CHATTY_IS_MANAGER (self), CHATTY_PROTOCOL_NONE); - return self->active_protocols; + protocols = self->active_protocols; + + if (chatty_account_get_status (CHATTY_ACCOUNT (self->mm_account)) == CHATTY_CONNECTED) + protocols = protocols | CHATTY_PROTOCOL_MMS_SMS; + + return protocols; } @@ -2376,7 +2322,8 @@ chatty_manager_find_chat (GListModel *model, chat = g_list_model_get_item (model, i); - if (chatty_pp_chat_are_same (CHATTY_PP_CHAT (chat), CHATTY_PP_CHAT (item))) + if (CHATTY_IS_PP_CHAT (chat) && + chatty_pp_chat_are_same (CHATTY_PP_CHAT (chat), CHATTY_PP_CHAT (item))) return chat; } @@ -2535,6 +2482,14 @@ chatty_manager_save_account_finish (ChattyManager *self, return g_task_propagate_boolean (G_TASK (result), error); } +ChattyAccount * +chatty_manager_get_mm_account (ChattyManager *self) +{ + g_return_val_if_fail (CHATTY_IS_MANAGER (self), NULL); + + return CHATTY_ACCOUNT (self->mm_account); +} + ChattyChat * chatty_manager_find_chat_with_name (ChattyManager *self, const char *account_id, @@ -2557,7 +2512,7 @@ chatty_manager_find_chat_with_name (ChattyManager *self, chat = g_list_model_get_item (chat_list, i); account = chatty_chat_get_account (chat); - if (g_strcmp0 (chatty_account_get_username (account), account_id) != 0) + if (g_strcmp0 (chatty_item_get_username (CHATTY_ITEM (account)), account_id) != 0) continue; id = chatty_chat_get_chat_name (chat); @@ -2575,7 +2530,7 @@ chatty_manager_find_chat_with_name (ChattyManager *self, account = g_list_model_get_item (accounts, i); if (!CHATTY_IS_MA_ACCOUNT (account) || - g_strcmp0 (chatty_account_get_username (account), account_id) != 0) + g_strcmp0 (chatty_item_get_username (CHATTY_ITEM (account)), account_id) != 0) continue; chat_list = chatty_ma_account_get_chat_list (CHATTY_MA_ACCOUNT (account)); @@ -2627,22 +2582,14 @@ chatty_manager_set_uri (ChattyManager *self, { g_autoptr(ChattyChat) chat = NULL; g_autofree char *who = NULL; - ChattyContact *contact; - PurpleAccount *pp_account; - ChattyPpBuddy *buddy; ChattyAccount *account; - PurpleBuddy *pp_buddy; - ChattyChat *item; - GPtrArray *buddies; - const char *alias, *country_code; + const char *country_code; if (!uri || !*uri) return FALSE; - pp_account = purple_accounts_find ("SMS", "prpl-mm-sms"); - account = (ChattyAccount *)chatty_pp_account_get_object (pp_account); - - if (!purple_account_is_connected (pp_account)) + account = chatty_manager_get_mm_account (self); + if (chatty_account_get_status (account) != CHATTY_CONNECTED) return FALSE; country_code = chatty_settings_get_country_iso_code (chatty_settings_get_default ()); @@ -2650,35 +2597,8 @@ chatty_manager_set_uri (ChattyManager *self, if (!who) return FALSE; - contact = chatty_eds_find_by_number (self->chatty_eds, who); - - if (contact) - alias = chatty_item_get_name (CHATTY_ITEM (contact)); - else - alias = who; - - pp_buddy = purple_find_buddy (pp_account, who); - - if (!pp_buddy) { - pp_buddy = purple_buddy_new (pp_account, who, alias); - - purple_blist_add_buddy (pp_buddy, NULL, NULL, NULL); - } - - buddy = chatty_pp_buddy_get_object (pp_buddy); - - if (buddy && contact) - chatty_pp_buddy_set_contact (buddy, contact); - - chat = (ChattyChat *)chatty_pp_chat_new_im_chat (pp_account, pp_buddy, FALSE); - item = chatty_manager_add_chat (self, chat); - - purple_blist_node_set_bool (PURPLE_BLIST_NODE(pp_buddy), "chatty-autojoin", TRUE); - - buddies = g_ptr_array_new_full (1, g_free); - g_ptr_array_add (buddies, g_strdup (who)); - chatty_account_start_direct_chat_async (account, buddies, NULL, NULL); - g_signal_emit_by_name (item, "changed"); + chat = chatty_mm_account_start_chat (CHATTY_MM_ACCOUNT (account), who); + g_signal_emit (self, signals[OPEN_CHAT], 0, chat); return TRUE; } diff --git a/src/chatty-manager.h b/src/chatty-manager.h index b206272..543010b 100644 --- a/src/chatty-manager.h +++ b/src/chatty-manager.h @@ -67,6 +67,7 @@ gboolean chatty_manager_save_account_finish (ChattyManager *self, ChattyChat *chatty_manager_find_chat_with_name (ChattyManager *self, const char *account_id, const char *chat_id); +ChattyAccount *chatty_manager_get_mm_account (ChattyManager *self); ChattyChat *chatty_manager_add_chat (ChattyManager *self, ChattyChat *chat); gboolean chatty_manager_set_uri (ChattyManager *self, diff --git a/src/chatty-message-row.c b/src/chatty-message-row.c index 177791b..3e98463 100644 --- a/src/chatty-message-row.c +++ b/src/chatty-message-row.c @@ -118,7 +118,11 @@ message_activate_gesture_cb (ChattyMessageRow *self) if (!info || !info->path) return; - file = g_file_new_build_filename (g_get_user_cache_dir (), "chatty", info->path, NULL); + if (self->protocol == CHATTY_PROTOCOL_MMS_SMS || self->protocol == CHATTY_PROTOCOL_MMS) + file = g_file_new_build_filename (g_get_user_data_dir (), "chatty", info->path, NULL); + else + file = g_file_new_build_filename (g_get_user_cache_dir (), "chatty", info->path, NULL); + uri = g_file_get_uri (file); gtk_show_uri_on_window (NULL, uri, GDK_CURRENT_TIME, NULL); @@ -253,7 +257,7 @@ chatty_message_row_new (ChattyMessage *message, gtk_widget_set_halign (self->content_grid, GTK_ALIGN_START); gtk_widget_set_halign (self->message_event_box, GTK_ALIGN_START); gtk_widget_set_halign (self->author_label, GTK_ALIGN_START); - } else if (direction == CHATTY_DIRECTION_OUT && protocol == CHATTY_PROTOCOL_SMS) { + } else if (direction == CHATTY_DIRECTION_OUT && protocol == CHATTY_PROTOCOL_MMS_SMS) { gtk_style_context_add_class (sc, "bubble_green"); } else if (direction == CHATTY_DIRECTION_OUT) { gtk_style_context_add_class (sc, "bubble_blue"); @@ -317,6 +321,8 @@ chatty_message_row_hide_user_detail (ChattyMessageRow *self) g_return_if_fail (CHATTY_IS_MESSAGE_ROW (self)); gtk_widget_hide (self->author_label); - gtk_widget_hide (self->avatar_image); - gtk_widget_show (self->hidden_box); + if (gtk_widget_get_visible (self->avatar_image)) { + gtk_widget_hide (self->avatar_image); + gtk_widget_show (self->hidden_box); + } } diff --git a/src/chatty-message.c b/src/chatty-message.c index de2efce..63c6f8e 100644 --- a/src/chatty-message.c +++ b/src/chatty-message.c @@ -321,13 +321,9 @@ chatty_message_get_user_name (ChattyMessage *self) g_return_val_if_fail (CHATTY_IS_MESSAGE (self), ""); if (!self->user_name && self->user) { - if (CHATTY_IS_CONTACT (self->user)) - user_name = chatty_contact_get_value (CHATTY_CONTACT (self->user)); - else if (CHATTY_IS_PP_BUDDY (self->user)) - user_name = chatty_pp_buddy_get_id (CHATTY_PP_BUDDY (self->user)); - else if (CHATTY_IS_MA_BUDDY (self->user)) - user_name = chatty_ma_buddy_get_id (CHATTY_MA_BUDDY (self->user)); - else + user_name = chatty_item_get_username (self->user); + + if (!user_name || !*user_name) user_name = chatty_item_get_name (self->user); } diff --git a/src/chatty-mm-chat.c b/src/chatty-mm-chat.c new file mode 100644 index 0000000..1c13a21 --- /dev/null +++ b/src/chatty-mm-chat.c @@ -0,0 +1,685 @@ +/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- */ +/* chatty-mm-chat.c + * + * Copyright 2020 Purism SPC + * + * Author(s): + * Mohammed Sadiq + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "chatty-mm-chat" + +#include + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "contrib/gtk.h" +#include "chatty-settings.h" +#include "chatty-utils.h" +#include "users/chatty-mm-buddy.h" +#include "users/chatty-mm-account.h" +#include "chatty-notification.h" +#include "chatty-history.h" +#include "chatty-mm-chat.h" +#include "chatty-log.h" + +/** + * SECTION: chatty-mm-chat + * @title: ChattyMmChat + * @short_description: An abstraction over ModemManager + * @include: "chatty-mm-chat.h" + */ + +struct _ChattyMmChat +{ + ChattyChat parent_instance; + + ChattyNotification *notification; + ChattyMessage *last_notify_message; + ChattyEds *chatty_eds; + ChattyMmAccount *account; + ChattyHistory *history_db; + GListStore *chat_users; + GListStore *message_store; + /* A Queue of #GTask */ + GQueue *message_queue; + /* Index of @chat_users for the message + that's currently being sent */ + guint sender_index; + + char *last_message; + char *chat_id; + char *name; + guint unread_count; + guint last_msg_time; + ChattyProtocol protocol; + gboolean is_im; + gboolean history_is_loading; + gboolean is_sending_message; +}; + +G_DEFINE_TYPE (ChattyMmChat, chatty_mm_chat, CHATTY_TYPE_CHAT) + +static void +chatty_mm_chat_set_data (ChattyChat *chat, + gpointer account, + gpointer history_db) +{ + ChattyMmChat *self = (ChattyMmChat *)chat; + + g_assert (CHATTY_IS_MM_CHAT (self)); + g_assert (CHATTY_IS_MM_ACCOUNT (account)); + g_assert (!self->account); + g_assert (!self->history_db); + + g_set_object (&self->account, account); + g_set_object (&self->history_db, history_db); +} + +static gboolean +chatty_mm_chat_is_im (ChattyChat *chat) +{ + ChattyMmChat *self = (ChattyMmChat *)chat; + + g_assert (CHATTY_IS_MM_CHAT (self)); + + return self->is_im; +} + +static const char * +chatty_mm_chat_get_chat_name (ChattyChat *chat) +{ + ChattyMmChat *self = (ChattyMmChat *)chat; + + g_assert (CHATTY_IS_MM_CHAT (self)); + + if (self->chat_id) + return self->chat_id; + + return ""; +} + +static ChattyAccount * +chatty_mm_chat_get_account (ChattyChat *chat) +{ + ChattyMmChat *self = (ChattyMmChat *)chat; + + g_assert (CHATTY_IS_MM_CHAT (self)); + + return CHATTY_ACCOUNT (self->account); +} + +static void +mm_chat_load_db_messages_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(ChattyMmChat) self = user_data; + g_autoptr(GPtrArray) messages = NULL; + g_autoptr(GError) error = NULL; + + g_assert (CHATTY_IS_MM_CHAT (self)); + + messages = chatty_history_get_messages_finish (self->history_db, result, &error); + self->history_is_loading = FALSE; + + if (messages && messages->len) { + g_list_store_splice (self->message_store, 0, 0, messages->pdata, messages->len); + self->last_notify_message = g_object_ref (messages->pdata[messages->len - 1]); + g_signal_emit_by_name (self, "changed", 0); + } else if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + g_warning ("Error fetching messages: %s,", error->message); + } +} + +static void +chatty_mm_chat_load_past_messages (ChattyChat *chat, + int count) +{ + ChattyMmChat *self = (ChattyMmChat *)chat; + GListModel *model; + + g_assert (CHATTY_IS_MM_CHAT (self)); + g_assert (count > 0); + + if (self->history_is_loading) + return; + + self->history_is_loading = TRUE; + model = chatty_chat_get_messages (chat); + + chatty_history_get_messages_async (self->history_db, chat, + g_list_model_get_item (model, 0), + count, mm_chat_load_db_messages_cb, + g_object_ref (self)); +} + +static GListModel * +chatty_mm_chat_get_messages (ChattyChat *chat) +{ + ChattyMmChat *self = (ChattyMmChat *)chat; + + g_assert (CHATTY_IS_MM_CHAT (self)); + + return G_LIST_MODEL (self->message_store); +} + +static GListModel * +chatty_mm_chat_get_users (ChattyChat *chat) +{ + ChattyMmChat *self = (ChattyMmChat *)chat; + + g_assert (CHATTY_IS_MM_CHAT (self)); + + return G_LIST_MODEL (self->chat_users); +} + +static const char * +chatty_mm_chat_get_last_message (ChattyChat *chat) +{ + ChattyMmChat *self = (ChattyMmChat *)chat; + g_autoptr(ChattyMessage) message = NULL; + GListModel *model; + guint n_items; + + g_assert (CHATTY_IS_MM_CHAT (self)); + + model = G_LIST_MODEL (self->message_store); + n_items = g_list_model_get_n_items (model); + + if (n_items == 0) + return ""; + + message = g_list_model_get_item (model, n_items - 1); + + return chatty_message_get_text (message); +} + +static guint +chatty_mm_chat_get_unread_count (ChattyChat *chat) +{ + ChattyMmChat *self = (ChattyMmChat *)chat; + + g_assert (CHATTY_IS_MM_CHAT (self)); + + return self->unread_count; +} + +static void +chatty_mm_chat_set_unread_count (ChattyChat *chat, + guint unread_count) +{ + ChattyMmChat *self = (ChattyMmChat *)chat; + + g_assert (CHATTY_IS_MM_CHAT (self)); + + if (self->unread_count == unread_count) + return; + + self->unread_count = unread_count; + g_signal_emit_by_name (self, "changed", 0); +} + +static time_t +chatty_mm_chat_get_last_msg_time (ChattyChat *chat) +{ + ChattyMmChat *self = (ChattyMmChat *)chat; + g_autoptr(ChattyMessage) message = NULL; + GListModel *model; + guint n_items; + + g_assert (CHATTY_IS_MM_CHAT (self)); + + model = G_LIST_MODEL (self->message_store); + n_items = g_list_model_get_n_items (model); + + if (n_items == 0) + return 0; + + message = g_list_model_get_item (model, n_items - 1); + + return chatty_message_get_time (message); +} + +static void mm_chat_send_message_from_queue (ChattyMmChat *self); + +static void +mm_chat_send_message_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + ChattyMmChat *self; + g_autoptr(GTask) task = user_data; + g_autoptr(ChattyMmBuddy) buddy = NULL; + g_autoptr(GError) error = NULL; + ChattyMessage *message; + gboolean is_mms; + + g_assert (G_IS_TASK (task)); + + self = g_task_get_source_object (task); + g_assert (CHATTY_IS_MM_CHAT (self)); + + message = g_task_get_task_data (task); + g_assert (CHATTY_IS_MESSAGE (message)); + + chatty_mm_account_send_message_finish (self->account, result, &error); + + /* Regardless of the error state, we continue to send the rest + * of the messages from queue. + */ + if (error) { + CHATTY_DEBUG_MSG ("Error sending message: %s", error->message); + chatty_message_set_status (message, CHATTY_STATUS_SENDING_FAILED, 0); + } + + is_mms = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (message), "mms")); + + if (!is_mms) { + GListModel *users; + + users = G_LIST_MODEL (self->chat_users); + buddy = g_list_model_get_item (users, self->sender_index); + } + + self->is_sending_message = FALSE; + + /* If we don't have any more buddies, we have sent the message + to all buddies in the list. Reset sender index and mark task + as done */ + if (!buddy || is_mms) { + self->sender_index = 0; + g_task_return_boolean (task, TRUE); + g_object_unref (g_queue_pop_head (self->message_queue)); + } + + mm_chat_send_message_from_queue (self); +} + +/* + * Try sending message from queue if not empty. + * For SMS messages, the message is sent to individual + * buddies sequentially and the message is removed + * from queue only after the message is sent to every + * buddy in the chat list + */ +static void +mm_chat_send_message_from_queue (ChattyMmChat *self) +{ + g_autoptr(ChattyMmBuddy) buddy = NULL; + ChattyMessage *message; + GListModel *users; + GTask *task; + gboolean is_mms; + + g_assert (CHATTY_IS_MM_CHAT (self)); + + if (self->is_sending_message || + !self->message_queue || + !self->message_queue->length) + return; + + users = G_LIST_MODEL (self->chat_users); + g_return_if_fail (g_list_model_get_n_items (users) > 0); + + self->is_sending_message = TRUE; + task = g_queue_peek_head (self->message_queue); + message = g_task_get_task_data (task); + is_mms = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (message), "mms")); + + /* + * For SMS, we iterate over the buddy list and send individual message + * to each buddy sequentially + */ + if (!is_mms) { + /* Get the current buddy in the index */ + buddy = g_list_model_get_item (users, self->sender_index); + /* Increment the index which shall be used in the next iteration */ + self->sender_index++; + g_assert (buddy); + } + + chatty_mm_account_send_message_async (self->account, CHATTY_CHAT (self), + buddy, message, + g_task_get_cancellable (task), + mm_chat_send_message_cb, + g_object_ref (task)); +} + +static void +chatty_mm_chat_send_message_async (ChattyChat *chat, + ChattyMessage *message, + GAsyncReadyCallback callback, + gpointer user_data) +{ + ChattyMmChat *self = (ChattyMmChat *)chat; + GTask *task; + + g_assert (CHATTY_IS_MM_CHAT (self)); + g_assert (CHATTY_IS_MESSAGE (message)); + + if (!chatty_message_get_uid (message)) { + g_autofree char *uuid = NULL; + + uuid = g_uuid_string_random (); + chatty_message_set_uid (message, uuid); + } + + task = g_task_new (self, NULL, callback, user_data); + g_task_set_task_data (task, g_object_ref (message), g_object_unref); + g_queue_push_head (self->message_queue, task); + + mm_chat_send_message_from_queue (self); +} + +static const char * +chatty_mm_chat_get_name (ChattyItem *item) +{ + ChattyMmChat *self = (ChattyMmChat *)item; + + g_assert (CHATTY_IS_MM_CHAT (self)); + + /* If we have a cached name, return that */ + if (self->name && *self->name) + return self->name; + + if (self->chat_id) + return self->chat_id; + + return ""; +} + +static const char * +chatty_mm_chat_get_username (ChattyItem *item) +{ + return "SMS"; +} + +static ChattyProtocol +chatty_mm_chat_get_protocols (ChattyItem *item) +{ + ChattyMmChat *self = (ChattyMmChat *)item; + + g_assert (CHATTY_IS_MM_CHAT (self)); + + return CHATTY_PROTOCOL_MMS_SMS; +} + +static GdkPixbuf * +chatty_mm_chat_get_avatar (ChattyItem *item) +{ + ChattyMmChat *self = (ChattyMmChat *)item; + + g_assert (CHATTY_IS_MM_CHAT (self)); + + if (g_list_model_get_n_items (G_LIST_MODEL (self->chat_users)) == 1) { + g_autoptr(ChattyMmBuddy) buddy = NULL; + + buddy = g_list_model_get_item (G_LIST_MODEL (self->chat_users), 0); + + return chatty_item_get_avatar (CHATTY_ITEM (buddy)); + } + + return NULL; +} + +static void +chatty_mm_chat_finalize (GObject *object) +{ + ChattyMmChat *self = (ChattyMmChat *)object; + + g_queue_free_full (self->message_queue, g_object_unref); + g_list_store_remove_all (self->chat_users); + g_list_store_remove_all (self->message_store); + g_clear_object (&self->notification); + g_clear_object (&self->last_notify_message); + g_clear_object (&self->history_db); + g_clear_object (&self->chatty_eds); + g_object_unref (self->message_store); + g_object_unref (self->chat_users); + g_free (self->last_message); + g_free (self->chat_id); + + G_OBJECT_CLASS (chatty_mm_chat_parent_class)->finalize (object); +} + +static void +chatty_mm_chat_class_init (ChattyMmChatClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + ChattyItemClass *item_class = CHATTY_ITEM_CLASS (klass); + ChattyChatClass *chat_class = CHATTY_CHAT_CLASS (klass); + + object_class->finalize = chatty_mm_chat_finalize; + + item_class->get_name = chatty_mm_chat_get_name; + item_class->get_username = chatty_mm_chat_get_username; + item_class->get_protocols = chatty_mm_chat_get_protocols; + item_class->get_avatar = chatty_mm_chat_get_avatar; + + chat_class->set_data = chatty_mm_chat_set_data; + chat_class->is_im = chatty_mm_chat_is_im; + chat_class->get_chat_name = chatty_mm_chat_get_chat_name; + chat_class->get_account = chatty_mm_chat_get_account; + chat_class->load_past_messages = chatty_mm_chat_load_past_messages; + chat_class->get_messages = chatty_mm_chat_get_messages; + chat_class->get_users = chatty_mm_chat_get_users; + chat_class->get_last_message = chatty_mm_chat_get_last_message; + chat_class->get_unread_count = chatty_mm_chat_get_unread_count; + chat_class->set_unread_count = chatty_mm_chat_set_unread_count; + chat_class->get_last_msg_time = chatty_mm_chat_get_last_msg_time; + chat_class->send_message_async = chatty_mm_chat_send_message_async; +} + +static void +chatty_mm_chat_init (ChattyMmChat *self) +{ + self->chat_users = g_list_store_new (CHATTY_TYPE_MM_BUDDY); + self->message_store = g_list_store_new (CHATTY_TYPE_MESSAGE); + self->message_queue = g_queue_new (); + self->notification = chatty_notification_new (); +} + +ChattyMmChat * +chatty_mm_chat_new (const char *name, + const char *alias, + ChattyProtocol protocol, + gboolean is_im) +{ + ChattyMmChat *self; + + self = g_object_new (CHATTY_TYPE_MM_CHAT, NULL); + self->chat_id = g_strdup (name); + self->name = g_strdup (alias); + self->protocol = protocol; + self->is_im = !!is_im; + + return self; +} + +void +chatty_mm_chat_set_eds (ChattyMmChat *self, + ChattyEds *chatty_eds) +{ + guint n_items; + + g_return_if_fail (CHATTY_IS_MM_CHAT (self)); + g_return_if_fail (!chatty_eds || CHATTY_IS_EDS (chatty_eds)); + + if (!g_set_object (&self->chatty_eds, chatty_eds)) + return; + + n_items = g_list_model_get_n_items (G_LIST_MODEL (self->chat_users)); + + for (guint i = 0; i < n_items; i++) { + g_autoptr(ChattyMmBuddy) buddy = NULL; + ChattyContact *contact; + const char *phone; + + buddy = g_list_model_get_item (G_LIST_MODEL (self->chat_users), i); + phone = chatty_mm_buddy_get_number (buddy); + contact = chatty_eds_find_by_number (self->chatty_eds, phone); + if (contact) { + chatty_mm_buddy_set_contact (buddy, contact); + + if (n_items == 1) { + g_free (self->name); + self->name = g_strdup (chatty_item_get_name (CHATTY_ITEM (contact))); + g_object_notify (G_OBJECT (self), "name"); + g_signal_emit_by_name (self, "avatar-changed"); + } + } + } +} + +ChattyMessage * +chatty_mm_chat_find_message_with_id (ChattyMmChat *self, + const char *id) +{ + guint n_items; + + g_return_val_if_fail (CHATTY_IS_MM_CHAT (self), NULL); + g_return_val_if_fail (id, NULL); + + n_items = g_list_model_get_n_items (G_LIST_MODEL (self->message_store)); + + if (n_items == 0) + return NULL; + + /* Search from end, the item is more likely to be at the end */ + for (guint i = n_items; i > 0; i--) { + g_autoptr(ChattyMessage) message = NULL; + const char *message_id; + + message = g_list_model_get_item (G_LIST_MODEL (self->message_store), i - 1); + message_id = chatty_message_get_id (message); + + /* + * Once we have a message with no id, all preceding items shall likely + * have loaded from database, and thus no id, so don’t bother searching. + */ + if (!message_id) + break; + + if (g_str_equal (id, message_id)) + return message; + } + + return NULL; +} + +ChattyMmBuddy * +chatty_mm_chat_get_user (ChattyMmChat *self) +{ + g_autoptr(ChattyMmBuddy) buddy = NULL; + + g_return_val_if_fail (CHATTY_IS_MM_CHAT (self), NULL); + + if (g_list_model_get_n_items (G_LIST_MODEL (self->chat_users)) != 1) + return NULL; + + buddy = g_list_model_get_item (G_LIST_MODEL (self->chat_users), 0); + + return buddy; +} + +void +chatty_mm_chat_append_message (ChattyMmChat *self, + ChattyMessage *message) +{ + g_return_if_fail (CHATTY_IS_MM_CHAT (self)); + g_return_if_fail (CHATTY_IS_MESSAGE (message)); + + g_list_store_append (self->message_store, message); + g_signal_emit_by_name (self, "changed", 0); +} + +void +chatty_mm_chat_prepend_message (ChattyMmChat *self, + ChattyMessage *message) +{ + g_return_if_fail (CHATTY_IS_MM_CHAT (self)); + g_return_if_fail (CHATTY_IS_MESSAGE (message)); + + g_list_store_insert (self->message_store, 0, message); + g_signal_emit_by_name (self, "changed", 0); +} + +void +chatty_mm_chat_prepend_messages (ChattyMmChat *self, + GPtrArray *messages) +{ + g_return_if_fail (CHATTY_IS_MM_CHAT (self)); + + if (!messages || messages->len == 0) + return; + + g_return_if_fail (CHATTY_IS_MESSAGE (messages->pdata[0])); + + g_list_store_splice (self->message_store, 0, 0, messages->pdata, messages->len); + g_signal_emit_by_name (self, "changed", 0); +} + +void +chatty_mm_chat_add_user (ChattyMmChat *self, + ChattyMmBuddy *buddy) +{ + g_return_if_fail (CHATTY_IS_MM_CHAT (self)); + g_return_if_fail (CHATTY_IS_MM_BUDDY (buddy)); + + g_list_store_append (self->chat_users, buddy); +} + +void +chatty_mm_chat_add_users (ChattyMmChat *self, + GPtrArray *users) +{ + g_return_if_fail (CHATTY_IS_MM_CHAT (self)); + + if (!users || users->len == 0) + return; + + g_return_if_fail (CHATTY_IS_MM_BUDDY (users->pdata[0])); + + g_list_store_splice (self->chat_users, 0, 0, users->pdata, users->len); +} + +void +chatty_mm_chat_delete (ChattyMmChat *self) +{ + ChattyAccount *account; + + g_return_if_fail (CHATTY_IS_MM_CHAT (self)); + + account = chatty_chat_get_account (CHATTY_CHAT (self)); + chatty_mm_account_delete_chat (CHATTY_MM_ACCOUNT (account), CHATTY_CHAT (self)); +} + +/** + * chatty_mm_chat_show_notification: + * @self: A #ChattyMmChat + * + * Show notification for the last unread #ChattyMessage, + * if any. + */ +void +chatty_mm_chat_show_notification (ChattyMmChat *self) +{ + g_autoptr(ChattyMessage) message = NULL; + ChattyChat *chat; + guint n_items; + + g_return_if_fail (CHATTY_IS_MM_CHAT (self)); + + chat = CHATTY_CHAT (self); + + n_items = g_list_model_get_n_items (chatty_chat_get_messages (chat)); + message = g_list_model_get_item (chatty_chat_get_messages (chat), n_items - 1); + + if (g_set_object (&self->last_notify_message, message)) + chatty_notification_show_message (self->notification, chat, message, NULL); +} diff --git a/src/chatty-mm-chat.h b/src/chatty-mm-chat.h new file mode 100644 index 0000000..549e36e --- /dev/null +++ b/src/chatty-mm-chat.h @@ -0,0 +1,52 @@ +/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- */ +/* chatty-pp-chat.h + * + * Copyright 2020 Purism SPC + * + * Author(s): + * Mohammed Sadiq + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include + +#include "chatty-chat.h" +#include "users/chatty-account.h" +#include "users/chatty-mm-buddy.h" +#include "chatty-message.h" +#include "chatty-contact-provider.h" +#include "chatty-enums.h" + +G_BEGIN_DECLS + +#define CHATTY_TYPE_MM_CHAT (chatty_mm_chat_get_type ()) + +G_DECLARE_FINAL_TYPE (ChattyMmChat, chatty_mm_chat, CHATTY, MM_CHAT, ChattyChat) + +ChattyMmChat *chatty_mm_chat_new (const char *name, + const char *alias, + ChattyProtocol protocol, + gboolean is_im); +void chatty_mm_chat_set_eds (ChattyMmChat *self, + ChattyEds *chatty_eds); +void chatty_mm_chat_append_message (ChattyMmChat *self, + ChattyMessage *message); +void chatty_mm_chat_prepend_message (ChattyMmChat *self, + ChattyMessage *message); +void chatty_mm_chat_prepend_messages (ChattyMmChat *self, + GPtrArray *messages); +ChattyMessage *chatty_mm_chat_find_message_with_id (ChattyMmChat *self, + const char *id); +ChattyMmBuddy *chatty_mm_chat_get_user (ChattyMmChat *self); +void chatty_mm_chat_add_user (ChattyMmChat *self, + ChattyMmBuddy *buddy); +void chatty_mm_chat_add_users (ChattyMmChat *self, + GPtrArray *users); +void chatty_mm_chat_delete (ChattyMmChat *self); +void chatty_mm_chat_show_notification (ChattyMmChat *self); + +G_END_DECLS diff --git a/src/chatty-notification.c b/src/chatty-notification.c index e1492ab..93cfae7 100644 --- a/src/chatty-notification.c +++ b/src/chatty-notification.c @@ -45,7 +45,10 @@ create_new_notification (ChattyNotification *self, g_notification_add_button_with_target (self->notification, _("Open Message"), "app.open-chat", "(ss)", chatty_chat_get_chat_name (chat), - chatty_chat_get_username (chat)); + chatty_item_get_username (CHATTY_ITEM (chat))); +#if GLIB_CHECK_VERSION(2,70,0) + g_notification_set_category (self->notification, "im.received"); +#endif } static gboolean diff --git a/src/chatty-pp-chat.c b/src/chatty-pp-chat.c index 8a89050..cb721db 100644 --- a/src/chatty-pp-chat.c +++ b/src/chatty-pp-chat.c @@ -382,7 +382,7 @@ chat_find_user (ChattyPpChat *self, g_autoptr(ChattyPpBuddy) buddy = NULL; buddy = g_list_model_get_item (G_LIST_MODEL (self->chat_users), i); - if (chatty_pp_buddy_get_id (buddy) == user) { + if (chatty_item_get_username (CHATTY_ITEM (buddy)) == user) { if (index) *index = i; @@ -492,35 +492,6 @@ chatty_pp_chat_get_chat_name (ChattyChat *chat) return ""; } -static const char * -chatty_pp_chat_get_username (ChattyChat *chat) -{ - ChattyPpChat *self = (ChattyPpChat *)chat; - const char *username = NULL; - - g_assert (CHATTY_IS_PP_CHAT (self)); - - if (self->username && *self->username) - return self->username; - - if (self->pp_chat) - username = purple_account_get_username (self->pp_chat->account); - - if (self->buddy) - username = purple_account_get_username (self->buddy->account); - - if (self->conv) - username = purple_account_get_username (self->conv->account); - - if (username && *username && !self->username) - self->username = chatty_utils_jabber_id_strip (username); - - if (self->username) - return self->username; - - return ""; -} - static ChattyAccount * chatty_pp_chat_get_account (ChattyChat *chat) { @@ -860,18 +831,6 @@ chatty_pp_chat_get_name (ChattyItem *item) g_assert (CHATTY_IS_PP_CHAT (self)); - /* If available, return locally saved contact name for SMS chats */ - if (self->buddy && - chatty_item_get_protocols (CHATTY_ITEM (item)) == CHATTY_PROTOCOL_SMS) { - PurpleBlistNode *node; - - node = PURPLE_BLIST_NODE (self->buddy); - - if (node->ui_data && - chatty_pp_buddy_get_contact (node->ui_data)) - return chatty_item_get_name (node->ui_data); - } - if (self->pp_chat) name = purple_chat_get_name (self->pp_chat); else if (self->buddy) @@ -908,6 +867,35 @@ chatty_pp_chat_get_name (ChattyItem *item) return name; } +static const char * +chatty_pp_chat_get_username (ChattyItem *item) +{ + ChattyPpChat *self = (ChattyPpChat *)item; + const char *username = NULL; + + g_assert (CHATTY_IS_PP_CHAT (self)); + + if (self->username && *self->username) + return self->username; + + if (self->pp_chat) + username = purple_account_get_username (self->pp_chat->account); + + if (self->buddy) + username = purple_account_get_username (self->buddy->account); + + if (self->conv) + username = purple_account_get_username (self->conv->account); + + if (username && *username && !self->username) + self->username = chatty_utils_jabber_id_strip (username); + + if (self->username) + return self->username; + + return ""; +} + static ChattyProtocol chatty_pp_chat_get_protocols (ChattyItem *item) { @@ -1023,6 +1011,7 @@ chatty_pp_chat_class_init (ChattyPpChatClass *klass) object_class->finalize = chatty_pp_chat_finalize; item_class->get_name = chatty_pp_chat_get_name; + item_class->get_username = chatty_pp_chat_get_username; item_class->get_protocols = chatty_pp_chat_get_protocols; item_class->get_avatar = chatty_pp_chat_get_avatar; item_class->set_avatar_async = chatty_pp_chat_set_avatar_async; @@ -1031,7 +1020,6 @@ chatty_pp_chat_class_init (ChattyPpChatClass *klass) chat_class->is_im = chatty_pp_chat_is_im; chat_class->has_file_upload = chatty_pp_chat_has_file_upload; chat_class->get_chat_name = chatty_pp_chat_get_chat_name; - chat_class->get_username = chatty_pp_chat_get_username; chat_class->get_account = chatty_pp_chat_get_account; chat_class->load_past_messages = chatty_pp_chat_load_past_messages; chat_class->is_loading_history = chatty_pp_chat_is_loading_history; @@ -1915,25 +1903,6 @@ chatty_pp_chat_delete (ChattyPpChat *self) } } -static void -write_buddy_contact_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - g_autoptr(GTask) task = user_data; - GError *error = NULL; - gboolean status; - - g_assert (G_IS_TASK (task)); - - status = chatty_eds_write_contact_finish (result, &error); - - if (error) - g_task_return_error (task, error); - else - g_task_return_boolean (task, status); -} - void chatty_pp_chat_save_to_contacts_async (ChattyPpChat *self, GAsyncReadyCallback callback, @@ -1951,26 +1920,7 @@ chatty_pp_chat_save_to_contacts_async (ChattyPpChat *self, purple_blist_node_set_bool (PURPLE_BLIST_NODE (self->buddy), "chatty-notifications", TRUE); task = g_task_new (self, NULL, callback, user_data); - - if (chatty_item_get_protocols (CHATTY_ITEM (self)) == CHATTY_PROTOCOL_SMS) { - ChattyPpBuddy *buddy; - g_autofree char *number = NULL; - const char *who, *country_code; - - who = purple_buddy_get_name (self->buddy); - country_code = chatty_settings_get_country_iso_code (chatty_settings_get_default ()); - number = chatty_utils_check_phonenumber (who, country_code); - buddy = chatty_pp_buddy_get_object (self->buddy); - - if (!chatty_pp_buddy_get_contact (buddy)) - chatty_eds_write_contact_async (who, number, write_buddy_contact_cb, - g_steal_pointer (&task)); - else - g_task_return_boolean (task, TRUE); - } else { - g_task_return_boolean (task, TRUE); - } - + g_task_return_boolean (task, TRUE); } gboolean diff --git a/src/chatty-secret-store.c b/src/chatty-secret-store.c index 045d8c6..6a11aab 100644 --- a/src/chatty-secret-store.c +++ b/src/chatty-secret-store.c @@ -83,11 +83,12 @@ chatty_secret_store_save_async (ChattyAccount *account, key ? key : "", device_id); schema = secret_store_get_schema (); server = chatty_ma_account_get_homeserver (CHATTY_MA_ACCOUNT (account)); - label = g_strdup_printf (_("Chatty password for \"%s\""), chatty_account_get_username (account)); + label = g_strdup_printf (_("Chatty password for \"%s\""), + chatty_item_get_username (CHATTY_ITEM (account))); secret_password_store (schema, NULL, label, credentials, cancellable, callback, user_data, - CHATTY_USERNAME_ATTRIBUTE, chatty_account_get_username (account), + CHATTY_USERNAME_ATTRIBUTE, chatty_item_get_username (CHATTY_ITEM (account)), CHATTY_SERVER_ATTRIBUTE, server, CHATTY_PROTOCOL_ATTRIBUTE, PROTOCOL_MATRIX_STR, NULL); @@ -121,7 +122,7 @@ secret_load_cb (GObject *object, g_assert_true (G_IS_TASK (task)); - secrets = secret_service_search_finish (NULL, result, &error); + secrets = secret_password_search_finish (result, &error); CHATTY_TRACE_MSG ("secret accounts loaded, has-error: %d", !!error); if (error) { @@ -153,25 +154,23 @@ chatty_secret_load_async (GCancellable *cancellable, gpointer user_data) { const SecretSchema *schema; - g_autoptr(GHashTable) attr = NULL; GTask *task; g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); schema = secret_store_get_schema (); task = g_task_new (NULL, cancellable, callback, user_data); + /** With using SECRET_SCHEMA_DONT_MATCH_NAME we need some other attribute * (apart from the schema name itself) to use for the lookup. * The protocol attribute seems like a reasonable choice. */ - attr = secret_attributes_build (schema, - CHATTY_PROTOCOL_ATTRIBUTE, PROTOCOL_MATRIX_STR, - NULL); - + secret_password_search (schema, + SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS, + cancellable, secret_load_cb, task, + CHATTY_PROTOCOL_ATTRIBUTE, PROTOCOL_MATRIX_STR, + NULL); CHATTY_TRACE_MSG ("loading secret accounts"); - secret_service_search (NULL, schema, attr, - SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS, - cancellable, secret_load_cb, task); } GPtrArray * @@ -190,19 +189,17 @@ chatty_secret_delete_async (ChattyAccount *account, gpointer user_data) { const SecretSchema *schema; - g_autoptr(GHashTable) attr = NULL; const char *server; g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); schema = secret_store_get_schema (); server = chatty_ma_account_get_homeserver (CHATTY_MA_ACCOUNT (account)); - attr = secret_attributes_build (schema, - CHATTY_USERNAME_ATTRIBUTE, chatty_account_get_username (account), - CHATTY_SERVER_ATTRIBUTE, server, - CHATTY_PROTOCOL_ATTRIBUTE, PROTOCOL_MATRIX_STR, - NULL); - secret_service_clear (NULL, schema, attr, cancellable, callback, user_data); + secret_password_clear (schema, cancellable, callback, user_data, + CHATTY_USERNAME_ATTRIBUTE, chatty_item_get_username (CHATTY_ITEM (account)), + CHATTY_SERVER_ATTRIBUTE, server, + CHATTY_PROTOCOL_ATTRIBUTE, PROTOCOL_MATRIX_STR, + NULL); } gboolean @@ -211,5 +208,5 @@ chatty_secret_delete_finish (GAsyncResult *result, { g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); - return secret_service_clear_finish (NULL, result, error); + return secret_password_clear_finish (result, error); } diff --git a/src/chatty-settings.c b/src/chatty-settings.c index f09727d..5ee1a40 100644 --- a/src/chatty-settings.c +++ b/src/chatty-settings.c @@ -60,6 +60,7 @@ enum { PROP_INDICATE_UNKNOWN_CONTACTS, PROP_CONVERT_EMOTICONS, PROP_RETURN_SENDS_MESSAGE, + PROP_REQUEST_SMS_DELIVERY_REPORTS, PROP_MAM_ENABLED, N_PROPS }; @@ -112,6 +113,10 @@ chatty_settings_get_property (GObject *object, g_value_set_boolean (value, chatty_settings_get_return_sends_message (self)); break; + case PROP_REQUEST_SMS_DELIVERY_REPORTS: + g_value_set_boolean (value, chatty_settings_request_sms_delivery_reports (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -174,6 +179,11 @@ chatty_settings_set_property (GObject *object, g_value_get_boolean (value)); break; + case PROP_REQUEST_SMS_DELIVERY_REPORTS: + g_settings_set_boolean (self->settings, "request-sms-delivery-reports", + g_value_get_boolean (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -201,6 +211,8 @@ chatty_settings_constructed (GObject *object) self, "indicate-unknown-contacts", G_SETTINGS_BIND_DEFAULT); g_settings_bind (self->settings, "return-sends-message", self, "return-sends-message", G_SETTINGS_BIND_DEFAULT); + g_settings_bind (self->settings, "request-sms-delivery-reports", + self, "request-sms-delivery-reports", G_SETTINGS_BIND_DEFAULT); self->country_code = g_settings_get_string (self->settings, "country-code"); } @@ -289,6 +301,13 @@ chatty_settings_class_init (ChattySettingsClass *klass) FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + properties[PROP_REQUEST_SMS_DELIVERY_REPORTS] = + g_param_spec_boolean ("request-sms-delivery-reports", + "Request SMS delivery reports", + "Whether to request delivery reports for outgoing SMS", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, N_PROPS, properties); } @@ -530,6 +549,15 @@ chatty_settings_set_country_iso_code (ChattySettings *self, g_settings_set (G_SETTINGS (self->settings), "country-code", "s", country_code); } +gboolean +chatty_settings_request_sms_delivery_reports (ChattySettings *self) +{ + g_return_val_if_fail (CHATTY_IS_SETTINGS (self), FALSE); + + return g_settings_get_boolean (G_SETTINGS (self->settings), + "request-sms-delivery-reports"); +} + gboolean chatty_settings_get_experimental_features (ChattySettings *self) { diff --git a/src/chatty-settings.h b/src/chatty-settings.h index 571d53f..cd4406e 100644 --- a/src/chatty-settings.h +++ b/src/chatty-settings.h @@ -51,6 +51,7 @@ void chatty_settings_set_window_geometry (ChattySettings *se const char *chatty_settings_get_country_iso_code (ChattySettings *self); void chatty_settings_set_country_iso_code (ChattySettings *self, const char *iso_code); +gboolean chatty_settings_request_sms_delivery_reports (ChattySettings *self); gboolean chatty_settings_get_experimental_features (ChattySettings *self); void chatty_settings_enable_experimental_features (ChattySettings *self, gboolean enable); diff --git a/src/chatty-text-item.c b/src/chatty-text-item.c index e0dc88d..5915e38 100644 --- a/src/chatty-text-item.c +++ b/src/chatty-text-item.c @@ -142,6 +142,7 @@ chatty_text_item_init (ChattyTextItem *self) "wrap-mode", PANGO_WRAP_WORD_CHAR, "xalign", 0.0, NULL); + gtk_label_set_selectable (GTK_LABEL (self->content_label), TRUE); gtk_container_add (GTK_CONTAINER (self), self->content_label); } diff --git a/src/chatty-utils.c b/src/chatty-utils.c index 4cfe65e..d441645 100644 --- a/src/chatty-utils.c +++ b/src/chatty-utils.c @@ -188,7 +188,7 @@ chatty_utils_username_is_valid (const char *name, valid |= CHATTY_PROTOCOL_TELEGRAM; } - if (protocol & CHATTY_PROTOCOL_SMS && len < 20) { + if (protocol & CHATTY_PROTOCOL_MMS_SMS && len < 20) { const char *end; guint end_len; @@ -202,7 +202,7 @@ chatty_utils_username_is_valid (const char *name, end_len++; if (end_len == len) - valid |= CHATTY_PROTOCOL_SMS; + valid |= CHATTY_PROTOCOL_MMS_SMS; } return valid; diff --git a/src/chatty-window.c b/src/chatty-window.c index 85484fc..4271f4e 100644 --- a/src/chatty-window.c +++ b/src/chatty-window.c @@ -23,6 +23,7 @@ #include "chatty-manager.h" #include "chatty-list-row.h" #include "chatty-settings.h" +#include "chatty-mm-chat.h" #include "chatty-pp-chat.h" #include "chatty-chat-view.h" #include "chatty-manager.h" @@ -181,20 +182,19 @@ static gboolean window_chat_name_matches (ChattyItem *item, ChattyWindow *self) { - ChattyProtocol protocols, protocol; + ChattyProtocol protocol; g_assert (CHATTY_IS_CHAT (item)); g_assert (CHATTY_IS_WINDOW (self)); - protocols = chatty_manager_get_active_protocols (self->manager); protocol = chatty_item_get_protocols (item); - if (protocol != CHATTY_PROTOCOL_MATRIX && - !(protocols & chatty_item_get_protocols (item))) - return FALSE; + if ((!self->chat_needle || !*self->chat_needle) && + CHATTY_IS_MM_CHAT (item)) + return TRUE; /* FIXME: Not a good idea */ - if (chatty_item_get_protocols (item) != CHATTY_PROTOCOL_SMS) { + if (chatty_item_get_protocols (item) != CHATTY_PROTOCOL_MMS_SMS) { ChattyAccount *account; if (CHATTY_IS_PP_CHAT (item) && @@ -243,7 +243,7 @@ chatty_window_open_item (ChattyWindow *self, if (CHATTY_IS_CONTACT (item)) { const char *number; - number = chatty_contact_get_value (CHATTY_CONTACT (item)); + number = chatty_item_get_username (item); chatty_window_set_uri (self, number); return; @@ -265,17 +265,16 @@ chatty_window_open_item (ChattyWindow *self, if (CHATTY_IS_PP_CHAT (item)) chat = CHATTY_CHAT (item); - if (chatty_item_get_protocols (item) == CHATTY_PROTOCOL_SMS && - CHATTY_IS_PP_BUDDY (item) && - !chatty_pp_buddy_get_contact (CHATTY_PP_BUDDY (item))) - gtk_widget_show (self->menu_add_contact_button); - if (CHATTY_IS_PP_CHAT (chat)) { chatty_pp_chat_join (CHATTY_PP_CHAT (chat)); gtk_filter_changed (self->chat_filter, GTK_FILTER_CHANGE_DIFFERENT); window_chat_changed_cb (self); } + + if (CHATTY_IS_MM_CHAT (item)) { + chatty_window_open_chat (CHATTY_WINDOW (self), CHATTY_CHAT (item)); + } } static void @@ -354,7 +353,7 @@ window_new_message_clicked_cb (ChattyWindow *self) if (CHATTY_IS_CONTACT (item) && chatty_contact_is_dummy (CHATTY_CONTACT (item))) - phone_number = chatty_contact_get_value (CHATTY_CONTACT (item)); + phone_number = chatty_item_get_username (item); if (phone_number) chatty_window_set_uri (self, phone_number); @@ -384,7 +383,7 @@ window_add_chat_button_clicked_cb (ChattyWindow *self) { g_assert (CHATTY_IS_WINDOW (self)); - if (chatty_manager_get_active_protocols (self->manager) == CHATTY_PROTOCOL_SMS) + if (chatty_manager_get_active_protocols (self->manager) == CHATTY_PROTOCOL_MMS_SMS) window_new_message_clicked_cb (self); else gtk_popover_popup (GTK_POPOVER (self->header_chat_list_new_msg_popover)); @@ -471,6 +470,8 @@ window_delete_buddy_clicked_cb (ChattyWindow *self) CHATTY_CHAT (self->selected_item)); if (CHATTY_IS_PP_CHAT (self->selected_item)) { chatty_pp_chat_delete (CHATTY_PP_CHAT (self->selected_item)); + } else if (CHATTY_IS_MM_CHAT (self->selected_item)) { + chatty_mm_chat_delete (CHATTY_MM_CHAT (self->selected_item)); } else { g_return_if_reached (); } @@ -526,17 +527,46 @@ write_contact_cb (GObject *object, gtk_widget_destroy (dialog); } +static void +write_eds_contact_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(ChattyWindow) self = user_data; + g_autoptr(GError) error = NULL; + GtkWidget *dialog; + + g_assert (CHATTY_IS_WINDOW (self)); + + if (chatty_eds_write_contact_finish (result, &error)) + return; + + dialog = gtk_message_dialog_new (GTK_WINDOW (self), + GTK_DIALOG_MODAL, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_CLOSE, + _("Error saving contact: %s"), error->message); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + static void window_add_contact_clicked_cb (ChattyWindow *self) { g_assert (CHATTY_IS_WINDOW (self)); g_return_if_fail (self->selected_item); - if (!CHATTY_IS_PP_CHAT (self->selected_item)) - return; + if (CHATTY_IS_PP_CHAT (self->selected_item)) { + chatty_pp_chat_save_to_contacts_async (CHATTY_PP_CHAT (self->selected_item), + write_contact_cb, g_object_ref (self)); + } else if (CHATTY_IS_MM_CHAT (self->selected_item)) { + const char *phone; - chatty_pp_chat_save_to_contacts_async (CHATTY_PP_CHAT (self->selected_item), - write_contact_cb, g_object_ref (self)); + phone = chatty_chat_get_chat_name (CHATTY_CHAT (self->selected_item)); + chatty_eds_write_contact_async ("", phone, + write_eds_contact_cb, + g_object_ref (self)); + } gtk_widget_hide (self->menu_add_contact_button); } @@ -624,8 +654,8 @@ window_active_protocols_changed_cb (ChattyWindow *self) g_assert (CHATTY_IS_WINDOW (self)); protocols = chatty_manager_get_active_protocols (self->manager); - has_sms = !!(protocols & CHATTY_PROTOCOL_SMS); - has_im = !!(protocols & ~CHATTY_PROTOCOL_SMS); + has_sms = !!(protocols & CHATTY_PROTOCOL_MMS_SMS); + has_im = !!(protocols & ~CHATTY_PROTOCOL_MMS_SMS); gtk_widget_set_sensitive (self->header_add_chat_button, has_sms || has_im); gtk_widget_set_sensitive (self->menu_new_group_message_button, has_im); @@ -861,6 +891,8 @@ void chatty_window_open_chat (ChattyWindow *self, ChattyChat *chat) { + gboolean can_delete; + g_return_if_fail (CHATTY_IS_WINDOW (self)); g_return_if_fail (CHATTY_IS_CHAT (chat)); g_debug ("opening item of type: %s, protocol: %d", @@ -871,9 +903,30 @@ chatty_window_open_chat (ChattyWindow *self, window_set_item (self, CHATTY_ITEM (chat)); window_chat_changed_cb (self); - gtk_widget_set_visible (self->delete_button, CHATTY_IS_PP_CHAT (chat)); + gtk_widget_set_visible (self->leave_button, !CHATTY_IS_MM_CHAT (chat)); + can_delete = CHATTY_IS_PP_CHAT (chat) || CHATTY_IS_MM_CHAT (chat); + gtk_widget_set_visible (self->delete_button, can_delete); hdy_leaflet_set_visible_child (HDY_LEAFLET (self->content_box), self->chat_view); + gtk_widget_hide (self->menu_add_contact_button); if (chatty_window_get_active_chat (self)) chatty_chat_set_unread_count (chat, 0); + + if (CHATTY_IS_MM_CHAT (chat)) { + GListModel *users; + const char *name; + + users = chatty_chat_get_users (chat); + name = chatty_chat_get_chat_name (chat); + + if (g_list_model_get_n_items (users) == 1 && + chatty_utils_username_is_valid (name, CHATTY_PROTOCOL_MMS_SMS)) { + g_autoptr(ChattyMmBuddy) buddy = NULL; + + buddy = g_list_model_get_item (users, 0); + + if (!chatty_mm_buddy_get_contact (buddy)) + gtk_widget_show (self->menu_add_contact_button); + } + } } diff --git a/src/dialogs/chatty-ma-account-details.c b/src/dialogs/chatty-ma-account-details.c index 5ace255..7f34033 100644 --- a/src/dialogs/chatty-ma-account-details.c +++ b/src/dialogs/chatty-ma-account-details.c @@ -43,6 +43,11 @@ struct _ChattyMaAccountDetails ChattyAccount *account; GtkWidget *avatar_image; + GtkWidget *delete_avatar_button; + GtkWidget *delete_button_stack; + GtkWidget *delete_button_image; + GtkWidget *delete_avatar_spinner; + GtkWidget *status_label; GtkWidget *name_entry; GtkWidget *email_box; @@ -53,6 +58,7 @@ struct _ChattyMaAccountDetails GtkWidget *device_id_label; guint modified : 1; + guint is_deleting_avatar : 1; gulong status_id; }; @@ -72,6 +78,31 @@ static guint signals[N_SIGNALS]; G_DEFINE_TYPE (ChattyMaAccountDetails, chatty_ma_account_details, HDY_TYPE_PREFERENCES_PAGE) +static void +update_delete_avatar_button_state (ChattyMaAccountDetails *self) +{ + GtkStack *button_stack; + ChattyStatus status; + gboolean has_avatar = FALSE, can_delete; + + if (CHATTY_IS_ITEM (self->account) && + chatty_item_get_avatar_file (CHATTY_ITEM (self->account))) + has_avatar = TRUE; + + status = chatty_account_get_status (CHATTY_ACCOUNT (self->account)); + can_delete = has_avatar && !self->is_deleting_avatar && status == CHATTY_CONNECTED; + gtk_widget_set_sensitive (self->delete_avatar_button, can_delete); + + button_stack = GTK_STACK (self->delete_button_stack); + + if (self->is_deleting_avatar) + gtk_stack_set_visible_child (button_stack, self->delete_avatar_spinner); + else + gtk_stack_set_visible_child (button_stack, self->delete_button_image); + + g_object_set (self->delete_avatar_spinner, "active", self->is_deleting_avatar, NULL); +} + static char * ma_account_show_dialog_load_avatar (ChattyMaAccountDetails *self) { @@ -106,6 +137,33 @@ ma_details_avatar_button_clicked_cb (ChattyMaAccountDetails *self) file_name, NULL, NULL, NULL); } +static void +ma_details_delete_avatar_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(ChattyMaAccountDetails) self = user_data; + + self->is_deleting_avatar = FALSE; + update_delete_avatar_button_state (self); +} + +static void +ma_details_delete_avatar_button_clicked_cb (ChattyMaAccountDetails *self) +{ + g_assert (CHATTY_IS_MA_ACCOUNT_DETAILS (self)); + + if (self->is_deleting_avatar) + return; + + g_warning ("xxxxxxxxxx"); + self->is_deleting_avatar = TRUE; + update_delete_avatar_button_state (self); + chatty_item_set_avatar_async (CHATTY_ITEM (self->account), NULL, NULL, + ma_details_delete_avatar_cb, + g_object_ref (self)); +} + static void ma_details_name_entry_changed_cb (ChattyMaAccountDetails *self, GtkEntry *entry) @@ -329,6 +387,7 @@ ma_details_status_changed_cb (ChattyMaAccountDetails *self) status_text = _("disconnected"); gtk_label_set_text (GTK_LABEL (self->status_label), status_text); + update_delete_avatar_button_state (self); if (status == CHATTY_CONNECTED) { chatty_ma_account_get_details_async (CHATTY_MA_ACCOUNT (self->account), NULL, @@ -403,6 +462,11 @@ chatty_ma_account_details_class_init (ChattyMaAccountDetailsClass *klass) "ui/chatty-ma-account-details.ui"); gtk_widget_class_bind_template_child (widget_class, ChattyMaAccountDetails, avatar_image); + gtk_widget_class_bind_template_child (widget_class, ChattyMaAccountDetails, delete_avatar_button); + gtk_widget_class_bind_template_child (widget_class, ChattyMaAccountDetails, delete_button_stack); + gtk_widget_class_bind_template_child (widget_class, ChattyMaAccountDetails, delete_button_image); + gtk_widget_class_bind_template_child (widget_class, ChattyMaAccountDetails, delete_avatar_spinner); + gtk_widget_class_bind_template_child (widget_class, ChattyMaAccountDetails, status_label); gtk_widget_class_bind_template_child (widget_class, ChattyMaAccountDetails, name_entry); gtk_widget_class_bind_template_child (widget_class, ChattyMaAccountDetails, email_box); @@ -413,6 +477,7 @@ chatty_ma_account_details_class_init (ChattyMaAccountDetailsClass *klass) gtk_widget_class_bind_template_child (widget_class, ChattyMaAccountDetails, device_id_label); gtk_widget_class_bind_template_callback (widget_class, ma_details_avatar_button_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, ma_details_delete_avatar_button_clicked_cb); gtk_widget_class_bind_template_callback (widget_class, ma_details_name_entry_changed_cb); gtk_widget_class_bind_template_callback (widget_class, ma_details_delete_account_clicked_cb); } @@ -538,7 +603,7 @@ chatty_ma_account_details_set_item (ChattyMaAccountDetails *self, gtk_label_set_text (GTK_LABEL (self->homeserver_label), chatty_ma_account_get_homeserver (CHATTY_MA_ACCOUNT (self->account))); gtk_label_set_text (GTK_LABEL (self->matrix_id_label), - chatty_account_get_username (account)); + chatty_item_get_username (CHATTY_ITEM (account))); chatty_avatar_set_item (CHATTY_AVATAR (self->avatar_image), CHATTY_ITEM (account)); diff --git a/src/dialogs/chatty-new-chat-dialog.c b/src/dialogs/chatty-new-chat-dialog.c index e270fd0..7b6d9ed 100644 --- a/src/dialogs/chatty-new-chat-dialog.c +++ b/src/dialogs/chatty-new-chat-dialog.c @@ -80,10 +80,25 @@ G_DEFINE_TYPE (ChattyNewChatDialog, chatty_new_chat_dialog, GTK_TYPE_DIALOG) static void dialog_active_protocols_changed_cb (ChattyNewChatDialog *self) { + ChattyAccount *mm_account; + ChattyProtocol protocol; + gboolean valid; + g_assert (CHATTY_IS_NEW_CHAT_DIALOG (self)); self->active_protocols = chatty_manager_get_active_protocols (self->manager); gtk_filter_changed (self->filter, GTK_FILTER_CHANGE_DIFFERENT); + + protocol = CHATTY_PROTOCOL_MMS_SMS; + valid = protocol == chatty_utils_username_is_valid (self->search_str, protocol); + mm_account = chatty_manager_get_mm_account (self->manager); + valid = valid && chatty_account_get_status (mm_account) == CHATTY_CONNECTED; + gtk_widget_set_visible (self->new_contact_row, valid); + + if (valid || g_list_model_get_n_items (G_LIST_MODEL (self->slice_model)) > 0) + gtk_stack_set_visible_child (GTK_STACK (self->contact_list_stack), self->contact_list_view); + else + gtk_stack_set_visible_child (GTK_STACK (self->contact_list_stack), self->empty_search_view); } @@ -123,6 +138,20 @@ dialog_filter_item_cb (ChattyItem *item, return chatty_item_matches (item, self->search_str, self->active_protocols, TRUE); } +static void +new_chat_list_changed_cb (ChattyNewChatDialog *self) +{ + guint n_items; + + g_assert (CHATTY_IS_NEW_CHAT_DIALOG (self)); + + n_items = g_list_model_get_n_items (G_LIST_MODEL (self->slice_model)); + + if (n_items > 0 || gtk_widget_get_visible (self->new_contact_row)) + gtk_stack_set_visible_child (GTK_STACK (self->contact_list_stack), self->contact_list_view); + else + gtk_stack_set_visible_child (GTK_STACK (self->contact_list_stack), self->empty_search_view); +} static void chatty_new_chat_dialog_update_new_contact_row (ChattyNewChatDialog *self) @@ -222,7 +251,7 @@ static void contact_search_entry_changed_cb (ChattyNewChatDialog *self, GtkEntry *entry) { - PurpleAccount *account; + ChattyAccount *account; g_autofree char *old_needle = NULL; const char *str; GtkFilterChange change; @@ -257,10 +286,10 @@ contact_search_entry_changed_cb (ChattyNewChatDialog *self, chatty_list_row_set_item (CHATTY_LIST_ROW (self->new_contact_row), CHATTY_ITEM (self->dummy_contact)); - protocol = CHATTY_PROTOCOL_SMS; + protocol = CHATTY_PROTOCOL_MMS_SMS; valid = protocol == chatty_utils_username_is_valid (self->search_str, protocol); - account = purple_accounts_find ("SMS", "prpl-mm-sms"); - valid = valid && purple_account_is_connected (account); + account = chatty_manager_get_mm_account (self->manager); + valid = valid && chatty_account_get_status (account) == CHATTY_CONNECTED; gtk_widget_set_visible (self->new_contact_row, valid); if (valid || g_list_model_get_n_items (G_LIST_MODEL (self->slice_model)) > 0) @@ -337,12 +366,7 @@ account_list_row_activated_cb (ChattyNewChatDialog *self, self->selected_account = account; gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (prefix_radio), TRUE); - - if (chatty_item_is_sms (CHATTY_ITEM (account))) { - chatty_new_chat_set_edit_mode (self, FALSE); - } else { - chatty_new_chat_set_edit_mode (self, TRUE); - } + chatty_new_chat_set_edit_mode (self, TRUE); } @@ -401,7 +425,7 @@ chatty_new_chat_add_account_to_list (ChattyNewChatDialog *self, protocol = chatty_item_get_protocols (CHATTY_ITEM (account)); // TODO list supported protocols here - if (protocol & ~(CHATTY_PROTOCOL_SMS | + if (protocol & ~(CHATTY_PROTOCOL_MMS_SMS | CHATTY_PROTOCOL_XMPP | CHATTY_PROTOCOL_MATRIX | CHATTY_PROTOCOL_TELEGRAM | @@ -431,7 +455,8 @@ chatty_new_chat_add_account_to_list (ChattyNewChatDialog *self, (gpointer)prefix_radio_button); hdy_action_row_add_prefix (row, GTK_WIDGET (prefix_radio_button )); - hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (row), chatty_account_get_username (account)); + hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (row), + chatty_item_get_username (CHATTY_ITEM (account))); gtk_container_add (GTK_CONTAINER (self->accounts_list), GTK_WIDGET (row)); @@ -589,20 +614,26 @@ chatty_new_chat_dialog_init (ChattyNewChatDialog *self) self->dummy_prefix_radio = gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON (NULL)); self->manager = g_object_ref (chatty_manager_get_default ()); - self->filter = gtk_custom_filter_new ((GtkCustomFilterFunc)dialog_filter_item_cb, self, NULL); - g_signal_connect_object (self->manager, "notify::active-protocols", - G_CALLBACK (dialog_active_protocols_changed_cb), self, G_CONNECT_SWAPPED); - dialog_active_protocols_changed_cb (self); sorter = gtk_custom_sorter_new ((GCompareDataFunc)chatty_item_compare, NULL, NULL); sort_model = gtk_sort_list_model_new (chatty_manager_get_contact_list (self->manager), sorter); + + self->filter = gtk_custom_filter_new ((GtkCustomFilterFunc)dialog_filter_item_cb, self, NULL); filter_model = gtk_filter_list_model_new (G_LIST_MODEL (sort_model), self->filter); + self->slice_model = gtk_slice_list_model_new (G_LIST_MODEL (filter_model), 0, ITEMS_COUNT); + g_signal_connect_object (self->slice_model, "items-changed", + G_CALLBACK (new_chat_list_changed_cb), self, + G_CONNECT_SWAPPED); gtk_list_box_bind_model (GTK_LIST_BOX (self->chats_listbox), G_LIST_MODEL (self->slice_model), (GtkListBoxCreateWidgetFunc)chatty_list_contact_row_new, NULL, NULL); + g_signal_connect_object (self->manager, "notify::active-protocols", + G_CALLBACK (dialog_active_protocols_changed_cb), self, G_CONNECT_SWAPPED); + dialog_active_protocols_changed_cb (self); + chatty_new_chat_dialog_update_new_contact_row (self); } diff --git a/src/dialogs/chatty-new-muc-dialog.c b/src/dialogs/chatty-new-muc-dialog.c index aee4dc2..a186bc1 100644 --- a/src/dialogs/chatty-new-muc-dialog.c +++ b/src/dialogs/chatty-new-muc-dialog.c @@ -135,7 +135,7 @@ chatty_new_muc_add_account_to_list (ChattyNewMucDialog *self, (gpointer)prefix_radio_button); hdy_action_row_add_prefix (row, GTK_WIDGET(prefix_radio_button )); - hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (row), chatty_account_get_username (CHATTY_ACCOUNT (account))); + hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (row), chatty_item_get_username (CHATTY_ITEM (account))); gtk_container_add (GTK_CONTAINER(self->accounts_list), GTK_WIDGET(row)); @@ -158,9 +158,7 @@ chatty_new_muc_populate_account_list (ChattyNewMucDialog *self) account = chatty_pp_account_get_object (l->data); - if (!chatty_item_is_sms (CHATTY_ITEM (account))) { - chatty_new_muc_add_account_to_list (self, account); - } + chatty_new_muc_add_account_to_list (self, account); } row = HDY_ACTION_ROW(gtk_list_box_get_row_at_index (GTK_LIST_BOX(self->accounts_list), 0)); diff --git a/src/dialogs/chatty-pp-account-details.c b/src/dialogs/chatty-pp-account-details.c index c4dcd26..533aa19 100644 --- a/src/dialogs/chatty-pp-account-details.c +++ b/src/dialogs/chatty-pp-account-details.c @@ -351,7 +351,7 @@ chatty_pp_account_details_set_item (ChattyPpAccountDetails *self, if (!g_set_object (&self->account, account) || !account) return; - account_name = chatty_account_get_username (account); + account_name = chatty_item_get_username (CHATTY_ITEM (account)); protocol_name = chatty_account_get_protocol_name (account); gtk_label_set_text (GTK_LABEL (self->account_id_label), account_name); diff --git a/src/dialogs/chatty-pp-chat-info.c b/src/dialogs/chatty-pp-chat-info.c index aa5d665..f8430cb 100644 --- a/src/dialogs/chatty-pp-chat-info.c +++ b/src/dialogs/chatty-pp-chat-info.c @@ -245,7 +245,9 @@ chatty_pp_chat_info_update (ChattyPpChatInfo *self) G_CONNECT_SWAPPED); pp_chat_info_encrypt_changed_cb (self); - if (protocol == CHATTY_PROTOCOL_SMS) { + if (protocol == CHATTY_PROTOCOL_MMS_SMS || + protocol == CHATTY_PROTOCOL_MMS) { + gtk_widget_set_sensitive (self->avatar_button, FALSE); gtk_label_set_text (GTK_LABEL (self->user_id_title), _("Phone Number:")); } else if (protocol == CHATTY_PROTOCOL_XMPP) { gtk_label_set_text (GTK_LABEL (self->user_id_title), _("XMPP ID:")); @@ -292,9 +294,8 @@ chatty_pp_chat_info_update (ChattyPpChatInfo *self) gtk_widget_hide (self->status_label); } - gtk_widget_set_visible (self->notification_switch, - protocol != CHATTY_PROTOCOL_MATRIX); - if (protocol != CHATTY_PROTOCOL_MATRIX) + gtk_widget_set_visible (self->notification_switch, CHATTY_IS_PP_CHAT (self->chat)); + if (CHATTY_IS_PP_CHAT (self->chat)) gtk_switch_set_state (GTK_SWITCH (self->notification_switch), chatty_pp_chat_get_show_notifications (CHATTY_PP_CHAT (self->chat))); diff --git a/src/dialogs/chatty-settings-dialog.c b/src/dialogs/chatty-settings-dialog.c index 132575d..d916708 100644 --- a/src/dialogs/chatty-settings-dialog.c +++ b/src/dialogs/chatty-settings-dialog.c @@ -79,6 +79,8 @@ struct _ChattySettingsDialog GtkWidget *new_account_id_entry; GtkWidget *new_password_entry; + GtkWidget *delivery_reports_switch; + GtkWidget *send_receipts_switch; GtkWidget *message_archive_switch; GtkWidget *message_carbons_row; @@ -379,7 +381,7 @@ settings_delete_account_clicked_cb (ChattySettingsDialog *self) gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("Delete account %s?"), - chatty_account_get_username (CHATTY_ACCOUNT (self->selected_account))); + chatty_item_get_username (CHATTY_ITEM (self->selected_account))); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT); @@ -411,6 +413,14 @@ settings_pp_details_delete_cb (ChattySettingsDialog *self) settings_delete_account_clicked_cb (self); } +static void +sms_mms_settings_row_activated_cb (ChattySettingsDialog *self) +{ + g_assert (CHATTY_IS_SETTINGS_DIALOG (self)); + + gtk_stack_set_visible_child_name (GTK_STACK (self->main_stack), "message-settings-view"); +} + static void settings_pw_entry_icon_clicked_cb (ChattySettingsDialog *self, GtkEntryIconPosition icon_pos, @@ -640,7 +650,7 @@ chatty_account_row_new (ChattyAccount *account) CHATTY_PROTOCOL_TELEGRAM | CHATTY_PROTOCOL_DELTA | CHATTY_PROTOCOL_THREEPL | - CHATTY_PROTOCOL_SMS)) + CHATTY_PROTOCOL_MMS_SMS)) return NULL; spinner = gtk_spinner_new (); @@ -663,7 +673,7 @@ chatty_account_row_new (ChattyAccount *account) account_enabled_switch, "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); - hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (row), chatty_account_get_username (CHATTY_ACCOUNT (account))); + hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (row), chatty_item_get_username (CHATTY_ITEM (account))); hdy_action_row_set_subtitle (row, chatty_account_get_protocol_name (CHATTY_ACCOUNT (account))); gtk_container_add (GTK_CONTAINER (row), account_enabled_switch); hdy_action_row_set_activatable_widget (row, NULL); @@ -690,9 +700,6 @@ chatty_settings_dialog_populate_account_list (ChattySettingsDialog *self) account = g_list_model_get_item (model, i); - if (chatty_item_is_sms (CHATTY_ITEM (account))) - continue; - row = chatty_account_row_new (account); if (!row) @@ -750,6 +757,10 @@ chatty_settings_dialog_constructed (GObject *object) self->return_sends_switch, "active", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + g_object_bind_property (settings, "request-sms-delivery-reports", + self->delivery_reports_switch, "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + chatty_settings_dialog_populate_account_list (self); } @@ -801,6 +812,8 @@ chatty_settings_dialog_class_init (ChattySettingsDialogClass *klass) gtk_widget_class_bind_template_child (widget_class, ChattySettingsDialog, new_account_id_entry); gtk_widget_class_bind_template_child (widget_class, ChattySettingsDialog, new_password_entry); + gtk_widget_class_bind_template_child (widget_class, ChattySettingsDialog, delivery_reports_switch); + gtk_widget_class_bind_template_child (widget_class, ChattySettingsDialog, send_receipts_switch); gtk_widget_class_bind_template_child (widget_class, ChattySettingsDialog, message_archive_switch); gtk_widget_class_bind_template_child (widget_class, ChattySettingsDialog, message_carbons_row); @@ -822,6 +835,7 @@ chatty_settings_dialog_class_init (ChattySettingsDialogClass *klass) gtk_widget_class_bind_template_callback (widget_class, chatty_settings_save_clicked_cb); gtk_widget_class_bind_template_callback (widget_class, settings_pp_details_changed_cb); gtk_widget_class_bind_template_callback (widget_class, settings_pp_details_delete_cb); + gtk_widget_class_bind_template_callback (widget_class, sms_mms_settings_row_activated_cb); gtk_widget_class_bind_template_callback (widget_class, account_list_row_activated_cb); gtk_widget_class_bind_template_callback (widget_class, chatty_settings_back_clicked_cb); gtk_widget_class_bind_template_callback (widget_class, chatty_settings_cancel_clicked_cb); diff --git a/src/matrix/chatty-ma-account.c b/src/matrix/chatty-ma-account.c index 50b02a1..cca2656 100644 --- a/src/matrix/chatty-ma-account.c +++ b/src/matrix/chatty-ma-account.c @@ -62,6 +62,7 @@ struct _ChattyMaAccount gboolean homeserver_valid; gboolean account_enabled; + gboolean avatar_is_loading; gboolean is_loading; gboolean save_account_pending; gboolean save_password_pending; @@ -74,6 +75,45 @@ struct _ChattyMaAccount G_DEFINE_TYPE (ChattyMaAccount, chatty_ma_account, CHATTY_TYPE_ACCOUNT) +static void +ma_account_get_avatar_pixbuf_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(ChattyMaAccount) self = user_data; + g_autoptr(GError) error = NULL; + GdkPixbuf *pixbuf; + + g_assert (CHATTY_IS_MA_ACCOUNT (self)); + + pixbuf = matrix_utils_get_pixbuf_finish (result, &error); + + self->avatar_is_loading = FALSE; + if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Error loading avatar file: %s", error->message); + + if (!error) { + g_set_object (&self->avatar, pixbuf); + g_signal_emit_by_name (self, "avatar-changed"); + } +} + +static void +ma_account_get_avatar_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(ChattyMaAccount) self = user_data; + + self->avatar_is_loading = FALSE; + + if (matrix_api_get_file_finish (self->matrix_api, result, NULL)) { + g_clear_object (&self->avatar); + g_signal_emit_by_name (self, "avatar-changed"); + chatty_history_update_user (self->history_db, CHATTY_ACCOUNT (self)); + } +} + static ChattyMaChat * matrix_find_chat_with_id (ChattyMaAccount *self, const char *room_id, @@ -157,7 +197,7 @@ matrix_parse_room_data (ChattyMaAccount *self, chat = matrix_find_chat_with_id (self, room_id->data, &index); room_data = matrix_utils_json_object_get_object (joined_rooms, room_id->data); - CHATTY_TRACE_MSG ("joined room: %s, new: %d", room_id->data, !!chat); + CHATTY_TRACE (room_id->data, "joined room, new: %d, room:", !!chat); if (!chat) { chat = g_object_new (CHATTY_TYPE_MA_CHAT, "room-id", room_id->data, NULL); @@ -200,8 +240,6 @@ handle_get_homeserver (ChattyMaAccount *self, JsonObject *object, GError *error) { - CHATTY_ENTRY; - g_assert (CHATTY_IS_MA_ACCOUNT (self)); if (error) { @@ -213,8 +251,6 @@ handle_get_homeserver (ChattyMaAccount *self, g_warning ("Couldn't connect to ‘/.well-known/matrix/client’ "); matrix_api_set_homeserver (self->matrix_api, "https://chat.librem.one"); } - - CHATTY_EXIT; } static void @@ -235,8 +271,6 @@ handle_password_login (ChattyMaAccount *self, JsonObject *object, GError *error) { - CHATTY_ENTRY; - g_assert (CHATTY_IS_MA_ACCOUNT (self)); /* If no error, Api is informing us that logging in succeeded. @@ -300,8 +334,6 @@ handle_password_login (ChattyMaAccount *self, self->status = CHATTY_CONNECTED; g_object_notify (G_OBJECT (self), "status"); } - - CHATTY_EXIT; } static void @@ -489,30 +521,6 @@ chatty_ma_account_get_status (ChattyAccount *account) return self->status; } -static const char * -chatty_ma_account_get_username (ChattyAccount *account) -{ - ChattyMaAccount *self = (ChattyMaAccount *)account; - - g_assert (CHATTY_IS_MA_ACCOUNT (self)); - - if (matrix_api_get_username (self->matrix_api)) - return matrix_api_get_username (self->matrix_api); - - return ""; -} - -static void -chatty_ma_account_set_username (ChattyAccount *account, - const char *username) -{ - ChattyMaAccount *self = (ChattyMaAccount *)account; - - g_assert (CHATTY_IS_MA_ACCOUNT (self)); - - matrix_api_set_username (self->matrix_api, username); -} - static gboolean chatty_ma_account_get_enabled (ChattyAccount *account) { @@ -539,16 +547,16 @@ chatty_ma_account_set_enabled (ChattyAccount *account, if (!self->matrix_enc && enable) { CHATTY_TRACE_MSG ("Create new enc. user: %s has pickle: %d, has key: %d", - chatty_account_get_username (account), FALSE, FALSE); + chatty_item_get_username (CHATTY_ITEM (account)), FALSE, FALSE); self->matrix_enc = matrix_enc_new (self->matrix_db, NULL, NULL); matrix_api_set_enc (self->matrix_api, self->matrix_enc); } self->account_enabled = enable; network_monitor = g_network_monitor_get_default (); - CHATTY_TRACE_MSG ("Enable account %s: %d, is loading: %d", - chatty_account_get_username (account), - enable, self->is_loading); + CHATTY_TRACE (chatty_item_get_username (CHATTY_ITEM (account)), + "Enable account: %d, is loading: %d, user:", + enable, self->is_loading); if (self->account_enabled && g_network_monitor_get_connectivity (network_monitor) == G_NETWORK_CONNECTIVITY_FULL) { @@ -625,16 +633,13 @@ chatty_ma_account_connect (ChattyAccount *account, { ChattyMaAccount *self = (ChattyMaAccount *)account; - CHATTY_ENTRY; - g_assert (CHATTY_IS_MA_ACCOUNT (self)); if (!chatty_account_get_enabled (account)) - CHATTY_EXIT; + return; g_clear_handle_id (&self->connect_id, g_source_remove); self->connect_id = g_timeout_add (300, account_connect, g_object_ref (account)); - CHATTY_EXIT; } static void @@ -817,6 +822,30 @@ chatty_ma_account_set_name (ChattyItem *item, self->name = g_strdup (name); } +static const char * +chatty_ma_account_get_username (ChattyItem *item) +{ + ChattyMaAccount *self = (ChattyMaAccount *)item; + + g_assert (CHATTY_IS_MA_ACCOUNT (self)); + + if (matrix_api_get_username (self->matrix_api)) + return matrix_api_get_username (self->matrix_api); + + return ""; +} + +static void +chatty_ma_account_set_username (ChattyItem *item, + const char *username) +{ + ChattyMaAccount *self = (ChattyMaAccount *)item; + + g_assert (CHATTY_IS_MA_ACCOUNT (self)); + + matrix_api_set_username (self->matrix_api, username); +} + static ChattyFileInfo * chatty_ma_account_get_avatar_file (ChattyItem *item) { @@ -830,6 +859,88 @@ chatty_ma_account_get_avatar_file (ChattyItem *item) return NULL; } +static GdkPixbuf * +chatty_ma_account_get_avatar (ChattyItem *item) +{ + ChattyMaAccount *self = (ChattyMaAccount *)item; + + g_assert (CHATTY_IS_MA_ACCOUNT (self)); + + if (self->avatar) + return self->avatar; + + if (!self->avatar_file || !self->avatar_file->url || + self->avatar_is_loading) + return NULL; + + self->avatar_is_loading = TRUE; + if (self->avatar_file->path) { + g_autofree char *path = NULL; + path = g_build_filename (g_get_user_cache_dir (), "chatty", + self->avatar_file->path, NULL); + + matrix_utils_get_pixbuf_async (path, + NULL, + ma_account_get_avatar_pixbuf_cb, + g_object_ref (self)); + + } else { + matrix_api_get_file_async (self->matrix_api, NULL, self->avatar_file, + NULL, NULL, + ma_account_get_avatar_cb, + g_object_ref (self)); + } + + return NULL; +} + +static void +ma_account_set_user_avatar_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + ChattyMaAccount *self; + g_autoptr(GTask) task = user_data; + GError *error = NULL; + + g_assert (G_IS_TASK (task)); + + self = g_task_get_source_object (task); + g_assert (CHATTY_MA_ACCOUNT (self)); + + matrix_api_set_user_avatar_finish (self->matrix_api, result, &error); + + if (error) + g_task_return_error (task, error); + else + g_task_return_boolean (task, TRUE); +} + +static void +chatty_ma_account_set_avatar_async (ChattyItem *item, + const char *file_name, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + ChattyMaAccount *self = (ChattyMaAccount *)item; + g_autoptr(GTask) task = NULL; + + g_assert (CHATTY_IS_MA_ACCOUNT (self)); + + task = g_task_new (self, cancellable, callback, user_data); + + if (!file_name && !chatty_item_get_avatar_file (item)) + { + g_task_return_boolean (task, TRUE); + return; + } + + matrix_api_set_user_avatar_async (self->matrix_api, file_name, cancellable, + ma_account_set_user_avatar_cb, + g_steal_pointer (&task)); +} + static void chatty_ma_account_finalize (GObject *object) { @@ -867,12 +978,14 @@ chatty_ma_account_class_init (ChattyMaAccountClass *klass) item_class->get_protocols = chatty_ma_account_get_protocols; item_class->get_name = chatty_ma_account_get_name; item_class->set_name = chatty_ma_account_set_name; + item_class->get_username = chatty_ma_account_get_username; + item_class->set_username = chatty_ma_account_set_username; item_class->get_avatar_file = chatty_ma_account_get_avatar_file; + item_class->get_avatar = chatty_ma_account_get_avatar; + item_class->set_avatar_async = chatty_ma_account_set_avatar_async; account_class->get_protocol_name = chatty_ma_account_get_protocol_name; account_class->get_status = chatty_ma_account_get_status; - account_class->get_username = chatty_ma_account_get_username; - account_class->set_username = chatty_ma_account_set_username; account_class->get_enabled = chatty_ma_account_get_enabled; account_class->set_enabled = chatty_ma_account_set_enabled; account_class->get_password = chatty_ma_account_get_password; @@ -906,7 +1019,7 @@ chatty_ma_account_new (const char *username, self = g_object_new (CHATTY_TYPE_MA_ACCOUNT, NULL); - chatty_account_set_username (CHATTY_ACCOUNT (self), username); + chatty_item_set_username (CHATTY_ITEM (self), username); chatty_account_set_password (CHATTY_ACCOUNT (self), password); return self; @@ -943,19 +1056,19 @@ ma_account_get_value (const char *str, } ChattyMaAccount * -chatty_ma_account_new_secret (gpointer secret_item) +chatty_ma_account_new_secret (gpointer secret_retrievable) { ChattyMaAccount *self = NULL; g_autoptr(GHashTable) attributes = NULL; - SecretItem *item = secret_item; + SecretRetrievable *item = secret_retrievable; g_autoptr(SecretValue) value = NULL; const char *username, *homeserver, *credentials = NULL; char *password, *token, *device_id; char *password_str, *token_str = NULL; - g_return_val_if_fail (SECRET_IS_ITEM (item), NULL); + g_return_val_if_fail (SECRET_IS_RETRIEVABLE (item), NULL); - value = secret_item_get_secret (item); + value = secret_retrievable_retrieve_secret_sync (item, NULL, NULL); if (value) credentials = secret_value_get_text (value); @@ -963,7 +1076,7 @@ chatty_ma_account_new_secret (gpointer secret_item) if (!credentials) return NULL; - attributes = secret_item_get_attributes (item); + attributes = secret_retrievable_get_attributes (item); username = g_hash_table_lookup (attributes, CHATTY_USERNAME_ATTRIBUTE); homeserver = g_hash_table_lookup (attributes, CHATTY_SERVER_ATTRIBUTE); @@ -1021,9 +1134,9 @@ db_load_account_cb (GObject *object, const char *pickle; pickle = g_object_get_data (G_OBJECT (task), "pickle"); - CHATTY_TRACE_MSG ("Create new enc. user: %s has pickle: %d, has key: %d", - chatty_account_get_username (CHATTY_ACCOUNT (self)), - !!pickle, !!self->pickle_key); + CHATTY_TRACE (chatty_item_get_username (CHATTY_ITEM (self)), + "Create new enc. has pickle: %d, has key: %d, user:", + !!pickle, !!self->pickle_key); self->matrix_enc = matrix_enc_new (self->matrix_db, pickle, self->pickle_key); matrix_api_set_enc (self->matrix_api, self->matrix_enc); if (!pickle) @@ -1034,16 +1147,16 @@ db_load_account_cb (GObject *object, if (!matrix_db_load_account_finish (self->matrix_db, result, &error)) { if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("Error loading account %s: %s", - chatty_account_get_username (CHATTY_ACCOUNT (self)), + chatty_item_get_username (CHATTY_ITEM (self)), error->message); return; } enabled = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (task), "enabled")); self->next_batch = g_strdup (g_object_get_data (G_OBJECT (task), "batch")); - CHATTY_TRACE_MSG ("Loaded %s from db. enabled: %d, has next-batch: %d", - chatty_account_get_username (CHATTY_ACCOUNT (self)), - !!enabled, !!self->next_batch); + CHATTY_TRACE (chatty_item_get_username (CHATTY_ITEM (self)), + "Loaded from db. enabled: %d, has next-batch: %d, user:", + !!enabled, !!self->next_batch); self->is_loading = TRUE; matrix_api_set_next_batch (self->matrix_api, self->next_batch); @@ -1066,9 +1179,9 @@ db_load_chats_cb (GObject *object, chats = chatty_history_get_chats_finish (self->history_db, result, &error); self->db_chat_list = chats; - CHATTY_TRACE_MSG ("%s Loaded %u chats from db", - chatty_account_get_username (CHATTY_ACCOUNT (self)), - !chats ? 0 : chats->len); + CHATTY_TRACE (chatty_item_get_username (CHATTY_ITEM (self)), + "Loaded %u chats from db, user:", + !chats ? 0 : chats->len); if (error) g_warning ("Error getting chats: %s", error->message); @@ -1146,7 +1259,7 @@ ma_account_db_save_cb (GObject *object, status = matrix_db_save_account_finish (self->matrix_db, result, &error); if (error || !status) CHATTY_TRACE_MSG ("Saving %s failed", - chatty_account_get_username (CHATTY_ACCOUNT (self))); + chatty_item_get_username (CHATTY_ITEM (self))); if (error || !status) self->save_account_pending = TRUE; @@ -1167,8 +1280,6 @@ ma_account_save_cb (GObject *object, GError *error = NULL; gboolean status; - CHATTY_ENTRY; - g_assert (G_IS_ASYNC_RESULT (result)); g_assert (G_IS_TASK (task)); @@ -1198,8 +1309,6 @@ ma_account_save_cb (GObject *object, } else { g_task_return_boolean (task, status); } - - CHATTY_EXIT; } void @@ -1213,7 +1322,7 @@ chatty_ma_account_save_async (ChattyMaAccount *self, g_return_if_fail (CHATTY_IS_MA_ACCOUNT (self)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - g_return_if_fail (*chatty_account_get_username (CHATTY_ACCOUNT (self))); + g_return_if_fail (*chatty_item_get_username (CHATTY_ITEM (self))); g_return_if_fail (*chatty_account_get_password (CHATTY_ACCOUNT (self))); g_return_if_fail (*chatty_ma_account_get_homeserver (self)); diff --git a/src/matrix/chatty-ma-account.h b/src/matrix/chatty-ma-account.h index 888c912..bc05d82 100644 --- a/src/matrix/chatty-ma-account.h +++ b/src/matrix/chatty-ma-account.h @@ -25,7 +25,7 @@ G_DECLARE_FINAL_TYPE (ChattyMaAccount, chatty_ma_account, CHATTY, MA_ACCOUNT, Ch ChattyMaAccount *chatty_ma_account_new (const char *username, const char *password); -ChattyMaAccount *chatty_ma_account_new_secret (gpointer secret_item); +ChattyMaAccount *chatty_ma_account_new_secret (gpointer secret_retrievable); void chatty_ma_account_set_history_db (ChattyMaAccount *self, gpointer history_db); void chatty_ma_account_set_db (ChattyMaAccount *self, diff --git a/src/matrix/chatty-ma-buddy.c b/src/matrix/chatty-ma-buddy.c index 1000db9..81b5567 100644 --- a/src/matrix/chatty-ma-buddy.c +++ b/src/matrix/chatty-ma-buddy.c @@ -126,6 +126,29 @@ chatty_ma_buddy_set_name (ChattyItem *item, self->name = g_strdup (name); } +/* + * chatty_ma_buddy_get_username: + * + * Get the user id of @item. The id is usually a + * fully qualified Matrix ID (@user:example.com), + * but it can also be the username alone (user). + * + * Returns: (transfer none): the id of Buddy. + * or an empty string if not found or on error. + */ +static const char * +chatty_ma_buddy_get_username (ChattyItem *item) +{ + ChattyMaBuddy *self = (ChattyMaBuddy *)item; + + g_assert (CHATTY_IS_MA_BUDDY (self)); + + if (self->matrix_id) + return self->matrix_id; + + return ""; +} + static GdkPixbuf * chatty_ma_buddy_get_avatar (ChattyItem *item) { @@ -165,6 +188,7 @@ chatty_ma_buddy_class_init (ChattyMaBuddyClass *klass) item_class->matches = chatty_ma_buddy_matches; item_class->get_name = chatty_ma_buddy_get_name; item_class->set_name = chatty_ma_buddy_set_name; + item_class->get_username = chatty_ma_buddy_get_username; item_class->get_avatar = chatty_ma_buddy_get_avatar; /** @@ -209,28 +233,6 @@ chatty_ma_buddy_new (const char *matrix_id, return self; } -/** - * chatty_ma_buddy_get_id: - * @self: a #ChattyMaBuddy - * - * Get the user id of @self. The id is usually a - * fully qualified Matrix ID (@user:example.com), - * but it can also be the username alone (user). - * - * Returns: (transfer none): the id of Buddy. - * or an empty string if not found or on error. - */ -const char * -chatty_ma_buddy_get_id (ChattyMaBuddy *self) -{ - g_return_val_if_fail (CHATTY_IS_MA_BUDDY (self), ""); - - if (self->matrix_id) - return self->matrix_id; - - return ""; -} - guint chatty_ma_buddy_get_id_hash (ChattyMaBuddy *self) { diff --git a/src/matrix/chatty-ma-chat.c b/src/matrix/chatty-ma-chat.c index 86a4e8c..0b4f1bf 100644 --- a/src/matrix/chatty-ma-chat.c +++ b/src/matrix/chatty-ma-chat.c @@ -80,6 +80,8 @@ struct _ChattyMaChat guint is_sending_message : 1; guint notification_shown : 1; + guint user_list_loading : 1; + guint user_list_loaded : 1; guint state_is_sync : 1; guint state_is_syncing : 1; /* Set if the complete buddy list is loaded */ @@ -147,7 +149,7 @@ chatty_mat_chat_update_name (ChattyMaChat *self) buddy = g_list_model_get_item (G_LIST_MODEL (self->buddy_list), i); /* Don't add self to create room name */ - if (g_strcmp0 (chatty_ma_buddy_get_id (CHATTY_MA_BUDDY (buddy)), chatty_account_get_username (self->account)) == 0) { + if (g_strcmp0 (chatty_item_get_username (buddy), chatty_item_get_username (CHATTY_ITEM (self))) == 0) { count--; continue; } @@ -195,7 +197,7 @@ ma_chat_find_buddy (ChattyMaChat *self, buddy = g_list_model_get_item (model, i); if (id_hash == chatty_ma_buddy_get_id_hash (buddy) && - g_str_equal (chatty_ma_buddy_get_id (buddy), matrix_id)) { + g_str_equal (chatty_item_get_username (CHATTY_ITEM (buddy)), matrix_id)) { if (index) *index = i; @@ -295,60 +297,37 @@ ma_chat_new_file (ChattyMaChat *self, return file; } -#if 0 static void handle_m_room_member (ChattyMaChat *self, - JsonObject *object, - GListStore *members_list) + JsonObject *object) { GListModel *model; ChattyMaBuddy *buddy; JsonObject *content; - const char *value, *membership, *sender, *name; + const char *membership, *sender, *name; g_assert (CHATTY_IS_MA_CHAT (self)); g_assert (object); - g_assert (G_IS_LIST_STORE (members_list)); - - value = matrix_utils_json_object_get_string (object, "room_id"); - if (g_strcmp0 (value, self->room_id) != 0) { - g_warning ("room_id '%s' doesn't match '%s", value, self->room_id); - return; - } sender = matrix_utils_json_object_get_string (object, "sender"); content = matrix_utils_json_object_get_object (object, "content"); membership = matrix_utils_json_object_get_string (content, "membership"); - model = G_LIST_MODEL (members_list); + model = G_LIST_MODEL (self->buddy_list); buddy = ma_chat_find_buddy (self, model, sender, NULL); - - if (!buddy && members_list != self->buddy_list) { - model = G_LIST_MODEL (self->buddy_list); - buddy = ma_chat_find_buddy (self, model, sender, NULL); - } - name = matrix_utils_json_object_get_string (content, "displayname"); - if (buddy) - chatty_item_set_name (CHATTY_ITEM (buddy), name); if (g_strcmp0 (membership, "join") == 0) { - if (buddy && model == (gpointer)members_list) - return; - - if (buddy) - g_list_store_append (members_list, buddy); - else - buddy = ma_chat_add_buddy (self, members_list, sender); + if (!buddy) + buddy = ma_chat_add_buddy (self, self->buddy_list, sender); chatty_item_set_name (CHATTY_ITEM (buddy), name); - } else if (buddy && - g_strcmp0 (membership, "leave") == 0) { self->keys_claimed = FALSE; - chatty_utils_remove_list_item (members_list, buddy); + } else if (buddy && g_strcmp0 (membership, "leave") == 0) { + chatty_utils_remove_list_item (self->buddy_list, buddy); g_clear_pointer (&self->generated_name, g_free); + self->keys_claimed = FALSE; } } -#endif static void handle_m_room_name (ChattyMaChat *self, @@ -727,15 +706,13 @@ matrix_send_message_from_queue (ChattyMaChat *self) { g_autoptr(ChattyMessage) message = NULL; - CHATTY_ENTRY; - g_assert (CHATTY_IS_MA_CHAT (self)); if (self->is_sending_message || !self->message_queue || !self->message_queue->length || self->message_timeout_id) - CHATTY_EXIT; + return; message = g_queue_pop_head (self->message_queue); self->is_sending_message = TRUE; @@ -743,7 +720,6 @@ matrix_send_message_from_queue (ChattyMaChat *self) self->room_id, message, ma_chat_send_message_cb, g_object_ref (self)); - CHATTY_EXIT; } static void @@ -821,8 +797,6 @@ upload_out_group_key_cb (GObject *obj, ChattyMaChat *self = user_data; g_autoptr(GError) error = NULL; - CHATTY_ENTRY; - g_assert (CHATTY_IS_MA_CHAT (self)); self->claiming_keys = FALSE; @@ -832,11 +806,10 @@ upload_out_group_key_cb (GObject *obj, if (error) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("error uploading group keys: %s", error->message); - CHATTY_EXIT; + return; } matrix_send_message_from_queue (self); - CHATTY_EXIT; } static void @@ -850,8 +823,6 @@ claim_key_cb (GObject *obj, g_autoptr(GError) error = NULL; JsonObject *object; - CHATTY_ENTRY; - g_assert (CHATTY_IS_MA_CHAT (self)); root = matrix_api_claim_keys_finish (self->matrix_api, result, &error); @@ -860,7 +831,7 @@ claim_key_cb (GObject *obj, self->claiming_keys = FALSE; if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("error: %s", error->message); - CHATTY_EXIT; + return; } object = matrix_utils_json_object_get_object (root, "one_time_keys"); @@ -888,7 +859,6 @@ claim_key_cb (GObject *obj, G_LIST_MODEL (self->buddy_list), upload_out_group_key_cb, self); - CHATTY_EXIT; } static void @@ -902,8 +872,6 @@ query_key_cb (GObject *obj, g_autoptr(GError) error = NULL; JsonObject *object; - CHATTY_ENTRY; - g_assert (CHATTY_IS_MA_CHAT (self)); g_return_if_fail (!self->keys_claimed); @@ -913,7 +881,7 @@ query_key_cb (GObject *obj, self->claiming_keys = FALSE; if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("error: %s", error->message); - CHATTY_EXIT; + return; } object = matrix_utils_json_object_get_object (root, "device_keys"); @@ -940,7 +908,54 @@ query_key_cb (GObject *obj, matrix_api_claim_keys_async (self->matrix_api, G_LIST_MODEL (self->buddy_list), claim_key_cb, self); - CHATTY_EXIT; +} + +static void +get_chat_users_cb (GObject *obj, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(ChattyMaChat) self = user_data; + g_autoptr(JsonObject) object = NULL; + g_autoptr(GList) members = NULL; + g_autoptr(GError) error = NULL; + JsonObject *joined; + + g_assert (CHATTY_IS_MA_CHAT (self)); + + object = matrix_api_get_room_users_finish (self->matrix_api, result, &error); + + self->user_list_loading = FALSE; + self->user_list_loaded = !error; + + if (error) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Error loading chat members: %s", error->message); + + return; + } + + joined = matrix_utils_json_object_get_object (object, "joined"); + members = json_object_get_members (joined); + + for (GList *member = members; member; member = member->next) { + ChattyMaBuddy *buddy; + JsonObject *data; + const char *name; + + buddy = ma_chat_find_buddy (self, G_LIST_MODEL (self->buddy_list), member->data, NULL); + if (!buddy) + buddy = ma_chat_add_buddy (self, self->buddy_list, member->data); + + data = json_object_get_object_member (joined, member->data); + name = matrix_utils_json_object_get_string (data, "display_name"); + chatty_item_set_name (CHATTY_ITEM (buddy), name); + }; + + self->claiming_keys = TRUE; + matrix_api_query_keys_async (self->matrix_api, + G_LIST_MODEL (self->buddy_list), + NULL, query_key_cb, self); } #if 0 @@ -1050,12 +1065,34 @@ get_room_name_cb (GObject *obj, self->room_name = g_strdup (name); chatty_history_update_chat (self->history_db, CHATTY_CHAT (self)); - CHATTY_TRACE_MSG ("Got room name, room: %s (%s)", - self->room_id, chatty_item_get_name (CHATTY_ITEM (self))); + CHATTY_TRACE (self->room_id, "Got room name, room:"); g_object_notify (G_OBJECT (self), "name"); } +static void +get_room_encryption_cb (GObject *obj, + GAsyncResult *result, + gpointer user_data) +{ + ChattyMaChat *self = user_data; + g_autoptr(GError) error = NULL; + + g_assert (CHATTY_IS_MA_CHAT (self)); + + self->encryption = matrix_api_get_room_encryption_finish (self->matrix_api, result, &error); + + CHATTY_TRACE (self->room_id, "Got room encryption state: encrypted: %d, room:", + !!self->encryption); + g_object_notify (G_OBJECT (self), "encrypt"); + + CHATTY_TRACE (self->room_id, "Getting room name for"); + matrix_api_get_room_name_async (self->matrix_api, + self->room_id, + get_room_name_cb, + self); +} + static void parse_chat_array (ChattyMaChat *self, JsonArray *array) @@ -1086,7 +1123,7 @@ parse_chat_array (ChattyMaChat *self, buddy = ma_chat_add_buddy (self, self->buddy_list, sender); if (!self->self_buddy && - g_strcmp0 (sender, chatty_chat_get_username (CHATTY_CHAT (self))) == 0) + g_strcmp0 (sender, chatty_item_get_username (CHATTY_ITEM (self))) == 0) g_set_object (&self->self_buddy, buddy); if (g_str_equal (type, "m.room.name")) { @@ -1097,6 +1134,8 @@ parse_chat_array (ChattyMaChat *self, handle_m_room_encryption (self, object); } else if (g_str_equal (type, "m.room.encrypted")) { handle_m_room_encrypted (self, buddy, object); + } else if (g_str_equal (type, "m.room.member")) { + handle_m_room_member (self, object); } } @@ -1221,8 +1260,8 @@ db_room_room_cb (GObject *object, g_free (self->prev_batch); self->prev_batch = matrix_db_load_room_finish (self->matrix_db, result, &error); - CHATTY_TRACE_MSG ("Load chat %s from db, success: %d, has prev-batch: %d", - self->room_id, !error, !!self->prev_batch); + CHATTY_TRACE (self->room_id, "Load from db, success: %d, has prev-batch: %d, chat:", + !error, !!self->prev_batch); if (error) g_warning ("Error loading prev batch: %s", error->message); @@ -1310,16 +1349,6 @@ chatty_ma_chat_get_chat_name (ChattyChat *chat) return self->room_id; } -static const char * -chatty_ma_chat_get_username (ChattyChat *chat) -{ - ChattyMaChat *self = (ChattyMaChat *)chat; - - g_assert (CHATTY_IS_MA_CHAT (self)); - - return matrix_api_get_username (self->matrix_api); -} - static void chatty_ma_chat_real_past_messages (ChattyChat *chat, int count) @@ -1335,8 +1364,7 @@ chatty_ma_chat_real_past_messages (ChattyChat *chat, if (self->history_is_loading) return; - CHATTY_TRACE_MSG ("Loading %d past messages from %s(%s)", count, self->room_id, - chatty_item_get_name (CHATTY_ITEM (chat))); + CHATTY_TRACE (self->room_id, "Loading %d past messages from", count); self->history_is_loading = TRUE; g_object_notify (G_OBJECT (self), "loading-history"); @@ -1523,8 +1551,6 @@ chatty_ma_chat_send_message_async (ChattyChat *chat, { ChattyMaChat *self = (ChattyMaChat *)chat; - CHATTY_ENTRY; - g_assert (CHATTY_IS_MA_CHAT (self)); g_assert (CHATTY_IS_MESSAGE (message)); @@ -1537,11 +1563,17 @@ chatty_ma_chat_send_message_async (ChattyChat *chat, if (chatty_chat_get_encryption (chat) != CHATTY_ENCRYPTION_ENABLED || self->keys_claimed) matrix_send_message_from_queue (self); - else if (!self->state_is_syncing && !self->claiming_keys) + else if (!self->user_list_loaded && !self->user_list_loading) { + self->user_list_loading = TRUE; + matrix_api_get_room_users_async (self->matrix_api, self->room_id, + get_chat_users_cb, + g_object_ref (self)); + } else if (!self->state_is_syncing && !self->claiming_keys) { + self->claiming_keys = TRUE; matrix_api_query_keys_async (self->matrix_api, G_LIST_MODEL (self->buddy_list), NULL, query_key_cb, self); - CHATTY_EXIT; + } } static void @@ -1613,6 +1645,16 @@ chatty_ma_chat_get_name (ChattyItem *item) return ""; } +static const char * +chatty_ma_chat_get_username (ChattyItem *item) +{ + ChattyMaChat *self = (ChattyMaChat *)item; + + g_assert (CHATTY_IS_MA_CHAT (self)); + + return matrix_api_get_username (self->matrix_api); +} + static ChattyItemState chatty_ma_chat_get_state (ChattyItem *item) { @@ -1749,6 +1791,7 @@ chatty_ma_chat_class_init (ChattyMaChatClass *klass) object_class->finalize = chatty_ma_chat_finalize; item_class->get_name = chatty_ma_chat_get_name; + item_class->get_username = chatty_ma_chat_get_username; item_class->get_state = chatty_ma_chat_get_state; item_class->set_state = chatty_ma_chat_set_state; item_class->get_protocols = chatty_ma_chat_get_protocols; @@ -1757,7 +1800,6 @@ chatty_ma_chat_class_init (ChattyMaChatClass *klass) chat_class->is_im = chatty_ma_chat_is_im; chat_class->get_chat_name = chatty_ma_chat_get_chat_name; - chat_class->get_username = chatty_ma_chat_get_username; chat_class->load_past_messages = chatty_ma_chat_real_past_messages; chat_class->is_loading_history = chatty_ma_chat_is_loading_history; chat_class->get_messages = chatty_ma_chat_get_messages; @@ -1870,13 +1912,11 @@ chatty_ma_chat_set_data (ChattyMaChat *self, if (!self->state_is_sync && !self->state_is_syncing && self->matrix_api && self->matrix_enc) { self->state_is_syncing = TRUE; - CHATTY_TRACE_MSG ("Getting room name for '%s(%s)'", self->room_id, - chatty_item_get_name (CHATTY_ITEM (self))); - - matrix_api_get_room_name_async (self->matrix_api, - self->room_id, - get_room_name_cb, - self); + CHATTY_TRACE (self->room_id, "Getting encryption state for"); + matrix_api_get_room_encryption_async (self->matrix_api, + self->room_id, + get_room_encryption_cb, + self); } } diff --git a/src/matrix/matrix-api.c b/src/matrix/matrix-api.c index de0fde2..5ff559c 100644 --- a/src/matrix/matrix-api.c +++ b/src/matrix/matrix-api.c @@ -304,8 +304,6 @@ api_upload_group_keys_cb (GObject *obj, g_autoptr(JsonObject) object = NULL; GError *error = NULL; - CHATTY_ENTRY; - object = g_task_propagate_pointer (G_TASK (result), &error); if (error) { @@ -314,8 +312,6 @@ api_upload_group_keys_cb (GObject *obj, } else { g_task_return_boolean (task, TRUE); } - - CHATTY_EXIT; } static void @@ -358,6 +354,27 @@ matrix_get_room_name_cb (GObject *obj, } } +static void +matrix_get_room_encryption_cb (GObject *obj, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(GTask) task = user_data; + g_autoptr(JsonObject) object = NULL; + const char *encryption; + GError *error = NULL; + + object = g_task_propagate_pointer (G_TASK (result), &error); + + if (error && + !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && + !g_error_matches (error, MATRIX_ERROR, M_NOT_FOUND)) + g_warning ("Error loading encryption state: %s", error->message); + + encryption = matrix_utils_json_object_get_string (object, "algorithm"); + g_task_return_pointer (task, g_strdup (encryption), g_free); +} + static void matrix_get_members_cb (GObject *obj, GAsyncResult *result, @@ -367,8 +384,6 @@ matrix_get_members_cb (GObject *obj, JsonObject *object = NULL; GError *error = NULL; - CHATTY_ENTRY; - object = g_task_propagate_pointer (G_TASK (result), &error); if (error) { @@ -377,8 +392,6 @@ matrix_get_members_cb (GObject *obj, } else { g_task_return_pointer (task, object, (GDestroyNotify)json_object_unref); } - - CHATTY_EXIT; } static void @@ -453,8 +466,8 @@ schedule_resync (gpointer user_data) network_monitor = g_network_monitor_get_default (); connectivity = g_network_monitor_get_connectivity (network_monitor); - CHATTY_TRACE_MSG ("Schedule sync for user %s, sync now: %d", self->username, - connectivity == G_NETWORK_CONNECTIVITY_FULL); + CHATTY_TRACE (self->username, "Schedule sync. sync now: %d, user: ", + connectivity == G_NETWORK_CONNECTIVITY_FULL); if (connectivity == G_NETWORK_CONNECTIVITY_FULL) matrix_start_sync (self); @@ -478,7 +491,7 @@ handle_common_errors (MatrixApi *self, if (g_error_matches (error, MATRIX_ERROR, M_UNKNOWN_TOKEN) && self->password) { - CHATTY_TRACE_MSG ("Re-logging in %s", self->username); + CHATTY_TRACE (self->username, "Re-logging in "); self->login_success = FALSE; self->room_list_loaded = FALSE; g_clear_pointer (&self->access_token, matrix_utils_free_buffer); @@ -508,7 +521,7 @@ handle_common_errors (MatrixApi *self, if (g_network_monitor_get_connectivity (network_monitor) == G_NETWORK_CONNECTIVITY_FULL) { g_clear_handle_id (&self->resync_id, g_source_remove); - CHATTY_TRACE_MSG ("Schedule sync for user %s", self->username); + CHATTY_TRACE (self->username, "Schedule sync for user "); self->resync_id = g_timeout_add_seconds (URI_REQUEST_TIMEOUT, schedule_resync, self); return TRUE; @@ -620,13 +633,13 @@ matrix_upload_key_cb (GObject *obj, self->sync_failed = TRUE; self->callback (self->cb_object, self, MATRIX_UPLOAD_KEY, NULL, error); g_debug ("Error uploading key: %s", error->message); - CHATTY_EXIT; + return; } self->callback (self->cb_object, self, MATRIX_UPLOAD_KEY, root, NULL); object = matrix_utils_json_object_get_object (root, "one_time_key_counts"); - CHATTY_TRACE_MSG ("Uploaded %d keys", + CHATTY_TRACE_MSG ("Uploaded %ld keys", matrix_utils_json_object_get_int (object, "signed_curve25519")); if (!handle_one_time_keys (self, object) && @@ -1257,6 +1270,59 @@ matrix_api_get_room_state_finish (MatrixApi *self, return g_task_propagate_pointer (G_TASK (result), error); } +static void +matrix_get_room_users_cb (GObject *obj, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(GTask) task = user_data; + JsonObject *object; + GError *error = NULL; + + g_assert (G_IS_TASK (task)); + + object = g_task_propagate_pointer (G_TASK (result), &error); + + if (error) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_debug ("Error getting room members: %s", error->message); + g_task_return_error (task, error); + } else { + g_task_return_pointer (task, object, (GDestroyNotify)json_object_unref); + } +} + +void +matrix_api_get_room_users_async (MatrixApi *self, + const char *room_id, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autofree char *uri = NULL; + GTask *task; + + g_return_if_fail (MATRIX_IS_API (self)); + g_return_if_fail (room_id && *room_id); + + task = g_task_new (self, self->cancellable, callback, user_data); + + uri = g_strconcat ("/_matrix/client/r0/rooms/", room_id, "/joined_members", NULL); + matrix_net_send_json_async (self->matrix_net, -1, NULL, uri, SOUP_METHOD_GET, + NULL, self->cancellable, matrix_get_room_users_cb, task); +} + +JsonObject * +matrix_api_get_room_users_finish (MatrixApi *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (MATRIX_IS_API (self), NULL); + g_return_val_if_fail (G_IS_TASK (result), NULL); + g_return_val_if_fail (!error || !*error, NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} + void matrix_api_get_room_name_async (MatrixApi *self, const char *room_id, @@ -1287,6 +1353,36 @@ matrix_api_get_room_name_finish (MatrixApi *self, return g_task_propagate_pointer (G_TASK (result), error); } +void +matrix_api_get_room_encryption_async (MatrixApi *self, + const char *room_id, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autofree char *uri = NULL; + GTask *task; + + g_return_if_fail (MATRIX_IS_API (self)); + g_return_if_fail (room_id && *room_id); + + task = g_task_new (self, self->cancellable, callback, user_data); + uri = g_strconcat ("/_matrix/client/r0/rooms/", room_id, "/state/m.room.encryption", NULL); + matrix_net_send_json_async (self->matrix_net, -1, NULL, uri, SOUP_METHOD_GET, + NULL, self->cancellable, matrix_get_room_encryption_cb, task); +} + +char * +matrix_api_get_room_encryption_finish (MatrixApi *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (MATRIX_IS_API (self), NULL); + g_return_val_if_fail (G_IS_TASK (result), NULL); + g_return_val_if_fail (!error || !*error, NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} + void matrix_api_get_members_async (MatrixApi *self, const char *room_id, @@ -1419,7 +1515,7 @@ matrix_api_query_keys_async (MatrixApi *self, buddy = g_list_model_get_item (member_list, i); json_object_set_array_member (child, - chatty_ma_buddy_get_id (buddy), + chatty_item_get_username (CHATTY_ITEM (buddy)), json_array_new ()); } @@ -1483,7 +1579,7 @@ matrix_api_claim_keys_async (MatrixApi *self, if (key_json) json_object_set_object_member (child, - chatty_ma_buddy_get_id (buddy), + chatty_item_get_username (CHATTY_ITEM (buddy)), key_json); } @@ -1549,13 +1645,11 @@ matrix_api_get_file_finish (MatrixApi *self, GAsyncResult *result, GError **error) { - CHATTY_ENTRY; - g_return_val_if_fail (MATRIX_IS_API (self), FALSE); g_return_val_if_fail (G_IS_TASK (result), FALSE); g_return_val_if_fail (!error || !*error, FALSE); - CHATTY_RETURN (g_task_propagate_boolean (G_TASK (result), error)); + return g_task_propagate_boolean (G_TASK (result), error); } static void @@ -1590,7 +1684,7 @@ api_send_message_encrypted (MatrixApi *self, g_object_set_data_full (G_OBJECT (task), "message", g_object_ref (message), g_object_unref); - CHATTY_TRACE_MSG ("Sending encrypted message. room: %s, event id: %s", room_id, id); + CHATTY_TRACE (room_id, "Sending encrypted message. event id: %s, room:", id); uri = g_strdup_printf ("/_matrix/client/r0/rooms/%s/send/m.room.encrypted/%s", room_id, id); @@ -1620,7 +1714,7 @@ api_send_message (MatrixApi *self, g_object_set_data_full (G_OBJECT (task), "message", g_object_ref (message), g_object_unref); - CHATTY_TRACE_MSG ("Sending message. room: %s, event id: %s", room_id, id); + CHATTY_TRACE (room_id, "Sending message. event id: %s, room:", id); /* https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid */ uri = g_strdup_printf ("/_matrix/client/r0/rooms/%s/send/m.room.message/%s", room_id, id); @@ -1859,7 +1953,7 @@ matrix_api_get_user_info_async (MatrixApi *self, if (!user_id) user_id = self->username; - CHATTY_TRACE_MSG ("Getting user info: %s", user_id); + CHATTY_TRACE (user_id, "Getting user info: "); task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, g_strdup (user_id), g_free); @@ -1924,8 +2018,7 @@ matrix_api_set_name_async (MatrixApi *self, g_return_if_fail (MATRIX_IS_API (self)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - CHATTY_TRACE_MSG ("Setting name to '%s' for user '%s'", - name, self->username); + CHATTY_TRACE (self->username, "Setting name to '%s' for user ", name); if (name && *name) { root = json_object_new (); @@ -1952,6 +2045,72 @@ matrix_api_set_name_finish (MatrixApi *self, return g_task_propagate_boolean (G_TASK (result), error); } +static void +api_set_user_avatar_cb (GObject *obj, + GAsyncResult *result, + gpointer user_data) +{ + MatrixApi *self; + g_autoptr(GTask) task = user_data; + g_autoptr(JsonObject) object = NULL; + GError *error = NULL; + + g_assert (G_IS_TASK (task)); + + self = g_task_get_source_object (task); + g_assert (MATRIX_IS_API (self)); + + object = g_task_propagate_pointer (G_TASK (result), &error); + + CHATTY_TRACE_MSG ("Setting avatar success: %d", !error); + + if (error) + g_task_return_error (task, error); + else + g_task_return_boolean (task, TRUE); +} + +void +matrix_api_set_user_avatar_async (MatrixApi *self, + const char *file_name, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail (MATRIX_IS_API (self)); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (self, cancellable, callback, user_data); + + if (!file_name) { + g_autofree char *uri = NULL; + const char *data; + + data = "{\"avatar_url\":\"\"}"; + uri = g_strdup_printf ("/_matrix/client/r0/profile/%s/avatar_url", self->username); + matrix_net_send_data_async (self->matrix_net, 2, g_strdup (data), strlen (data), + uri, SOUP_METHOD_PUT, NULL, self->cancellable, + api_set_user_avatar_cb, g_steal_pointer (&task)); + } else { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "Setting new user avatar not implemented"); + } +} + +gboolean +matrix_api_set_user_avatar_finish (MatrixApi *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (MATRIX_IS_API (self), FALSE); + g_return_val_if_fail (G_IS_TASK (result), FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} + static void api_get_3pid_cb (GObject *obj, GAsyncResult *result, @@ -2021,7 +2180,7 @@ matrix_api_get_3pid_async (MatrixApi *self, g_return_if_fail (MATRIX_IS_API (self)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - CHATTY_TRACE_MSG ("Getting user 3pid: %s", self->username); + CHATTY_TRACE (self->username, "Getting 3pid of user "); task = g_task_new (self, cancellable, callback, user_data); matrix_net_send_json_async (self->matrix_net, 1, NULL, diff --git a/src/matrix/matrix-api.h b/src/matrix/matrix-api.h index 212147a..fc6c63b 100644 --- a/src/matrix/matrix-api.h +++ b/src/matrix/matrix-api.h @@ -70,6 +70,13 @@ void matrix_api_get_room_state_async (MatrixApi *self, JsonArray *matrix_api_get_room_state_finish (MatrixApi *self, GAsyncResult *result, GError **error); +void matrix_api_get_room_users_async (MatrixApi *self, + const char *room_id, + GAsyncReadyCallback callback, + gpointer user_data); +JsonObject *matrix_api_get_room_users_finish (MatrixApi *self, + GAsyncResult *result, + GError **error); void matrix_api_get_room_name_async (MatrixApi *self, const char *room_id, GAsyncReadyCallback callback, @@ -77,6 +84,13 @@ void matrix_api_get_room_name_async (MatrixApi *self, JsonObject *matrix_api_get_room_name_finish (MatrixApi *self, GAsyncResult *result, GError **error); +void matrix_api_get_room_encryption_async (MatrixApi *self, + const char *room_id, + GAsyncReadyCallback callback, + gpointer user_data); +char *matrix_api_get_room_encryption_finish (MatrixApi *self, + GAsyncResult *result, + GError **error); void matrix_api_get_members_async (MatrixApi *self, const char *room_id, GAsyncReadyCallback callback, @@ -177,6 +191,14 @@ void matrix_api_set_name_async (MatrixApi *self, gboolean matrix_api_set_name_finish (MatrixApi *self, GAsyncResult *result, GError **error); +void matrix_api_set_user_avatar_async (MatrixApi *self, + const char *file_name, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean matrix_api_set_user_avatar_finish (MatrixApi *self, + GAsyncResult *result, + GError **error); void matrix_api_get_3pid_async (MatrixApi *self, GCancellable *cancellable, GAsyncReadyCallback callback, diff --git a/src/matrix/matrix-db.c b/src/matrix/matrix-db.c index 0016daa..c40dec6 100644 --- a/src/matrix/matrix-db.c +++ b/src/matrix/matrix-db.c @@ -943,7 +943,7 @@ matrix_db_save_account_async (MatrixDb *self, g_task_set_task_data (task, matrix_db_save_account, NULL); object = G_OBJECT (task); - username = chatty_account_get_username (CHATTY_ACCOUNT (account)); + username = chatty_item_get_username (CHATTY_ITEM (account)); if (g_application_get_default ()) g_application_hold (g_application_get_default ()); @@ -990,7 +990,7 @@ matrix_db_load_account_async (MatrixDb *self, g_task_set_source_tag (task, matrix_db_load_account_async); g_task_set_task_data (task, matrix_db_load_account, NULL); - username = chatty_account_get_username (CHATTY_ACCOUNT (account)); + username = chatty_item_get_username (CHATTY_ITEM (account)); g_object_set_data_full (G_OBJECT (task), "device", g_strdup (device_id), g_free); g_object_set_data_full (G_OBJECT (task), "username", g_strdup (username), g_free); g_object_set_data_full (G_OBJECT (task), "account", g_object_ref (account), g_object_unref); @@ -1029,7 +1029,7 @@ matrix_db_save_room_async (MatrixDb *self, g_task_set_source_tag (task, matrix_db_save_room_async); g_task_set_task_data (task, matrix_db_save_room, NULL); - username = chatty_account_get_username (CHATTY_ACCOUNT (account)); + username = chatty_item_get_username (CHATTY_ITEM (account)); g_object_set_data_full (G_OBJECT (task), "room", g_strdup (room_id), g_free); g_object_set_data_full (G_OBJECT (task), "username", g_strdup (username), g_free); g_object_set_data_full (G_OBJECT (task), "account-id", g_strdup (username), g_free); @@ -1070,7 +1070,7 @@ matrix_db_load_room_async (MatrixDb *self, g_task_set_source_tag (task, matrix_db_load_room_async); g_task_set_task_data (task, matrix_db_load_room, NULL); - username = chatty_account_get_username (CHATTY_ACCOUNT (account)); + username = chatty_item_get_username (CHATTY_ITEM (account)); g_object_set_data_full (G_OBJECT (task), "room", g_strdup (room_id), g_free); g_object_set_data_full (G_OBJECT (task), "username", g_strdup (username), g_free); g_object_set_data_full (G_OBJECT (task), "account-id", g_strdup (username), g_free); @@ -1107,7 +1107,7 @@ matrix_db_delete_account_async (MatrixDb *self, g_task_set_source_tag (task, matrix_db_delete_account_async); g_task_set_task_data (task, matrix_db_delete_account, NULL); - username = chatty_account_get_username (CHATTY_ACCOUNT (account)); + username = chatty_item_get_username (CHATTY_ITEM (account)); g_object_set_data_full (G_OBJECT (task), "username", g_strdup (username), g_free); g_object_set_data_full (G_OBJECT (task), "account", g_object_ref (account), g_object_unref); diff --git a/src/matrix/matrix-enc.c b/src/matrix/matrix-enc.c index 17eb8ec..145c04c 100644 --- a/src/matrix/matrix-enc.c +++ b/src/matrix/matrix-enc.c @@ -877,7 +877,7 @@ handle_m_room_key (MatrixEnc *self, strlen (self->pickle_key), pickle, length); pickle[length] = '\0'; - CHATTY_TRACE_MSG ("saving session, room id: %s", room_id); + CHATTY_TRACE (room_id, "saving session, room id: "); if (self->matrix_db) matrix_db_add_session_async (self->matrix_db, self->user_id, self->device_id, room_id, session_id, sender_key, @@ -1194,7 +1194,7 @@ matrix_enc_create_out_group_keys (MatrixEnc *self, devices = chatty_ma_buddy_get_devices (buddy); user = json_object_new (); - json_object_set_object_member (root, chatty_ma_buddy_get_id (buddy), user); + json_object_set_object_member (root, chatty_item_get_username (CHATTY_ITEM (buddy)), user); for (GList *node = devices; node; node = node->next) { OlmSession *olm_session = NULL; @@ -1250,7 +1250,7 @@ matrix_enc_create_out_group_keys (MatrixEnc *self, json_object_set_object_member (object, "content", child); /* User specific data */ - json_object_set_string_member (object, "recipient", chatty_ma_buddy_get_id (buddy)); + json_object_set_string_member (object, "recipient", chatty_item_get_username (CHATTY_ITEM (buddy))); /* Device specific data */ child = json_object_new (); diff --git a/src/matrix/matrix-net.c b/src/matrix/matrix-net.c index eebce9a..298bfbd 100644 --- a/src/matrix/matrix-net.c +++ b/src/matrix/matrix-net.c @@ -564,11 +564,13 @@ matrix_net_get_file_async (MatrixNet *self, GAsyncReadyCallback callback, gpointer user_data) { + g_autofree char *url = NULL; SoupMessage *msg; GTask *task; g_return_if_fail (MATRIX_IS_NET (self)); g_return_if_fail (!message || CHATTY_IS_MESSAGE (message)); + g_return_if_fail (file && file->url); if (message) g_object_ref (message); @@ -576,7 +578,18 @@ matrix_net_get_file_async (MatrixNet *self, if (!cancellable) cancellable = self->cancellable; - msg = soup_message_new (SOUP_METHOD_GET, file->url); + if (g_str_has_prefix (file->url, "mxc://")) { + const char *file_url; + + file_url = file->url + strlen ("mxc://"); + url = g_strconcat (self->homeserver, + "/_matrix/media/r0/download/", file_url, NULL); + } + + if (!url) + url = g_strdup (file->url); + + msg = soup_message_new (SOUP_METHOD_GET, url); task = g_task_new (self, cancellable, callback, user_data); g_object_set_data (G_OBJECT (task), "progress", progress_callback); diff --git a/src/meson.build b/src/meson.build index 9b5f103..7ca0a82 100644 --- a/src/meson.build +++ b/src/meson.build @@ -26,8 +26,10 @@ libsrc = [ 'matrix/chatty-ma-chat.c', 'users/chatty-item.c', 'users/chatty-contact.c', + 'users/chatty-mm-buddy.c', 'users/chatty-pp-buddy.c', 'users/chatty-account.c', + 'users/chatty-mm-account.c', 'users/chatty-pp-account.c', 'chatty-fp-row.c', 'chatty-list-row.c', @@ -38,6 +40,7 @@ libsrc = [ 'chatty-log.c', 'chatty-avatar.c', 'chatty-chat.c', + 'chatty-mm-chat.c', 'chatty-pp-chat.c', 'chatty-contact-provider.c', 'chatty-message.c', @@ -106,6 +109,8 @@ chatty_deps = [ dependency('libsoup-2.4'), dependency('json-glib-1.0'), libolm_dep, + dependency('mm-glib', version: '>= 1.12.0'), + dependency('gspell-1'), libebook_dep, libfeedback_dep, libm_dep, diff --git a/src/ui/chatty-dialog-new-chat.ui b/src/ui/chatty-dialog-new-chat.ui index 768e61e..d1b3c0d 100644 --- a/src/ui/chatty-dialog-new-chat.ui +++ b/src/ui/chatty-dialog-new-chat.ui @@ -172,12 +172,13 @@ start - - True - False - True + + none + + + @@ -185,13 +186,12 @@ - - + + True + False + True none - - - diff --git a/src/ui/chatty-ma-account-details.ui b/src/ui/chatty-ma-account-details.ui index ac7fc7a..ba56d40 100644 --- a/src/ui/chatty-ma-account-details.ui +++ b/src/ui/chatty-ma-account-details.ui @@ -7,21 +7,60 @@ 1 - + 1 - 1 - - 0 center - 0 - 0 - 0 - 24 - + center - + 1 - 96 + 1 + 0 + center + 0 + 0 + 0 + 24 + + + + 1 + 96 + + + + + + + 1 + end + end + 27 + 3 + + + + 1 + 24 + 24 + 1 + 1 + + + 1 + user-trash-symbolic + + + + + 1 + + + + + diff --git a/src/ui/chatty-ma-chat-info.ui b/src/ui/chatty-ma-chat-info.ui index 0f8d8b5..a3efe0a 100644 --- a/src/ui/chatty-ma-chat-info.ui +++ b/src/ui/chatty-ma-chat-info.ui @@ -62,6 +62,7 @@ start end left + 1 1 @@ -88,6 +89,7 @@ start end left + 1 1 diff --git a/src/ui/chatty-pp-chat-info.ui b/src/ui/chatty-pp-chat-info.ui index b22a2c9..6e22b28 100644 --- a/src/ui/chatty-pp-chat-info.ui +++ b/src/ui/chatty-pp-chat-info.ui @@ -145,6 +145,7 @@ start end left + 1 1 @@ -171,6 +172,7 @@ start end left + 1 1 diff --git a/src/ui/chatty-settings-dialog.ui b/src/ui/chatty-settings-dialog.ui index d4e3579..4c16d31 100644 --- a/src/ui/chatty-settings-dialog.ui +++ b/src/ui/chatty-settings-dialog.ui @@ -107,6 +107,30 @@ True + + + True + + + True + + + + True + True + False + Message Settings + SMS/MMS Settings + + + + + + + + @@ -283,6 +307,37 @@ + + + True + + + True + SMS Settings + + + True + False + Delivery Reports + Request delivery reports for SMS messages + delivery_reports_switch + + + True + center + + + + + + + + + + message-settings-view + + + 1 diff --git a/src/users/chatty-account.c b/src/users/chatty-account.c index 1782aba..6f3a926 100644 --- a/src/users/chatty-account.c +++ b/src/users/chatty-account.c @@ -54,23 +54,6 @@ chatty_account_real_get_status (ChattyAccount *self) return CHATTY_DISCONNECTED; } -static const char * -chatty_account_real_get_username (ChattyAccount *self) -{ - g_assert (CHATTY_IS_ACCOUNT (self)); - - return ""; -} - -static void -chatty_account_real_set_username (ChattyAccount *self, - const char *username) -{ - g_assert (CHATTY_IS_ACCOUNT (self)); - - /* Do nothing */ -} - static GListModel * chatty_account_real_get_buddies (ChattyAccount *self) { @@ -339,8 +322,6 @@ chatty_account_class_init (ChattyAccountClass *klass) klass->get_protocol_name = chatty_account_real_get_protocol_name; klass->get_status = chatty_account_real_get_status; - klass->get_username = chatty_account_real_get_username; - klass->set_username = chatty_account_real_set_username; klass->get_buddies = chatty_account_real_get_buddies; klass->buddy_exists = chatty_account_real_buddy_exists; klass->get_enabled = chatty_account_real_get_enabled; @@ -412,23 +393,6 @@ chatty_account_get_status (ChattyAccount *self) return CHATTY_ACCOUNT_GET_CLASS (self)->get_status (self); } -const char * -chatty_account_get_username (ChattyAccount *self) -{ - g_return_val_if_fail (CHATTY_IS_ACCOUNT (self), ""); - - return CHATTY_ACCOUNT_GET_CLASS (self)->get_username (self); -} - -void -chatty_account_set_username (ChattyAccount *self, - const char *username) -{ - g_return_if_fail (CHATTY_IS_ACCOUNT (self)); - - CHATTY_ACCOUNT_GET_CLASS (self)->set_username (self, username); -} - GListModel * chatty_account_get_buddies (ChattyAccount *self) { diff --git a/src/users/chatty-account.h b/src/users/chatty-account.h index c60531d..849df26 100644 --- a/src/users/chatty-account.h +++ b/src/users/chatty-account.h @@ -28,9 +28,6 @@ struct _ChattyAccountClass const char *(*get_protocol_name) (ChattyAccount *self); ChattyStatus (*get_status) (ChattyAccount *self); - const char *(*get_username) (ChattyAccount *self); - void (*set_username) (ChattyAccount *self, - const char *username); GListModel *(*get_buddies) (ChattyAccount *self); gboolean (*buddy_exists) (ChattyAccount *self, const char *buddy_username); @@ -82,9 +79,6 @@ struct _ChattyAccountClass const char *chatty_account_get_protocol_name (ChattyAccount *self); ChattyStatus chatty_account_get_status (ChattyAccount *self); -const char *chatty_account_get_username (ChattyAccount *self); -void chatty_account_set_username (ChattyAccount *self, - const char *username); GListModel *chatty_account_get_buddies (ChattyAccount *self); gboolean chatty_account_buddy_exists (ChattyAccount *self, const char *buddy_username); diff --git a/src/users/chatty-contact.c b/src/users/chatty-contact.c index 0b7da87..979e67b 100644 --- a/src/users/chatty-contact.c +++ b/src/users/chatty-contact.c @@ -75,15 +75,18 @@ chatty_contact_matches (ChattyItem *item, g_assert (CHATTY_IS_CONTACT (self)); - value = chatty_contact_get_value (self); + value = chatty_item_get_username (item); protocol = chatty_item_get_protocols (item); - if (protocol == CHATTY_PROTOCOL_SMS && - protocols & CHATTY_PROTOCOL_SMS) { + if (protocol == CHATTY_PROTOCOL_MMS_SMS && + protocols & CHATTY_PROTOCOL_MMS_SMS) { ChattySettings *settings; const char *country; EPhoneNumberMatch match; + if (strstr (value, needle)) + return TRUE; + settings = chatty_settings_get_default (); country = chatty_settings_get_country_iso_code (settings); match = e_phone_number_compare_strings_with_region (value, needle, country, NULL); @@ -122,6 +125,21 @@ chatty_contact_get_name (ChattyItem *item) return ""; } +static const char * +chatty_contact_get_username (ChattyItem *item) +{ + ChattyContact *self = (ChattyContact *)item; + + g_assert (CHATTY_IS_CONTACT (self)); + + if (!self->value && self->attribute) + self->value = e_vcard_attribute_get_value (self->attribute); + + if (self->value) + return self->value; + + return ""; +} static GdkPixbuf * chatty_contact_get_avatar (ChattyItem *item) @@ -210,6 +228,7 @@ chatty_contact_class_init (ChattyContactClass *klass) item_class->get_protocols = chatty_contact_get_protocols; item_class->matches = chatty_contact_matches; item_class->get_name = chatty_contact_get_name; + item_class->get_username = chatty_contact_get_username; item_class->get_avatar = chatty_contact_get_avatar; item_class->get_avatar_async = chatty_contact_get_avatar_async; } @@ -260,31 +279,6 @@ chatty_contact_set_name (ChattyContact *self, self->name = g_strdup (name); } -/** - * chatty_contact_get_value: - * @self: A #ChattyContact - * - * Get the value stored in @self. It can be a phone - * number, an XMPP ID, etc. - * Also see chatty_contact_get_value_type(). - * - * Returns: (transfer none): The value of @self. - * Or an empty string if no value. - */ -const char * -chatty_contact_get_value (ChattyContact *self) -{ - g_return_val_if_fail (CHATTY_IS_CONTACT (self), NULL); - - if (!self->value && self->attribute) - self->value = e_vcard_attribute_get_value (self->attribute); - - if (self->value) - return self->value; - - return ""; -} - void chatty_contact_set_value (ChattyContact *self, const char *value) @@ -344,6 +338,39 @@ chatty_contact_get_uid (ChattyContact *self) return ""; } +gboolean +chatty_contact_is_exact_match (ChattyContact *self, + const char *value, + ChattyProtocol protocols) +{ + const char *contact_value; + ChattyProtocol protocol; + + g_assert (CHATTY_IS_CONTACT (self)); + + contact_value = chatty_item_get_username (CHATTY_ITEM (self)); + protocol = chatty_item_get_protocols (CHATTY_ITEM (self)); + + if (protocol & (CHATTY_PROTOCOL_MMS_SMS | CHATTY_PROTOCOL_MMS) && + protocols & (CHATTY_PROTOCOL_MMS_SMS | CHATTY_PROTOCOL_MMS)) { + ChattySettings *settings; + const char *country; + EPhoneNumberMatch match; + + if (g_str_equal (contact_value, value)) + return TRUE; + + settings = chatty_settings_get_default (); + country = chatty_settings_get_country_iso_code (settings); + match = e_phone_number_compare_strings_with_region (contact_value, value, country, NULL); + + if (match == E_PHONE_NUMBER_MATCH_EXACT || + match == E_PHONE_NUMBER_MATCH_NATIONAL) + return TRUE; + } + + return FALSE; +} /** * chatty_contact_clear_cache: diff --git a/src/users/chatty-contact.h b/src/users/chatty-contact.h index 2600359..32cce5f 100644 --- a/src/users/chatty-contact.h +++ b/src/users/chatty-contact.h @@ -28,11 +28,13 @@ ChattyContact *chatty_contact_new (EContact *cont ChattyProtocol protocol); void chatty_contact_set_name (ChattyContact *self, const char *name); -const char *chatty_contact_get_value (ChattyContact *self); void chatty_contact_set_value (ChattyContact *self, const char *value); const char *chatty_contact_get_value_type (ChattyContact *self); const char *chatty_contact_get_uid (ChattyContact *self); +gboolean chatty_contact_is_exact_match (ChattyContact *self, + const char *value, + ChattyProtocol protocols); gboolean chatty_contact_is_dummy (ChattyContact *self); G_END_DECLS diff --git a/src/users/chatty-item.c b/src/users/chatty-item.c index 129f1c5..c4e26ed 100644 --- a/src/users/chatty-item.c +++ b/src/users/chatty-item.c @@ -91,6 +91,21 @@ chatty_item_real_set_name (ChattyItem *self, /* Do Nothing */ } +static const char * +chatty_item_real_get_username (ChattyItem *self) +{ + g_assert (CHATTY_IS_ITEM (self)); + + return ""; +} + +static void +chatty_item_real_set_username (ChattyItem *self, + const char *username) +{ + /* Do Nothing */ +} + static ChattyItemState chatty_item_real_get_state (ChattyItem *self) { @@ -241,6 +256,8 @@ chatty_item_class_init (ChattyItemClass *klass) klass->matches = chatty_item_real_matches; klass->get_name = chatty_item_real_get_name; klass->set_name = chatty_item_real_set_name; + klass->get_username = chatty_item_real_get_username; + klass->set_username = chatty_item_real_set_username; klass->get_state = chatty_item_real_get_state; klass->set_state = chatty_item_real_set_state; klass->get_avatar_file = chatty_item_real_get_avatar_file; @@ -333,14 +350,6 @@ chatty_item_get_protocols (ChattyItem *self) return CHATTY_ITEM_GET_CLASS (self)->get_protocols (self); } -gboolean -chatty_item_is_sms (ChattyItem *self) -{ - g_return_val_if_fail (CHATTY_IS_ITEM (self), FALSE); - - return chatty_item_get_protocols (self) == CHATTY_PROTOCOL_SMS; -} - /** * chatty_item_matches: * @self: a #ChattyItem @@ -444,6 +453,35 @@ chatty_item_set_name (ChattyItem *self, CHATTY_ITEM_GET_CLASS (self)->set_name (self, name); } +/** + * chatty_item_get_username: + * @self: a #ChattyItem + * + * Returns the username/value of the item. It can be a phone + * number, an XMPP/matrix ID, etc, depending on what @self + * represents. For a #ChattyChat, the username of the account + * for the chat was created, is returned. + * + * Returns: the value of the item + */ +const char * +chatty_item_get_username (ChattyItem *self) +{ + g_return_val_if_fail (CHATTY_IS_ITEM (self), NULL); + + return CHATTY_ITEM_GET_CLASS (self)->get_username (self); +} + +void +chatty_item_set_username (ChattyItem *self, + const char *username) +{ + g_return_if_fail (CHATTY_IS_ITEM (self)); + g_return_if_fail (username && *username); + + return CHATTY_ITEM_GET_CLASS (self)->set_username (self, username); +} + /** * chatty_item_get_state: * @self: a #ChattyItem diff --git a/src/users/chatty-item.h b/src/users/chatty-item.h index 7e33540..f9280fe 100644 --- a/src/users/chatty-item.h +++ b/src/users/chatty-item.h @@ -33,6 +33,9 @@ struct _ChattyItemClass const char *(*get_name) (ChattyItem *self); void (*set_name) (ChattyItem *self, const char *name); + const char *(*get_username) (ChattyItem *self); + void (*set_username) (ChattyItem *self, + const char *username); ChattyItemState (*get_state) (ChattyItem *self); void (*set_state) (ChattyItem *self, ChattyItemState state); @@ -56,7 +59,6 @@ struct _ChattyItemClass }; ChattyProtocol chatty_item_get_protocols (ChattyItem *self); -gboolean chatty_item_is_sms (ChattyItem *self); gboolean chatty_item_matches (ChattyItem *self, const char *needle, ChattyProtocol protocols, @@ -66,6 +68,9 @@ int chatty_item_compare (ChattyItem *a, const char *chatty_item_get_name (ChattyItem *self); void chatty_item_set_name (ChattyItem *self, const char *name); +const char *chatty_item_get_username (ChattyItem *self); +void chatty_item_set_username (ChattyItem *self, + const char *username); ChattyItemState chatty_item_get_state (ChattyItem *self); void chatty_item_set_state (ChattyItem *self, ChattyItemState state); diff --git a/src/users/chatty-mm-account.c b/src/users/chatty-mm-account.c new file mode 100644 index 0000000..de24049 --- /dev/null +++ b/src/users/chatty-mm-account.c @@ -0,0 +1,1100 @@ +/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- */ +/* chatty-mm-account.c + * + * Copyright 2020, 2021 Purism SPC + * + * Author(s): + * Mohammed Sadiq + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "chatty-mm-account" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "chatty-settings.h" +#include "chatty-account.h" +#include "chatty-window.h" +#include "chatty-history.h" +#include "chatty-mm-chat.h" +#include "chatty-utils.h" +#include "itu-e212-iso.h" +#include "chatty-mm-account.h" +#include "chatty-log.h" + +/** + * SECTION: chatty-mm-account + * @title: ChattyMmAccount + * @short_description: An abstraction over #MMObject + * @include: "chatty-mm-account.h" + * + */ + +struct _ChattyMmDevice +{ + GObject parent_instance; + + MMObject *mm_object; +}; + +#define CHATTY_TYPE_MM_DEVICE (chatty_mm_device_get_type ()) +G_DECLARE_FINAL_TYPE (ChattyMmDevice, chatty_mm_device, CHATTY, MM_DEVICE, GObject) +G_DEFINE_TYPE (ChattyMmDevice, chatty_mm_device, G_TYPE_OBJECT) + +static void +chatty_mm_device_finalize (GObject *object) +{ + ChattyMmDevice *self = (ChattyMmDevice *)object; + + g_clear_object (&self->mm_object); + + G_OBJECT_CLASS (chatty_mm_device_parent_class)->finalize (object); +} +static void +chatty_mm_device_class_init (ChattyMmDeviceClass *klass) +{ + G_OBJECT_CLASS (klass)->finalize = chatty_mm_device_finalize; +} + +static void +chatty_mm_device_init (ChattyMmDevice *self) +{ +} + +static ChattyMmDevice * +chatty_mm_device_new (void) +{ + return g_object_new (CHATTY_TYPE_MM_DEVICE, NULL); +} + +struct _ChattyMmAccount +{ + ChattyAccount parent_instance; + + ChattyHistory *history_db; + ChattyEds *chatty_eds; + + MMManager *mm_manager; + GListStore *device_list; + GListStore *chat_list; + GHashTable *pending_sms; + GCancellable *cancellable; + + ChattyStatus status; + + guint mm_watch_id; + gboolean mm_loaded; +}; + +typedef struct _MessagingData { + ChattyMmAccount *object; + char *message_path; +} MessagingData; + +G_DEFINE_TYPE (ChattyMmAccount, chatty_mm_account, CHATTY_TYPE_ACCOUNT) + +static char * +strip_phone_number (const char *number) +{ + g_auto(GStrv) phone = NULL; + + if (!number || !*number) + return NULL; + + phone = g_strsplit_set (number, "() -", 0); + + return g_strjoinv ("", phone); +} + +static ChattyMmDevice * +mm_account_lookup_device (ChattyMmAccount *self, + MMObject *object, + MMModemMessaging *mm_messaging) +{ + guint n_items; + + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + g_assert (MM_IS_OBJECT (object) || MM_IS_MODEM_MESSAGING (mm_messaging)); + + n_items = g_list_model_get_n_items (G_LIST_MODEL (self->device_list)); + + for (guint i = 0; i < n_items; i++) { + g_autoptr(ChattyMmDevice) device = NULL; + + device = g_list_model_get_item (G_LIST_MODEL (self->device_list), i); + + if (device->mm_object == object) + return g_steal_pointer (&device); + + if (mm_object_peek_modem_messaging (device->mm_object) == mm_messaging) + return g_steal_pointer (&device); + } + + return NULL; +} + +static void +sent_message_delete_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + ChattyMmAccount *self; + MMModemMessaging *messaging = (MMModemMessaging *)object; + g_autoptr(GTask) task = user_data; + g_autoptr(GError) error = NULL; + ChattyMessage *message; + ChattyChat *chat; + + g_assert (G_IS_TASK (task)); + + self = g_task_get_source_object (task); + chat = g_object_get_data (G_OBJECT (task), "chat"); + message = g_task_get_task_data (task); + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + g_assert (CHATTY_IS_CHAT (chat)); + + /* We add the item to db only if we are able to delete it from modem */ + if (mm_modem_messaging_delete_finish (messaging, result, &error)) + chatty_history_add_message (self->history_db, chat, message); + else if (error) + g_warning ("Error deleting message: %s", error->message); + + g_task_return_boolean (task, TRUE); +} + +static gboolean +get_message_reference (gpointer user_data) +{ + ChattyMmAccount *self; + GTask *task = user_data; + ChattyMmDevice *device; + ChattyMessage *message; + MMSms *sms; + + g_assert (G_IS_TASK (task)); + + sms = g_object_get_data (G_OBJECT (task), "sms"); + self = g_task_get_source_object (task); + device = g_object_get_data (G_OBJECT (task), "device"); + message = g_task_get_task_data (task); + + + chatty_message_set_status (message, CHATTY_STATUS_SENT, 0); + chatty_message_set_sms_id (message, mm_sms_get_message_reference (sms)); + g_hash_table_insert (self->pending_sms, + GINT_TO_POINTER (chatty_message_get_sms_id (message)), + g_object_ref (message)); + + CHATTY_TRACE_MSG ("deleting message %s", mm_sms_get_path (sms)); + mm_modem_messaging_delete (mm_object_peek_modem_messaging (device->mm_object), + mm_sms_get_path (sms), + g_task_get_cancellable (task), + sent_message_delete_cb, + task); + + return G_SOURCE_REMOVE; +} + +static void +sms_send_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + ChattyMmAccount *self; + g_autoptr(GTask) task = user_data; + MMSms *sms = (MMSms *)object; + ChattyMessage *message; + ChattyChat *chat; + GError *error = NULL; + + g_assert (G_IS_TASK (task)); + + self = g_task_get_source_object (task); + chat = g_object_get_data (G_OBJECT (task), "chat"); + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + g_assert (CHATTY_IS_CHAT (chat)); + + message = g_task_get_task_data (task); + + if (!mm_sms_send_finish (sms, result, &error)) { + chatty_message_set_status (message, CHATTY_STATUS_SENDING_FAILED, 0); + chatty_history_add_message (self->history_db, chat, message); + g_debug ("Failed to send sms: %s", error->message); + g_task_return_error (task, error); + return; + } + + chatty_message_set_status (message, CHATTY_STATUS_SENT, 0); + + g_object_set_data_full (G_OBJECT (task), "sms", g_object_ref (sms), g_object_unref); + + /* + * HACK: There seems some slight delay with updating message_reference with my AT modem. + * So if mm_sms_get_message_reference (sms) returns 0, try again after some timeout. + */ + if (mm_sms_get_message_reference (sms)) + get_message_reference (task); + else + g_timeout_add (100, get_message_reference, g_steal_pointer (&task)); +} + +static void +sms_create_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(GTask) task = user_data; + g_autoptr(MMSms) sms = NULL; + GCancellable *cancellable; + GError *error = NULL; + + sms = mm_modem_messaging_create_finish (MM_MODEM_MESSAGING (object), result, &error); + + if (!sms) { + g_debug ("Failed creating sms: %s", error->message); + g_task_return_error (task, error); + return; + } + + cancellable = g_task_get_cancellable (task); + + CHATTY_TRACE_MSG ("Sending message"); + mm_sms_send (sms, cancellable, sms_send_cb, + g_steal_pointer (&task)); +} + +static void +mm_account_delete_message_async (ChattyMmAccount *self, + ChattyMmDevice *device, + MMSms *sms, + GAsyncReadyCallback callback, + gpointer user_data) +{ + const char *sms_path; + + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + g_assert (CHATTY_IS_MM_DEVICE (device)); + g_assert (MM_IS_SMS (sms)); + + sms_path = mm_sms_get_path (sms); + CHATTY_TRACE_MSG ("deleting message %s", sms_path); + mm_modem_messaging_delete (mm_object_peek_modem_messaging (device->mm_object), + sms_path, self->cancellable, NULL, NULL); +} + +static gboolean +mm_account_add_sms (ChattyMmAccount *self, + ChattyMmDevice *device, + MMSms *sms, + MMSmsState state) +{ + g_autoptr(ChattyMessage) message = NULL; + g_autoptr(GDateTime) date_time = NULL; + ChattyChat *chat; + g_autofree char *phone = NULL; + g_autofree char *uuid = NULL; + const char *msg; + ChattyMsgDirection direction = CHATTY_DIRECTION_UNKNOWN; + gint64 unix_time = 0; + guint position; + + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + g_assert (MM_IS_SMS (sms)); + + msg = mm_sms_get_text (sms); + if (!msg) + return FALSE; + + phone = chatty_utils_check_phonenumber (mm_sms_get_number (sms), + chatty_settings_get_country_iso_code (chatty_settings_get_default ())); + if (!phone) + phone = mm_sms_dup_number (sms); + + CHATTY_TRACE (phone, "received message from "); + + chat = chatty_mm_account_start_chat (self, phone); + + if (state == MM_SMS_STATE_RECEIVED) + direction = CHATTY_DIRECTION_IN; + else if (state == MM_SMS_STATE_SENT) + direction = CHATTY_DIRECTION_OUT; + + date_time = g_date_time_new_from_iso8601 (mm_sms_get_timestamp (sms), NULL); + if (date_time) + unix_time = g_date_time_to_unix (date_time); + if (!unix_time) + unix_time = time (NULL); + + uuid = g_uuid_string_random (); + message = chatty_message_new (CHATTY_ITEM (chatty_mm_chat_get_user (CHATTY_MM_CHAT (chat))), + msg, uuid, unix_time, CHATTY_MESSAGE_TEXT, direction, 0); + chatty_mm_chat_append_message (CHATTY_MM_CHAT (chat), message); + chatty_history_add_message (self->history_db, chat, message); + g_signal_emit_by_name (chat, "changed", 0); + chatty_mm_chat_show_notification (CHATTY_MM_CHAT (chat)); + + if (chatty_utils_get_item_position (G_LIST_MODEL (self->chat_list), chat, &position)) + g_list_model_items_changed (G_LIST_MODEL (self->chat_list), position, 1, 1); + + if (direction == CHATTY_DIRECTION_IN) + mm_account_delete_message_async (self, device, sms, NULL, NULL); + + return TRUE; +} + +static void +sms_state_changed_cb (ChattyMmAccount *self, + GParamSpec *pspec, + MMSms *sms) +{ + MMSmsState state; + + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + g_assert (MM_IS_SMS (sms)); + + state = mm_sms_get_state (sms); + + if (state == MM_SMS_STATE_RECEIVED) { + ChattyMmDevice *device; + + device = g_object_get_data (G_OBJECT (sms), "device"); + if (mm_account_add_sms (self, device, sms, state)) { + CHATTY_TRACE_MSG ("deleting message %s", mm_sms_get_path (sms)); + mm_modem_messaging_delete (mm_object_peek_modem_messaging (device->mm_object), + mm_sms_get_path (sms), + NULL, NULL, NULL); + } + } +} + +static void +parse_sms (ChattyMmAccount *self, + ChattyMmDevice *device, + MMSms *sms) +{ + MMSmsPduType type; + MMSmsState state; + guint sms_id; + + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + g_assert (MM_IS_SMS (sms)); + + sms_id = mm_sms_get_message_reference (sms); + g_debug ("parsing sms, id: %u, path: %s", sms_id, mm_sms_get_path (sms)); + state = mm_sms_get_state (sms); + type = mm_sms_get_pdu_type (sms); + + if (state == MM_SMS_STATE_SENDING || + state == MM_SMS_STATE_SENT) + return; + + if (type == MM_SMS_PDU_TYPE_STATUS_REPORT) { + guint delivery_state; + + delivery_state = mm_sms_get_delivery_state (sms); + if (delivery_state <= MM_SMS_DELIVERY_STATE_COMPLETED_REPLACED_BY_SC) { + ChattyMessage *message; + + message = g_hash_table_lookup (self->pending_sms, GINT_TO_POINTER (sms_id)); + if (message) { + ChattyChat *chat; + + chatty_message_set_status (message, CHATTY_STATUS_DELIVERED, 0); + chat = chatty_mm_account_find_chat (self, mm_sms_get_number (sms)); + if (chat) + chatty_history_add_message (self->history_db, chat, message); + } + + CHATTY_TRACE_MSG ("deleting message %s", mm_sms_get_path (sms)); + mm_modem_messaging_delete (mm_object_peek_modem_messaging (device->mm_object), + mm_sms_get_path (sms), + NULL, NULL, NULL); + g_hash_table_remove (self->pending_sms, GINT_TO_POINTER (sms_id)); + } + } else if (type == MM_SMS_PDU_TYPE_CDMA_DELIVER || + type == MM_SMS_PDU_TYPE_DELIVER) { + if (state == MM_SMS_STATE_RECEIVED && mm_account_add_sms (self, device, sms, state)) { + CHATTY_TRACE_MSG ("deleting message %s", mm_sms_get_path (sms)); + mm_modem_messaging_delete (mm_object_peek_modem_messaging (device->mm_object), + mm_sms_get_path (sms), + NULL, NULL, NULL); + } else if (state == MM_SMS_STATE_RECEIVING) { + g_object_set_data_full (G_OBJECT (sms), "device", + g_object_ref (device), + g_object_unref); + g_signal_connect_object (sms, "notify::state", + G_CALLBACK (sms_state_changed_cb), + self, + G_CONNECT_SWAPPED | G_CONNECT_AFTER); + } + } +} + +static void +mm_account_messaging_list_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + ChattyMmAccount *self; + MMModemMessaging *mm_messaging = (MMModemMessaging *)object; + MessagingData *data = user_data; + g_autoptr(GError) error = NULL; + g_autoptr(ChattyMmDevice) device = NULL; + GList *list; + char *path; + + g_assert (data); + g_assert (CHATTY_IS_MM_ACCOUNT (data->object)); + self = data->object; + + list = mm_modem_messaging_list_finish (mm_messaging, result, &error); + CHATTY_TRACE_MSG ("message listed. has-error: %d, message count: %d", + !!error, g_list_length (list)); + + if (error) { + g_debug ("Error listing messages: %s", error->message); + return; + } + + path = data->message_path; + device = mm_account_lookup_device (self, NULL, mm_messaging); + + for (GList *node = list; node; node = node->next) + if (!path || g_str_equal (mm_sms_get_path (node->data), path)) { + parse_sms (self, device, node->data); + + if (path) + break; + } + + g_object_unref (data->object); + g_free (data->message_path); + g_free (data); +} + +static void +mm_account_sms_recieved_cb (ChattyMmAccount *self, + char *arg_path, + gboolean arg_received, + MMModemMessaging *mm_messaging) +{ + MessagingData *data; + + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + g_assert (MM_IS_MODEM_MESSAGING (mm_messaging)); + + if (!arg_path) + return; + + data = g_new0 (MessagingData, 1); + data->object = g_object_ref (self); + data->message_path = g_strdup (arg_path); + + CHATTY_TRACE_MSG ("List modem messages"); + + mm_modem_messaging_list (mm_messaging, self->cancellable, + mm_account_messaging_list_cb, + data); +} + +static void +mm_object_added_cb (ChattyMmAccount *self, + GDBusObject *object) +{ + g_autoptr(ChattyMmAccount) account = NULL; + g_autoptr(ChattyMmDevice) device = NULL; + MessagingData *data; + MMSim *sim; + + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + g_assert (MM_IS_OBJECT (object)); + + CHATTY_TRACE_MSG ("modem %p found, has messaging: %d", object, + !!mm_object_peek_modem_messaging (MM_OBJECT (object))); + + if (!mm_object_peek_modem_messaging (MM_OBJECT (object))) + return; + + device = chatty_mm_device_new (); + device->mm_object = g_object_ref (MM_OBJECT (object)); + g_list_store_append (self->device_list, device); + self->status = CHATTY_CONNECTED; + + /* Status change is emitted only when the first device is added */ + if (g_list_model_get_n_items (G_LIST_MODEL (self->device_list)) == 1) + g_object_notify (G_OBJECT (self), "status"); + + /* We already have the messaging object, so SIM should be ready too */ + sim = mm_modem_get_sim_sync (mm_object_peek_modem (MM_OBJECT (object)), + NULL, NULL); + + if (sim) { + ChattySettings *settings; + const char *code; + + settings = chatty_settings_get_default (); + code = get_country_iso_for_mcc (mm_sim_get_imsi (sim)); + + if (code && *code) + chatty_settings_set_country_iso_code (settings, code); + } + + g_signal_connect_swapped (mm_object_peek_modem_messaging (MM_OBJECT (object)), + "added", + G_CALLBACK (mm_account_sms_recieved_cb), self); + CHATTY_TRACE_MSG ("List messages from modem %p", object); + + data = g_new0 (MessagingData, 1); + data->object = g_object_ref (self); + + mm_modem_messaging_list (mm_object_peek_modem_messaging (MM_OBJECT (object)), + NULL, + mm_account_messaging_list_cb, + data); +} + +static void +mm_object_removed_cb (ChattyMmAccount *self, + GDBusObject *object) +{ + gsize n_items; + + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + g_assert (MM_IS_OBJECT (object)); + + n_items = g_list_model_get_n_items (G_LIST_MODEL (self->device_list)); + + for (guint i = 0; i < n_items; i++) { + g_autoptr(ChattyMmDevice) device = NULL; + + device = g_list_model_get_item (G_LIST_MODEL (self->device_list), i); + if (g_strcmp0 (mm_object_get_path (MM_OBJECT (object)), + mm_object_get_path (device->mm_object)) == 0) { + g_list_store_remove (self->device_list, i); + break; + } + } + + if (g_list_model_get_n_items (G_LIST_MODEL (self->device_list)) == 0) { + self->status = CHATTY_DISCONNECTED; + g_object_notify (G_OBJECT (self), "status"); + } +} + +static void +mm_interface_added_cb (ChattyMmAccount *self, + GDBusObject *object, + GDBusInterface *interface) +{ + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + g_assert (G_IS_DBUS_INTERFACE (interface)); + + if (MM_IS_MODEM_MESSAGING (interface)) + mm_object_added_cb (self, object); +} + +static void +mm_interface_removed_cb (ChattyMmAccount *self, + GDBusObject *object, + GDBusInterface *interface) +{ + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + g_assert (G_IS_DBUS_INTERFACE (interface)); + + if (MM_IS_MODEM_MESSAGING (interface)) + mm_object_removed_cb (self, object); +} + +static void mm_new_cb (GObject *object, + GAsyncResult *result, + gpointer user_data); +static void +mm_appeared_cb (GDBusConnection *connection, + const char *name, + const char *name_owner, + ChattyMmAccount *self) +{ + GTask *task; + + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + g_assert (G_IS_DBUS_CONNECTION (connection)); + + g_debug ("Modem Manager appeared"); + + if (self->mm_manager) + return; + + task = g_task_new (self, NULL, NULL, NULL); + mm_manager_new (connection, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, + NULL, mm_new_cb, task); +} + +static void +mm_vanished_cb (GDBusConnection *connection, + const char *name, + ChattyMmAccount *self) +{ + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + g_assert (G_IS_DBUS_CONNECTION (connection)); + + g_debug ("Modem Manager vanished"); + + g_clear_object (&self->mm_manager); + g_list_store_remove_all (self->device_list); + + self->status = CHATTY_DISCONNECTED; + g_object_notify (G_OBJECT (self), "status"); +} + +static void +mm_new_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(GTask) task = user_data; + ChattyMmAccount *self; + GError *error = NULL; + GList *objects; + + g_assert (G_IS_TASK (task)); + + self = g_task_get_source_object (task); + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + + self->mm_manager = mm_manager_new_finish (result, &error); + + if (!self->mm_watch_id) + self->mm_watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM, + MM_DBUS_SERVICE, + G_BUS_NAME_WATCHER_FLAGS_AUTO_START, + (GBusNameAppearedCallback)mm_appeared_cb, + (GBusNameVanishedCallback)mm_vanished_cb, + g_object_ref (self), + g_object_unref); + if (error) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Error creating ModemManager: %s", error->message); + g_task_return_error (task, error); + return; + } + + g_signal_connect_swapped (G_DBUS_OBJECT_MANAGER (self->mm_manager), + "object-added", + G_CALLBACK (mm_object_added_cb), self); + g_signal_connect_swapped (G_DBUS_OBJECT_MANAGER (self->mm_manager), + "object-removed", + G_CALLBACK (mm_object_removed_cb), self); + g_signal_connect_swapped (G_DBUS_OBJECT_MANAGER (self->mm_manager), + "interface-added", + G_CALLBACK (mm_interface_added_cb), self); + g_signal_connect_swapped (G_DBUS_OBJECT_MANAGER (self->mm_manager), + "interface-removed", + G_CALLBACK (mm_interface_removed_cb), self); + + objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (self->mm_manager)); + + for (GList *node = objects; node; node = node->next) + mm_object_added_cb (self, node->data); + g_list_free_full (objects, g_object_unref); + + g_task_return_boolean (task, TRUE); +} + +static const char * +chatty_mm_account_get_protocol_name (ChattyAccount *account) +{ + return "SMS"; +} + +static ChattyStatus +chatty_mm_account_get_status (ChattyAccount *account) +{ + ChattyMmAccount *self = (ChattyMmAccount *)account; + + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + + return self->status; +} + +static ChattyProtocol +chatty_mm_account_get_protocols (ChattyItem *item) +{ + return CHATTY_PROTOCOL_MMS_SMS; +} + +static const char * +chatty_mm_account_get_username (ChattyItem *item) +{ + return "SMS"; +} + +static void +chatty_mm_account_finalize (GObject *object) +{ + ChattyMmAccount *self = (ChattyMmAccount *)object; + + if (self->cancellable) + g_cancellable_cancel (self->cancellable); + g_clear_object (&self->cancellable); + g_list_store_remove_all (self->device_list); + + g_clear_handle_id (&self->mm_watch_id, g_bus_unwatch_name); + g_clear_object (&self->chat_list); + g_clear_object (&self->device_list); + g_clear_object (&self->chatty_eds); + + G_OBJECT_CLASS (chatty_mm_account_parent_class)->finalize (object); +} + +static void +chatty_mm_account_class_init (ChattyMmAccountClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + ChattyItemClass *item_class = CHATTY_ITEM_CLASS (klass); + ChattyAccountClass *account_class = CHATTY_ACCOUNT_CLASS (klass); + + object_class->finalize = chatty_mm_account_finalize; + + item_class->get_protocols = chatty_mm_account_get_protocols; + item_class->get_username = chatty_mm_account_get_username; + + account_class->get_protocol_name = chatty_mm_account_get_protocol_name; + account_class->get_status = chatty_mm_account_get_status; +} + +static void +chatty_mm_account_init (ChattyMmAccount *self) +{ + self->chat_list = g_list_store_new (CHATTY_TYPE_MM_CHAT); + self->device_list = g_list_store_new (CHATTY_TYPE_MM_DEVICE); + self->pending_sms = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, g_object_unref); +} + +ChattyMmAccount * +chatty_mm_account_new (void) +{ + return g_object_new (CHATTY_TYPE_MM_ACCOUNT, NULL); +} + +void +chatty_mm_account_set_eds (ChattyMmAccount *self, + ChattyEds *eds) +{ + guint n_items; + + g_return_if_fail (CHATTY_IS_MM_ACCOUNT (self)); + g_return_if_fail (!eds || CHATTY_IS_EDS (eds)); + + if (!g_set_object (&self->chatty_eds, eds)) + return; + + n_items = g_list_model_get_n_items (G_LIST_MODEL (self->chat_list)); + for (guint i = 0; i < n_items; i++) { + g_autoptr(ChattyMmChat) chat = NULL; + + chat = g_list_model_get_item (G_LIST_MODEL (self->chat_list), i); + chatty_mm_chat_set_eds (chat, self->chatty_eds); + } +} + +void +chatty_mm_account_set_history_db (ChattyMmAccount *self, + gpointer history_db) +{ + g_return_if_fail (CHATTY_IS_MM_ACCOUNT (self)); + g_return_if_fail (!history_db || CHATTY_IS_HISTORY (history_db)); + g_return_if_fail (!self->history_db); + + g_set_object (&self->history_db, history_db); +} + +GListModel * +chatty_mm_account_get_chat_list (ChattyMmAccount *self) +{ + g_return_val_if_fail (CHATTY_IS_MM_ACCOUNT (self), NULL); + + return G_LIST_MODEL (self->chat_list); +} + +static void +get_bus_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(GTask) task = user_data; + g_autoptr(GDBusConnection) connection = NULL; + GCancellable *cancellable; + GError *error = NULL; + + connection = g_bus_get_finish (result, &error); + + if (error) { + g_warning ("Error getting bus: %s", error->message); + g_task_return_error (task, error); + return; + } + + cancellable = g_task_get_cancellable (task); + mm_manager_new (connection, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, + cancellable, mm_new_cb, + g_steal_pointer (&task)); +} + +static void +mm_get_chats_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + ChattyMmAccount *self; + GTask *task = user_data; + GPtrArray *chats = NULL; + GCancellable *cancellable; + g_autoptr(GError) error = NULL; + + self = g_task_get_source_object (task); + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + + chats = chatty_history_get_chats_finish (self->history_db, result, &error); + + if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Error loading chat: %s", error->message); + + CHATTY_TRACE_MSG ("Loaded %d chats from history db", !chats ? 0 : chats->len); + + if (chats) { + for (guint i = 0; i < chats->len; i++) { + chatty_chat_set_data (chats->pdata[i], self, self->history_db); + chatty_mm_chat_set_eds (chats->pdata[i], self->chatty_eds); + } + + g_list_store_splice (self->chat_list, 0, 0, chats->pdata, chats->len); + } + + cancellable = g_task_get_cancellable (task); + g_bus_get (G_BUS_TYPE_SYSTEM, cancellable, get_bus_cb, task); +} + +void +chatty_mm_account_load_async (ChattyMmAccount *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + g_return_if_fail (CHATTY_IS_MM_ACCOUNT (self)); + g_return_if_fail (self->history_db); + g_return_if_fail (!self->mm_watch_id); + g_return_if_fail (!self->mm_loaded); + + if (!self->cancellable) + self->cancellable = g_cancellable_new (); + + self->mm_loaded = TRUE; + task = g_task_new (self, self->cancellable, callback, user_data); + CHATTY_TRACE_MSG ("Loading chats from history db"); + chatty_history_get_chats_async (self->history_db, CHATTY_ACCOUNT (self), + mm_get_chats_cb, task); +} + +gboolean +chatty_mm_account_load_finish (ChattyMmAccount *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (CHATTY_MM_ACCOUNT (self), FALSE); + g_return_val_if_fail (G_IS_TASK (result), FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +ChattyChat * +chatty_mm_account_find_chat (ChattyMmAccount *self, + const char *phone) +{ + gsize n_items; + + g_return_val_if_fail (CHATTY_MM_ACCOUNT (self), NULL); + + g_assert (CHATTY_IS_MM_ACCOUNT (self)); + + if (!phone || !*phone) + return NULL; + + n_items = g_list_model_get_n_items (G_LIST_MODEL (self->chat_list)); + + for (guint i = 0; i < n_items; i++) { + g_autoptr(ChattyChat) chat = NULL; + g_autoptr(ChattyItem) item = NULL; + g_autofree char *number1 = NULL; + g_autofree char *number2 = NULL; + GListModel *user_list; + const char *user_id, *country_code; + + chat = g_list_model_get_item (G_LIST_MODEL (self->chat_list), i); + + if (!chatty_chat_is_im (chat)) + continue; + + user_list = chatty_chat_get_users (chat); + + if (user_list) + item = g_list_model_get_item (user_list, 0); + + if (item) + user_id = chatty_mm_buddy_get_number (CHATTY_MM_BUDDY (item)); + else + user_id = chatty_chat_get_chat_name (CHATTY_CHAT (chat)); + + country_code = chatty_settings_get_country_iso_code (chatty_settings_get_default ()); + number1 = chatty_utils_check_phonenumber (user_id, country_code); + number2 = chatty_utils_check_phonenumber (phone, country_code); + + if (g_strcmp0 (number1, number2) == 0) + return chat; + } + + return NULL; +} + +ChattyChat * +chatty_mm_account_start_chat (ChattyMmAccount *self, + const char *phone) +{ + ChattyChat *chat; + + g_return_val_if_fail (CHATTY_IS_MM_ACCOUNT (self), NULL); + + if (!phone || !*phone) + return NULL; + + chat = chatty_mm_account_find_chat (self, phone); + + if (!chat) { + g_autoptr(ChattyMmBuddy) buddy = NULL; + + chat = (ChattyChat *)chatty_mm_chat_new (phone, NULL, CHATTY_PROTOCOL_MMS_SMS, TRUE); + chatty_chat_set_data (chat, self, self->history_db); + buddy = chatty_mm_buddy_new (phone, NULL); + chatty_mm_chat_add_user (CHATTY_MM_CHAT (chat), buddy); + chatty_mm_chat_set_eds (CHATTY_MM_CHAT (chat), self->chatty_eds); + + g_list_store_append (self->chat_list, chat); + g_object_unref (chat); + } + + return chat; +} + +void +chatty_mm_account_delete_chat (ChattyMmAccount *self, + ChattyChat *chat) +{ + g_return_if_fail (CHATTY_IS_MM_ACCOUNT (self)); + g_return_if_fail (CHATTY_IS_MM_CHAT (chat)); + + chatty_utils_remove_list_item (self->chat_list, chat); +} + +gboolean +chatty_mm_account_has_mms_feature (ChattyMmAccount *self) +{ + g_return_val_if_fail (CHATTY_IS_MM_ACCOUNT (self), FALSE); + + /* TODO */ + return FALSE; +} + +void +chatty_mm_account_send_message_async (ChattyMmAccount *self, + ChattyChat *chat, + ChattyMmBuddy *buddy, + ChattyMessage *message, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(MMSmsProperties) sms_properties = NULL; + g_autoptr(GTask) task = NULL; + g_autofree char *phone = NULL; + ChattySettings *settings; + ChattyMmDevice *device; + guint position; + gboolean is_mms, request_report; + + g_return_if_fail (CHATTY_IS_MM_ACCOUNT (self)); + g_return_if_fail (CHATTY_IS_MM_CHAT (chat)); + g_return_if_fail (CHATTY_IS_MM_BUDDY (buddy)); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_task_data (task, message, NULL); + + g_object_set_data_full (G_OBJECT (task), "chat", g_object_ref (chat), + g_object_unref); + + device = g_list_model_get_item (G_LIST_MODEL (self->device_list), 0); + g_object_set_data_full (G_OBJECT (task), "device", device, g_object_unref); + + if (!device) { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "No modem found"); + return; + } + + is_mms = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (message), "mms")); + + if (is_mms) { + CHATTY_TRACE_MSG ("Creating MMS message"); + + /* TODO: Handle MMS */ + return; + } + + phone = strip_phone_number (chatty_mm_buddy_get_number (buddy)); + + if (!phone || !*phone) { + g_task_return_new_error (task, + G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, + "%s is not a valid phone number", + chatty_mm_buddy_get_number (buddy)); + return; + } + + settings = chatty_settings_get_default (); + request_report = chatty_settings_request_sms_delivery_reports (settings); + sms_properties = mm_sms_properties_new (); + mm_sms_properties_set_text (sms_properties, chatty_message_get_text (message)); + mm_sms_properties_set_number (sms_properties, phone); + mm_sms_properties_set_delivery_report_request (sms_properties, request_report); + mm_sms_properties_set_validity_relative (sms_properties, 168); + + chatty_mm_chat_append_message (CHATTY_MM_CHAT (chat), message); + + if (chatty_utils_get_item_position (G_LIST_MODEL (self->chat_list), chat, &position)) + g_list_model_items_changed (G_LIST_MODEL (self->chat_list), position, 1, 1); + + CHATTY_TRACE (phone, "Creating sms message to number: "); + mm_modem_messaging_create (mm_object_peek_modem_messaging (device->mm_object), + sms_properties, cancellable, + sms_create_cb, + g_steal_pointer (&task)); +} + +gboolean +chatty_mm_account_send_message_finish (ChattyMmAccount *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (CHATTY_MM_ACCOUNT (self), FALSE); + g_return_val_if_fail (G_IS_TASK (result), FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} diff --git a/src/users/chatty-mm-account.h b/src/users/chatty-mm-account.h new file mode 100644 index 0000000..f93065a --- /dev/null +++ b/src/users/chatty-mm-account.h @@ -0,0 +1,59 @@ +/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- */ +/* chatty-mm-account.h + * + * Copyright 2020,2021 Purism SPC + * + * Author(s): + * Mohammed Sadiq + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include + +#include "chatty-chat.h" +#include "chatty-mm-buddy.h" +#include "chatty-account.h" +#include "chatty-contact-provider.h" +#include "chatty-enums.h" + +G_BEGIN_DECLS + +#define CHATTY_TYPE_MM_ACCOUNT (chatty_mm_account_get_type ()) + +G_DECLARE_FINAL_TYPE (ChattyMmAccount, chatty_mm_account, CHATTY, MM_ACCOUNT, ChattyAccount) + +ChattyMmAccount *chatty_mm_account_new (void); +void chatty_mm_account_set_eds (ChattyMmAccount *self, + ChattyEds *eds); +void chatty_mm_account_set_history_db (ChattyMmAccount *self, + gpointer history_db); +GListModel *chatty_mm_account_get_chat_list (ChattyMmAccount *self); +void chatty_mm_account_load_async (ChattyMmAccount *self, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean chatty_mm_account_load_finish (ChattyMmAccount *self, + GAsyncResult *result, + GError **error); +ChattyChat *chatty_mm_account_find_chat (ChattyMmAccount *self, + const char *phone); +ChattyChat *chatty_mm_account_start_chat (ChattyMmAccount *self, + const char *phone); +void chatty_mm_account_delete_chat (ChattyMmAccount *self, + ChattyChat *chat); +gboolean chatty_mm_account_has_mms_feature (ChattyMmAccount *self); +void chatty_mm_account_send_message_async (ChattyMmAccount *self, + ChattyChat *chat, + ChattyMmBuddy *buddy, + ChattyMessage *message, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean chatty_mm_account_send_message_finish (ChattyMmAccount *self, + GAsyncResult *result, + GError **error); + +G_END_DECLS diff --git a/src/users/chatty-mm-buddy.c b/src/users/chatty-mm-buddy.c new file mode 100644 index 0000000..b5c62d5 --- /dev/null +++ b/src/users/chatty-mm-buddy.c @@ -0,0 +1,202 @@ +/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- */ +/* chatty-pp-buddy.c + * + * Copyright 2020 Purism SPC + * + * Author(s): + * Mohammed Sadiq + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "chatty-mm-buddy" + +#define _GNU_SOURCE +#include +#include +#include + +#include "chatty-settings.h" +#include "chatty-account.h" +#include "chatty-mm-account.h" +#include "chatty-window.h" +#include "chatty-mm-buddy.h" + +/** + * SECTION: chatty-mm-buddy + * @title: ChattyMmBuddy + * @short_description: An abstraction over ModemManager + * @include: "chatty-mm-buddy.h" + */ + +struct _ChattyMmBuddy +{ + ChattyItem parent_instance; + + char *phone_number; + char *name; + ChattyContact *contact; +}; + +G_DEFINE_TYPE (ChattyMmBuddy, chatty_mm_buddy, CHATTY_TYPE_ITEM) + +enum { + CHANGED, + N_SIGNALS +}; + +static guint signals[N_SIGNALS]; + +static ChattyProtocol +chatty_mm_buddy_get_protocols (ChattyItem *item) +{ + ChattyMmBuddy *self = (ChattyMmBuddy *)item; + + g_assert (CHATTY_IS_MM_BUDDY (self)); + + return CHATTY_PROTOCOL_MMS_SMS; +} + +static gboolean +chatty_mm_buddy_matches (ChattyItem *item, + const char *needle, + ChattyProtocol protocols, + gboolean match_name) +{ + ChattyMmBuddy *self = (ChattyMmBuddy *)item; + + if (self->phone_number) + return strcasestr (self->phone_number, needle) != NULL; + + return FALSE; +} + +static const char * +chatty_mm_buddy_get_name (ChattyItem *item) +{ + ChattyMmBuddy *self = (ChattyMmBuddy *)item; + + g_assert (CHATTY_IS_MM_BUDDY (self)); + + if (self->name) + return self->name; + + return ""; +} + +static GdkPixbuf * +chatty_mm_buddy_get_avatar (ChattyItem *item) +{ + ChattyMmBuddy *self = (ChattyMmBuddy *)item; + + g_assert (CHATTY_IS_MM_BUDDY (self)); + + if (self->contact) + return chatty_item_get_avatar (CHATTY_ITEM (self->contact)); + + return NULL; +} + +static void +chatty_mm_buddy_finalize (GObject *object) +{ + ChattyMmBuddy *self = (ChattyMmBuddy *)object; + + g_clear_object (&self->contact); + g_clear_pointer (&self->phone_number, g_free); + g_clear_pointer (&self->name, g_free); + + G_OBJECT_CLASS (chatty_mm_buddy_parent_class)->finalize (object); +} + +static void +chatty_mm_buddy_class_init (ChattyMmBuddyClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + ChattyItemClass *item_class = CHATTY_ITEM_CLASS (klass); + + object_class->finalize = chatty_mm_buddy_finalize; + + item_class->get_protocols = chatty_mm_buddy_get_protocols; + item_class->matches = chatty_mm_buddy_matches; + item_class->get_name = chatty_mm_buddy_get_name; + item_class->get_avatar = chatty_mm_buddy_get_avatar; + + /** + * ChattyMmBuddy::changed: + * @self: a #ChattyMmBuddy + * + * changed signal is emitted when any detail + * of the buddy changes. + */ + signals [CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); +} + +static void +chatty_mm_buddy_init (ChattyMmBuddy *self) +{ +} + +ChattyMmBuddy * +chatty_mm_buddy_new (const char *phone_number, + const char *name) +{ + ChattyMmBuddy *self; + + self = g_object_new (CHATTY_TYPE_MM_BUDDY, NULL); + self->phone_number = g_strdup (phone_number); + self->name = g_strdup (name); + + return self; +} + +/** + * chatty_mm_buddy_get_number: + * @self: a #ChattyMmBuddy + * + * Get the phone number of @self. + * + * Returns: (transfer none): the phone number of Buddy. + * or an empty string if not found or on error. + */ +const char * +chatty_mm_buddy_get_number (ChattyMmBuddy *self) +{ + g_return_val_if_fail (CHATTY_IS_MM_BUDDY (self), ""); + + /* Prefer local copy of the phone number, as it will + * be well formatted, or in international format, + * while there is no guarantee for that to be the case + * for the value saved in contacts + */ + if (self->phone_number) + return self->phone_number; + + if (self->contact) + return chatty_item_get_username (CHATTY_ITEM (self->contact)); + + return ""; +} + +ChattyContact * +chatty_mm_buddy_get_contact (ChattyMmBuddy *self) +{ + g_return_val_if_fail (CHATTY_IS_MM_BUDDY (self), NULL); + + return self->contact; +} + +void +chatty_mm_buddy_set_contact (ChattyMmBuddy *self, + ChattyContact *contact) +{ + g_return_if_fail (CHATTY_IS_MM_BUDDY (self)); + g_return_if_fail (!contact || CHATTY_IS_CONTACT (contact)); + + g_set_object (&self->contact, contact); +} diff --git a/src/users/chatty-mm-buddy.h b/src/users/chatty-mm-buddy.h new file mode 100644 index 0000000..7237dac --- /dev/null +++ b/src/users/chatty-mm-buddy.h @@ -0,0 +1,33 @@ +/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- */ +/* chatty-mm-buddy.h + * + * Copyright 2020 Purism SPC + * + * Author(s): + * Mohammed Sadiq + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "chatty-contact.h" +#include "chatty-item.h" +#include "chatty-enums.h" + +G_BEGIN_DECLS + +#define CHATTY_TYPE_MM_BUDDY (chatty_mm_buddy_get_type ()) + +G_DECLARE_FINAL_TYPE (ChattyMmBuddy, chatty_mm_buddy, CHATTY, MM_BUDDY, ChattyItem) + +ChattyMmBuddy *chatty_mm_buddy_new (const char *phone_number, + const char *name); +const char *chatty_mm_buddy_get_number (ChattyMmBuddy *self); +ChattyContact *chatty_mm_buddy_get_contact (ChattyMmBuddy *self); +void chatty_mm_buddy_set_contact (ChattyMmBuddy *self, + ChattyContact *contact); + +G_END_DECLS diff --git a/src/users/chatty-pp-account.c b/src/users/chatty-pp-account.c index 07f3b19..50c416c 100644 --- a/src/users/chatty-pp-account.c +++ b/src/users/chatty-pp-account.c @@ -23,6 +23,7 @@ #include "chatty-window.h" #include "chatty-pp-chat.h" #include "chatty-pp-account.h" +#include "chatty-log.h" /** * SECTION: chatty-pp-account @@ -130,7 +131,7 @@ account_connect (ChattyPpAccount *self) if (!purple_status_is_online (pp_status)) return G_SOURCE_REMOVE; - g_debug ("connecting to %s", chatty_account_get_username (CHATTY_ACCOUNT (self))); + CHATTY_DEBUG (chatty_item_get_username (CHATTY_ITEM (self)), "connecting to"); purple_account_connect (self->pp_account); return G_SOURCE_REMOVE; @@ -139,7 +140,7 @@ account_connect (ChattyPpAccount *self) static void chatty_pp_account_create (ChattyPpAccount *self) { - const char *protocol_id; + const char *protocol_id = NULL; ChattyProtocol protocol; g_assert (CHATTY_IS_PP_ACCOUNT (self)); @@ -150,8 +151,6 @@ chatty_pp_account_create (ChattyPpAccount *self) protocol_id = "prpl-jabber"; else if (protocol == CHATTY_PROTOCOL_MATRIX) protocol_id = "prpl-matrix"; - else if (protocol == CHATTY_PROTOCOL_SMS) - protocol_id = "prpl-mm-sms"; else if (protocol == CHATTY_PROTOCOL_TELEGRAM) protocol_id = "prpl-telegram"; @@ -161,11 +160,6 @@ chatty_pp_account_create (ChattyPpAccount *self) { purple_account_set_string (self->pp_account, "home_server", self->server_url); } - else if (protocol == CHATTY_PROTOCOL_SMS) - { - purple_account_set_password (self->pp_account, NULL); - purple_account_set_remember_password (self->pp_account, TRUE); - } } static void @@ -184,8 +178,6 @@ chatty_pp_load_protocol (ChattyPpAccount *self) protocol = CHATTY_PROTOCOL_XMPP; else if (g_str_equal (protocol_id, "prpl-matrix")) protocol = CHATTY_PROTOCOL_MATRIX; - else if (g_str_equal (protocol_id, "prpl-mm-sms")) - protocol = CHATTY_PROTOCOL_SMS; else if (g_str_equal (protocol_id, "prpl-telegram")) protocol = CHATTY_PROTOCOL_TELEGRAM; else if (g_str_equal (protocol_id, "prpl-delta")) @@ -221,27 +213,6 @@ chatty_pp_account_get_status (ChattyAccount *account) return CHATTY_DISCONNECTED; } -static const gchar * -chatty_pp_account_get_username (ChattyAccount *account) -{ - ChattyPpAccount *self = (ChattyPpAccount *)account; - - g_assert (CHATTY_IS_PP_ACCOUNT (self)); - - return purple_account_get_username (self->pp_account); -} - -static void -chatty_pp_account_set_username (ChattyAccount *account, - const char *username) -{ - ChattyPpAccount *self = (ChattyPpAccount *)account; - - g_assert (CHATTY_IS_PP_ACCOUNT (self)); - - purple_account_set_username (self->pp_account, username); -} - static GListModel * chatty_pp_account_get_buddies (ChattyAccount *account) { @@ -346,9 +317,6 @@ chatty_pp_account_disconnect (ChattyAccount *account) g_assert (CHATTY_IS_PP_ACCOUNT (self)); - if (chatty_item_is_sms (CHATTY_ITEM (self))) - return; - status = chatty_account_get_status (CHATTY_ACCOUNT (self)); if (status == CHATTY_DISCONNECTED) @@ -607,6 +575,27 @@ chatty_pp_account_set_name (ChattyItem *item, purple_account_set_alias (self->pp_account, name); } +static const char * +chatty_pp_account_get_username (ChattyItem *item) +{ + ChattyPpAccount *self = (ChattyPpAccount *)item; + + g_assert (CHATTY_IS_PP_ACCOUNT (self)); + + return purple_account_get_username (self->pp_account); +} + +static void +chatty_pp_account_set_username (ChattyItem *item, + const char *username) +{ + ChattyPpAccount *self = (ChattyPpAccount *)item; + + g_assert (CHATTY_IS_PP_ACCOUNT (self)); + + purple_account_set_username (self->pp_account, username); +} + static GdkPixbuf * chatty_icon_from_data (const guchar *buf, gsize size) @@ -786,13 +775,13 @@ chatty_pp_account_class_init (ChattyPpAccountClass *klass) item_class->get_protocols = chatty_pp_account_get_protocols; item_class->get_name = chatty_pp_account_get_name; item_class->set_name = chatty_pp_account_set_name; + item_class->get_username = chatty_pp_account_get_username; + item_class->set_username = chatty_pp_account_set_username; item_class->get_avatar = chatty_pp_account_get_avatar; item_class->set_avatar_async = chatty_pp_account_set_avatar_async; account_class->get_protocol_name = chatty_pp_account_get_protocol_name; account_class->get_status = chatty_pp_account_get_status; - account_class->get_username = chatty_pp_account_get_username; - account_class->set_username = chatty_pp_account_set_username; account_class->get_buddies = chatty_pp_account_get_buddies; account_class->buddy_exists = chatty_pp_account_buddy_exists; account_class->get_enabled = chatty_pp_account_get_enabled; @@ -873,8 +862,7 @@ chatty_pp_account_new (ChattyProtocol protocol, { ChattyPpAccount *self; - g_return_val_if_fail (protocol & (CHATTY_PROTOCOL_SMS | - CHATTY_PROTOCOL_XMPP | + g_return_val_if_fail (protocol & (CHATTY_PROTOCOL_XMPP | CHATTY_PROTOCOL_MATRIX | CHATTY_PROTOCOL_TELEGRAM), NULL); g_return_val_if_fail (username && *username, NULL); diff --git a/src/users/chatty-pp-buddy.c b/src/users/chatty-pp-buddy.c index b9c6db6..36edc4f 100644 --- a/src/users/chatty-pp-buddy.c +++ b/src/users/chatty-pp-buddy.c @@ -129,9 +129,6 @@ chatty_add_new_buddy (ChattyPpBuddy *self) g_debug ("%s: %s ", __func__, purple_buddy_get_name (self->pp_buddy)); - if (chatty_item_get_protocols (CHATTY_ITEM (self)) != CHATTY_PROTOCOL_SMS) - purple_account_add_buddy_with_invite (self->pp_account, self->pp_buddy, NULL); - conv = purple_find_conversation_with_account (PURPLE_CONV_TYPE_IM, self->username, self->pp_account); @@ -235,6 +232,30 @@ chatty_pp_buddy_set_name (ChattyItem *item, purple_blist_alias_contact (contact, self->name); } +static const char * +chatty_pp_buddy_get_username (ChattyItem *item) +{ + ChattyPpBuddy *self = (ChattyPpBuddy *)item; + const char *name; + + g_assert (CHATTY_IS_PP_BUDDY (self)); + + if (self->contact) + return chatty_item_get_username (CHATTY_ITEM (self->contact)); + + if (self->chat_buddy && self->chat_buddy->name) + return self->chat_buddy->name; + + if (!self->pp_buddy) + return ""; + + name = purple_buddy_get_name (self->pp_buddy); + + if (!name) + name = ""; + + return name; +} static gboolean load_icon (gpointer user_data) @@ -424,6 +445,7 @@ chatty_pp_buddy_class_init (ChattyPpBuddyClass *klass) item_class->matches = chatty_pp_buddy_matches; item_class->get_name = chatty_pp_buddy_get_name; item_class->set_name = chatty_pp_buddy_set_name; + item_class->get_username = chatty_pp_buddy_get_username; item_class->get_avatar = chatty_pp_buddy_get_avatar; properties[PROP_PURPLE_ACCOUNT] = @@ -516,42 +538,6 @@ chatty_pp_buddy_get_buddy (ChattyPpBuddy *self) return self->pp_buddy; } - -/** - * chatty_pp_buddy_get_id: - * @self: a #ChattyPpBuddy - * - * Get the user id of @self. For XMPP, this - * is the XMPP name (eg: name@example.com). - * For SMS buddy, this is the phone number. - * - * Returns: (transfer none): the id of Buddy. - * or an empty string if not found or on error. - */ -const char * -chatty_pp_buddy_get_id (ChattyPpBuddy *self) -{ - const char *name; - - g_return_val_if_fail (CHATTY_IS_PP_BUDDY (self), ""); - - if (self->contact) - return chatty_contact_get_value (self->contact); - - if (self->chat_buddy && self->chat_buddy->name) - return self->chat_buddy->name; - - if (!self->pp_buddy) - return ""; - - name = purple_buddy_get_name (self->pp_buddy); - - if (!name) - name = ""; - - return name; -} - ChattyContact * chatty_pp_buddy_get_contact (ChattyPpBuddy *self) { diff --git a/src/users/chatty-pp-buddy.h b/src/users/chatty-pp-buddy.h index b121daf..bc781ce 100644 --- a/src/users/chatty-pp-buddy.h +++ b/src/users/chatty-pp-buddy.h @@ -30,7 +30,6 @@ ChattyAccount *chatty_pp_buddy_get_account (ChattyPpBuddy *self); void chatty_pp_buddy_set_chat (ChattyPpBuddy *self, PurpleConversation *conv); PurpleBuddy *chatty_pp_buddy_get_buddy (ChattyPpBuddy *self); -const char *chatty_pp_buddy_get_id (ChattyPpBuddy *self); ChattyContact *chatty_pp_buddy_get_contact (ChattyPpBuddy *self); void chatty_pp_buddy_set_contact (ChattyPpBuddy *self, ChattyContact *contact); diff --git a/src/users/itu-e212-iso.h b/src/users/itu-e212-iso.h new file mode 100644 index 0000000..e5808fd --- /dev/null +++ b/src/users/itu-e212-iso.h @@ -0,0 +1,295 @@ +/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- */ +/* chatty-manager.c + * + * Copyright 2020 Purism SPC + * + * Mapping from E.212 MCC to Countries in ISO 3166-1 alpha-2 format + * + * Author(s): + * Mohammed Sadiq + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#pragma once + +#include + + +struct mcc_list +{ + guint mcc; + char code[3]; +}; + +/* + * Extracted from: + * https://www.itu.int/dms_pub/itu-t/opb/sp/T-SP-E.212B-2018-PDF-E.pdf + */ +struct mcc_list mcc_list[] = { + {202, "GR"}, + {204, "NL"}, + {206, "BE"}, + {208, "FR"}, + {212, "MC"}, + {213, "AD"}, + {214, "ES"}, + {216, "HU"}, + {218, "BA"}, + {219, "HR"}, + {220, "RS"}, + {221, "XK"}, + {222, "IT"}, + {225, "VA"}, + {226, "RO"}, + {228, "CH"}, + {230, "CZ"}, + {231, "SK"}, + {232, "AT"}, + {234, "GB"}, + {235, "GB"}, + {238, "DK"}, + {240, "SE"}, + {242, "NO"}, + {244, "FI"}, + {246, "LT"}, + {247, "LV"}, + {248, "EE"}, + {250, "RU"}, + {255, "UA"}, + {257, "BY"}, + {259, "MD"}, + {260, "PL"}, + {262, "DE"}, + {266, "GI"}, + {268, "PT"}, + {270, "LU"}, + {272, "IE"}, + {274, "IS"}, + {276, "AL"}, + {278, "MT"}, + {280, "CY"}, + {282, "GE"}, + {283, "AM"}, + {284, "BG"}, + {286, "TR"}, + {288, "FO"}, + {290, "GL"}, + {292, "SM"}, + {293, "SI"}, + {294, "MK"}, + {295, "LI"}, + {297, "ME"}, + {302, "CA"}, + {308, "PM"}, + {310, "US"}, + {311, "US"}, + {312, "US"}, + {313, "US"}, + {314, "US"}, + {315, "US"}, + {316, "US"}, + {330, "PR"}, + {332, "VI"}, + {334, "MX"}, + {338, "JM"}, + /* Guadeloupe and Martinique are part of France */ + {340, "GP"}, + {340, "MQ"}, + {342, "BB"}, + {344, "AG"}, + {346, "KY"}, + {348, "VG"}, + {350, "BM"}, + {352, "GD"}, + {354, "MS"}, + {356, "KN"}, + {358, "LC"}, + {360, "VC"}, + {362, "CW"}, + {363, "AW"}, + {364, "BS"}, + {365, "AI"}, + {366, "DM"}, + {368, "CU"}, + {370, "DO"}, + {372, "HT"}, + {374, "TT"}, + {376, "TC"}, + {400, "AZ"}, + {401, "KZ"}, + {402, "BT"}, + {404, "IN"}, + {405, "IN"}, + {406, "IN"}, + {410, "PK"}, + {412, "AF"}, + {413, "LK"}, + {414, "MM"}, + {415, "LB"}, + {416, "JO"}, + {417, "SY"}, + {418, "IQ"}, + {419, "KW"}, + {420, "SA"}, + {421, "YE"}, + {422, "OM"}, + {424, "AE"}, + {425, "IL"}, + {426, "BH"}, + {427, "QA"}, + {428, "MN"}, + {429, "NP"}, + {430, "AE"}, + {431, "AE"}, + {432, "IR"}, + {434, "UZ"}, + {436, "TJ"}, + {437, "KG"}, + {438, "TM"}, + {440, "JP"}, + {441, "JP"}, + {450, "KP"}, + {452, "VN"}, + {454, "HK"}, + {455, "MO"}, + {456, "KH"}, + {457, "LA"}, + {460, "CN"}, + {461, "CN"}, + {466, "TW"}, + {467, "KR"}, + {470, "BD"}, + {472, "MV"}, + {502, "MY"}, + {505, "AU"}, + {510, "ID"}, + {514, "TL"}, + {515, "PH"}, + {520, "TH"}, + {525, "SG"}, + {528, "BN"}, + {530, "NZ"}, + {536, "NR"}, + {537, "PG"}, + {539, "TO"}, + {540, "SB"}, + {541, "VU"}, + {542, "FJ"}, + {543, "WF"}, + {544, "AS"}, + {545, "KI"}, + {546, "NC"}, + {547, "PF"}, + {548, "CK"}, + {549, "AS"}, + {550, "FM"}, + {551, "MH"}, + {552, "PW"}, + {553, "TV"}, + {554, "TK"}, + {555, "NU"}, + {602, "EG"}, + {603, "DZ"}, + {604, "MA"}, + {605, "TN"}, + {606, "LY"}, + {607, "GM"}, + {608, "SN"}, + {609, "MR"}, + {610, "ML"}, + {611, "GN"}, + {612, "CI"}, + {613, "BF"}, + {614, "NE"}, + {615, "TG"}, + {616, "BJ"}, + {617, "MU"}, + {618, "LR"}, + {619, "SL"}, + {620, "GH"}, + {621, "NG"}, + {622, "TD"}, + {623, "CF"}, + {624, "CM"}, + {625, "CV"}, + {626, "ST"}, + {627, "GQ"}, + {628, "GA"}, + {629, "CG"}, + {630, "CD"}, + {631, "AO"}, + {632, "GW"}, + {633, "SC"}, + {634, "SD"}, + {635, "RW"}, + {636, "ET"}, + {637, "SO"}, + {638, "DJ"}, + {639, "KE"}, + {640, "TZ"}, + {641, "UG"}, + {642, "BI"}, + {643, "MZ"}, + {645, "ZM"}, + {646, "MG"}, + {647, "RE"}, + {648, "ZW"}, + {649, "NA"}, + {650, "MW"}, + {651, "LS"}, + {652, "BW"}, + {653, "SZ"}, + {654, "KM"}, + {655, "ZA"}, + {657, "ER"}, + {658, "SH"}, + {659, "SS"}, + {702, "BZ"}, + {704, "GT"}, + {706, "SV"}, + {708, "HN"}, + {710, "NI"}, + {712, "CR"}, + {714, "PA"}, + {716, "PE"}, + {722, "AR"}, + {724, "BR"}, + {730, "CL"}, + {732, "CO"}, + {734, "VE"}, + {736, "BO"}, + {738, "GY"}, + {740, "EC"}, + {742, "GF"}, + {744, "PY"}, + {746, "SR"}, + {748, "UY"}, + {750, "FK"}, +}; + +/* + * @mcc_str should have MCC as prefix, + * It doesn't matter if any thing else is followed. + * So it's okay to pass an IMSI. + */ +static inline const char * +get_country_iso_for_mcc (const char *mcc_str) +{ + g_autofree char *str = NULL; + guint64 mcc; + + if (!mcc_str || strlen (mcc_str) < 3) + return NULL; + + str = g_strndup (mcc_str, 3); + mcc = g_ascii_strtoull (str, NULL, 10); + + for (guint i = 0; i < G_N_ELEMENTS (mcc_list); i++) + if (mcc_list[i].mcc == mcc) + return mcc_list[i].code; + + g_warning ("invalid MCC code: %" G_GUINT64_FORMAT, mcc); + + return NULL; +} diff --git a/src/xeps/chatty-xep-0313.c b/src/xeps/chatty-xep-0313.c index 410654a..18e2140 100644 --- a/src/xeps/chatty-xep-0313.c +++ b/src/xeps/chatty-xep-0313.c @@ -5,7 +5,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -#define G_LOG_DOMAIN "chatty-xeps" +#define G_LOG_DOMAIN "chatty-xeps-0313" #include #include @@ -21,6 +21,7 @@ #include "chatty-pp-chat.h" #include "chatty-manager.h" #include "chatty-settings.h" +#include "chatty-log.h" #define NS_FWDv0 "urn:xmpp:forward:0" #define NS_SIDv0 "urn:xmpp:sid:0" @@ -177,7 +178,7 @@ static void chatty_mam_ctx_del(PurpleAccount *pa) { g_return_if_fail(pa != NULL); - g_debug("Cleaning context for %s", purple_account_get_username(pa)); + CHATTY_DEBUG (purple_account_get_username (pa), "Cleaning context for "); g_hash_table_remove(ht_mam_ctx, purple_account_get_username(pa)); } @@ -503,8 +504,8 @@ cb_chatty_mam_bare_info(PurpleConnection *pc, } mamq->start = g_date_time_format(dt,"%FT%TZ"); g_date_time_unref(dt); - g_debug ("Server supports MAM %s on %s; Querying by %s from %s after %s", - var, bare, qid, mamq->start, mamq->after); + CHATTY_DEBUG (bare, "Server supports MAM %s; Querying by %s from %s after %s, id:", + var, qid, mamq->start, mamq->after); // Request MAM backlog chatty_mam_query_archive(mamq); // Also - request preferences and correct them if required @@ -656,8 +657,12 @@ cb_chatty_mam_msg_received (PurpleConnection *pc, node_sid = xmlnode_get_child_with_namespace (msg, "stanza-id", NS_SIDv0); if(mamc->cur_msg != NULL) { + g_autofree char *str = NULL; + + str = g_strdup_printf ("%s %s", from, to); + // Skip resubmission - break the loop - g_debug ("Received resubmission %s %s %s", id, from, to); + CHATTY_DEBUG (str, "Received resubmission %s", id); return FALSE; } @@ -706,7 +711,7 @@ cb_chatty_mam_msg_received (PurpleConnection *pc, // store the SID in history at the least - to know where to start. message = msg; peer = from; - g_debug ("Received forward id %s from %s", stanza_id, peer); + CHATTY_DEBUG (peer, "Received forward id %s from", stanza_id); } // check history and drop the dup msg_type = xmlnode_get_attrib(message, "type"); @@ -760,7 +765,7 @@ cb_chatty_mam_msg_received (PurpleConnection *pc, message = msg; peer = from; } - g_debug ("Stealing parser for MAM, from %s at ID %s", peer, stanza_id); + CHATTY_DEBUG (peer, "Stealing parser for MAM, ID %s user:", stanza_id); /** * Before we resume message processing we need to pre-cook the message. * If there's no body the whole server_got_stuff is skipped, so we may never diff --git a/src/xeps/chatty-xep-0352.c b/src/xeps/chatty-xep-0352.c index 0182226..3c98a91 100644 --- a/src/xeps/chatty-xep-0352.c +++ b/src/xeps/chatty-xep-0352.c @@ -12,6 +12,7 @@ #include "users/chatty-pp-account.h" #include "chatty-xep-0352.h" #include "xeps.h" +#include "chatty-log.h" #include #include "prpl.h" @@ -67,9 +68,6 @@ on_screensaver_active_changed (GtkApplication *app) if (!CHATTY_IS_PP_ACCOUNT (ca)) continue; - if (chatty_item_is_sms (CHATTY_ITEM (ca))) - continue; - if (!chatty_pp_account_has_features (ca, CHATTY_PP_ACCOUNT_FEATURES_CSI)) continue; @@ -79,9 +77,8 @@ on_screensaver_active_changed (GtkApplication *app) if (!conn) continue; - g_debug ("Setting csi for %s to %sactive", - purple_account_get_username (pa), - blank ? "in" : ""); + CHATTY_DEBUG (purple_account_get_username (pa), "Setting csi to %sactive user:", + blank ? "in" : ""); csi_set_active (conn, !blank); } } @@ -111,7 +108,7 @@ on_xmlnode_received (PurpleConnection *gc, if (!g_strcmp0 (xmlns, CHATTY_XEPS_NS_CSI)) { ChattyPpAccount *ca = chatty_pp_account_get_object (pa); g_return_if_fail (ca); - g_debug ("Server of %s supports CSI", purple_account_get_username (pa)); + CHATTY_DEBUG (purple_account_get_username (pa), "Server supports CSI, user:"); chatty_pp_account_update_features (ca, CHATTY_PP_ACCOUNT_FEATURES_CSI); /* Sync status with screen blank */ diff --git a/src/xeps/xeps.c b/src/xeps/xeps.c index 499b30f..6249f7c 100644 --- a/src/xeps/xeps.c +++ b/src/xeps/xeps.c @@ -16,6 +16,7 @@ #include "chatty-xep-0184.h" #include "chatty-xep-0313.h" #include "chatty-xep-0352.h" +#include "chatty-log.h" static PurplePlugin *jabber = NULL; @@ -67,7 +68,7 @@ jabber_disco_bare_items_result_cb(JabberStream *js, const char *from, continue; // find out some useful nodes perhaps? Like, eh... ava, nick - g_debug ("Discovered node %s on bare %s", node, jid); + CHATTY_DEBUG (jid, "Discovered node %s on bare", node); purple_signal_emit (jabber, "jabber-bare-items", js->gc, jid, node); } } diff --git a/tests/account.c b/tests/account.c index 2aaf34d..4f5211d 100644 --- a/tests/account.c +++ b/tests/account.c @@ -32,18 +32,13 @@ test_account (ChattyAccount *ac, g_assert_true (CHATTY_IS_PP_ACCOUNT (ac)); - if (is_sms) - g_assert_true (chatty_item_is_sms (CHATTY_ITEM (ac))); - else - g_assert_false (chatty_item_is_sms (CHATTY_ITEM (ac))); - account = chatty_pp_account_get_account (CHATTY_PP_ACCOUNT (ac)); g_assert_nonnull (account); str = chatty_pp_account_get_protocol_id (CHATTY_PP_ACCOUNT (ac)); g_assert_cmpstr (str, ==, protocol_id); - str = chatty_account_get_username (CHATTY_ACCOUNT (ac)); + str = chatty_item_get_username (CHATTY_ITEM (ac)); g_assert_cmpstr (str, ==, username); chatty_account_set_enabled (ac, TRUE); diff --git a/tests/matrix-db.c b/tests/matrix-db.c index 4331dd8..4587cb3 100644 --- a/tests/matrix-db.c +++ b/tests/matrix-db.c @@ -53,7 +53,7 @@ account_matches_username (gconstpointer account, g_assert_true (CHATTY_IS_MA_ACCOUNT ((gpointer)account)); g_assert_true (id && *id); - return g_strcmp0 (id, chatty_account_get_username ((gpointer)account)) == 0; + return g_strcmp0 (id, chatty_item_get_username ((gpointer)account)) == 0; } static void @@ -114,7 +114,7 @@ add_matrix_account (MatrixDb *db, g_assert_no_error (error); g_assert_true (success); g_assert_cmpstr (g_object_get_data (G_OBJECT (task), "username"), ==, - chatty_account_get_username (CHATTY_ACCOUNT (account))); + chatty_item_get_username (CHATTY_ITEM (account))); g_assert_cmpint (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (task), "enabled")), ==, GPOINTER_TO_INT (g_object_get_data (object, "enabled"))); g_assert_cmpstr (g_object_get_data (G_OBJECT (task), "pickle"), ==, diff --git a/tests/utils.c b/tests/utils.c index eb80add..8583292 100644 --- a/tests/utils.c +++ b/tests/utils.c @@ -108,14 +108,14 @@ test_utils_username_valid (void) ChattyProtocol account_protocol; } data; data array[] = { - { "0123456789", CHATTY_PROTOCOL_SMS}, - { "+1 (1234) 5678", CHATTY_PROTOCOL_SMS}, - { "+91123456789", CHATTY_PROTOCOL_SMS}, - { "+91-1234-56789", CHATTY_PROTOCOL_SMS}, - { "+1 213 321 4567", CHATTY_PROTOCOL_SMS | CHATTY_PROTOCOL_TELEGRAM}, - { "+12133214567", CHATTY_PROTOCOL_SMS | CHATTY_PROTOCOL_TELEGRAM}, - { "+919995123456", CHATTY_PROTOCOL_SMS | CHATTY_PROTOCOL_TELEGRAM}, - { "5555", CHATTY_PROTOCOL_SMS}, + { "0123456789", CHATTY_PROTOCOL_MMS_SMS}, + { "+1 (1234) 5678", CHATTY_PROTOCOL_MMS_SMS}, + { "+91123456789", CHATTY_PROTOCOL_MMS_SMS}, + { "+91-1234-56789", CHATTY_PROTOCOL_MMS_SMS}, + { "+1 213 321 4567", CHATTY_PROTOCOL_MMS_SMS | CHATTY_PROTOCOL_TELEGRAM}, + { "+12133214567", CHATTY_PROTOCOL_MMS_SMS | CHATTY_PROTOCOL_TELEGRAM}, + { "+919995123456", CHATTY_PROTOCOL_MMS_SMS | CHATTY_PROTOCOL_TELEGRAM}, + { "5555", CHATTY_PROTOCOL_MMS_SMS}, { "valid@xmpp.example.com", CHATTY_PROTOCOL_XMPP}, { "@valid:example.com", CHATTY_PROTOCOL_MATRIX}, { "@നല്ല:matrix.example.com", 0}, -- GitLab From 763d4c84ef4e0f5003b64f495fbed171cdc0f2cf Mon Sep 17 00:00:00 2001 From: Evangelos Ribeiro Tzaras Date: Tue, 31 Aug 2021 23:29:16 +0200 Subject: [PATCH 4/6] d/control: Update build dependencies --- debian/control | 2 ++ 1 file changed, 2 insertions(+) diff --git a/debian/control b/debian/control index 26601f5..e05f347 100644 --- a/debian/control +++ b/debian/control @@ -15,9 +15,11 @@ Build-Depends: libebook1.2-dev, libfeedback-dev, libgcrypt20-dev, + libgspell-1-dev, libgtk-3-dev, libhandy-1-dev (>= 1.1.90), libjson-glib-dev, + libmm-glib-dev (>= 1.12.0), libolm-dev, libphonenumber-dev, libpurple-dev, -- GitLab From e44172ef125b45286ee0759404f4a80860b73884 Mon Sep 17 00:00:00 2001 From: Evangelos Ribeiro Tzaras Date: Wed, 1 Sep 2021 02:12:37 +0200 Subject: [PATCH 5/6] Document and release 0.4.0beta2-1 into experimental --- debian/changelog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/debian/changelog b/debian/changelog index d07116e..5c6faa6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +chatty (0.4.0~beta2-1) experimental; urgency=medium + + * New upstream version 0.4.0~beta2 + * d/control: Add libgspell-1-dev build dependency + + -- Evangelos Ribeiro Tzaras Tue, 31 Aug 2021 23:30:07 +0200 + chatty (0.3.4-2) unstable; urgency=medium * ci: No need to use experimental anymore -- GitLab From e8f149b19b1fa4b1cc7f18a66c6f8c99b3b106cd Mon Sep 17 00:00:00 2001 From: Evangelos Ribeiro Tzaras Date: Thu, 2 Sep 2021 09:28:14 +0200 Subject: [PATCH 6/6] Document and release 0.4.0~beta2-1pureos1 --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 66d3db3..74bb41a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +chatty (0.4.0~beta2-1pureos1) byzantium; urgency=medium + + * Upload to byzantium + + -- Evangelos Ribeiro Tzaras Thu, 02 Sep 2021 09:27:30 +0200 + chatty (0.4.0~beta2-1) experimental; urgency=medium * New upstream version 0.4.0~beta2 -- GitLab