From bd8c8b5a8c7568412b12ccfa94410786a752f9c9 Mon Sep 17 00:00:00 2001 From: "M. Sarmad Qadeer" Date: Tue, 14 May 2024 23:59:51 +0500 Subject: [PATCH 1/5] web: fix the links of faq & some other (#4144) * web: fix the links of faq & some other * web: fix links * web: update .eleventy.js to fix links * web: fix some links and improve the logic * web: fix links & improve the logic * remove semicolon * update links --------- Co-authored-by: Evgeny Poberezkin --- PRIVACY.md | 7 +- ...221206-simplex-chat-v4.3-voice-messages.md | 2 +- ...vision-funding-v5-videos-files-passcode.md | 2 +- ...local-file-encryption-directory-service.md | 2 +- ...desktop-quantum-resistant-better-groups.md | 2 +- ...40416-dangers-of-metadata-in-messengers.md | 6 +- ...ransparency-v5-7-better-user-experience.md | 8 +-- blog/lang/fr-fr/README_fr.md | 40 +++++------ docs/CLI.md | 2 +- docs/SECURITY.md | 2 +- docs/lang/cs/CLI.md | 2 +- docs/lang/cs/README.md | 38 +++++----- docs/lang/cs/SERVER.md | 2 +- docs/lang/fr/CLI.md | 2 +- docs/lang/pl/CLI.md | 10 +-- docs/lang/pl/README.md | 72 +++++++++---------- docs/lang/pl/SERVER.md | 2 +- docs/lang/pl/SIMPLEX.md | 2 +- website/.eleventy.js | 39 +++++++--- 19 files changed, 132 insertions(+), 110 deletions(-) diff --git a/PRIVACY.md b/PRIVACY.md index 3b4ba2ba8a..66dff0e807 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -1,5 +1,6 @@ --- layout: layouts/privacy.html +permalink: /privacy/index.html --- # SimpleX Chat Privacy Policy and Conditions of Use @@ -10,7 +11,7 @@ SimpleX Chat communication protocol is the first protocol that has no user profi Double ratchet algorithm has such important properties as [forward secrecy](/docs/GLOSSARY.md#forward-secrecy), sender [repudiation](/docs/GLOSSARY.md#) and break-in recovery (also known as [post-compromise security](/docs/GLOSSARY.md#post-compromise-security)). -If you believe that any part of this document is not aligned with our mission or values, please raise it with us via [email](chat@simplex.chat) or [chat](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23%2F%3Fv%3D1%26dh%3DMCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion). +If you believe that any part of this document is not aligned with our mission or values, please raise it with us via [email](mailto:chat@simplex.chat) or [chat](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23%2F%3Fv%3D1%26dh%3DMCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion). ## Privacy Policy @@ -78,7 +79,7 @@ When you choose to use instant push notifications in SimpleX iOS app, because th Preset notification server cannot observe the actual addresses of these queues, as a separate address is used to subscribe to the notifications. It also cannot observe who sends messages to you. Apple push notifications servers can only observe how many notifications are sent to you, but not from how many contacts, or from which messaging relays, as notifications are delivered to your device end-to-end encrypted by one of the preset notification servers - these notifications only contain end-to-end encrypted metadata, not even encrypted message content, and they look completely random to Apple push notification servers. -You can read more about the design of iOS push notifications [here](https://simplex.chat/blog/20220404-simplex-chat-instant-notifications.html#our-ios-approach-has-one-trade-off). +You can read more about the design of iOS push notifications [here](./blog/20220404-simplex-chat-instant-notifications.md#our-ios-approach-has-one-trade-off). #### Another information stored on the servers @@ -115,7 +116,7 @@ We will update this Privacy Policy as needed so that it is current, accurate, an Please also read our Conditions of Use of Software and Infrastructure below. -If you have questions about our Privacy Policy please contact us via [email](chat@simplex.chat) or [chat](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23%2F%3Fv%3D1%26dh%3DMCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion). +If you have questions about our Privacy Policy please contact us via [email](mailto:chat@simplex.chat) or [chat](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23%2F%3Fv%3D1%26dh%3DMCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion). ## Conditions of Use of Software and Infrastructure diff --git a/blog/20221206-simplex-chat-v4.3-voice-messages.md b/blog/20221206-simplex-chat-v4.3-voice-messages.md index 1ca25ce5d0..07a6e227f0 100644 --- a/blog/20221206-simplex-chat-v4.3-voice-messages.md +++ b/blog/20221206-simplex-chat-v4.3-voice-messages.md @@ -14,7 +14,7 @@ permalink: "/blog/20221206-simplex-chat-v4.3-voice-messages.html" ## SimpleX Chat reviews -Since we published [the security assessment of SimpleX Chat](https://simplex.chat/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html) completed by Trail of Bits in November, several sites published the reviews and included it in their recommendations: +Since we published [the security assessment of SimpleX Chat](./20221108-simplex-chat-v4.2-security-audit-new-website.md) completed by Trail of Bits in November, several sites published the reviews and included it in their recommendations: - Privacy Guides added SimpleX Chat to [the recommended private and secure messengers](https://www.privacyguides.org/real-time-communication/#simplex-chat). - Mike Kuketz – a well-known security expert – published [the review of SimpleX Chat](https://www.kuketz-blog.de/simplex-eindruecke-vom-messenger-ohne-identifier/) and added it to [the messenger matrix](https://www.messenger-matrix.de). diff --git a/blog/20230422-simplex-chat-vision-funding-v5-videos-files-passcode.md b/blog/20230422-simplex-chat-vision-funding-v5-videos-files-passcode.md index 0a66124934..6f32586bc1 100644 --- a/blog/20230422-simplex-chat-vision-funding-v5-videos-files-passcode.md +++ b/blog/20230422-simplex-chat-vision-funding-v5-videos-files-passcode.md @@ -42,7 +42,7 @@ Many large tech companies prioritizing value extraction over value creation earn We started working full-time on the project in 2021 when [Portman Wills](https://www.linkedin.com/in/portmanwills/) and [Peter Briffett](https://www.linkedin.com/in/peterbriffett/) (the founders of [Wagestream](https://wagestream.com/en/) where I led the engineering team) supported the company very early on, and several other angel investors joined later. In July 2022 SimpleX Chat raised a pre-seed funding from the VC fund [Village Global](https://www.villageglobal.vc) - its co-founder [Ben Casnocha](https://casnocha.com) was very excited about our vision of privacy-first fully decentralized messaging and community platform, both for the individual users and for the companies, independent of any crypto-currencies, that might grow to replace large centralized platforms, such as WhatsApp, Telegram and Signal. -Overall we raised from our investors approximately $370,000 for a small share of the company to allow the project team working full time for almost two years, funding product design and development, infrastructure, and also [the security assessment by Trail of Bits](https://simplex.chat/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html). A large part of this money is not spent yet. +Overall we raised from our investors approximately $370,000 for a small share of the company to allow the project team working full time for almost two years, funding product design and development, infrastructure, and also [the security assessment by Trail of Bits](./20221108-simplex-chat-v4.2-security-audit-new-website.md). A large part of this money is not spent yet. The project was hugely supported by the users as well - collectively, [you donated](https://github.com/simplex-chat/simplex-chat#help-us-with-donations) over $25,000. Without these donations the investment we raised would not be possible, because we believe that voluntary user donations can sustain the project in the long term – it already covers all infrastructure costs. There are only two ways an Internet service can exist - either users are paying for it, or the users data becomes the product for the real customers, as happened with many large Internet companies. In the latter case the users are losing much more money than they are saving by giving away their privacy and the rights to the content they create on the centralized platforms. diff --git a/blog/20230925-simplex-chat-v5-3-desktop-app-local-file-encryption-directory-service.md b/blog/20230925-simplex-chat-v5-3-desktop-app-local-file-encryption-directory-service.md index fc924a8706..0222c25d77 100644 --- a/blog/20230925-simplex-chat-v5-3-desktop-app-local-file-encryption-directory-service.md +++ b/blog/20230925-simplex-chat-v5-3-desktop-app-local-file-encryption-directory-service.md @@ -50,7 +50,7 @@ Other limitations of the desktop app: - you cannot send voice messages. - there is no support for calls yet. -You can download the desktop app for Linux and Mac via [downloads page](https://simplex.chat/downloads). Windows version will be available soon. +You can download the desktop app for Linux and Mac via [downloads page](../docs/DOWNLOADS.md). Windows version will be available soon. ## Group directory service and other group improvements diff --git a/blog/20231125-simplex-chat-v5-4-link-mobile-desktop-quantum-resistant-better-groups.md b/blog/20231125-simplex-chat-v5-4-link-mobile-desktop-quantum-resistant-better-groups.md index 4fbfc400ad..7f50446bfa 100644 --- a/blog/20231125-simplex-chat-v5-4-link-mobile-desktop-quantum-resistant-better-groups.md +++ b/blog/20231125-simplex-chat-v5-4-link-mobile-desktop-quantum-resistant-better-groups.md @@ -40,7 +40,7 @@ This is only possible when both devices are connected to the same local network. **On desktop** -If you don't have desktop app installed yet, [download it](https://simplex.chat/downloads/) and create any chat profile - you don't need to use it, and when you create it there are no server requests sent and no accounts are created. Think about it as about user profile on your computer. +If you don't have desktop app installed yet, [download it](../docs/DOWNLOADS.md) and create any chat profile - you don't need to use it, and when you create it there are no server requests sent and no accounts are created. Think about it as about user profile on your computer. Then in desktop app settings choose *Link a mobile* - it will show a QR code. diff --git a/blog/20240416-dangers-of-metadata-in-messengers.md b/blog/20240416-dangers-of-metadata-in-messengers.md index 3b30003798..2b99c755d3 100644 --- a/blog/20240416-dangers-of-metadata-in-messengers.md +++ b/blog/20240416-dangers-of-metadata-in-messengers.md @@ -33,7 +33,7 @@ For example, while WhatsApp messages are [end-to-end encrypted](https://faq.what This is called [metadata](https://en.wikipedia.org/wiki/Metadata). It reveals a wealth of information about you and your connections, and in the hands of a centralized monopoly, this can and does get misused in incredibly dangerous ways. Once such metadata is logged, it can create very detailed profiles about who you are, everywhere you’ve been, and everyone you’ve ever spoken to. In settling for apps that normalize this while giving you the illusion of privacy in their marketing, we are doing ourselves a disservice by accepting this as the default. Collectively, we aren’t doing enough to protect ourselves and our social graph from this invasive overreach. -When stored, aggregated and analyzed, this metadata provides ample information that could potentially incriminate someone or be submitted to authorities. When WhatsApp and Facebook Messenger enabled end-to-end encryption for messages, of course it was a welcome and widely celebrated change. But it’s important to remember that not all end-to-end encryption utilizes the same standards, [some implementations are more secure](https://simplex.chat/blog/20240314-simplex-chat-v5-6-quantum-resistance-signal-double-ratchet-algorithm.html#how-secure-is-end-to-end-encryption-in-different-messengers) than others, so it’s something that shouldn’t necessarily be accepted at face value. More importantly: collecting and storing an obscene amount of metadata should invite global scrutiny, considering this data is often combined with whatever other information companies like Meta harvest about your identity (which is [a lot](https://www.vox.com/recode/23172691/meta-tracking-privacy-hospitals).) +When stored, aggregated and analyzed, this metadata provides ample information that could potentially incriminate someone or be submitted to authorities. When WhatsApp and Facebook Messenger enabled end-to-end encryption for messages, of course it was a welcome and widely celebrated change. But it’s important to remember that not all end-to-end encryption utilizes the same standards, [some implementations are more secure](./20240314-simplex-chat-v5-6-quantum-resistance-signal-double-ratchet-algorithm.md#how-secure-is-end-to-end-encryption-in-different-messengers) than others, so it’s something that shouldn’t necessarily be accepted at face value. More importantly: collecting and storing an obscene amount of metadata should invite global scrutiny, considering this data is often combined with whatever other information companies like Meta harvest about your identity (which is [a lot](https://www.vox.com/recode/23172691/meta-tracking-privacy-hospitals).) @@ -45,8 +45,8 @@ But we also need to acknowledge that the world is becoming increasingly dangerou End-to-end encryption is a solid start, but it's just the beginning of our pursuit for true privacy and security. True privacy means that even when legal demands come knocking, there's no useful metadata to hand over. It's not enough to just protect the content of messages; we need consistent innovation in protecting metadata too. -Changing ingrained habits is tough, but your privacy is always worth the fight. Although giants like WhatsApp and Telegram may dominate global messaging for now, increasing concerns about data harvesting and AI-driven surveillance are fueling demand for alternatives. SimpleX Chat aims to be one of those strong alternatives, hence its radical focus on a decentralized framework with no user identifiers (in other words, nothing that uniquely identifies users on the protocol level to their contacts or to the relays) and extra optionality (self-hosting an [SMP server](https://simplex.chat/docs/server.html) or [XFTP server](https://simplex.chat/docs/xftp-server.html), access via Tor, [chat profiles](https://simplex.chat/docs/guide/chat-profiles.html) with incognito mode, etc.) +Changing ingrained habits is tough, but your privacy is always worth the fight. Although giants like WhatsApp and Telegram may dominate global messaging for now, increasing concerns about data harvesting and AI-driven surveillance are fueling demand for alternatives. SimpleX Chat aims to be one of those strong alternatives, hence its radical focus on a decentralized framework with no user identifiers (in other words, nothing that uniquely identifies users on the protocol level to their contacts or to the relays) and extra optionality (self-hosting an [SMP server](../docs/server.md) or [XFTP server](../docs/xftp-server.md), access via Tor, [chat profiles](../docs/guide/chat-profiles.md) with incognito mode, etc.) As of today, most messaging alternatives, including SimpleX, will have some limitations. But with the limited resources we have, we are committed to daily progress towards creating a truly private messenger that anyone can use while maintaining the features that users have come to know and love in messaging interfaces. We want to be the prime example of a messenger that achieves genuine privacy without compromising it for convenience. We need to be able to reliably move away from small and niche use cases to endorsing and enforcing global standards for privacy and making it accessible for all users regardless of their technical expertise. -We’re grateful for the users and [donors](https://github.com/simplex-chat/simplex-chat#help-us-with-donations) who have been following along on this journey thus far and helping with feedback, anything from bug reports to identifying potential risks. Building in the open has always been a necessity for transparency and ongoing [auditability](https://simplex.chat/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html), because we don’t want anyone to just take our word for it. [See for yourself](https://github.com/simplex-chat) and engage in the discussions. We fully expect you to hold us accountable to our word. +We’re grateful for the users and [donors](https://github.com/simplex-chat/simplex-chat#help-us-with-donations) who have been following along on this journey thus far and helping with feedback, anything from bug reports to identifying potential risks. Building in the open has always been a necessity for transparency and ongoing [auditability](./20221108-simplex-chat-v4.2-security-audit-new-website.md), because we don’t want anyone to just take our word for it. [See for yourself](https://github.com/simplex-chat) and engage in the discussions. We fully expect you to hold us accountable to our word. diff --git a/blog/20240426-simplex-legally-binding-transparency-v5-7-better-user-experience.md b/blog/20240426-simplex-legally-binding-transparency-v5-7-better-user-experience.md index a321dbc6fa..59d5897f41 100644 --- a/blog/20240426-simplex-legally-binding-transparency-v5-7-better-user-experience.md +++ b/blog/20240426-simplex-legally-binding-transparency-v5-7-better-user-experience.md @@ -23,10 +23,10 @@ Also, we added Lithuanian interface language to the Android and desktop apps, th We are committed to open-source, privacy and security. Here are the recent changes we made: -- We now have a [Transparency Reports](https://simplex.chat/transparency/) page. -- We updated our [Privacy Policy](https://github.com/simplex-chat/simplex-chat/blob/stable/PRIVACY.md) to remove undefined terms "impermissible" and "acceptable", which would allow us to remove anything we don't like, without any clarity on what that is. You can see the edits [here](https://github.com/simplex-chat/simplex-chat/pull/4076/files). -- We published a new page with [Frequently Asked Questions](https://simplex.chat/faq/), thanks to the guidance from users. -- We also have a new [Security Policy](https://simplex.chat/security/) – we welcome your feedback on it. +- We now have a [Transparency Reports](../docs/TRANSPARENCY.md) page. +- We updated our [Privacy Policy](../PRIVACY.md) to remove undefined terms "impermissible" and "acceptable", which would allow us to remove anything we don't like, without any clarity on what that is. You can see the edits [here](https://github.com/simplex-chat/simplex-chat/pull/4076/files). +- We published a new page with [Frequently Asked Questions](../docs/FAQ.md), thanks to the guidance from users. +- We also have a new [Security Policy](../docs/SECURITY.md) – we welcome your feedback on it. What do we mean by “legally binding transparency?”. It includes these principles: - Accountability: an empty promise or commitment to transparency that is not legally binding is just marketing, and can provide opportunities for the organizations to be misleading or not disclose important information that can affect their users privacy and security. diff --git a/blog/lang/fr-fr/README_fr.md b/blog/lang/fr-fr/README_fr.md index bebc42d7bc..471f3ce046 100644 --- a/blog/lang/fr-fr/README_fr.md +++ b/blog/lang/fr-fr/README_fr.md @@ -1,6 +1,6 @@ # Blog -4 févr. 2023 [SimpleX Chat v4.5 publié](./20230103-simplex-chat-v4.4-disappearing-messages.md) +4 févr. 2023 [SimpleX Chat v4.5 publié](../../20230103-simplex-chat-v4.4-disappearing-messages.md) - profils de chat multiples. - brouillon de message. @@ -10,7 +10,7 @@ Nous avons également ajouté [l'interface en italien](#french-language-interface), grâce à nos utilisateurs et à Weblate ! -3 janv. 2023 [SimpleX Chat v4.4 publié](./20230103-simplex-chat-v4.4-disappearing-messages.md) +3 janv. 2023 [SimpleX Chat v4.4 publié](../../20230103-simplex-chat-v4.4-disappearing-messages.md) - messages éphèméres. - messages "en direct" (dynamique). @@ -19,7 +19,7 @@ Nous avons également ajouté [l'interface en italien](#french-language-interfac Nous avons également ajouté [l'interface en français](#french-language-interface), grâce à nos utilisateurs et à Weblate ! -6 déc. 2022 [SimpleX Chat : révision et sortie de la v4.3](./20221206-simplex-chat-v4.3-voice-messages.md) +6 déc. 2022 [SimpleX Chat : révision et sortie de la v4.3](../../20221206-simplex-chat-v4.3-voice-messages.md) Critiques de novembre : @@ -35,7 +35,7 @@ Sortie de la v4.3 : - amélioration de la configuration du serveur SMP et du support des mots de passe du serveur - améliorations de la confidentialité et de la sécurité : protection de l'écran de l'application, sécurité des liens SimpleX, etc. -8 nov. 2022 [Audit de sécurité par Trail of Bits, nouveau site web et sortie de la v4.2](./20221108-simplex-chat-v4.2-security-audit-new-website.md) +8 nov. 2022 [Audit de sécurité par Trail of Bits, nouveau site web et sortie de la v4.2](../../20221108-simplex-chat-v4.2-security-audit-new-website.md) _"Avez-vous été audité ou devons-nous simplement vous ignorer ?"_ @@ -51,7 +51,7 @@ Sortie de la v4.2 : - changer manuellement de contact ou de membre vers une autre adresse / serveur (BETA) - recevoir des fichiers plus rapidement (BETA) -28 sept. 2022 [v4 : chiffrement de la base de données locale](./20220928-simplex-chat-v4-encrypted-database.md) +28 sept. 2022 [v4 : chiffrement de la base de données locale](../../20220928-simplex-chat-v4-encrypted-database.md) - base de données locale de chat chiffrée - si vous utilisez déjà l'application, vous pouvez chiffrer la base de données dans les paramètres de l'application. - support pour les serveurs WebRTC ICE auto-hébergés @@ -61,7 +61,7 @@ Sortie de la v4.2 : - support des images animées dans l'application Android - Interface utilisateur en allemand pour les applications mobiles -1 sept. 2022 [v3.2 : Mode Incognito](./20220901-simplex-chat-v3.2-incognito-mode.md) +1 sept. 2022 [v3.2 : Mode Incognito](../../20220901-simplex-chat-v3.2-incognito-mode.md) - Mode Incognito - utiliser un nouveau nom de profil aléatoire pour chaque contact - utiliser des adresses de serveur .onion avec Tor @@ -71,7 +71,7 @@ Sortie de la v4.2 : L'audit d'implémentation est prévu pour Octobre ! -8 août 2022 [v3.1 : groupes de discussion](./20220808-simplex-chat-v3.1-chat-groups.md) +8 août 2022 [v3.1 : groupes de discussion](../../20220808-simplex-chat-v3.1-chat-groups.md) - enfin, des groupes de chat secrets - personne d'autre que les membres ne sait qu'ils existent ! - accès aux serveurs de messagerie via Tor sur toutes les plateformes @@ -79,37 +79,37 @@ L'audit d'implémentation est prévu pour Octobre ! - protocole de chat publié - nouvelles icônes d'application -23 juil. 2022 [v3.1-beta : accès aux serveurs via Tor](./20220723-simplex-chat-v3.1-tor-groups-efficiency.md) +23 juil. 2022 [v3.1-beta : accès aux serveurs via Tor](../../20220723-simplex-chat-v3.1-tor-groups-efficiency.md) - application terminale : accès aux serveurs de messagerie via un proxy SOCKS5 (par exemple, Tor). - applications mobiles : rejoindre et quitter des groupes de discussion. - utilisation optimisée de la batterie et du trafic - réduction jusqu'à 90x ! - deux configurations docker pour les serveurs SMP auto-hébergés. -11 juil. 2022 [v3 : notifications push instantanées pour iOS et appels audio/vidéo](./20220711-simplex-chat-v3-released-ios-notifications-audio-video-calls-database-export-import-protocol-improvements.md) : +11 juil. 2022 [v3 : notifications push instantanées pour iOS et appels audio/vidéo](../../20220711-simplex-chat-v3-released-ios-notifications-audio-video-calls-database-export-import-protocol-improvements.md) : - exportation et importation de la base de données de chat - appels audio/vidéo chiffrés de bout en bout - amélioration de la confidentialité du protocole et des performances -4 juin 2022 [v2.2 : nouveaux paramètres de confidentialité et de sécurité](./20220604-simplex-chat-new-privacy-security-settings.md) +4 juin 2022 [v2.2 : nouveaux paramètres de confidentialité et de sécurité](../../20220604-simplex-chat-new-privacy-security-settings.md) -24 mai 2022 [v2.1 : effacement des messages pour une meilleure confidentialité des conversations](./20220524-simplex-chat-better-privacy.md) +24 mai 2022 [v2.1 : effacement des messages pour une meilleure confidentialité des conversations](../../20220524-simplex-chat-better-privacy.md) -11 mai 2022 [Publication de la v2.0 - envoi d'images et de fichiers dans les applications mobiles](./20220511-simplex-chat-v2-images-files.md) +11 mai 2022 [Publication de la v2.0 - envoi d'images et de fichiers dans les applications mobiles](../../20220511-simplex-chat-v2-images-files.md) -04 avr. 2022 [Notifications instantanées pour les applications mobiles SimpleX Chat](./20220404-simplex-chat-instant-notifications.md) +04 avr. 2022 [Notifications instantanées pour les applications mobiles SimpleX Chat](../../20220404-simplex-chat-instant-notifications.md) -08 mars 2022 [Applications mobiles pour iOS et Android](./20220308-simplex-chat-mobile-apps.md) +08 mars 2022 [Applications mobiles pour iOS et Android](../../20220308-simplex-chat-mobile-apps.md) -14 févr. 2022. [SimpleX Chat : rejoignez notre version bêta publique pour iOS](./20220214-simplex-chat-ios-public-beta.md) +14 févr. 2022. [SimpleX Chat : rejoignez notre version bêta publique pour iOS](../../20220214-simplex-chat-ios-public-beta.md) -12 janv. 2022. [SimpleX Chat v1 : la plateforme de chat et d'application la plus privée et la plus sécurisée](./20220112-simplex-chat-v1-released.md) +12 janv. 2022. [SimpleX Chat v1 : la plateforme de chat et d'application la plus privée et la plus sécurisée](../../20220112-simplex-chat-v1-released.md) -08 déc. 2021. [Sortie de SimpleX Chat v0.5 : la première plateforme de chat 100% privée par définition - aucun accès à votre graphe de connexions](./20211208-simplex-chat-v0.5-released.md) +08 déc. 2021. [Sortie de SimpleX Chat v0.5 : la première plateforme de chat 100% privée par définition - aucun accès à votre graphe de connexions](../../20211208-simplex-chat-v0.5-released.md) -14 septembre 2021. [SimpleX Chat v0.4 publié : chat open-source qui utilise un protocole de routage de messages préservant la confidentialité](./20210914-simplex-chat-v0.4-released.md) +14 septembre 2021. [SimpleX Chat v0.4 publié : chat open-source qui utilise un protocole de routage de messages préservant la confidentialité](../../20210914-simplex-chat-v0.4-released.md) -12 mai 2021. [Prototype de chat SimpleX](./20210512-simplex-chat-terminal-ui.md) +12 mai 2021. [Prototype de chat SimpleX](../../20210512-simplex-chat-terminal-ui.md) -22 oct. 2020. [SimpleX Chat](./20201022-simplex-chat.md) +22 oct. 2020. [SimpleX Chat](../../20201022-simplex-chat.md) diff --git a/docs/CLI.md b/docs/CLI.md index d4f799c7af..abc09b0e7c 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -98,7 +98,7 @@ git checkout stable DOCKER_BUILDKIT=1 docker build --output ~/.local/bin . ``` -> **Please note:** If you encounter `` version `GLIBC_2.28' not found `` error, rebuild it with `haskell:8.10.7-stretch` base image (change it in your local [Dockerfile](Dockerfile)). +> **Please note:** If you encounter `` version `GLIBC_2.28' not found `` error, rebuild it with `haskell:8.10.7-stretch` base image (change it in your local [Dockerfile](/Dockerfile)). #### In any OS diff --git a/docs/SECURITY.md b/docs/SECURITY.md index 77f588ec26..0885e31725 100644 --- a/docs/SECURITY.md +++ b/docs/SECURITY.md @@ -8,7 +8,7 @@ revision: 23.04.2024 While great care is taken to ensure the highest level of security and privacy in SimpleX network servers and clients, all software can have flaws, and we believe it is a critical part of an organization's social responsibility to minimize the impact of these flaws through continual vulnerability discovery efforts, defense in depth design, and prompt remediation and notification. -The security assessment of SimpleX cryptography and networking was done by Trail of Bits in [November 2022](https://simplex.chat/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html). +The security assessment of SimpleX cryptography and networking was done by Trail of Bits in [November 2022](../blog/20221108-simplex-chat-v4.2-security-audit-new-website.md). We are planning design review of SimpleX protocols in July 2024 and implementation review in December 2024/January 2025. diff --git a/docs/lang/cs/CLI.md b/docs/lang/cs/CLI.md index 9cbce8e6fe..338e48e57e 100644 --- a/docs/lang/cs/CLI.md +++ b/docs/lang/cs/CLI.md @@ -97,7 +97,7 @@ git checkout stable DOCKER_BUILDKIT=1 docker build --output ~/.local/bin . ``` -> **Upozornění:** Pokud narazíte na chybu `` verze `GLIBC_2.28' nenalezena ``, obnovte jej pomocí základního obrazu `haskell:8.10.7-stretch` (změňte jej ve svém lokálním [Dockerfile](Dockerfile)). +> **Upozornění:** Pokud narazíte na chybu `` verze `GLIBC_2.28' nenalezena ``, obnovte jej pomocí základního obrazu `haskell:8.10.7-stretch` (změňte jej ve svém lokálním [Dockerfile](/Dockerfile)). #### V libovolném operačním systému diff --git a/docs/lang/cs/README.md b/docs/lang/cs/README.md index f536cb1aa6..7eab61395e 100644 --- a/docs/lang/cs/README.md +++ b/docs/lang/cs/README.md @@ -26,7 +26,7 @@ - 🚀 [TestFlight preview for iOS](https://testflight.apple.com/join/DWuT2LQu) s novými funkcemi o 1-2 týdny dříve - **omezeno na 10 000 uživatelů**! - 🖥 K dispozici jako terminálová (konzolová) [aplikace / CLI](#zap-quick-installation-of-a-terminal-app) v systémech Linux, MacOS, Windows. -**NOVINKA**: Bezpečnostní audit od [Trail of Bits](https://www.trailofbits.com/about), [nové webové stránky](https://simplex.chat) a vydána verze 4.2! [Viz oznámení](./blog/20221108-simplex-chat-v4.2-security-audit-new-website.md). +**NOVINKA**: Bezpečnostní audit od [Trail of Bits](https://www.trailofbits.com/about), [nové webové stránky](https://simplex.chat) a vydána verze 4.2! [Viz oznámení](../../../blog/20221108-simplex-chat-v4.2-security-audit-new-website.md). ## Obsah @@ -62,23 +62,23 @@ Nestačí používat end-to-end šifrovaný messenger, všichni bychom měli pou ### Úplné soukromí vaší identity, profilu, kontaktů a metadat. -**Na rozdíl od všech ostatních existujících platforem pro zasílání zpráv nemá SimpleX přiřazeny žádné identifikátory uživatelů** - dokonce ani náhodná čísla. To chrání soukromí toho, s kým komunikujete, a skrývá to před servery platformy SimpleX i před jakýmikoli pozorovateli. [Více informací](./docs/SIMPLEX.md#full-privacy-of-your-identity-profile-contacts-and-metadata). +**Na rozdíl od všech ostatních existujících platforem pro zasílání zpráv nemá SimpleX přiřazeny žádné identifikátory uživatelů** - dokonce ani náhodná čísla. To chrání soukromí toho, s kým komunikujete, a skrývá to před servery platformy SimpleX i před jakýmikoli pozorovateli. [Více informací](./SIMPLEX.md#full-privacy-of-your-identity-profile-contacts-and-metadata). ### Nejlepší ochrana proti spamu a zneužití -Protože na platformě SimpleX nemáte žádný identifikátor, nelze vás kontaktovat, pokud nesdílíte odkaz na jednorázovou pozvánku nebo volitelnou dočasnou uživatelskou adresu. [Více informací](./docs/SIMPLEX.md#nejlepší-ochrana-před-spamem-a-zneužitím). +Protože na platformě SimpleX nemáte žádný identifikátor, nelze vás kontaktovat, pokud nesdílíte odkaz na jednorázovou pozvánku nebo volitelnou dočasnou uživatelskou adresu. [Více informací](./SIMPLEX.md#nejlepší-ochrana-proti-spamu-a-zneužití). ### Úplné vlastnictví, kontrola a zabezpečení vašich dat -SimpleX ukládá všechna uživatelská data na klientských zařízeních, zprávy jsou pouze dočasně uchovávány na relay serverech SimpleX, dokud nejsou přijaty. [Více informací](./docs/SIMPLEX.md#complete-ownership-control-and-security-of-your-data). +SimpleX ukládá všechna uživatelská data na klientských zařízeních, zprávy jsou pouze dočasně uchovávány na relay serverech SimpleX, dokud nejsou přijaty. [Více informací](./SIMPLEX.md#complete-ownership-control-and-security-of-your-data). ### Uživatelé vlastní síť SimpleX -Můžete používat SimpleX s vlastními servery a přitom komunikovat s lidmi, kteří používají servery předkonfigurované v aplikacích nebo jakékoli jiné servery SimpleX. [Více informací](./docs/SIMPLEX.md#users-own-simplex-network). +Můžete používat SimpleX s vlastními servery a přitom komunikovat s lidmi, kteří používají servery předkonfigurované v aplikacích nebo jakékoli jiné servery SimpleX. [Více informací](./SIMPLEX.md#users-own-simplex-network). ## Často kladené otázky -1. _Jak může SimpleX doručovat zprávy bez identifikátorů uživatelů?_ Viz [oznámení o vydání v2](./blog/20220511-simplex-chat-v2-images-files.md#prvni-platforma-zasilani-zpráv-bez-identifikátoru-uživatele), kde je vysvětleno, jak SimpleX funguje. +1. _Jak může SimpleX doručovat zprávy bez identifikátorů uživatelů?_ Viz [oznámení o vydání v2](../../../blog/20220511-simplex-chat-v2-images-files.md#the-first-messaging-platform-without-user-identifiers), kde je vysvětleno, jak SimpleX funguje. 2. _Proč bych neměl používat jen Signal?_ Signal je centralizovaná platforma, která k identifikaci svých uživatelů a jejich kontaktů používá telefonní čísla. To znamená, že zatímco obsah vašich zpráv na službě Signal je chráněn robustním šifrováním end-to-end, pro službu Signal je viditelné velké množství metadat - s kým a kdy hovoříte. @@ -88,17 +88,17 @@ Můžete používat SimpleX s vlastními servery a přitom komunikovat s lidmi, Poslední aktualizace: V současné době je k dispozici několik nových aplikací, např: -[Vydání verze 4.5 - s více uživatelskými profily, návrhem zpráv, izolací transportu a italským rozhraním](./blog/20230204-simplex-chat-v4-5-user-chat-profiles.md). +[Vydání verze 4.5 - s více uživatelskými profily, návrhem zpráv, izolací transportu a italským rozhraním](../../../blog/20230204-simplex-chat-v4-5-user-chat-profiles.md). -[03. 01. 2023. v4.4 vydána - s mizejícími zprávami, "živými" zprávami, bezpečnostním ověřováním spojení, GIFy a nálepkami a s francouzským jazykem rozhraní](./blog/20230103-simplex-chat-v4.4-disappearing-messages.md). +[03. 01. 2023. v4.4 vydána - s mizejícími zprávami, "živými" zprávami, bezpečnostním ověřováním spojení, GIFy a nálepkami a s francouzským jazykem rozhraní](../../../blog/20230103-simplex-chat-v4.4-disappearing-messages.md). -[prosinec 06, 2022. Listopadové recenze a vydána verze 4.3 - s okamžitými hlasovými zprávami, nevratným mazáním odeslaných zpráv a vylepšenou konfigurací serveru](./blog/20221206-simplex-chat-v4.3-hlasove-zpravy.md). +[prosinec 06, 2022. Listopadové recenze a vydána verze 4.3 - s okamžitými hlasovými zprávami, nevratným mazáním odeslaných zpráv a vylepšenou konfigurací serveru](../../../blog/20221206-simplex-chat-v4.3-voice-messages.md). -[Nov 08, 2022. Bezpečnostní audit Trail of Bits, vydány nové webové stránky a verze 4.2](./blog/20221108-simplex-chat-v4.2-security-audit-new-website.md). +[Nov 08, 2022. Bezpečnostní audit Trail of Bits, vydány nové webové stránky a verze 4.2](../../../blog/20221108-simplex-chat-v4.2-security-audit-new-website.md). -[28. 9. 2022. v4.0: šifrovaná lokální databáze chatu a mnoho dalších změn](./blog/20220928-simplex-chat-v4-encrypted-database.md). +[28. 9. 2022. v4.0: šifrovaná lokální databáze chatu a mnoho dalších změn](../../../blog/20220928-simplex-chat-v4-encrypted-database.md). -[Všechny aktualizace](./blog) +[Všechny aktualizace](../../../blog) ## Vytvoření soukromého připojení @@ -118,13 +118,13 @@ Po instalaci chatovacího klienta jednoduše spusťte `simplex-chat` z terminál ![simplex-chat](./images/connection.gif) -Více informací o [instalaci a používání terminálové aplikace](./docs/CLI.md). +Více informací o [instalaci a používání terminálové aplikace](./CLI.md). ## Návrh platformy SimpleX SimpleX je síť klient-server s unikátní topologií sítě, která využívá redundantní, jednorázové uzly pro předávání zpráv (relay nodes) k asynchronnímu předávání zpráv prostřednictvím jednosměrných (simplexních) front zpráv, což zajišťuje anonymitu příjemce i odesílatele. -Na rozdíl od sítí P2P jsou všechny zprávy předávány přes jeden nebo několik serverových uzlů, které ani nemusí mít perzistenci. Současná implementace [SMP serveru](https://github.com/simplex-chat/simplexmq#smp-server) ve skutečnosti používá ukládání zpráv v paměti a uchovává pouze záznamy o frontách. SimpleX poskytuje lepší ochranu metadat než návrhy P2P, protože k doručování zpráv se nepoužívají globální identifikátory účastníků, a vyhýbá se [problémům sítí P2P](./docs/SIMPLEX.md#comparison-with-p2p-messaging-protocols). +Na rozdíl od sítí P2P jsou všechny zprávy předávány přes jeden nebo několik serverových uzlů, které ani nemusí mít perzistenci. Současná implementace [SMP serveru](https://github.com/simplex-chat/simplexmq#smp-server) ve skutečnosti používá ukládání zpráv v paměti a uchovává pouze záznamy o frontách. SimpleX poskytuje lepší ochranu metadat než návrhy P2P, protože k doručování zpráv se nepoužívají globální identifikátory účastníků, a vyhýbá se [problémům sítí P2P](./SIMPLEX.md#comparison-with-p2p-messaging-protocols). Na rozdíl od federativních sítí nemají uzly serveru **záznamy o uživatelích**, **nekomunikují mezi sebou** a **neukládají zprávy** po jejich doručení příjemcům. Neexistuje způsob, jak zjistit úplný seznam serverů účastnících se sítě SimpleX. Tato konstrukce se vyhýbá problému viditelnosti metadat, který mají všechny federované sítě, a lépe chrání před útoky na celou síť. @@ -132,7 +132,7 @@ Informace o uživatelích, jejich kontaktech a skupinách mají pouze klientská Další informace o cílech a technickém návrhu platformy naleznete v dokumentu [SimpleX whitepaper](https://github.com/simplex-chat/simplexmq/blob/stable/protocol/overview-tjr.md). -Formát zpráv zasílaných mezi klienty chatu prostřednictvím [SimpleX Messaging Protocol](https://github.com/simplex-chat/simplexmq/blob/stable/protocol/simplex-messaging.md) viz [SimpleX Chat Protocol](./docs/protocol/simplex-chat.md). +Formát zpráv zasílaných mezi klienty chatu prostřednictvím [SimpleX Messaging Protocol](https://github.com/simplex-chat/simplexmq/blob/stable/protocol/simplex-messaging.md) viz [SimpleX Chat Protocol](../../protocol/simplex-chat.md). ## Soukromí: technické podrobnosti a omezení @@ -149,7 +149,7 @@ Co je již implementováno: 6. Počínaje verzí v2 protokolu SMP (současná verze je v4) jsou všechna metadata zprávy včetně času, kdy byla zpráva přijata serverem (zaokrouhleno na sekundy), odesílána příjemcům uvnitř šifrované obálky, takže ani v případě kompromitace TLS je nelze pozorovat. 7. Pro spojení klient-server je povoleno pouze TLS 1.2/1.3, omezené na kryptografické algoritmy: CHACHA20POLY1305_SHA256, Ed25519/Ed448, Curve25519/Curve448. 8. Na ochranu proti útokům typu replay vyžadují servery SimpleX [tlsunique channel binding](https://www.rfc-editor.org/rfc/rfc5929.html) jako ID relace v každém klientském příkazu podepsaném efemérním klíčem per-queue. -9. Pro ochranu vaší IP adresy podporují všichni klienti SimpleX Chat přístup k serverům pro zasílání zpráv přes Tor - více informací najdete v [oznámení o vydání v3.1](./blog/20220808-simplex-chat-v3.1-chat-groups.md). +9. Pro ochranu vaší IP adresy podporují všichni klienti SimpleX Chat přístup k serverům pro zasílání zpráv přes Tor - více informací najdete v [oznámení o vydání v3.1](../../../blog/20220808-simplex-chat-v3.1-chat-groups.md). 10. Šifrování místní databáze s přístupovou frází - kontakty, skupiny a všechny odeslané a přijaté zprávy jsou uloženy šifrovaně. Pokud jste používali SimpleX Chat před verzí 4.0, musíte šifrování povolit prostřednictvím nastavení aplikace. 11. Izolace transportu - pro provoz různých uživatelských profilů se používají různá spojení TCP a okruhy Tor, volitelně - pro různá spojení kontaktů a členů skupin. @@ -166,7 +166,7 @@ Můžete: - použít knihovnu SimpleX Chat k integraci funkcí chatu do svých mobilních aplikací. - vytvářet chatovací boty a služby v jazyce Haskell - viz [simple](./apps/simplex-bot/) a více [advanced chat bot example](./apps/simplex-bot-advanced/). - vytvářet chatovací boty a služby v libovolném jazyce se spuštěným terminálem SimpleX Chat CLI jako lokálním serverem WebSocket. Viz [TypeScript SimpleX Chat client](./packages/simplex-chat-client/) a [JavaScript chat bot example](./packages/simplex-chat-client/typescript/examples/squaring-bot.js). -- spustit [simplex-chat terminal CLI](./docs/CLI.md) pro provádění jednotlivých příkazů chatu, např. pro odesílání zpráv v rámci provádění shellových skriptů. +- spustit [simplex-chat terminal CLI](./CLI.md) pro provádění jednotlivých příkazů chatu, např. pro odesílání zpráv v rámci provádění shellových skriptů. Pokud uvažujete o vývoji s platformou SimpleX, obraťte se na nás pro případné rady a podporu. @@ -253,7 +253,7 @@ Aktuální jazyky rozhraní: - Italština: [@unbranched](https://github.com/unbranched) - Ruština: projektový tým -Jazyky ve vývoji: Čínština, hindština, čeština, japonština, holandština a [mnoho dalších](https://hosted.weblate.org/projects/simplex-chat/#languages). Další jazyky budeme přidávat, jakmile budou některé z již přidaných jazyků dokončeny - navrhněte prosím nové jazyky, projděte si [průvodce překladem](./docs/TRANSLATIONS.md) a kontaktujte nás! +Jazyky ve vývoji: Čínština, hindština, čeština, japonština, holandština a [mnoho dalších](https://hosted.weblate.org/projects/simplex-chat/#languages). Další jazyky budeme přidávat, jakmile budou některé z již přidaných jazyků dokončeny - navrhněte prosím nové jazyky, projděte si [průvodce překladem](./TRANSLATIONS.md) a kontaktujte nás! ## Přispívejte @@ -294,7 +294,7 @@ Zakladatel SimpleX Chat Protokoly a bezpečnostní model [SimpleX](https://github.com/simplex-chat/simplexmq/blob/master/protocol/overview-tjr.md) byly revidovány a ve verzi 1.0.0 došlo k mnoha zlomovým změnám a vylepšením. -Bezpečnostní audit provedla v říjnu 2022 společnost [Trail of Bits](https://www.trailofbits.com/about) a většina oprav byla vydána ve verzi 4.2.0 - viz [oznámení](./blog/20221108-simplex-chat-v4.2-security-audit-new-website.md). +Bezpečnostní audit provedla v říjnu 2022 společnost [Trail of Bits](https://www.trailofbits.com/about) a většina oprav byla vydána ve verzi 4.2.0 - viz [oznámení](../../../blog/20221108-simplex-chat-v4.2-security-audit-new-website.md). SimpleX Chat je stále relativně ranou fází platformy (mobilní aplikace byly vydány v březnu 2022), takže můžete objevit některé chyby a chybějící funkce. Velmi oceníme, pokud nám dáte vědět o všem, co je třeba opravit nebo vylepšit. diff --git a/docs/lang/cs/SERVER.md b/docs/lang/cs/SERVER.md index 3dd2f3780c..f75adeb8cf 100644 --- a/docs/lang/cs/SERVER.md +++ b/docs/lang/cs/SERVER.md @@ -12,7 +12,7 @@ SMP server je relay server používaný k předávání zpráv v síti SimpleX. Klienti SimpleX pouze určují, který server bude použit pro příjem zpráv, a to pro každý kontakt (nebo spojení skupiny s členem skupiny) zvlášť, přičemž tyto servery jsou pouze dočasné, protože adresa pro doručování se může změnit. -_Upozornění_: když změníte servery v konfiguraci aplikace, ovlivní to pouze to, který server bude použit pro nové kontakty, stávající kontakty se na nové servery automaticky nepřesunou, ale můžete je přesunout ručně pomocí tlačítka ["Změnit adresu příjmu"](../blog/20221108-simplex-chat-v4.2-bezpecnostni-audit-novy-website.md#zmeny-dorucovani-adresy-beta) na stránkách s informacemi o kontaktech/členech - brzy bude automatizováno. +_Upozornění_: když změníte servery v konfiguraci aplikace, ovlivní to pouze to, který server bude použit pro nové kontakty, stávající kontakty se na nové servery automaticky nepřesunou, ale můžete je přesunout ručně pomocí tlačítka ["Změnit adresu příjmu"](../../../blog/20221108-simplex-chat-v4.2-security-audit-new-website.md#change-your-delivery-address-beta) na stránkách s informacemi o kontaktech/členech - brzy bude automatizováno. ## Instalace diff --git a/docs/lang/fr/CLI.md b/docs/lang/fr/CLI.md index bb596491f1..e5093f20c0 100644 --- a/docs/lang/fr/CLI.md +++ b/docs/lang/fr/CLI.md @@ -97,7 +97,7 @@ git checkout stable DOCKER_BUILDKIT=1 docker build --output ~/.local/bin . ``` -> **Veuillez noter** : Si vous rencontrez l'erreur ``version `GLIBC_2.28' non trouvée``, reconstruisez-le avec l'image de base `haskell:8.10.7-stretch`(changez-la dans votre [Dockerfile](Dockerfile) local). +> **Veuillez noter** : Si vous rencontrez l'erreur ``version `GLIBC_2.28' non trouvée``, reconstruisez-le avec l'image de base `haskell:8.10.7-stretch`(changez-la dans votre [Dockerfile](/Dockerfile) local). #### Utiliser Haskell stack diff --git a/docs/lang/pl/CLI.md b/docs/lang/pl/CLI.md index 585eca3e31..0a72b163bb 100644 --- a/docs/lang/pl/CLI.md +++ b/docs/lang/pl/CLI.md @@ -97,7 +97,7 @@ git checkout stable DOCKER_BUILDKIT=1 docker build --output ~/.local/bin . ``` -> **Uwaga:** Jeśli napotkasz błąd `` version `GLIBC_2.28' not found ``, przebuduj go z obrazem bazowym `haskell:8.10.7-stretch` (zmień go w Twoim lokalnym pliku [Dockerfile](Dockerfile)). +> **Uwaga:** Jeśli napotkasz błąd `` version `GLIBC_2.28' not found ``, przebuduj go z obrazem bazowym `haskell:8.10.7-stretch` (zmień go w Twoim lokalnym pliku [Dockerfile](/Dockerfile)). #### Używając Haskella na dowolnym systemie operacyjnym @@ -200,7 +200,7 @@ Po uruchomieniu czatu zostaniesz poproszony o podanie swojej "nazwy wyświetlane Poniższy schemat przedstawia sposób łączenia się z kontaktem i wysyłania do niego wiadomości:
- +
Gdy już skonfigurujesz swój profil lokalny, wpisz `/c` (oznaczające `/connect`), aby utworzyć nowe połączenie i wygenerować zaproszenie. Wyślij to zaproszenie do swojego kontaktu za pośrednictwem dowolnego innego kanału komunikacji. @@ -219,7 +219,7 @@ Użyj `/help` na czacie, by uzyskać listę pozostałych dostępnych komend. Aby utworzyć grupę, użyj `/g `, a następnie dodaj do niej kontakty za pomocą `/a `. Możesz wysyłać wiadomości do grupy wpisując `# `. Użyj `/help groups`, by uzyskać listę pozostałych dostępnych komend. -![simplex-chat](../images/groups.gif) +![simplex-chat](/images/groups.gif) > **Uwaga**: informacje o grupach nie są przechowywane na żadnym serwerze, są one zapisywane jako lista członków w bazie danych aplikacji klientów, do których będą wysyłane wiadomości. @@ -227,7 +227,7 @@ Aby utworzyć grupę, użyj `/g `, a następnie dodaj do niej konta Możesz wysłać plik do kontaktu za pomocą `/f @ <ścieżka_do_pliku>` - odbiorca będzie musiał go zaakceptować przed rozpoczęciem wysyłania. Użyj `/help files`, by uzyskać listę pozostałych dostępnych komend. -![simplex-chat](../images/files.gif) +![simplex-chat](/images/files.gif) Możesz wysyłać pliki do grupy za pomocą `/f # <ścieżka_do_pliku>`. @@ -241,4 +241,4 @@ Prośby o kontakt możesz przyjąć za pomocą komendy `/ac ` oraz odrzuc Użyj `/help address`, by uzyskać listę pozostałych dostępnych komend. -![simplex-chat](../images/user-addresses.gif) +![simplex-chat](/images/user-addresses.gif) diff --git a/docs/lang/pl/README.md b/docs/lang/pl/README.md index ba40a42d74..23ca00c3e6 100644 --- a/docs/lang/pl/README.md +++ b/docs/lang/pl/README.md @@ -50,7 +50,7 @@ Możesz połączyć się z naszym zespołem za pośrednictwem aplikacji, korzyst Odpowiadamy na pytania manualnie, więc nie jest to natychmiastowe - może to potrwać do 24 godzin. -Jeśli jesteś zainteresowany pomocą w integracji otwartoźródłowych modeli językowych i [dołączeniem do naszego zespołu](./docs/lang/pl/JOIN_TEAM.md), skontaktuj się z nami. +Jeśli jesteś zainteresowany pomocą w integracji otwartoźródłowych modeli językowych i [dołączeniem do naszego zespołu](../../JOIN_TEAM.md), skontaktuj się z nami. ## Dołącz do grup użytkowników @@ -62,7 +62,7 @@ Możesz również: - krytykować aplikację i dokonywać porównań z innymi komunikatorami. - udostępniać nowe komunikatory, które Twoim zdaniem mogą być interesujące z punktu widzenia prywatności, o ile nie spamujesz. - udostępniać niektóre publikacje związane z prywatnością, raczej dość rzadko. -- po wstępnym zatwierdzeniu przez administratora w prywatnej wiadomości, udostępnić link do utworzonej grupy, ale tylko raz. Gdy grupa ma więcej niż 10 członków, może zostać przesłana do [SimpleX Directory Service](./docs/DIRECTORY.md), gdzie nowi użytkownicy będą mogli ją odkryć. +- po wstępnym zatwierdzeniu przez administratora w prywatnej wiadomości, udostępnić link do utworzonej grupy, ale tylko raz. Gdy grupa ma więcej niż 10 członków, może zostać przesłana do [SimpleX Directory Service](../../DIRECTORY.md), gdzie nowi użytkownicy będą mogli ją odkryć. Musisz: - być uprzejmym wobec innych użytkowników. @@ -104,11 +104,11 @@ Kanał, za pośrednictwem którego udostępniasz link, nie musi być bezpieczny Wykonaj prywatne połączenie Conversation Połączenie wideo -Po wykonaniu połączenia możesz [zweryfikować kod bezpieczeństwa połączenia](./blog/20230103-simplex-chat-v4.4-disappearing-messages.md#connection-security-verification). +Po wykonaniu połączenia możesz [zweryfikować kod bezpieczeństwa połączenia](../../../blog/20230103-simplex-chat-v4.4-disappearing-messages.md#connection-security-verification). ## Poradnik dla użytkownika (NOWE) -Przeczytaj o funkcjach i ustawieniach aplikacji w nowym [Przewodniku użytkownika](./docs/guide/README.md). +Przeczytaj o funkcjach i ustawieniach aplikacji w nowym [Przewodniku użytkownika](../../guide/README.md). ## Pomóż nam przetłumaczyć SimpleX Chat @@ -139,13 +139,13 @@ Dołącz do naszych tłumaczy, aby pomóc SimpleX w rozwoju! |🇺🇦 uk|Українська| |[![android app](https://hosted.weblate.org/widgets/simplex-chat/uk/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/uk/)
[![ios app](https://hosted.weblate.org/widgets/simplex-chat/uk/ios/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/ios/uk/)|[![website](https://hosted.weblate.org/widgets/simplex-chat/uk/website/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/website/uk/)|| |🇨🇳 zh-CHS|简体中文|[sith-on-mars](https://github.com/sith-on-mars)

[Float-hu](https://github.com/Float-hu)|[![android app](https://hosted.weblate.org/widgets/simplex-chat/zh_Hans/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/)
[![ios app](https://hosted.weblate.org/widgets/simplex-chat/zh_Hans/ios/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/ios/zh_Hans/)
 |

[![website](https://hosted.weblate.org/widgets/simplex-chat/zh_Hans/website/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/website/zh_Hans/)|| -Trwają prace nad wersjami językowymi: Arabski, japoński, koreański, portugalski i [inne](https://hosted.weblate.org/projects/simplex-chat/#languages). Będziemy dodawać kolejne języki, gdy niektóre z już dodanych zostaną ukończone - zasugeruj nowe języki, przejrzyj [przewodnik po tłumaczeniach](./docs/lang/pl/TRANSLATIONS.md) i skontaktuj się z nami! +Trwają prace nad wersjami językowymi: Arabski, japoński, koreański, portugalski i [inne](https://hosted.weblate.org/projects/simplex-chat/#languages). Będziemy dodawać kolejne języki, gdy niektóre z już dodanych zostaną ukończone - zasugeruj nowe języki, przejrzyj [przewodnik po tłumaczeniach](./TRANSLATIONS.md) i skontaktuj się z nami! ## Kontrybuuj Chcielibyśmy, abyś przyczynił się do naszego rozwoju! Możesz nam pomóc: -- [dzieląc się motywem kolorystycznym](./docs/THEMES.md), którego używasz w aplikacji na Androida! +- [dzieląc się motywem kolorystycznym](../../THEMES.md), którego używasz w aplikacji na Androida! - pisząc samouczki lub poradniki, które dotyczą hostowania serwerów, automatyzacji czatbotów itp. - współtworząc bazy wiedzy SimpleX Chat. - rozwijając funkcje - skontaktuj się z nami za pośrednictwem czatu, abyśmy mogli pomóc Ci zacząć. @@ -208,23 +208,23 @@ Używanie szyfrowanego komunikatora end-to-end nie jest wystarczające. Powinni ### Kompletna prywatność Twojej tożsamości, profilu, kontaktów i metadanych. -**W przeciwieństwie do innych komunikatorów, SimpleX nie posiada żadnych identyfikatorów przypisanych do użytkowników**. Nie posiada nawet numerów generowanych losowo. Zapewnia to prywatność tego, z kim się komunikujesz, ukrywając jego tożsamość oraz fakt komunikacji przed serwerami platformy SimpleX i wszelkimi obserwatorami [Czytaj więcej](./docs/lang/pl/SIMPLEX.md#full-privacy-of-your-identity-profile-contacts-and-metadata). +**W przeciwieństwie do innych komunikatorów, SimpleX nie posiada żadnych identyfikatorów przypisanych do użytkowników**. Nie posiada nawet numerów generowanych losowo. Zapewnia to prywatność tego, z kim się komunikujesz, ukrywając jego tożsamość oraz fakt komunikacji przed serwerami platformy SimpleX i wszelkimi obserwatorami [Czytaj więcej](./SIMPLEX.md#full-privacy-of-your-identity-profile-contacts-and-metadata). ### Najlepsza ochrona przed spamem i nadużyciami -Ponieważ na platformie SimpleX nie masz identyfikatora ani stałego adresu, nikt nie może się z Tobą skontaktować, chyba że udostępnisz jednorazowy lub tymczasowy adres użytkownika, w postaci kodu QR lub linku. [Czytaj więcej](./docs/lang/pl/SIMPLEX.md#the-best-protection-against-spam-and-abuse). +Ponieważ na platformie SimpleX nie masz identyfikatora ani stałego adresu, nikt nie może się z Tobą skontaktować, chyba że udostępnisz jednorazowy lub tymczasowy adres użytkownika, w postaci kodu QR lub linku. [Czytaj więcej](./SIMPLEX.md#the-best-protection-against-spam-and-abuse). ### Pełna kontrola i bezpieczeństwo Twoich danych -SimpleX przechowuje wszystkie dane użytkownika na urządzeniach klienckich, wiadomości są przechowywane tymczasowo na serwerach przekaźnikowych SimpleX do momentu ich odebrania, po czym są trwale usuwane. [Czytaj więcej](./docs/lang/pl/SIMPLEX.md#complete-ownership-control-and-security-of-your-data). +SimpleX przechowuje wszystkie dane użytkownika na urządzeniach klienckich, wiadomości są przechowywane tymczasowo na serwerach przekaźnikowych SimpleX do momentu ich odebrania, po czym są trwale usuwane. [Czytaj więcej](./SIMPLEX.md#complete-ownership-control-and-security-of-your-data). ### Użytkownicy są właścicielami sieci SimpleX -Możesz używać SimpleX na własnych serwerach i nadal komunikować się z ludźmi za pomocą serwerów, które są wstępnie skonfigurowane w aplikacjach lub z dowolnymi innymi serwerami SimpleX. [Czytaj więcej](./docs/lang/pl/SIMPLEX.md#users-own-simplex-network). +Możesz używać SimpleX na własnych serwerach i nadal komunikować się z ludźmi za pomocą serwerów, które są wstępnie skonfigurowane w aplikacjach lub z dowolnymi innymi serwerami SimpleX. [Czytaj więcej](./SIMPLEX.md#users-own-simplex-network). ## Często zadawane pytania -1. _W jaki sposób SimpleX może dostarczać wiadomości bez jakichkolwiek identyfikatorów użytkownika?_ Zobacz [ogłoszenie wydania v2](./blog/20220511-simplex-chat-v2-images-files.md#the-first-messaging-platform-without-user-identifiers) wyjaśniające jak SimpleX działa. +1. _W jaki sposób SimpleX może dostarczać wiadomości bez jakichkolwiek identyfikatorów użytkownika?_ Zobacz [ogłoszenie wydania v2](../../../blog/20220511-simplex-chat-v2-images-files.md#the-first-messaging-platform-without-user-identifiers) wyjaśniające jak SimpleX działa. 2. _Dlaczego po prostu nie mogę używać Signal?_ Signal to scentralizowana platforma, która wykorzystuje numery telefonów do identyfikacji użytkowników i ich kontaktów. Oznacza to, że podczas gdy treść wiadomości w Signal jest chroniona solidnym szyfrowaniem end-to-end, istnieje duża ilość metadanych widocznych dla Signal - to, z kim rozmawiasz i kiedy. @@ -234,29 +234,29 @@ Możesz używać SimpleX na własnych serwerach i nadal komunikować się z lud Najnowsze i ważne wiadomości: -[Mar 23, 2024. SimpleX network: real privacy and stable profits, non-profits for protocols, v5.6 released with quantum resistant e2e encryption and simple profile migration.](./blog/20240323-simplex-network-privacy-non-profit-v5-6-quantum-resistant-e2e-encryption-simple-migration.md) +[Mar 23, 2024. SimpleX network: real privacy and stable profits, non-profits for protocols, v5.6 released with quantum resistant e2e encryption and simple profile migration.](../../../blog/20240323-simplex-network-privacy-non-profit-v5-6-quantum-resistant-e2e-encryption-simple-migration.md) -[Mar 14, 2024. SimpleX Chat v5.6 beta: adding quantum resistance to Signal double ratchet algorithm.](./blog/20240314-simplex-chat-v5-6-quantum-resistance-signal-double-ratchet-algorithm.md) +[Mar 14, 2024. SimpleX Chat v5.6 beta: adding quantum resistance to Signal double ratchet algorithm.](../../../blog/20240314-simplex-chat-v5-6-quantum-resistance-signal-double-ratchet-algorithm.md) -[Jan 24, 2024. SimpleX Chat: free infrastructure from Linode, v5.5 released with private notes, group history and a simpler UX to connect.](./blog/20240124-simplex-chat-infrastructure-costs-v5-5-simplex-ux-private-notes-group-history.md) +[Jan 24, 2024. SimpleX Chat: free infrastructure from Linode, v5.5 released with private notes, group history and a simpler UX to connect.](../../../blog/20240124-simplex-chat-infrastructure-costs-v5-5-simplex-ux-private-notes-group-history.md) -[Nov 25, 2023. SimpleX Chat v5.4 released: link mobile and desktop apps via quantum resistant protocol, and much better groups](./blog/20231125-simplex-chat-v5-4-link-mobile-desktop-quantum-resistant-better-groups.md). +[Nov 25, 2023. SimpleX Chat v5.4 released: link mobile and desktop apps via quantum resistant protocol, and much better groups](../../../blog/20231125-simplex-chat-v5-4-link-mobile-desktop-quantum-resistant-better-groups.md). -[Sep 25, 2023. SimpleX Chat v5.3 released: desktop app, local file encryption, improved groups and directory service](./blog/20230925-simplex-chat-v5-3-desktop-app-local-file-encryption-directory-service.md). +[Sep 25, 2023. SimpleX Chat v5.3 released: desktop app, local file encryption, improved groups and directory service](../../../blog/20230925-simplex-chat-v5-3-desktop-app-local-file-encryption-directory-service.md). -[Jul 22, 2023. SimpleX Chat: v5.2 released with message delivery receipts](./blog/20230722-simplex-chat-v5-2-message-delivery-receipts.md). +[Jul 22, 2023. SimpleX Chat: v5.2 released with message delivery receipts](../../../blog/20230722-simplex-chat-v5-2-message-delivery-receipts.md). -[May 23, 2023. SimpleX Chat: v5.1 released with message reactions and self-destruct passcode](./blog/20230523-simplex-chat-v5-1-message-reactions-self-destruct-passcode.md). +[May 23, 2023. SimpleX Chat: v5.1 released with message reactions and self-destruct passcode](../../../blog/20230523-simplex-chat-v5-1-message-reactions-self-destruct-passcode.md). -[Apr 22, 2023. SimpleX Chat: vision and funding, v5.0 released with videos and files up to 1gb](./blog/20230422-simplex-chat-vision-funding-v5-videos-files-passcode.md). +[Apr 22, 2023. SimpleX Chat: vision and funding, v5.0 released with videos and files up to 1gb](../../../blog/20230422-simplex-chat-vision-funding-v5-videos-files-passcode.md). -[Mar 1, 2023. SimpleX File Transfer Protocol – send large files efficiently, privately and securely, soon to be integrated into SimpleX Chat apps.](./blog/20230301-simplex-file-transfer-protocol.md). +[Mar 1, 2023. SimpleX File Transfer Protocol – send large files efficiently, privately and securely, soon to be integrated into SimpleX Chat apps.](../../../blog/20230301-simplex-file-transfer-protocol.md). -[Nov 8, 2022. Security audit by Trail of Bits, the new website and v4.2 released](./blog/20221108-simplex-chat-v4.2-security-audit-new-website.md). +[Nov 8, 2022. Security audit by Trail of Bits, the new website and v4.2 released](../../../blog/20221108-simplex-chat-v4.2-security-audit-new-website.md). -[Sep 28, 2022. v4.0: encrypted local chat database and many other changes](./blog/20220928-simplex-chat-v4-encrypted-database.md). +[Sep 28, 2022. v4.0: encrypted local chat database and many other changes](../../../blog/20220928-simplex-chat-v4-encrypted-database.md). -[All updates](./blog) +[All updates](../../../blog) ## :zap: Szybka instalacja terminalowej wersji aplikacji @@ -268,13 +268,13 @@ Po pobraniu klienta czatu można go uruchomić za pomocą polecenia `simplex-cha ![simplex-chat](./images/connection.gif) -Przeczytaj więcej o [instalowaniu i używaniu terminalowej wersji czatu](./docs/lang/pl/CLI.md). +Przeczytaj więcej o [instalowaniu i używaniu terminalowej wersji czatu](./CLI.md). ## Budowa Platformy SimpleX SimpleX to sieć typu klient-serwer z unikatową topologią sieciową, która wykorzystuje redundantne, jednorazowe węzły przekazywania wiadomości do asynchronicznego przekazywania wiadomości za pośrednictwem jednokierunkowych (simpleksowych) kolejek wiadomości, zapewniając anonimowość odbiorcy i nadawcy. -W przeciwieństwie do sieci P2P, wszystkie wiadomości są przekazywane przez jeden lub kilka węzłów serwera, które nawet nie muszą być trwałe. Obecna implementacja [serwera SMP](https://github.com/simplex-chat/simplexmq#smp-server) wykorzystuje przechowywanie wiadomości w pamięci, utrzymując jedynie rejestr kolejki. SimpleX zapewnia lepszą ochronę metadanych niż projekty P2P, ponieważ żadne globalne identyfikatory uczestników nie są używane do dostarczania wiadomości i pozwala to uniknąć [różnych problemów związanych z sieciami P2P](./docs/lang/pl/SIMPLEX.md#comparison-with-p2p-messaging-protocols). +W przeciwieństwie do sieci P2P, wszystkie wiadomości są przekazywane przez jeden lub kilka węzłów serwera, które nawet nie muszą być trwałe. Obecna implementacja [serwera SMP](https://github.com/simplex-chat/simplexmq#smp-server) wykorzystuje przechowywanie wiadomości w pamięci, utrzymując jedynie rejestr kolejki. SimpleX zapewnia lepszą ochronę metadanych niż projekty P2P, ponieważ żadne globalne identyfikatory uczestników nie są używane do dostarczania wiadomości i pozwala to uniknąć [różnych problemów związanych z sieciami P2P](./SIMPLEX.md#comparison-with-p2p-messaging-protocols). W przeciwieństwie do sieci sfederowanych, węzły serwera **nie posiadają danych użytkowników**, **nie komunikują się ze sobą** i **nie przechowują wiadomości** po ich dostarczeniu do odbiorców. Nie ma możliwości na odkrycie pełnej listy serwerów działających w sieci SimpleX. Taka konstrukcja pozwala uniknąć problemu związanego z widocznością metadanych, z którym borykają się wszystkie sieci sfederowane i pozwala ona na lepszą ochronę przed atakami obejmującymi całą sieć. @@ -282,29 +282,29 @@ Informacje o użytkownikach, ich kontaktach i grupach znajdują się wyłącznie Przeczytaj [whitepaper SimpleX](https://github.com/simplex-chat/simplexmq/blob/stable/protocol/overview-tjr.md) po więcej informacji o zadaniach platformy oraz by dowiedzieć się jak wygląda koncepcja techniczna modelu. -Zobacz [Protokół Czatu SimpleX](./docs/protocol/simplex-chat.md) by dowiedzieć się o formacie wiadomości wysyłanych między klientem czatu za pośrednictwem [Protokołu Wiadomości SimpleX](https://github.com/simplex-chat/simplexmq/blob/stable/protocol/simplex-messaging.md). +Zobacz [Protokół Czatu SimpleX](../../protocol/simplex-chat.md) by dowiedzieć się o formacie wiadomości wysyłanych między klientem czatu za pośrednictwem [Protokołu Wiadomości SimpleX](https://github.com/simplex-chat/simplexmq/blob/stable/protocol/simplex-messaging.md). ## Prywatność i bezpieczeństwo: szczegóły techniczne i ograniczenia Prace nad SimpleX Chat wciąż trwają - udostępniamy nowe ulepszenia, gdy tylko będą gotowe. To Ty musisz zdecydować, czy obecny stan jest wystarczająco dobry dla Twojego przypadku zastosowania. -Stworzyliśmy [słownik pojęć](./docs/GLOSSARY.md) używany do opisu systemów komunikacyjnych, aby pomóc zrozumieć niektóre z poniższych pojęć oraz aby pomóc Ci w porównaniu zalet i wad różnych systemów komunikacyjnych. +Stworzyliśmy [słownik pojęć](../../GLOSSARY.md) używany do opisu systemów komunikacyjnych, aby pomóc zrozumieć niektóre z poniższych pojęć oraz aby pomóc Ci w porównaniu zalet i wad różnych systemów komunikacyjnych. Co zostało już wprowadzone: -1. Zamiast identyfikatorów użytkownika używanych przez wszystkie inne platformy, nawet te najbardziej prywatne, SimpleX używa [pairwise per-queue identifiers](./docs/GLOSSARY.md#pairwise-pseudonymous-identifier) (2 adresy dla każdej jednokierunkowej kolejki wiadomości, z opcjonalnym trzecim adresem dla powiadomień push na iOS, 2 kolejki w każdym połączeniu między użytkownikami). Sprawia to, że trudniej jest w ten sposób obserwować przebieg połączeń sieciowych na poziomie aplikacji, ponieważ dla `n` użytkowników może istnieć do `n * (n-1)` kolejek wiadomości. -2. [Szyfrowanie end-to-end](./docs/GLOSSARY.md#end-to-end-encryption) w każdej kolejce wiadomości używając [cryptoboxa NaCl](https://nacl.cr.yp.to/box.html). Zostało to dodane, aby umożliwić redundancję w przyszłości (przekazywanie każdej wiadomości przez kilka serwerów), aby uniknąć posiadania tego samego ciphertext w różnych kolejkach (które byłyby widoczne tylko dla atakującego, w przypadku przejęcia TLS). Klucze szyfrujące używane do tego szyfrowania nie są rotowane, zamiast tego planujemy rotować kolejki. Do negocjacji kluczy używane są klucze Curve25519. -3. Szyfrowanie end-to-end [double ratchet](./docs/GLOSSARY.md#double-ratchet-algorithm) w każdej rozmowie między dwoma użytkownikami (lub członkami grupy). Jest to ten sam algorytm, który jest używany w Signal i wielu innych komunikatorach; zapewnia on komunikację OTR z [forward secrecy](./docs/GLOSSARY.md#forward-secrecy) (każda wiadomość jest szyfrowana własnym kluczem efemerycznym) i [break-in recovery](./docs/GLOSSARY.md#post-compromise-security) (klucze są często renegocjowane w ramach wymiany wiadomości). Dwie pary kluczy Curve448 są używane do początkowego [key agreement](./docs/GLOSSARY.md#key-agreement-protocol), strona inicjująca przekazuje te klucze przez link połączenia, a strona akceptująca - w nagłówku wiadomości potwierdzającej. +1. Zamiast identyfikatorów użytkownika używanych przez wszystkie inne platformy, nawet te najbardziej prywatne, SimpleX używa [pairwise per-queue identifiers](../../GLOSSARY.md#pairwise-pseudonymous-identifier) (2 adresy dla każdej jednokierunkowej kolejki wiadomości, z opcjonalnym trzecim adresem dla powiadomień push na iOS, 2 kolejki w każdym połączeniu między użytkownikami). Sprawia to, że trudniej jest w ten sposób obserwować przebieg połączeń sieciowych na poziomie aplikacji, ponieważ dla `n` użytkowników może istnieć do `n * (n-1)` kolejek wiadomości. +2. [Szyfrowanie end-to-end](../../GLOSSARY.md#end-to-end-encryption) w każdej kolejce wiadomości używając [cryptoboxa NaCl](https://nacl.cr.yp.to/box.html). Zostało to dodane, aby umożliwić redundancję w przyszłości (przekazywanie każdej wiadomości przez kilka serwerów), aby uniknąć posiadania tego samego ciphertext w różnych kolejkach (które byłyby widoczne tylko dla atakującego, w przypadku przejęcia TLS). Klucze szyfrujące używane do tego szyfrowania nie są rotowane, zamiast tego planujemy rotować kolejki. Do negocjacji kluczy używane są klucze Curve25519. +3. Szyfrowanie end-to-end [double ratchet](../../GLOSSARY.md#double-ratchet-algorithm) w każdej rozmowie między dwoma użytkownikami (lub członkami grupy). Jest to ten sam algorytm, który jest używany w Signal i wielu innych komunikatorach; zapewnia on komunikację OTR z [forward secrecy](../../GLOSSARY.md#forward-secrecy) (każda wiadomość jest szyfrowana własnym kluczem efemerycznym) i [break-in recovery](../../GLOSSARY.md#post-compromise-security) (klucze są często renegocjowane w ramach wymiany wiadomości). Dwie pary kluczy Curve448 są używane do początkowego [key agreement](../../GLOSSARY.md#key-agreement-protocol), strona inicjująca przekazuje te klucze przez link połączenia, a strona akceptująca - w nagłówku wiadomości potwierdzającej. 4. Dodatkowa warstwa szyfrowania przy użyciu NaCL cryptobox dla wiadomości dostarczanych z serwera do odbiorcy. Warstwa ta pozwala uniknąć wspólnego szyfrogramu między wysyłanym i odbieranym ruchem serwera wewnątrz TLS (i nie ma też wspólnych identyfikatorów). -5. Kilka poziomów [content padding](./docs/GLOSSARY.md#message-padding) w celu utrudnienia ataków na rozmiar wiadomości. +5. Kilka poziomów [content padding](../../GLOSSARY.md#message-padding) w celu utrudnienia ataków na rozmiar wiadomości. 6. Wszystkie metadane wiadomości, w tym czas odebrania wiadomości przez serwer (zaokrąglony do sekundy), są wysyłane do odbiorców w zaszyfrowanej postaci, więc nawet jeśli TLS zostanie przejęty, nie można ich zobaczyć. 7. Dozwolone są tylko TLS 1.2/1.3 dla połączeń klient-serwer, z ograniczeniem do algorytmów kryptograficznych: CHACHA20POLY1305_SHA256, Ed25519/Ed448, Curve25519/Curve448. 8. Aby zapobiec atakom typu replay, serwery SimpleX wymagają [tlsunique channel binding](https://www.rfc-editor.org/rfc/rfc5929.html) jako identyfikatora sesji w każdym poleceniu klienta podpisanym kluczem efemerycznym dla każdej kolejki. -9. Aby ochronić swój adres IP, wszystkie klienty SimpleX Chat obsługują dostęp do serwerów komunikacyjnych za pośrednictwem Tora - zobacz [v3.1 release announcement](./blog/20220808-simplex-chat-v3.1-chat-groups.md) po więcej szczegółów. +9. Aby ochronić swój adres IP, wszystkie klienty SimpleX Chat obsługują dostęp do serwerów komunikacyjnych za pośrednictwem Tora - zobacz [v3.1 release announcement](../../../blog/20220808-simplex-chat-v3.1-chat-groups.md) po więcej szczegółów. 10. Lokalne szyfrowanie bazy danych z hasłem - kontakty, grupy oraz wszystkie wysłane i odebrane wiadomości są przechowywane w postaci zaszyfrowanej. Jeśli korzystałeś z SimpleX Chat przed wersją v4.0, musisz włączyć szyfrowanie w ustawieniach aplikacji. 11. Izolacja transportu - różne połączenia TCP i obwody Tor używane są dla ruchu różnych profili użytkowników, opcjonalnie - dla różnych kontaktów i połączeń członków grupy. 12. Ręczne obracanie kolejki wiadomości w celu przeniesienia konwersacji do innego przekaźnika SMP. -13. Wysyłanie zaszyfrowanych plików end-to-end przy użyciu [protokołu XFTP](https://simplex.chat/blog/20230301-simplex-file-transfer-protocol.html). +13. Wysyłanie zaszyfrowanych plików end-to-end przy użyciu [protokołu XFTP](../../../blog/20230301-simplex-file-transfer-protocol.md). 14. Szyfrowanie plików lokalnych. Planujemy dodać: @@ -322,7 +322,7 @@ Możesz: - korzystać z biblioteki SimpleX Chat w celu zintegrowania funkcji czatu z aplikacjami mobilnymi. - tworzyć boty i usługi czatu w języku Haskell - zobacz [prosty](./apps/simplex-bot/) i bardziej [zaawansowany przykład bota czatu](./apps/simplex-bot-advanced/). - tworzenie chat botów i usług w dowolnym języku z wykorzystaniem terminala CLI SimpleX Chat jako lokalnego serwera WebSocket. Zobacz [TypeScript SimpleX Chat client](./packages/simplex-chat-client/) i [JavaScript chat bot example](./packages/simplex-chat-client/typescript/examples/squaring-bot.js). -- uruchomić [simplex-chat w terminal ](./docs/lang/pl/CLI.md), aby wykonywać poszczególne polecenia czatu, np. wysyłać wiadomości w ramach wykonywania skryptu powłoki. +- uruchomić [simplex-chat w terminal ](./CLI.md), aby wykonywać poszczególne polecenia czatu, np. wysyłać wiadomości w ramach wykonywania skryptu powłoki. Jeśli chcesz rozwijać platformę SimpleX, skontaktuj się z nami, aby uzyskać porady i wsparcie. @@ -365,7 +365,7 @@ Dołącz również do grupy [#simplex-devs](https://simplex.chat/contact#/?v=1-2 - ✅ Ulepszone połączenia audio i wideo. - ✅ Obsługa starszego systemu operacyjnego Android i 32-bitowych procesorów. - ✅ Ukryte profile czatu. -- ✅ Wysyłanie i odbieranie dużych plików przez [protokół XFTP](./blog/20230301-simplex-file-transfer-protocol.md). +- ✅ Wysyłanie i odbieranie dużych plików przez [protokół XFTP](../../../blog/20230301-simplex-file-transfer-protocol.md). - ✅ Wiadomości wideo. - ✅ Kod dostępu do aplikacji. - ✅ Ulepszenie interfejsu Androidowej aplikacji. @@ -402,7 +402,7 @@ Dołącz również do grupy [#simplex-devs](https://simplex.chat/contact#/?v=1-2 [Protokoły i model bezpieczeństwa SimpleX](https://github.com/simplex-chat/simplexmq/blob/master/protocol/overview-tjr.md) zostały poddane przeglądowi i zawierały wiele istotnych zmian i ulepszeń w wersji v1.0.0. -Audyt bezpieczeństwa został przeprowadzony w październiku 2022 r. przez [Trail of Bits](https://www.trailofbits.com/about), a większość poprawek została wydana w wersji 4.2.0 - zobacz [ogłoszenie](./blog/20221108-simplex-chat-v4.2-security-audit-new-website.md). +Audyt bezpieczeństwa został przeprowadzony w październiku 2022 r. przez [Trail of Bits](https://www.trailofbits.com/about), a większość poprawek została wydana w wersji 4.2.0 - zobacz [ogłoszenie](../../../blog/20221108-simplex-chat-v4.2-security-audit-new-website.md). SimpleX Chat jest nadal na stosunkowo wczesnym etapie rozwoju (aplikacje mobilne zostały wydane w marcu 2022 r.), więc możesz odkryć pewne błędy i brakujące funkcje. Będziemy bardzo wdzięczni za poinformowanie nas o wszystkim, co wymaga naprawy lub ulepszenia. diff --git a/docs/lang/pl/SERVER.md b/docs/lang/pl/SERVER.md index 72cb51a4bf..f4bffc1bcc 100644 --- a/docs/lang/pl/SERVER.md +++ b/docs/lang/pl/SERVER.md @@ -13,7 +13,7 @@ Serwer SMP to serwer przekaźnikowy używany do przekazywania wiadomości w siec Klienty SimpleX określają tylko, który serwer jest używany do odbierania wiadomości, oddzielnie dla każdego kontaktu (lub połączenia grupowego z członkiem grupy), a serwery te są tylko tymczasowe, ponieważ adres dostawy może ulec zmianie. -_Uwaga_: gdy zmienisz serwery w ustawieniach aplikacji, wpłynie to tylko na to, który serwer będzie używany dla nowych kontaktów, istniejące kontakty nie zostaną automatycznie przeniesione na nowe serwery, ale możesz przenieść je ręcznie za pomocą przycisku ["Zmień adres odbiorczy"](../blog/20221108-simplex-chat-v4.2-security-audit-new-website.md#change-your-delivery-address-beta) na stronie z informacjami kontaktu/członka - wkrótce zostanie to zautomatyzowane. +_Uwaga_: gdy zmienisz serwery w ustawieniach aplikacji, wpłynie to tylko na to, który serwer będzie używany dla nowych kontaktów, istniejące kontakty nie zostaną automatycznie przeniesione na nowe serwery, ale możesz przenieść je ręcznie za pomocą przycisku ["Zmień adres odbiorczy"](../../../blog/20221108-simplex-chat-v4.2-security-audit-new-website.md#change-your-delivery-address-beta) na stronie z informacjami kontaktu/członka - wkrótce zostanie to zautomatyzowane. ## Instalacja diff --git a/docs/lang/pl/SIMPLEX.md b/docs/lang/pl/SIMPLEX.md index ff7106d84c..a1ceb8c5e4 100644 --- a/docs/lang/pl/SIMPLEX.md +++ b/docs/lang/pl/SIMPLEX.md @@ -11,7 +11,7 @@ revision: 07.02.2023 Istniejące komunikatory oraz protokoły borykają się ze wszystkimi lub kilkoma podanymi problemami: - Brak zachowania prywatności profilu i kontaktów użytkownika (zachowanie poufności metadanych). -- Brak ochrony (lub jedynie opcjonalna ochrona) przed atakami MITM przez dostawcę usług przy użyciu szyfrowania [end to end](1) +- Brak ochrony (lub jedynie opcjonalna ochrona) przed atakami MITM przez dostawcę usług przy użyciu szyfrowania [end to end][1] - Niechciane wiadomości (spam i nadużycia). - Brak własności danych i ich ochrony. - Dla nietechnicznych użytkowników używanie niescentralizowanych protokołów jest skomplikowane. diff --git a/website/.eleventy.js b/website/.eleventy.js index 64aedbc04c..a0a35f3366 100644 --- a/website/.eleventy.js +++ b/website/.eleventy.js @@ -6,6 +6,7 @@ const uri = require('fast-uri') const i18n = require('eleventy-plugin-i18n') const fs = require("fs") const path = require("path") +const matter = require('gray-matter') const pluginRss = require('@11ty/eleventy-plugin-rss') const { JSDOM } = require('jsdom') @@ -388,16 +389,36 @@ module.exports = function (ty) { linkify: true, replaceLink: function (link, _env) { let parsed = uri.parse(link) - if (parsed.scheme || parsed.host || !parsed.path.endsWith(".md")) { - return link + if (parsed.scheme || parsed.host) return link + + let hostFile = path.resolve(_env.page.inputPath) + let linkFile = path.resolve(hostFile, '..', parsed.path) + if (parsed.path.startsWith('/')) { + let srcIndex = hostFile.indexOf("/src") + if (srcIndex !== -1) { + linkFile = path.join(hostFile.slice(0, srcIndex + 4), parsed.path) + } } - if (parsed.path.startsWith("../../blog")) { - parsed.path = parsed.path.replace("../../blog", "/blog") + + if (fs.existsSync(linkFile) && fs.statSync(linkFile).isFile()) { + // this condition works if the link is a valid website file + const fileContent = fs.readFileSync(linkFile, 'utf8') + parsed.path = (matter(fileContent).data?.permalink || parsed.path).replace(/\.md$/, ".html").toLowerCase() + } else if (!fs.existsSync(linkFile)) { + linkFile = linkFile.replace('/website/src', '') + if (fs.existsSync(linkFile)) { + // this condition works if the link is a valid project file + const githubUrl = "https://github.com/simplex-chat/simplex-chat/blob/stable" + const keyword = "/simplex-chat" + index = linkFile.indexOf(keyword) + linkFile = linkFile.substring(index + keyword.length) + parsed.path = `${githubUrl}${linkFile}` + } else { + // if the link is not a valid website file or project file + throw new Error(`Broken link: ${parsed.path} in ${hostFile}`) + } } - if (parsed.path.startsWith("../PRIVACY.md")) { - parsed.path = parsed.path.replace("../PRIVACY.md", "/privacy") - } - parsed.path = parsed.path.replace(/\.md$/, ".html").toLowerCase() + return uri.serialize(parsed) } }).use(markdownItAnchor, { @@ -422,4 +443,4 @@ module.exports = function (ty) { htmlTemplateEngine: 'njk', dataTemplateEngine: 'njk', } -} +} \ No newline at end of file From d02668386f3a09eb4998b8fefa5263ed60873a38 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Tue, 14 May 2024 21:32:53 +0100 Subject: [PATCH 2/5] directory: debug logging (#4104) --- .../src/Directory/Events.hs | 66 +++-- .../src/Directory/Service.hs | 264 ++++++++++-------- 2 files changed, 187 insertions(+), 143 deletions(-) diff --git a/apps/simplex-directory-service/src/Directory/Events.hs b/apps/simplex-directory-service/src/Directory/Events.hs index 76f57585a8..87950ecce7 100644 --- a/apps/simplex-directory-service/src/Directory/Events.hs +++ b/apps/simplex-directory-service/src/Directory/Events.hs @@ -14,6 +14,7 @@ module Directory.Events DirectoryRole (..), SDirectoryRole (..), crDirectoryEvent, + directoryCmdTag, viewName, ) where @@ -21,6 +22,8 @@ where import Control.Applicative ((<|>)) import Data.Attoparsec.Text (Parser) import qualified Data.Attoparsec.Text as A +import Data.Char (isSpace) +import Data.Either (fromRight) import Data.Functor (($>)) import Data.Text (Text) import qualified Data.Text as T @@ -34,13 +37,11 @@ import Simplex.Chat.Types import Simplex.Chat.Types.Shared import Simplex.Messaging.Encoding.String import Simplex.Messaging.Util ((<$?>)) -import Data.Char (isSpace) -import Data.Either (fromRight) data DirectoryEvent = DEContactConnected Contact | DEGroupInvitation {contact :: Contact, groupInfo :: GroupInfo, fromMemberRole :: GroupMemberRole, memberRole :: GroupMemberRole} - | DEServiceJoinedGroup {contactId :: ContactId, groupInfo :: GroupInfo, hostMember :: GroupMember} + | DEServiceJoinedGroup {contactId :: ContactId, groupInfo :: GroupInfo, hostMember :: GroupMember} | DEGroupUpdated {contactId :: ContactId, fromGroup :: GroupInfo, toGroup :: GroupInfo} | DEContactRoleChanged GroupInfo ContactId GroupMemberRole -- contactId here is the contact whose role changed | DEServiceRoleChanged GroupInfo GroupMemberRole @@ -140,25 +141,26 @@ directoryCmdP = cmdStrP = (tagP >>= \(ADCT u t) -> ADC u <$> (cmdP t <|> pure (DCCommandError t))) <|> pure (ADC SDRUser DCUnknownCommand) - tagP = A.takeTill (== ' ') >>= \case - "help" -> u DCHelp_ - "h" -> u DCHelp_ - "next" -> u DCSearchNext_ - "all" -> u DCAllGroups_ - "new" -> u DCRecentGroups_ - "submit" -> u DCSubmitGroup_ - "confirm" -> u DCConfirmDuplicateGroup_ - "list" -> u DCListUserGroups_ - "ls" -> u DCListUserGroups_ - "delete" -> u DCDeleteGroup_ - "approve" -> su DCApproveGroup_ - "reject" -> su DCRejectGroup_ - "suspend" -> su DCSuspendGroup_ - "resume" -> su DCResumeGroup_ - "last" -> su DCListLastGroups_ - "exec" -> su DCExecuteCommand_ - "x" -> su DCExecuteCommand_ - _ -> fail "bad command tag" + tagP = + A.takeTill (== ' ') >>= \case + "help" -> u DCHelp_ + "h" -> u DCHelp_ + "next" -> u DCSearchNext_ + "all" -> u DCAllGroups_ + "new" -> u DCRecentGroups_ + "submit" -> u DCSubmitGroup_ + "confirm" -> u DCConfirmDuplicateGroup_ + "list" -> u DCListUserGroups_ + "ls" -> u DCListUserGroups_ + "delete" -> u DCDeleteGroup_ + "approve" -> su DCApproveGroup_ + "reject" -> su DCRejectGroup_ + "suspend" -> su DCSuspendGroup_ + "resume" -> su DCResumeGroup_ + "last" -> su DCListLastGroups_ + "exec" -> su DCExecuteCommand_ + "x" -> su DCExecuteCommand_ + _ -> fail "bad command tag" where u = pure . ADCT SDRUser su = pure . ADCT SDRSuperUser @@ -192,3 +194,23 @@ directoryCmdP = viewName :: String -> String viewName n = if ' ' `elem` n then "'" <> n <> "'" else n + +directoryCmdTag :: DirectoryCmd r -> Text +directoryCmdTag = \case + DCHelp -> "help" + DCSearchGroup _ -> "search" + DCSearchNext -> "next" + DCAllGroups -> "all" + DCRecentGroups -> "new" + DCSubmitGroup _ -> "submit" + DCConfirmDuplicateGroup {} -> "confirm" + DCListUserGroups -> "list" + DCDeleteGroup {} -> "delete" + DCApproveGroup {} -> "approve" + DCRejectGroup {} -> "reject" + DCSuspendGroup {} -> "suspend" + DCResumeGroup {} -> "resume" + DCListLastGroups _ -> "last" + DCExecuteCommand _ -> "exec" + DCUnknownCommand -> "unknown" + DCCommandError _ -> "error" diff --git a/apps/simplex-directory-service/src/Directory/Service.hs b/apps/simplex-directory-service/src/Directory/Service.hs index d158b57e22..eefb1f77a4 100644 --- a/apps/simplex-directory-service/src/Directory/Service.hs +++ b/apps/simplex-directory-service/src/Directory/Service.hs @@ -2,9 +2,9 @@ {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE MultiWayIf #-} module Directory.Service ( welcomeGetOpts, @@ -15,6 +15,7 @@ where import Control.Concurrent (forkIO) import Control.Concurrent.Async import Control.Concurrent.STM +import Control.Logger.Simple import Control.Monad import qualified Data.ByteString.Char8 as B import Data.Maybe (fromMaybe, maybeToList) @@ -37,7 +38,7 @@ import Simplex.Chat.Options import Simplex.Chat.Protocol (MsgContent (..)) import Simplex.Chat.Types import Simplex.Chat.Types.Shared -import Simplex.Chat.View (serializeChatResponse, simplexChatContact) +import Simplex.Chat.View (serializeChatResponse, simplexChatContact, viewContactName, viewGroupName) import Simplex.Messaging.Encoding.String import Simplex.Messaging.TMap (TMap) import qualified Simplex.Messaging.TMap as TM @@ -96,9 +97,11 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi DEUnsupportedMessage _ct _ciId -> pure () DEItemEditIgnored _ct -> pure () DEItemDeleteIgnored _ct -> pure () - DEContactCommand ct ciId aCmd -> case aCmd of - ADC SDRUser cmd -> deUserCommand env ct ciId cmd - ADC SDRSuperUser cmd -> deSuperUserCommand ct ciId cmd + DEContactCommand ct ciId (ADC sUser cmd) -> do + logInfo $ "command received " <> directoryCmdTag cmd + case sUser of + SDRUser -> deUserCommand env ct ciId cmd + SDRSuperUser -> deSuperUserCommand ct ciId cmd where withSuperUsers action = void . forkIO $ forM_ superUsers $ \KnownContact {contactId} -> action contactId notifySuperUsers s = withSuperUsers $ \contactId -> sendMessage' cc contactId s @@ -107,7 +110,7 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi withGroupReg GroupInfo {groupId, localDisplayName} err action = do atomically (getGroupReg st groupId) >>= \case Just gr -> action gr - Nothing -> putStrLn $ T.unpack $ "Error: " <> err <> ", group: " <> localDisplayName <> ", can't find group registration ID " <> tshow groupId + Nothing -> logError $ "Error: " <> err <> ", group: " <> localDisplayName <> ", can't find group registration ID " <> tshow groupId groupInfoText GroupProfile {displayName = n, fullName = fn, description = d} = n <> (if n == fn || T.null fn then "" else " (" <> fn <> ")") <> maybe "" ("\nWelcome message:\n" <>) d userGroupReference gr GroupInfo {groupProfile = GroupProfile {displayName}} = userGroupReference' gr displayName @@ -152,23 +155,25 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi deContactConnected :: Contact -> IO () deContactConnected ct = when (contactDirect ct) $ do - unless testing $ putStrLn $ T.unpack (localDisplayName' ct) <> " connected" + logInfo $ (viewContactName ct) <> " connected" sendMessage cc ct $ - "Welcome to " <> serviceName <> " service!\n\ - \Send a search string to find groups or */help* to learn how to add groups to directory.\n\n\ - \For example, send _privacy_ to find groups about privacy.\n\ - \Or send */all* or */new* to list groups.\n\n\ - \Content and privacy policy: https://simplex.chat/docs/directory.html" + ("Welcome to " <> serviceName <> " service!\n") + <> "Send a search string to find groups or */help* to learn how to add groups to directory.\n\n\ + \For example, send _privacy_ to find groups about privacy.\n\ + \Or send */all* or */new* to list groups.\n\n\ + \Content and privacy policy: https://simplex.chat/docs/directory.html" deGroupInvitation :: Contact -> GroupInfo -> GroupMemberRole -> GroupMemberRole -> IO () deGroupInvitation ct g@GroupInfo {groupProfile = GroupProfile {displayName, fullName}} fromMemberRole memberRole = do + logInfo $ "invited to group " <> viewGroupName g <> " by " <> viewContactName ct case badRolesMsg $ groupRolesStatus fromMemberRole memberRole of Just msg -> sendMessage cc ct msg - Nothing -> getDuplicateGroup g >>= \case - Just DGUnique -> processInvitation ct g - Just DGRegistered -> askConfirmation - Just DGReserved -> sendMessage cc ct $ groupAlreadyListed g - Nothing -> sendMessage cc ct "Error: getDuplicateGroup. Please notify the developers." + Nothing -> + getDuplicateGroup g >>= \case + Just DGUnique -> processInvitation ct g + Just DGRegistered -> askConfirmation + Just DGReserved -> sendMessage cc ct $ groupAlreadyListed g + Nothing -> sendMessage cc ct "Error: getDuplicateGroup. Please notify the developers." where askConfirmation = do ugrId <- addGroupReg st ct g GRSPendingConfirmation @@ -205,7 +210,8 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi _ -> Nothing deServiceJoinedGroup :: ContactId -> GroupInfo -> GroupMember -> IO () - deServiceJoinedGroup ctId g owner = + deServiceJoinedGroup ctId g owner = do + logInfo $ "service joined group " <> viewGroupName g withGroupReg g "joined group" $ \gr -> when (ctId `isOwner` gr) $ do setGroupRegOwner st gr owner @@ -214,7 +220,8 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi sendChatCmd cc (APICreateGroupLink groupId GRMember) >>= \case CRGroupLinkCreated {connReqContact} -> do setGroupStatus st gr GRSPendingUpdate - notifyOwner gr + notifyOwner + gr "Created the public link to join the group via this directory service that is always online.\n\n\ \Please add it to the group welcome message.\n\ \For example, add:" @@ -228,24 +235,26 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi _ -> notifyOwner gr $ unexpectedError "can't create group link" deGroupUpdated :: ContactId -> GroupInfo -> GroupInfo -> IO () - deGroupUpdated ctId fromGroup toGroup = + deGroupUpdated ctId fromGroup toGroup = do + logInfo $ "group updated " <> viewGroupName toGroup unless (sameProfile p p') $ do withGroupReg toGroup "group updated" $ \gr -> do let userGroupRef = userGroupReference gr toGroup readTVarIO (groupRegStatus gr) >>= \case GRSPendingConfirmation -> pure () GRSProposed -> pure () - GRSPendingUpdate -> groupProfileUpdate >>= \case - GPNoServiceLink -> - when (ctId `isOwner` gr) $ notifyOwner gr $ "The profile updated for " <> userGroupRef <> ", but the group link is not added to the welcome message." - GPServiceLinkAdded - | ctId `isOwner` gr -> groupLinkAdded gr - | otherwise -> notifyOwner gr "The group link is added by another group member, your registration will not be processed.\n\nPlease update the group profile yourself." - GPServiceLinkRemoved -> when (ctId `isOwner` gr) $ notifyOwner gr $ "The group link of " <> userGroupRef <> " is removed from the welcome message, please add it." - GPHasServiceLink -> when (ctId `isOwner` gr) $ groupLinkAdded gr - GPServiceLinkError -> do - when (ctId `isOwner` gr) $ notifyOwner gr $ "Error: " <> serviceName <> " has no group link for " <> userGroupRef <> ". Please report the error to the developers." - putStrLn $ "Error: no group link for " <> userGroupRef + GRSPendingUpdate -> + groupProfileUpdate >>= \case + GPNoServiceLink -> + when (ctId `isOwner` gr) $ notifyOwner gr $ "The profile updated for " <> userGroupRef <> ", but the group link is not added to the welcome message." + GPServiceLinkAdded + | ctId `isOwner` gr -> groupLinkAdded gr + | otherwise -> notifyOwner gr "The group link is added by another group member, your registration will not be processed.\n\nPlease update the group profile yourself." + GPServiceLinkRemoved -> when (ctId `isOwner` gr) $ notifyOwner gr $ "The group link of " <> userGroupRef <> " is removed from the welcome message, please add it." + GPHasServiceLink -> when (ctId `isOwner` gr) $ groupLinkAdded gr + GPServiceLinkError -> do + when (ctId `isOwner` gr) $ notifyOwner gr $ "Error: " <> serviceName <> " has no group link for " <> userGroupRef <> ". Please report the error to the developers." + logError $ "Error: no group link for " <> T.pack userGroupRef GRSPendingApproval n -> processProfileChange gr $ n + 1 GRSActive -> processProfileChange gr 1 GRSSuspended -> processProfileChange gr 1 @@ -288,7 +297,7 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi notifyOwner gr $ "The group " <> userGroupRef <> " is updated!\nIt is hidden from the directory until approved." notifySuperUsers $ "The group " <> groupRef <> " is updated." checkRolesSendToApprove gr n' - GPServiceLinkError -> putStrLn $ "Error: no group link for " <> groupRef <> " pending approval." + GPServiceLinkError -> logError $ "Error: no group link for " <> T.pack groupRef <> " pending approval." groupProfileUpdate = profileUpdate <$> sendChatCmd cc (APIGetGroupLink groupId) where profileUpdate = \case @@ -297,7 +306,7 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi groupLink2 = safeDecodeUtf8 $ strEncode $ simplexChatContact connReqContact hadLinkBefore = groupLink1 `isInfix` description p || groupLink2 `isInfix` description p hasLinkNow = groupLink1 `isInfix` description p' || groupLink2 `isInfix` description p' - in if + in if | hadLinkBefore && hasLinkNow -> GPHasServiceLink | hadLinkBefore -> GPServiceLinkRemoved | hasLinkNow -> GPServiceLinkAdded @@ -311,18 +320,20 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi sendToApprove :: GroupInfo -> GroupReg -> GroupApprovalId -> IO () sendToApprove GroupInfo {groupProfile = p@GroupProfile {displayName, image = image'}} GroupReg {dbGroupId, dbContactId} gaId = do - ct_ <- getContact cc dbContactId + ct_ <- getContact cc dbContactId gr_ <- getGroupAndSummary cc dbGroupId let membersStr = maybe "" (\(_, s) -> "_" <> tshow (currentMembers s) <> " members_\n") gr_ - text = maybe ("The group ID " <> tshow dbGroupId <> " submitted: ") (\c -> localDisplayName' c <> " submitted the group ID " <> tshow dbGroupId <> ": ") ct_ - <> "\n" <> groupInfoText p <> "\n" <> membersStr <> "\nTo approve send:" + text = + maybe ("The group ID " <> tshow dbGroupId <> " submitted: ") (\c -> localDisplayName' c <> " submitted the group ID " <> tshow dbGroupId <> ": ") ct_ + <> ("\n" <> groupInfoText p <> "\n" <> membersStr <> "\nTo approve send:") msg = maybe (MCText text) (\image -> MCImage {text, image}) image' withSuperUsers $ \cId -> do sendComposedMessage' cc cId Nothing msg sendMessage' cc cId $ "/approve " <> show dbGroupId <> ":" <> viewName (T.unpack displayName) <> " " <> show gaId deContactRoleChanged :: GroupInfo -> ContactId -> GroupMemberRole -> IO () - deContactRoleChanged g@GroupInfo {membership = GroupMember {memberRole = serviceRole}} ctId contactRole = + deContactRoleChanged g@GroupInfo {membership = GroupMember {memberRole = serviceRole}} ctId contactRole = do + logInfo $ "contact ID " <> tshow ctId <> " role changed in group " <> viewGroupName g <> " to " <> tshow contactRole withGroupReg g "contact role changed" $ \gr -> do let userGroupRef = userGroupReference gr g uCtRole = "Your role in the group " <> userGroupRef <> " is changed to " <> ctRole @@ -348,6 +359,7 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi deServiceRoleChanged :: GroupInfo -> GroupMemberRole -> IO () deServiceRoleChanged g serviceRole = do + logInfo $ "service role changed in group " <> viewGroupName g <> " to " <> tshow serviceRole withGroupReg g "service role changed" $ \gr -> do let userGroupRef = userGroupReference gr g uSrvRole = serviceName <> " role in the group " <> userGroupRef <> " is changed to " <> srvRole @@ -371,11 +383,12 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi srvRole = "*" <> B.unpack (strEncode serviceRole) <> "*" suSrvRole = "(" <> serviceName <> " role is changed to " <> srvRole <> ")." whenContactIsOwner gr action = - getGroupMember gr >>= - mapM_ (\cm@GroupMember {memberRole} -> when (memberRole == GROwner && memberActive cm) action) + getGroupMember gr + >>= mapM_ (\cm@GroupMember {memberRole} -> when (memberRole == GROwner && memberActive cm) action) deContactRemovedFromGroup :: ContactId -> GroupInfo -> IO () - deContactRemovedFromGroup ctId g = + deContactRemovedFromGroup ctId g = do + logInfo $ "contact ID " <> tshow ctId <> " removed from group " <> viewGroupName g withGroupReg g "contact removed" $ \gr -> do when (ctId `isOwner` gr) $ do setGroupStatus st gr GRSRemoved @@ -383,7 +396,8 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi notifySuperUsers $ "The group " <> groupReference g <> " is de-listed (group owner is removed)." deContactLeftGroup :: ContactId -> GroupInfo -> IO () - deContactLeftGroup ctId g = + deContactLeftGroup ctId g = do + logInfo $ "contact ID " <> tshow ctId <> " left group " <> viewGroupName g withGroupReg g "contact left" $ \gr -> do when (ctId `isOwner` gr) $ do setGroupStatus st gr GRSRemoved @@ -391,7 +405,8 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi notifySuperUsers $ "The group " <> groupReference g <> " is de-listed (group owner left)." deServiceRemovedFromGroup :: GroupInfo -> IO () - deServiceRemovedFromGroup g = + deServiceRemovedFromGroup g = do + logInfo $ "service removed from group " <> viewGroupName g withGroupReg g "service removed" $ \gr -> do setGroupStatus st gr GRSRemoved notifyOwner gr $ serviceName <> " is removed from the group " <> userGroupReference gr g <> ".\n\nThe group is no longer listed in the directory." @@ -402,11 +417,15 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi DCHelp -> sendMessage cc ct $ "You must be the owner to add the group to the directory:\n\ - \1. Invite " <> serviceName <> " bot to your group as *admin* (you can send `/list` to see all groups you submitted).\n\ - \2. " <> serviceName <> " bot will create a public group link for the new members to join even when you are offline.\n\ - \3. You will then need to add this link to the group welcome message.\n\ - \4. Once the link is added, service admins will approve the group (it can take up to 24 hours), and everybody will be able to find it in directory.\n\n\ - \Start from inviting the bot to your group as admin - it will guide you through the process" + \1. Invite " + <> serviceName + <> " bot to your group as *admin* (you can send `/list` to see all groups you submitted).\n\ + \2. " + <> serviceName + <> " bot will create a public group link for the new members to join even when you are offline.\n\ + \3. You will then need to add this link to the group welcome message.\n\ + \4. Once the link is added, service admins will approve the group (it can take up to 24 hours), and everybody will be able to find it in directory.\n\n\ + \Start from inviting the bot to your group as admin - it will guide you through the process" DCSearchGroup s -> withFoundListedGroups (Just s) $ sendSearchResults s DCSearchNext -> atomically (TM.lookup (contactId' ct) searchRequests) >>= \case @@ -434,13 +453,13 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi Nothing -> sendReply $ "Group ID " <> show ugrId <> " not found" Just g@GroupInfo {groupProfile = GroupProfile {displayName}} | displayName == gName -> - readTVarIO groupRegStatus >>= \case - GRSPendingConfirmation -> do - getDuplicateGroup g >>= \case - Nothing -> sendMessage cc ct "Error: getDuplicateGroup. Please notify the developers." - Just DGReserved -> sendMessage cc ct $ groupAlreadyListed g - _ -> processInvitation ct g - _ -> sendReply $ "Error: the group ID " <> show ugrId <> " (" <> T.unpack displayName <> ") is not pending confirmation." + readTVarIO groupRegStatus >>= \case + GRSPendingConfirmation -> do + getDuplicateGroup g >>= \case + Nothing -> sendMessage cc ct "Error: getDuplicateGroup. Please notify the developers." + Just DGReserved -> sendMessage cc ct $ groupAlreadyListed g + _ -> processInvitation ct g + _ -> sendReply $ "Error: the group ID " <> show ugrId <> " (" <> T.unpack displayName <> ") is not pending confirmation." | otherwise -> sendReply $ "Group ID " <> show ugrId <> " has the display name " <> T.unpack displayName DCListUserGroups -> atomically (getUserGroupRegs st $ contactId' ct) >>= \grs -> do @@ -462,7 +481,7 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi let gs' = takeTop searchResults gs moreGroups = length gs - length gs' more = if moreGroups > 0 then ", sending top " <> show (length gs') else "" - sendReply $ "Found " <> show (length gs) <> " group(s)" <> more <> "." + sendReply $ "Found " <> show (length gs) <> " group(s)" <> more <> "." updateSearchRequest (STSearch s) $ groupIds gs' sendFoundGroups gs' moreGroups sendAllGroups takeFirst sortName searchType = \case @@ -499,74 +518,76 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi msg = maybe (MCText text) (\image -> MCImage {text, image}) image_ sendComposedMessage cc ct Nothing msg when (moreGroups > 0) $ - sendComposedMessage cc ct Nothing $ MCText $ "Send */next* or just *.* for " <> tshow moreGroups <> " more result(s)." + sendComposedMessage cc ct Nothing $ + MCText $ + "Send */next* or just *.* for " <> tshow moreGroups <> " more result(s)." deSuperUserCommand :: Contact -> ChatItemId -> DirectoryCmd 'DRSuperUser -> IO () deSuperUserCommand ct ciId cmd | superUser `elem` superUsers = case cmd of - DCApproveGroup {groupId, displayName = n, groupApprovalId} -> do - getGroupAndReg groupId n >>= \case - Nothing -> sendReply $ "The group " <> groupRef <> " not found (getGroupAndReg)." - Just (g, gr) -> - readTVarIO (groupRegStatus gr) >>= \case - GRSPendingApproval gaId - | gaId == groupApprovalId -> do - getDuplicateGroup g >>= \case - Nothing -> sendReply "Error: getDuplicateGroup. Please notify the developers." - Just DGReserved -> sendReply $ "The group " <> groupRef <> " is already listed in the directory." - _ -> do - getGroupRolesStatus g gr >>= \case - Just GRSOk -> do - setGroupStatus st gr GRSActive - sendReply "Group approved!" - notifyOwner gr $ "The group " <> userGroupReference' gr n <> " is approved and listed in directory!\nPlease note: if you change the group profile it will be hidden from directory until it is re-approved." - Just GRSServiceNotAdmin -> replyNotApproved serviceNotAdmin - Just GRSContactNotOwner -> replyNotApproved "user is not an owner." - Just GRSBadRoles -> replyNotApproved $ "user is not an owner, " <> serviceNotAdmin - Nothing -> sendReply "Error: getGroupRolesStatus. Please notify the developers." - where - replyNotApproved reason = sendReply $ "Group is not approved: " <> reason - serviceNotAdmin = serviceName <> " is not an admin." - | otherwise -> sendReply "Incorrect approval code" - _ -> sendReply $ "Error: the group " <> groupRef <> " is not pending approval." - where - groupRef = groupReference' groupId n - DCRejectGroup _gaId _gName -> pure () - DCSuspendGroup groupId gName -> do - let groupRef = groupReference' groupId gName - getGroupAndReg groupId gName >>= \case - Nothing -> sendReply $ "The group " <> groupRef <> " not found (getGroupAndReg)." - Just (_, gr) -> - readTVarIO (groupRegStatus gr) >>= \case - GRSActive -> do - setGroupStatus st gr GRSSuspended - notifyOwner gr $ "The group " <> userGroupReference' gr gName <> " is suspended and hidden from directory. Please contact the administrators." - sendReply "Group suspended!" - _ -> sendReply $ "The group " <> groupRef <> " is not active, can't be suspended." - DCResumeGroup groupId gName -> do - let groupRef = groupReference' groupId gName - getGroupAndReg groupId gName >>= \case - Nothing -> sendReply $ "The group " <> groupRef <> " not found (getGroupAndReg)." - Just (_, gr) -> - readTVarIO (groupRegStatus gr) >>= \case - GRSSuspended -> do - setGroupStatus st gr GRSActive - notifyOwner gr $ "The group " <> userGroupReference' gr gName <> " is listed in the directory again!" - sendReply "Group listing resumed!" - _ -> sendReply $ "The group " <> groupRef <> " is not suspended, can't be resumed." - DCListLastGroups count -> - readTVarIO (groupRegs st) >>= \grs -> do - sendReply $ show (length grs) <> " registered group(s)" <> (if length grs > count then ", showing the last " <> show count else "") - void . forkIO $ forM_ (reverse $ take count grs) $ \gr@GroupReg {dbGroupId, dbContactId} -> do - ct_ <- getContact cc dbContactId - let ownerStr = "Owner: " <> maybe "getContact error" localDisplayName' ct_ - sendGroupInfo ct gr dbGroupId $ Just ownerStr - DCExecuteCommand cmdStr -> - sendChatCmdStr cc cmdStr >>= \r -> do - ts <- getCurrentTime - tz <- getCurrentTimeZone - sendReply $ serializeChatResponse (Nothing, Just user) ts tz Nothing r - DCCommandError tag -> sendReply $ "Command error: " <> show tag + DCApproveGroup {groupId, displayName = n, groupApprovalId} -> + getGroupAndReg groupId n >>= \case + Nothing -> sendReply $ "The group " <> groupRef <> " not found (getGroupAndReg)." + Just (g, gr) -> + readTVarIO (groupRegStatus gr) >>= \case + GRSPendingApproval gaId + | gaId == groupApprovalId -> do + getDuplicateGroup g >>= \case + Nothing -> sendReply "Error: getDuplicateGroup. Please notify the developers." + Just DGReserved -> sendReply $ "The group " <> groupRef <> " is already listed in the directory." + _ -> do + getGroupRolesStatus g gr >>= \case + Just GRSOk -> do + setGroupStatus st gr GRSActive + sendReply "Group approved!" + notifyOwner gr $ "The group " <> userGroupReference' gr n <> " is approved and listed in directory!\nPlease note: if you change the group profile it will be hidden from directory until it is re-approved." + Just GRSServiceNotAdmin -> replyNotApproved serviceNotAdmin + Just GRSContactNotOwner -> replyNotApproved "user is not an owner." + Just GRSBadRoles -> replyNotApproved $ "user is not an owner, " <> serviceNotAdmin + Nothing -> sendReply "Error: getGroupRolesStatus. Please notify the developers." + where + replyNotApproved reason = sendReply $ "Group is not approved: " <> reason + serviceNotAdmin = serviceName <> " is not an admin." + | otherwise -> sendReply "Incorrect approval code" + _ -> sendReply $ "Error: the group " <> groupRef <> " is not pending approval." + where + groupRef = groupReference' groupId n + DCRejectGroup _gaId _gName -> pure () + DCSuspendGroup groupId gName -> do + let groupRef = groupReference' groupId gName + getGroupAndReg groupId gName >>= \case + Nothing -> sendReply $ "The group " <> groupRef <> " not found (getGroupAndReg)." + Just (_, gr) -> + readTVarIO (groupRegStatus gr) >>= \case + GRSActive -> do + setGroupStatus st gr GRSSuspended + notifyOwner gr $ "The group " <> userGroupReference' gr gName <> " is suspended and hidden from directory. Please contact the administrators." + sendReply "Group suspended!" + _ -> sendReply $ "The group " <> groupRef <> " is not active, can't be suspended." + DCResumeGroup groupId gName -> do + let groupRef = groupReference' groupId gName + getGroupAndReg groupId gName >>= \case + Nothing -> sendReply $ "The group " <> groupRef <> " not found (getGroupAndReg)." + Just (_, gr) -> + readTVarIO (groupRegStatus gr) >>= \case + GRSSuspended -> do + setGroupStatus st gr GRSActive + notifyOwner gr $ "The group " <> userGroupReference' gr gName <> " is listed in the directory again!" + sendReply "Group listing resumed!" + _ -> sendReply $ "The group " <> groupRef <> " is not suspended, can't be resumed." + DCListLastGroups count -> + readTVarIO (groupRegs st) >>= \grs -> do + sendReply $ show (length grs) <> " registered group(s)" <> (if length grs > count then ", showing the last " <> show count else "") + void . forkIO $ forM_ (reverse $ take count grs) $ \gr@GroupReg {dbGroupId, dbContactId} -> do + ct_ <- getContact cc dbContactId + let ownerStr = "Owner: " <> maybe "getContact error" localDisplayName' ct_ + sendGroupInfo ct gr dbGroupId $ Just ownerStr + DCExecuteCommand cmdStr -> + sendChatCmdStr cc cmdStr >>= \r -> do + ts <- getCurrentTime + tz <- getCurrentTimeZone + sendReply $ serializeChatResponse (Nothing, Just user) ts tz Nothing r + DCCommandError tag -> sendReply $ "Command error: " <> show tag | otherwise = sendReply "You are not allowed to use this command" where superUser = KnownContact {contactId = contactId' ct, localDisplayName = localDisplayName' ct} @@ -577,8 +598,9 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi getGroup cc gId $>>= \g@GroupInfo {groupProfile = GroupProfile {displayName}} -> if displayName == gName - then atomically (getGroupReg st gId) - $>>= \gr -> pure $ Just (g, gr) + then + atomically (getGroupReg st gId) + $>>= \gr -> pure $ Just (g, gr) else pure Nothing sendGroupInfo :: Contact -> GroupReg -> GroupId -> Maybe Text -> IO () From 93ae1145bca7b70d1f6a79655d173c733d3a1516 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Wed, 15 May 2024 11:16:38 +0100 Subject: [PATCH 3/5] core: update simplexmq (use MVar for better DB connection concurrency) (#4183) * core: update simplexmq (use MVar for better DB connection concurrency) * focus failing tests * add timeouts to test * fix tests * more delays * increase timeouts * prints * delay * delay * empty * more delays * enable all --------- Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> --- cabal.project | 2 +- scripts/nix/sha256map.nix | 2 +- tests/ChatTests/Direct.hs | 12 ++++++++++++ tests/ChatTests/Groups.hs | 27 +++++++++++++++++++-------- tests/ChatTests/Profiles.hs | 34 +++++++++++++++++++++++----------- tests/ChatTests/Utils.hs | 2 +- 6 files changed, 57 insertions(+), 22 deletions(-) diff --git a/cabal.project b/cabal.project index 80e42fc652..334e106121 100644 --- a/cabal.project +++ b/cabal.project @@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: 762909ce33c01d71bbabfed156e1e9faeebbedca + tag: f51cf1deacd14aced88652ccfa06a746999e2ac6 source-repository-package type: git diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index 2b168c51ba..7577d1eae5 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."762909ce33c01d71bbabfed156e1e9faeebbedca" = "0jsfi792n84zk6bhfhg138mn0mv6wvvfmm0d6qxmplpb6glcbqaj"; + "https://github.com/simplex-chat/simplexmq.git"."f51cf1deacd14aced88652ccfa06a746999e2ac6" = "1q1lcjbapq0mrz0z99shajn364jf7j2j6gm5qir2h230l5fg6mih"; "https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d"; "https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl"; diff --git a/tests/ChatTests/Direct.hs b/tests/ChatTests/Direct.hs index 54f31ff50f..2e549d9dd1 100644 --- a/tests/ChatTests/Direct.hs +++ b/tests/ChatTests/Direct.hs @@ -298,6 +298,7 @@ testPlanInvitationLinkOwn tmp = alice ##> ("/_connect plan 1 " <> inv) alice <## "invitation link: ok to connect" -- conn_req_inv is forgotten after connection + threadDelay 100000 alice @@@ [("@alice_1", lastChatFeature), ("@alice_2", lastChatFeature)] alice `send` "@alice_2 hi" alice @@ -1068,20 +1069,25 @@ testNegotiateCall = -- alice confirms call by sending WebRTC answer alice ##> ("/_call answer @2 " <> serialize testWebRTCSession) alice <## "ok" + threadDelay 100000 alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "outgoing call: connecting...")]) bob <## "alice continued the WebRTC call" repeatM_ 3 $ getTermLine bob + threadDelay 100000 bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "incoming call: connecting...")]) -- participants can update calls as connected alice ##> "/_call status @2 connected" alice <## "ok" + threadDelay 100000 alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "outgoing call: in progress (00:00)")]) bob ##> "/_call status @2 connected" bob <## "ok" + threadDelay 100000 bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "incoming call: in progress (00:00)")]) -- either party can end the call bob ##> "/_call end @2" bob <## "ok" + threadDelay 100000 bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "incoming call: ended (00:00)")]) alice <## "call with bob ended" alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "outgoing call: ended (00:00)")]) @@ -2290,6 +2296,7 @@ testSwitchContact = alice <## "bob: you started changing address" bob <## "alice changed address for you" alice <## "bob: you changed address" + threadDelay 100000 alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "started changing address..."), (1, "you changed address")]) bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "started changing address for you..."), (0, "changed address for you")]) alice <##> bob @@ -2317,6 +2324,7 @@ testAbortSwitchContact tmp = do bob <## "alice started changing address for you" bob <## "alice changed address for you" alice <## "bob: you changed address" + threadDelay 100000 alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "started changing address..."), (1, "started changing address..."), (1, "you changed address")]) bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "started changing address for you..."), (0, "started changing address for you..."), (0, "changed address for you")]) alice <##> bob @@ -2331,6 +2339,7 @@ testSwitchGroupMember = alice <## "#team: you started changing address for bob" bob <## "#team: alice changed address for you" alice <## "#team: you changed address for bob" + threadDelay 100000 alice #$> ("/_get chat #1 count=100", chat, [(1, e2eeInfoNoPQStr), (0, "connected"), (1, "started changing address for bob..."), (1, "you changed address for bob")]) bob #$> ("/_get chat #1 count=100", chat, groupFeatures <> [(0, "connected"), (0, "started changing address for you..."), (0, "changed address for you")]) alice #> "#team hey" @@ -2362,6 +2371,7 @@ testAbortSwitchGroupMember tmp = do bob <## "#team: alice started changing address for you" bob <## "#team: alice changed address for you" alice <## "#team: you changed address for bob" + threadDelay 100000 alice #$> ("/_get chat #1 count=100", chat, [(1, e2eeInfoNoPQStr), (0, "connected"), (1, "started changing address for bob..."), (1, "started changing address for bob..."), (1, "you changed address for bob")]) bob #$> ("/_get chat #1 count=100", chat, groupFeatures <> [(0, "connected"), (0, "started changing address for you..."), (0, "started changing address for you..."), (0, "changed address for you")]) alice #> "#team hey" @@ -2511,6 +2521,7 @@ testSyncRatchet tmp = alice <## "bob: connection synchronized" bob <## "alice: connection synchronized" + threadDelay 100000 bob #$> ("/_get chat @2 count=3", chat, [(1, "connection synchronization started"), (0, "connection synchronization agreed"), (0, "connection synchronized")]) alice #$> ("/_get chat @2 count=2", chat, [(0, "connection synchronization agreed"), (0, "connection synchronized")]) @@ -2550,6 +2561,7 @@ testSyncRatchetCodeReset tmp = alice <## "bob: connection synchronized" bob <## "alice: connection synchronized" + threadDelay 100000 bob #$> ("/_get chat @2 count=4", chat, [(1, "connection synchronization started"), (0, "connection synchronization agreed"), (0, "security code changed"), (0, "connection synchronized")]) alice #$> ("/_get chat @2 count=2", chat, [(0, "connection synchronization agreed"), (0, "connection synchronized")]) diff --git a/tests/ChatTests/Groups.hs b/tests/ChatTests/Groups.hs index 46fedd1b48..3948168dae 100644 --- a/tests/ChatTests/Groups.hs +++ b/tests/ChatTests/Groups.hs @@ -69,7 +69,7 @@ chatGroupTests = do it "group is known if host contact was deleted" testPlanHostContactDeletedGroupLinkKnown it "own group link" testPlanGroupLinkOwn it "connecting via group link" testPlanGroupLinkConnecting - xit "re-join existing group after leaving" testPlanGroupLinkLeaveRejoin + it "re-join existing group after leaving" testPlanGroupLinkLeaveRejoin describe "group links without contact" $ do it "join via group link without creating contact" testGroupLinkNoContact it "invitees were previously connected as contacts" testGroupLinkNoContactInviteesWereConnected @@ -2578,13 +2578,15 @@ testPlanGroupLinkOwn tmp = testPlanGroupLinkConnecting :: HasCallStack => FilePath -> IO () testPlanGroupLinkConnecting tmp = do - gLink <- withNewTestChatCfg tmp cfg "alice" aliceProfile $ \alice -> do + -- gLink <- withNewTestChatCfg tmp cfg "alice" aliceProfile $ \alice -> do + gLink <- withNewTestChatCfg tmp cfg "alice" aliceProfile $ \a -> withTestOutput a $ \alice -> do alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" alice ##> "/create link #team" getGroupLink alice "team" GRMember True - withNewTestChatCfg tmp cfg "bob" bobProfile $ \bob -> do + -- withNewTestChatCfg tmp cfg "bob" bobProfile $ \bob -> do + withNewTestChatCfg tmp cfg "bob" bobProfile $ \b -> withTestOutput b $ \bob -> do threadDelay 100000 bob ##> ("/c " <> gLink) @@ -2598,13 +2600,15 @@ testPlanGroupLinkConnecting tmp = do bob <## "group link: connecting, allowed to reconnect" threadDelay 100000 - withTestChatCfg tmp cfg "alice" $ \alice -> do + -- withTestChatCfg tmp cfg "alice" $ \alice -> do + withTestChatCfg tmp cfg "alice" $ \a -> withTestOutput a $ \alice -> do alice <### [ "1 group links active", "#team: group is empty", "bob (Bob): accepting request to join group #team..." ] - withTestChatCfg tmp cfg "bob" $ \bob -> do + -- withTestChatCfg tmp cfg "bob" $ \bob -> do + withTestChatCfg tmp cfg "bob" $ \b -> withTestOutput b $ \bob -> do threadDelay 500000 bob ##> ("/_connect plan 1 " <> gLink) bob <## "group link: connecting" @@ -2621,9 +2625,8 @@ testPlanGroupLinkConnecting tmp = do testPlanGroupLinkLeaveRejoin :: HasCallStack => FilePath -> IO () testPlanGroupLinkLeaveRejoin = testChatCfg2 testCfgGroupLinkViaContact aliceProfile bobProfile $ - \a b -> do - let alice = a {printOutput = True} - bob = b {printOutput = True} + -- \alice bob -> do + \a b -> withTestOutput a $ \alice -> withTestOutput b $ \bob -> do alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" @@ -2643,6 +2646,8 @@ testPlanGroupLinkLeaveRejoin = bob <## "#team: you joined the group" ] + threadDelay 100000 + bob ##> ("/_connect plan 1 " <> gLink) bob <## "group link: known group #team" bob <## "use #team to send messages" @@ -2651,6 +2656,8 @@ testPlanGroupLinkLeaveRejoin = bob <## "group link: known group #team" bob <## "use #team to send messages" + threadDelay 100000 + bob ##> "/leave #team" concurrentlyN_ [ do @@ -2659,6 +2666,8 @@ testPlanGroupLinkLeaveRejoin = alice <## "#team: bob left the group" ] + threadDelay 100000 + bob ##> ("/_connect plan 1 " <> gLink) bob <## "group link: ok to connect" @@ -3296,6 +3305,7 @@ testGroupSyncRatchet tmp = alice <## "#team bob: connection synchronized" bob <## "#team alice: connection synchronized" + threadDelay 100000 bob #$> ("/_get chat #1 count=3", chat, [(1, "connection synchronization started for alice"), (0, "connection synchronization agreed"), (0, "connection synchronized")]) alice #$> ("/_get chat #1 count=2", chat, [(0, "connection synchronization agreed"), (0, "connection synchronized")]) @@ -3336,6 +3346,7 @@ testGroupSyncRatchetCodeReset tmp = alice <## "#team bob: connection synchronized" bob <## "#team alice: connection synchronized" + threadDelay 100000 bob #$> ("/_get chat #1 count=4", chat, [(1, "connection synchronization started for alice"), (0, "connection synchronization agreed"), (0, "security code changed"), (0, "connection synchronized")]) alice #$> ("/_get chat #1 count=2", chat, [(0, "connection synchronization agreed"), (0, "connection synchronized")]) diff --git a/tests/ChatTests/Profiles.hs b/tests/ChatTests/Profiles.hs index a8afa05af3..3d92a73313 100644 --- a/tests/ChatTests/Profiles.hs +++ b/tests/ChatTests/Profiles.hs @@ -397,6 +397,7 @@ testDeduplicateContactRequests = testChat3 aliceProfile bobProfile cathProfile $ bob ##> ("/c " <> cLink) bob <## "contact address: known contact alice" bob <## "use @alice to send messages" + threadDelay 100000 alice @@@ [("@bob", lastChatFeature)] bob @@@ [("@alice", lastChatFeature), (":2", ""), (":1", "")] bob ##> "/_delete :1" @@ -470,6 +471,7 @@ testDeduplicateContactRequestsProfileChange = testChat3 aliceProfile bobProfile bob ##> ("/c " <> cLink) bob <## "contact address: known contact alice" bob <## "use @alice to send messages" + threadDelay 100000 alice @@@ [("@robert", lastChatFeature)] bob @@@ [("@alice", lastChatFeature), (":3", ""), (":2", ""), (":1", "")] bob ##> "/_delete :1" @@ -488,6 +490,7 @@ testDeduplicateContactRequestsProfileChange = testChat3 aliceProfile bobProfile bob <## "use @alice to send messages" alice <##> bob + threadDelay 100000 alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "hi"), (0, "hey"), (1, "hi"), (0, "hey")]) bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "hi"), (1, "hey"), (0, "hi"), (1, "hey")]) @@ -655,6 +658,7 @@ testPlanAddressOwn tmp = "alice_2 (Alice): contact is connected" ] + threadDelay 100000 alice @@@ [("@alice_1", lastChatFeature), ("@alice_2", lastChatFeature)] alice `send` "@alice_2 hi" alice @@ -1948,21 +1952,29 @@ testGroupPrefsDirectForRole = testChat4 aliceProfile bobProfile cathProfile danP bob <## "#team: cath added dan (Daniel) to the group (connecting...)" bob <## "#team: new member dan is connected" ] - -- dan cannot send direct messages to alice (owner) + + -- dan cannot send direct messages to alice dan ##> "@alice hello alice" dan <## "bad chat command: direct messages not allowed" (alice hello dan" - dan <## "alice (Alice): contact is connected" - -- and now dan can too + alice + <### [ "member #team dan does not have direct connection, creating", + "contact for member #team dan is created", + "sent invitation to connect directly to member #team dan", + WithTime "@dan hello dan" + ] + dan + <### [ "#team alice is creating direct contact alice with you", + WithTime "alice> hello dan" + ] + concurrently_ + (alice <## "dan (Daniel): contact is connected") + (dan <## "alice (Alice): contact is connected") + + -- now dan can send messages to alice dan #> "@alice hi alice" alice <# "dan> hi alice" where diff --git a/tests/ChatTests/Utils.hs b/tests/ChatTests/Utils.hs index a3ee0d5ca8..8c022d5bfd 100644 --- a/tests/ChatTests/Utils.hs +++ b/tests/ChatTests/Utils.hs @@ -298,7 +298,7 @@ itemId i = show $ length chatFeatures + i (@@@) :: HasCallStack => TestCC -> [(String, String)] -> Expectation (@@@) cc res = do - threadDelay 10000 + threadDelay 100000 getChats mapChats cc res mapChats :: [(String, String, Maybe ConnStatus)] -> [(String, String)] From a0c257962e907fa050390e735c08080a74c723a9 Mon Sep 17 00:00:00 2001 From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com> Date: Wed, 15 May 2024 17:43:20 +0700 Subject: [PATCH 4/5] android, desktop: fix file menu and icon (#4185) * android, desktop: fix file menu and icon * simplify * space --- .../chat/simplex/common/model/ChatModel.kt | 6 +++++ .../common/views/chat/item/CIFileView.kt | 22 ++++++++++++++----- .../common/views/chat/item/ChatItemView.kt | 10 +++------ .../common/views/chat/item/FramedItemView.kt | 3 +-- 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 3838cd45a6..441e8e0186 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -2653,6 +2653,12 @@ data class CIFile( return res } + fun forwardingAllowed(): Boolean = when { + chatModel.connectedToRemote() && cachedRemoteFileRequests[fileSource] != false && loaded -> true + getLoadedFilePath(this) != null -> true + else -> false + } + companion object { fun getSample( fileId: Long = 1, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt index 6ad75057f6..f079a152ab 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt @@ -1,6 +1,7 @@ package chat.simplex.common.views.chat.item import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CornerSize import androidx.compose.foundation.shape.RoundedCornerShape @@ -28,6 +29,7 @@ import java.net.URI fun CIFileView( file: CIFile?, edited: Boolean, + showMenu: MutableState, receiveFile: (Long) -> Unit ) { val saveFileLauncher = rememberSaveFileLauncher(ciFile = file) @@ -86,7 +88,7 @@ fun CIFileView( ) FileProtocol.LOCAL -> {} } - file.fileStatus is CIFileStatus.RcvComplete || (file.fileStatus is CIFileStatus.SndStored && file.fileProtocol == FileProtocol.LOCAL) -> { + file.forwardingAllowed() -> { withLongRunningApi(slow = 600_000) { var filePath = getLoadedFilePath(file) if (chatModel.connectedToRemote() && filePath == null) { @@ -136,8 +138,7 @@ fun CIFileView( Box( Modifier .size(42.dp) - .clip(RoundedCornerShape(4.dp)) - .clickable(onClick = { fileAction() }), + .clip(RoundedCornerShape(4.dp)), contentAlignment = Alignment.Center ) { if (file != null) { @@ -154,7 +155,13 @@ fun CIFileView( FileProtocol.SMP -> progressIndicator() FileProtocol.LOCAL -> {} } - is CIFileStatus.SndComplete -> fileIcon(innerIcon = painterResource(MR.images.ic_check_filled)) + is CIFileStatus.SndComplete -> { + if ((file.forwardingAllowed() || (chatModel.connectedToRemote() && CIFile.cachedRemoteFileRequests[file.fileSource] == true))) { + fileIcon() + } else { + fileIcon(innerIcon = painterResource(MR.images.ic_check_filled)) + } + } is CIFileStatus.SndCancelled -> fileIcon(innerIcon = painterResource(MR.images.ic_close)) is CIFileStatus.SndError -> fileIcon(innerIcon = painterResource(MR.images.ic_close)) is CIFileStatus.RcvInvitation -> @@ -181,7 +188,12 @@ fun CIFileView( } Row( - Modifier.clickable(onClick = { fileAction() }).padding(top = 4.dp, bottom = 6.dp, start = 6.dp, end = 12.dp), + Modifier + .combinedClickable( + onClick = { fileAction() }, + onLongClick = { showMenu.value = true } + ) + .padding(top = 4.dp, bottom = 6.dp, start = 6.dp, end = 12.dp), //Modifier.clickable(enabled = file?.fileSource != null) { if (file?.fileSource != null && getLoadedFilePath(file) != null) openFile(file.fileSource) }.padding(top = 4.dp, bottom = 6.dp, start = 6.dp, end = 12.dp), verticalAlignment = Alignment.Bottom, horizontalArrangement = Arrangement.spacedBy(2.dp) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt index 10078dc266..8dc7f8bcea 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt @@ -204,14 +204,10 @@ fun ChatItemView( } val clipboard = LocalClipboardManager.current val cachedRemoteReqs = remember { CIFile.cachedRemoteFileRequests } - fun fileForwardingAllowed() = when { - cItem.file != null && chatModel.connectedToRemote() && cachedRemoteReqs[cItem.file.fileSource] != false && cItem.file.loaded -> true - getLoadedFilePath(cItem.file) != null -> true - else -> false - } + val copyAndShareAllowed = when { cItem.content.text.isNotEmpty() -> true - fileForwardingAllowed() -> true + cItem.file?.forwardingAllowed() == true -> true else -> false } @@ -261,7 +257,7 @@ fun ChatItemView( }) } if (cItem.meta.itemDeleted == null && - (cItem.file == null || fileForwardingAllowed()) && + (cItem.file == null || cItem.file.forwardingAllowed()) && !cItem.isLiveDummy && !live ) { ItemAction(stringResource(MR.strings.forward_chat_item), painterResource(MR.images.ic_forward), onClick = { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt index 2ac97321c6..09cb1cfd23 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt @@ -23,7 +23,6 @@ import chat.simplex.common.model.* import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.* import chat.simplex.common.views.helpers.* -import chat.simplex.common.views.chat.MEMBER_IMAGE_SIZE import chat.simplex.res.MR import kotlin.math.min @@ -179,7 +178,7 @@ fun FramedItemView( @Composable fun ciFileView(ci: ChatItem, text: String) { - CIFileView(ci.file, ci.meta.itemEdited, receiveFile) + CIFileView(ci.file, ci.meta.itemEdited, showMenu, receiveFile) if (text != "" || ci.meta.isLive) { CIMarkdownText(ci, chatTTL, linkMode = linkMode, uriHandler) } From 4c0d47bbd4bdc9737a68cb255a70b9042b148f8b Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Wed, 15 May 2024 15:30:05 +0400 Subject: [PATCH 5/5] core: message statuses for sending proxies (#4161) * core: delivery path * update simplexmq * via proxy snd flags * error statuses * rework errors * proxy expired errors * corrections * move backwards compatibile parser to new type * update simplexmq * names * refactor, style * simplexmq * refactor --------- Co-authored-by: Evgeny Poberezkin --- cabal.project | 2 +- scripts/nix/sha256map.nix | 2 +- simplex-chat.cabal | 1 + src/Simplex/Chat.hs | 62 ++++++++----- src/Simplex/Chat/Messages.hs | 89 ++++++++++++++++--- .../M20240510_chat_items_via_proxy.hs | 20 +++++ src/Simplex/Chat/Migrations/chat_schema.sql | 5 +- src/Simplex/Chat/Store/Messages.hs | 56 ++++++++---- src/Simplex/Chat/Store/Migrations.hs | 4 +- tests/ChatClient.hs | 7 +- 10 files changed, 191 insertions(+), 57 deletions(-) create mode 100644 src/Simplex/Chat/Migrations/M20240510_chat_items_via_proxy.hs diff --git a/cabal.project b/cabal.project index 334e106121..7ff4fa4a10 100644 --- a/cabal.project +++ b/cabal.project @@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: f51cf1deacd14aced88652ccfa06a746999e2ac6 + tag: 2d2cc86bd82a5c604901ae813a6cbcc21f3e2024 source-repository-package type: git diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index 7577d1eae5..6ac9ac1a99 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."f51cf1deacd14aced88652ccfa06a746999e2ac6" = "1q1lcjbapq0mrz0z99shajn364jf7j2j6gm5qir2h230l5fg6mih"; + "https://github.com/simplex-chat/simplexmq.git"."2d2cc86bd82a5c604901ae813a6cbcc21f3e2024" = "0gny4qywp2hda9fqs5gm3zkr8vd4jriy3iqp7ap7bwc12is6n8zw"; "https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d"; "https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl"; diff --git a/simplex-chat.cabal b/simplex-chat.cabal index e8f3601d96..d5ebcffab2 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -143,6 +143,7 @@ library Simplex.Chat.Migrations.M20240402_item_forwarded Simplex.Chat.Migrations.M20240430_ui_theme Simplex.Chat.Migrations.M20240501_chat_deleted + Simplex.Chat.Migrations.M20240510_chat_items_via_proxy Simplex.Chat.Mobile Simplex.Chat.Mobile.File Simplex.Chat.Mobile.Shared diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 2925ef000e..f2f6fb2783 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -100,7 +100,7 @@ import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), Migrati import Simplex.Messaging.Agent.Store.SQLite.DB (SlowQueryStats (..)) import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import qualified Simplex.Messaging.Agent.Store.SQLite.Migrations as Migrations -import Simplex.Messaging.Client (defaultNetworkConfig) +import Simplex.Messaging.Client (ProxyClientError (..), defaultNetworkConfig) import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..)) import qualified Simplex.Messaging.Crypto.File as CF @@ -113,6 +113,7 @@ import Simplex.Messaging.Protocol (AProtoServerWithAuth (..), AProtocolType (..) import qualified Simplex.Messaging.Protocol as SMP import Simplex.Messaging.ServiceScheme (ServiceScheme (..)) import qualified Simplex.Messaging.TMap as TM +import Simplex.Messaging.Transport (TransportError (..)) import Simplex.Messaging.Transport.Client (defaultSocksProxy) import Simplex.Messaging.Util import Simplex.Messaging.Version @@ -699,10 +700,7 @@ processChatCommand' vr = \case (,) <$> getAChatItem db vr user chatRef itemId <*> liftIO (getChatItemVersions db itemId) let itemVersions = if null versions then maybeToList $ mkItemVersion ci else versions memberDeliveryStatuses <- case (cType, dir) of - (SCTGroup, SMDSnd) -> do - withStore' (`getGroupSndStatuses` itemId) >>= \case - [] -> pure Nothing - memStatuses -> pure $ Just $ map (uncurry MemberDeliveryStatus) memStatuses + (SCTGroup, SMDSnd) -> L.nonEmpty <$> withStore' (`getGroupSndStatuses` itemId) _ -> pure Nothing forwardedFromChatItem <- getForwardedFromItem user ci pure $ CRChatItemInfo user aci ChatItemInfo {itemVersions, memberDeliveryStatuses, forwardedFromChatItem} @@ -3896,7 +3894,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = withAckMessage' agentConnId meta $ void $ saveDirectRcvMSG conn meta msgBody - SENT msgId -> + SENT msgId _proxy -> sentMsgDeliveryEvent conn msgId OK -> -- [async agent commands] continuation on receiving OK @@ -4029,10 +4027,13 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = notifyMemberConnected gInfo m $ Just ct let connectedIncognito = contactConnIncognito ct || incognitoMembership gInfo when (memberCategory m == GCPreMember) $ probeMatchingContactsAndMembers ct connectedIncognito True - SENT msgId -> do + SENT msgId proxy -> do sentMsgDeliveryEvent conn msgId checkSndInlineFTComplete conn msgId - updateDirectItemStatus ct conn msgId $ CISSndSent SSPComplete + ci_ <- withStore $ \db -> do + ci_ <- updateDirectItemStatus' db ct conn msgId (CISSndSent SSPComplete) + forM ci_ $ \ci -> liftIO $ setDirectSndChatItemViaProxy db user ct ci (isJust proxy) + forM_ ci_ $ \ci -> toView $ CRChatItemStatusUpdated user (AChatItem SCTDirect SMDSnd (DirectChat ct) ci) SWITCH qd phase cStats -> do toView $ CRContactSwitch user ct (SwitchProgress qd phase cStats) when (phase `elem` [SPStarted, SPCompleted]) $ case qd of @@ -4067,13 +4068,15 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = OK -> -- [async agent commands] continuation on receiving OK when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> pure () + MWARN msgId err -> + updateDirectItemStatus ct conn msgId (CISSndWarning $ agentSndError err) MERR msgId err -> do - updateDirectItemStatus ct conn msgId $ agentErrToItemStatus err + updateDirectItemStatus ct conn msgId (CISSndError $ agentSndError err) toView $ CRChatError (Just user) (ChatErrorAgent err $ Just connEntity) incAuthErrCounter connEntity conn err MERRS msgIds err -> do -- error cannot be AUTH error here - updateDirectItemsStatus ct conn (L.toList msgIds) $ agentErrToItemStatus err + updateDirectItemsStatus ct conn (L.toList msgIds) (CISSndError $ agentSndError err) toView $ CRChatError (Just user) (ChatErrorAgent err $ Just connEntity) ERR err -> do toView $ CRChatError (Just user) (ChatErrorAgent err $ Just connEntity) @@ -4411,10 +4414,10 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = RCVD msgMeta msgRcpt -> withAckMessage' agentConnId msgMeta $ groupMsgReceived gInfo m conn msgMeta msgRcpt - SENT msgId -> do + SENT msgId proxy -> do sentMsgDeliveryEvent conn msgId checkSndInlineFTComplete conn msgId - updateGroupItemStatus gInfo m conn msgId $ CISSndSent SSPComplete + updateGroupItemStatus gInfo m conn msgId (CISSndSent SSPComplete) (Just $ isJust proxy) SWITCH qd phase cStats -> do toView $ CRGroupMemberSwitch user gInfo m (SwitchProgress qd phase cStats) when (phase `elem` [SPStarted, SPCompleted]) $ case qd of @@ -4450,13 +4453,15 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = OK -> -- [async agent commands] continuation on receiving OK when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> pure () + MWARN msgId err -> + withStore' $ \db -> updateGroupItemErrorStatus db msgId (groupMemberId' m) (CISSndWarning $ agentSndError err) MERR msgId err -> do - withStore' $ \db -> updateGroupItemErrorStatus db msgId (groupMemberId' m) $ agentErrToItemStatus err + withStore' $ \db -> updateGroupItemErrorStatus db msgId (groupMemberId' m) (CISSndError $ agentSndError err) -- group errors are silenced to reduce load on UI event log -- toView $ CRChatError (Just user) (ChatErrorAgent err $ Just connEntity) incAuthErrCounter connEntity conn err MERRS msgIds err -> do - let newStatus = agentErrToItemStatus err + let newStatus = CISSndError $ agentSndError err -- error cannot be AUTH error here withStore' $ \db -> forM_ msgIds $ \msgId -> updateGroupItemErrorStatus db msgId (groupMemberId' m) newStatus `catchAll_` pure () @@ -4517,7 +4522,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = updateDirectCIFileStatus db vr user fileId $ CIFSSndTransfer 0 1 toView $ CRSndFileStart user ci ft sendFileChunk user ft - SENT msgId -> do + SENT msgId _proxy -> do withStore' $ \db -> updateSndFileChunkSent db ft msgId unless (fileStatus == FSCancelled) $ sendFileChunk user ft MERR _ err -> do @@ -4729,9 +4734,21 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = sentMsgDeliveryEvent Connection {connId} msgId = withStore' $ \db -> updateSndMsgDeliveryStatus db connId msgId MDSSndSent - agentErrToItemStatus :: AgentErrorType -> CIStatus 'MDSnd - agentErrToItemStatus (SMP _ AUTH) = CISSndErrorAuth - agentErrToItemStatus err = CISSndError . T.unpack . safeDecodeUtf8 $ strEncode err + agentSndError :: AgentErrorType -> SndError + agentSndError = \case + SMP _ AUTH -> SndErrAuth + SMP _ QUOTA -> SndErrQuota + BROKER _ e -> brokerError SndErrRelay e + SMP proxySrv (SMP.PROXY (SMP.BROKER e)) -> brokerError (SndErrProxy proxySrv) e + AP.PROXY proxySrv _ (ProxyProtocolError (SMP.PROXY (SMP.BROKER e))) -> brokerError (SndErrProxyRelay proxySrv) e + e -> SndErrOther . safeDecodeUtf8 $ strEncode e + where + brokerError srvErr = \case + NETWORK -> SndErrExpired + TIMEOUT -> SndErrExpired + HOST -> srvErr SrvErrHost + SMP.TRANSPORT TEVersion -> srvErr SrvErrVersion + e -> srvErr . SrvErrOther . safeDecodeUtf8 $ strEncode e badRcvFileChunk :: RcvFileTransfer -> String -> CM () badRcvFileChunk ft err = @@ -6060,7 +6077,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = checkIntegrityCreateItem (CDGroupRcv gInfo m) msgMeta `catchChatError` \_ -> pure () forM_ msgRcpts $ \MsgReceipt {agentMsgId, msgRcptStatus} -> do withStore' $ \db -> updateSndMsgDeliveryStatus db connId agentMsgId $ MDSSndRcvd msgRcptStatus - updateGroupItemStatus gInfo m conn agentMsgId $ CISSndRcvd msgRcptStatus SSPComplete + updateGroupItemStatus gInfo m conn agentMsgId (CISSndRcvd msgRcptStatus SSPComplete) Nothing updateDirectItemsStatus :: Contact -> Connection -> [AgentMsgId] -> CIStatus 'MDSnd -> CM () updateDirectItemsStatus ct conn msgIds newStatus = do @@ -6097,11 +6114,12 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = | otherwise -> updateGroupSndStatus db itemId groupMemberId newStatus $> True _ -> pure False - updateGroupItemStatus :: GroupInfo -> GroupMember -> Connection -> AgentMsgId -> CIStatus 'MDSnd -> CM () - updateGroupItemStatus gInfo@GroupInfo {groupId} GroupMember {groupMemberId} Connection {connId} msgId newMemStatus = + updateGroupItemStatus :: GroupInfo -> GroupMember -> Connection -> AgentMsgId -> CIStatus 'MDSnd -> Maybe Bool -> CM () + updateGroupItemStatus gInfo@GroupInfo {groupId} GroupMember {groupMemberId} Connection {connId} msgId newMemStatus viaProxy_ = withStore' (\db -> getGroupChatItemByAgentMsgId db user groupId connId msgId) >>= \case Just (CChatItem SMDSnd ChatItem {meta = CIMeta {itemStatus = CISSndRcvd _ SSPComplete}}) -> pure () Just (CChatItem SMDSnd ChatItem {meta = CIMeta {itemId, itemStatus}}) -> do + forM_ viaProxy_ $ \viaProxy -> withStore' $ \db -> setGroupSndViaProxy db itemId groupMemberId viaProxy memStatusChanged <- updateGroupMemSndStatus itemId groupMemberId newMemStatus when memStatusChanged $ do memStatusCounts <- withStore' (`getGroupSndStatusCounts` itemId) @@ -6724,7 +6742,7 @@ mkChatItem :: (ChatTypeI c, MsgDirectionI d) => ChatDirection c d -> ChatItemId mkChatItem cd ciId content file quotedItem sharedMsgId itemForwarded itemTimed live itemTs forwardedByMember currentTs = let itemText = ciContentToText content itemStatus = ciCreateStatus content - meta = mkCIMeta ciId content itemText itemStatus sharedMsgId itemForwarded Nothing False itemTimed (justTrue live) currentTs itemTs forwardedByMember currentTs currentTs + meta = mkCIMeta ciId content itemText itemStatus Nothing sharedMsgId itemForwarded Nothing False itemTimed (justTrue live) currentTs itemTs forwardedByMember currentTs currentTs in ChatItem {chatDir = toCIDirection cd, meta, content, formattedText = parseMaybeMarkdownList itemText, quotedItem, reactions = [], file} deleteDirectCI :: MsgDirectionI d => User -> Contact -> ChatItem 'CTDirect d -> Bool -> Bool -> CM ChatResponse diff --git a/src/Simplex/Chat/Messages.hs b/src/Simplex/Chat/Messages.hs index 9ca191d3f9..83417efa59 100644 --- a/src/Simplex/Chat/Messages.hs +++ b/src/Simplex/Chat/Messages.hs @@ -29,6 +29,7 @@ import qualified Data.ByteString.Lazy.Char8 as LB import Data.Char (isSpace) import Data.Int (Int64) import Data.Kind (Constraint) +import Data.List.NonEmpty (NonEmpty) import Data.Maybe (fromMaybe, isJust, isNothing) import Data.Text (Text) import qualified Data.Text as T @@ -345,6 +346,7 @@ data CIMeta (c :: ChatType) (d :: MsgDirection) = CIMeta itemTs :: ChatItemTs, itemText :: Text, itemStatus :: CIStatus d, + sentViaProxy :: Maybe Bool, itemSharedMsgId :: Maybe SharedMsgId, itemForwarded :: Maybe CIForwardedFrom, itemDeleted :: Maybe (CIDeleted c), @@ -359,8 +361,8 @@ data CIMeta (c :: ChatType) (d :: MsgDirection) = CIMeta } deriving (Show) -mkCIMeta :: forall c d. ChatTypeI c => ChatItemId -> CIContent d -> Text -> CIStatus d -> Maybe SharedMsgId -> Maybe CIForwardedFrom -> Maybe (CIDeleted c) -> Bool -> Maybe CITimed -> Maybe Bool -> UTCTime -> ChatItemTs -> Maybe GroupMemberId -> UTCTime -> UTCTime -> CIMeta c d -mkCIMeta itemId itemContent itemText itemStatus itemSharedMsgId itemForwarded itemDeleted itemEdited itemTimed itemLive currentTs itemTs forwardedByMember createdAt updatedAt = +mkCIMeta :: forall c d. ChatTypeI c => ChatItemId -> CIContent d -> Text -> CIStatus d -> Maybe Bool -> Maybe SharedMsgId -> Maybe CIForwardedFrom -> Maybe (CIDeleted c) -> Bool -> Maybe CITimed -> Maybe Bool -> UTCTime -> ChatItemTs -> Maybe GroupMemberId -> UTCTime -> UTCTime -> CIMeta c d +mkCIMeta itemId itemContent itemText itemStatus sentViaProxy itemSharedMsgId itemForwarded itemDeleted itemEdited itemTimed itemLive currentTs itemTs forwardedByMember createdAt updatedAt = let deletable = case itemContent of CISndMsgContent _ -> case chatTypeI @c of @@ -368,7 +370,7 @@ mkCIMeta itemId itemContent itemText itemStatus itemSharedMsgId itemForwarded it _ -> diffUTCTime currentTs itemTs < nominalDay && isNothing itemDeleted _ -> False editable = deletable && isNothing itemForwarded - in CIMeta {itemId, itemTs, itemText, itemStatus, itemSharedMsgId, itemForwarded, itemDeleted, itemEdited, itemTimed, itemLive, deletable, editable, forwardedByMember, createdAt, updatedAt} + in CIMeta {itemId, itemTs, itemText, itemStatus, sentViaProxy, itemSharedMsgId, itemForwarded, itemDeleted, itemEdited, itemTimed, itemLive, deletable, editable, forwardedByMember, createdAt, updatedAt} dummyMeta :: ChatItemId -> UTCTime -> Text -> CIMeta c 'MDSnd dummyMeta itemId ts itemText = @@ -377,6 +379,7 @@ dummyMeta itemId ts itemText = itemTs = ts, itemText, itemStatus = CISSndNew, + sentViaProxy = Nothing, itemSharedMsgId = Nothing, itemForwarded = Nothing, itemDeleted = Nothing, @@ -683,8 +686,9 @@ data CIStatus (d :: MsgDirection) where CISSndNew :: CIStatus 'MDSnd CISSndSent :: SndCIStatusProgress -> CIStatus 'MDSnd CISSndRcvd :: MsgReceiptStatus -> SndCIStatusProgress -> CIStatus 'MDSnd - CISSndErrorAuth :: CIStatus 'MDSnd - CISSndError :: String -> CIStatus 'MDSnd + CISSndErrorAuth :: CIStatus 'MDSnd -- deprecated + CISSndError :: SndError -> CIStatus 'MDSnd + CISSndWarning :: SndError -> CIStatus 'MDSnd CISRcvNew :: CIStatus 'MDRcv CISRcvRead :: CIStatus 'MDRcv CISInvalid :: Text -> CIStatus 'MDSnd @@ -703,7 +707,8 @@ instance MsgDirectionI d => StrEncoding (CIStatus d) where CISSndSent sndProgress -> "snd_sent " <> strEncode sndProgress CISSndRcvd msgRcptStatus sndProgress -> "snd_rcvd " <> strEncode msgRcptStatus <> " " <> strEncode sndProgress CISSndErrorAuth -> "snd_error_auth" - CISSndError e -> "snd_error " <> encodeUtf8 (T.pack e) + CISSndError sndErr -> "snd_error " <> strEncode sndErr + CISSndWarning sndErr -> "snd_warning " <> strEncode sndErr CISRcvNew -> "rcv_new" CISRcvRead -> "rcv_read" CISInvalid {} -> "invalid" @@ -721,17 +726,68 @@ instance StrEncoding ACIStatus where "snd_sent" -> ACIStatus SMDSnd . CISSndSent <$> ((A.space *> strP) <|> pure SSPComplete) "snd_rcvd" -> ACIStatus SMDSnd <$> (CISSndRcvd <$> (A.space *> strP) <*> ((A.space *> strP) <|> pure SSPComplete)) "snd_error_auth" -> pure $ ACIStatus SMDSnd CISSndErrorAuth - "snd_error" -> ACIStatus SMDSnd . CISSndError . T.unpack . safeDecodeUtf8 <$> (A.space *> A.takeByteString) + "snd_error" -> ACIStatus SMDSnd . CISSndError <$> (A.space *> strP) + "snd_warning" -> ACIStatus SMDSnd . CISSndWarning <$> (A.space *> strP) "rcv_new" -> pure $ ACIStatus SMDRcv CISRcvNew "rcv_read" -> pure $ ACIStatus SMDRcv CISRcvRead _ -> fail "bad status" +-- see serverHostError in agent +data SndError + = SndErrAuth + | SndErrQuota + | SndErrExpired -- TIMEOUT/NETWORK errors + | SndErrRelay {srvError :: SrvError} -- BROKER errors (other than TIMEOUT/NETWORK) + | SndErrProxy {proxyServer :: String, srvError :: SrvError} -- SMP PROXY errors + | SndErrProxyRelay {proxyServer :: String, srvError :: SrvError} -- PROXY BROKER errors + | SndErrOther {sndError :: Text} -- other errors + deriving (Eq, Show) + +data SrvError + = SrvErrHost + | SrvErrVersion + | SrvErrOther {srvError :: Text} + deriving (Eq, Show) + +instance StrEncoding SndError where + strEncode = \case + SndErrAuth -> "auth" + SndErrQuota -> "quota" + SndErrExpired -> "expired" + SndErrRelay srvErr -> "relay " <> strEncode srvErr + SndErrProxy proxy srvErr -> "proxy " <> encodeUtf8 (T.pack proxy) <> " " <> strEncode srvErr + SndErrProxyRelay proxy srvErr -> "proxy_relay " <> encodeUtf8 (T.pack proxy) <> " " <> strEncode srvErr + SndErrOther e -> "other " <> encodeUtf8 e + strP = + A.takeWhile1 (/= ' ') >>= \case + "auth" -> pure SndErrAuth + "quota" -> pure SndErrQuota + "expired" -> pure SndErrExpired + "relay" -> SndErrRelay <$> (A.space *> strP) + "proxy" -> SndErrProxy . T.unpack . safeDecodeUtf8 <$> (A.space *> A.takeWhile1 (/= ' ') <* A.space) <*> strP + "proxy_relay" -> SndErrProxyRelay . T.unpack . safeDecodeUtf8 <$> (A.space *> A.takeWhile1 (/= ' ') <* A.space) <*> strP + "other" -> SndErrOther . safeDecodeUtf8 <$> (A.space *> A.takeByteString) + s -> SndErrOther . safeDecodeUtf8 . (s <>) <$> A.takeByteString -- for backward compatibility with `CISSndError String` + +instance StrEncoding SrvError where + strEncode = \case + SrvErrHost -> "host" + SrvErrVersion -> "version" + SrvErrOther e -> "other " <> encodeUtf8 e + strP = + A.takeWhile1 (/= ' ') >>= \case + "host" -> pure SrvErrHost + "version" -> pure SrvErrVersion + "other" -> SrvErrOther . safeDecodeUtf8 <$> (A.space *> A.takeByteString) + _ -> fail "bad SrvError" + data JSONCIStatus = JCISSndNew | JCISSndSent {sndProgress :: SndCIStatusProgress} | JCISSndRcvd {msgRcptStatus :: MsgReceiptStatus, sndProgress :: SndCIStatusProgress} - | JCISSndErrorAuth - | JCISSndError {agentError :: String} + | JCISSndErrorAuth -- deprecated + | JCISSndError {agentError :: SndError} + | JCISSndWarning {agentError :: SndError} | JCISRcvNew | JCISRcvRead | JCISInvalid {text :: Text} @@ -743,7 +799,8 @@ jsonCIStatus = \case CISSndSent sndProgress -> JCISSndSent sndProgress CISSndRcvd msgRcptStatus sndProgress -> JCISSndRcvd msgRcptStatus sndProgress CISSndErrorAuth -> JCISSndErrorAuth - CISSndError e -> JCISSndError e + CISSndError sndErr -> JCISSndError sndErr + CISSndWarning sndErr -> JCISSndWarning sndErr CISRcvNew -> JCISRcvNew CISRcvRead -> JCISRcvRead CISInvalid text -> JCISInvalid text @@ -754,7 +811,8 @@ jsonACIStatus = \case JCISSndSent sndProgress -> ACIStatus SMDSnd $ CISSndSent sndProgress JCISSndRcvd msgRcptStatus sndProgress -> ACIStatus SMDSnd $ CISSndRcvd msgRcptStatus sndProgress JCISSndErrorAuth -> ACIStatus SMDSnd CISSndErrorAuth - JCISSndError e -> ACIStatus SMDSnd $ CISSndError e + JCISSndError sndErr -> ACIStatus SMDSnd $ CISSndError sndErr + JCISSndWarning sndErr -> ACIStatus SMDSnd $ CISSndWarning sndErr JCISRcvNew -> ACIStatus SMDRcv CISRcvNew JCISRcvRead -> ACIStatus SMDRcv CISRcvRead JCISInvalid text -> ACIStatus SMDSnd $ CISInvalid text @@ -1041,7 +1099,7 @@ instance TextEncoding CIForwardedFromTag where data ChatItemInfo = ChatItemInfo { itemVersions :: [ChatItemVersion], - memberDeliveryStatuses :: Maybe [MemberDeliveryStatus], + memberDeliveryStatuses :: Maybe (NonEmpty MemberDeliveryStatus), forwardedFromChatItem :: Maybe AChatItem } deriving (Show) @@ -1070,7 +1128,8 @@ mkItemVersion ChatItem {content, meta} = version <$> ciMsgContent content data MemberDeliveryStatus = MemberDeliveryStatus { groupMemberId :: GroupMemberId, - memberDeliveryStatus :: CIStatus 'MDSnd + memberDeliveryStatus :: CIStatus 'MDSnd, + sentViaProxy :: Maybe Bool } deriving (Eq, Show) @@ -1108,6 +1167,10 @@ $(JQ.deriveJSON defaultJSON ''CITimed) $(JQ.deriveJSON (enumJSON $ dropPrefix "SSP") ''SndCIStatusProgress) +$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "SrvErr") ''SrvError) + +$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "SndErr") ''SndError) + $(JQ.deriveJSON (sumTypeJSON $ dropPrefix "JCIS") ''JSONCIStatus) instance MsgDirectionI d => FromJSON (CIStatus d) where diff --git a/src/Simplex/Chat/Migrations/M20240510_chat_items_via_proxy.hs b/src/Simplex/Chat/Migrations/M20240510_chat_items_via_proxy.hs new file mode 100644 index 0000000000..3c32034344 --- /dev/null +++ b/src/Simplex/Chat/Migrations/M20240510_chat_items_via_proxy.hs @@ -0,0 +1,20 @@ +{-# LANGUAGE QuasiQuotes #-} + +module Simplex.Chat.Migrations.M20240510_chat_items_via_proxy where + +import Database.SQLite.Simple (Query) +import Database.SQLite.Simple.QQ (sql) + +m20240510_chat_items_via_proxy :: Query +m20240510_chat_items_via_proxy = + [sql| +ALTER TABLE chat_items ADD COLUMN via_proxy INTEGER; +ALTER TABLE group_snd_item_statuses ADD COLUMN via_proxy INTEGER; +|] + +down_m20240510_chat_items_via_proxy :: Query +down_m20240510_chat_items_via_proxy = + [sql| +ALTER TABLE chat_items DROP COLUMN via_proxy; +ALTER TABLE group_snd_item_statuses DROP COLUMN via_proxy; +|] diff --git a/src/Simplex/Chat/Migrations/chat_schema.sql b/src/Simplex/Chat/Migrations/chat_schema.sql index e700acd4d4..3ac9b9a98e 100644 --- a/src/Simplex/Chat/Migrations/chat_schema.sql +++ b/src/Simplex/Chat/Migrations/chat_schema.sql @@ -392,7 +392,8 @@ CREATE TABLE chat_items( fwd_from_msg_dir INTEGER, fwd_from_contact_id INTEGER REFERENCES contacts ON DELETE SET NULL, fwd_from_group_id INTEGER REFERENCES groups ON DELETE SET NULL, - fwd_from_chat_item_id INTEGER REFERENCES chat_items ON DELETE SET NULL + fwd_from_chat_item_id INTEGER REFERENCES chat_items ON DELETE SET NULL, + via_proxy INTEGER ); CREATE TABLE chat_item_messages( chat_item_id INTEGER NOT NULL REFERENCES chat_items ON DELETE CASCADE, @@ -503,6 +504,8 @@ CREATE TABLE group_snd_item_statuses( group_snd_item_status TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT(datetime('now')), updated_at TEXT NOT NULL DEFAULT(datetime('now')) + , + via_proxy INTEGER ); CREATE TABLE IF NOT EXISTS "sent_probes"( sent_probe_id INTEGER PRIMARY KEY, diff --git a/src/Simplex/Chat/Store/Messages.hs b/src/Simplex/Chat/Store/Messages.hs index 35e515cc2a..b0a0495c16 100644 --- a/src/Simplex/Chat/Store/Messages.hs +++ b/src/Simplex/Chat/Store/Messages.hs @@ -95,6 +95,7 @@ module Simplex.Chat.Store.Messages lookupChatItemByFileId, getChatItemByGroupId, updateDirectChatItemStatus, + setDirectSndChatItemViaProxy, getTimedItems, getChatItemTTL, setChatItemTTL, @@ -108,6 +109,7 @@ module Simplex.Chat.Store.Messages createGroupSndStatus, getGroupSndStatus, updateGroupSndStatus, + setGroupSndViaProxy, getGroupSndStatuses, getGroupSndStatusCounts, getGroupHistoryItems, @@ -806,7 +808,7 @@ getLocalChatPreview_ db user (LocalChatPD _ noteFolderId lastItemId_ stats) = do -- this function can be changed so it never fails, not only avoid failure on invalid json toLocalChatItem :: UTCTime -> ChatItemRow -> Either StoreError (CChatItem 'CTLocal) -toLocalChatItem currentTs ((itemId, itemTs, AMsgDirection msgDir, itemContentText, itemText, itemStatus, sharedMsgId) :. (itemDeleted, deletedTs, itemEdited, createdAt, updatedAt) :. forwardedFromRow :. (timedTTL, timedDeleteAt, itemLive) :. (fileId_, fileName_, fileSize_, filePath, fileKey, fileNonce, fileStatus_, fileProtocol_)) = +toLocalChatItem currentTs ((itemId, itemTs, AMsgDirection msgDir, itemContentText, itemText, itemStatus, sentViaProxy, sharedMsgId) :. (itemDeleted, deletedTs, itemEdited, createdAt, updatedAt) :. forwardedFromRow :. (timedTTL, timedDeleteAt, itemLive) :. (fileId_, fileName_, fileSize_, filePath, fileKey, fileNonce, fileStatus_, fileProtocol_)) = chatItem $ fromRight invalid $ dbParseACIContent itemContentText where invalid = ACIContent msgDir $ CIInvalidJSON itemContentText @@ -839,7 +841,7 @@ toLocalChatItem currentTs ((itemId, itemTs, AMsgDirection msgDir, itemContentTex _ -> Just (CIDeleted @CTLocal deletedTs) itemEdited' = fromMaybe False itemEdited itemForwarded = toCIForwardedFrom forwardedFromRow - in mkCIMeta itemId content itemText status sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed itemLive currentTs itemTs Nothing createdAt updatedAt + in mkCIMeta itemId content itemText status sentViaProxy sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed itemLive currentTs itemTs Nothing createdAt updatedAt ciTimed :: Maybe CITimed ciTimed = timedTTL >>= \ttl -> Just CITimed {ttl, deleteAt = timedDeleteAt} @@ -1407,7 +1409,7 @@ type ChatItemModeRow = (Maybe Int, Maybe UTCTime, Maybe Bool) type ChatItemForwardedFromRow = (Maybe CIForwardedFromTag, Maybe Text, Maybe MsgDirection, Maybe Int64, Maybe Int64, Maybe Int64) type ChatItemRow = - (Int64, ChatItemTs, AMsgDirection, Text, Text, ACIStatus, Maybe SharedMsgId) + (Int64, ChatItemTs, AMsgDirection, Text, Text, ACIStatus, Maybe Bool, Maybe SharedMsgId) :. (Int, Maybe UTCTime, Maybe Bool, UTCTime, UTCTime) :. ChatItemForwardedFromRow :. ChatItemModeRow @@ -1426,7 +1428,7 @@ toQuote (quotedItemId, quotedSharedMsgId, quotedSentAt, quotedMsgContent, _) dir -- this function can be changed so it never fails, not only avoid failure on invalid json toDirectChatItem :: UTCTime -> ChatItemRow :. QuoteRow -> Either StoreError (CChatItem 'CTDirect) -toDirectChatItem currentTs (((itemId, itemTs, AMsgDirection msgDir, itemContentText, itemText, itemStatus, sharedMsgId) :. (itemDeleted, deletedTs, itemEdited, createdAt, updatedAt) :. forwardedFromRow :. (timedTTL, timedDeleteAt, itemLive) :. (fileId_, fileName_, fileSize_, filePath, fileKey, fileNonce, fileStatus_, fileProtocol_)) :. quoteRow) = +toDirectChatItem currentTs (((itemId, itemTs, AMsgDirection msgDir, itemContentText, itemText, itemStatus, sentViaProxy, sharedMsgId) :. (itemDeleted, deletedTs, itemEdited, createdAt, updatedAt) :. forwardedFromRow :. (timedTTL, timedDeleteAt, itemLive) :. (fileId_, fileName_, fileSize_, filePath, fileKey, fileNonce, fileStatus_, fileProtocol_)) :. quoteRow) = chatItem $ fromRight invalid $ dbParseACIContent itemContentText where invalid = ACIContent msgDir $ CIInvalidJSON itemContentText @@ -1459,7 +1461,7 @@ toDirectChatItem currentTs (((itemId, itemTs, AMsgDirection msgDir, itemContentT _ -> Just (CIDeleted @CTDirect deletedTs) itemEdited' = fromMaybe False itemEdited itemForwarded = toCIForwardedFrom forwardedFromRow - in mkCIMeta itemId content itemText status sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed itemLive currentTs itemTs Nothing createdAt updatedAt + in mkCIMeta itemId content itemText status sentViaProxy sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed itemLive currentTs itemTs Nothing createdAt updatedAt ciTimed :: Maybe CITimed ciTimed = timedTTL >>= \ttl -> Just CITimed {ttl, deleteAt = timedDeleteAt} @@ -1483,7 +1485,7 @@ toGroupQuote qr@(_, _, _, _, quotedSent) quotedMember_ = toQuote qr $ direction -- this function can be changed so it never fails, not only avoid failure on invalid json toGroupChatItem :: UTCTime -> Int64 -> ChatItemRow :. Only (Maybe GroupMemberId) :. MaybeGroupMemberRow :. GroupQuoteRow :. MaybeGroupMemberRow -> Either StoreError (CChatItem 'CTGroup) -toGroupChatItem currentTs userContactId (((itemId, itemTs, AMsgDirection msgDir, itemContentText, itemText, itemStatus, sharedMsgId) :. (itemDeleted, deletedTs, itemEdited, createdAt, updatedAt) :. forwardedFromRow :. (timedTTL, timedDeleteAt, itemLive) :. (fileId_, fileName_, fileSize_, filePath, fileKey, fileNonce, fileStatus_, fileProtocol_)) :. Only forwardedByMember :. memberRow_ :. (quoteRow :. quotedMemberRow_) :. deletedByGroupMemberRow_) = do +toGroupChatItem currentTs userContactId (((itemId, itemTs, AMsgDirection msgDir, itemContentText, itemText, itemStatus, sentViaProxy, sharedMsgId) :. (itemDeleted, deletedTs, itemEdited, createdAt, updatedAt) :. forwardedFromRow :. (timedTTL, timedDeleteAt, itemLive) :. (fileId_, fileName_, fileSize_, filePath, fileKey, fileNonce, fileStatus_, fileProtocol_)) :. Only forwardedByMember :. memberRow_ :. (quoteRow :. quotedMemberRow_) :. deletedByGroupMemberRow_) = do chatItem $ fromRight invalid $ dbParseACIContent itemContentText where member_ = toMaybeGroupMember userContactId memberRow_ @@ -1521,7 +1523,7 @@ toGroupChatItem currentTs userContactId (((itemId, itemTs, AMsgDirection msgDir, _ -> Just (maybe (CIDeleted @CTGroup deletedTs) (CIModerated deletedTs) deletedByGroupMember_) itemEdited' = fromMaybe False itemEdited itemForwarded = toCIForwardedFrom forwardedFromRow - in mkCIMeta itemId content itemText status sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed itemLive currentTs itemTs forwardedByMember createdAt updatedAt + in mkCIMeta itemId content itemText status sentViaProxy sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed itemLive currentTs itemTs forwardedByMember createdAt updatedAt ciTimed :: Maybe CITimed ciTimed = timedTTL >>= \ttl -> Just CITimed {ttl, deleteAt = timedDeleteAt} @@ -1600,6 +1602,11 @@ updateDirectChatItemStatus db user@User {userId} ct@Contact {contactId} itemId i liftIO $ DB.execute db "UPDATE chat_items SET item_status = ?, updated_at = ? WHERE user_id = ? AND contact_id = ? AND chat_item_id = ?" (itemStatus, currentTs, userId, contactId, itemId) pure ci {meta = (meta ci) {itemStatus}} +setDirectSndChatItemViaProxy :: DB.Connection -> User -> Contact -> ChatItem 'CTDirect 'MDSnd -> Bool -> IO (ChatItem 'CTDirect 'MDSnd) +setDirectSndChatItemViaProxy db User {userId} Contact {contactId} ci viaProxy = do + DB.execute db "UPDATE chat_items SET via_proxy = ? WHERE user_id = ? AND contact_id = ? AND chat_item_id = ?" (viaProxy, userId, contactId, chatItemId' ci) + pure ci {meta = (meta ci) {sentViaProxy = Just viaProxy}} + updateDirectChatItem :: MsgDirectionI d => DB.Connection -> User -> Contact -> ChatItemId -> CIContent d -> Bool -> Bool -> Maybe CITimed -> Maybe MessageId -> ExceptT StoreError IO (ChatItem 'CTDirect d) updateDirectChatItem db user ct@Contact {contactId} itemId newContent edited live timed_ msgId_ = do ci <- liftEither . correctDir =<< getDirectCIWithReactions db user ct itemId @@ -1758,7 +1765,7 @@ getDirectChatItem db User {userId} contactId itemId = ExceptT $ do [sql| SELECT -- ChatItem - i.chat_item_id, i.item_ts, i.item_sent, i.item_content, i.item_text, i.item_status, i.shared_msg_id, + i.chat_item_id, i.item_ts, i.item_sent, i.item_content, i.item_text, i.item_status, i.via_proxy, i.shared_msg_id, i.item_deleted, i.item_deleted_ts, i.item_edited, i.created_at, i.updated_at, i.fwd_from_tag, i.fwd_from_chat_name, i.fwd_from_msg_dir, i.fwd_from_contact_id, i.fwd_from_group_id, i.fwd_from_chat_item_id, i.timed_ttl, i.timed_delete_at, i.item_live, @@ -2001,7 +2008,7 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do [sql| SELECT -- ChatItem - i.chat_item_id, i.item_ts, i.item_sent, i.item_content, i.item_text, i.item_status, i.shared_msg_id, + i.chat_item_id, i.item_ts, i.item_sent, i.item_content, i.item_text, i.item_status, i.via_proxy, i.shared_msg_id, i.item_deleted, i.item_deleted_ts, i.item_edited, i.created_at, i.updated_at, i.fwd_from_tag, i.fwd_from_chat_name, i.fwd_from_msg_dir, i.fwd_from_contact_id, i.fwd_from_group_id, i.fwd_from_chat_item_id, i.timed_ttl, i.timed_delete_at, i.item_live, @@ -2105,7 +2112,7 @@ getLocalChatItem db User {userId} folderId itemId = ExceptT $ do [sql| SELECT -- ChatItem - i.chat_item_id, i.item_ts, i.item_sent, i.item_content, i.item_text, i.item_status, i.shared_msg_id, + i.chat_item_id, i.item_ts, i.item_sent, i.item_content, i.item_text, i.item_status, i.via_proxy, i.shared_msg_id, i.item_deleted, i.item_deleted_ts, i.item_edited, i.created_at, i.updated_at, i.fwd_from_tag, i.fwd_from_chat_name, i.fwd_from_msg_dir, i.fwd_from_contact_id, i.fwd_from_group_id, i.fwd_from_chat_item_id, i.timed_ttl, i.timed_delete_at, i.item_live, @@ -2538,16 +2545,31 @@ updateGroupSndStatus db itemId memberId status = do |] (status, currentTs, itemId, memberId) -getGroupSndStatuses :: DB.Connection -> ChatItemId -> IO [(GroupMemberId, CIStatus 'MDSnd)] -getGroupSndStatuses db itemId = - DB.query +setGroupSndViaProxy :: DB.Connection -> ChatItemId -> GroupMemberId -> Bool -> IO () +setGroupSndViaProxy db itemId memberId viaProxy = + DB.execute db [sql| - SELECT group_member_id, group_snd_item_status - FROM group_snd_item_statuses - WHERE chat_item_id = ? + UPDATE group_snd_item_statuses + SET via_proxy = ? + WHERE chat_item_id = ? AND group_member_id = ? |] - (Only itemId) + (viaProxy, itemId, memberId) + +getGroupSndStatuses :: DB.Connection -> ChatItemId -> IO [MemberDeliveryStatus] +getGroupSndStatuses db itemId = + map memStatus + <$> DB.query + db + [sql| + SELECT group_member_id, group_snd_item_status, via_proxy + FROM group_snd_item_statuses + WHERE chat_item_id = ? + |] + (Only itemId) + where + memStatus (groupMemberId, memberDeliveryStatus, sentViaProxy) = + MemberDeliveryStatus {groupMemberId, memberDeliveryStatus, sentViaProxy} getGroupSndStatusCounts :: DB.Connection -> ChatItemId -> IO [(CIStatus 'MDSnd, Int)] getGroupSndStatusCounts db itemId = diff --git a/src/Simplex/Chat/Store/Migrations.hs b/src/Simplex/Chat/Store/Migrations.hs index e25f255bd8..ccc69d100e 100644 --- a/src/Simplex/Chat/Store/Migrations.hs +++ b/src/Simplex/Chat/Store/Migrations.hs @@ -107,6 +107,7 @@ import Simplex.Chat.Migrations.M20240324_custom_data import Simplex.Chat.Migrations.M20240402_item_forwarded import Simplex.Chat.Migrations.M20240430_ui_theme import Simplex.Chat.Migrations.M20240501_chat_deleted +import Simplex.Chat.Migrations.M20240510_chat_items_via_proxy import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..)) schemaMigrations :: [(String, Query, Maybe Query)] @@ -213,7 +214,8 @@ schemaMigrations = ("20240324_custom_data", m20240324_custom_data, Just down_m20240324_custom_data), ("20240402_item_forwarded", m20240402_item_forwarded, Just down_m20240402_item_forwarded), ("20240430_ui_theme", m20240430_ui_theme, Just down_m20240430_ui_theme), - ("20240501_chat_deleted", m20240501_chat_deleted, Just down_m20240501_chat_deleted) + ("20240501_chat_deleted", m20240501_chat_deleted, Just down_m20240501_chat_deleted), + ("20240510_chat_items_via_proxy", m20240510_chat_items_via_proxy, Just down_m20240510_chat_items_via_proxy) ] -- | The list of migrations in ascending order by date diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs index 4897a3b3d3..2bcd52ab3f 100644 --- a/tests/ChatClient.hs +++ b/tests/ChatClient.hs @@ -37,6 +37,7 @@ import Simplex.Chat.Types import Simplex.FileTransfer.Description (kb, mb) import Simplex.FileTransfer.Server (runXFTPServerBlocking) import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..), defaultFileExpiration) +import Simplex.FileTransfer.Transport (supportedFileServerVRange) import Simplex.Messaging.Agent (disposeAgentClient) import Simplex.Messaging.Agent.Env.SQLite import Simplex.Messaging.Agent.Protocol (currentSMPAgentVersion, duplexHandshakeSMPAgentVersion, pqdrSMPAgentVersion, supportedSMPAgentVRange) @@ -44,6 +45,7 @@ import Simplex.Messaging.Agent.RetryInterval import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), closeSQLiteStore) import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import Simplex.Messaging.Client (ProtocolClientConfig (..), defaultNetworkConfig) +import Simplex.Messaging.Client.Agent (defaultSMPClientAgentConfig) import Simplex.Messaging.Crypto.Ratchet (supportedE2EEncryptVRange) import qualified Simplex.Messaging.Crypto.Ratchet as CR import Simplex.Messaging.Server (runSMPServerBlocking) @@ -427,7 +429,9 @@ serverCfg = smpServerVRange = supportedServerSMPRelayVRange, transportConfig = defaultTransportServerConfig, smpHandshakeTimeout = 1000000, - controlPort = Nothing + controlPort = Nothing, + smpAgentCfg = defaultSMPClientAgentConfig, + allowSMPProxy = False } withSmpServer :: IO () -> IO () @@ -458,6 +462,7 @@ xftpServerConfig = caCertificateFile = "tests/fixtures/tls/ca.crt", privateKeyFile = "tests/fixtures/tls/server.key", certificateFile = "tests/fixtures/tls/server.crt", + xftpServerVRange = supportedFileServerVRange, logStatsInterval = Nothing, logStatsStartTime = 0, serverStatsLogFile = "tests/tmp/xftp-server-stats.daily.log",