Compare commits

..

245 Commits

Author SHA1 Message Date
Evgeny Poberezkin c96ad36923 Merge branch 'ab/mq-client' into ab/mq-client-android 2024-11-02 09:17:00 +00:00
Evgeny Poberezkin b75602f248 Merge branch 'master' into master-android 2024-11-02 09:02:54 +00:00
Evgeny Poberezkin d391cc23e7 Merge branch 'master' into ab/mq-client 2024-11-02 08:27:12 +00:00
IC Rainbow 4b6b247892 fix server config for mq master 2024-10-29 16:14:36 +02:00
IC Rainbow 4f0277a0a0 Use simplexmq with client_library flag 2024-10-29 14:11:37 +02:00
Evgeny Poberezkin 5d599a59b7 Merge branch 'master' into master-android 2024-10-18 18:38:02 +01:00
Evgeny Poberezkin af6b57e453 Merge branch 'master' into master-android 2024-10-18 14:21:30 +01:00
Evgeny Poberezkin 6bd2794548 Merge branch 'master' into master-android 2024-10-12 10:56:47 +01:00
Evgeny Poberezkin 59d86274cd Merge branch 'master' into master-android 2024-10-11 15:47:47 +01:00
Evgeny Poberezkin b6a9b25acd Merge branch 'master' into master-android 2024-10-08 14:08:16 +01:00
Evgeny Poberezkin 0e3b71866b Merge branch 'master' into master-android 2024-10-07 23:32:07 +01:00
Evgeny Poberezkin 3e9e7e29c0 Merge branch 'master' into master-android 2024-09-30 18:29:51 +01:00
Evgeny Poberezkin 531248f61a Merge branch 'master' into master-android 2024-09-26 13:45:40 +01:00
Evgeny Poberezkin 2defbdc361 Merge branch 'master' into master-android 2024-09-20 09:45:53 +01:00
Evgeny Poberezkin 8ce3d335b0 Merge branch 'master' into master-android 2024-09-18 09:20:04 +01:00
Evgeny Poberezkin 2295c2a85f Merge branch 'master' into master-android 2024-09-17 22:30:41 +01:00
Evgeny Poberezkin aa06b08e69 Merge branch 'master' into master-android 2024-09-16 13:17:54 +01:00
Evgeny Poberezkin 0b681d900b Merge branch 'master' into master-android 2024-09-12 22:20:35 +01:00
Evgeny Poberezkin a87153714f Merge branch 'master' into master-android 2024-09-12 15:22:58 +01:00
Evgeny Poberezkin a08200b3ab Merge branch 'master' into master-android 2024-09-05 12:07:51 +01:00
Evgeny Poberezkin 810f05d88f Merge branch 'master' into master-android 2024-08-31 12:06:19 +01:00
Evgeny Poberezkin 00aa615df7 Merge branch 'master' into master-android 2024-08-25 17:19:49 +01:00
Evgeny Poberezkin 6c3fd9b428 Merge branch 'master' into master-android 2024-08-22 12:24:03 +01:00
Evgeny Poberezkin c9f5066c60 Merge branch 'master' into master-android 2024-08-21 19:52:24 +01:00
Evgeny Poberezkin bc96e95849 Merge branch 'master' into master-android 2024-08-15 07:33:13 +01:00
Evgeny Poberezkin 8a301567e1 Merge branch 'master' into master-android 2024-08-14 20:30:44 +01:00
Evgeny Poberezkin afa6083cb3 Merge branch 'master' into master-android 2024-08-11 14:36:31 +01:00
Evgeny Poberezkin 160c253966 Merge branch 'master' into master-android 2024-08-10 21:53:33 +01:00
Evgeny Poberezkin 3d7434c52f Merge branch 'master' into master-android 2024-08-09 23:35:01 +01:00
Evgeny Poberezkin c78df15cd6 Merge branch 'master' into master-android 2024-08-08 13:19:17 +01:00
Evgeny Poberezkin 701bef2a50 Merge branch 'master' into master-android 2024-08-06 19:55:42 +01:00
Evgeny Poberezkin 560d9b1b03 Merge branch 'master' into master-android 2024-08-03 08:58:20 +01:00
Evgeny Poberezkin b82349164f Merge branch 'master' into master-android 2024-07-29 22:14:48 +01:00
Evgeny Poberezkin 30df42d20d Merge branch master into master-android 2024-07-28 22:08:05 +01:00
Evgeny Poberezkin 01345cd423 Merge branch 'master' into master-android 2024-07-15 23:06:44 +01:00
Evgeny Poberezkin 38a2f3b458 Merge branch 'master' into master-android 2024-07-09 19:42:02 +01:00
Evgeny Poberezkin f739c7f226 Merge branch 'master' into master-android 2024-07-09 19:08:24 +01:00
Evgeny Poberezkin 28fb67fe92 Merge branch 'master' into master-android 2024-07-03 09:08:20 +01:00
Evgeny Poberezkin d2b643de01 Merge branch 'master' into master-android 2024-07-02 15:08:07 +01:00
Evgeny Poberezkin bae9685950 Merge branch 'master' into master-android 2024-06-20 20:34:11 +01:00
Evgeny Poberezkin ccd34f5ab1 Merge branch 'master' into master-android 2024-06-18 23:40:07 +01:00
Evgeny Poberezkin dd8081a9d4 Merge branch 'master' into master-android 2024-06-03 14:06:41 +01:00
Evgeny Poberezkin d65909cb53 Merge branch 'master' into master-android 2024-06-01 00:23:02 +01:00
Evgeny Poberezkin e99b543c2f Merge branch 'master' into master-android 2024-05-29 08:34:51 +01:00
Evgeny Poberezkin 312d315d73 Merge branch 'master' into master-android 2024-05-24 21:34:06 +01:00
Evgeny Poberezkin cdf5520084 Merge branch 'master' into master-android 2024-05-20 19:04:29 +01:00
Evgeny Poberezkin 170e8ddeaf Merge branch 'master' into master-android 2024-05-17 15:41:24 +01:00
Evgeny Poberezkin 68f7956512 Merge branch 'master' into master-android 2024-05-13 21:13:29 +01:00
Evgeny Poberezkin e424b3eb66 Merge branch 'master' into master-android 2024-05-12 11:37:39 +01:00
Evgeny Poberezkin fb7a414e3c Merge branch 'master' into master-android 2024-05-11 23:56:47 +01:00
Evgeny Poberezkin 5966d6d648 Merge branch 'master' into master-android 2024-05-10 22:17:57 +01:00
Evgeny Poberezkin c6ebc9a4d9 Merge branch 'master' into master-android 2024-05-10 15:25:58 +01:00
Evgeny Poberezkin 05ec8b0676 Merge branch 'master' into master-android 2024-05-05 21:02:59 +01:00
Evgeny Poberezkin 6980f643ff Merge branch 'master' into master-android 2024-05-05 17:43:26 +01:00
Evgeny Poberezkin b60dd3e3e8 Merge branch 'master' into master-android 2024-05-02 16:48:46 +01:00
Evgeny Poberezkin 17a5770442 Merge branch 'master' into master-android 2024-04-27 09:24:01 +01:00
Evgeny Poberezkin 439bbc15a5 Merge branch 'master' into master-android 2024-04-26 15:42:30 +01:00
Evgeny Poberezkin 3688141972 Merge branch 'master' into master-android 2024-04-25 13:40:50 +01:00
Evgeny Poberezkin d79f8f8345 Merge branch 'master' into master-android 2024-04-25 11:15:41 +01:00
Evgeny Poberezkin 923ad7470c Merge branch 'master' into master-android 2024-04-22 22:24:30 +01:00
Evgeny Poberezkin 833abb252e Merge branch 'master' into master-android 2024-04-22 17:53:11 +01:00
Evgeny Poberezkin c60207f211 Merge branch 'master' into master-android 2024-04-22 08:08:03 +01:00
Evgeny Poberezkin d5665a421f Merge branch 'master' into master-android 2024-04-20 19:39:40 +01:00
Evgeny Poberezkin b2aa0ffc3c Merge branch 'master' into master-android 2024-04-19 21:18:33 +01:00
Evgeny Poberezkin e50a5cfaa1 Merge branch 'master' into master-android 2024-04-03 11:44:21 +01:00
Evgeny Poberezkin 73ec44a9be Merge branch 'master' into master-android 2024-04-01 14:37:08 +01:00
Evgeny Poberezkin 59fe6ab8ce Merge branch 'master' into master-android 2024-03-21 19:00:42 +00:00
Evgeny Poberezkin ec64be7c89 Merge branch 'master' into master-android 2024-03-19 16:08:24 +00:00
Evgeny Poberezkin 41984e56c0 Merge branch 'master' into master-android 2024-03-12 18:29:34 +00:00
Evgeny Poberezkin c435510b2e Merge branch 'master' into master-android 2024-03-12 15:21:02 +00:00
Evgeny Poberezkin de65d0e0a2 Merge branch 'master' into master-android 2024-03-11 09:27:17 +00:00
Evgeny Poberezkin 9cde37c6f5 Merge branch 'master' into master-android 2024-02-11 11:41:36 +00:00
Evgeny Poberezkin 9514acf6aa Merge branch 'master' into master-android 2024-02-03 18:39:08 +00:00
Evgeny Poberezkin 40d54f3f16 Merge branch 'master' into master-android 2024-02-02 08:32:46 +00:00
Evgeny Poberezkin 3249c5e463 Merge branch 'master' into master-android 2024-01-30 23:18:11 +00:00
Evgeny Poberezkin e440fba582 Merge branch 'master' into master-android 2024-01-27 20:06:31 +00:00
Evgeny Poberezkin 43cedbce26 Merge branch 'master' into master-android 2024-01-23 21:13:20 +00:00
spaced4ndy ebad009553 Merge branch 'master-ghc8107' into master-android 2024-01-22 18:44:55 +04:00
spaced4ndy 35cf113d98 Merge branch 'master' into master-ghc8107 2024-01-22 18:44:13 +04:00
Evgeny Poberezkin e82d2cbea2 Merge branch 'master-ghc8107' into master-android 2024-01-20 21:36:45 +00:00
Evgeny Poberezkin a0e056994e Merge branch 'master' into master-ghc8107 2024-01-20 21:36:23 +00:00
Evgeny Poberezkin e4d8ea17fc Merge branch 'master-ghc8107' into master-android 2024-01-20 15:07:37 +00:00
Evgeny Poberezkin e69db17804 Merge branch 'master' into master-ghc8107 2024-01-20 15:07:20 +00:00
Evgeny Poberezkin 9282cca396 Merge branch 'master-ghc8107' into master-android 2024-01-17 19:30:14 +00:00
Evgeny Poberezkin ad2eaeb004 Merge branch 'master' into master-ghc8107 2024-01-17 19:29:49 +00:00
Evgeny Poberezkin 3f44c9af24 Merge branch 'master-ghc8107' into master-android 2024-01-10 12:09:28 +00:00
Evgeny Poberezkin 4296515473 Merge branch 'master' into master-ghc8107 2024-01-10 12:06:48 +00:00
Evgeny Poberezkin 88830a2e09 Merge branch 'master-ghc8107' into master-android 2024-01-09 11:07:50 +00:00
Evgeny Poberezkin c8a269e391 Merge branch 'master' into master-ghc8107 2024-01-09 11:07:32 +00:00
Evgeny Poberezkin a45897007c Merge branch 'master-ghc8107' into master-android 2023-12-30 19:32:05 +00:00
Evgeny Poberezkin 42ea1df342 Merge branch 'master' into master-ghc8107 2023-12-30 19:19:39 +00:00
Evgeny Poberezkin 47214e33ea Merge branch 'master-ghc8107' into master-android 2023-12-27 21:02:49 +00:00
Evgeny Poberezkin ff239d81e5 Merge branch 'master' into master-ghc8107 2023-12-27 21:02:11 +00:00
Evgeny Poberezkin 0a6e47dd61 Merge branch 'master' into master-ghc8107 2023-12-27 20:57:25 +00:00
Evgeny Poberezkin ee75219ed3 core: 5.4.2.1 (simplexmq 5.5.0.2) 2023-12-27 20:57:05 +00:00
Evgeny Poberezkin 2e337a753f Merge branch 'master-ghc8107' into master-android 2023-12-27 14:09:42 +00:00
Evgeny Poberezkin 9918d91def Merge branch 'master' into master-ghc8107 2023-12-27 14:09:23 +00:00
Evgeny Poberezkin 82dd5751c1 Merge branch 'master-ghc8107' into master-android 2023-12-26 21:27:06 +00:00
Evgeny Poberezkin de637fab50 Merge branch 'master' into master-ghc8107 2023-12-26 21:12:36 +00:00
Evgeny Poberezkin cb49f6f01d Merge branch 'master-ghc8107' into master-android 2023-12-23 19:57:27 +00:00
Evgeny Poberezkin 6d0a83aa58 Merge branch 'master' into master-ghc8107 2023-12-23 19:57:08 +00:00
Evgeny Poberezkin 05f57e98ad Merge branch 'master-ghc8107' into master-android 2023-12-23 17:25:14 +00:00
Evgeny Poberezkin 6a66525927 Merge branch 'master' into master-ghc8107 2023-12-23 17:24:47 +00:00
Evgeny Poberezkin 9530a6055a Merge branch 'master-ghc8107' into master-android 2023-12-23 15:12:49 +00:00
Evgeny Poberezkin f5ed8debcc Merge branch 'master' into master-ghc8107 2023-12-23 15:11:38 +00:00
Evgeny Poberezkin 355d2449c5 fix for GHC 8.10.7 2023-12-23 15:05:36 +00:00
Evgeny Poberezkin f7382cdd6f Merge branch 'master' into master-ghc8107 2023-12-23 13:52:44 +00:00
Evgeny Poberezkin dc8d10d068 core: limit process package version to 1.6.17 2023-12-21 10:54:54 +00:00
Evgeny Poberezkin 951245d33f Merge branch 'master-ghc8107' into master-android 2023-12-21 10:00:28 +00:00
Evgeny Poberezkin 521b901cc9 Merge branch 'master' into master-ghc8107 (without changes for iPhone7) 2023-12-21 09:59:25 +00:00
Evgeny Poberezkin 6311ba451b Merge branch 'master-ghc8107' into master-android 2023-12-21 00:45:32 +00:00
Evgeny Poberezkin 754c76d6fd Merge branch 'master' into master-ghc8107 2023-12-21 00:45:10 +00:00
Evgeny Poberezkin aea7ff1c89 nix: fix script 2023-12-13 20:27:58 +00:00
Evgeny Poberezkin 77ac972e09 Merge branch 'master-ghc8107' into master-android 2023-12-13 12:36:06 +00:00
Evgeny Poberezkin 256f85024f Merge branch 'master' into master-ghc8107 2023-12-13 12:35:37 +00:00
Evgeny Poberezkin 870f9e42dd Merge branch 'master' into master-ghc8107 2023-12-12 20:09:39 +00:00
Evgeny Poberezkin 0a4a3a24e1 Merge branch 'master-ghc8107' into master-android 2023-12-11 17:36:51 +00:00
Evgeny Poberezkin 8a66390a78 fix for GHC 8.10.7 2023-12-11 16:14:56 +00:00
Evgeny Poberezkin 3d48eded3d Merge branch 'master-ghc8107' into master-android 2023-12-11 14:56:47 +00:00
Evgeny Poberezkin 6546426ec0 Merge branch 'master' into master-ghc8107 2023-12-11 14:56:25 +00:00
spaced4ndy 4a7ceb00fb Merge branch 'master-ghc8107' into master-android 2023-12-11 17:53:08 +04:00
spaced4ndy 53560378bb Merge branch 'master' into master-ghc8107 2023-12-11 17:52:48 +04:00
Evgeny Poberezkin cdb3b6aafd Merge branch 'master-ghc8107' into master-android 2023-12-07 15:12:49 +00:00
Evgeny Poberezkin 9f3d3e8ba4 Merge branch 'master' into master-ghc8107 2023-12-07 15:00:23 +00:00
Evgeny Poberezkin 047aad592e Merge branch 'master-ghc8107' into master-android 2023-12-04 12:32:05 +00:00
Evgeny Poberezkin 087acd9180 changes to support GHC 8.10.7 (#3512)
* Revert "raise lower bound on mtl to a real version (#3499)"

This reverts commit f94c0311c1.

* Revert "core: expand ranges to fit ghc 8.10 & 9.6 (#3496)"

This reverts commit 9a1c7f41f7.

* update simplexmq

* remove netword-transport fork

* compatibility with GHC 8.10.7

* simplexmq

* fix test

* simplexmq, deps

* update sqlcipher deps in sha256nix

* fix index-state in cabal.project

* index-state

* remove import

* add cabal.project.freeze

* simplexmq

* remove freeze

* simplexmq

* bytestring,simplexmq

* template-haskell, simplexmq

* simplexmq

* simplexmq

* simplexmq

* mtl

* simplexmq

* remove duplicate index-state
2023-12-04 12:29:49 +00:00
Evgeny Poberezkin 0b822e4a5c Merge branch 'master' into master-ghc8107 2023-12-04 10:07:16 +00:00
Evgeny Poberezkin f8a469488e Merge branch 'master' into master-ghc8107 2023-12-02 12:30:36 +00:00
Evgeny Poberezkin 3b5e806418 Merge branch 'master' into master-ghc8107 2023-12-01 17:46:12 +00:00
spaced4ndy 79e208193a Merge branch 'master-ghc8107' into master-android 2023-11-30 21:11:25 +04:00
spaced4ndy ef5c13b1c1 Merge branch 'master' into master-ghc8107 2023-11-30 21:10:51 +04:00
spaced4ndy 38533213d2 Merge branch 'master' into master-ghc8107 2023-11-30 20:56:51 +04:00
spaced4ndy 5f1aa6fa9d Merge branch 'master-ghc8107' into master-android 2023-11-27 19:18:22 +04:00
spaced4ndy b0002fe07d Merge branch 'master' into master-ghc8107 2023-11-27 19:17:39 +04:00
Evgeny Poberezkin ef21fd1d26 Merge branch 'master-ghc8107' into master-android 2023-11-26 19:59:46 +00:00
Evgeny Poberezkin 4a311b9578 fix for ghc 8.10.7 2023-11-26 19:34:39 +00:00
Evgeny Poberezkin b8da5e225b Merge branch 'master' into master-ghc8107 2023-11-26 18:53:40 +00:00
Evgeny Poberezkin f27de052cf Merge branch 'master' into master-android 2023-11-26 11:51:16 +00:00
Evgeny Poberezkin 5cc537f14c Merge branch 'master' into master-ghc8107 2023-11-26 11:50:50 +00:00
Evgeny Poberezkin dc8ca4cf89 Merge tag 'v5.4.0' into master-android 2023-11-25 11:23:23 +00:00
Evgeny Poberezkin b62dd801f1 Merge branch 'master-ghc8107' into master-android 2023-11-24 20:02:05 +00:00
Evgeny Poberezkin 0c096e2c89 Merge branch 'master' into master-ghc8107 2023-11-24 19:00:30 +00:00
Evgeny Poberezkin cc127e56fe Merge branch 'master' into master-android 2023-11-23 16:23:02 +00:00
Evgeny Poberezkin 1781495ee3 Merge branch 'master' into master-ghc8107 2023-11-23 16:22:46 +00:00
Evgeny Poberezkin 831231d8e6 Merge branch 'master-ghc8107' into master-android 2023-11-21 21:16:04 +00:00
Evgeny Poberezkin 45102442f4 Merge branch 'master' into master-ghc8107 2023-11-21 21:15:10 +00:00
spaced4ndy f323c8e112 Merge branch 'master-ghc8107' into master-android 2023-11-21 19:42:28 +04:00
spaced4ndy 3bdc6b5e28 Merge branch 'master' into master-ghc8107 2023-11-21 19:41:06 +04:00
Evgeny Poberezkin d8373262bc Merge branch 'master-ghc8107' into master-android 2023-11-21 00:01:20 +00:00
Evgeny Poberezkin 3597d34716 Merge branch 'master' into master-ghc8107 2023-11-21 00:00:59 +00:00
Evgeny Poberezkin bd4259e89e update hpack 2023-11-20 14:43:20 +00:00
Evgeny Poberezkin 55ead740cc update hpack 2023-11-20 14:43:05 +00:00
Evgeny Poberezkin 5ef0eda2d7 Merge branch 'master-ghc8107' into master-android 2023-11-20 14:30:27 +00:00
Evgeny Poberezkin 49a9b0e7d6 update hpack version 2023-11-20 14:30:10 +00:00
Evgeny Poberezkin 45ada450a2 Merge branch 'master-ghc8107' into master-android 2023-11-20 13:24:07 +00:00
Evgeny Poberezkin 307a1b3c5e fix for ghc 8.10.7 2023-11-20 13:23:45 +00:00
Evgeny Poberezkin ed6b3bbead Merge branch 'master' into master-ghc8107 2023-11-20 13:01:22 +00:00
spaced4ndy 901610eec5 Merge branch 'master-ghc8107' into master-android 2023-11-20 14:08:10 +04:00
spaced4ndy 7d4127c51d Merge branch 'master' into master-ghc8107 2023-11-20 14:07:08 +04:00
Evgeny Poberezkin 13215d91d7 Merge branch 'master-ghc8107' into master-android 2023-11-20 00:07:12 +00:00
Evgeny Poberezkin e1a8099474 fix for GHC 8.10.7 2023-11-20 00:06:45 +00:00
Evgeny Poberezkin daa8d9bb21 Merge branch 'master' into master-ghc8107 2023-11-19 23:42:13 +00:00
Evgeny Poberezkin 5fcbade1bc Merge branch 'master-ghc8107' into master-android 2023-11-17 11:49:35 +00:00
Evgeny Poberezkin 3937ffa9a6 Merge branch 'master' into master-ghc8107 2023-11-17 11:47:52 +00:00
Evgeny Poberezkin 80ddb50e1c Merge branch 'master-ghc8107' into master-android 2023-11-16 18:55:17 +00:00
Evgeny Poberezkin f6e66f1c53 Merge branch 'master' into master-ghc8107 2023-11-16 18:13:02 +00:00
Evgeny Poberezkin 0c23ff9ae3 Merge branch 'master-ghc8107' into master-android 2023-11-11 13:57:32 +00:00
Evgeny Poberezkin 1570bc2b99 Merge branch 'master' into master-ghc8107 2023-11-11 13:56:53 +00:00
Evgeny Poberezkin 1e2104cabf Merge branch 'master-ghc8107' into master-android 2023-11-11 09:52:07 +00:00
Evgeny Poberezkin f3014f258d Merge branch 'master' into master-ghc8107 2023-11-11 09:51:42 +00:00
Evgeny Poberezkin f0991cc0ba Merge branch 'master-ghc8107' into master-android 2023-11-10 21:22:19 +00:00
Evgeny Poberezkin 74b78a8d7b Merge branch 'master' into master-ghc8107 2023-11-10 21:11:08 +00:00
Evgeny Poberezkin 82cd70a75c Merge branch 'master-ghc8107' into master-android 2023-11-10 21:08:11 +00:00
Evgeny Poberezkin fe4eb7b5af Merge branch 'master' into master-ghc8107 without changes, to skip update for ghc 9.6.3 2023-11-10 21:04:20 +00:00
Evgeny Poberezkin c459e71d02 Merge branch 'master-ghc8107' into master-android 2023-11-06 11:26:40 +00:00
Evgeny Poberezkin 2516d5a393 Merge branch 'master' into master-ghc8107 2023-11-06 11:26:22 +00:00
spaced4ndy 477d98d75a Merge branch 'master-ghc8107' into master-android 2023-11-06 11:42:39 +04:00
spaced4ndy 4253cd7fb9 Merge branch 'master' into master-ghc8107 2023-11-06 11:41:55 +04:00
Evgeny Poberezkin ca78958667 Merge branch 'master-ghc8107' into master-android 2023-11-04 13:39:35 +00:00
Evgeny Poberezkin 1f5b80d560 fix for ghc8107 2023-11-04 13:37:25 +00:00
Evgeny Poberezkin 2de111e76c Merge branch 'master' into master-ghc8107 2023-11-04 13:02:08 +00:00
spaced4ndy 8343285d93 Merge branch 'master-ghc8107' into master-android 2023-10-30 21:01:16 +04:00
spaced4ndy 5dbe2b2745 Merge branch 'master' into master-ghc8107 2023-10-30 21:00:11 +04:00
Evgeny Poberezkin fb9485190d Merge branch 'master-ghc8107' into master-android 2023-10-29 18:27:00 +00:00
Evgeny Poberezkin 6881600e06 Merge branch 'master' into master-ghc8107 2023-10-29 18:24:13 +00:00
spaced4ndy 9ed723bafa Merge branch 'master-ghc8107' into master-android 2023-10-25 10:48:37 +04:00
spaced4ndy 9ded1c9821 Merge branch 'master' into master-ghc8107 2023-10-25 10:47:35 +04:00
spaced4ndy bb374c68b1 Merge branch 'master-ghc8107' into master-android 2023-10-24 18:14:22 +04:00
spaced4ndy c3e82a6a4e Merge branch 'master' into master-ghc8107 2023-10-24 18:13:56 +04:00
spaced4ndy 7c12e82042 Merge branch 'master-ghc8107' into master-android 2023-10-24 17:41:19 +04:00
spaced4ndy e7e66ff873 Merge branch 'master' into master-ghc8107 2023-10-24 17:40:55 +04:00
Evgeny Poberezkin c4d7e5307c Merge branch 'master-ghc8107' into master-android 2023-10-22 15:35:55 +01:00
Evgeny Poberezkin d6b9a45a39 Merge branch 'master' into master-ghc8107 2023-10-22 15:10:33 +01:00
Evgeny Poberezkin 7fd3b4d6ba Merge branch 'master-ghc8107' into master-android 2023-10-18 22:45:11 +01:00
Evgeny Poberezkin 4004aafbc5 Merge branch 'master' into master-ghc8107 2023-10-18 22:44:27 +01:00
spaced4ndy 95008eeeaf Merge branch 'master-ghc8107' into master-android 2023-10-16 20:05:49 +04:00
spaced4ndy c7a8992043 core: fix compilation for ghc 8.10.7 2023-10-16 20:05:13 +04:00
spaced4ndy ea2b5f2ccf Merge branch 'master-ghc8107' into master-android 2023-10-16 19:28:48 +04:00
spaced4ndy ed9f277421 Merge branch 'master' into master-ghc8107 2023-10-16 19:28:06 +04:00
Evgeny Poberezkin 5c14c3b349 Merge branch 'master-ghc8107' into master-android 2023-10-15 18:54:16 +01:00
Evgeny Poberezkin d8fb31f167 Merge branch 'master' into master-ghc8107 2023-10-15 18:53:23 +01:00
Evgeny Poberezkin 02db38ffd3 Merge branch 'master-ghc8107' into master-android 2023-10-11 21:58:16 +01:00
Evgeny Poberezkin 7692195bfa core: fix for ghc 8.10.7 2023-10-11 21:57:53 +01:00
Evgeny Poberezkin c435cbdc7b Merge branch 'master-ghc8107' into master-android 2023-10-11 21:28:43 +01:00
Evgeny Poberezkin effc281271 Merge branch 'master' into master-ghc8107 2023-10-11 21:27:21 +01:00
spaced4ndy 41eb2e5689 Merge branch 'master-ghc8107' into master-android 2023-10-11 13:22:30 +04:00
spaced4ndy 67d74a0a27 Merge branch 'master' into master-ghc8107 2023-10-11 13:21:46 +04:00
Evgeny Poberezkin f66405e79b Merge branch 'master-ghc8107' into master-android 2023-10-09 17:31:56 +01:00
Evgeny Poberezkin 74d186af16 Merge branch 'master' into master-ghc8107 2023-10-09 17:31:27 +01:00
Evgeny Poberezkin 187fef0c5a Merge branch 'master-ghc8107' into master-android 2023-10-09 14:05:21 +01:00
Evgeny Poberezkin 4782cab507 Merge branch 'master' into master-ghc8107 2023-10-09 14:05:04 +01:00
Evgeny Poberezkin bcbee67709 Merge branch 'master-ghc8107' into master-android 2023-10-08 08:38:40 +01:00
Evgeny Poberezkin 2501cbe55d Merge branch 'master' into master-ghc8107 2023-10-08 08:38:02 +01:00
Evgeny Poberezkin 2bd049db87 Merge branch 'master-ghc8107' into master-android 2023-10-07 21:10:22 +01:00
Evgeny Poberezkin 6b8b9ab4fd Merge branch 'master' into master-ghc8107 2023-10-07 19:06:38 +01:00
Evgeny Poberezkin 30db24265e Merge branch 'master-ghc8107' into master-android 2023-10-02 23:04:44 +01:00
Evgeny Poberezkin 316d605899 Merge branch 'master' into master-ghc8107 2023-10-02 23:04:13 +01:00
Evgeny Poberezkin b4257f7767 Merge branch 'master-ghc8107' into master-android 2023-10-01 13:21:50 +01:00
Evgeny Poberezkin a3f2d5c919 Merge branch 'master' into master-ghc8107 2023-10-01 13:20:06 +01:00
Evgeny Poberezkin cf46469cd5 Merge branch 'master-ghc8107' into master-android 2023-10-01 11:19:50 +01:00
Evgeny Poberezkin 0312fde818 Merge branch 'master' into master-ghc8107 2023-10-01 11:19:27 +01:00
Evgeny Poberezkin 9defa44f0c Merge branch 'master-ghc8107' into master-android 2023-09-29 13:15:23 +01:00
Evgeny Poberezkin 915b53054c Merge branch 'master' into master-ghc8107 2023-09-29 13:14:57 +01:00
Evgeny Poberezkin f81557b4fd Merge branch 'master-ghc8107' into master-android 2023-09-27 22:10:06 +01:00
Evgeny Poberezkin e273bd1239 Merge branch 'master' into master-ghc8107 2023-09-27 22:04:00 +01:00
spaced4ndy a63caf4640 Merge branch 'master-ghc8107' into master-android 2023-09-27 20:38:36 +04:00
spaced4ndy e7f0234134 Merge branch 'master' into master-ghc8107 2023-09-27 20:11:39 +04:00
Evgeny Poberezkin 340552321e Merge branch 'master-ghc8107' into master-android 2023-09-27 16:07:24 +01:00
Evgeny Poberezkin 98a3fc214d Merge branch 'master' into master-ghc8107 2023-09-27 16:04:25 +01:00
Evgeny Poberezkin 6a578cfe3c Merge branch 'master-ghc8107' into master-android 2023-09-25 16:53:04 +01:00
Evgeny Poberezkin dacc075fe8 Merge branch 'master' into master-ghc8107 2023-09-25 16:52:33 +01:00
spaced4ndy 55418e2bc0 Merge branch 'master-ghc8107' into master-android 2023-09-25 17:43:59 +04:00
spaced4ndy f2b5c0f3a8 Merge branch 'master' into master-ghc8107 2023-09-25 17:43:37 +04:00
spaced4ndy 5ebdf5dba9 Merge branch 'master-ghc8107' into master-android 2023-09-25 17:07:14 +04:00
spaced4ndy 8e045764df Merge branch 'master' into master-ghc8107 2023-09-25 16:40:08 +04:00
Evgeny Poberezkin 503d3d77e6 Merge branch 'master' into master-ghc8107 2023-09-23 08:47:28 +01:00
Evgeny Poberezkin 81bd7d97c5 Merge branch 'master' into master-ghc8107 2023-09-22 17:21:54 +01:00
Evgeny Poberezkin 8f57925067 compatibility with GHC 8.10.7 2023-09-22 14:01:25 +01:00
Evgeny Poberezkin 9bf99db82e Merge branch 'master' into master-ghc8107 2023-09-22 13:46:50 +01:00
Evgeny Poberezkin 5615cdbf1a Merge branch 'master' into master-android 2023-09-21 17:04:47 +01:00
Evgeny Poberezkin d802ae0058 Merge branch 'master' into master-android 2023-09-21 12:06:10 +01:00
Evgeny Poberezkin 8f2278198c Merge branch 'master' into master-android 2023-09-20 14:55:25 +01:00
spaced4ndy 10937a5a4e Merge branch 'master' into master-android 2023-09-20 17:36:53 +04:00
Evgeny Poberezkin 6aff6e9804 Merge branch 'master-ghc9' into master-ghc8107 2023-09-18 21:56:35 +01:00
Evgeny Poberezkin 95477cae7e core: use commit from simplexmq branch master-ghc8107 2023-09-18 21:45:50 +01:00
49 changed files with 990 additions and 2575 deletions
+23 -21
View File
@@ -149,9 +149,9 @@
6419EC582AB97507004A607A /* CIMemberCreatedContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6419EC572AB97507004A607A /* CIMemberCreatedContactView.swift */; }; 6419EC582AB97507004A607A /* CIMemberCreatedContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6419EC572AB97507004A607A /* CIMemberCreatedContactView.swift */; };
6432857C2925443C00FBE5C8 /* GroupPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6432857B2925443C00FBE5C8 /* GroupPreferencesView.swift */; }; 6432857C2925443C00FBE5C8 /* GroupPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6432857B2925443C00FBE5C8 /* GroupPreferencesView.swift */; };
643B3B452CCBEB080083A2CF /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 643B3B402CCBEB080083A2CF /* libgmpxx.a */; }; 643B3B452CCBEB080083A2CF /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 643B3B402CCBEB080083A2CF /* libgmpxx.a */; };
643B3B462CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-CTfGB7l09cqEHVIdvhrnH5-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 643B3B412CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-CTfGB7l09cqEHVIdvhrnH5-ghc9.6.3.a */; }; 643B3B462CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 643B3B412CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9-ghc9.6.3.a */; };
643B3B472CCBEB080083A2CF /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 643B3B422CCBEB080083A2CF /* libffi.a */; }; 643B3B472CCBEB080083A2CF /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 643B3B422CCBEB080083A2CF /* libffi.a */; };
643B3B482CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-CTfGB7l09cqEHVIdvhrnH5.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 643B3B432CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-CTfGB7l09cqEHVIdvhrnH5.a */; }; 643B3B482CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 643B3B432CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9.a */; };
643B3B492CCBEB080083A2CF /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 643B3B442CCBEB080083A2CF /* libgmp.a */; }; 643B3B492CCBEB080083A2CF /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 643B3B442CCBEB080083A2CF /* libgmp.a */; };
6440CA00288857A10062C672 /* CIEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440C9FF288857A10062C672 /* CIEventView.swift */; }; 6440CA00288857A10062C672 /* CIEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440C9FF288857A10062C672 /* CIEventView.swift */; };
6440CA03288AECA70062C672 /* AddGroupMembersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440CA02288AECA70062C672 /* AddGroupMembersView.swift */; }; 6440CA03288AECA70062C672 /* AddGroupMembersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440CA02288AECA70062C672 /* AddGroupMembersView.swift */; };
@@ -160,6 +160,11 @@
64466DC829FC2B3B00E3D48D /* CreateSimpleXAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */; }; 64466DC829FC2B3B00E3D48D /* CreateSimpleXAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */; };
64466DCC29FFE3E800E3D48D /* MailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64466DCB29FFE3E800E3D48D /* MailView.swift */; }; 64466DCC29FFE3E800E3D48D /* MailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64466DCB29FFE3E800E3D48D /* MailView.swift */; };
6448BBB628FA9D56000D2AB9 /* GroupLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */; }; 6448BBB628FA9D56000D2AB9 /* GroupLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */; };
6449333A2AF8E51000AC506E /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933352AF8E51000AC506E /* libgmpxx.a */; };
6449333B2AF8E51000AC506E /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933362AF8E51000AC506E /* libgmp.a */; };
6449333C2AF8E51000AC506E /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933372AF8E51000AC506E /* libffi.a */; };
6449333D2AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933382AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8-ghc9.6.3.a */; };
6449333E2AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933392AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8.a */; };
644EFFDE292BCD9D00525D5B /* ComposeVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFDD292BCD9D00525D5B /* ComposeVoiceView.swift */; }; 644EFFDE292BCD9D00525D5B /* ComposeVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFDD292BCD9D00525D5B /* ComposeVoiceView.swift */; };
644EFFE0292CFD7F00525D5B /* CIVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFDF292CFD7F00525D5B /* CIVoiceView.swift */; }; 644EFFE0292CFD7F00525D5B /* CIVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFDF292CFD7F00525D5B /* CIVoiceView.swift */; };
644EFFE2292D089800525D5B /* FramedCIVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFE1292D089800525D5B /* FramedCIVoiceView.swift */; }; 644EFFE2292D089800525D5B /* FramedCIVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFE1292D089800525D5B /* FramedCIVoiceView.swift */; };
@@ -492,9 +497,9 @@
6419EC572AB97507004A607A /* CIMemberCreatedContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIMemberCreatedContactView.swift; sourceTree = "<group>"; }; 6419EC572AB97507004A607A /* CIMemberCreatedContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIMemberCreatedContactView.swift; sourceTree = "<group>"; };
6432857B2925443C00FBE5C8 /* GroupPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupPreferencesView.swift; sourceTree = "<group>"; }; 6432857B2925443C00FBE5C8 /* GroupPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupPreferencesView.swift; sourceTree = "<group>"; };
643B3B402CCBEB080083A2CF /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libgmpxx.a; path = Libraries/libgmpxx.a; sourceTree = "<group>"; }; 643B3B402CCBEB080083A2CF /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libgmpxx.a; path = Libraries/libgmpxx.a; sourceTree = "<group>"; };
643B3B412CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-CTfGB7l09cqEHVIdvhrnH5-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libHSsimplex-chat-6.1.1.0-CTfGB7l09cqEHVIdvhrnH5-ghc9.6.3.a"; path = "Libraries/libHSsimplex-chat-6.1.1.0-CTfGB7l09cqEHVIdvhrnH5-ghc9.6.3.a"; sourceTree = "<group>"; }; 643B3B412CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9-ghc9.6.3.a"; path = "Libraries/libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9-ghc9.6.3.a"; sourceTree = "<group>"; };
643B3B422CCBEB080083A2CF /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libffi.a; path = Libraries/libffi.a; sourceTree = "<group>"; }; 643B3B422CCBEB080083A2CF /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libffi.a; path = Libraries/libffi.a; sourceTree = "<group>"; };
643B3B432CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-CTfGB7l09cqEHVIdvhrnH5.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libHSsimplex-chat-6.1.1.0-CTfGB7l09cqEHVIdvhrnH5.a"; path = "Libraries/libHSsimplex-chat-6.1.1.0-CTfGB7l09cqEHVIdvhrnH5.a"; sourceTree = "<group>"; }; 643B3B432CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9.a"; path = "Libraries/libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9.a"; sourceTree = "<group>"; };
643B3B442CCBEB080083A2CF /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libgmp.a; path = Libraries/libgmp.a; sourceTree = "<group>"; }; 643B3B442CCBEB080083A2CF /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libgmp.a; path = Libraries/libgmp.a; sourceTree = "<group>"; };
6440C9FF288857A10062C672 /* CIEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIEventView.swift; sourceTree = "<group>"; }; 6440C9FF288857A10062C672 /* CIEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIEventView.swift; sourceTree = "<group>"; };
6440CA02288AECA70062C672 /* AddGroupMembersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGroupMembersView.swift; sourceTree = "<group>"; }; 6440CA02288AECA70062C672 /* AddGroupMembersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGroupMembersView.swift; sourceTree = "<group>"; };
@@ -503,6 +508,11 @@
64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateSimpleXAddress.swift; sourceTree = "<group>"; }; 64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateSimpleXAddress.swift; sourceTree = "<group>"; };
64466DCB29FFE3E800E3D48D /* MailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailView.swift; sourceTree = "<group>"; }; 64466DCB29FFE3E800E3D48D /* MailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailView.swift; sourceTree = "<group>"; };
6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupLinkView.swift; sourceTree = "<group>"; }; 6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupLinkView.swift; sourceTree = "<group>"; };
644933352AF8E51000AC506E /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
644933362AF8E51000AC506E /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
644933372AF8E51000AC506E /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
644933382AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8-ghc9.6.3.a"; sourceTree = "<group>"; };
644933392AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8.a"; sourceTree = "<group>"; };
644EFFDD292BCD9D00525D5B /* ComposeVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeVoiceView.swift; sourceTree = "<group>"; }; 644EFFDD292BCD9D00525D5B /* ComposeVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeVoiceView.swift; sourceTree = "<group>"; };
644EFFDF292CFD7F00525D5B /* CIVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIVoiceView.swift; sourceTree = "<group>"; }; 644EFFDF292CFD7F00525D5B /* CIVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIVoiceView.swift; sourceTree = "<group>"; };
644EFFE1292D089800525D5B /* FramedCIVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FramedCIVoiceView.swift; sourceTree = "<group>"; }; 644EFFE1292D089800525D5B /* FramedCIVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FramedCIVoiceView.swift; sourceTree = "<group>"; };
@@ -663,8 +673,8 @@
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */, 5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */, 5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */, CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */,
643B3B482CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-CTfGB7l09cqEHVIdvhrnH5.a in Frameworks */, 643B3B482CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9.a in Frameworks */,
643B3B462CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-CTfGB7l09cqEHVIdvhrnH5-ghc9.6.3.a in Frameworks */, 643B3B462CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9-ghc9.6.3.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -815,8 +825,8 @@
643B3B422CCBEB080083A2CF /* libffi.a */, 643B3B422CCBEB080083A2CF /* libffi.a */,
643B3B442CCBEB080083A2CF /* libgmp.a */, 643B3B442CCBEB080083A2CF /* libgmp.a */,
643B3B402CCBEB080083A2CF /* libgmpxx.a */, 643B3B402CCBEB080083A2CF /* libgmpxx.a */,
643B3B412CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-CTfGB7l09cqEHVIdvhrnH5-ghc9.6.3.a */, 643B3B412CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9-ghc9.6.3.a */,
643B3B432CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-CTfGB7l09cqEHVIdvhrnH5.a */, 643B3B432CCBEB080083A2CF /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9.a */,
5CA059C2279559F40002BEB4 /* Shared */, 5CA059C2279559F40002BEB4 /* Shared */,
5CDCAD462818589900503DA2 /* SimpleX NSE */, 5CDCAD462818589900503DA2 /* SimpleX NSE */,
CEE723A82C3BD3D70009AE93 /* SimpleX SE */, CEE723A82C3BD3D70009AE93 /* SimpleX SE */,
@@ -912,9 +922,10 @@
5CB924DF27A8678B00ACCCDD /* UserSettings */ = { 5CB924DF27A8678B00ACCCDD /* UserSettings */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
643B3B4C2CCFD34B0083A2CF /* NetworkAndServers */,
5CB924D627A8563F00ACCCDD /* SettingsView.swift */, 5CB924D627A8563F00ACCCDD /* SettingsView.swift */,
5CB346E62868D76D001FD2EF /* NotificationsView.swift */, 5CB346E62868D76D001FD2EF /* NotificationsView.swift */,
5C9C2DA6289957AE00CC63B1 /* AdvancedNetworkSettings.swift */,
5C9C2DA82899DA6F00CC63B1 /* NetworkAndServers.swift */,
5CADE79929211BB900072E13 /* PreferencesView.swift */, 5CADE79929211BB900072E13 /* PreferencesView.swift */,
5C5DB70D289ABDD200730FFF /* AppearanceSettings.swift */, 5C5DB70D289ABDD200730FFF /* AppearanceSettings.swift */,
5C05DF522840AA1D00C683F9 /* CallSettings.swift */, 5C05DF522840AA1D00C683F9 /* CallSettings.swift */,
@@ -922,6 +933,9 @@
5CC036DF29C488D500C0EF20 /* HiddenProfileView.swift */, 5CC036DF29C488D500C0EF20 /* HiddenProfileView.swift */,
5C577F7C27C83AA10006112D /* MarkdownHelp.swift */, 5C577F7C27C83AA10006112D /* MarkdownHelp.swift */,
5C3F1D57284363C400EC8A82 /* PrivacySettings.swift */, 5C3F1D57284363C400EC8A82 /* PrivacySettings.swift */,
5C93292E29239A170090FFF9 /* ProtocolServersView.swift */,
5C93293029239BED0090FFF9 /* ProtocolServerView.swift */,
5C9329402929248A0090FFF9 /* ScanProtocolServer.swift */,
5CB2084E28DA4B4800D024EC /* RTCServers.swift */, 5CB2084E28DA4B4800D024EC /* RTCServers.swift */,
64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */, 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */,
18415845648CA4F5A8BCA272 /* UserProfilesView.swift */, 18415845648CA4F5A8BCA272 /* UserProfilesView.swift */,
@@ -1052,18 +1066,6 @@
path = Database; path = Database;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
643B3B4C2CCFD34B0083A2CF /* NetworkAndServers */ = {
isa = PBXGroup;
children = (
5C9329402929248A0090FFF9 /* ScanProtocolServer.swift */,
5C93293029239BED0090FFF9 /* ProtocolServerView.swift */,
5C93292E29239A170090FFF9 /* ProtocolServersView.swift */,
5C9C2DA6289957AE00CC63B1 /* AdvancedNetworkSettings.swift */,
5C9C2DA82899DA6F00CC63B1 /* NetworkAndServers.swift */,
);
path = NetworkAndServers;
sourceTree = "<group>";
};
6440CA01288AEC770062C672 /* Group */ = { 6440CA01288AEC770062C672 /* Group */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -71,7 +71,7 @@ if(NOT APPLE)
else() else()
# Without direct linking it can't find hs_init in linking step # Without direct linking it can't find hs_init in linking step
add_library( rts SHARED IMPORTED ) add_library( rts SHARED IMPORTED )
FILE(GLOB RTSLIB ${CMAKE_SOURCE_DIR}/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/libHSrts*_thr-*.${OS_LIB_EXT}) FILE(GLOB RTSLIB ${CMAKE_SOURCE_DIR}/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/deps/libHSrts*_thr-*.${OS_LIB_EXT})
set_target_properties( rts PROPERTIES IMPORTED_LOCATION ${RTSLIB}) set_target_properties( rts PROPERTIES IMPORTED_LOCATION ${RTSLIB})
target_link_libraries(app-lib rts simplex) target_link_libraries(app-lib rts simplex)
+4 -6
View File
@@ -9,7 +9,6 @@ module Main where
import Control.Concurrent.Async import Control.Concurrent.Async
import Control.Concurrent.STM import Control.Concurrent.STM
import Control.Monad import Control.Monad
import Data.Text (Text)
import qualified Data.Text as T import qualified Data.Text as T
import Simplex.Chat.Bot import Simplex.Chat.Bot
import Simplex.Chat.Controller import Simplex.Chat.Controller
@@ -19,7 +18,6 @@ import Simplex.Chat.Messages.CIContent
import Simplex.Chat.Options import Simplex.Chat.Options
import Simplex.Chat.Terminal (terminalChatConfig) import Simplex.Chat.Terminal (terminalChatConfig)
import Simplex.Chat.Types import Simplex.Chat.Types
import Simplex.Messaging.Util (tshow)
import System.Directory (getAppUserDataDirectory) import System.Directory (getAppUserDataDirectory)
import Text.Read import Text.Read
@@ -36,7 +34,7 @@ welcomeGetOpts = do
putStrLn $ "db: " <> dbFilePrefix <> "_chat.db, " <> dbFilePrefix <> "_agent.db" putStrLn $ "db: " <> dbFilePrefix <> "_chat.db, " <> dbFilePrefix <> "_agent.db"
pure opts pure opts
welcomeMessage :: Text welcomeMessage :: String
welcomeMessage = "Hello! I am a simple squaring bot.\nIf you send me a number, I will calculate its square" welcomeMessage = "Hello! I am a simple squaring bot.\nIf you send me a number, I will calculate its square"
mySquaringBot :: User -> ChatController -> IO () mySquaringBot :: User -> ChatController -> IO ()
@@ -49,10 +47,10 @@ mySquaringBot _user cc = do
contactConnected contact contactConnected contact
sendMessage cc contact welcomeMessage sendMessage cc contact welcomeMessage
CRNewChatItems {chatItems = (AChatItem _ SMDRcv (DirectChat contact) ChatItem {content = mc@CIRcvMsgContent {}}) : _} -> do CRNewChatItems {chatItems = (AChatItem _ SMDRcv (DirectChat contact) ChatItem {content = mc@CIRcvMsgContent {}}) : _} -> do
let msg = ciContentToText mc let msg = T.unpack $ ciContentToText mc
number_ = readMaybe (T.unpack msg) :: Maybe Integer number_ = readMaybe msg :: Maybe Integer
sendMessage cc contact $ case number_ of sendMessage cc contact $ case number_ of
Just n -> msg <> " * " <> msg <> " = " <> tshow (n * n) Just n -> msg <> " * " <> msg <> " = " <> show (n * n)
_ -> "\"" <> msg <> "\" is not a number" _ -> "\"" <> msg <> "\" is not a number"
_ -> pure () _ -> pure ()
where where
@@ -21,7 +21,6 @@ import Simplex.Chat.Messages.CIContent
import Simplex.Chat.Options import Simplex.Chat.Options
import Simplex.Chat.Protocol (MsgContent (..)) import Simplex.Chat.Protocol (MsgContent (..))
import Simplex.Chat.Types import Simplex.Chat.Types
import Simplex.Messaging.Util (tshow)
import System.Directory (getAppUserDataDirectory) import System.Directory (getAppUserDataDirectory)
welcomeGetOpts :: IO BroadcastBotOpts welcomeGetOpts :: IO BroadcastBotOpts
@@ -49,14 +48,14 @@ broadcastBot BroadcastBotOpts {publishers, welcomeMessage, prohibitedMessage} _u
CRContactsList _ cts -> void . forkIO $ do CRContactsList _ cts -> void . forkIO $ do
let cts' = filter broadcastTo cts let cts' = filter broadcastTo cts
forM_ cts' $ \ct' -> sendComposedMessage cc ct' Nothing mc forM_ cts' $ \ct' -> sendComposedMessage cc ct' Nothing mc
sendReply $ "Forwarded to " <> tshow (length cts') <> " contact(s)" sendReply $ "Forwarded to " <> show (length cts') <> " contact(s)"
r -> putStrLn $ "Error getting contacts list: " <> show r r -> putStrLn $ "Error getting contacts list: " <> show r
else sendReply "!1 Message is not supported!" else sendReply "!1 Message is not supported!"
| otherwise -> do | otherwise -> do
sendReply prohibitedMessage sendReply prohibitedMessage
deleteMessage cc ct $ chatItemId' ci deleteMessage cc ct $ chatItemId' ci
where where
sendReply = sendComposedMessage cc ct (Just $ chatItemId' ci) . MCText sendReply = sendComposedMessage cc ct (Just $ chatItemId' ci) . textMsgContent
publisher = KnownContact {contactId = contactId' ct, localDisplayName = localDisplayName' ct} publisher = KnownContact {contactId = contactId' ct, localDisplayName = localDisplayName' ct}
allowContent = \case allowContent = \case
MCText _ -> True MCText _ -> True
@@ -7,7 +7,6 @@
module Broadcast.Options where module Broadcast.Options where
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Text (Text)
import Options.Applicative import Options.Applicative
import Simplex.Chat.Bot.KnownContacts import Simplex.Chat.Bot.KnownContacts
import Simplex.Chat.Controller (updateStr, versionNumber, versionString) import Simplex.Chat.Controller (updateStr, versionNumber, versionString)
@@ -16,14 +15,14 @@ import Simplex.Chat.Options (ChatCmdLog (..), ChatOpts (..), CoreChatOpts, coreC
data BroadcastBotOpts = BroadcastBotOpts data BroadcastBotOpts = BroadcastBotOpts
{ coreOptions :: CoreChatOpts, { coreOptions :: CoreChatOpts,
publishers :: [KnownContact], publishers :: [KnownContact],
welcomeMessage :: Text, welcomeMessage :: String,
prohibitedMessage :: Text prohibitedMessage :: String
} }
defaultWelcomeMessage :: [KnownContact] -> Text defaultWelcomeMessage :: [KnownContact] -> String
defaultWelcomeMessage ps = "Hello! I am a broadcast bot.\nI broadcast messages to all connected users from " <> knownContactNames ps <> "." defaultWelcomeMessage ps = "Hello! I am a broadcast bot.\nI broadcast messages to all connected users from " <> knownContactNames ps <> "."
defaultProhibitedMessage :: [KnownContact] -> Text defaultProhibitedMessage :: [KnownContact] -> String
defaultProhibitedMessage ps = "Sorry, only these users can broadcast messages: " <> knownContactNames ps <> ". Your message is deleted." defaultProhibitedMessage ps = "Sorry, only these users can broadcast messages: " <> knownContactNames ps <> ". Your message is deleted."
broadcastBotOpts :: FilePath -> FilePath -> Parser BroadcastBotOpts broadcastBotOpts :: FilePath -> FilePath -> Parser BroadcastBotOpts
@@ -89,11 +89,10 @@ crDirectoryEvent = \case
CRChatErrors {chatErrors} -> Just $ DELogChatResponse $ "chat errors: " <> T.intercalate ", " (map tshow chatErrors) CRChatErrors {chatErrors} -> Just $ DELogChatResponse $ "chat errors: " <> T.intercalate ", " (map tshow chatErrors)
_ -> Nothing _ -> Nothing
data DirectoryRole = DRUser | DRAdmin | DRSuperUser data DirectoryRole = DRUser | DRSuperUser
data SDirectoryRole (r :: DirectoryRole) where data SDirectoryRole (r :: DirectoryRole) where
SDRUser :: SDirectoryRole 'DRUser SDRUser :: SDirectoryRole 'DRUser
SDRAdmin :: SDirectoryRole 'DRAdmin
SDRSuperUser :: SDirectoryRole 'DRSuperUser SDRSuperUser :: SDirectoryRole 'DRSuperUser
deriving instance Show (SDirectoryRole r) deriving instance Show (SDirectoryRole r)
@@ -108,14 +107,12 @@ data DirectoryCmdTag (r :: DirectoryRole) where
DCListUserGroups_ :: DirectoryCmdTag 'DRUser DCListUserGroups_ :: DirectoryCmdTag 'DRUser
DCDeleteGroup_ :: DirectoryCmdTag 'DRUser DCDeleteGroup_ :: DirectoryCmdTag 'DRUser
DCSetRole_ :: DirectoryCmdTag 'DRUser DCSetRole_ :: DirectoryCmdTag 'DRUser
DCApproveGroup_ :: DirectoryCmdTag 'DRAdmin DCApproveGroup_ :: DirectoryCmdTag 'DRSuperUser
DCRejectGroup_ :: DirectoryCmdTag 'DRAdmin DCRejectGroup_ :: DirectoryCmdTag 'DRSuperUser
DCSuspendGroup_ :: DirectoryCmdTag 'DRAdmin DCSuspendGroup_ :: DirectoryCmdTag 'DRSuperUser
DCResumeGroup_ :: DirectoryCmdTag 'DRAdmin DCResumeGroup_ :: DirectoryCmdTag 'DRSuperUser
DCListLastGroups_ :: DirectoryCmdTag 'DRAdmin DCListLastGroups_ :: DirectoryCmdTag 'DRSuperUser
DCListPendingGroups_ :: DirectoryCmdTag 'DRAdmin DCListPendingGroups_ :: DirectoryCmdTag 'DRSuperUser
DCShowGroupLink_ :: DirectoryCmdTag 'DRAdmin
DCSendToGroupOwner_ :: DirectoryCmdTag 'DRAdmin
DCExecuteCommand_ :: DirectoryCmdTag 'DRSuperUser DCExecuteCommand_ :: DirectoryCmdTag 'DRSuperUser
deriving instance Show (DirectoryCmdTag r) deriving instance Show (DirectoryCmdTag r)
@@ -133,14 +130,12 @@ data DirectoryCmd (r :: DirectoryRole) where
DCListUserGroups :: DirectoryCmd 'DRUser DCListUserGroups :: DirectoryCmd 'DRUser
DCDeleteGroup :: UserGroupRegId -> GroupName -> DirectoryCmd 'DRUser DCDeleteGroup :: UserGroupRegId -> GroupName -> DirectoryCmd 'DRUser
DCSetRole :: GroupId -> GroupName -> GroupMemberRole -> DirectoryCmd 'DRUser DCSetRole :: GroupId -> GroupName -> GroupMemberRole -> DirectoryCmd 'DRUser
DCApproveGroup :: {groupId :: GroupId, displayName :: GroupName, groupApprovalId :: GroupApprovalId} -> DirectoryCmd 'DRAdmin DCApproveGroup :: {groupId :: GroupId, displayName :: GroupName, groupApprovalId :: GroupApprovalId} -> DirectoryCmd 'DRSuperUser
DCRejectGroup :: GroupId -> GroupName -> DirectoryCmd 'DRAdmin DCRejectGroup :: GroupId -> GroupName -> DirectoryCmd 'DRSuperUser
DCSuspendGroup :: GroupId -> GroupName -> DirectoryCmd 'DRAdmin DCSuspendGroup :: GroupId -> GroupName -> DirectoryCmd 'DRSuperUser
DCResumeGroup :: GroupId -> GroupName -> DirectoryCmd 'DRAdmin DCResumeGroup :: GroupId -> GroupName -> DirectoryCmd 'DRSuperUser
DCListLastGroups :: Int -> DirectoryCmd 'DRAdmin DCListLastGroups :: Int -> DirectoryCmd 'DRSuperUser
DCListPendingGroups :: Int -> DirectoryCmd 'DRAdmin DCListPendingGroups :: Int -> DirectoryCmd 'DRSuperUser
DCShowGroupLink :: GroupId -> GroupName -> DirectoryCmd 'DRAdmin
DCSendToGroupOwner :: GroupId -> GroupName -> Text -> DirectoryCmd 'DRAdmin
DCExecuteCommand :: String -> DirectoryCmd 'DRSuperUser DCExecuteCommand :: String -> DirectoryCmd 'DRSuperUser
DCUnknownCommand :: DirectoryCmd 'DRUser DCUnknownCommand :: DirectoryCmd 'DRUser
DCCommandError :: DirectoryCmdTag r -> DirectoryCmd r DCCommandError :: DirectoryCmdTag r -> DirectoryCmd r
@@ -173,20 +168,17 @@ directoryCmdP =
"ls" -> u DCListUserGroups_ "ls" -> u DCListUserGroups_
"delete" -> u DCDeleteGroup_ "delete" -> u DCDeleteGroup_
"role" -> u DCSetRole_ "role" -> u DCSetRole_
"approve" -> au DCApproveGroup_ "approve" -> su DCApproveGroup_
"reject" -> au DCRejectGroup_ "reject" -> su DCRejectGroup_
"suspend" -> au DCSuspendGroup_ "suspend" -> su DCSuspendGroup_
"resume" -> au DCResumeGroup_ "resume" -> su DCResumeGroup_
"last" -> au DCListLastGroups_ "last" -> su DCListLastGroups_
"pending" -> au DCListPendingGroups_ "pending" -> su DCListPendingGroups_
"link" -> au DCShowGroupLink_
"owner" -> au DCSendToGroupOwner_
"exec" -> su DCExecuteCommand_ "exec" -> su DCExecuteCommand_
"x" -> su DCExecuteCommand_ "x" -> su DCExecuteCommand_
_ -> fail "bad command tag" _ -> fail "bad command tag"
where where
u = pure . ADCT SDRUser u = pure . ADCT SDRUser
au = pure . ADCT SDRAdmin
su = pure . ADCT SDRSuperUser su = pure . ADCT SDRSuperUser
cmdP :: DirectoryCmdTag r -> Parser (DirectoryCmd r) cmdP :: DirectoryCmdTag r -> Parser (DirectoryCmd r)
cmdP = \case cmdP = \case
@@ -211,11 +203,6 @@ directoryCmdP =
DCResumeGroup_ -> gc DCResumeGroup DCResumeGroup_ -> gc DCResumeGroup
DCListLastGroups_ -> DCListLastGroups <$> (A.space *> A.decimal <|> pure 10) DCListLastGroups_ -> DCListLastGroups <$> (A.space *> A.decimal <|> pure 10)
DCListPendingGroups_ -> DCListPendingGroups <$> (A.space *> A.decimal <|> pure 10) DCListPendingGroups_ -> DCListPendingGroups <$> (A.space *> A.decimal <|> pure 10)
DCShowGroupLink_ -> gc DCShowGroupLink
DCSendToGroupOwner_ -> do
(groupId, displayName) <- gc (,)
msg <- A.space *> A.takeText
pure $ DCSendToGroupOwner groupId displayName msg
DCExecuteCommand_ -> DCExecuteCommand . T.unpack <$> (A.space *> A.takeText) DCExecuteCommand_ -> DCExecuteCommand . T.unpack <$> (A.space *> A.takeText)
where where
gc f = f <$> (A.space *> A.decimal <* A.char ':') <*> displayNameP gc f = f <$> (A.space *> A.decimal <* A.char ':') <*> displayNameP
@@ -226,8 +213,8 @@ directoryCmdP =
quoted c = A.char c *> takeNameTill (== c) <* A.char c quoted c = A.char c *> takeNameTill (== c) <* A.char c
refChar c = c > ' ' && c /= '#' && c /= '@' refChar c = c > ' ' && c /= '#' && c /= '@'
viewName :: Text -> Text viewName :: String -> String
viewName n = if any (== ' ') (T.unpack n) then "'" <> n <> "'" else n viewName n = if ' ' `elem` n then "'" <> n <> "'" else n
directoryCmdTag :: DirectoryCmd r -> Text directoryCmdTag :: DirectoryCmd r -> Text
directoryCmdTag = \case directoryCmdTag = \case
@@ -247,8 +234,6 @@ directoryCmdTag = \case
DCResumeGroup {} -> "resume" DCResumeGroup {} -> "resume"
DCListLastGroups _ -> "last" DCListLastGroups _ -> "last"
DCListPendingGroups _ -> "pending" DCListPendingGroups _ -> "pending"
DCShowGroupLink {} -> "link"
DCSendToGroupOwner {} -> "owner"
DCExecuteCommand _ -> "exec" DCExecuteCommand _ -> "exec"
DCUnknownCommand -> "unknown" DCUnknownCommand -> "unknown"
DCCommandError _ -> "error" DCCommandError _ -> "error"
@@ -11,7 +11,6 @@ module Directory.Options
) )
where where
import qualified Data.Text as T
import Options.Applicative import Options.Applicative
import Simplex.Chat.Bot.KnownContacts import Simplex.Chat.Bot.KnownContacts
import Simplex.Chat.Controller (updateStr, versionNumber, versionString) import Simplex.Chat.Controller (updateStr, versionNumber, versionString)
@@ -19,10 +18,9 @@ import Simplex.Chat.Options (ChatOpts (..), ChatCmdLog (..), CoreChatOpts, coreC
data DirectoryOpts = DirectoryOpts data DirectoryOpts = DirectoryOpts
{ coreOptions :: CoreChatOpts, { coreOptions :: CoreChatOpts,
adminUsers :: [KnownContact],
superUsers :: [KnownContact], superUsers :: [KnownContact],
directoryLog :: Maybe FilePath, directoryLog :: Maybe FilePath,
serviceName :: T.Text, serviceName :: String,
searchResults :: Int, searchResults :: Int,
testing :: Bool testing :: Bool
} }
@@ -30,13 +28,6 @@ data DirectoryOpts = DirectoryOpts
directoryOpts :: FilePath -> FilePath -> Parser DirectoryOpts directoryOpts :: FilePath -> FilePath -> Parser DirectoryOpts
directoryOpts appDir defaultDbFileName = do directoryOpts appDir defaultDbFileName = do
coreOptions <- coreChatOptsP appDir defaultDbFileName coreOptions <- coreChatOptsP appDir defaultDbFileName
adminUsers <-
option
parseKnownContacts
( long "admin-users"
<> metavar "ADMIN_USERS"
<> help "Comma-separated list of admin-users in the format CONTACT_ID:DISPLAY_NAME who will be allowed to manage the directory"
)
superUsers <- superUsers <-
option option
parseKnownContacts parseKnownContacts
@@ -61,10 +52,9 @@ directoryOpts appDir defaultDbFileName = do
pure pure
DirectoryOpts DirectoryOpts
{ coreOptions, { coreOptions,
adminUsers,
superUsers, superUsers,
directoryLog, directoryLog,
serviceName = T.pack serviceName, serviceName,
searchResults = 10, searchResults = 10,
testing = False testing = False
} }
@@ -17,11 +17,13 @@ import Control.Concurrent.Async
import Control.Concurrent.STM import Control.Concurrent.STM
import Control.Logger.Simple import Control.Logger.Simple
import Control.Monad import Control.Monad
import qualified Data.ByteString.Char8 as B
import Data.Maybe (fromMaybe, maybeToList) import Data.Maybe (fromMaybe, maybeToList)
import Data.Set (Set) import Data.Set (Set)
import qualified Data.Set as S import qualified Data.Set as S
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as T import qualified Data.Text as T
import Data.Text.Encoding (decodeLatin1)
import Data.Time.Clock (diffUTCTime, getCurrentTime) import Data.Time.Clock (diffUTCTime, getCurrentTime)
import Data.Time.LocalTime (getCurrentTimeZone) import Data.Time.LocalTime (getCurrentTimeZone)
import Directory.Events import Directory.Events
@@ -35,7 +37,6 @@ import Simplex.Chat.Core
import Simplex.Chat.Messages import Simplex.Chat.Messages
import Simplex.Chat.Options import Simplex.Chat.Options
import Simplex.Chat.Protocol (MsgContent (..)) import Simplex.Chat.Protocol (MsgContent (..))
import Simplex.Chat.Store.Shared (StoreError (..))
import Simplex.Chat.Types import Simplex.Chat.Types
import Simplex.Chat.Types.Shared import Simplex.Chat.Types.Shared
import Simplex.Chat.View (serializeChatResponse, simplexChatContact, viewContactName, viewGroupName) import Simplex.Chat.View (serializeChatResponse, simplexChatContact, viewContactName, viewGroupName)
@@ -78,7 +79,7 @@ welcomeGetOpts = do
pure opts pure opts
directoryService :: DirectoryStore -> DirectoryOpts -> User -> ChatController -> IO () directoryService :: DirectoryStore -> DirectoryOpts -> User -> ChatController -> IO ()
directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchResults, testing} user@User {userId} cc = do directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testing} user@User {userId} cc = do
initializeBotAddress' (not testing) cc initializeBotAddress' (not testing) cc
env <- newServiceState env <- newServiceState
race_ (forever $ void getLine) . forever $ do race_ (forever $ void getLine) . forever $ do
@@ -101,7 +102,6 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
logInfo $ "command received " <> directoryCmdTag cmd logInfo $ "command received " <> directoryCmdTag cmd
case sUser of case sUser of
SDRUser -> deUserCommand env ct ciId cmd SDRUser -> deUserCommand env ct ciId cmd
SDRAdmin -> deAdminCommand ct ciId cmd
SDRSuperUser -> deSuperUserCommand ct ciId cmd SDRSuperUser -> deSuperUserCommand ct ciId cmd
DELogChatResponse r -> logInfo r DELogChatResponse r -> logInfo r
where where
@@ -118,9 +118,9 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
userGroupReference gr GroupInfo {groupProfile = GroupProfile {displayName}} = userGroupReference' gr displayName userGroupReference gr GroupInfo {groupProfile = GroupProfile {displayName}} = userGroupReference' gr displayName
userGroupReference' GroupReg {userGroupRegId} displayName = groupReference' userGroupRegId displayName userGroupReference' GroupReg {userGroupRegId} displayName = groupReference' userGroupRegId displayName
groupReference GroupInfo {groupId, groupProfile = GroupProfile {displayName}} = groupReference' groupId displayName groupReference GroupInfo {groupId, groupProfile = GroupProfile {displayName}} = groupReference' groupId displayName
groupReference' groupId displayName = "ID " <> tshow groupId <> " (" <> displayName <> ")" groupReference' groupId displayName = "ID " <> show groupId <> " (" <> T.unpack displayName <> ")"
groupAlreadyListed GroupInfo {groupProfile = GroupProfile {displayName, fullName}} = groupAlreadyListed GroupInfo {groupProfile = GroupProfile {displayName, fullName}} =
"The group " <> displayName <> " (" <> fullName <> ") is already listed in the directory, please choose another name." T.unpack $ "The group " <> displayName <> " (" <> fullName <> ") is already listed in the directory, please choose another name."
getGroups :: Text -> IO (Maybe [(GroupInfo, GroupSummary)]) getGroups :: Text -> IO (Maybe [(GroupInfo, GroupSummary)])
getGroups = getGroups_ . Just getGroups = getGroups_ . Just
@@ -151,7 +151,7 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
processInvitation ct g@GroupInfo {groupId, groupProfile = GroupProfile {displayName}} = do processInvitation ct g@GroupInfo {groupId, groupProfile = GroupProfile {displayName}} = do
void $ addGroupReg st ct g GRSProposed void $ addGroupReg st ct g GRSProposed
r <- sendChatCmd cc $ APIJoinGroup groupId r <- sendChatCmd cc $ APIJoinGroup groupId
sendMessage cc ct $ case r of sendMessage cc ct $ T.unpack $ case r of
CRUserAcceptedGroupSent {} -> "Joining the group " <> displayName <> "" CRUserAcceptedGroupSent {} -> "Joining the group " <> displayName <> ""
_ -> "Error joining group " <> displayName <> ", please re-send the invitation!" _ -> "Error joining group " <> displayName <> ", please re-send the invitation!"
@@ -179,10 +179,10 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
where where
askConfirmation = do askConfirmation = do
ugrId <- addGroupReg st ct g GRSPendingConfirmation ugrId <- addGroupReg st ct g GRSPendingConfirmation
sendMessage cc ct $ "The group " <> displayName <> " (" <> fullName <> ") is already submitted to the directory.\nTo confirm the registration, please send:" sendMessage cc ct $ T.unpack $ "The group " <> displayName <> " (" <> fullName <> ") is already submitted to the directory.\nTo confirm the registration, please send:"
sendMessage cc ct $ "/confirm " <> tshow ugrId <> ":" <> viewName displayName sendMessage cc ct $ "/confirm " <> show ugrId <> ":" <> viewName (T.unpack displayName)
badRolesMsg :: GroupRolesStatus -> Maybe Text badRolesMsg :: GroupRolesStatus -> Maybe String
badRolesMsg = \case badRolesMsg = \case
GRSOk -> Nothing GRSOk -> Nothing
GRSServiceNotAdmin -> Just "You must grant directory service *admin* role to register the group" GRSServiceNotAdmin -> Just "You must grant directory service *admin* role to register the group"
@@ -218,7 +218,7 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
when (ctId `isOwner` gr) $ do when (ctId `isOwner` gr) $ do
setGroupRegOwner st gr owner setGroupRegOwner st gr owner
let GroupInfo {groupId, groupProfile = GroupProfile {displayName}} = g let GroupInfo {groupId, groupProfile = GroupProfile {displayName}} = g
notifyOwner gr $ "Joined the group " <> displayName <> ", creating the link…" notifyOwner gr $ T.unpack $ "Joined the group " <> displayName <> ", creating the link…"
sendChatCmd cc (APICreateGroupLink groupId GRMember) >>= \case sendChatCmd cc (APICreateGroupLink groupId GRMember) >>= \case
CRGroupLinkCreated {connReqContact} -> do CRGroupLinkCreated {connReqContact} -> do
setGroupStatus st gr GRSPendingUpdate setGroupStatus st gr GRSPendingUpdate
@@ -227,7 +227,7 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
"Created the public link to join the group via this directory service that is always online.\n\n\ "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\ \Please add it to the group welcome message.\n\
\For example, add:" \For example, add:"
notifyOwner gr $ "Link to join the group " <> displayName <> ": " <> strEncodeTxt (simplexChatContact connReqContact) notifyOwner gr $ "Link to join the group " <> T.unpack displayName <> ": " <> B.unpack (strEncode $ simplexChatContact connReqContact)
CRChatCmdError _ (ChatError e) -> case e of CRChatCmdError _ (ChatError e) -> case e of
CEGroupUserRole {} -> notifyOwner gr "Failed creating group link, as service is no longer an admin." CEGroupUserRole {} -> notifyOwner gr "Failed creating group link, as service is no longer an admin."
CEGroupMemberUserRemoved -> notifyOwner gr "Failed creating group link, as service is removed from the group." CEGroupMemberUserRemoved -> notifyOwner gr "Failed creating group link, as service is removed from the group."
@@ -256,7 +256,7 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
GPHasServiceLink -> when (ctId `isOwner` gr) $ groupLinkAdded gr GPHasServiceLink -> when (ctId `isOwner` gr) $ groupLinkAdded gr
GPServiceLinkError -> do GPServiceLinkError -> do
when (ctId `isOwner` gr) $ notifyOwner gr $ "Error: " <> serviceName <> " has no group link for " <> userGroupRef <> ". Please report the error to the developers." 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 " <> userGroupRef logError $ "Error: no group link for " <> T.pack userGroupRef
GRSPendingApproval n -> processProfileChange gr $ n + 1 GRSPendingApproval n -> processProfileChange gr $ n + 1
GRSActive -> processProfileChange gr 1 GRSActive -> processProfileChange gr 1
GRSSuspended -> processProfileChange gr 1 GRSSuspended -> processProfileChange gr 1
@@ -277,7 +277,7 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
_ -> do _ -> do
let gaId = 1 let gaId = 1
setGroupStatus st gr $ GRSPendingApproval gaId setGroupStatus st gr $ GRSPendingApproval gaId
notifyOwner gr $ "Thank you! The group link for " <> userGroupReference gr toGroup <> " is added to the welcome message.\nYou will be notified once the group is added to the directory - it may take up to 48 hours." notifyOwner gr $ "Thank you! The group link for " <> userGroupReference gr toGroup <> " is added to the welcome message.\nYou will be notified once the group is added to the directory - it may take up to 24 hours."
checkRolesSendToApprove gr gaId checkRolesSendToApprove gr gaId
processProfileChange gr n' = do processProfileChange gr n' = do
setGroupStatus st gr GRSPendingUpdate setGroupStatus st gr GRSPendingUpdate
@@ -299,13 +299,13 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
notifyOwner gr $ "The group " <> userGroupRef <> " is updated!\nIt is hidden from the directory until approved." notifyOwner gr $ "The group " <> userGroupRef <> " is updated!\nIt is hidden from the directory until approved."
notifySuperUsers $ "The group " <> groupRef <> " is updated." notifySuperUsers $ "The group " <> groupRef <> " is updated."
checkRolesSendToApprove gr n' checkRolesSendToApprove gr n'
GPServiceLinkError -> logError $ "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) groupProfileUpdate = profileUpdate <$> sendChatCmd cc (APIGetGroupLink groupId)
where where
profileUpdate = \case profileUpdate = \case
CRGroupLink {connReqContact} -> CRGroupLink {connReqContact} ->
let groupLink1 = strEncodeTxt connReqContact let groupLink1 = safeDecodeUtf8 $ strEncode connReqContact
groupLink2 = strEncodeTxt $ simplexChatContact connReqContact groupLink2 = safeDecodeUtf8 $ strEncode $ simplexChatContact connReqContact
hadLinkBefore = groupLink1 `isInfix` description p || groupLink2 `isInfix` description p hadLinkBefore = groupLink1 `isInfix` description p || groupLink2 `isInfix` description p
hasLinkNow = groupLink1 `isInfix` description p' || groupLink2 `isInfix` description p' hasLinkNow = groupLink1 `isInfix` description p' || groupLink2 `isInfix` description p'
in if in if
@@ -331,7 +331,7 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
msg = maybe (MCText text) (\image -> MCImage {text, image}) image' msg = maybe (MCText text) (\image -> MCImage {text, image}) image'
withSuperUsers $ \cId -> do withSuperUsers $ \cId -> do
sendComposedMessage' cc cId Nothing msg sendComposedMessage' cc cId Nothing msg
sendMessage' cc cId $ "/approve " <> tshow dbGroupId <> ":" <> viewName displayName <> " " <> tshow gaId sendMessage' cc cId $ "/approve " <> show dbGroupId <> ":" <> viewName (T.unpack displayName) <> " " <> show gaId
deContactRoleChanged :: GroupInfo -> ContactId -> GroupMemberRole -> IO () deContactRoleChanged :: GroupInfo -> ContactId -> GroupMemberRole -> IO ()
deContactRoleChanged g@GroupInfo {membership = GroupMember {memberRole = serviceRole}} ctId contactRole = do deContactRoleChanged g@GroupInfo {membership = GroupMember {memberRole = serviceRole}} ctId contactRole = do
@@ -356,7 +356,7 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
where where
rStatus = groupRolesStatus contactRole serviceRole rStatus = groupRolesStatus contactRole serviceRole
groupRef = groupReference g groupRef = groupReference g
ctRole = "*" <> strEncodeTxt contactRole <> "*" ctRole = "*" <> B.unpack (strEncode contactRole) <> "*"
suCtRole = "(user role is set to " <> ctRole <> ")." suCtRole = "(user role is set to " <> ctRole <> ")."
deServiceRoleChanged :: GroupInfo -> GroupMemberRole -> IO () deServiceRoleChanged :: GroupInfo -> GroupMemberRole -> IO ()
@@ -382,7 +382,7 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
_ -> pure () _ -> pure ()
where where
groupRef = groupReference g groupRef = groupReference g
srvRole = "*" <> strEncodeTxt serviceRole <> "*" srvRole = "*" <> B.unpack (strEncode serviceRole) <> "*"
suSrvRole = "(" <> serviceName <> " role is changed to " <> srvRole <> ")." suSrvRole = "(" <> serviceName <> " role is changed to " <> srvRole <> ")."
whenContactIsOwner gr action = whenContactIsOwner gr action =
getGroupMember gr getGroupMember gr
@@ -426,7 +426,7 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
<> serviceName <> serviceName
<> " bot will create a public group link for the new members to join even when you are offline.\n\ <> " 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\ \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 48 hours), and everybody will be able to find it in directory.\n\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" \Start from inviting the bot to your group as admin - it will guide you through the process"
DCSearchGroup s -> withFoundListedGroups (Just s) $ sendSearchResults s DCSearchGroup s -> withFoundListedGroups (Just s) $ sendSearchResults s
DCSearchNext -> DCSearchNext ->
@@ -448,47 +448,44 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
DCRecentGroups -> withFoundListedGroups Nothing $ sendAllGroups takeRecent "the most recent" STRecent DCRecentGroups -> withFoundListedGroups Nothing $ sendAllGroups takeRecent "the most recent" STRecent
DCSubmitGroup _link -> pure () DCSubmitGroup _link -> pure ()
DCConfirmDuplicateGroup ugrId gName -> DCConfirmDuplicateGroup ugrId gName ->
withUserGroupReg ugrId gName $ \g@GroupInfo {groupProfile = GroupProfile {displayName}} gr -> withUserGroupReg ugrId gName $ \gr g@GroupInfo {groupProfile = GroupProfile {displayName}} ->
readTVarIO (groupRegStatus gr) >>= \case readTVarIO (groupRegStatus gr) >>= \case
GRSPendingConfirmation -> GRSPendingConfirmation ->
getDuplicateGroup g >>= \case getDuplicateGroup g >>= \case
Nothing -> sendMessage cc ct "Error: getDuplicateGroup. Please notify the developers." Nothing -> sendMessage cc ct "Error: getDuplicateGroup. Please notify the developers."
Just DGReserved -> sendMessage cc ct $ groupAlreadyListed g Just DGReserved -> sendMessage cc ct $ groupAlreadyListed g
_ -> processInvitation ct g _ -> processInvitation ct g
_ -> sendReply $ "Error: the group ID " <> tshow ugrId <> " (" <> displayName <> ") is not pending confirmation." _ -> sendReply $ "Error: the group ID " <> show ugrId <> " (" <> T.unpack displayName <> ") is not pending confirmation."
DCListUserGroups -> DCListUserGroups ->
atomically (getUserGroupRegs st $ contactId' ct) >>= \grs -> do atomically (getUserGroupRegs st $ contactId' ct) >>= \grs -> do
sendReply $ tshow (length grs) <> " registered group(s)" sendReply $ show (length grs) <> " registered group(s)"
void . forkIO $ forM_ (reverse grs) $ \gr@GroupReg {userGroupRegId} -> void . forkIO $ forM_ (reverse grs) $ \gr@GroupReg {userGroupRegId} ->
sendGroupInfo ct gr userGroupRegId Nothing sendGroupInfo ct gr userGroupRegId Nothing
DCDeleteGroup ugrId gName -> DCDeleteGroup ugrId gName ->
withUserGroupReg ugrId gName $ \GroupInfo {groupProfile = GroupProfile {displayName}} gr -> do withUserGroupReg ugrId gName $ \gr GroupInfo {groupProfile = GroupProfile {displayName}} -> do
delGroupReg st gr delGroupReg st gr
sendReply $ "Your group " <> displayName <> " is deleted from the directory" sendReply $ T.unpack $ "Your group " <> displayName <> " is deleted from the directory"
DCSetRole gId gName mRole -> DCSetRole ugrId gName mRole ->
(if isAdmin then withGroupAndReg sendReply else withUserGroupReg) gId gName $ withUserGroupReg ugrId gName $ \_gr GroupInfo {groupId, groupProfile = GroupProfile {displayName}} -> do
\GroupInfo {groupId, groupProfile = GroupProfile {displayName}} _gr -> do gLink_ <- setGroupLinkRole cc groupId mRole
gLink_ <- setGroupLinkRole cc groupId mRole sendReply $ T.unpack $ case gLink_ of
sendReply $ case gLink_ of Nothing -> "Error: the initial member role for the group " <> displayName <> " was NOT upgated"
Nothing -> "Error: the initial member role for the group " <> displayName <> " was NOT upgated" Just gLink ->
Just gLink -> ("The initial member role for the group " <> displayName <> " is set to *" <> decodeLatin1 (strEncode mRole) <> "*\n\n")
("The initial member role for the group " <> displayName <> " is set to *" <> strEncodeTxt mRole <> "*\n\n") <> ("*Please note*: it applies only to members joining via this link: " <> safeDecodeUtf8 (strEncode $ simplexChatContact gLink))
<> ("*Please note*: it applies only to members joining via this link: " <> strEncodeTxt (simplexChatContact gLink))
DCUnknownCommand -> sendReply "Unknown command" DCUnknownCommand -> sendReply "Unknown command"
DCCommandError tag -> sendReply $ "Command error: " <> tshow tag DCCommandError tag -> sendReply $ "Command error: " <> show tag
where where
knownCt = knownContact ct
isAdmin = knownCt `elem` adminUsers || knownCt `elem` superUsers
withUserGroupReg ugrId gName action = withUserGroupReg ugrId gName action =
atomically (getUserGroupReg st (contactId' ct) ugrId) >>= \case atomically (getUserGroupReg st (contactId' ct) ugrId) >>= \case
Nothing -> sendReply $ "Group ID " <> tshow ugrId <> " not found" Nothing -> sendReply $ "Group ID " <> show ugrId <> " not found"
Just gr@GroupReg {dbGroupId} -> do Just gr@GroupReg {dbGroupId} -> do
getGroup cc dbGroupId >>= \case getGroup cc dbGroupId >>= \case
Nothing -> sendReply $ "Group ID " <> tshow ugrId <> " not found" Nothing -> sendReply $ "Group ID " <> show ugrId <> " not found"
Just g@GroupInfo {groupProfile = GroupProfile {displayName}} Just g@GroupInfo {groupProfile = GroupProfile {displayName}}
| displayName == gName -> action g gr | displayName == gName -> action gr g
| otherwise -> sendReply $ "Group ID " <> tshow ugrId <> " has the display name " <> displayName | otherwise -> sendReply $ "Group ID " <> show ugrId <> " has the display name " <> T.unpack displayName
sendReply = mkSendReply ct ciId sendReply = sendComposedMessage cc ct (Just ciId) . textMsgContent
withFoundListedGroups s_ action = withFoundListedGroups s_ action =
getGroups_ s_ >>= \case getGroups_ s_ >>= \case
Just groups -> atomically (filterListedGroups st groups) >>= action Just groups -> atomically (filterListedGroups st groups) >>= action
@@ -498,8 +495,8 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
gs -> do gs -> do
let gs' = takeTop searchResults gs let gs' = takeTop searchResults gs
moreGroups = length gs - length gs' moreGroups = length gs - length gs'
more = if moreGroups > 0 then ", sending top " <> tshow (length gs') else "" more = if moreGroups > 0 then ", sending top " <> show (length gs') else ""
sendReply $ "Found " <> tshow (length gs) <> " group(s)" <> more <> "." sendReply $ "Found " <> show (length gs) <> " group(s)" <> more <> "."
updateSearchRequest (STSearch s) $ groupIds gs' updateSearchRequest (STSearch s) $ groupIds gs'
sendFoundGroups gs' moreGroups sendFoundGroups gs' moreGroups
sendAllGroups takeFirst sortName searchType = \case sendAllGroups takeFirst sortName searchType = \case
@@ -507,8 +504,8 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
gs -> do gs -> do
let gs' = takeFirst searchResults gs let gs' = takeFirst searchResults gs
moreGroups = length gs - length gs' moreGroups = length gs - length gs'
more = if moreGroups > 0 then ", sending " <> sortName <> " " <> tshow (length gs') else "" more = if moreGroups > 0 then ", sending " <> sortName <> " " <> show (length gs') else ""
sendReply $ tshow (length gs) <> " group(s) listed" <> more <> "." sendReply $ show (length gs) <> " group(s) listed" <> more <> "."
updateSearchRequest searchType $ groupIds gs' updateSearchRequest searchType $ groupIds gs'
sendFoundGroups gs' moreGroups sendFoundGroups gs' moreGroups
sendNextSearchResults takeFirst SearchRequest {searchType, sentGroups} = \case sendNextSearchResults takeFirst SearchRequest {searchType, sentGroups} = \case
@@ -519,7 +516,7 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
let gs' = takeFirst searchResults $ filterNotSent sentGroups gs let gs' = takeFirst searchResults $ filterNotSent sentGroups gs
sentGroups' = sentGroups <> groupIds gs' sentGroups' = sentGroups <> groupIds gs'
moreGroups = length gs - S.size sentGroups' moreGroups = length gs - S.size sentGroups'
sendReply $ "Sending " <> tshow (length gs') <> " more group(s)." sendReply $ "Sending " <> show (length gs') <> " more group(s)."
updateSearchRequest searchType sentGroups' updateSearchRequest searchType sentGroups'
sendFoundGroups gs' moreGroups sendFoundGroups gs' moreGroups
updateSearchRequest :: SearchType -> Set GroupId -> IO () updateSearchRequest :: SearchType -> Set GroupId -> IO ()
@@ -530,10 +527,9 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
sendFoundGroups gs moreGroups = sendFoundGroups gs moreGroups =
void . forkIO $ do void . forkIO $ do
forM_ gs $ forM_ gs $
\(GroupInfo {groupId, groupProfile = p@GroupProfile {image = image_}}, GroupSummary {currentMembers}) -> do \(GroupInfo {groupProfile = p@GroupProfile {image = image_}}, GroupSummary {currentMembers}) -> do
let membersStr = "_" <> tshow currentMembers <> " members_" let membersStr = "_" <> tshow currentMembers <> " members_"
showId = if isAdmin then tshow groupId <> ". " else "" text = groupInfoText p <> "\n" <> membersStr
text = showId <> groupInfoText p <> "\n" <> membersStr
msg = maybe (MCText text) (\image -> MCImage {text, image}) image_ msg = maybe (MCText text) (\image -> MCImage {text, image}) image_
sendComposedMessage cc ct Nothing msg sendComposedMessage cc ct Nothing msg
when (moreGroups > 0) $ when (moreGroups > 0) $
@@ -541,134 +537,92 @@ directoryService st DirectoryOpts {adminUsers, superUsers, serviceName, searchRe
MCText $ MCText $
"Send */next* or just *.* for " <> tshow moreGroups <> " more result(s)." "Send */next* or just *.* for " <> tshow moreGroups <> " more result(s)."
deAdminCommand :: Contact -> ChatItemId -> DirectoryCmd 'DRAdmin -> IO () deSuperUserCommand :: Contact -> ChatItemId -> DirectoryCmd 'DRSuperUser -> IO ()
deAdminCommand ct ciId cmd deSuperUserCommand ct ciId cmd
| knownCt `elem` adminUsers || knownCt `elem` superUsers = case cmd of | superUser `elem` superUsers = case cmd of
DCApproveGroup {groupId, displayName = n, groupApprovalId} -> DCApproveGroup {groupId, displayName = n, groupApprovalId} ->
withGroupAndReg sendReply groupId n $ \g gr -> getGroupAndReg groupId n >>= \case
readTVarIO (groupRegStatus gr) >>= \case Nothing -> sendReply $ "The group " <> groupRef <> " not found (getGroupAndReg)."
GRSPendingApproval gaId Just (g, gr) ->
| gaId == groupApprovalId -> do readTVarIO (groupRegStatus gr) >>= \case
getDuplicateGroup g >>= \case GRSPendingApproval gaId
Nothing -> sendReply "Error: getDuplicateGroup. Please notify the developers." | gaId == groupApprovalId -> do
Just DGReserved -> sendReply $ "The group " <> groupRef <> " is already listed in the directory." getDuplicateGroup g >>= \case
_ -> do Nothing -> sendReply "Error: getDuplicateGroup. Please notify the developers."
getGroupRolesStatus g gr >>= \case Just DGReserved -> sendReply $ "The group " <> groupRef <> " is already listed in the directory."
Just GRSOk -> do _ -> do
setGroupStatus st gr GRSActive getGroupRolesStatus g gr >>= \case
let approved = "The group " <> userGroupReference' gr n <> " is approved" Just GRSOk -> do
notifyOwner gr $ approved <> " and listed in directory!\nPlease note: if you change the group profile it will be hidden from directory until it is re-approved." setGroupStatus st gr GRSActive
sendReply "Group approved!" sendReply "Group approved!"
notifyOtherSuperUsers $ approved <> " by " <> viewName (localDisplayName' ct) 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 GRSServiceNotAdmin -> replyNotApproved serviceNotAdmin
Just GRSContactNotOwner -> replyNotApproved "user is not an owner." Just GRSContactNotOwner -> replyNotApproved "user is not an owner."
Just GRSBadRoles -> replyNotApproved $ "user is not an owner, " <> serviceNotAdmin Just GRSBadRoles -> replyNotApproved $ "user is not an owner, " <> serviceNotAdmin
Nothing -> sendReply "Error: getGroupRolesStatus. Please notify the developers." Nothing -> sendReply "Error: getGroupRolesStatus. Please notify the developers."
where where
replyNotApproved reason = sendReply $ "Group is not approved: " <> reason replyNotApproved reason = sendReply $ "Group is not approved: " <> reason
serviceNotAdmin = serviceName <> " is not an admin." serviceNotAdmin = serviceName <> " is not an admin."
| otherwise -> sendReply "Incorrect approval code" | otherwise -> sendReply "Incorrect approval code"
_ -> sendReply $ "Error: the group " <> groupRef <> " is not pending approval." _ -> sendReply $ "Error: the group " <> groupRef <> " is not pending approval."
where where
groupRef = groupReference' groupId n groupRef = groupReference' groupId n
DCRejectGroup _gaId _gName -> pure () DCRejectGroup _gaId _gName -> pure ()
DCSuspendGroup groupId gName -> do DCSuspendGroup groupId gName -> do
let groupRef = groupReference' groupId gName let groupRef = groupReference' groupId gName
withGroupAndReg sendReply groupId gName $ \_ gr -> getGroupAndReg groupId gName >>= \case
readTVarIO (groupRegStatus gr) >>= \case Nothing -> sendReply $ "The group " <> groupRef <> " not found (getGroupAndReg)."
GRSActive -> do Just (_, gr) ->
setGroupStatus st gr GRSSuspended readTVarIO (groupRegStatus gr) >>= \case
let suspended = "The group " <> userGroupReference' gr gName <> " is suspended" GRSActive -> do
notifyOwner gr $ suspended <> " and hidden from directory. Please contact the administrators." setGroupStatus st gr GRSSuspended
sendReply "Group suspended!" notifyOwner gr $ "The group " <> userGroupReference' gr gName <> " is suspended and hidden from directory. Please contact the administrators."
notifyOtherSuperUsers $ suspended <> " by " <> viewName (localDisplayName' ct) sendReply "Group suspended!"
_ -> sendReply $ "The group " <> groupRef <> " is not active, can't be suspended." _ -> sendReply $ "The group " <> groupRef <> " is not active, can't be suspended."
DCResumeGroup groupId gName -> do DCResumeGroup groupId gName -> do
let groupRef = groupReference' groupId gName let groupRef = groupReference' groupId gName
withGroupAndReg sendReply groupId gName $ \_ gr -> getGroupAndReg groupId gName >>= \case
readTVarIO (groupRegStatus gr) >>= \case Nothing -> sendReply $ "The group " <> groupRef <> " not found (getGroupAndReg)."
GRSSuspended -> do Just (_, gr) ->
setGroupStatus st gr GRSActive readTVarIO (groupRegStatus gr) >>= \case
let groupStr = "The group " <> userGroupReference' gr gName GRSSuspended -> do
notifyOwner gr $ groupStr <> " is listed in the directory again!" setGroupStatus st gr GRSActive
sendReply "Group listing resumed!" notifyOwner gr $ "The group " <> userGroupReference' gr gName <> " is listed in the directory again!"
notifyOtherSuperUsers $ groupStr <> " listing resumed by " <> viewName (localDisplayName' ct) sendReply "Group listing resumed!"
_ -> sendReply $ "The group " <> groupRef <> " is not suspended, can't be resumed." _ -> sendReply $ "The group " <> groupRef <> " is not suspended, can't be resumed."
DCListLastGroups count -> listGroups count False DCListLastGroups count -> listGroups count False
DCListPendingGroups count -> listGroups count True DCListPendingGroups count -> listGroups count True
DCShowGroupLink groupId gName -> do DCExecuteCommand cmdStr ->
let groupRef = groupReference' groupId gName sendChatCmdStr cc cmdStr >>= \r -> do
withGroupAndReg sendReply groupId gName $ \_ _ -> ts <- getCurrentTime
sendChatCmd cc (APIGetGroupLink groupId) >>= \case tz <- getCurrentTimeZone
CRGroupLink {connReqContact, memberRole} -> sendReply $ serializeChatResponse (Nothing, Just user) ts tz Nothing r
sendReply $ T.unlines DCCommandError tag -> sendReply $ "Command error: " <> show tag
[ "The link to join the group " <> groupRef <> ":",
strEncodeTxt $ simplexChatContact connReqContact,
"New member role: " <> strEncodeTxt memberRole
]
CRChatCmdError _ (ChatErrorStore (SEGroupLinkNotFound _)) ->
sendReply $ "The group " <> groupRef <> " has no public link."
r -> do
ts <- getCurrentTime
tz <- getCurrentTimeZone
let resp = T.pack $ serializeChatResponse (Nothing, Just user) ts tz Nothing r
sendReply $ "Unexpected error:\n" <> resp
DCSendToGroupOwner groupId gName msg -> do
let groupRef = groupReference' groupId gName
withGroupAndReg sendReply groupId gName $ \_ gr@GroupReg {dbContactId} -> do
notifyOwner gr msg
owner_ <- getContact cc dbContactId
let ownerInfo = "the owner of the group " <> groupRef
ownerName ct' = "@" <> viewName (localDisplayName' ct') <> ", "
sendReply $ "Forwarded to " <> maybe "" ownerName owner_ <> ownerInfo
DCCommandError tag -> sendReply $ "Command error: " <> tshow tag
| otherwise = sendReply "You are not allowed to use this command" | otherwise = sendReply "You are not allowed to use this command"
where where
knownCt = knownContact ct superUser = KnownContact {contactId = contactId' ct, localDisplayName = localDisplayName' ct}
sendReply = mkSendReply ct ciId sendReply = sendComposedMessage cc ct (Just ciId) . textMsgContent
notifyOtherSuperUsers s = withSuperUsers $ \ctId -> unless (ctId == contactId' ct) $ sendMessage' cc ctId s
listGroups count pending = listGroups count pending =
readTVarIO (groupRegs st) >>= \groups -> do readTVarIO (groupRegs st) >>= \groups -> do
grs <- grs <-
if pending if pending
then filterM (fmap pendingApproval . readTVarIO . groupRegStatus) groups then filterM (fmap pendingApproval . readTVarIO . groupRegStatus) groups
else pure groups else pure groups
sendReply $ tshow (length grs) <> " registered group(s)" <> (if length grs > count then ", showing the last " <> tshow count else "") 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 void . forkIO $ forM_ (reverse $ take count grs) $ \gr@GroupReg {dbGroupId, dbContactId} -> do
ct_ <- getContact cc dbContactId ct_ <- getContact cc dbContactId
let ownerStr = "Owner: " <> maybe "getContact error" localDisplayName' ct_ let ownerStr = "Owner: " <> maybe "getContact error" localDisplayName' ct_
sendGroupInfo ct gr dbGroupId $ Just ownerStr sendGroupInfo ct gr dbGroupId $ Just ownerStr
deSuperUserCommand :: Contact -> ChatItemId -> DirectoryCmd 'DRSuperUser -> IO () getGroupAndReg :: GroupId -> GroupName -> IO (Maybe (GroupInfo, GroupReg))
deSuperUserCommand ct ciId cmd getGroupAndReg gId gName =
| knownContact ct `elem` superUsers = case cmd of getGroup cc gId
DCExecuteCommand cmdStr -> $>>= \g@GroupInfo {groupProfile = GroupProfile {displayName}} ->
sendChatCmdStr cc cmdStr >>= \r -> do if displayName == gName
ts <- getCurrentTime then
tz <- getCurrentTimeZone atomically (getGroupReg st gId)
sendReply $ T.pack $ serializeChatResponse (Nothing, Just user) ts tz Nothing r $>>= \gr -> pure $ Just (g, gr)
DCCommandError tag -> sendReply $ "Command error: " <> tshow tag else pure Nothing
| otherwise = sendReply "You are not allowed to use this command"
where
sendReply = mkSendReply ct ciId
knownContact :: Contact -> KnownContact
knownContact ct = KnownContact {contactId = contactId' ct, localDisplayName = localDisplayName' ct}
mkSendReply :: Contact -> ChatItemId -> Text -> IO ()
mkSendReply ct ciId = sendComposedMessage cc ct (Just ciId) . MCText
withGroupAndReg :: (Text -> IO ()) -> GroupId -> GroupName -> (GroupInfo -> GroupReg -> IO ()) -> IO ()
withGroupAndReg sendReply gId gName action =
getGroup cc gId >>= \case
Nothing -> sendReply $ "Group ID " <> tshow gId <> " not found (getGroup)"
Just g@GroupInfo {groupProfile = GroupProfile {displayName}}
| displayName == gName ->
atomically (getGroupReg st gId) >>= \case
Nothing -> sendReply $ "Registration for group ID " <> tshow gId <> " not found (getGroupReg)"
Just gr -> action g gr
| otherwise ->
sendReply $ "Group ID " <> tshow gId <> " has the display name " <> displayName
sendGroupInfo :: Contact -> GroupReg -> GroupId -> Maybe Text -> IO () sendGroupInfo :: Contact -> GroupReg -> GroupId -> Maybe Text -> IO ()
sendGroupInfo ct gr@GroupReg {dbGroupId} useGroupId ownerStr_ = do sendGroupInfo ct gr@GroupReg {dbGroupId} useGroupId ownerStr_ = do
@@ -714,8 +668,5 @@ setGroupLinkRole cc gId mRole = resp <$> sendChatCmd cc (APIGroupLinkMemberRole
CRGroupLink _ _ gLink _ -> Just gLink CRGroupLink _ _ gLink _ -> Just gLink
_ -> Nothing _ -> Nothing
unexpectedError :: Text -> Text unexpectedError :: String -> String
unexpectedError err = "Unexpected error: " <> err <> ", please notify the developers." unexpectedError err = "Unexpected error: " <> err <> ", please notify the developers."
strEncodeTxt :: StrEncoding a => a -> Text
strEncodeTxt = safeDecodeUtf8 . strEncode
+1 -1
View File
@@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd
source-repository-package source-repository-package
type: git type: git
location: https://github.com/simplex-chat/simplexmq.git location: https://github.com/simplex-chat/simplexmq.git
tag: 93f30c8edf9243ad2291dd6427d87328e282560a tag: 8a9d4f2af768d5d6a2b60c00d507529242f9b984
source-repository-package source-repository-package
type: git type: git
+17 -15
View File
@@ -8,23 +8,25 @@ layout: layouts/jobs.html
SimpleX Chat Ltd is a seed stage startup with a lot of user growth in 2022-2023, and a lot of exciting technical and product problems to solve to grow faster. SimpleX Chat Ltd is a seed stage startup with a lot of user growth in 2022-2023, and a lot of exciting technical and product problems to solve to grow faster.
We currently have 6 full-time people in the team. We currently have 4 full-time people in the team - all engineers, including the founder.
We want to add up to 3 people to the team.
We want to add 2 people to the team.
## Who we are looking for ## Who we are looking for
### Web designer & developer for a website contract ### Product/UI designer
You will work with the founder and a product marketing expert to convert the stories we want to tell our current and prospective users into interactive experiences. You will be designing the user experience and the interface of both the app and the website in collaboration with the team.
You are an expert in creating interactive web experiences: The current focus of the app is privacy and security, but we hope to have the design that would support the feeling of psychological safety, enabling people to achieve the results in the smallest amount of time.
- 15+ years of web development and design experience.
- Passionate about communications, privacy and data ownership.
- Competent using PhotoShop, 3D modelling, etc.
- Competent in Web tech, including JavaScript, animations, etc.
We will NOT consider agencies or groups it must be one person working on the project. You are an experienced and innovative product designer with:
- 8+ years of user experience and visual design.
- Expertise in typography and high sensitivity to colors.
- Exceptional precision and attention to details.
- Strong opinions (weakly held).
- A strong empathy.
### Application Haskell engineer ### Application Haskell engineer
@@ -32,12 +34,13 @@ You will work with the Haskell core of the client applications and with the netw
You are an expert in language models, databases and Haskell: You are an expert in language models, databases and Haskell:
- expert knowledge of SQL. - expert knowledge of SQL.
- Haskell strictness, exceptions, [concurrency](https://simonmar.github.io/pages/pcph.html), STM, [type systems](https://thinkingwithtypes.com). - Haskell exception handling, concurrency, STM, type systems.
- 15y+ of software engineering experience in complex projects. - 8y+ of software engineering experience in complex projects,
- deep understanding of the common programming principles: - deep understanding of the common programming principles:
- data structures, bits and bytes, text encoding. - data structures, bits and bytes, text encoding.
- [functional software design](https://mitp-content-server.mit.edu/books/content/sectbyfn/books_pres_0/6515/sicp.zip/index.html) and algorithms. - software design and algorithms.
- protocols and networking. - concurrency.
- networking.
## About you ## About you
@@ -45,7 +48,6 @@ You are an expert in language models, databases and Haskell:
- already use SimpleX Chat to communicate with friends/family or participate in public SimpleX Chat groups. - already use SimpleX Chat to communicate with friends/family or participate in public SimpleX Chat groups.
- passionate about privacy, security and communications. - passionate about privacy, security and communications.
- interested to make contributions to SimpleX Chat open-source project in your free time before we hire you, as an extended test. - interested to make contributions to SimpleX Chat open-source project in your free time before we hire you, as an extended test.
- you founded (and probably failed) at least one startup, or spent more time working for yourself than being employed.
- **Exceptionally pragmatic, very fast and customer-focussed**: - **Exceptionally pragmatic, very fast and customer-focussed**:
- care about the customers (aka users) and about the product we build much more than about the code quality, technology stack, etc. - care about the customers (aka users) and about the product we build much more than about the code quality, technology stack, etc.
-24
View File
@@ -1,24 +0,0 @@
# Server operators
## Problem
All preconfigured servers operated by a single company create a risk that user connections can be analysed by aggregating transport information from these servers.
The solution is to have more than one operator servers pre-configured in the app.
For operators to be protected from any violations of rights of other users or third parties by the users who use servers of these operators, the users have to explicitely accept conditions of use with the operator, in the same way they accept conditions of use with SimpleX Chat Ltd by downloading the app.
## Solution
Allow to assign operators to servers, both with preconfigured operators and servers, and with user-defined operators. Agent added support for server roles, chat app could:
- allow assigning server roles only on the operator level.
- only on server level.
- on both, with server roles overriding operator roles (that would require a different type for server for chat app).
For simplicity of both UX and logic it is probably better to allow assigning roles only on operators' level, and servers without set operators can be used for both roles.
For agreements, it is sufficient to record the signatures of these agreements on users' devices, together with the copy of signed agreement (or its hash and version) in a separate table. The terms themselves could be:
- included in the app - either in code or in migration.
- referenced with a stable link to a particular commit.
The first solution seems better, as it avoids any third party dependency, and the agreement size is relatively small (~31kb), to reduce size we can store it compressed.
Generated
+470 -282
View File
@@ -16,6 +16,21 @@
"type": "github" "type": "github"
} }
}, },
"blank": {
"locked": {
"lastModified": 1625557891,
"narHash": "sha256-O8/MWsPBGhhyPoPLHZAuoZiiHo9q6FLlEeIDEXuj6T4=",
"owner": "divnix",
"repo": "blank",
"rev": "5a5d2684073d9f563072ed07c871d577a6c614a8",
"type": "github"
},
"original": {
"owner": "divnix",
"repo": "blank",
"type": "github"
}
},
"cabal-32": { "cabal-32": {
"flake": false, "flake": false,
"locked": { "locked": {
@@ -83,6 +98,64 @@
"type": "github" "type": "github"
} }
}, },
"devshell": {
"inputs": {
"flake-utils": [
"haskellNix",
"tullia",
"std",
"flake-utils"
],
"nixpkgs": [
"haskellNix",
"tullia",
"std",
"nixpkgs"
]
},
"locked": {
"lastModified": 1663445644,
"narHash": "sha256-+xVlcK60x7VY1vRJbNUEAHi17ZuoQxAIH4S4iUFUGBA=",
"owner": "numtide",
"repo": "devshell",
"rev": "e3dc3e21594fe07bdb24bdf1c8657acaa4cb8f66",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "devshell",
"type": "github"
}
},
"dmerge": {
"inputs": {
"nixlib": [
"haskellNix",
"tullia",
"std",
"nixpkgs"
],
"yants": [
"haskellNix",
"tullia",
"std",
"yants"
]
},
"locked": {
"lastModified": 1659548052,
"narHash": "sha256-fzI2gp1skGA8mQo/FBFrUAtY0GQkAIAaV/V127TJPyY=",
"owner": "divnix",
"repo": "data-merge",
"rev": "d160d18ce7b1a45b88344aa3f13ed1163954b497",
"type": "github"
},
"original": {
"owner": "divnix",
"repo": "data-merge",
"type": "github"
}
},
"flake-compat": { "flake-compat": {
"flake": false, "flake": false,
"locked": { "locked": {
@@ -100,34 +173,74 @@
"type": "github" "type": "github"
} }
}, },
"flake-parts": { "flake-compat_2": {
"inputs": { "flake": false,
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": { "locked": {
"lastModified": 1698579227, "lastModified": 1650374568,
"narHash": "sha256-KVWjFZky+gRuWennKsbo6cWyo7c/z/VgCte5pR9pEKg=", "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=",
"owner": "hercules-ci", "owner": "edolstra",
"repo": "flake-parts", "repo": "flake-compat",
"rev": "f76e870d64779109e41370848074ac4eaa1606ec", "rev": "b4a34015c698c7793d592d66adbab377907a2be8",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "hercules-ci", "owner": "edolstra",
"repo": "flake-parts", "repo": "flake-compat",
"type": "github" "type": "github"
} }
}, },
"flake-utils": { "flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": { "locked": {
"lastModified": 1701680307, "lastModified": 1676283394,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725", "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_3": {
"locked": {
"lastModified": 1653893745,
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_4": {
"locked": {
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -153,51 +266,33 @@
"type": "github" "type": "github"
} }
}, },
"ghc98X": { "gomod2nix": {
"flake": false, "inputs": {
"nixpkgs": "nixpkgs_2",
"utils": "utils"
},
"locked": { "locked": {
"lastModified": 1715066704, "lastModified": 1655245309,
"narHash": "sha256-F0EVR8x/fcpj1st+hz96Wdsz5uwVIOziGKAwRxLOYJw=", "narHash": "sha256-d/YPoQ/vFn1+GTmSdvbSBSTOai61FONxB4+Lt6w/IVI=",
"ref": "ghc-9.8", "owner": "tweag",
"rev": "78a253543d466ac511a1664a3e6aff032ca684d5", "repo": "gomod2nix",
"revCount": 61757, "rev": "40d32f82fc60d66402eb0972e6e368aeab3faf58",
"submodules": true, "type": "github"
"type": "git",
"url": "https://gitlab.haskell.org/ghc/ghc"
}, },
"original": { "original": {
"ref": "ghc-9.8", "owner": "tweag",
"submodules": true, "repo": "gomod2nix",
"type": "git", "type": "github"
"url": "https://gitlab.haskell.org/ghc/ghc"
}
},
"ghc99": {
"flake": false,
"locked": {
"lastModified": 1726585445,
"narHash": "sha256-IdwQBex4boY6s0Plj5+ixf36rfYSUyMdTWrztKvZH30=",
"ref": "refs/heads/master",
"rev": "7fd9e5e29ab54eb406880077463e8552e2ddd39a",
"revCount": 67238,
"submodules": true,
"type": "git",
"url": "https://gitlab.haskell.org/ghc/ghc"
},
"original": {
"submodules": true,
"type": "git",
"url": "https://gitlab.haskell.org/ghc/ghc"
} }
}, },
"hackage": { "hackage": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1702513363, "lastModified": 1702340598,
"narHash": "sha256-kloro9uEe8aYhPMoMjVNq2rfrXNgMOZhOPwVH5DH2K0=", "narHash": "sha256-CC0HI+6iKPtH+8r/ZfcpW5v/OYvL7zMwpr0xfkXV1zU=",
"owner": "input-output-hk", "owner": "input-output-hk",
"repo": "hackage.nix", "repo": "hackage.nix",
"rev": "a9d931d0398da67846fa257922a924829233cb91", "rev": "24617c569995e38bf3b83b48eec6628a50fdb4fb",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -214,43 +309,33 @@
"cabal-36": "cabal-36", "cabal-36": "cabal-36",
"cardano-shell": "cardano-shell", "cardano-shell": "cardano-shell",
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
"flake-utils": "flake-utils_2",
"ghc-8.6.5-iohk": "ghc-8.6.5-iohk", "ghc-8.6.5-iohk": "ghc-8.6.5-iohk",
"ghc98X": "ghc98X",
"ghc99": "ghc99",
"hackage": [ "hackage": [
"hackage" "hackage"
], ],
"hls-1.10": "hls-1.10",
"hls-2.0": "hls-2.0",
"hls-2.2": "hls-2.2",
"hls-2.3": "hls-2.3",
"hls-2.4": "hls-2.4",
"hls-2.5": "hls-2.5",
"hls-2.6": "hls-2.6",
"hpc-coveralls": "hpc-coveralls", "hpc-coveralls": "hpc-coveralls",
"hydra": "hydra", "hydra": "hydra",
"iserv-proxy": "iserv-proxy", "iserv-proxy": "iserv-proxy",
"nixpkgs": [ "nixpkgs": [
"haskellNix", "nixpkgs"
"nixpkgs-unstable"
], ],
"nixpkgs-2003": "nixpkgs-2003", "nixpkgs-2003": "nixpkgs-2003",
"nixpkgs-2105": "nixpkgs-2105", "nixpkgs-2105": "nixpkgs-2105",
"nixpkgs-2111": "nixpkgs-2111", "nixpkgs-2111": "nixpkgs-2111",
"nixpkgs-2205": "nixpkgs-2205", "nixpkgs-2205": "nixpkgs-2205",
"nixpkgs-2211": "nixpkgs-2211", "nixpkgs-2211": "nixpkgs-2211",
"nixpkgs-2305": "nixpkgs-2305",
"nixpkgs-2311": "nixpkgs-2311",
"nixpkgs-unstable": "nixpkgs-unstable", "nixpkgs-unstable": "nixpkgs-unstable",
"old-ghc-nix": "old-ghc-nix", "old-ghc-nix": "old-ghc-nix",
"stackage": "stackage" "stackage": "stackage",
"tullia": "tullia"
}, },
"locked": { "locked": {
"lastModified": 1705833500, "lastModified": 1677975916,
"narHash": "sha256-rUIr6JNbCedt1g4gVYVvE9t0oFU6FUspCA0DS5cA8Bg=", "narHash": "sha256-dbe8lEEPyfzjdRwpePClv7J9p9lQg7BwbBqAMCw4RLw=",
"owner": "input-output-hk", "owner": "input-output-hk",
"repo": "haskell.nix", "repo": "haskell.nix",
"rev": "d0c35e75cbbc6858770af42ac32b0b85495fbd71", "rev": "ab5efd87ce3fd8ade38a01d97693d29a4f1ae7e4",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -260,125 +345,6 @@
"type": "github" "type": "github"
} }
}, },
"hls-1.10": {
"flake": false,
"locked": {
"lastModified": 1680000865,
"narHash": "sha256-rc7iiUAcrHxwRM/s0ErEsSPxOR3u8t7DvFeWlMycWgo=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "b08691db779f7a35ff322b71e72a12f6e3376fd9",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "1.10.0.0",
"repo": "haskell-language-server",
"type": "github"
}
},
"hls-2.0": {
"flake": false,
"locked": {
"lastModified": 1687698105,
"narHash": "sha256-OHXlgRzs/kuJH8q7Sxh507H+0Rb8b7VOiPAjcY9sM1k=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "783905f211ac63edf982dd1889c671653327e441",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "2.0.0.1",
"repo": "haskell-language-server",
"type": "github"
}
},
"hls-2.2": {
"flake": false,
"locked": {
"lastModified": 1693064058,
"narHash": "sha256-8DGIyz5GjuCFmohY6Fa79hHA/p1iIqubfJUTGQElbNk=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "b30f4b6cf5822f3112c35d14a0cba51f3fe23b85",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "2.2.0.0",
"repo": "haskell-language-server",
"type": "github"
}
},
"hls-2.3": {
"flake": false,
"locked": {
"lastModified": 1695910642,
"narHash": "sha256-tR58doOs3DncFehHwCLczJgntyG/zlsSd7DgDgMPOkI=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "458ccdb55c9ea22cd5d13ec3051aaefb295321be",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "2.3.0.0",
"repo": "haskell-language-server",
"type": "github"
}
},
"hls-2.4": {
"flake": false,
"locked": {
"lastModified": 1699862708,
"narHash": "sha256-YHXSkdz53zd0fYGIYOgLt6HrA0eaRJi9mXVqDgmvrjk=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "54507ef7e85fa8e9d0eb9a669832a3287ffccd57",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "2.4.0.1",
"repo": "haskell-language-server",
"type": "github"
}
},
"hls-2.5": {
"flake": false,
"locked": {
"lastModified": 1701080174,
"narHash": "sha256-fyiR9TaHGJIIR0UmcCb73Xv9TJq3ht2ioxQ2mT7kVdc=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "27f8c3d3892e38edaef5bea3870161815c4d014c",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "2.5.0.0",
"repo": "haskell-language-server",
"type": "github"
}
},
"hls-2.6": {
"flake": false,
"locked": {
"lastModified": 1705325287,
"narHash": "sha256-+P87oLdlPyMw8Mgoul7HMWdEvWP/fNlo8jyNtwME8E8=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "6e0b342fa0327e628610f2711f8c3e4eaaa08b1e",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "2.6.0.0",
"repo": "haskell-language-server",
"type": "github"
}
},
"hpc-coveralls": { "hpc-coveralls": {
"flake": false, "flake": false,
"locked": { "locked": {
@@ -418,14 +384,37 @@
"type": "indirect" "type": "indirect"
} }
}, },
"incl": {
"inputs": {
"nixlib": [
"haskellNix",
"tullia",
"std",
"nixpkgs"
]
},
"locked": {
"lastModified": 1669263024,
"narHash": "sha256-E/+23NKtxAqYG/0ydYgxlgarKnxmDbg6rCMWnOBqn9Q=",
"owner": "divnix",
"repo": "incl",
"rev": "ce7bebaee048e4cd7ebdb4cee7885e00c4e2abca",
"type": "github"
},
"original": {
"owner": "divnix",
"repo": "incl",
"type": "github"
}
},
"iserv-proxy": { "iserv-proxy": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1707968597, "lastModified": 1670983692,
"narHash": "sha256-C53NqToxl+n9s1pQ0iLtiH6P5vX3rM+NW/mFt4Ykpsk=", "narHash": "sha256-avLo34JnI9HNyOuauK5R69usJm+GfW3MlyGlYxZhTgY=",
"ref": "hkm/remote-iserv", "ref": "hkm/remote-iserv",
"rev": "1b7f8aeb37bbc7c00f04e44d9379aa15a4409e8b", "rev": "50d0abb3317ac439a4e7495b185a64af9b7b9300",
"revCount": 18, "revCount": 10,
"type": "git", "type": "git",
"url": "https://gitlab.haskell.org/hamishmack/iserv-proxy.git" "url": "https://gitlab.haskell.org/hamishmack/iserv-proxy.git"
}, },
@@ -451,22 +440,32 @@
"type": "github" "type": "github"
} }
}, },
"mac2ios": { "n2c": {
"inputs": { "inputs": {
"flake-parts": "flake-parts", "flake-utils": [
"nixpkgs": "nixpkgs_2" "haskellNix",
"tullia",
"std",
"flake-utils"
],
"nixpkgs": [
"haskellNix",
"tullia",
"std",
"nixpkgs"
]
}, },
"locked": { "locked": {
"lastModified": 1699767871, "lastModified": 1665039323,
"narHash": "sha256-kxeCUfwC/Vgh2FvVMlBUq0eVx1JvfHyN+5MPKUik9mE=", "narHash": "sha256-SAh3ZjFGsaCI8FRzXQyp56qcGdAqgKEfJWPCQ0Sr7tQ=",
"owner": "zw3rk", "owner": "nlewo",
"repo": "mobile-core-tools", "repo": "nix2container",
"rev": "4dcb77d5ea896d749381806dfab5358851b08951", "rev": "b008fe329ffb59b67bf9e7b08ede6ee792f2741a",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "zw3rk", "owner": "nlewo",
"repo": "mobile-core-tools", "repo": "nix2container",
"type": "github" "type": "github"
} }
}, },
@@ -491,6 +490,95 @@
"type": "github" "type": "github"
} }
}, },
"nix-nomad": {
"inputs": {
"flake-compat": "flake-compat_2",
"flake-utils": [
"haskellNix",
"tullia",
"nix2container",
"flake-utils"
],
"gomod2nix": "gomod2nix",
"nixpkgs": [
"haskellNix",
"tullia",
"nixpkgs"
],
"nixpkgs-lib": [
"haskellNix",
"tullia",
"nixpkgs"
]
},
"locked": {
"lastModified": 1658277770,
"narHash": "sha256-T/PgG3wUn8Z2rnzfxf2VqlR1CBjInPE0l1yVzXxPnt0=",
"owner": "tristanpemble",
"repo": "nix-nomad",
"rev": "054adcbdd0a836ae1c20951b67ed549131fd2d70",
"type": "github"
},
"original": {
"owner": "tristanpemble",
"repo": "nix-nomad",
"type": "github"
}
},
"nix2container": {
"inputs": {
"flake-utils": "flake-utils_3",
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1658567952,
"narHash": "sha256-XZ4ETYAMU7XcpEeAFP3NOl9yDXNuZAen/aIJ84G+VgA=",
"owner": "nlewo",
"repo": "nix2container",
"rev": "60bb43d405991c1378baf15a40b5811a53e32ffa",
"type": "github"
},
"original": {
"owner": "nlewo",
"repo": "nix2container",
"type": "github"
}
},
"nixago": {
"inputs": {
"flake-utils": [
"haskellNix",
"tullia",
"std",
"flake-utils"
],
"nixago-exts": [
"haskellNix",
"tullia",
"std",
"blank"
],
"nixpkgs": [
"haskellNix",
"tullia",
"std",
"nixpkgs"
]
},
"locked": {
"lastModified": 1661824785,
"narHash": "sha256-/PnwdWoO/JugJZHtDUioQp3uRiWeXHUdgvoyNbXesz8=",
"owner": "nix-community",
"repo": "nixago",
"rev": "8c1f9e5f1578d4b2ea989f618588d62a335083c3",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixago",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1657693803, "lastModified": 1657693803,
@@ -557,11 +645,11 @@
}, },
"nixpkgs-2205": { "nixpkgs-2205": {
"locked": { "locked": {
"lastModified": 1685573264, "lastModified": 1672580127,
"narHash": "sha256-Zffu01pONhs/pqH07cjlF10NnMDLok8ix5Uk4rhOnZQ=", "narHash": "sha256-3lW3xZslREhJogoOkjeZtlBtvFMyxHku7I/9IVehhT8=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "380be19fbd2d9079f677978361792cb25e8a3635", "rev": "0874168639713f547c05947c76124f78441ea46c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -573,11 +661,11 @@
}, },
"nixpkgs-2211": { "nixpkgs-2211": {
"locked": { "locked": {
"lastModified": 1688392541, "lastModified": 1675730325,
"narHash": "sha256-lHrKvEkCPTUO+7tPfjIcb7Trk6k31rz18vkyqmkeJfY=", "narHash": "sha256-uNvD7fzO5hNlltNQUAFBPlcEjNG5Gkbhl/ROiX+GZU4=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "ea4c80b39be4c09702b0cb3b42eab59e2ba4f24b", "rev": "b7ce17b1ebf600a72178f6302c77b6382d09323f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -587,56 +675,6 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs-2305": {
"locked": {
"lastModified": 1705033721,
"narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-23.05-darwin",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-2311": {
"locked": {
"lastModified": 1719957072,
"narHash": "sha256-gvFhEf5nszouwLAkT9nWsDzocUTqLWHuL++dvNjMp9I=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7144d6241f02d171d25fba3edeaf15e0f2592105",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-23.11-darwin",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"dir": "lib",
"lastModified": 1696019113,
"narHash": "sha256-X3+DKYWJm93DRSdC5M6K5hLqzSya9BjibtBsuARoPco=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "f5892ddac112a1e9b3612c39af1b72987ee5783a",
"type": "github"
},
"original": {
"dir": "lib",
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-regression": { "nixpkgs-regression": {
"locked": { "locked": {
"lastModified": 1643052045, "lastModified": 1643052045,
@@ -655,36 +693,98 @@
}, },
"nixpkgs-unstable": { "nixpkgs-unstable": {
"locked": { "locked": {
"lastModified": 1694822471, "lastModified": 1675758091,
"narHash": "sha256-6fSDCj++lZVMZlyqOe9SIOL8tYSBz1bI8acwovRwoX8=", "narHash": "sha256-7gFSQbSVAFUHtGCNHPF7mPc5CcqDk9M2+inlVPZSneg=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "47585496bcb13fb72e4a90daeea2f434e2501998", "rev": "747927516efcb5e31ba03b7ff32f61f6d47e7d87",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "47585496bcb13fb72e4a90daeea2f434e2501998",
"type": "github" "type": "github"
} }
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1698434055, "lastModified": 1653581809,
"narHash": "sha256-Phxi5mUKSoL7A0IYUiYtkI9e8NcGaaV5PJEaJApU1Ko=", "narHash": "sha256-Uvka0V5MTGbeOfWte25+tfRL3moECDh1VwokWSZUdoY=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "1a3c95e3b23b3cdb26750621c08cc2f1560cb883", "rev": "83658b28fe638a170a19b8933aa008b30640fbd1",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-23.05", "ref": "nixos-unstable",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nixpkgs_3": {
"locked": {
"lastModified": 1654807842,
"narHash": "sha256-ADymZpr6LuTEBXcy6RtFHcUZdjKTBRTMYwu19WOx17E=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "fc909087cc3386955f21b4665731dbdaceefb1d8",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1665087388,
"narHash": "sha256-FZFPuW9NWHJteATOf79rZfwfRn5fE0wi9kRzvGfDHPA=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "95fda953f6db2e9496d2682c4fc7b82f959878f7",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_5": {
"locked": {
"lastModified": 1676726892,
"narHash": "sha256-M7OYVR6dKmzmlebIjybFf3l18S2uur8lMyWWnHQooLY=",
"owner": "angerman",
"repo": "nixpkgs",
"rev": "729469087592bdea58b360de59dadf6d58714c42",
"type": "github"
},
"original": {
"owner": "angerman",
"ref": "release-22.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nosys": {
"locked": {
"lastModified": 1667881534,
"narHash": "sha256-FhwJ15uPLRsvaxtt/bNuqE/ykMpNAPF0upozFKhTtXM=",
"owner": "divnix",
"repo": "nosys",
"rev": "2d0d5207f6a230e9d0f660903f8db9807b54814f",
"type": "github"
},
"original": {
"owner": "divnix",
"repo": "nosys",
"type": "github"
}
},
"old-ghc-nix": { "old-ghc-nix": {
"flake": false, "flake": false,
"locked": { "locked": {
@@ -707,21 +807,17 @@
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
"hackage": "hackage", "hackage": "hackage",
"haskellNix": "haskellNix", "haskellNix": "haskellNix",
"mac2ios": "mac2ios", "nixpkgs": "nixpkgs_5"
"nixpkgs": [
"haskellNix",
"nixpkgs-2305"
]
} }
}, },
"stackage": { "stackage": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1726532152, "lastModified": 1677888571,
"narHash": "sha256-LRXbVY3M2S8uQWdwd2zZrsnVPEvt2GxaHGoy8EFFdJA=", "narHash": "sha256-YkhRNOaN6QVagZo1cfykYV8KqkI8/q6r2F5+jypOma4=",
"owner": "input-output-hk", "owner": "input-output-hk",
"repo": "stackage.nix", "repo": "stackage.nix",
"rev": "c77b3530cebad603812cb111c6f64968c2d2337d", "rev": "cb50e6fabdfb2d7e655059039012ad0623f06a27",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -730,18 +826,110 @@
"type": "github" "type": "github"
} }
}, },
"systems": { "std": {
"inputs": {
"arion": [
"haskellNix",
"tullia",
"std",
"blank"
],
"blank": "blank",
"devshell": "devshell",
"dmerge": "dmerge",
"flake-utils": "flake-utils_4",
"incl": "incl",
"makes": [
"haskellNix",
"tullia",
"std",
"blank"
],
"microvm": [
"haskellNix",
"tullia",
"std",
"blank"
],
"n2c": "n2c",
"nixago": "nixago",
"nixpkgs": "nixpkgs_4",
"nosys": "nosys",
"yants": "yants"
},
"locked": { "locked": {
"lastModified": 1681028828, "lastModified": 1674526466,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", "narHash": "sha256-tMTaS0bqLx6VJ+K+ZT6xqsXNpzvSXJTmogkraBGzymg=",
"owner": "nix-systems", "owner": "divnix",
"repo": "default", "repo": "std",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", "rev": "516387e3d8d059b50e742a2ff1909ed3c8f82826",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nix-systems", "owner": "divnix",
"repo": "default", "repo": "std",
"type": "github"
}
},
"tullia": {
"inputs": {
"nix-nomad": "nix-nomad",
"nix2container": "nix2container",
"nixpkgs": [
"haskellNix",
"nixpkgs"
],
"std": "std"
},
"locked": {
"lastModified": 1675695930,
"narHash": "sha256-B7rEZ/DBUMlK1AcJ9ajnAPPxqXY6zW2SBX+51bZV0Ac=",
"owner": "input-output-hk",
"repo": "tullia",
"rev": "621365f2c725608f381b3ad5b57afef389fd4c31",
"type": "github"
},
"original": {
"owner": "input-output-hk",
"repo": "tullia",
"type": "github"
}
},
"utils": {
"locked": {
"lastModified": 1653893745,
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"yants": {
"inputs": {
"nixpkgs": [
"haskellNix",
"tullia",
"std",
"nixpkgs"
]
},
"locked": {
"lastModified": 1667096281,
"narHash": "sha256-wRRec6ze0gJHmGn6m57/zhz/Kdvp9HS4Nl5fkQ+uIuA=",
"owner": "divnix",
"repo": "yants",
"rev": "d18f356ec25cb94dc9c275870c3a7927a10f8c3c",
"type": "github"
},
"original": {
"owner": "divnix",
"repo": "yants",
"type": "github" "type": "github"
} }
} }
+24 -278
View File
@@ -1,15 +1,15 @@
{ {
description = "nix flake for simplex-chat"; description = "nix flake for simplex-chat";
inputs.nixpkgs.url = "github:angerman/nixpkgs/release-22.11";
inputs.haskellNix.url = "github:input-output-hk/haskell.nix/armv7a"; inputs.haskellNix.url = "github:input-output-hk/haskell.nix/armv7a";
inputs.nixpkgs.follows = "haskellNix/nixpkgs-2305"; inputs.haskellNix.inputs.nixpkgs.follows = "nixpkgs";
inputs.mac2ios.url = "github:zw3rk/mobile-core-tools";
inputs.hackage = { inputs.hackage = {
url = "github:input-output-hk/hackage.nix"; url = "github:input-output-hk/hackage.nix";
flake = false; flake = false;
}; };
inputs.haskellNix.inputs.hackage.follows = "hackage"; inputs.haskellNix.inputs.hackage.follows = "hackage";
inputs.flake-utils.url = "github:numtide/flake-utils"; inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = { self, haskellNix, nixpkgs, flake-utils, mac2ios, ... }: outputs = { self, haskellNix, nixpkgs, flake-utils, ... }:
let systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ]; in let systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ]; in
flake-utils.lib.eachSystem systems (system: flake-utils.lib.eachSystem systems (system:
# this android26 overlay makes the pkgsCross.{aarch64-android,armv7a-android-prebuilt} to set stdVer to 26 (Android 8). # this android26 overlay makes the pkgsCross.{aarch64-android,armv7a-android-prebuilt} to set stdVer to 26 (Android 8).
@@ -30,7 +30,7 @@
# `appendOverlays` with a singleton is identical to `extend`. # `appendOverlays` with a singleton is identical to `extend`.
let pkgs = haskellNix.legacyPackages.${system}.appendOverlays [android26]; in let pkgs = haskellNix.legacyPackages.${system}.appendOverlays [android26]; in
let drv' = { extra-modules, pkgs', ... }: pkgs'.haskell-nix.project { let drv' = { extra-modules, pkgs', ... }: pkgs'.haskell-nix.project {
compiler-nix-name = "ghc963"; compiler-nix-name = "ghc8107";
index-state = "2023-12-12T00:00:00Z"; index-state = "2023-12-12T00:00:00Z";
# We need this, to specify we want the cabal project. # We need this, to specify we want the cabal project.
# If the stack.yaml was dropped, this would not be necessary. # If the stack.yaml was dropped, this would not be necessary.
@@ -40,12 +40,9 @@
src = ./.; src = ./.;
}; };
sha256map = import ./scripts/nix/sha256map.nix; sha256map = import ./scripts/nix/sha256map.nix;
modules = [ modules = [{
({ pkgs, lib, ...}: lib.mkIf (!pkgs.stdenv.hostPlatform.isWindows) {
# This patch adds `dl` as an extra-library to direct-sqlciper, which is needed
# on pretty much all unix platforms, but then blows up on windows m(
packages.direct-sqlcipher.patches = [ ./scripts/nix/direct-sqlcipher-2.3.27.patch ]; packages.direct-sqlcipher.patches = [ ./scripts/nix/direct-sqlcipher-2.3.27.patch ];
}) }
({ pkgs,lib, ... }: lib.mkIf (pkgs.stdenv.hostPlatform.isAndroid) { ({ pkgs,lib, ... }: lib.mkIf (pkgs.stdenv.hostPlatform.isAndroid) {
packages.simplex-chat.components.library.ghcOptions = [ "-pie" ]; packages.simplex-chat.components.library.ghcOptions = [ "-pie" ];
})] ++ extra-modules; })] ++ extra-modules;
@@ -67,9 +64,6 @@
}); in }); in
let iosPostInstall = bundleName: '' let iosPostInstall = bundleName: ''
${pkgs.tree}/bin/tree $out ${pkgs.tree}/bin/tree $out
mkdir tmp
find ./dist -name "libHS*-ghc*.a" -exec cp {} tmp \;
(cd tmp; ${pkgs.tree}/bin/tree .; ar x libHS*.a; for o in *.o; do if /usr/bin/otool -xv $o|grep ldadd ; then echo $o; fi; done; cd ..; rm -fR tmp)
mkdir -p $out/_pkg mkdir -p $out/_pkg
# copy over includes, we might want those, but maybe not. # copy over includes, we might want those, but maybe not.
# cp -r $out/lib/*/*/include $out/_pkg/ # cp -r $out/lib/*/*/include $out/_pkg/
@@ -80,18 +74,6 @@
find ${pkgs.gmp6.override { withStatic = true; }}/lib -name "*.a" -exec cp {} $out/_pkg \; find ${pkgs.gmp6.override { withStatic = true; }}/lib -name "*.a" -exec cp {} $out/_pkg \;
# There is no static libc # There is no static libc
${pkgs.tree}/bin/tree $out/_pkg ${pkgs.tree}/bin/tree $out/_pkg
for pkg in $out/_pkg/*.a; do
chmod +w $pkg
${mac2ios.packages.${system}.mac2ios}/bin/mac2ios $pkg
chmod -w $pkg
done
mkdir tmp
find $out/_pkg -name "libHS*-ghc*.a" -exec cp {} tmp \;
(cd tmp; ${pkgs.tree}/bin/tree .; ar x libHS*.a; for o in *.o; do if /usr/bin/otool -xv $o|grep ldadd ; then echo $o; fi; done; cd ..; rm -fR tmp)
sha256sum $out/_pkg/*.a
(cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/${bundleName}.zip *) (cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/${bundleName}.zip *)
rm -fR $out/_pkg rm -fR $out/_pkg
mkdir -p $out/nix-support mkdir -p $out/nix-support
@@ -137,150 +119,13 @@
hardeningDisable = [ "fortify" ]; hardeningDisable = [ "fortify" ];
} }
);in { );in {
# STATIC x86_64-linux
"${pkgs.pkgsCross.musl64.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.musl64).simplex-chat.components.exes.simplex-chat; "${pkgs.pkgsCross.musl64.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.musl64).simplex-chat.components.exes.simplex-chat;
# STATIC i686-linux "${pkgs.pkgsCross.musl32.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.musl32).simplex-chat.components.exes.simplex-chat;
"${pkgs.pkgsCross.musl32.hostPlatform.system}-static:exe:simplex-chat" = (drv' {
pkgs' = pkgs.pkgsCross.musl32;
extra-modules = [{
# 32 bit patches
packages.basement.patches = [
./scripts/nix/basement-pr-573.patch
];
packages.memory.patches = [
./scripts/nix/memory-pr-99.patch
];
}];
}).simplex-chat.components.exes.simplex-chat;
# WINDOWS x86_64-mingwW64
"${pkgs.pkgsCross.mingwW64.hostPlatform.system}:exe:simplex-chat" = (drv' {
pkgs' = pkgs.pkgsCross.mingwW64;
extra-modules = [{
packages.direct-sqlcipher.flags.openssl = true;
packages.bitvec.flags.simd = false;
packages.direct-sqlcipher.patches = [
./scripts/nix/direct-sqlcipher-2.3.27-win.patch
];
packages.direct-sqlcipher.components.library.libs = pkgs.lib.mkForce [
(pkgs.pkgsCross.mingwW64.openssl) #.override) # { static = true; enableKTLS = false; })
];
packages.simplexmq.components.library.libs = pkgs.lib.mkForce [
(pkgs.pkgsCross.mingwW64.openssl) #.override) # { static = true; enableKTLS = false; })
];
packages.unix-time.postPatch = ''
sed -i 's/mingwex//g' unix-time.cabal
'';
}];
}).simplex-chat.components.exes.simplex-chat.override {
postInstall = ''
set -x
${pkgs.tree}/bin/tree $out
mkdir -p $out/_pkg
cp $out/bin/* $out/_pkg
${pkgs.tree}/bin/tree $out/_pkg
(cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/${pkgs.pkgsCross.mingwW64.hostPlatform.system}-simplex-chat.zip *)
rm -fR $out/_pkg
mkdir -p $out/nix-support
echo "file binary-dist \"$(echo $out/*.zip)\"" \
> $out/nix-support/hydra-build-products
'';
};
"${pkgs.pkgsCross.mingwW64.hostPlatform.system}:lib:simplex-chat" = (drv' rec {
pkgs' = pkgs.pkgsCross.mingwW64;
extra-modules = [{
packages.direct-sqlcipher.flags.openssl = true;
# simd will try to read __cpu_model, which we don't expose
# from the rts (yet!).
packages.bitvec.flags.simd = false;
packages.direct-sqlcipher.patches = [
./scripts/nix/direct-sqlcipher-2.3.27-win.patch
];
packages.direct-sqlcipher.components.library.libs = pkgs.lib.mkForce [
pkgs.pkgsCross.mingwW64.openssl
];
packages.simplexmq.flags.client_library = true;
packages.simplexmq.components.library.libs = pkgs.lib.mkForce [
pkgs.pkgsCross.mingwW64.openssl
];
packages.unix-time.postPatch = ''
sed -i 's/mingwex//g' unix-time.cabal
'';
}];
}).simplex-chat.components.library
.override (p: {
# enableShared = false;
setupBuildFlags = p.component.setupBuildFlags ++ map (x: "--ghc-option=${x}") [
"-shared"
"-threaded"
"-o" "libsimplex.dll"
# "-optl-lHSrts_thr"
"-optl-lffi"
# "-optl-static-libgcc"
# We can't do -optl-static-libstdc++ with gcc. g++ might
# but then we are chaning the compiler altogether.
"${./libsimplex.dll.def}"
];
postInstall = ''
set -x
function deps() {
${pkgs.binutils}/bin/strings "$1" | grep '.\.dll'|grep -v -E 'Winsock|ADVAPI32|dbghelp|KERNEL32|msvcrt|ntdll|ole32|RPCRT4|SHELL32|USER32|WINMM|WS2_32|kernel32|GDI32'|grep -v "$1"
}
${pkgs.tree}/bin/tree $out
mkdir -p $out/_pkg
cp libsimplex.dll $out/_pkg
cp libsimplex.dll.a $out/_pkg
mkdir $out/libs
find ${pkgs.lib.getBin pkgs.pkgsCross.mingwW64.openssl} -name "*.dll" -exec cp {} $out/libs \;
find ${pkgs.lib.getBin pkgs.pkgsCross.mingwW64.libffi} -name "*.dll" -exec cp {} $out/libs \;
find ${pkgs.lib.getBin pkgs.pkgsCross.mingwW64.gmp} -name "*.dll" -exec cp {} $out/libs \;
find ${pkgs.lib.getBin pkgs.pkgsCross.mingwW64.stdenv.cc.cc} -name "*.dll" -exec cp {} $out/libs \;
find ${pkgs.lib.getBin pkgs.pkgsCross.mingwW64.windows.mcfgthreads} -name "*.dll" -exec cp {} $out/libs \;
pushd $out/_pkg
function copyDeps() {
for dep in $(deps "$1"); do
if [ ! -f "$dep" ]; then
if [ ! -f ../libs/"$dep" ]; then
echo "WARN: $1 -> $dep not found!"
else
cp ../libs/"$dep" .
copyDeps "$dep"
fi
fi
done
}
copyDeps libsimplex.dll
popd
${pkgs.tree}/bin/tree $out/_pkg
(cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-${pkgs.pkgsCross.mingwW64.hostPlatform.system}-libsimplex.zip *)
rm -fR $out/_pkg
mkdir -p $out/nix-support
echo "file binary-dist \"$(echo $out/*.zip)\"" \
> $out/nix-support/hydra-build-products
'';
});
# "${pkgs.pkgsCross.muslpi.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.muslpi).simplex-chat.components.exes.simplex-chat; # "${pkgs.pkgsCross.muslpi.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.muslpi).simplex-chat.components.exes.simplex-chat;
# STATIC aarch64-linux
"${pkgs.pkgsCross.aarch64-multiplatform-musl.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.aarch64-multiplatform-musl).simplex-chat.components.exes.simplex-chat; "${pkgs.pkgsCross.aarch64-multiplatform-musl.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.aarch64-multiplatform-musl).simplex-chat.components.exes.simplex-chat;
"armv7a-android:lib:support" = (drv android32Pkgs).android-support.components.library.override (p: { "armv7a-android:lib:support" = (drv android32Pkgs).android-support.components.library.override {
smallAddressSpace = true; smallAddressSpace = true; enableShared = false;
# we won't want -dyamic (see aarch64-android:lib:simplex-chat) setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsupport.so" ];
enableShared = false;
# we also do not want to have any dependencies listed (especially no rts!)
enableStatic = false;
# This used to work with 8.10.7...
# setupBuildFlags = p.component.setupBuildFlags ++ map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsupport.so" ];
# ... but now with 9.6+
# we have to do the -shared thing by hand.
postBuild = ''
armv7a-unknown-linux-androideabi-ghc -shared -o libsupport.so \
-optl-Wl,-u,setLineBuffering \
-optl-Wl,-u,pipe_std_to_socket \
dist/build/*.a
'';
postInstall = '' postInstall = ''
mkdir -p $out/_pkg mkdir -p $out/_pkg
@@ -293,29 +138,14 @@
echo "file binary-dist \"$(echo $out/*.zip)\"" \ echo "file binary-dist \"$(echo $out/*.zip)\"" \
> $out/nix-support/hydra-build-products > $out/nix-support/hydra-build-products
''; '';
}); };
# The android-support package is at "aarch64-android:lib:support" = (drv androidPkgs).android-support.components.library.override {
# https://github.com/simplex-chat/android-support smallAddressSpace = true; enableShared = false;
"aarch64-android:lib:support" = (drv androidPkgs).android-support.components.library.override (p: { setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsupport.so" ];
smallAddressSpace = true;
# no -dynamic
enableShared = false;
# but also no -staticlib
enableStatic = false;
# we have to do the -shared thing by hand.
postBuild = ''
aarch64-unknown-linux-android-ghc -shared -o libsupport.so \
-optl-Wl,-u,setLineBuffering \
-optl-Wl,-u,pipe_std_to_socket \
dist/build/*.a
'';
postInstall = '' postInstall = ''
mkdir -p $out/_pkg mkdir -p $out/_pkg
cp libsupport.so $out/_pkg cp libsupport.so $out/_pkg
ls -lah $out/_pkg/*
${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so.1 $out/_pkg/libsupport.so ${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so.1 $out/_pkg/libsupport.so
(cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-aarch64-android-libsupport.zip *) (cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-aarch64-android-libsupport.zip *)
rm -fR $out/_pkg rm -fR $out/_pkg
@@ -324,11 +154,10 @@
echo "file binary-dist \"$(echo $out/*.zip)\"" \ echo "file binary-dist \"$(echo $out/*.zip)\"" \
> $out/nix-support/hydra-build-products > $out/nix-support/hydra-build-products
''; '';
}); };
"armv7a-android:lib:simplex-chat" = (drv' { "armv7a-android:lib:simplex-chat" = (drv' {
pkgs' = android32Pkgs; pkgs' = android32Pkgs;
extra-modules = [{ extra-modules = [{
packages.text.flags.simdutf = false;
packages.direct-sqlcipher.flags.openssl = true; packages.direct-sqlcipher.flags.openssl = true;
packages.direct-sqlcipher.components.library.libs = pkgs.lib.mkForce [ packages.direct-sqlcipher.components.library.libs = pkgs.lib.mkForce [
(android32Pkgs.openssl.override { static = true; enableKTLS = false; }) (android32Pkgs.openssl.override { static = true; enableKTLS = false; })
@@ -340,56 +169,13 @@
packages.simplexmq.components.library.libs = pkgs.lib.mkForce [ packages.simplexmq.components.library.libs = pkgs.lib.mkForce [
(android32Pkgs.openssl.override { static = true; enableKTLS = false; }) (android32Pkgs.openssl.override { static = true; enableKTLS = false; })
]; ];
# 32 bit patches
packages.basement.patches = [
./scripts/nix/basement-pr-573.patch
];
packages.memory.patches = [
./scripts/nix/memory-pr-99.patch
];
}]; }];
}).simplex-chat.components.library.override (p: { }).simplex-chat.components.library.override {
smallAddressSpace = true; smallAddressSpace = true; enableShared = false;
# we want -shared, but not -dyanmic, hence `enableShared = false`.
enableShared = false;
# we _do_ want rts, and other libs. Hence `enableStatic = true`.
enableStatic = true;
# for android we build a shared library, passing these arguments is a bit tricky, as # for android we build a shared library, passing these arguments is a bit tricky, as
# we want only the threaded rts (HSrts_thr) and ffi to be linked, but not fed into iserv for # we want only the threaded rts (HSrts_thr) and ffi to be linked, but not fed into iserv for
# template haskell cross compilation. Thus we just pass them as linker options (-optl). # template haskell cross compilation. Thus we just pass them as linker options (-optl).
setupBuildFlags = p.component.setupBuildFlags setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsimplex.so" "-optl-lHSrts_thr" "-optl-lffi"];
# flags to tell GHC we want to produce a -shared object, and we want to also link
# - the ffi library (ffi)
++ map (x: "--ghc-option=${x}") [
"-shared" "-o" "libsimplex.so"
"-threaded"
# "-debug"
"-optl-lffi"
]
# This is fairly idiotic. LLD will strip out foreign exported
# symbols (a GHC bug? Codegen bug?). So we need to pass `-u <sym>`
# to ensure they stay in the produced library. Having them
# _undefined_ and _lazy_ (lld will tell with -y <sym> that the
# symbol is lazy), makes them _defined_. m(
++ map (sym: "--ghc-option=-optl-Wl,-u,${sym}") [
"chat_close_store"
"chat_decrypt_file"
"chat_decrypt_media"
"chat_encrypt_file"
"chat_encrypt_media"
"chat_migrate_init"
"chat_parse_markdown"
"chat_parse_server"
"chat_password_hash"
"chat_read_file"
"chat_recv_msg"
"chat_recv_msg_wait"
"chat_send_cmd"
"chat_send_remote_cmd"
"chat_valid_name"
"chat_json_length"
"chat_write_file"
];
postInstall = '' postInstall = ''
set -x set -x
${pkgs.tree}/bin/tree $out ${pkgs.tree}/bin/tree $out
@@ -433,11 +219,10 @@
echo "file binary-dist \"$(echo $out/*.zip)\"" \ echo "file binary-dist \"$(echo $out/*.zip)\"" \
> $out/nix-support/hydra-build-products > $out/nix-support/hydra-build-products
''; '';
}); };
"aarch64-android:lib:simplex-chat" = (drv' { "aarch64-android:lib:simplex-chat" = (drv' {
pkgs' = androidPkgs; pkgs' = androidPkgs;
extra-modules = [{ extra-modules = [{
packages.text.flags.simdutf = false;
packages.direct-sqlcipher.flags.openssl = true; packages.direct-sqlcipher.flags.openssl = true;
packages.direct-sqlcipher.components.library.libs = pkgs.lib.mkForce [ packages.direct-sqlcipher.components.library.libs = pkgs.lib.mkForce [
(androidPkgs.openssl.override { static = true; }) (androidPkgs.openssl.override { static = true; })
@@ -450,50 +235,12 @@
(androidPkgs.openssl.override { static = true; }) (androidPkgs.openssl.override { static = true; })
]; ];
}]; }];
}).simplex-chat.components.library.override (p: { }).simplex-chat.components.library.override {
smallAddressSpace = true; smallAddressSpace = true; enableShared = false;
# we do not want a dynamically linked object, even though we _do_
# want to produce a _shared_ object. But `shared` implied -dyanmic
# with cabal, so we disable and pass `-shared` explicitly.
enableShared = false;
# we do want static (e.g. pass all dependencies in, so we get -staticlib)
enableStatic = true;
# for android we build a shared library, passing these arguments is a bit tricky, as # for android we build a shared library, passing these arguments is a bit tricky, as
# we want only the threaded rts (HSrts_thr) and ffi to be linked, but not fed into iserv for # we want only the threaded rts (HSrts_thr) and ffi to be linked, but not fed into iserv for
# template haskell cross compilation. Thus we just pass them as linker options (-optl). # template haskell cross compilation. Thus we just pass them as linker options (-optl).
setupBuildFlags = p.component.setupBuildFlags setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsimplex.so" "-optl-lHSrts_thr" "-optl-lffi"];
# flags to tell GHC we want to produce a -shared object, and we want to also link
# - the ffi library (ffi)
++ map (x: "--ghc-option=${x}") [
"-shared" "-o" "libsimplex.so"
"-threaded"
# "-debug"
"-optl-lffi"
]
# This is fairly idiotic. LLD will strip out foreign exported
# symbols (a GHC bug? Codegen bug?). So we need to pass `-u <sym>`
# to ensure they stay in the produced library. Having them
# _undefined_ and _lazy_ (lld will tell with -y <sym> that the
# symbol is lazy), makes them _defined_. m(
++ map (sym: "--ghc-option=-optl-Wl,-u,${sym}") [
"chat_close_store"
"chat_decrypt_file"
"chat_decrypt_media"
"chat_encrypt_file"
"chat_encrypt_media"
"chat_migrate_init"
"chat_parse_markdown"
"chat_parse_server"
"chat_password_hash"
"chat_read_file"
"chat_recv_msg"
"chat_recv_msg_wait"
"chat_send_cmd"
"chat_send_remote_cmd"
"chat_valid_name"
"chat_json_length"
"chat_write_file"
];
postInstall = '' postInstall = ''
set -x set -x
${pkgs.tree}/bin/tree $out ${pkgs.tree}/bin/tree $out
@@ -537,7 +284,7 @@
echo "file binary-dist \"$(echo $out/*.zip)\"" \ echo "file binary-dist \"$(echo $out/*.zip)\"" \
> $out/nix-support/hydra-build-products > $out/nix-support/hydra-build-products
''; '';
}); };
}; };
# builds for iOS and iOS simulator # builds for iOS and iOS simulator
@@ -552,8 +299,7 @@
packages.entropy.flags.DoNotGetEntropy = true; packages.entropy.flags.DoNotGetEntropy = true;
packages.simplexmq.flags.client_library = true; packages.simplexmq.flags.client_library = true;
packages.simplexmq.components.library.libs = pkgs.lib.mkForce [ packages.simplexmq.components.library.libs = pkgs.lib.mkForce [
# TODO: have a cross override for iOS, that sets this. (pkgs.openssl.override { static = true; })
((pkgs.openssl.override { static = true; }).overrideDerivation (old: { CFLAGS = "-mcpu=apple-a7 -march=armv8-a+norcpc" ;}))
]; ];
}]; }];
}).simplex-chat.components.library.override ( }).simplex-chat.components.library.override (
-2
View File
@@ -29,7 +29,6 @@ dependencies:
- email-validate == 2.3.* - email-validate == 2.3.*
- exceptions == 0.10.* - exceptions == 0.10.*
- filepath == 1.4.* - filepath == 1.4.*
- file-embed == 0.0.15.*
- http-types == 0.12.* - http-types == 0.12.*
- http2 >= 4.2.2 && < 4.3 - http2 >= 4.2.2 && < 4.3
- memory == 0.18.* - memory == 0.18.*
@@ -39,7 +38,6 @@ dependencies:
- optparse-applicative >= 0.15 && < 0.17 - optparse-applicative >= 0.15 && < 0.17
- random >= 1.1 && < 1.3 - random >= 1.1 && < 1.3
- record-hasfield == 1.0.* - record-hasfield == 1.0.*
- scientific ==0.3.7.*
- simple-logger == 0.1.* - simple-logger == 0.1.*
- simplexmq >= 5.0 - simplexmq >= 5.0
- socks == 0.6.* - socks == 0.6.*
-10
View File
@@ -1,10 +0,0 @@
#!/bin/bash
security create-keychain -p "" simplex.keychain
security set-keychain-settings -u simplex.keychain
security add-certificates -k simplex.keychain "Developer ID Application: SimpleX Chat Ltd (5NN7GUYB6T).cer"
security add-certificates -k simplex.keychain "Developer ID Certification Authority.cer"
# Private key with access from any app
security import "SimpleX Chat.p12" -P "" -k simplex.keychain -A
# Public key
security import "SimpleX Chat.pem" -k simplex.keychain
@@ -2,7 +2,7 @@
set -e set -e
trap "rm apps/multiplatform/local.properties 2> /dev/null || true; rm local.properties 2> /dev/null || true; rm /tmp/simplex.keychain" EXIT trap "rm apps/multiplatform/local.properties || true; rm local.properties || true; rm /tmp/simplex.keychain || true" EXIT
echo "desktop.mac.signing.identity=Developer ID Application: SimpleX Chat Ltd (5NN7GUYB6T)" >> apps/multiplatform/local.properties echo "desktop.mac.signing.identity=Developer ID Application: SimpleX Chat Ltd (5NN7GUYB6T)" >> apps/multiplatform/local.properties
echo "desktop.mac.signing.keychain=/tmp/simplex.keychain" >> apps/multiplatform/local.properties echo "desktop.mac.signing.keychain=/tmp/simplex.keychain" >> apps/multiplatform/local.properties
echo "desktop.mac.notarization.apple_id=$APPLE_SIMPLEX_NOTARIZATION_APPLE_ID" >> apps/multiplatform/local.properties echo "desktop.mac.notarization.apple_id=$APPLE_SIMPLEX_NOTARIZATION_APPLE_ID" >> apps/multiplatform/local.properties
@@ -10,10 +10,6 @@ echo "desktop.mac.notarization.password=$APPLE_SIMPLEX_NOTARIZATION_PASSWORD" >>
echo "desktop.mac.notarization.team_id=5NN7GUYB6T" >> apps/multiplatform/local.properties echo "desktop.mac.notarization.team_id=5NN7GUYB6T" >> apps/multiplatform/local.properties
echo "$APPLE_SIMPLEX_SIGNING_KEYCHAIN" | base64 --decode -o /tmp/simplex.keychain echo "$APPLE_SIMPLEX_SIGNING_KEYCHAIN" | base64 --decode -o /tmp/simplex.keychain
security unlock-keychain -p "" /tmp/simplex.keychain
# Adding keychain to the list of keychains.
# Otherwise, it can find cert but exits while signing with "error: The specified item could not be found in the keychain."
security list-keychains -s `security list-keychains | xargs` /tmp/simplex.keychain
scripts/desktop/build-lib-mac.sh scripts/desktop/build-lib-mac.sh
cd apps/multiplatform cd apps/multiplatform
./gradlew packageDmg ./gradlew packageDmg
+2 -2
View File
@@ -8,7 +8,7 @@ function readlink() {
OS=linux OS=linux
ARCH=${1:-`uname -a | rev | cut -d' ' -f2 | rev`} ARCH=${1:-`uname -a | rev | cut -d' ' -f2 | rev`}
GHC_VERSION=9.6.3 GHC_VERSION=8.10.7
if [ "$ARCH" == "aarch64" ]; then if [ "$ARCH" == "aarch64" ]; then
COMPOSE_ARCH=arm64 COMPOSE_ARCH=arm64
@@ -25,7 +25,7 @@ for elem in "${exports[@]}"; do count=$(grep -R "$elem$" libsimplex.dll.def | wc
for elem in "${exports[@]}"; do count=$(grep -R "\"$elem\"" flake.nix | wc -l); if [ $count -ne 2 ]; then echo Wrong exports in flake.nix. Add \"$elem\" in two places of the file; exit 1; fi ; done for elem in "${exports[@]}"; do count=$(grep -R "\"$elem\"" flake.nix | wc -l); if [ $count -ne 2 ]; then echo Wrong exports in flake.nix. Add \"$elem\" in two places of the file; exit 1; fi ; done
rm -rf $BUILD_DIR rm -rf $BUILD_DIR
cabal build lib:simplex-chat --ghc-options='-optl-Wl,-rpath,$ORIGIN -flink-rts -threaded' --constraint 'simplexmq +client_library' cabal build lib:simplex-chat --ghc-options='-optl-Wl,-rpath,$ORIGIN' --ghc-options="-optl-L$(ghc --print-libdir)/rts -optl-Wl,--as-needed,-lHSrts_thr-ghc$GHC_VERSION" --constraint 'simplexmq +client_library'
cd $BUILD_DIR/build cd $BUILD_DIR/build
#patchelf --add-needed libHSrts_thr-ghc${GHC_VERSION}.so libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so #patchelf --add-needed libHSrts_thr-ghc${GHC_VERSION}.so libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so
#patchelf --add-rpath '$ORIGIN' libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so #patchelf --add-rpath '$ORIGIN' libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so
+3 -19
View File
@@ -5,14 +5,13 @@ set -e
OS=mac OS=mac
ARCH="${1:-`uname -a | rev | cut -d' ' -f1 | rev`}" ARCH="${1:-`uname -a | rev | cut -d' ' -f1 | rev`}"
COMPOSE_ARCH=$ARCH COMPOSE_ARCH=$ARCH
GHC_VERSION=9.6.3 GHC_VERSION=8.10.7
if [ "$ARCH" == "arm64" ]; then if [ "$ARCH" == "arm64" ]; then
ARCH=aarch64 ARCH=aarch64
else else
COMPOSE_ARCH=x64 COMPOSE_ARCH=x64
fi fi
LIB_EXT=dylib LIB_EXT=dylib
LIB=libHSsimplex-chat-*-inplace-ghc*.$LIB_EXT LIB=libHSsimplex-chat-*-inplace-ghc*.$LIB_EXT
GHC_LIBS_DIR=$(ghc --print-libdir) GHC_LIBS_DIR=$(ghc --print-libdir)
@@ -24,26 +23,13 @@ for elem in "${exports[@]}"; do count=$(grep -R "$elem$" libsimplex.dll.def | wc
for elem in "${exports[@]}"; do count=$(grep -R "\"$elem\"" flake.nix | wc -l); if [ $count -ne 2 ]; then echo Wrong exports in flake.nix. Add \"$elem\" in two places of the file; exit 1; fi ; done for elem in "${exports[@]}"; do count=$(grep -R "\"$elem\"" flake.nix | wc -l); if [ $count -ne 2 ]; then echo Wrong exports in flake.nix. Add \"$elem\" in two places of the file; exit 1; fi ; done
rm -rf $BUILD_DIR rm -rf $BUILD_DIR
cabal build lib:simplex-chat lib:simplex-chat --ghc-options="-optl-Wl,-rpath,@loader_path -optl-Wl,-L$GHC_LIBS_DIR/$ARCH-osx-ghc-$GHC_VERSION -optl-lHSrts_thr-ghc$GHC_VERSION -optl-lffi" --constraint 'simplexmq +client_library' cabal build lib:simplex-chat lib:simplex-chat --ghc-options="-optl-Wl,-rpath,@loader_path -optl-Wl,-L$GHC_LIBS_DIR/rts -optl-lHSrts_thr-ghc8.10.7 -optl-lffi" --constraint 'simplexmq +client_library'
cd $BUILD_DIR/build cd $BUILD_DIR/build
mkdir deps 2> /dev/null || true mkdir deps 2> /dev/null || true
# It's not included by default for some reason. Compiled lib tries to find system one but it's not always available # It's not included by default for some reason. Compiled lib tries to find system one but it's not always available
#cp $GHC_LIBS_DIR/libffi.dylib ./deps cp $GHC_LIBS_DIR/rts/libffi.dylib ./deps
(
BUILD=$PWD
cp /tmp/libffi-3.4.4/*-apple-darwin*/.libs/libffi.dylib $BUILD/deps || \
( \
cd /tmp && \
curl --tlsv1.2 "https://gitlab.haskell.org/ghc/libffi-tarballs/-/raw/libffi-3.4.4/libffi-3.4.4.tar.gz?inline=false" -o libffi.tar.gz && \
tar -xzvf libffi.tar.gz && \
cd "libffi-3.4.4" && \
./configure && \
make && \
cp *-apple-darwin*/.libs/libffi.dylib $BUILD/deps \
)
)
DYLIBS=`otool -L $LIB | grep @rpath | tail -n +2 | cut -d' ' -f 1 | cut -d'/' -f2` DYLIBS=`otool -L $LIB | grep @rpath | tail -n +2 | cut -d' ' -f 1 | cut -d'/' -f2`
RPATHS=`otool -l $LIB | grep "path "| cut -d' ' -f11` RPATHS=`otool -l $LIB | grep "path "| cut -d' ' -f11`
@@ -84,8 +70,6 @@ function copy_deps() {
} }
copy_deps $LIB copy_deps $LIB
# Special case
cp $(ghc --print-libdir)/$ARCH-osx-ghc-$GHC_VERSION/libHSghc-boot-th-$GHC_VERSION-ghc$GHC_VERSION.dylib deps
rm deps/`basename $LIB` rm deps/`basename $LIB`
cd - cd -
-242
View File
@@ -1,242 +0,0 @@
From 38be2c93acb6f459d24ed6c626981c35ccf44095 Mon Sep 17 00:00:00 2001
From: Sylvain Henry <sylvain@haskus.fr>
Date: Thu, 16 Feb 2023 15:40:45 +0100
Subject: [PATCH] Fix build on 32-bit architectures
---
Basement/Bits.hs | 4 ++++
Basement/From.hs | 24 -----------------------
Basement/Numerical/Additive.hs | 4 ++++
Basement/Numerical/Conversion.hs | 20 +++++++++++++++++++
Basement/PrimType.hs | 6 +++++-
Basement/Types/OffsetSize.hs | 22 +++++++++++++++++++--
6 files changed, 53 insertions(+), 27 deletions(-)
diff --git a/Basement/Bits.hs b/Basement/Bits.hs
index 7eeea0f5..24520ed7 100644
--- a/Basement/Bits.hs
+++ b/Basement/Bits.hs
@@ -54,8 +54,12 @@ import GHC.Int
import Basement.Compat.Primitive
#if WORD_SIZE_IN_BITS < 64
+#if __GLASGOW_HASKELL__ >= 904
+import GHC.Exts
+#else
import GHC.IntWord64
#endif
+#endif
-- | operation over finite bits
class FiniteBitsOps bits where
diff --git a/Basement/From.hs b/Basement/From.hs
index 7bbe141c..80014b3e 100644
--- a/Basement/From.hs
+++ b/Basement/From.hs
@@ -272,23 +272,11 @@ instance (NatWithinBound (CountOf ty) n, KnownNat n, PrimType ty)
tryFrom = BlockN.toBlockN . UArray.toBlock . BoxArray.mapToUnboxed id
instance (KnownNat n, NatWithinBound Word8 n) => From (Zn64 n) Word8 where
-#if __GLASGOW_HASKELL__ >= 904
- from = narrow . unZn64 where narrow (W64# w) = W8# (wordToWord8# (word64ToWord# (GHC.Prim.word64ToWord# w)))
-#else
from = narrow . unZn64 where narrow (W64# w) = W8# (wordToWord8# (word64ToWord# w))
-#endif
instance (KnownNat n, NatWithinBound Word16 n) => From (Zn64 n) Word16 where
-#if __GLASGOW_HASKELL__ >= 904
- from = narrow . unZn64 where narrow (W64# w) = W16# (wordToWord16# (word64ToWord# (GHC.Prim.word64ToWord# w)))
-#else
from = narrow . unZn64 where narrow (W64# w) = W16# (wordToWord16# (word64ToWord# w))
-#endif
instance (KnownNat n, NatWithinBound Word32 n) => From (Zn64 n) Word32 where
-#if __GLASGOW_HASKELL__ >= 904
- from = narrow . unZn64 where narrow (W64# w) = W32# (wordToWord32# (word64ToWord# (GHC.Prim.word64ToWord# w)))
-#else
from = narrow . unZn64 where narrow (W64# w) = W32# (wordToWord32# (word64ToWord# w))
-#endif
instance From (Zn64 n) Word64 where
from = unZn64
instance From (Zn64 n) Word128 where
@@ -297,23 +285,11 @@ instance From (Zn64 n) Word256 where
from = from . unZn64
instance (KnownNat n, NatWithinBound Word8 n) => From (Zn n) Word8 where
-#if __GLASGOW_HASKELL__ >= 904
- from = narrow . naturalToWord64 . unZn where narrow (W64# w) = W8# (wordToWord8# (word64ToWord# (GHC.Prim.word64ToWord# w)))
-#else
from = narrow . naturalToWord64 . unZn where narrow (W64# w) = W8# (wordToWord8# (word64ToWord# w))
-#endif
instance (KnownNat n, NatWithinBound Word16 n) => From (Zn n) Word16 where
-#if __GLASGOW_HASKELL__ >= 904
- from = narrow . naturalToWord64 . unZn where narrow (W64# w) = W16# (wordToWord16# (word64ToWord# (GHC.Prim.word64ToWord# w)))
-#else
from = narrow . naturalToWord64 . unZn where narrow (W64# w) = W16# (wordToWord16# (word64ToWord# w))
-#endif
instance (KnownNat n, NatWithinBound Word32 n) => From (Zn n) Word32 where
-#if __GLASGOW_HASKELL__ >= 904
- from = narrow . naturalToWord64 . unZn where narrow (W64# w) = W32# (wordToWord32# (word64ToWord# (GHC.Prim.word64ToWord# w)))
-#else
from = narrow . naturalToWord64 . unZn where narrow (W64# w) = W32# (wordToWord32# (word64ToWord# w))
-#endif
instance (KnownNat n, NatWithinBound Word64 n) => From (Zn n) Word64 where
from = naturalToWord64 . unZn
instance (KnownNat n, NatWithinBound Word128 n) => From (Zn n) Word128 where
diff --git a/Basement/Numerical/Additive.hs b/Basement/Numerical/Additive.hs
index d0dfb973..8ab65aa0 100644
--- a/Basement/Numerical/Additive.hs
+++ b/Basement/Numerical/Additive.hs
@@ -30,8 +30,12 @@ import qualified Basement.Types.Word128 as Word128
import qualified Basement.Types.Word256 as Word256
#if WORD_SIZE_IN_BITS < 64
+#if __GLASGOW_HASKELL__ >= 904
+import GHC.Exts
+#else
import GHC.IntWord64
#endif
+#endif
-- | Represent class of things that can be added together,
-- contains a neutral element and is commutative.
diff --git a/Basement/Numerical/Conversion.hs b/Basement/Numerical/Conversion.hs
index db502c07..fddc8232 100644
--- a/Basement/Numerical/Conversion.hs
+++ b/Basement/Numerical/Conversion.hs
@@ -26,8 +26,12 @@ import GHC.Word
import Basement.Compat.Primitive
#if WORD_SIZE_IN_BITS < 64
+#if __GLASGOW_HASKELL__ >= 904
+import GHC.Exts
+#else
import GHC.IntWord64
#endif
+#endif
intToInt64 :: Int -> Int64
#if WORD_SIZE_IN_BITS == 64
@@ -96,11 +100,22 @@ int64ToWord64 (I64# i) = W64# (int64ToWord64# i)
#endif
#if WORD_SIZE_IN_BITS == 64
+#if __GLASGOW_HASKELL__ >= 904
+word64ToWord# :: Word64# -> Word#
+word64ToWord# i = word64ToWord# i
+#else
word64ToWord# :: Word# -> Word#
word64ToWord# i = i
+#endif
{-# INLINE word64ToWord# #-}
#endif
+#if WORD_SIZE_IN_BITS < 64
+word64ToWord32# :: Word64# -> Word32#
+word64ToWord32# i = wordToWord32# (word64ToWord# i)
+{-# INLINE word64ToWord32# #-}
+#endif
+
-- | 2 Word32s
data Word32x2 = Word32x2 {-# UNPACK #-} !Word32
{-# UNPACK #-} !Word32
@@ -113,9 +128,14 @@ word64ToWord32s (W64# w64) = Word32x2 (W32# (wordToWord32# (uncheckedShiftRL# (G
word64ToWord32s (W64# w64) = Word32x2 (W32# (wordToWord32# (uncheckedShiftRL# w64 32#))) (W32# (wordToWord32# w64))
#endif
#else
+#if __GLASGOW_HASKELL__ >= 904
+word64ToWord32s :: Word64 -> Word32x2
+word64ToWord32s (W64# w64) = Word32x2 (W32# (word64ToWord32# (uncheckedShiftRL64# w64 32#))) (W32# (word64ToWord32# w64))
+#else
word64ToWord32s :: Word64 -> Word32x2
word64ToWord32s (W64# w64) = Word32x2 (W32# (word64ToWord# (uncheckedShiftRL64# w64 32#))) (W32# (word64ToWord# w64))
#endif
+#endif
wordToChar :: Word -> Char
wordToChar (W# word) = C# (chr# (word2Int# word))
diff --git a/Basement/PrimType.hs b/Basement/PrimType.hs
index f8ca2926..a888ec91 100644
--- a/Basement/PrimType.hs
+++ b/Basement/PrimType.hs
@@ -54,7 +54,11 @@ import Basement.Nat
import qualified Prelude (quot)
#if WORD_SIZE_IN_BITS < 64
-import GHC.IntWord64
+#if __GLASGOW_HASKELL__ >= 904
+import GHC.Exts
+#else
+import GHC.IntWord64
+#endif
#endif
#ifdef FOUNDATION_BOUNDS_CHECK
diff --git a/Basement/Types/OffsetSize.hs b/Basement/Types/OffsetSize.hs
index cd944927..1ea80dad 100644
--- a/Basement/Types/OffsetSize.hs
+++ b/Basement/Types/OffsetSize.hs
@@ -70,8 +70,12 @@ import Data.List (foldl')
import qualified Prelude
#if WORD_SIZE_IN_BITS < 64
+#if __GLASGOW_HASKELL__ >= 904
+import GHC.Exts
+#else
import GHC.IntWord64
#endif
+#endif
-- | File size in bytes
newtype FileSize = FileSize Word64
@@ -225,20 +229,26 @@ countOfRoundUp alignment (CountOf n) = CountOf ((n + (alignment-1)) .&. compleme
csizeOfSize :: CountOf Word8 -> CSize
#if WORD_SIZE_IN_BITS < 64
+#if __GLASGOW_HASKELL__ >= 904
+csizeOfSize (CountOf (I# sz)) = CSize (W32# (wordToWord32# (int2Word# sz)))
+#else
csizeOfSize (CountOf (I# sz)) = CSize (W32# (int2Word# sz))
+#endif
#else
#if __GLASGOW_HASKELL__ >= 904
csizeOfSize (CountOf (I# sz)) = CSize (W64# (wordToWord64# (int2Word# sz)))
-
#else
csizeOfSize (CountOf (I# sz)) = CSize (W64# (int2Word# sz))
-
#endif
#endif
csizeOfOffset :: Offset8 -> CSize
#if WORD_SIZE_IN_BITS < 64
+#if __GLASGOW_HASKELL__ >= 904
+csizeOfOffset (Offset (I# sz)) = CSize (W32# (wordToWord32# (int2Word# sz)))
+#else
csizeOfOffset (Offset (I# sz)) = CSize (W32# (int2Word# sz))
+#endif
#else
#if __GLASGOW_HASKELL__ >= 904
csizeOfOffset (Offset (I# sz)) = CSize (W64# (wordToWord64# (int2Word# sz)))
@@ -250,7 +260,11 @@ csizeOfOffset (Offset (I# sz)) = CSize (W64# (int2Word# sz))
sizeOfCSSize :: CSsize -> CountOf Word8
sizeOfCSSize (CSsize (-1)) = error "invalid size: CSSize is -1"
#if WORD_SIZE_IN_BITS < 64
+#if __GLASGOW_HASKELL__ >= 904
+sizeOfCSSize (CSsize (I32# sz)) = CountOf (I# (int32ToInt# sz))
+#else
sizeOfCSSize (CSsize (I32# sz)) = CountOf (I# sz)
+#endif
#else
#if __GLASGOW_HASKELL__ >= 904
sizeOfCSSize (CSsize (I64# sz)) = CountOf (I# (int64ToInt# sz))
@@ -261,7 +275,11 @@ sizeOfCSSize (CSsize (I64# sz)) = CountOf (I# sz)
sizeOfCSize :: CSize -> CountOf Word8
#if WORD_SIZE_IN_BITS < 64
+#if __GLASGOW_HASKELL__ >= 904
+sizeOfCSize (CSize (W32# sz)) = CountOf (I# (word2Int# (word32ToWord# sz)))
+#else
sizeOfCSize (CSize (W32# sz)) = CountOf (I# (word2Int# sz))
+#endif
#else
#if __GLASGOW_HASKELL__ >= 904
sizeOfCSize (CSize (W64# sz)) = CountOf (I# (word2Int# (word64ToWord# sz)))
@@ -1,12 +0,0 @@
diff --git a/direct-sqlcipher.cabal b/direct-sqlcipher.cabal
index 728ba3e..c63745e 100644
--- a/direct-sqlcipher.cabal
+++ b/direct-sqlcipher.cabal
@@ -84,6 +84,8 @@ library
cc-options: -DSQLITE_TEMP_STORE=2
-DSQLITE_HAS_CODEC
+ extra-libraries: ws2_32
+
if !os(windows) && !os(android)
extra-libraries: pthread
-36
View File
@@ -1,36 +0,0 @@
From 2738929ce15b4c8704bbbac24a08539b5d4bf30e Mon Sep 17 00:00:00 2001
From: sternenseemann <sternenseemann@systemli.org>
Date: Mon, 14 Aug 2023 10:51:30 +0200
Subject: [PATCH] Data.Memory.Internal.CompatPrim64: fix 32 bit with GHC >= 9.4
Since 9.4, GHC.Prim exports Word64# operations like timesWord64# even on
i686 whereas GHC.IntWord64 no longer exists. Therefore, we can just use
the ready made solution.
Closes #98, as it should be the better solution.
---
Data/Memory/Internal/CompatPrim64.hs | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Data/Memory/Internal/CompatPrim64.hs b/Data/Memory/Internal/CompatPrim64.hs
index b9eef8a..a134c88 100644
--- a/Data/Memory/Internal/CompatPrim64.hs
+++ b/Data/Memory/Internal/CompatPrim64.hs
@@ -150,6 +150,7 @@ w64# :: Word# -> Word# -> Word# -> Word64#
w64# w _ _ = w
#elif WORD_SIZE_IN_BITS == 32
+#if __GLASGOW_HASKELL__ < 904
import GHC.IntWord64
import GHC.Prim (Word#)
@@ -158,6 +159,9 @@ timesWord64# a b =
let !ai = word64ToInt64# a
!bi = word64ToInt64# b
in int64ToWord64# (timesInt64# ai bi)
+#else
+import GHC.Prim
+#endif
w64# :: Word# -> Word# -> Word# -> Word64#
w64# _ hw lw =
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"https://github.com/simplex-chat/simplexmq.git"."93f30c8edf9243ad2291dd6427d87328e282560a" = "1zf0sp9dy6kz4zvyz6mdgmhydps7khcq84n30irp983w1xh7gzs7"; "https://github.com/simplex-chat/simplexmq.git"."8a9d4f2af768d5d6a2b60c00d507529242f9b984" = "0kb8hq37fc5g198wq7dswnlwjzk67q8rrzil2dii5lc6xfr47jbs";
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "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/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d";
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl"; "https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
-17
View File
@@ -150,13 +150,10 @@ library
Simplex.Chat.Migrations.M20240920_user_order Simplex.Chat.Migrations.M20240920_user_order
Simplex.Chat.Migrations.M20241008_indexes Simplex.Chat.Migrations.M20241008_indexes
Simplex.Chat.Migrations.M20241010_contact_requests_contact_id Simplex.Chat.Migrations.M20241010_contact_requests_contact_id
Simplex.Chat.Migrations.M20241027_server_operators
Simplex.Chat.Mobile Simplex.Chat.Mobile
Simplex.Chat.Mobile.File Simplex.Chat.Mobile.File
Simplex.Chat.Mobile.Shared Simplex.Chat.Mobile.Shared
Simplex.Chat.Mobile.WebRTC Simplex.Chat.Mobile.WebRTC
Simplex.Chat.Operators
Simplex.Chat.Operators.Conditions
Simplex.Chat.Options Simplex.Chat.Options
Simplex.Chat.ProfileGenerator Simplex.Chat.ProfileGenerator
Simplex.Chat.Protocol Simplex.Chat.Protocol
@@ -216,7 +213,6 @@ library
, directory ==1.3.* , directory ==1.3.*
, email-validate ==2.3.* , email-validate ==2.3.*
, exceptions ==0.10.* , exceptions ==0.10.*
, file-embed ==0.0.15.*
, filepath ==1.4.* , filepath ==1.4.*
, http-types ==0.12.* , http-types ==0.12.*
, http2 >=4.2.2 && <4.3 , http2 >=4.2.2 && <4.3
@@ -227,7 +223,6 @@ library
, optparse-applicative >=0.15 && <0.17 , optparse-applicative >=0.15 && <0.17
, random >=1.1 && <1.3 , random >=1.1 && <1.3
, record-hasfield ==1.0.* , record-hasfield ==1.0.*
, scientific ==0.3.7.*
, simple-logger ==0.1.* , simple-logger ==0.1.*
, simplexmq >=5.0 , simplexmq >=5.0
, socks ==0.6.* , socks ==0.6.*
@@ -281,7 +276,6 @@ executable simplex-bot
, directory ==1.3.* , directory ==1.3.*
, email-validate ==2.3.* , email-validate ==2.3.*
, exceptions ==0.10.* , exceptions ==0.10.*
, file-embed ==0.0.15.*
, filepath ==1.4.* , filepath ==1.4.*
, http-types ==0.12.* , http-types ==0.12.*
, http2 >=4.2.2 && <4.3 , http2 >=4.2.2 && <4.3
@@ -292,7 +286,6 @@ executable simplex-bot
, optparse-applicative >=0.15 && <0.17 , optparse-applicative >=0.15 && <0.17
, random >=1.1 && <1.3 , random >=1.1 && <1.3
, record-hasfield ==1.0.* , record-hasfield ==1.0.*
, scientific ==0.3.7.*
, simple-logger ==0.1.* , simple-logger ==0.1.*
, simplex-chat , simplex-chat
, simplexmq >=5.0 , simplexmq >=5.0
@@ -347,7 +340,6 @@ executable simplex-bot-advanced
, directory ==1.3.* , directory ==1.3.*
, email-validate ==2.3.* , email-validate ==2.3.*
, exceptions ==0.10.* , exceptions ==0.10.*
, file-embed ==0.0.15.*
, filepath ==1.4.* , filepath ==1.4.*
, http-types ==0.12.* , http-types ==0.12.*
, http2 >=4.2.2 && <4.3 , http2 >=4.2.2 && <4.3
@@ -358,7 +350,6 @@ executable simplex-bot-advanced
, optparse-applicative >=0.15 && <0.17 , optparse-applicative >=0.15 && <0.17
, random >=1.1 && <1.3 , random >=1.1 && <1.3
, record-hasfield ==1.0.* , record-hasfield ==1.0.*
, scientific ==0.3.7.*
, simple-logger ==0.1.* , simple-logger ==0.1.*
, simplex-chat , simplex-chat
, simplexmq >=5.0 , simplexmq >=5.0
@@ -416,7 +407,6 @@ executable simplex-broadcast-bot
, directory ==1.3.* , directory ==1.3.*
, email-validate ==2.3.* , email-validate ==2.3.*
, exceptions ==0.10.* , exceptions ==0.10.*
, file-embed ==0.0.15.*
, filepath ==1.4.* , filepath ==1.4.*
, http-types ==0.12.* , http-types ==0.12.*
, http2 >=4.2.2 && <4.3 , http2 >=4.2.2 && <4.3
@@ -427,7 +417,6 @@ executable simplex-broadcast-bot
, optparse-applicative >=0.15 && <0.17 , optparse-applicative >=0.15 && <0.17
, random >=1.1 && <1.3 , random >=1.1 && <1.3
, record-hasfield ==1.0.* , record-hasfield ==1.0.*
, scientific ==0.3.7.*
, simple-logger ==0.1.* , simple-logger ==0.1.*
, simplex-chat , simplex-chat
, simplexmq >=5.0 , simplexmq >=5.0
@@ -483,7 +472,6 @@ executable simplex-chat
, directory ==1.3.* , directory ==1.3.*
, email-validate ==2.3.* , email-validate ==2.3.*
, exceptions ==0.10.* , exceptions ==0.10.*
, file-embed ==0.0.15.*
, filepath ==1.4.* , filepath ==1.4.*
, http-types ==0.12.* , http-types ==0.12.*
, http2 >=4.2.2 && <4.3 , http2 >=4.2.2 && <4.3
@@ -494,7 +482,6 @@ executable simplex-chat
, optparse-applicative >=0.15 && <0.17 , optparse-applicative >=0.15 && <0.17
, random >=1.1 && <1.3 , random >=1.1 && <1.3
, record-hasfield ==1.0.* , record-hasfield ==1.0.*
, scientific ==0.3.7.*
, simple-logger ==0.1.* , simple-logger ==0.1.*
, simplex-chat , simplex-chat
, simplexmq >=5.0 , simplexmq >=5.0
@@ -556,7 +543,6 @@ executable simplex-directory-service
, directory ==1.3.* , directory ==1.3.*
, email-validate ==2.3.* , email-validate ==2.3.*
, exceptions ==0.10.* , exceptions ==0.10.*
, file-embed ==0.0.15.*
, filepath ==1.4.* , filepath ==1.4.*
, http-types ==0.12.* , http-types ==0.12.*
, http2 >=4.2.2 && <4.3 , http2 >=4.2.2 && <4.3
@@ -567,7 +553,6 @@ executable simplex-directory-service
, optparse-applicative >=0.15 && <0.17 , optparse-applicative >=0.15 && <0.17
, random >=1.1 && <1.3 , random >=1.1 && <1.3
, record-hasfield ==1.0.* , record-hasfield ==1.0.*
, scientific ==0.3.7.*
, simple-logger ==0.1.* , simple-logger ==0.1.*
, simplex-chat , simplex-chat
, simplexmq >=5.0 , simplexmq >=5.0
@@ -657,7 +642,6 @@ test-suite simplex-chat-test
, directory ==1.3.* , directory ==1.3.*
, email-validate ==2.3.* , email-validate ==2.3.*
, exceptions ==0.10.* , exceptions ==0.10.*
, file-embed ==0.0.15.*
, filepath ==1.4.* , filepath ==1.4.*
, generic-random ==1.5.* , generic-random ==1.5.*
, http-types ==0.12.* , http-types ==0.12.*
@@ -669,7 +653,6 @@ test-suite simplex-chat-test
, optparse-applicative >=0.15 && <0.17 , optparse-applicative >=0.15 && <0.17
, random >=1.1 && <1.3 , random >=1.1 && <1.3
, record-hasfield ==1.0.* , record-hasfield ==1.0.*
, scientific ==0.3.7.*
, silently ==1.2.* , silently ==1.2.*
, simple-logger ==0.1.* , simple-logger ==0.1.*
, simplex-chat , simplex-chat
+141 -301
View File
@@ -6,7 +6,6 @@
{-# LANGUAGE LambdaCase #-} {-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
@@ -44,7 +43,7 @@ import Data.Functor (($>))
import Data.Functor.Identity import Data.Functor.Identity
import Data.Int (Int64) import Data.Int (Int64)
import Data.List (find, foldl', isSuffixOf, mapAccumL, partition, sortOn, zipWith4) import Data.List (find, foldl', isSuffixOf, mapAccumL, partition, sortOn, zipWith4)
import Data.List.NonEmpty (NonEmpty (..), (<|)) import Data.List.NonEmpty (NonEmpty (..), nonEmpty, toList, (<|))
import qualified Data.List.NonEmpty as L import qualified Data.List.NonEmpty as L
import Data.Map.Strict (Map) import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M import qualified Data.Map.Strict as M
@@ -68,7 +67,6 @@ import Simplex.Chat.Messages
import Simplex.Chat.Messages.Batch (MsgBatch (..), batchMessages) import Simplex.Chat.Messages.Batch (MsgBatch (..), batchMessages)
import Simplex.Chat.Messages.CIContent import Simplex.Chat.Messages.CIContent
import Simplex.Chat.Messages.CIContent.Events import Simplex.Chat.Messages.CIContent.Events
import Simplex.Chat.Operators
import Simplex.Chat.Options import Simplex.Chat.Options
import Simplex.Chat.ProfileGenerator (generateRandomProfile) import Simplex.Chat.ProfileGenerator (generateRandomProfile)
import Simplex.Chat.Protocol import Simplex.Chat.Protocol
@@ -99,7 +97,7 @@ import qualified Simplex.FileTransfer.Transport as XFTP
import Simplex.FileTransfer.Types (FileErrorType (..), RcvFileId, SndFileId) import Simplex.FileTransfer.Types (FileErrorType (..), RcvFileId, SndFileId)
import Simplex.Messaging.Agent as Agent import Simplex.Messaging.Agent as Agent
import Simplex.Messaging.Agent.Client (SubInfo (..), agentClientStore, getAgentQueuesInfo, getAgentWorkersDetails, getAgentWorkersSummary, getFastNetworkConfig, ipAddressProtected, withLockMap) import Simplex.Messaging.Agent.Client (SubInfo (..), agentClientStore, getAgentQueuesInfo, getAgentWorkersDetails, getAgentWorkersSummary, getFastNetworkConfig, ipAddressProtected, withLockMap)
import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), InitialAgentServers (..), ServerCfg (..), ServerRoles (..), allRoles, createAgentStore, defaultAgentConfig) import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), InitialAgentServers (..), ServerCfg (..), createAgentStore, defaultAgentConfig, enabledServerCfg, presetServerCfg)
import Simplex.Messaging.Agent.Lock (withLock) import Simplex.Messaging.Agent.Lock (withLock)
import Simplex.Messaging.Agent.Protocol import Simplex.Messaging.Agent.Protocol
import qualified Simplex.Messaging.Agent.Protocol as AP (AgentErrorType (..)) import qualified Simplex.Messaging.Agent.Protocol as AP (AgentErrorType (..))
@@ -139,32 +137,6 @@ import qualified UnliftIO.Exception as E
import UnliftIO.IO (hClose, hSeek, hTell, openFile) import UnliftIO.IO (hClose, hSeek, hTell, openFile)
import UnliftIO.STM import UnliftIO.STM
operatorSimpleXChat :: NewServerOperator
operatorSimpleXChat =
ServerOperator
{ operatorId = DBNewEntity,
operatorTag = Just OTSimplex,
tradeName = "SimpleX Chat",
legalName = Just "SimpleX Chat Ltd",
serverDomains = ["simplex.im"],
conditionsAcceptance = CARequired Nothing,
enabled = True,
roles = allRoles
}
operatorXYZ :: NewServerOperator
operatorXYZ =
ServerOperator
{ operatorId = DBNewEntity,
operatorTag = Just OTXyz,
tradeName = "XYZ",
legalName = Just "XYZ Ltd",
serverDomains = ["xyz.com"],
conditionsAcceptance = CARequired Nothing,
enabled = False,
roles = ServerRoles {storage = False, proxy = True}
}
defaultChatConfig :: ChatConfig defaultChatConfig :: ChatConfig
defaultChatConfig = defaultChatConfig =
ChatConfig ChatConfig
@@ -175,25 +147,13 @@ defaultChatConfig =
}, },
chatVRange = supportedChatVRange, chatVRange = supportedChatVRange,
confirmMigrations = MCConsole, confirmMigrations = MCConsole,
presetServers = defaultServers =
PresetServers DefaultAgentServers
{ operators = { smp = _defaultSMPServers,
[ PresetOperator useSMP = 4,
{ operator = Just operatorSimpleXChat,
smp = simplexChatSMPServers,
useSMP = 4,
xftp = map (presetServer True) $ L.toList defaultXFTPServers,
useXFTP = 3
},
PresetOperator
{ operator = Just operatorXYZ,
smp = xyzSMPServers,
useSMP = 3,
xftp = xyzXFTPServers,
useXFTP = 3
}
],
ntf = _defaultNtfServers, ntf = _defaultNtfServers,
xftp = L.map (presetServerCfg True) defaultXFTPServers,
useXFTP = L.length defaultXFTPServers,
netCfg = defaultNetworkConfig netCfg = defaultNetworkConfig
}, },
tbqSize = 1024, tbqSize = 1024,
@@ -217,52 +177,29 @@ defaultChatConfig =
chatHooks = defaultChatHooks chatHooks = defaultChatHooks
} }
simplexChatSMPServers :: [NewUserServer 'PSMP] _defaultSMPServers :: NonEmpty (ServerCfg 'PSMP)
simplexChatSMPServers = _defaultSMPServers =
map L.fromList $
(presetServer True) map
[ "smp://0YuTwO05YJWS8rkjn9eLJDjQhFKvIYd8d4xG8X1blIU=@smp8.simplex.im,beccx4yfxxbvyhqypaavemqurytl6hozr47wfc7uuecacjqdvwpw2xid.onion", (presetServerCfg True)
"smp://SkIkI6EPd2D63F4xFKfHk7I1UGZVNn6k1QWZ5rcyr6w=@smp9.simplex.im,jssqzccmrcws6bhmn77vgmhfjmhwlyr3u7puw4erkyoosywgl67slqqd.onion", [ "smp://0YuTwO05YJWS8rkjn9eLJDjQhFKvIYd8d4xG8X1blIU=@smp8.simplex.im,beccx4yfxxbvyhqypaavemqurytl6hozr47wfc7uuecacjqdvwpw2xid.onion",
"smp://6iIcWT_dF2zN_w5xzZEY7HI2Prbh3ldP07YTyDexPjE=@smp10.simplex.im,rb2pbttocvnbrngnwziclp2f4ckjq65kebafws6g4hy22cdaiv5dwjqd.onion", "smp://SkIkI6EPd2D63F4xFKfHk7I1UGZVNn6k1QWZ5rcyr6w=@smp9.simplex.im,jssqzccmrcws6bhmn77vgmhfjmhwlyr3u7puw4erkyoosywgl67slqqd.onion",
"smp://1OwYGt-yqOfe2IyVHhxz3ohqo3aCCMjtB-8wn4X_aoY=@smp11.simplex.im,6ioorbm6i3yxmuoezrhjk6f6qgkc4syabh7m3so74xunb5nzr4pwgfqd.onion", "smp://6iIcWT_dF2zN_w5xzZEY7HI2Prbh3ldP07YTyDexPjE=@smp10.simplex.im,rb2pbttocvnbrngnwziclp2f4ckjq65kebafws6g4hy22cdaiv5dwjqd.onion",
"smp://UkMFNAXLXeAAe0beCa4w6X_zp18PwxSaSjY17BKUGXQ=@smp12.simplex.im,ie42b5weq7zdkghocs3mgxdjeuycheeqqmksntj57rmejagmg4eor5yd.onion", "smp://1OwYGt-yqOfe2IyVHhxz3ohqo3aCCMjtB-8wn4X_aoY=@smp11.simplex.im,6ioorbm6i3yxmuoezrhjk6f6qgkc4syabh7m3so74xunb5nzr4pwgfqd.onion",
"smp://enEkec4hlR3UtKx2NMpOUK_K4ZuDxjWBO1d9Y4YXVaA=@smp14.simplex.im,aspkyu2sopsnizbyfabtsicikr2s4r3ti35jogbcekhm3fsoeyjvgrid.onion", "smp://UkMFNAXLXeAAe0beCa4w6X_zp18PwxSaSjY17BKUGXQ=@smp12.simplex.im,ie42b5weq7zdkghocs3mgxdjeuycheeqqmksntj57rmejagmg4eor5yd.onion",
"smp://h--vW7ZSkXPeOUpfxlFGgauQmXNFOzGoizak7Ult7cw=@smp15.simplex.im,oauu4bgijybyhczbnxtlggo6hiubahmeutaqineuyy23aojpih3dajad.onion", "smp://enEkec4hlR3UtKx2NMpOUK_K4ZuDxjWBO1d9Y4YXVaA=@smp14.simplex.im,aspkyu2sopsnizbyfabtsicikr2s4r3ti35jogbcekhm3fsoeyjvgrid.onion",
"smp://hejn2gVIqNU6xjtGM3OwQeuk8ZEbDXVJXAlnSBJBWUA=@smp16.simplex.im,p3ktngodzi6qrf7w64mmde3syuzrv57y55hxabqcq3l5p6oi7yzze6qd.onion", "smp://h--vW7ZSkXPeOUpfxlFGgauQmXNFOzGoizak7Ult7cw=@smp15.simplex.im,oauu4bgijybyhczbnxtlggo6hiubahmeutaqineuyy23aojpih3dajad.onion",
"smp://ZKe4uxF4Z_aLJJOEsC-Y6hSkXgQS5-oc442JQGkyP8M=@smp17.simplex.im,ogtwfxyi3h2h5weftjjpjmxclhb5ugufa5rcyrmg7j4xlch7qsr5nuqd.onion", "smp://hejn2gVIqNU6xjtGM3OwQeuk8ZEbDXVJXAlnSBJBWUA=@smp16.simplex.im,p3ktngodzi6qrf7w64mmde3syuzrv57y55hxabqcq3l5p6oi7yzze6qd.onion",
"smp://PtsqghzQKU83kYTlQ1VKg996dW4Cw4x_bvpKmiv8uns=@smp18.simplex.im,lyqpnwbs2zqfr45jqkncwpywpbtq7jrhxnib5qddtr6npjyezuwd3nqd.onion", "smp://ZKe4uxF4Z_aLJJOEsC-Y6hSkXgQS5-oc442JQGkyP8M=@smp17.simplex.im,ogtwfxyi3h2h5weftjjpjmxclhb5ugufa5rcyrmg7j4xlch7qsr5nuqd.onion",
"smp://N_McQS3F9TGoh4ER0QstUf55kGnNSd-wXfNPZ7HukcM=@smp19.simplex.im,i53bbtoqhlc365k6kxzwdp5w3cdt433s7bwh3y32rcbml2vztiyyz5id.onion" "smp://PtsqghzQKU83kYTlQ1VKg996dW4Cw4x_bvpKmiv8uns=@smp18.simplex.im,lyqpnwbs2zqfr45jqkncwpywpbtq7jrhxnib5qddtr6npjyezuwd3nqd.onion",
] "smp://N_McQS3F9TGoh4ER0QstUf55kGnNSd-wXfNPZ7HukcM=@smp19.simplex.im,i53bbtoqhlc365k6kxzwdp5w3cdt433s7bwh3y32rcbml2vztiyyz5id.onion"
<> map
(presetServer False)
[ "smp://u2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU=@smp4.simplex.im,o5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion",
"smp://hpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg=@smp5.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion",
"smp://PQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo=@smp6.simplex.im,bylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion"
] ]
<> map
xyzSMPServers :: [NewUserServer 'PSMP] (presetServerCfg False)
xyzSMPServers = [ "smp://u2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU=@smp4.simplex.im,o5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion",
map "smp://hpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg=@smp5.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion",
(presetServer True) "smp://PQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo=@smp6.simplex.im,bylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion"
[ "smp://abcd@smp1.xyz.com", ]
"smp://abcd@smp2.xyz.com",
"smp://abcd@smp3.xyz.com",
"smp://abcd@smp4.xyz.com",
"smp://abcd@smp5.xyz.com",
"smp://abcd@smp6.xyz.com"
]
xyzXFTPServers :: [NewUserServer 'PXFTP]
xyzXFTPServers =
map
(presetServer True)
[ "xftp://abcd@xftp1.xyz.com",
"xftp://abcd@xftp2.xyz.com",
"xftp://abcd@xftp3.xyz.com",
"xftp://abcd@xftp4.xyz.com",
"xftp://abcd@xftp5.xyz.com",
"xftp://abcd@xftp6.xyz.com"
]
_defaultNtfServers :: [NtfServer] _defaultNtfServers :: [NtfServer]
_defaultNtfServers = _defaultNtfServers =
@@ -299,19 +236,16 @@ newChatController :: ChatDatabase -> Maybe User -> ChatConfig -> ChatOpts -> Boo
newChatController newChatController
ChatDatabase {chatStore, agentStore} ChatDatabase {chatStore, agentStore}
user user
cfg@ChatConfig {agentConfig = aCfg, presetServers, inlineFiles, deviceNameForRemote, confirmMigrations} cfg@ChatConfig {agentConfig = aCfg, defaultServers, inlineFiles, deviceNameForRemote, confirmMigrations}
ChatOpts {coreOptions = CoreChatOpts {smpServers, xftpServers, simpleNetCfg, logLevel, logConnections, logServerHosts, logFile, tbqSize, highlyAvailable, yesToUpMigrations}, deviceName, optFilesFolder, optTempDirectory, showReactions, allowInstantFiles, autoAcceptFileSize} ChatOpts {coreOptions = CoreChatOpts {smpServers, xftpServers, simpleNetCfg, logLevel, logConnections, logServerHosts, logFile, tbqSize, highlyAvailable, yesToUpMigrations}, deviceName, optFilesFolder, optTempDirectory, showReactions, allowInstantFiles, autoAcceptFileSize}
backgroundMode = do backgroundMode = do
let inlineFiles' = if allowInstantFiles || autoAcceptFileSize > 0 then inlineFiles else inlineFiles {sendChunks = 0, receiveInstant = False} let inlineFiles' = if allowInstantFiles || autoAcceptFileSize > 0 then inlineFiles else inlineFiles {sendChunks = 0, receiveInstant = False}
confirmMigrations' = if confirmMigrations == MCConsole && yesToUpMigrations then MCYesUp else confirmMigrations confirmMigrations' = if confirmMigrations == MCConsole && yesToUpMigrations then MCYesUp else confirmMigrations
config = cfg {logLevel, showReactions, tbqSize, subscriptionEvents = logConnections, hostEvents = logServerHosts, presetServers = presetServers', inlineFiles = inlineFiles', autoAcceptFileSize, highlyAvailable, confirmMigrations = confirmMigrations'} config = cfg {logLevel, showReactions, tbqSize, subscriptionEvents = logConnections, hostEvents = logServerHosts, defaultServers = configServers, inlineFiles = inlineFiles', autoAcceptFileSize, highlyAvailable, confirmMigrations = confirmMigrations'}
firstTime = dbNew chatStore firstTime = dbNew chatStore
currentUser <- newTVarIO user currentUser <- newTVarIO user
randomSMP <- randomPresetServers SPSMP presetServers'
randomXFTP <- randomPresetServers SPXFTP presetServers'
let randomServers = RandomServers {smpServers = randomSMP, xftpServers = randomXFTP}
currentRemoteHost <- newTVarIO Nothing currentRemoteHost <- newTVarIO Nothing
servers <- withTransaction chatStore $ \db -> agentServers db config randomServers servers <- agentServers config
smpAgent <- getSMPAgentClient aCfg {tbqSize} servers agentStore backgroundMode smpAgent <- getSMPAgentClient aCfg {tbqSize} servers agentStore backgroundMode
agentAsync <- newTVarIO Nothing agentAsync <- newTVarIO Nothing
random <- liftIO C.newRandom random <- liftIO C.newRandom
@@ -347,7 +281,6 @@ newChatController
ChatController ChatController
{ firstTime, { firstTime,
currentUser, currentUser,
randomServers,
currentRemoteHost, currentRemoteHost,
smpAgent, smpAgent,
agentAsync, agentAsync,
@@ -385,39 +318,28 @@ newChatController
contactMergeEnabled contactMergeEnabled
} }
where where
presetServers' :: PresetServers configServers :: DefaultAgentServers
presetServers' = presetServers {operators = operators', netCfg = netCfg'} configServers =
where let DefaultAgentServers {smp = defSmp, xftp = defXftp, netCfg} = defaultServers
PresetServers {operators, netCfg} = presetServers smp' = maybe defSmp (L.map enabledServerCfg) (nonEmpty smpServers)
netCfg' = updateNetworkConfig netCfg simpleNetCfg xftp' = maybe defXftp (L.map enabledServerCfg) (nonEmpty xftpServers)
operators' = case (smpServers, xftpServers) of in defaultServers {smp = smp', xftp = xftp', netCfg = updateNetworkConfig netCfg simpleNetCfg}
([], []) -> operators agentServers :: ChatConfig -> IO InitialAgentServers
(smpSrvs, []) -> L.map removeSMP operators <> [custom smpSrvs []] agentServers config@ChatConfig {defaultServers = defServers@DefaultAgentServers {ntf, netCfg}} = do
([], xftpSrvs) -> L.map removeXFTP operators <> [custom [] xftpSrvs] users <- withTransaction chatStore getUsers
(smpSrvs, xftpSrvs) -> [custom smpSrvs xftpSrvs] smp' <- getUserServers users SPSMP
removeSMP op = (op :: PresetOperator) {smp = []} xftp' <- getUserServers users SPXFTP
removeXFTP op = (op :: PresetOperator) {xftp = []}
custom smpSrvs xftpSrvs =
PresetOperator
{ operator = Nothing,
smp = map (presetServer True) smpSrvs,
useSMP = 0,
xftp = map (presetServer True) xftpSrvs,
useXFTP = 0
}
agentServers :: DB.Connection -> ChatConfig -> RandomServers -> IO InitialAgentServers
agentServers db ChatConfig {presetServers = PresetServers {operators = presetOps, ntf, netCfg}} randomServers = do
users <- getUsers db
opDomains <- operatorDomains <$> getUpdateServerOperators db presetOps (null users)
smp' <- getUserServers SPSMP users opDomains
xftp' <- getUserServers SPXFTP users opDomains
pure InitialAgentServers {smp = smp', xftp = xftp', ntf, netCfg} pure InitialAgentServers {smp = smp', xftp = xftp', ntf, netCfg}
where where
getUserServers :: forall p. (ProtocolTypeI p, UserProtocol p) => SProtocolType p -> [User] -> [(Text, ServerOperator)] -> IO (Map UserId (NonEmpty (ServerCfg p))) getUserServers :: forall p. (ProtocolTypeI p, UserProtocol p) => [User] -> SProtocolType p -> IO (Map UserId (NonEmpty (ServerCfg p)))
getUserServers p users opDomains = do getUserServers users protocol = case users of
let randomSrvs = rndServers p randomServers [] -> pure $ M.fromList [(1, cfgServers protocol defServers)]
fmap M.fromList $ forM users $ \u -> _ -> M.fromList <$> initialServers
(aUserId u,) . agentServerCfgs opDomains <$> getUpdateUserServers db p presetOps randomSrvs u where
initialServers :: IO [(UserId, NonEmpty (ServerCfg p))]
initialServers = mapM (\u -> (aUserId u,) <$> userServers u) users
userServers :: User -> IO (NonEmpty (ServerCfg p))
userServers user' = useServers config protocol <$> withTransaction chatStore (`getProtocolServers` user')
updateNetworkConfig :: NetworkConfig -> SimpleNetCfg -> NetworkConfig updateNetworkConfig :: NetworkConfig -> SimpleNetCfg -> NetworkConfig
updateNetworkConfig cfg SimpleNetCfg {socksProxy, socksMode, hostMode, requiredHostMode, smpProxyMode_, smpProxyFallback_, smpWebPort, tcpTimeout_, logTLSErrors} = updateNetworkConfig cfg SimpleNetCfg {socksProxy, socksMode, hostMode, requiredHostMode, smpProxyMode_, smpProxyFallback_, smpWebPort, tcpTimeout_, logTLSErrors} =
@@ -460,37 +382,33 @@ withFileLock :: String -> Int64 -> CM a -> CM a
withFileLock name = withEntityLock name . CLFile withFileLock name = withEntityLock name . CLFile
{-# INLINE withFileLock #-} {-# INLINE withFileLock #-}
serverCfg :: ProtoServerWithAuth p -> ServerCfg p useServers :: UserProtocol p => ChatConfig -> SProtocolType p -> [ServerCfg p] -> NonEmpty (ServerCfg p)
serverCfg server = ServerCfg {server, operator = Nothing, enabled = True, roles = allRoles} useServers ChatConfig {defaultServers} p = fromMaybe (cfgServers p defaultServers) . nonEmpty
useServers :: forall p. UserProtocol p => SProtocolType p -> RandomServers -> [UserServer p] -> NonEmpty (NewUserServer p) randomServers :: forall p. UserProtocol p => SProtocolType p -> ChatConfig -> IO (NonEmpty (ServerCfg p), [ServerCfg p])
useServers p rs servers = case L.nonEmpty servers of randomServers p ChatConfig {defaultServers} = do
Nothing -> rndServers p rs let srvs = cfgServers p defaultServers
Just srvs -> L.map (\srv -> (srv :: UserServer p) {serverId = DBNewEntity}) srvs (enbldSrvs, dsbldSrvs) = L.partition (\ServerCfg {enabled} -> enabled) srvs
toUse = cfgServersToUse p defaultServers
rndServers :: UserProtocol p => SProtocolType p -> RandomServers -> NonEmpty (NewUserServer p) if length enbldSrvs <= toUse
rndServers p RandomServers {smpServers, xftpServers} = case p of then pure (srvs, [])
SPSMP -> smpServers else do
SPXFTP -> xftpServers (enbldSrvs', srvsToDisable) <- splitAt toUse <$> shuffle enbldSrvs
let dsbldSrvs' = map (\srv -> (srv :: ServerCfg p) {enabled = False}) srvsToDisable
randomPresetServers :: forall p. UserProtocol p => SProtocolType p -> PresetServers -> IO (NonEmpty (NewUserServer p)) srvs' = sortOn server' $ enbldSrvs' <> dsbldSrvs' <> dsbldSrvs
randomPresetServers p PresetServers {operators} = toJust . L.nonEmpty . concat =<< mapM opSrvs operators pure (fromMaybe srvs $ L.nonEmpty srvs', srvs')
where where
toJust = \case server' ServerCfg {server = ProtoServerWithAuth srv _} = srv
Just a -> pure a
Nothing -> E.throwIO $ userError "no preset servers" cfgServers :: UserProtocol p => SProtocolType p -> DefaultAgentServers -> NonEmpty (ServerCfg p)
opSrvs :: PresetOperator -> IO [NewUserServer p] cfgServers p DefaultAgentServers {smp, xftp} = case p of
opSrvs op = do SPSMP -> smp
let srvs = operatorServers p op SPXFTP -> xftp
toUse = operatorServersToUse p op
(enbldSrvs, dsbldSrvs) = partition (\UserServer {enabled} -> enabled) srvs cfgServersToUse :: UserProtocol p => SProtocolType p -> DefaultAgentServers -> Int
if toUse <= 0 || toUse >= length enbldSrvs cfgServersToUse p DefaultAgentServers {useSMP, useXFTP} = case p of
then pure srvs SPSMP -> useSMP
else do SPXFTP -> useXFTP
(enbldSrvs', srvsToDisable) <- splitAt toUse <$> shuffle enbldSrvs
let dsbldSrvs' = map (\srv -> (srv :: NewUserServer p) {enabled = False}) srvsToDisable
pure $ sortOn server' $ enbldSrvs' <> dsbldSrvs' <> dsbldSrvs
server' UserServer {server = ProtoServerWithAuth srv _} = srv
-- enableSndFiles has no effect when mainApp is True -- enableSndFiles has no effect when mainApp is True
startChatController :: Bool -> Bool -> CM' (Async ()) startChatController :: Bool -> Bool -> CM' (Async ())
@@ -634,23 +552,19 @@ processChatCommand' vr = \case
forM_ profile $ \Profile {displayName} -> checkValidName displayName forM_ profile $ \Profile {displayName} -> checkValidName displayName
p@Profile {displayName} <- liftIO $ maybe generateRandomProfile pure profile p@Profile {displayName} <- liftIO $ maybe generateRandomProfile pure profile
u <- asks currentUser u <- asks currentUser
smpServers <- chooseServers SPSMP (smp, smpServers) <- chooseServers SPSMP
xftpServers <- chooseServers SPXFTP (xftp, xftpServers) <- chooseServers SPXFTP
users <- withFastStore' getUsers users <- withFastStore' getUsers
forM_ users $ \User {localDisplayName = n, activeUser, viewPwdHash} -> forM_ users $ \User {localDisplayName = n, activeUser, viewPwdHash} ->
when (n == displayName) . throwChatError $ when (n == displayName) . throwChatError $
if activeUser || isNothing viewPwdHash then CEUserExists displayName else CEInvalidDisplayName {displayName, validName = ""} if activeUser || isNothing viewPwdHash then CEUserExists displayName else CEInvalidDisplayName {displayName, validName = ""}
opDomains <- operatorDomains . fst <$> withFastStore getServerOperators
let smp = agentServerCfgs opDomains smpServers
xftp = agentServerCfgs opDomains xftpServers
auId <- withAgent (\a -> createUser a smp xftp) auId <- withAgent (\a -> createUser a smp xftp)
ts <- liftIO $ getCurrentTime >>= if pastTimestamp then coupleDaysAgo else pure ts <- liftIO $ getCurrentTime >>= if pastTimestamp then coupleDaysAgo else pure
user <- withFastStore $ \db -> createUserRecordAt db (AgentUserId auId) p True ts user <- withFastStore $ \db -> createUserRecordAt db (AgentUserId auId) p True ts
createPresetContactCards user `catchChatError` \_ -> pure () createPresetContactCards user `catchChatError` \_ -> pure ()
withFastStore $ \db -> do withFastStore $ \db -> createNoteFolder db user
createNoteFolder db user storeServers user smpServers
liftIO $ mapM_ (insertProtocolServer db SPSMP user ts) smpServers storeServers user xftpServers
liftIO $ mapM_ (insertProtocolServer db SPXFTP user ts) xftpServers
atomically . writeTVar u $ Just user atomically . writeTVar u $ Just user
pure $ CRActiveUser user pure $ CRActiveUser user
where where
@@ -659,11 +573,18 @@ processChatCommand' vr = \case
withFastStore $ \db -> do withFastStore $ \db -> do
createContact db user simplexStatusContactProfile createContact db user simplexStatusContactProfile
createContact db user simplexTeamContactProfile createContact db user simplexTeamContactProfile
chooseServers :: forall p. (ProtocolTypeI p, UserProtocol p) => SProtocolType p -> CM (NonEmpty (NewUserServer p)) chooseServers :: (ProtocolTypeI p, UserProtocol p) => SProtocolType p -> CM (NonEmpty (ServerCfg p), [ServerCfg p])
chooseServers p = do chooseServers protocol =
rs <- asks randomServers asks currentUser >>= readTVarIO >>= \case
srvs <- chatReadVar currentUser >>= mapM (\user -> withFastStore' $ \db -> getProtocolServers db p user) Nothing -> asks config >>= liftIO . randomServers protocol
pure $ useServers p rs $ fromMaybe [] srvs Just user -> chosenServers =<< withFastStore' (`getProtocolServers` user)
where
chosenServers servers = do
cfg <- asks config
pure (useServers cfg protocol servers, servers)
storeServers user servers =
unless (null servers) . withFastStore $
\db -> overwriteProtocolServers db user servers
coupleDaysAgo t = (`addUTCTime` t) . fromInteger . negate . (+ (2 * day)) <$> randomRIO (0, day) coupleDaysAgo t = (`addUTCTime` t) . fromInteger . negate . (+ (2 * day)) <$> randomRIO (0, day)
day = 86400 day = 86400
ListUsers -> CRUsersList <$> withFastStore' getUsersInfo ListUsers -> CRUsersList <$> withFastStore' getUsersInfo
@@ -1561,95 +1482,25 @@ processChatCommand' vr = \case
msgs <- lift $ withAgent' $ \a -> getConnectionMessages a acIds msgs <- lift $ withAgent' $ \a -> getConnectionMessages a acIds
let ntfMsgs = L.map (\msg -> receivedMsgInfo <$> msg) msgs let ntfMsgs = L.map (\msg -> receivedMsgInfo <$> msg) msgs
pure $ CRConnNtfMessages ntfMsgs pure $ CRConnNtfMessages ntfMsgs
GetUserProtoServers (AProtocolType p) -> withUser $ \user@User {userId} -> withServerProtocol p $ do APIGetUserProtoServers userId (AProtocolType p) -> withUserId userId $ \user -> withServerProtocol p $ do
(operators, smpServers, xftpServers) <- withFastStore (`getUserServers` user) cfg@ChatConfig {defaultServers} <- asks config
userServers <- liftIO $ groupByOperator $ case p of servers <- withFastStore' (`getProtocolServers` user)
SPSMP -> (operators, smpServers, []) pure $ CRUserProtoServers user $ AUPS $ UserProtoServers p (useServers cfg p servers) (cfgServers p defaultServers)
SPXFTP -> (operators, [], xftpServers) GetUserProtoServers aProtocol -> withUser $ \User {userId} ->
pure $ CRUserServers user userServers processChatCommand $ APIGetUserProtoServers userId aProtocol
SetUserProtoServers (AProtocolType p) servers -> withUser $ \user@User {userId} -> withServerProtocol p $ do APISetUserProtoServers userId (APSC p (ProtoServersConfig servers))
userServers <- liftIO . groupByOperator =<< withFastStore (`getUserServers` user) | null servers || any (\ServerCfg {enabled} -> enabled) servers -> withUserId userId $ \user -> withServerProtocol p $ do
-- disable operators servers and repace (or add) custom servers, or restore random defaults if empty list withFastStore $ \db -> overwriteProtocolServers db user servers
case L.nonEmpty userServers of cfg <- asks config
Just srvs -> processChatCommand $ APISetUserServers userId $ L.map updated srvs lift $ withAgent' $ \a -> setProtocolServers a (aUserId user) $ useServers cfg p servers
where ok user
updated UserOperatorServers {operator, smpServers, xftpServers} = | otherwise -> withUserId userId $ \user -> pure $ chatCmdError (Just user) "all servers are disabled"
UpdatedUserOperatorServers SetUserProtoServers serversConfig -> withUser $ \User {userId} ->
{ operator, processChatCommand $ APISetUserProtoServers userId serversConfig
smpServers = map (AUS SDBStored) smpServers,
xftpServers = map (AUS SDBStored) xftpServers
}
Nothing -> throwChatError $ CECommandError "no servers"
APITestProtoServer userId srv@(AProtoServerWithAuth _ server) -> withUserId userId $ \user -> APITestProtoServer userId srv@(AProtoServerWithAuth _ server) -> withUserId userId $ \user ->
lift $ CRServerTestResult user srv <$> withAgent' (\a -> testProtocolServer a (aUserId user) server) lift $ CRServerTestResult user srv <$> withAgent' (\a -> testProtocolServer a (aUserId user) server)
TestProtoServer srv -> withUser $ \User {userId} -> TestProtoServer srv -> withUser $ \User {userId} ->
processChatCommand $ APITestProtoServer userId srv processChatCommand $ APITestProtoServer userId srv
APITestServerOperator -> do
let serverOperator = ServerOperator {
operatorId = DBEntityId 1,
operatorTag = Just OTSimplex,
tradeName = "Simplex",
legalName = Just "Simplex",
serverDomains = ["simplex.im"],
conditionsAcceptance = CAAccepted Nothing,
enabled = True,
roles = ServerRoles {storage = True, proxy = True}
}
pure $ CRTestOperator serverOperator
APITestUsageConditionsAction -> do
ts <- liftIO getCurrentTime
let conditionsAction = Just $ UCAReview {
operators = [],
deadline = Just ts,
showNotice = True
}
pure $ CRTestUsageConditionsAction conditionsAction
APITestConditionsAcceptance -> do
let acceptance = CAAccepted Nothing
pure $ CRTestConditionsAcceptance acceptance
APITestServerRoles -> do
let serverRoles = ServerRoles {storage = True, proxy = True}
pure $ CRTestServerRoles serverRoles
APIGetServerOperators -> uncurry CRServerOperators <$> withFastStore getServerOperators
APISetServerOperators operatorsEnabled -> withFastStore $ \db -> do
liftIO $ setServerOperators db operatorsEnabled
uncurry CRServerOperators <$> getServerOperators db
APIGetUserServers userId -> withUserId userId $ \user -> withFastStore $ \db ->
CRUserServers user <$> (liftIO . groupByOperator =<< getUserServers db user)
APISetUserServers userId userServers -> withUserId userId $ \user -> do
let errors = validateUserServers userServers
unless (null errors) $ throwChatError (CECommandError $ "user servers validation error(s): " <> show errors)
(operators, smpServers, xftpServers) <- withFastStore $ \db -> do
setUserServers db user userServers
getUserServers db user
let opDomains = operatorDomains operators
rs <- asks randomServers
lift $ withAgent' $ \a -> do
let auId = aUserId user
setProtocolServers a auId $ agentServerCfgs opDomains $ useServers SPSMP rs smpServers
setProtocolServers a auId $ agentServerCfgs opDomains $ useServers SPXFTP rs xftpServers
ok_
APIValidateServers userServers -> pure $ CRUserServersValidation $ validateUserServers userServers
APIGetUsageConditions -> do
(usageConditions, acceptedConditions) <- withFastStore $ \db -> do
usageConditions <- getCurrentUsageConditions db
acceptedConditions <- liftIO $ getLatestAcceptedConditions db
pure (usageConditions, acceptedConditions)
-- TODO if db commit is different from source commit, conditionsText should be nothing in response
pure
CRUsageConditions
{ usageConditions,
conditionsText = usageConditionsText,
acceptedConditions
}
APISetConditionsNotified condId -> do
currentTs <- liftIO getCurrentTime
withFastStore' $ \db -> setConditionsNotified db condId currentTs
ok_
APIAcceptConditions condId opIds -> withFastStore $ \db -> do
currentTs <- liftIO getCurrentTime
acceptConditions db condId opIds currentTs
uncurry CRServerOperators <$> getServerOperators db
APISetChatItemTTL userId newTTL_ -> withUserId userId $ \user -> APISetChatItemTTL userId newTTL_ -> withUserId userId $ \user ->
checkStoreNotChanged $ checkStoreNotChanged $
withChatLock "setChatItemTTL" $ do withChatLock "setChatItemTTL" $ do
@@ -1902,7 +1753,8 @@ processChatCommand' vr = \case
canKeepLink (CRInvitationUri crData _) newUser = do canKeepLink (CRInvitationUri crData _) newUser = do
let ConnReqUriData {crSmpQueues = q :| _} = crData let ConnReqUriData {crSmpQueues = q :| _} = crData
SMPQueueUri {queueAddress = SMPQueueAddress {smpServer}} = q SMPQueueUri {queueAddress = SMPQueueAddress {smpServer}} = q
newUserServers <- map (\UserServer {server} -> protoServer server) <$> withFastStore' (\db -> getProtocolServers db SPSMP newUser) cfg <- asks config
newUserServers <- L.map (\ServerCfg {server} -> protoServer server) . useServers cfg SPSMP <$> withFastStore' (`getProtocolServers` newUser)
pure $ smpServer `elem` newUserServers pure $ smpServer `elem` newUserServers
updateConnRecord user@User {userId} conn@PendingContactConnection {customUserProfileId} newUser = do updateConnRecord user@User {userId} conn@PendingContactConnection {customUserProfileId} newUser = do
withAgent $ \a -> changeConnectionUser a (aUserId user) (aConnId' conn) (aUserId newUser) withAgent $ \a -> changeConnectionUser a (aUserId user) (aConnId' conn) (aUserId newUser)
@@ -2236,7 +2088,7 @@ processChatCommand' vr = \case
where where
changeMemberRole user gInfo members m gEvent = do changeMemberRole user gInfo members m gEvent = do
let GroupMember {memberId = mId, memberRole = mRole, memberStatus = mStatus, memberContactId, localDisplayName = cName} = m let GroupMember {memberId = mId, memberRole = mRole, memberStatus = mStatus, memberContactId, localDisplayName = cName} = m
assertUserGroupRole gInfo $ maximum ([GRAdmin, mRole, memRole] :: [GroupMemberRole]) assertUserGroupRole gInfo $ maximum [GRAdmin, mRole, memRole]
withGroupLock "memberRole" groupId . procCmd $ do withGroupLock "memberRole" groupId . procCmd $ do
unless (mRole == memRole) $ do unless (mRole == memRole) $ do
withFastStore' $ \db -> updateGroupMemberRole db user m memRole withFastStore' $ \db -> updateGroupMemberRole db user m memRole
@@ -2634,15 +2486,14 @@ processChatCommand' vr = \case
pure $ CRAgentSubsTotal user subsTotal hasSession pure $ CRAgentSubsTotal user subsTotal hasSession
GetAgentServersSummary userId -> withUserId userId $ \user -> do GetAgentServersSummary userId -> withUserId userId $ \user -> do
agentServersSummary <- lift $ withAgent' getAgentServersSummary agentServersSummary <- lift $ withAgent' getAgentServersSummary
withStore' $ \db -> do cfg <- asks config
users <- getUsers db (users, smpServers, xftpServers) <-
smpServers <- getServers db user SPSMP withStore' $ \db -> (,,) <$> getUsers db <*> getServers db cfg user SPSMP <*> getServers db cfg user SPXFTP
xftpServers <- getServers db user SPXFTP let presentedServersSummary = toPresentedServersSummary agentServersSummary users user smpServers xftpServers _defaultNtfServers
let presentedServersSummary = toPresentedServersSummary agentServersSummary users user smpServers xftpServers _defaultNtfServers pure $ CRAgentServersSummary user presentedServersSummary
pure $ CRAgentServersSummary user presentedServersSummary
where where
getServers :: (ProtocolTypeI p, UserProtocol p) => DB.Connection -> User -> SProtocolType p -> IO [ProtocolServer p] getServers :: (ProtocolTypeI p, UserProtocol p) => DB.Connection -> ChatConfig -> User -> SProtocolType p -> IO (NonEmpty (ProtocolServer p))
getServers db user p = map (\UserServer {server} -> protoServer server) <$> getProtocolServers db p user getServers db cfg user p = L.map (\ServerCfg {server} -> protoServer server) . useServers cfg p <$> getProtocolServers db user
ResetAgentServersStats -> withAgent resetAgentServersStats >> ok_ ResetAgentServersStats -> withAgent resetAgentServersStats >> ok_
GetAgentWorkers -> lift $ CRAgentWorkersSummary <$> withAgent' getAgentWorkersSummary GetAgentWorkers -> lift $ CRAgentWorkersSummary <$> withAgent' getAgentWorkersSummary
GetAgentWorkersDetails -> lift $ CRAgentWorkersDetails <$> withAgent' getAgentWorkersDetails GetAgentWorkersDetails -> lift $ CRAgentWorkersDetails <$> withAgent' getAgentWorkersDetails
@@ -3760,7 +3611,8 @@ receiveViaCompleteFD user fileId RcvFileDescr {fileDescrText, fileDescrComplete}
S.toList $ S.fromList $ concatMap (\FD.FileChunk {replicas} -> map (\FD.FileChunkReplica {server} -> server) replicas) chunks S.toList $ S.fromList $ concatMap (\FD.FileChunk {replicas} -> map (\FD.FileChunkReplica {server} -> server) replicas) chunks
getUnknownSrvs :: [XFTPServer] -> CM [XFTPServer] getUnknownSrvs :: [XFTPServer] -> CM [XFTPServer]
getUnknownSrvs srvs = do getUnknownSrvs srvs = do
knownSrvs <- map (\UserServer {server} -> protoServer server) <$> withStore' (\db -> getProtocolServers db SPXFTP user) cfg <- asks config
knownSrvs <- L.map (\ServerCfg {server} -> protoServer server) . useServers cfg SPXFTP <$> withStore' (`getProtocolServers` user)
pure $ filter (`notElem` knownSrvs) srvs pure $ filter (`notElem` knownSrvs) srvs
ipProtectedForSrvs :: [XFTPServer] -> CM Bool ipProtectedForSrvs :: [XFTPServer] -> CM Bool
ipProtectedForSrvs srvs = do ipProtectedForSrvs srvs = do
@@ -3972,7 +3824,7 @@ subscribeUserConnections vr onlyNeeded agentBatchSubscribe user = do
(sftConns, sfts) <- getSndFileTransferConns (sftConns, sfts) <- getSndFileTransferConns
(rftConns, rfts) <- getRcvFileTransferConns (rftConns, rfts) <- getRcvFileTransferConns
(pcConns, pcs) <- getPendingContactConns (pcConns, pcs) <- getPendingContactConns
let conns = concat ([ctConns, ucConns, mConns, sftConns, rftConns, pcConns] :: [[ConnId]]) let conns = concat [ctConns, ucConns, mConns, sftConns, rftConns, pcConns]
pure (conns, cts, ucs, gs, ms, sfts, rfts, pcs) pure (conns, cts, ucs, gs, ms, sfts, rfts, pcs)
-- subscribe using batched commands -- subscribe using batched commands
rs <- withAgent $ \a -> agentBatchSubscribe a conns rs <- withAgent $ \a -> agentBatchSubscribe a conns
@@ -4780,7 +4632,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
ctItem = AChatItem SCTDirect SMDSnd (DirectChat ct) ctItem = AChatItem SCTDirect SMDSnd (DirectChat ct)
SWITCH qd phase cStats -> do SWITCH qd phase cStats -> do
toView $ CRContactSwitch user ct (SwitchProgress qd phase cStats) toView $ CRContactSwitch user ct (SwitchProgress qd phase cStats)
when (phase == SPStarted || phase == SPCompleted) $ case qd of when (phase `elem` [SPStarted, SPCompleted]) $ case qd of
QDRcv -> createInternalChatItem user (CDDirectSnd ct) (CISndConnEvent $ SCESwitchQueue phase Nothing) Nothing QDRcv -> createInternalChatItem user (CDDirectSnd ct) (CISndConnEvent $ SCESwitchQueue phase Nothing) Nothing
QDSnd -> createInternalChatItem user (CDDirectRcv ct) (CIRcvConnEvent $ RCESwitchQueue phase) Nothing QDSnd -> createInternalChatItem user (CDDirectRcv ct) (CIRcvConnEvent $ RCESwitchQueue phase) Nothing
RSYNC rss cryptoErr_ cStats -> RSYNC rss cryptoErr_ cStats ->
@@ -5065,7 +4917,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
(Just fileDescrText, Just msgId) -> do (Just fileDescrText, Just msgId) -> do
partSize <- asks $ xftpDescrPartSize . config partSize <- asks $ xftpDescrPartSize . config
let parts = splitFileDescr partSize fileDescrText let parts = splitFileDescr partSize fileDescrText
pure . L.toList $ L.map (XMsgFileDescr msgId) parts pure . toList $ L.map (XMsgFileDescr msgId) parts
_ -> pure [] _ -> pure []
let fileDescrChatMsgs = map (ChatMessage senderVRange Nothing) fileDescrEvents let fileDescrChatMsgs = map (ChatMessage senderVRange Nothing) fileDescrEvents
GroupMember {memberId} = sender GroupMember {memberId} = sender
@@ -5191,7 +5043,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
when continued $ sendPendingGroupMessages user m conn when continued $ sendPendingGroupMessages user m conn
SWITCH qd phase cStats -> do SWITCH qd phase cStats -> do
toView $ CRGroupMemberSwitch user gInfo m (SwitchProgress qd phase cStats) toView $ CRGroupMemberSwitch user gInfo m (SwitchProgress qd phase cStats)
when (phase == SPStarted || phase == SPCompleted) $ case qd of when (phase `elem` [SPStarted, SPCompleted]) $ case qd of
QDRcv -> createInternalChatItem user (CDGroupSnd gInfo) (CISndConnEvent . SCESwitchQueue phase . Just $ groupMemberRef m) Nothing QDRcv -> createInternalChatItem user (CDGroupSnd gInfo) (CISndConnEvent . SCESwitchQueue phase . Just $ groupMemberRef m) Nothing
QDSnd -> createInternalChatItem user (CDGroupRcv gInfo m) (CIRcvConnEvent $ RCESwitchQueue phase) Nothing QDSnd -> createInternalChatItem user (CDGroupRcv gInfo m) (CIRcvConnEvent $ RCESwitchQueue phase) Nothing
RSYNC rss cryptoErr_ cStats -> RSYNC rss cryptoErr_ cStats ->
@@ -6755,17 +6607,15 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
messageWarning "x.grp.mem.con: neither member is invitee" messageWarning "x.grp.mem.con: neither member is invitee"
where where
inviteeXGrpMemCon :: GroupMemberIntro -> CM () inviteeXGrpMemCon :: GroupMemberIntro -> CM ()
inviteeXGrpMemCon GroupMemberIntro {introId, introStatus} = case introStatus of inviteeXGrpMemCon GroupMemberIntro {introId, introStatus}
GMIntroReConnected -> updateStatus introId GMIntroConnected | introStatus == GMIntroReConnected = updateStatus introId GMIntroConnected
GMIntroToConnected -> pure () | introStatus `elem` [GMIntroToConnected, GMIntroConnected] = pure ()
GMIntroConnected -> pure () | otherwise = updateStatus introId GMIntroToConnected
_ -> updateStatus introId GMIntroToConnected
forwardMemberXGrpMemCon :: GroupMemberIntro -> CM () forwardMemberXGrpMemCon :: GroupMemberIntro -> CM ()
forwardMemberXGrpMemCon GroupMemberIntro {introId, introStatus} = case introStatus of forwardMemberXGrpMemCon GroupMemberIntro {introId, introStatus}
GMIntroToConnected -> updateStatus introId GMIntroConnected | introStatus == GMIntroToConnected = updateStatus introId GMIntroConnected
GMIntroReConnected -> pure () | introStatus `elem` [GMIntroReConnected, GMIntroConnected] = pure ()
GMIntroConnected -> pure () | otherwise = updateStatus introId GMIntroReConnected
_ -> updateStatus introId GMIntroReConnected
updateStatus introId status = withStore' $ \db -> updateIntroStatus db introId status updateStatus introId status = withStore' $ \db -> updateIntroStatus db introId status
xGrpMemDel :: GroupInfo -> GroupMember -> MemberId -> RcvMessage -> UTCTime -> CM () xGrpMemDel :: GroupInfo -> GroupMember -> MemberId -> RcvMessage -> UTCTime -> CM ()
@@ -8230,24 +8080,14 @@ chatCommandP =
"/smp test " *> (TestProtoServer . AProtoServerWithAuth SPSMP <$> strP), "/smp test " *> (TestProtoServer . AProtoServerWithAuth SPSMP <$> strP),
"/xftp test " *> (TestProtoServer . AProtoServerWithAuth SPXFTP <$> strP), "/xftp test " *> (TestProtoServer . AProtoServerWithAuth SPXFTP <$> strP),
"/ntf test " *> (TestProtoServer . AProtoServerWithAuth SPNTF <$> strP), "/ntf test " *> (TestProtoServer . AProtoServerWithAuth SPNTF <$> strP),
"/_dec_operator" $> APITestServerOperator, "/_servers " *> (APISetUserProtoServers <$> A.decimal <* A.space <*> srvCfgP),
"/_dec_conditions_action" $> APITestUsageConditionsAction, "/smp " *> (SetUserProtoServers . APSC SPSMP . ProtoServersConfig . map enabledServerCfg <$> protocolServersP),
"/_dec_acceptance" $> APITestConditionsAcceptance, "/smp default" $> SetUserProtoServers (APSC SPSMP $ ProtoServersConfig []),
"/_dec_roles" $> APITestServerRoles, "/xftp " *> (SetUserProtoServers . APSC SPXFTP . ProtoServersConfig . map enabledServerCfg <$> protocolServersP),
"/smp " *> (SetUserProtoServers (AProtocolType SPSMP) . map (AProtoServerWithAuth SPSMP) <$> protocolServersP), "/xftp default" $> SetUserProtoServers (APSC SPXFTP $ ProtoServersConfig []),
"/smp default" $> SetUserProtoServers (AProtocolType SPSMP) [], "/_servers " *> (APIGetUserProtoServers <$> A.decimal <* A.space <*> strP),
"/xftp " *> (SetUserProtoServers (AProtocolType SPXFTP) . map (AProtoServerWithAuth SPXFTP) <$> protocolServersP),
"/xftp default" $> SetUserProtoServers (AProtocolType SPXFTP) [],
"/smp" $> GetUserProtoServers (AProtocolType SPSMP), "/smp" $> GetUserProtoServers (AProtocolType SPSMP),
"/xftp" $> GetUserProtoServers (AProtocolType SPXFTP), "/xftp" $> GetUserProtoServers (AProtocolType SPXFTP),
"/_operators" $> APIGetServerOperators,
"/_operators " *> (APISetServerOperators <$> jsonP),
"/_servers " *> (APIGetUserServers <$> A.decimal),
"/_servers " *> (APISetUserServers <$> A.decimal <* A.space <*> jsonP),
"/_validate_servers " *> (APIValidateServers <$> jsonP),
"/_conditions" $> APIGetUsageConditions,
"/_conditions_notified " *> (APISetConditionsNotified <$> A.decimal),
"/_accept_conditions " *> (APIAcceptConditions <$> A.decimal <*> _strP),
"/_ttl " *> (APISetChatItemTTL <$> A.decimal <* A.space <*> ciTTLDecimal), "/_ttl " *> (APISetChatItemTTL <$> A.decimal <* A.space <*> ciTTLDecimal),
"/ttl " *> (SetChatItemTTL <$> ciTTL), "/ttl " *> (SetChatItemTTL <$> ciTTL),
"/_ttl " *> (APIGetChatItemTTL <$> A.decimal), "/_ttl " *> (APIGetChatItemTTL <$> A.decimal),
@@ -8589,7 +8429,7 @@ chatCommandP =
onOffP onOffP
(Just <$> (AutoAccept <$> (" incognito=" *> onOffP <|> pure False) <*> optional (A.space *> msgContentP))) (Just <$> (AutoAccept <$> (" incognito=" *> onOffP <|> pure False) <*> optional (A.space *> msgContentP)))
(pure Nothing) (pure Nothing)
-- srvCfgP = strP >>= \case AProtocolType p -> APSC p <$> (A.space *> jsonP) srvCfgP = strP >>= \case AProtocolType p -> APSC p <$> (A.space *> jsonP)
rcCtrlAddressP = RCCtrlAddress <$> ("addr=" *> strP) <*> (" iface=" *> (jsonP <|> text1P)) rcCtrlAddressP = RCCtrlAddress <$> ("addr=" *> strP) <*> (" iface=" *> (jsonP <|> text1P))
text1P = safeDecodeUtf8 <$> A.takeTill (== ' ') text1P = safeDecodeUtf8 <$> A.takeTill (== ' ')
char_ = optional . A.char char_ = optional . A.char
+9 -7
View File
@@ -12,7 +12,6 @@ import Control.Concurrent.STM
import Control.Monad import Control.Monad
import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Char8 as B
import Data.List.NonEmpty (NonEmpty (..)) import Data.List.NonEmpty (NonEmpty (..))
import Data.Text (Text)
import qualified Data.Text as T import qualified Data.Text as T
import Simplex.Chat.Controller import Simplex.Chat.Controller
import Simplex.Chat.Core import Simplex.Chat.Core
@@ -32,10 +31,10 @@ chatBotRepl welcome answer _user cc = do
case resp of case resp of
CRContactConnected _ contact _ -> do CRContactConnected _ contact _ -> do
contactConnected contact contactConnected contact
void $ sendMessage cc contact $ T.pack welcome void $ sendMessage cc contact welcome
CRNewChatItems {chatItems = (AChatItem _ SMDRcv (DirectChat contact) ChatItem {content = mc@CIRcvMsgContent {}}) : _} -> do CRNewChatItems {chatItems = (AChatItem _ SMDRcv (DirectChat contact) ChatItem {content = mc@CIRcvMsgContent {}}) : _} -> do
let msg = T.unpack $ ciContentToText mc let msg = T.unpack $ ciContentToText mc
void $ sendMessage cc contact . T.pack =<< answer contact msg void $ sendMessage cc contact =<< answer contact msg
_ -> pure () _ -> pure ()
where where
contactConnected Contact {localDisplayName} = putStrLn $ T.unpack localDisplayName <> " connected" contactConnected Contact {localDisplayName} = putStrLn $ T.unpack localDisplayName <> " connected"
@@ -58,11 +57,11 @@ initializeBotAddress' logAddress cc = do
when logAddress $ putStrLn $ "Bot's contact address is: " <> B.unpack (strEncode uri) when logAddress $ putStrLn $ "Bot's contact address is: " <> B.unpack (strEncode uri)
void $ sendChatCmd cc $ AddressAutoAccept $ Just AutoAccept {acceptIncognito = False, autoReply = Nothing} void $ sendChatCmd cc $ AddressAutoAccept $ Just AutoAccept {acceptIncognito = False, autoReply = Nothing}
sendMessage :: ChatController -> Contact -> Text -> IO () sendMessage :: ChatController -> Contact -> String -> IO ()
sendMessage cc ct = sendComposedMessage cc ct Nothing . MCText sendMessage cc ct = sendComposedMessage cc ct Nothing . textMsgContent
sendMessage' :: ChatController -> ContactId -> Text -> IO () sendMessage' :: ChatController -> ContactId -> String -> IO ()
sendMessage' cc ctId = sendComposedMessage' cc ctId Nothing . MCText sendMessage' cc ctId = sendComposedMessage' cc ctId Nothing . textMsgContent
sendComposedMessage :: ChatController -> Contact -> Maybe ChatItemId -> MsgContent -> IO () sendComposedMessage :: ChatController -> Contact -> Maybe ChatItemId -> MsgContent -> IO ()
sendComposedMessage cc = sendComposedMessage' cc . contactId' sendComposedMessage cc = sendComposedMessage' cc . contactId'
@@ -84,6 +83,9 @@ deleteMessage cc ct chatItemId = do
contactRef :: Contact -> ChatRef contactRef :: Contact -> ChatRef
contactRef = ChatRef CTDirect . contactId' contactRef = ChatRef CTDirect . contactId'
textMsgContent :: String -> MsgContent
textMsgContent = MCText . T.pack
printLog :: ChatController -> ChatLogLevel -> String -> IO () printLog :: ChatController -> ChatLogLevel -> String -> IO ()
printLog cc level s printLog cc level s
| logLevel (config cc) <= level = putStrLn s | logLevel (config cc) <= level = putStrLn s
+2 -2
View File
@@ -18,8 +18,8 @@ data KnownContact = KnownContact
} }
deriving (Eq) deriving (Eq)
knownContactNames :: [KnownContact] -> Text knownContactNames :: [KnownContact] -> String
knownContactNames = T.intercalate ", " . map (("@" <>) . localDisplayName) knownContactNames = T.unpack . T.intercalate ", " . map (("@" <>) . localDisplayName)
parseKnownContacts :: ReadM [KnownContact] parseKnownContacts :: ReadM [KnownContact]
parseKnownContacts = eitherReader $ parseAll knownContactsP . encodeUtf8 . T.pack parseKnownContacts = eitherReader $ parseAll knownContactsP . encodeUtf8 . T.pack
+43 -63
View File
@@ -35,6 +35,7 @@ import qualified Data.ByteArray as BA
import Data.ByteString.Char8 (ByteString) import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Char8 as B
import Data.Char (ord) import Data.Char (ord)
import Data.Constraint (Dict (..))
import Data.Int (Int64) import Data.Int (Int64)
import Data.List.NonEmpty (NonEmpty) import Data.List.NonEmpty (NonEmpty)
import Data.Map.Strict (Map) import Data.Map.Strict (Map)
@@ -56,7 +57,6 @@ import Simplex.Chat.Call
import Simplex.Chat.Markdown (MarkdownList) import Simplex.Chat.Markdown (MarkdownList)
import Simplex.Chat.Messages import Simplex.Chat.Messages
import Simplex.Chat.Messages.CIContent import Simplex.Chat.Messages.CIContent
import Simplex.Chat.Operators
import Simplex.Chat.Protocol import Simplex.Chat.Protocol
import Simplex.Chat.Remote.AppVersion import Simplex.Chat.Remote.AppVersion
import Simplex.Chat.Remote.Types import Simplex.Chat.Remote.Types
@@ -70,7 +70,7 @@ import Simplex.Chat.Util (liftIOEither)
import Simplex.FileTransfer.Description (FileDescriptionURI) import Simplex.FileTransfer.Description (FileDescriptionURI)
import Simplex.Messaging.Agent (AgentClient, SubscriptionsInfo) import Simplex.Messaging.Agent (AgentClient, SubscriptionsInfo)
import Simplex.Messaging.Agent.Client (AgentLocks, AgentQueuesInfo (..), AgentWorkersDetails (..), AgentWorkersSummary (..), ProtocolTestFailure, SMPServerSubs, ServerQueueInfo, UserNetworkInfo) import Simplex.Messaging.Agent.Client (AgentLocks, AgentQueuesInfo (..), AgentWorkersDetails (..), AgentWorkersSummary (..), ProtocolTestFailure, SMPServerSubs, ServerQueueInfo, UserNetworkInfo)
import Simplex.Messaging.Agent.Env.SQLite (AgentConfig, NetworkConfig, ServerRoles) import Simplex.Messaging.Agent.Env.SQLite (AgentConfig, NetworkConfig, ServerCfg)
import Simplex.Messaging.Agent.Lock import Simplex.Messaging.Agent.Lock
import Simplex.Messaging.Agent.Protocol import Simplex.Messaging.Agent.Protocol
import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation, SQLiteStore, UpMigration, withTransaction, withTransactionPriority) import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation, SQLiteStore, UpMigration, withTransaction, withTransactionPriority)
@@ -84,7 +84,7 @@ import Simplex.Messaging.Crypto.Ratchet (PQEncryption)
import Simplex.Messaging.Encoding.String import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Notifications.Protocol (DeviceToken (..), NtfTknStatus) import Simplex.Messaging.Notifications.Protocol (DeviceToken (..), NtfTknStatus)
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON, parseAll, parseString, sumTypeJSON) import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON, parseAll, parseString, sumTypeJSON)
import Simplex.Messaging.Protocol (AProtoServerWithAuth, AProtocolType (..), CorrId, MsgId, NMsgMeta (..), NtfServer, ProtocolType (..), QueueId, SMPMsgMeta (..), SubscriptionMode (..), XFTPServer) import Simplex.Messaging.Protocol (AProtoServerWithAuth, AProtocolType (..), CorrId, MsgId, NMsgMeta (..), NtfServer, ProtocolType (..), ProtocolTypeI, QueueId, SMPMsgMeta (..), SProtocolType, SubscriptionMode (..), UserProtocol, XFTPServer, userProtocol)
import Simplex.Messaging.TMap (TMap) import Simplex.Messaging.TMap (TMap)
import Simplex.Messaging.Transport (TLS, simplexMQVersion) import Simplex.Messaging.Transport (TLS, simplexMQVersion)
import Simplex.Messaging.Transport.Client (SocksProxyWithAuth, TransportHost) import Simplex.Messaging.Transport.Client (SocksProxyWithAuth, TransportHost)
@@ -132,7 +132,7 @@ data ChatConfig = ChatConfig
{ agentConfig :: AgentConfig, { agentConfig :: AgentConfig,
chatVRange :: VersionRangeChat, chatVRange :: VersionRangeChat,
confirmMigrations :: MigrationConfirmation, confirmMigrations :: MigrationConfirmation,
presetServers :: PresetServers, defaultServers :: DefaultAgentServers,
tbqSize :: Natural, tbqSize :: Natural,
fileChunkSize :: Integer, fileChunkSize :: Integer,
xftpDescrPartSize :: Int, xftpDescrPartSize :: Int,
@@ -154,11 +154,6 @@ data ChatConfig = ChatConfig
chatHooks :: ChatHooks chatHooks :: ChatHooks
} }
data RandomServers = RandomServers
{ smpServers :: NonEmpty (NewUserServer 'PSMP),
xftpServers :: NonEmpty (NewUserServer 'PXFTP)
}
-- The hooks can be used to extend or customize chat core in mobile or CLI clients. -- The hooks can be used to extend or customize chat core in mobile or CLI clients.
data ChatHooks = ChatHooks data ChatHooks = ChatHooks
{ -- preCmdHook can be used to process or modify the commands before they are processed. { -- preCmdHook can be used to process or modify the commands before they are processed.
@@ -177,9 +172,12 @@ defaultChatHooks =
eventHook = \_ -> pure eventHook = \_ -> pure
} }
data PresetServers = PresetServers data DefaultAgentServers = DefaultAgentServers
{ operators :: NonEmpty PresetOperator, { smp :: NonEmpty (ServerCfg 'PSMP),
useSMP :: Int,
ntf :: [NtfServer], ntf :: [NtfServer],
xftp :: NonEmpty (ServerCfg 'PXFTP),
useXFTP :: Int,
netCfg :: NetworkConfig netCfg :: NetworkConfig
} }
@@ -205,7 +203,6 @@ data ChatDatabase = ChatDatabase {chatStore :: SQLiteStore, agentStore :: SQLite
data ChatController = ChatController data ChatController = ChatController
{ currentUser :: TVar (Maybe User), { currentUser :: TVar (Maybe User),
randomServers :: RandomServers,
currentRemoteHost :: TVar (Maybe RemoteHostId), currentRemoteHost :: TVar (Maybe RemoteHostId),
firstTime :: Bool, firstTime :: Bool,
smpAgent :: AgentClient, smpAgent :: AgentClient,
@@ -349,22 +346,12 @@ data ChatCommand
| APIGetGroupLink GroupId | APIGetGroupLink GroupId
| APICreateMemberContact GroupId GroupMemberId | APICreateMemberContact GroupId GroupMemberId
| APISendMemberContactInvitation {contactId :: ContactId, msgContent_ :: Maybe MsgContent} | APISendMemberContactInvitation {contactId :: ContactId, msgContent_ :: Maybe MsgContent}
| APIGetUserProtoServers UserId AProtocolType
| GetUserProtoServers AProtocolType | GetUserProtoServers AProtocolType
| SetUserProtoServers AProtocolType [AProtoServerWithAuth] | APISetUserProtoServers UserId AProtoServersConfig
| SetUserProtoServers AProtoServersConfig
| APITestProtoServer UserId AProtoServerWithAuth | APITestProtoServer UserId AProtoServerWithAuth
| TestProtoServer AProtoServerWithAuth | TestProtoServer AProtoServerWithAuth
| APITestServerOperator
| APITestUsageConditionsAction
| APITestConditionsAcceptance
| APITestServerRoles
| APIGetServerOperators
| APISetServerOperators (NonEmpty ServerOperator)
| APIGetUserServers UserId
| APISetUserServers UserId (NonEmpty UpdatedUserOperatorServers)
| APIValidateServers (NonEmpty UpdatedUserOperatorServers) -- response is CRUserServersValidation
| APIGetUsageConditions
| APISetConditionsNotified Int64
| APIAcceptConditions Int64 (NonEmpty Int64)
| APISetChatItemTTL UserId (Maybe Int64) | APISetChatItemTTL UserId (Maybe Int64)
| SetChatItemTTL (Maybe Int64) | SetChatItemTTL (Maybe Int64)
| APIGetChatItemTTL UserId | APIGetChatItemTTL UserId
@@ -590,15 +577,8 @@ data ChatResponse
| CRChatItemInfo {user :: User, chatItem :: AChatItem, chatItemInfo :: ChatItemInfo} | CRChatItemInfo {user :: User, chatItem :: AChatItem, chatItemInfo :: ChatItemInfo}
| CRChatItemId User (Maybe ChatItemId) | CRChatItemId User (Maybe ChatItemId)
| CRApiParsedMarkdown {formattedText :: Maybe MarkdownList} | CRApiParsedMarkdown {formattedText :: Maybe MarkdownList}
| CRUserProtoServers {user :: User, servers :: AUserProtoServers}
| CRServerTestResult {user :: User, testServer :: AProtoServerWithAuth, testFailure :: Maybe ProtocolTestFailure} | CRServerTestResult {user :: User, testServer :: AProtoServerWithAuth, testFailure :: Maybe ProtocolTestFailure}
| CRTestOperator {operator :: ServerOperator}
| CRTestUsageConditionsAction {conditionsAction :: Maybe UsageConditionsAction}
| CRTestConditionsAcceptance {acceptance :: ConditionsAcceptance}
| CRTestServerRoles {roles :: ServerRoles}
| CRServerOperators {operators :: [ServerOperator], conditionsAction :: Maybe UsageConditionsAction}
| CRUserServers {user :: User, userServers :: [UserOperatorServers]}
| CRUserServersValidation {serverErrors :: [UserServersError]}
| CRUsageConditions {usageConditions :: UsageConditions, conditionsText :: Text, acceptedConditions :: Maybe UsageConditions}
| CRChatItemTTL {user :: User, chatItemTTL :: Maybe Int64} | CRChatItemTTL {user :: User, chatItemTTL :: Maybe Int64}
| CRNetworkConfig {networkConfig :: NetworkConfig} | CRNetworkConfig {networkConfig :: NetworkConfig}
| CRContactInfo {user :: User, contact :: Contact, connectionStats_ :: Maybe ConnectionStats, customUserProfile :: Maybe Profile} | CRContactInfo {user :: User, contact :: Contact, connectionStats_ :: Maybe ConnectionStats, customUserProfile :: Maybe Profile}
@@ -961,23 +941,23 @@ instance ToJSON AgentQueueId where
toJSON = strToJSON toJSON = strToJSON
toEncoding = strToJEncoding toEncoding = strToJEncoding
-- data ProtoServersConfig p = ProtoServersConfig {servers :: [ServerCfg p]} data ProtoServersConfig p = ProtoServersConfig {servers :: [ServerCfg p]}
-- deriving (Show) deriving (Show)
-- data AProtoServersConfig = forall p. ProtocolTypeI p => APSC (SProtocolType p) (ProtoServersConfig p) data AProtoServersConfig = forall p. ProtocolTypeI p => APSC (SProtocolType p) (ProtoServersConfig p)
-- deriving instance Show AProtoServersConfig deriving instance Show AProtoServersConfig
-- data UserProtoServers p = UserProtoServers data UserProtoServers p = UserProtoServers
-- { serverProtocol :: SProtocolType p, { serverProtocol :: SProtocolType p,
-- protoServers :: NonEmpty (ServerCfg p), protoServers :: NonEmpty (ServerCfg p),
-- presetServers :: NonEmpty (ServerCfg p) presetServers :: NonEmpty (ServerCfg p)
-- } }
-- deriving (Show) deriving (Show)
-- data AUserProtoServers = forall p. (ProtocolTypeI p, UserProtocol p) => AUPS (UserProtoServers p) data AUserProtoServers = forall p. (ProtocolTypeI p, UserProtocol p) => AUPS (UserProtoServers p)
-- deriving instance Show AUserProtoServers deriving instance Show AUserProtoServers
data ArchiveConfig = ArchiveConfig {archivePath :: FilePath, disableCompression :: Maybe Bool, parentTempDirectory :: Maybe FilePath} data ArchiveConfig = ArchiveConfig {archivePath :: FilePath, disableCompression :: Maybe Bool, parentTempDirectory :: Maybe FilePath}
deriving (Show) deriving (Show)
@@ -1580,28 +1560,28 @@ $(JQ.deriveJSON defaultJSON ''CoreVersionInfo)
$(JQ.deriveJSON defaultJSON ''SlowSQLQuery) $(JQ.deriveJSON defaultJSON ''SlowSQLQuery)
-- instance ProtocolTypeI p => FromJSON (ProtoServersConfig p) where instance ProtocolTypeI p => FromJSON (ProtoServersConfig p) where
-- parseJSON = $(JQ.mkParseJSON defaultJSON ''ProtoServersConfig) parseJSON = $(JQ.mkParseJSON defaultJSON ''ProtoServersConfig)
-- instance ProtocolTypeI p => FromJSON (UserProtoServers p) where instance ProtocolTypeI p => FromJSON (UserProtoServers p) where
-- parseJSON = $(JQ.mkParseJSON defaultJSON ''UserProtoServers) parseJSON = $(JQ.mkParseJSON defaultJSON ''UserProtoServers)
-- instance ProtocolTypeI p => ToJSON (UserProtoServers p) where instance ProtocolTypeI p => ToJSON (UserProtoServers p) where
-- toJSON = $(JQ.mkToJSON defaultJSON ''UserProtoServers) toJSON = $(JQ.mkToJSON defaultJSON ''UserProtoServers)
-- toEncoding = $(JQ.mkToEncoding defaultJSON ''UserProtoServers) toEncoding = $(JQ.mkToEncoding defaultJSON ''UserProtoServers)
-- instance FromJSON AUserProtoServers where instance FromJSON AUserProtoServers where
-- parseJSON v = J.withObject "AUserProtoServers" parse v parseJSON v = J.withObject "AUserProtoServers" parse v
-- where where
-- parse o = do parse o = do
-- AProtocolType (p :: SProtocolType p) <- o .: "serverProtocol" AProtocolType (p :: SProtocolType p) <- o .: "serverProtocol"
-- case userProtocol p of case userProtocol p of
-- Just Dict -> AUPS <$> J.parseJSON @(UserProtoServers p) v Just Dict -> AUPS <$> J.parseJSON @(UserProtoServers p) v
-- Nothing -> fail $ "AUserProtoServers: unsupported protocol " <> show p Nothing -> fail $ "AUserProtoServers: unsupported protocol " <> show p
-- instance ToJSON AUserProtoServers where instance ToJSON AUserProtoServers where
-- toJSON (AUPS s) = $(JQ.mkToJSON defaultJSON ''UserProtoServers) s toJSON (AUPS s) = $(JQ.mkToJSON defaultJSON ''UserProtoServers) s
-- toEncoding (AUPS s) = $(JQ.mkToEncoding defaultJSON ''UserProtoServers) s toEncoding (AUPS s) = $(JQ.mkToEncoding defaultJSON ''UserProtoServers) s
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "RCS") ''RemoteCtrlSessionState) $(JQ.deriveJSON (sumTypeJSON $ dropPrefix "RCS") ''RemoteCtrlSessionState)
@@ -1,54 +0,0 @@
{-# LANGUAGE QuasiQuotes #-}
module Simplex.Chat.Migrations.M20241027_server_operators where
import Database.SQLite.Simple (Query)
import Database.SQLite.Simple.QQ (sql)
m20241027_server_operators :: Query
m20241027_server_operators =
[sql|
CREATE TABLE server_operators (
server_operator_id INTEGER PRIMARY KEY AUTOINCREMENT,
server_operator_tag TEXT,
trade_name TEXT NOT NULL,
legal_name TEXT,
server_domains TEXT,
enabled INTEGER NOT NULL DEFAULT 1,
role_storage INTEGER NOT NULL DEFAULT 1,
role_proxy INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE usage_conditions (
usage_conditions_id INTEGER PRIMARY KEY AUTOINCREMENT,
conditions_commit TEXT NOT NULL UNIQUE,
notified_at TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE operator_usage_conditions (
operator_usage_conditions_id INTEGER PRIMARY KEY AUTOINCREMENT,
server_operator_id INTEGER REFERENCES server_operators (server_operator_id) ON DELETE SET NULL ON UPDATE CASCADE,
server_operator_tag TEXT,
conditions_commit TEXT NOT NULL,
accepted_at TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX idx_operator_usage_conditions_server_operator_id ON operator_usage_conditions(server_operator_id);
CREATE UNIQUE INDEX idx_operator_usage_conditions_conditions_commit ON operator_usage_conditions(conditions_commit, server_operator_id);
|]
down_m20241027_server_operators :: Query
down_m20241027_server_operators =
[sql|
DROP INDEX idx_operator_usage_conditions_conditions_commit;
DROP INDEX idx_operator_usage_conditions_server_operator_id;
DROP TABLE operator_usage_conditions;
DROP TABLE usage_conditions;
DROP TABLE server_operators;
|]
@@ -589,33 +589,6 @@ CREATE TABLE note_folders(
unread_chat INTEGER NOT NULL DEFAULT 0 unread_chat INTEGER NOT NULL DEFAULT 0
); );
CREATE TABLE app_settings(app_settings TEXT NOT NULL); CREATE TABLE app_settings(app_settings TEXT NOT NULL);
CREATE TABLE server_operators(
server_operator_id INTEGER PRIMARY KEY AUTOINCREMENT,
server_operator_tag TEXT,
trade_name TEXT NOT NULL,
legal_name TEXT,
server_domains TEXT,
enabled INTEGER NOT NULL DEFAULT 1,
role_storage INTEGER NOT NULL DEFAULT 1,
role_proxy INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL DEFAULT(datetime('now')),
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
);
CREATE TABLE usage_conditions(
usage_conditions_id INTEGER PRIMARY KEY AUTOINCREMENT,
conditions_commit TEXT NOT NULL UNIQUE,
notified_at TEXT,
created_at TEXT NOT NULL DEFAULT(datetime('now')),
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
);
CREATE TABLE operator_usage_conditions(
operator_usage_conditions_id INTEGER PRIMARY KEY AUTOINCREMENT,
server_operator_id INTEGER REFERENCES server_operators(server_operator_id) ON DELETE SET NULL ON UPDATE CASCADE,
server_operator_tag TEXT,
conditions_commit TEXT NOT NULL,
accepted_at TEXT,
created_at TEXT NOT NULL DEFAULT(datetime('now'))
);
CREATE INDEX contact_profiles_index ON contact_profiles( CREATE INDEX contact_profiles_index ON contact_profiles(
display_name, display_name,
full_name full_name
@@ -917,10 +890,3 @@ CREATE INDEX idx_received_probes_group_member_id on received_probes(
group_member_id group_member_id
); );
CREATE INDEX idx_contact_requests_contact_id ON contact_requests(contact_id); CREATE INDEX idx_contact_requests_contact_id ON contact_requests(contact_id);
CREATE INDEX idx_operator_usage_conditions_server_operator_id ON operator_usage_conditions(
server_operator_id
);
CREATE UNIQUE INDEX idx_operator_usage_conditions_conditions_commit ON operator_usage_conditions(
conditions_commit,
server_operator_id
);
-447
View File
@@ -1,447 +0,0 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
module Simplex.Chat.Operators where
import Control.Applicative ((<|>))
import Data.Aeson (FromJSON (..), ToJSON (..))
import qualified Data.Aeson as J
import qualified Data.Aeson.Encoding as JE
import qualified Data.Aeson.TH as JQ
import Data.FileEmbed
import Data.Foldable (foldMap')
import Data.IORef
import Data.Int (Int64)
import Data.List (find, foldl')
import Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as L
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Data.Maybe (fromMaybe, isNothing, mapMaybe)
import Data.Scientific (floatingOrInteger)
import Data.Set (Set)
import qualified Data.Set as S
import Data.Text (Text)
import qualified Data.Text as T
import Data.Time (addUTCTime)
import Data.Time.Clock (UTCTime, nominalDay)
import Data.Type.Equality
import Database.SQLite.Simple.FromField (FromField (..))
import Database.SQLite.Simple.ToField (ToField (..))
import Language.Haskell.TH.Syntax (lift)
import Simplex.Chat.Operators.Conditions
import Simplex.Chat.Types.Util (textParseJSON)
import Simplex.Messaging.Agent.Env.SQLite (ServerCfg (..), ServerRoles (..), allRoles)
import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, fromTextField_, sumTypeJSON)
import Simplex.Messaging.Protocol (AProtoServerWithAuth (..), AProtocolType (..), ProtoServerWithAuth (..), ProtocolServer (..), ProtocolType (..), ProtocolTypeI, SProtocolType (..), UserProtocol)
import Simplex.Messaging.Transport.Client (TransportHost (..))
import Simplex.Messaging.Util (atomicModifyIORef'_, safeDecodeUtf8, (<$?>))
usageConditionsCommit :: Text
usageConditionsCommit = "165143a1112308c035ac00ed669b96b60599aa1c"
previousConditionsCommit :: Text
previousConditionsCommit = "edf99fcd1d7d38d2501d19608b94c084cf00f2ac"
usageConditionsText :: Text
usageConditionsText =
$( let s = $(embedFile =<< makeRelativeToProject "PRIVACY.md")
in [|stripFrontMatter (safeDecodeUtf8 $(lift s))|]
)
data DBStored = DBStored | DBNew
data SDBStored (s :: DBStored) where
SDBStored :: SDBStored 'DBStored
SDBNew :: SDBStored 'DBNew
deriving instance Show (SDBStored s)
class DBStoredI s where sdbStored :: SDBStored s
instance DBStoredI 'DBStored where sdbStored = SDBStored
instance DBStoredI 'DBNew where sdbStored = SDBNew
instance TestEquality SDBStored where
testEquality SDBStored SDBStored = Just Refl
testEquality SDBNew SDBNew = Just Refl
testEquality _ _ = Nothing
data DBEntityId' (s :: DBStored) where
DBEntityId :: Int64 -> DBEntityId' 'DBStored
DBNewEntity :: DBEntityId' 'DBNew
deriving instance Show (DBEntityId' s)
type DBEntityId = DBEntityId' 'DBStored
type DBNewEntity = DBEntityId' 'DBNew
data ADBEntityId = forall s. DBStoredI s => AEI (SDBStored s) (DBEntityId' s)
pattern ADBEntityId :: Int64 -> ADBEntityId
pattern ADBEntityId i = AEI SDBStored (DBEntityId i)
pattern ADBNewEntity :: ADBEntityId
pattern ADBNewEntity = AEI SDBNew DBNewEntity
data OperatorTag = OTSimplex | OTXyz
deriving (Eq, Ord, Show)
instance FromField OperatorTag where fromField = fromTextField_ textDecode
instance ToField OperatorTag where toField = toField . textEncode
instance FromJSON OperatorTag where
parseJSON = textParseJSON "OperatorTag"
instance ToJSON OperatorTag where
toJSON = J.String . textEncode
toEncoding = JE.text . textEncode
instance TextEncoding OperatorTag where
textDecode = \case
"simplex" -> Just OTSimplex
"xyz" -> Just OTXyz
_ -> Nothing
textEncode = \case
OTSimplex -> "simplex"
OTXyz -> "xyz"
-- this and other types only define instances of serialization for known DB IDs only,
-- entities without IDs cannot be serialized to JSON
instance FromField DBEntityId where fromField f = DBEntityId <$> fromField f
instance ToField DBEntityId where toField (DBEntityId i) = toField i
data UsageConditions = UsageConditions
{ conditionsId :: Int64,
conditionsCommit :: Text,
notifiedAt :: Maybe UTCTime,
createdAt :: UTCTime
}
deriving (Show)
data UsageConditionsAction
= UCAReview {operators :: [ServerOperator], deadline :: Maybe UTCTime, showNotice :: Bool}
| UCAAccepted {operators :: [ServerOperator]}
deriving (Show)
usageConditionsAction :: [ServerOperator] -> UsageConditions -> UTCTime -> Maybe UsageConditionsAction
usageConditionsAction operators UsageConditions {createdAt, notifiedAt} now = do
let enabledOperators = filter (\ServerOperator {enabled} -> enabled) operators
if
| null enabledOperators -> Nothing
| all conditionsAccepted enabledOperators ->
let acceptedForOperators = filter conditionsAccepted operators
in Just $ UCAAccepted acceptedForOperators
| otherwise ->
let acceptForOperators = filter (not . conditionsAccepted) enabledOperators
deadline = conditionsRequiredOrDeadline createdAt (fromMaybe now notifiedAt)
showNotice = isNothing notifiedAt
in Just $ UCAReview acceptForOperators deadline showNotice
conditionsRequiredOrDeadline :: UTCTime -> UTCTime -> Maybe UTCTime
conditionsRequiredOrDeadline createdAt notifiedAtOrNow =
if notifiedAtOrNow < addUTCTime (14 * nominalDay) createdAt
then Just $ conditionsDeadline notifiedAtOrNow
else Nothing -- required
where
conditionsDeadline :: UTCTime -> UTCTime
conditionsDeadline = addUTCTime (31 * nominalDay)
data ConditionsAcceptance
= CAAccepted {acceptedAt :: Maybe UTCTime}
| CARequired {deadline :: Maybe UTCTime}
deriving (Show)
type ServerOperator = ServerOperator' 'DBStored
type NewServerOperator = ServerOperator' 'DBNew
data AServerOperator = forall s. ASO (SDBStored s) (ServerOperator' s)
deriving instance Show AServerOperator
data ServerOperator' s = ServerOperator
{ operatorId :: DBEntityId' s,
operatorTag :: Maybe OperatorTag,
tradeName :: Text,
legalName :: Maybe Text,
serverDomains :: [Text],
conditionsAcceptance :: ConditionsAcceptance,
enabled :: Bool,
roles :: ServerRoles
}
deriving (Show)
conditionsAccepted :: ServerOperator -> Bool
conditionsAccepted ServerOperator {conditionsAcceptance} = case conditionsAcceptance of
CAAccepted {} -> True
_ -> False
data UserOperatorServers = UserOperatorServers
{ operator :: Maybe ServerOperator,
smpServers :: [UserServer 'PSMP],
xftpServers :: [UserServer 'PXFTP]
}
deriving (Show)
data UpdatedUserOperatorServers = UpdatedUserOperatorServers
{ operator :: Maybe ServerOperator,
smpServers :: [AUserServer 'PSMP],
xftpServers :: [AUserServer 'PXFTP]
}
deriving (Show)
updatedServers :: UserProtocol p => UpdatedUserOperatorServers -> SProtocolType p -> [AUserServer p]
updatedServers UpdatedUserOperatorServers {smpServers, xftpServers} = \case
SPSMP -> smpServers
SPXFTP -> xftpServers
type UserServer p = UserServer' 'DBStored p
type NewUserServer p = UserServer' 'DBNew p
data AUserServer p = forall s. AUS (SDBStored s) (UserServer' s p)
deriving instance Show (AUserServer p)
data UserServer' s p = UserServer
{ serverId :: DBEntityId' s,
server :: ProtoServerWithAuth p,
preset :: Bool,
tested :: Maybe Bool,
enabled :: Bool,
deleted :: Bool
}
deriving (Show)
data PresetOperator = PresetOperator
{ operator :: Maybe NewServerOperator,
smp :: [NewUserServer 'PSMP],
useSMP :: Int,
xftp :: [NewUserServer 'PXFTP],
useXFTP :: Int
}
operatorServers :: UserProtocol p => SProtocolType p -> PresetOperator -> [NewUserServer p]
operatorServers p PresetOperator {smp, xftp} = case p of
SPSMP -> smp
SPXFTP -> xftp
operatorServersToUse :: UserProtocol p => SProtocolType p -> PresetOperator -> Int
operatorServersToUse p PresetOperator {useSMP, useXFTP} = case p of
SPSMP -> useSMP
SPXFTP -> useXFTP
presetServer :: Bool -> ProtoServerWithAuth p -> NewUserServer p
presetServer enabled server =
UserServer {serverId = DBNewEntity, server, preset = True, tested = Nothing, enabled, deleted = False}
-- This function should be used inside DB transaction to update conditions in the database
-- it evaluates to (conditions to mark as accepted to SimpleX operator, current conditions, and conditions to add)
usageConditionsToAdd :: Bool -> UTCTime -> [UsageConditions] -> (Maybe UsageConditions, UsageConditions, [UsageConditions])
usageConditionsToAdd = usageConditionsToAdd' previousConditionsCommit usageConditionsCommit
-- This function is used in unit tests
usageConditionsToAdd' :: Text -> Text -> Bool -> UTCTime -> [UsageConditions] -> (Maybe UsageConditions, UsageConditions, [UsageConditions])
usageConditionsToAdd' prevCommit sourceCommit newUser createdAt = \case
[]
| newUser -> (Just sourceCond, sourceCond, [sourceCond])
| otherwise -> (Just prevCond, sourceCond, [prevCond, sourceCond])
where
prevCond = conditions 1 prevCommit
sourceCond = conditions 2 sourceCommit
conds
| hasSourceCond -> (Nothing, last conds, [])
| otherwise -> (Nothing, sourceCond, [sourceCond])
where
hasSourceCond = any ((sourceCommit ==) . conditionsCommit) conds
sourceCond = conditions cId sourceCommit
cId = maximum (map conditionsId conds) + 1
where
conditions cId commit = UsageConditions {conditionsId = cId, conditionsCommit = commit, notifiedAt = Nothing, createdAt}
-- This function should be used inside DB transaction to update operators.
-- It allows to add/remove/update preset operators in the database preserving enabled and roles settings,
-- and preserves custom operators without tags for forward compatibility.
updatedServerOperators :: NonEmpty PresetOperator -> [ServerOperator] -> [AServerOperator]
updatedServerOperators presetOps storedOps =
foldr addPreset [] presetOps
<> map (ASO SDBStored) (filter (isNothing . operatorTag) storedOps)
where
-- TODO remove domains of preset operators from custom
addPreset PresetOperator {operator} = case operator of
Nothing -> id
Just presetOp -> (storedOp' :)
where
storedOp' = case find ((operatorTag presetOp ==) . operatorTag) storedOps of
Just ServerOperator {operatorId, conditionsAcceptance, enabled, roles} ->
ASO SDBStored presetOp {operatorId, conditionsAcceptance, enabled, roles}
Nothing -> ASO SDBNew presetOp
-- This function should be used inside DB transaction to update servers.
updatedUserServers :: forall p. UserProtocol p => SProtocolType p -> NonEmpty PresetOperator -> NonEmpty (NewUserServer p) -> [UserServer p] -> NonEmpty (AUserServer p)
updatedUserServers _ _ randomSrvs [] = L.map (AUS SDBNew) randomSrvs
updatedUserServers p presetOps randomSrvs srvs =
fromMaybe (L.map (AUS SDBNew) randomSrvs) (L.nonEmpty updatedSrvs)
where
updatedSrvs = map userServer presetSrvs <> map (AUS SDBStored) (filter customServer srvs)
storedSrvs :: Map (ProtoServerWithAuth p) (UserServer p)
storedSrvs = foldl' (\ss srv@UserServer {server} -> M.insert server srv ss) M.empty srvs
customServer :: UserServer p -> Bool
customServer srv = not (preset srv) && all (`S.notMember` presetHosts) (srvHost srv)
presetSrvs :: [NewUserServer p]
presetSrvs = concatMap (operatorServers p) presetOps
presetHosts :: Set TransportHost
presetHosts = foldMap' (S.fromList . L.toList . srvHost) presetSrvs
userServer :: NewUserServer p -> AUserServer p
userServer srv@UserServer {server} = maybe (AUS SDBNew srv) (AUS SDBStored) (M.lookup server storedSrvs)
srvHost :: UserServer' s p -> NonEmpty TransportHost
srvHost UserServer {server = ProtoServerWithAuth srv _} = host srv
agentServerCfgs :: [(Text, ServerOperator)] -> NonEmpty (UserServer' s p) -> NonEmpty (ServerCfg p)
agentServerCfgs opDomains = L.map agentServer
where
agentServer :: UserServer' s p -> ServerCfg p
agentServer srv@UserServer {server, enabled} =
case find (\(d, _) -> any (matchingHost d) (srvHost srv)) opDomains of
Just (_, ServerOperator {operatorId = DBEntityId opId, enabled = opEnabled, roles}) ->
ServerCfg {server, operator = Just opId, enabled = opEnabled && enabled, roles}
Nothing ->
ServerCfg {server, operator = Nothing, enabled, roles = allRoles}
matchingHost :: Text -> TransportHost -> Bool
matchingHost d = \case
THDomainName h -> d `T.isSuffixOf` T.pack h
_ -> False
operatorDomains :: [ServerOperator] -> [(Text, ServerOperator)]
operatorDomains = foldr (\op ds -> foldr (\d -> ((d, op) :)) ds (serverDomains op)) []
groupByOperator :: ([ServerOperator], [UserServer 'PSMP], [UserServer 'PXFTP]) -> IO [UserOperatorServers]
groupByOperator (ops, smpSrvs, xftpSrvs) = do
ss <- mapM (\op -> (serverDomains op,) <$> newIORef (UserOperatorServers (Just op) [] [])) ops
custom <- newIORef $ UserOperatorServers Nothing [] []
mapM_ (addServer ss custom addSMP) (reverse smpSrvs)
mapM_ (addServer ss custom addXFTP) (reverse xftpSrvs)
mapM (readIORef . snd) ss
where
addServer :: [([Text], IORef UserOperatorServers)] -> IORef UserOperatorServers -> (UserServer p -> UserOperatorServers -> UserOperatorServers) -> UserServer p -> IO ()
addServer ss custom add srv =
let v = maybe custom snd $ find (\(ds, _) -> any (\d -> any (matchingHost d) (srvHost srv)) ds) ss
in atomicModifyIORef'_ v $ add srv
addSMP srv s@UserOperatorServers {smpServers} = (s :: UserOperatorServers) {smpServers = srv : smpServers}
addXFTP srv s@UserOperatorServers {xftpServers} = (s :: UserOperatorServers) {xftpServers = srv : xftpServers}
data UserServersError
= USEStorageMissing {protocol :: AProtocolType}
| USEProxyMissing {protocol :: AProtocolType}
| USEDuplicateServer {protocol :: AProtocolType, duplicateServer :: AProtoServerWithAuth, duplicateHost :: TransportHost}
deriving (Show)
validateUserServers :: NonEmpty UpdatedUserOperatorServers -> [UserServersError]
validateUserServers uss =
missingRolesErr SPSMP storage USEStorageMissing
<> missingRolesErr SPSMP proxy USEProxyMissing
<> missingRolesErr SPXFTP storage USEStorageMissing
<> duplicatServerErrs SPSMP
<> duplicatServerErrs SPXFTP
where
missingRolesErr :: (ProtocolTypeI p, UserProtocol p) => SProtocolType p -> (ServerRoles -> Bool) -> (AProtocolType -> UserServersError) -> [UserServersError]
missingRolesErr p roleSel err = [err (AProtocolType p) | hasRole]
where
hasRole =
any (\(AUS _ UserServer {deleted, enabled}) -> enabled && not deleted) $
concatMap (`updatedServers` p) $ filter roleEnabled (L.toList uss)
roleEnabled UpdatedUserOperatorServers {operator} =
maybe True (\ServerOperator {enabled, roles} -> enabled && roleSel roles) operator
duplicatServerErrs :: (ProtocolTypeI p, UserProtocol p) => SProtocolType p -> [UserServersError]
duplicatServerErrs p = mapMaybe duplicateErr_ srvs
where
srvs =
filter (\(AUS _ UserServer {deleted}) -> not deleted) $
concatMap (`updatedServers` p) (L.toList uss)
duplicateErr_ (AUS _ srv@UserServer {server}) =
USEDuplicateServer (AProtocolType p) (AProtoServerWithAuth p server)
<$> find (`S.member` duplicateHosts) (srvHost srv)
duplicateHosts = snd $ foldl' (\acc (AUS _ srv) -> foldl' addHost acc $ srvHost srv) (S.empty, S.empty) srvs
addHost (hs, dups) h
| h `S.member` hs = (hs, S.insert h dups)
| otherwise = (S.insert h hs, dups)
instance ToJSON ADBEntityId where
toEncoding (AEI _ dbId) = toEncoding dbId
toJSON (AEI _ dbId) = toJSON dbId
instance ToJSON (DBEntityId' s) where
toEncoding = \case
DBEntityId i -> toEncoding i
DBNewEntity -> JE.null_
toJSON = \case
DBEntityId i -> toJSON i
DBNewEntity -> J.Null
instance FromJSON ADBEntityId where
parseJSON (J.Null) = pure $ AEI SDBNew DBNewEntity
parseJSON (J.Number n) = case floatingOrInteger n of
Left (_ :: Double) -> fail "bad ADBEntityId"
Right i -> pure $ AEI SDBStored (DBEntityId $ fromInteger i)
parseJSON _ = fail "bad ADBEntityId"
instance DBStoredI s => FromJSON (DBEntityId' s) where
parseJSON v = (\(AEI _ dbId) -> checkDBStored dbId) <$?> parseJSON v
checkDBStored :: forall t s s'. (DBStoredI s, DBStoredI s') => t s' -> Either String (t s)
checkDBStored x = case testEquality (sdbStored @s) (sdbStored @s') of
Just Refl -> Right x
Nothing -> Left "bad DBStored"
$(JQ.deriveJSON defaultJSON ''UsageConditions)
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "CA") ''ConditionsAcceptance)
instance ToJSON (ServerOperator' s) where
toEncoding = $(JQ.mkToEncoding defaultJSON ''ServerOperator')
toJSON = $(JQ.mkToJSON defaultJSON ''ServerOperator')
instance DBStoredI s => FromJSON (ServerOperator' s) where
parseJSON = $(JQ.mkParseJSON defaultJSON ''ServerOperator')
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "UCA") ''UsageConditionsAction)
instance ProtocolTypeI p => ToJSON (UserServer' s p) where
toEncoding = $(JQ.mkToEncoding defaultJSON ''UserServer')
toJSON = $(JQ.mkToJSON defaultJSON ''UserServer')
instance (DBStoredI s, ProtocolTypeI p) => FromJSON (UserServer' s p) where
parseJSON = $(JQ.mkParseJSON defaultJSON ''UserServer')
instance ProtocolTypeI p => FromJSON (AUserServer p) where
parseJSON v = (AUS SDBStored <$> parseJSON v) <|> (AUS SDBNew <$> parseJSON v)
$(JQ.deriveJSON defaultJSON ''UserOperatorServers)
instance FromJSON UpdatedUserOperatorServers where
parseJSON = $(JQ.mkParseJSON defaultJSON ''UpdatedUserOperatorServers)
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "USE") ''UserServersError)
-19
View File
@@ -1,19 +0,0 @@
{-# LANGUAGE OverloadedStrings #-}
module Simplex.Chat.Operators.Conditions where
import Data.Char (isSpace)
import Data.Text (Text)
import qualified Data.Text as T
stripFrontMatter :: Text -> Text
stripFrontMatter =
T.unlines
. dropWhile ("# " `T.isPrefixOf`) -- strip title
. dropWhile (T.all isSpace)
. dropWhile fm
. (\ls -> let ls' = dropWhile (not . fm) ls in if null ls' then ls else ls')
. dropWhile fm
. T.lines
where
fm = ("---" `T.isPrefixOf`)
+2 -1
View File
@@ -7,6 +7,7 @@ module Simplex.Chat.Stats where
import qualified Data.Aeson.TH as J import qualified Data.Aeson.TH as J
import Data.List (partition) import Data.List (partition)
import Data.List.NonEmpty (NonEmpty)
import Data.Map.Strict (Map) import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M import qualified Data.Map.Strict as M
import Data.Maybe (fromMaybe, isJust) import Data.Maybe (fromMaybe, isJust)
@@ -130,7 +131,7 @@ data NtfServerSummary = NtfServerSummary
-- - users are passed to exclude hidden users from totalServersSummary; -- - users are passed to exclude hidden users from totalServersSummary;
-- - if currentUser is hidden, it should be accounted in totalServersSummary; -- - if currentUser is hidden, it should be accounted in totalServersSummary;
-- - known is set only in user level summaries based on passed userSMPSrvs and userXFTPSrvs -- - known is set only in user level summaries based on passed userSMPSrvs and userXFTPSrvs
toPresentedServersSummary :: AgentServersSummary -> [User] -> User -> [SMPServer] -> [XFTPServer] -> [NtfServer] -> PresentedServersSummary toPresentedServersSummary :: AgentServersSummary -> [User] -> User -> NonEmpty SMPServer -> NonEmpty XFTPServer -> [NtfServer] -> PresentedServersSummary
toPresentedServersSummary agentSummary users currentUser userSMPSrvs userXFTPSrvs userNtfSrvs = do toPresentedServersSummary agentSummary users currentUser userSMPSrvs userXFTPSrvs userNtfSrvs = do
let (userSMPSrvsSumms, allSMPSrvsSumms) = accSMPSrvsSummaries let (userSMPSrvsSumms, allSMPSrvsSumms) = accSMPSrvsSummaries
(userSMPCurr, userSMPPrev, userSMPProx) = smpSummsIntoCategories userSMPSrvsSumms (userSMPCurr, userSMPPrev, userSMPProx) = smpSummsIntoCategories userSMPSrvsSumms
+1 -3
View File
@@ -114,7 +114,6 @@ import Simplex.Chat.Migrations.M20240827_calls_uuid
import Simplex.Chat.Migrations.M20240920_user_order import Simplex.Chat.Migrations.M20240920_user_order
import Simplex.Chat.Migrations.M20241008_indexes import Simplex.Chat.Migrations.M20241008_indexes
import Simplex.Chat.Migrations.M20241010_contact_requests_contact_id import Simplex.Chat.Migrations.M20241010_contact_requests_contact_id
import Simplex.Chat.Migrations.M20241027_server_operators
import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..)) import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..))
schemaMigrations :: [(String, Query, Maybe Query)] schemaMigrations :: [(String, Query, Maybe Query)]
@@ -228,8 +227,7 @@ schemaMigrations =
("20240827_calls_uuid", m20240827_calls_uuid, Just down_m20240827_calls_uuid), ("20240827_calls_uuid", m20240827_calls_uuid, Just down_m20240827_calls_uuid),
("20240920_user_order", m20240920_user_order, Just down_m20240920_user_order), ("20240920_user_order", m20240920_user_order, Just down_m20240920_user_order),
("20241008_indexes", m20241008_indexes, Just down_m20241008_indexes), ("20241008_indexes", m20241008_indexes, Just down_m20241008_indexes),
("20241010_contact_requests_contact_id", m20241010_contact_requests_contact_id, Just down_m20241010_contact_requests_contact_id), ("20241010_contact_requests_contact_id", m20241010_contact_requests_contact_id, Just down_m20241010_contact_requests_contact_id)
("20241027_server_operators", m20241027_server_operators, Just down_m20241027_server_operators)
] ]
-- | The list of migrations in ascending order by date -- | The list of migrations in ascending order by date
+23 -307
View File
@@ -1,8 +1,5 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
@@ -50,19 +47,7 @@ module Simplex.Chat.Store.Profiles
getContactWithoutConnViaAddress, getContactWithoutConnViaAddress,
updateUserAddressAutoAccept, updateUserAddressAutoAccept,
getProtocolServers, getProtocolServers,
getUpdateUserServers,
-- overwriteOperatorsAndServers,
overwriteProtocolServers, overwriteProtocolServers,
insertProtocolServer,
getUpdateServerOperators,
getServerOperators,
getUserServers,
setServerOperators,
getCurrentUsageConditions,
getLatestAcceptedConditions,
setConditionsNotified,
acceptConditions,
setUserServers,
createCall, createCall,
deleteCalls, deleteCalls,
getCalls, getCalls,
@@ -85,14 +70,12 @@ import Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as L import qualified Data.List.NonEmpty as L
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as T
import Data.Text.Encoding (decodeLatin1, encodeUtf8) import Data.Text.Encoding (decodeLatin1, encodeUtf8)
import Data.Time.Clock (UTCTime (..), getCurrentTime) import Data.Time.Clock (UTCTime (..), getCurrentTime)
import Database.SQLite.Simple (NamedParam (..), Only (..), Query, (:.) (..)) import Database.SQLite.Simple (NamedParam (..), Only (..), (:.) (..))
import Database.SQLite.Simple.QQ (sql) import Database.SQLite.Simple.QQ (sql)
import Simplex.Chat.Call import Simplex.Chat.Call
import Simplex.Chat.Messages import Simplex.Chat.Messages
import Simplex.Chat.Operators
import Simplex.Chat.Protocol import Simplex.Chat.Protocol
import Simplex.Chat.Store.Direct import Simplex.Chat.Store.Direct
import Simplex.Chat.Store.Shared import Simplex.Chat.Store.Shared
@@ -100,7 +83,7 @@ import Simplex.Chat.Types
import Simplex.Chat.Types.Preferences import Simplex.Chat.Types.Preferences
import Simplex.Chat.Types.Shared import Simplex.Chat.Types.Shared
import Simplex.Chat.Types.UITheme import Simplex.Chat.Types.UITheme
import Simplex.Messaging.Agent.Env.SQLite (ServerRoles (..)) import Simplex.Messaging.Agent.Env.SQLite (ServerCfg (..))
import Simplex.Messaging.Agent.Protocol (ACorrId, ConnId, UserId) import Simplex.Messaging.Agent.Protocol (ACorrId, ConnId, UserId)
import Simplex.Messaging.Agent.Store.SQLite (firstRow, maybeFirstRow) import Simplex.Messaging.Agent.Store.SQLite (firstRow, maybeFirstRow)
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
@@ -108,7 +91,7 @@ import qualified Simplex.Messaging.Crypto as C
import qualified Simplex.Messaging.Crypto.Ratchet as CR import qualified Simplex.Messaging.Crypto.Ratchet as CR
import Simplex.Messaging.Encoding.String import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Parsers (defaultJSON) import Simplex.Messaging.Parsers (defaultJSON)
import Simplex.Messaging.Protocol (BasicAuth (..), ProtoServerWithAuth (..), ProtocolServer (..), ProtocolType (..), ProtocolTypeI (..), SProtocolType (..), SubscriptionMode, UserProtocol) import Simplex.Messaging.Protocol (BasicAuth (..), ProtoServerWithAuth (..), ProtocolServer (..), ProtocolTypeI (..), SubscriptionMode)
import Simplex.Messaging.Transport.Client (TransportHost) import Simplex.Messaging.Transport.Client (TransportHost)
import Simplex.Messaging.Util (eitherToMaybe, safeDecodeUtf8) import Simplex.Messaging.Util (eitherToMaybe, safeDecodeUtf8)
@@ -532,309 +515,42 @@ updateUserAddressAutoAccept db user@User {userId} autoAccept = do
Just AutoAccept {acceptIncognito, autoReply} -> (True, acceptIncognito, autoReply) Just AutoAccept {acceptIncognito, autoReply} -> (True, acceptIncognito, autoReply)
_ -> (False, False, Nothing) _ -> (False, False, Nothing)
getUpdateUserServers :: forall p. (ProtocolTypeI p, UserProtocol p) => DB.Connection -> SProtocolType p -> NonEmpty PresetOperator -> NonEmpty (NewUserServer p) -> User -> IO (NonEmpty (UserServer p)) getProtocolServers :: forall p. ProtocolTypeI p => DB.Connection -> User -> IO [ServerCfg p]
getUpdateUserServers db p presetOps randomSrvs user = do getProtocolServers db User {userId} =
ts <- getCurrentTime map toServerCfg
srvs <- getProtocolServers db p user
let srvs' = updatedUserServers p presetOps randomSrvs srvs
mapM (upsertServer ts) srvs'
where
upsertServer :: UTCTime -> AUserServer p -> IO (UserServer p)
upsertServer ts (AUS _ s@UserServer {serverId}) = case serverId of
DBNewEntity -> insertProtocolServer db p user ts s
DBEntityId _ -> updateProtocolServer db p ts s $> s
getProtocolServers :: forall p. ProtocolTypeI p => DB.Connection -> SProtocolType p -> User -> IO [UserServer p]
getProtocolServers db p User {userId} =
map toUserServer
<$> DB.query <$> DB.query
db db
[sql| [sql|
SELECT smp_server_id, host, port, key_hash, basic_auth, preset, tested, enabled SELECT host, port, key_hash, basic_auth, preset, tested, enabled
FROM protocol_servers FROM protocol_servers
WHERE user_id = ? AND protocol = ? WHERE user_id = ? AND protocol = ?;
|] |]
(userId, decodeLatin1 $ strEncode p) (userId, decodeLatin1 $ strEncode protocol)
where where
toUserServer :: (DBEntityId, NonEmpty TransportHost, String, C.KeyHash, Maybe Text, Bool, Maybe Bool, Bool) -> UserServer p protocol = protocolTypeI @p
toUserServer (serverId, host, port, keyHash, auth_, preset, tested, enabled) = toServerCfg :: (NonEmpty TransportHost, String, C.KeyHash, Maybe Text, Bool, Maybe Bool, Bool) -> ServerCfg p
let server = ProtoServerWithAuth (ProtocolServer p host port keyHash) (BasicAuth . encodeUtf8 <$> auth_) toServerCfg (host, port, keyHash, auth_, preset, tested, enabled) =
in UserServer {serverId, server, preset, tested, enabled, deleted = False} let server = ProtoServerWithAuth (ProtocolServer protocol host port keyHash) (BasicAuth . encodeUtf8 <$> auth_)
in ServerCfg {server, preset, tested, enabled}
-- TODO remove overwriteProtocolServers :: forall p. ProtocolTypeI p => DB.Connection -> User -> [ServerCfg p] -> ExceptT StoreError IO ()
-- overwriteOperatorsAndServers :: forall p. ProtocolTypeI p => DB.Connection -> User -> Maybe [ServerOperator] -> [ServerCfg p] -> ExceptT StoreError IO [ServerCfg p] overwriteProtocolServers db User {userId} servers =
-- overwriteOperatorsAndServers db user@User {userId} operators_ servers = do
overwriteProtocolServers :: ProtocolTypeI p => DB.Connection -> SProtocolType p -> User -> [UserServer p] -> ExceptT StoreError IO ()
overwriteProtocolServers db p User {userId} servers =
-- liftIO $ mapM_ (updateServerOperators_ db) operators_
checkConstraint SEUniqueID . ExceptT $ do checkConstraint SEUniqueID . ExceptT $ do
currentTs <- getCurrentTime currentTs <- getCurrentTime
DB.execute db "DELETE FROM protocol_servers WHERE user_id = ? AND protocol = ? " (userId, decodeLatin1 $ strEncode p) DB.execute db "DELETE FROM protocol_servers WHERE user_id = ? AND protocol = ? " (userId, protocol)
forM_ servers $ \UserServer {serverId, server, preset, tested, enabled} -> do forM_ servers $ \ServerCfg {server, preset, tested, enabled} -> do
let ProtoServerWithAuth ProtocolServer {host, port, keyHash} auth_ = server
DB.execute DB.execute
db db
[sql| [sql|
INSERT INTO protocol_servers INSERT INTO protocol_servers
(server_id, protocol, host, port, key_hash, basic_auth, preset, tested, enabled, user_id, created_at, updated_at) (protocol, host, port, key_hash, basic_auth, preset, tested, enabled, user_id, created_at, updated_at)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?) VALUES (?,?,?,?,?,?,?,?,?,?,?)
|] |]
(Only serverId :. serverColumns p server :. (preset, tested, enabled, userId, currentTs, currentTs)) ((protocol, host, port, keyHash, safeDecodeUtf8 . unBasicAuth <$> auth_) :. (preset, tested, enabled, userId, currentTs, currentTs))
pure $ Right () pure $ Right ()
insertProtocolServer :: forall p. ProtocolTypeI p => DB.Connection -> SProtocolType p -> User -> UTCTime -> NewUserServer p -> IO (UserServer p)
insertProtocolServer db p User {userId} ts srv@UserServer {server, preset, tested, enabled} = do
DB.execute
db
[sql|
INSERT INTO protocol_servers
(protocol, host, port, key_hash, basic_auth, preset, tested, enabled, user_id, created_at, updated_at)
VALUES (?,?,?,?,?,?,?,?,?,?,?)
|]
(serverColumns p server :. (preset, tested, enabled, userId, ts, ts))
sId <- insertedRowId db
pure (srv :: NewUserServer p) {serverId = DBEntityId sId}
updateProtocolServer :: ProtocolTypeI p => DB.Connection -> SProtocolType p -> UTCTime -> UserServer p -> IO ()
updateProtocolServer db p ts UserServer {serverId, server, preset, tested, enabled} =
DB.execute
db
[sql|
UPDATE protocol_servers
SET protocol = ?, host = ?, port = ?, key_hash = ?, basic_auth = ?,
preset = ?, tested = ?, enabled = ?, updated_at = ?
WHERE smp_server_id = ?
|]
(serverColumns p server :. (preset, tested, enabled, ts, serverId))
serverColumns :: ProtocolTypeI p => SProtocolType p -> ProtoServerWithAuth p -> (Text, NonEmpty TransportHost, String, C.KeyHash, Maybe Text)
serverColumns p (ProtoServerWithAuth ProtocolServer {host, port, keyHash} auth_) =
let protocol = decodeLatin1 $ strEncode p
auth = safeDecodeUtf8 . unBasicAuth <$> auth_
in (protocol, host, port, keyHash, auth)
getServerOperators :: DB.Connection -> ExceptT StoreError IO ([ServerOperator], Maybe UsageConditionsAction)
getServerOperators db = do
currentConds <- getCurrentUsageConditions db
liftIO $ do
now <- getCurrentTime
latestAcceptedConds_ <- getLatestAcceptedConditions db
let getConds op = (\ca -> op {conditionsAcceptance = ca}) <$> getOperatorConditions_ db op currentConds latestAcceptedConds_ now
operators <- mapM getConds =<< getServerOperators_ db
pure (operators, usageConditionsAction operators currentConds now)
getUserServers :: DB.Connection -> User -> ExceptT StoreError IO ([ServerOperator], [UserServer 'PSMP], [UserServer 'PXFTP])
getUserServers db user =
(,,)
<$> (fst <$> getServerOperators db)
<*> liftIO (getProtocolServers db SPSMP user)
<*> liftIO (getProtocolServers db SPXFTP user)
setServerOperators :: DB.Connection -> NonEmpty ServerOperator -> IO ()
setServerOperators db ops = do
currentTs <- getCurrentTime
mapM_ (updateServerOperator db currentTs) ops
updateServerOperator :: DB.Connection -> UTCTime -> ServerOperator -> IO ()
updateServerOperator db currentTs ServerOperator {operatorId, enabled, roles = ServerRoles {storage, proxy}} =
DB.execute
db
[sql|
UPDATE server_operators
SET enabled = ?, role_storage = ?, role_proxy = ?, updated_at = ?
WHERE server_operator_id = ?
|]
(enabled, storage, proxy, operatorId, currentTs)
getUpdateServerOperators :: DB.Connection -> NonEmpty PresetOperator -> Bool -> IO [ServerOperator]
getUpdateServerOperators db presetOps newUser = do
conds <- map toUsageConditions <$> DB.query_ db usageCondsQuery
now <- getCurrentTime
let (acceptForSimplex_, currentConds, condsToAdd) = usageConditionsToAdd newUser now conds
mapM_ insertConditions condsToAdd
latestAcceptedConds_ <- getLatestAcceptedConditions db
ops <- updatedServerOperators presetOps <$> getServerOperators_ db
forM ops $ \(ASO _ op) ->
case operatorId op of
DBNewEntity -> do
op' <- insertOperator op
case (operatorTag op', acceptForSimplex_) of
(Just OTSimplex, Just cond) -> autoAcceptConditions op' cond
_ -> pure op'
DBEntityId _ -> do
updateOperator op
getOperatorConditions_ db op currentConds latestAcceptedConds_ now >>= \case
CARequired Nothing | operatorTag op == Just OTSimplex -> autoAcceptConditions op currentConds
CARequired (Just ts) | ts < now -> autoAcceptConditions op currentConds
ca -> pure op {conditionsAcceptance = ca}
where where
insertConditions UsageConditions {conditionsId, conditionsCommit, notifiedAt, createdAt} = protocol = decodeLatin1 $ strEncode $ protocolTypeI @p
DB.execute
db
[sql|
INSERT INTO usage_conditions
(usage_conditions_id, conditions_commit, notified_at, created_at)
VALUES (?,?,?,?)
|]
(conditionsId, conditionsCommit, notifiedAt, createdAt)
updateOperator :: ServerOperator -> IO ()
updateOperator ServerOperator {operatorId, tradeName, legalName, serverDomains, enabled, roles = ServerRoles {storage, proxy}} =
DB.execute
db
[sql|
UPDATE server_operators
SET trade_name = ?, legal_name = ?, server_domains = ?, enabled = ?, role_storage = ?, role_proxy = ?
WHERE server_operator_id = ?
|]
(tradeName, legalName, T.intercalate "," serverDomains, enabled, storage, proxy, operatorId)
insertOperator :: NewServerOperator -> IO ServerOperator
insertOperator op@ServerOperator {operatorTag, tradeName, legalName, serverDomains, enabled, roles = ServerRoles {storage, proxy}} = do
DB.execute
db
[sql|
INSERT INTO server_operators
(server_operator_tag, trade_name, legal_name, server_domains, enabled, role_storage, role_proxy)
VALUES (?,?,?,?,?,?,?)
|]
(operatorTag, tradeName, legalName, T.intercalate "," serverDomains, enabled, storage, proxy)
opId <- insertedRowId db
pure op {operatorId = DBEntityId opId}
autoAcceptConditions op UsageConditions {conditionsCommit} =
acceptConditions_ db op conditionsCommit Nothing
$> op {conditionsAcceptance = CAAccepted Nothing}
serverOperatorQuery :: Query
serverOperatorQuery =
[sql|
SELECT server_operator_id, server_operator_tag, trade_name, legal_name,
server_domains, enabled, role_storage, role_proxy
FROM server_operators
|]
getServerOperators_ :: DB.Connection -> IO [ServerOperator]
getServerOperators_ db = map toServerOperator <$> DB.query_ db serverOperatorQuery
toServerOperator :: (DBEntityId, Maybe OperatorTag, Text, Maybe Text, Text, Bool, Bool, Bool) -> ServerOperator
toServerOperator (operatorId, operatorTag, tradeName, legalName, domains, enabled, storage, proxy) =
ServerOperator
{ operatorId,
operatorTag,
tradeName,
legalName,
serverDomains = T.splitOn "," domains,
conditionsAcceptance = CARequired Nothing,
enabled,
roles = ServerRoles {storage, proxy}
}
getOperatorConditions_ :: DB.Connection -> ServerOperator -> UsageConditions -> Maybe UsageConditions -> UTCTime -> IO ConditionsAcceptance
getOperatorConditions_ db ServerOperator {operatorId} UsageConditions {conditionsCommit = currentCommit, createdAt, notifiedAt} latestAcceptedConds_ now = do
case latestAcceptedConds_ of
Nothing -> pure $ CARequired Nothing -- no conditions accepted by any operator
Just UsageConditions {conditionsCommit = latestAcceptedCommit} -> do
operatorAcceptedConds_ <-
maybeFirstRow id $
DB.query
db
[sql|
SELECT conditions_commit, accepted_at
FROM operator_usage_conditions
WHERE server_operator_id = ?
ORDER BY operator_usage_conditions_id DESC
LIMIT 1
|]
(Only operatorId)
pure $ case operatorAcceptedConds_ of
Just (operatorCommit, acceptedAt_)
| operatorCommit /= latestAcceptedCommit -> CARequired Nothing -- TODO should we consider this operator disabled?
| currentCommit /= latestAcceptedCommit -> CARequired $ conditionsRequiredOrDeadline createdAt (fromMaybe now notifiedAt)
| otherwise -> CAAccepted acceptedAt_
_ -> CARequired Nothing -- no conditions were accepted for this operator
getCurrentUsageConditions :: DB.Connection -> ExceptT StoreError IO UsageConditions
getCurrentUsageConditions db =
ExceptT . firstRow toUsageConditions SEUsageConditionsNotFound $
DB.query_ db (usageCondsQuery <> " DESC LIMIT 1")
usageCondsQuery :: Query
usageCondsQuery =
[sql|
SELECT usage_conditions_id, conditions_commit, notified_at, created_at
FROM usage_conditions
ORDER BY usage_conditions_id
|]
toUsageConditions :: (Int64, Text, Maybe UTCTime, UTCTime) -> UsageConditions
toUsageConditions (conditionsId, conditionsCommit, notifiedAt, createdAt) =
UsageConditions {conditionsId, conditionsCommit, notifiedAt, createdAt}
getLatestAcceptedConditions :: DB.Connection -> IO (Maybe UsageConditions)
getLatestAcceptedConditions db =
maybeFirstRow toUsageConditions $
DB.query_
db
[sql|
SELECT usage_conditions_id, conditions_commit, notified_at, created_at
FROM usage_conditions
WHERE conditions_commit = (
SELECT conditions_commit
FROM operator_usage_conditions
ORDER BY accepted_at DESC
LIMIT 1
)
|]
setConditionsNotified :: DB.Connection -> Int64 -> UTCTime -> IO ()
setConditionsNotified db condId notifiedAt =
DB.execute db "UPDATE usage_conditions SET notified_at = ? WHERE usage_conditions_id = ?" (notifiedAt, condId)
acceptConditions :: DB.Connection -> Int64 -> NonEmpty Int64 -> UTCTime -> ExceptT StoreError IO ()
acceptConditions db condId opIds acceptedAt = do
UsageConditions {conditionsCommit} <- getUsageConditionsById_ db condId
operators <- mapM getServerOperator_ opIds
let ts = Just acceptedAt
liftIO $ forM_ operators $ \op -> acceptConditions_ db op conditionsCommit ts
where
getServerOperator_ opId =
ExceptT $ firstRow toServerOperator (SEOperatorNotFound opId) $
DB.query db (serverOperatorQuery <> " WHERE operator_id = ?") (Only opId)
acceptConditions_ :: DB.Connection -> ServerOperator -> Text -> Maybe UTCTime -> IO ()
acceptConditions_ db ServerOperator {operatorId, operatorTag} conditionsCommit acceptedAt =
DB.execute
db
[sql|
INSERT INTO operator_usage_conditions
(server_operator_id, server_operator_tag, conditions_commit, accepted_at)
VALUES (?,?,?,?)
|]
(operatorId, operatorTag, conditionsCommit, acceptedAt)
getUsageConditionsById_ :: DB.Connection -> Int64 -> ExceptT StoreError IO UsageConditions
getUsageConditionsById_ db conditionsId =
ExceptT . firstRow toUsageConditions SEUsageConditionsNotFound $
DB.query
db
[sql|
SELECT usage_conditions_id, conditions_commit, notified_at, created_at
FROM usage_conditions
WHERE usage_conditions_id = ?
|]
(Only conditionsId)
setUserServers :: DB.Connection -> User -> NonEmpty UpdatedUserOperatorServers -> ExceptT StoreError IO ()
setUserServers db user@User {userId} userServers = checkConstraint SEUniqueID $ liftIO $ do
ts <- getCurrentTime
forM_ userServers $ \UpdatedUserOperatorServers {operator, smpServers, xftpServers} -> do
mapM_ (updateServerOperator db ts) operator
mapM_ (upsertOrDelete SPSMP ts) smpServers
mapM_ (upsertOrDelete SPXFTP ts) xftpServers
where
upsertOrDelete :: ProtocolTypeI p => SProtocolType p -> UTCTime -> AUserServer p -> IO ()
upsertOrDelete p ts (AUS _ s@UserServer {serverId, deleted}) = case serverId of
DBNewEntity -> void $ insertProtocolServer db p user ts s
DBEntityId srvId
| deleted -> DB.execute db "DELETE FROM protocol_servers WHERE user_id = ? AND smp_server_id = ? AND preset = ?" (userId, srvId, False)
| otherwise -> updateProtocolServer db p ts s
createCall :: DB.Connection -> User -> Call -> UTCTime -> IO () createCall :: DB.Connection -> User -> Call -> UTCTime -> IO ()
createCall db user@User {userId} Call {contactId, callId, callUUID, chatItemId, callState} callTs = do createCall db user@User {userId} Call {contactId, callId, callUUID, chatItemId, callState} callTs = do
-2
View File
@@ -127,8 +127,6 @@ data StoreError
| SERemoteCtrlNotFound {remoteCtrlId :: RemoteCtrlId} | SERemoteCtrlNotFound {remoteCtrlId :: RemoteCtrlId}
| SERemoteCtrlDuplicateCA | SERemoteCtrlDuplicateCA
| SEProhibitedDeleteUser {userId :: UserId, contactId :: ContactId} | SEProhibitedDeleteUser {userId :: UserId, contactId :: ContactId}
| SEOperatorNotFound {serverOperatorId :: Int64}
| SEUsageConditionsNotFound
deriving (Show, Exception) deriving (Show, Exception)
$(J.deriveJSON (sumTypeJSON $ dropPrefix "SE") ''StoreError) $(J.deriveJSON (sumTypeJSON $ dropPrefix "SE") ''StoreError)
+16 -21
View File
@@ -1,7 +1,6 @@
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
module Simplex.Chat.Terminal where module Simplex.Chat.Terminal where
@@ -14,15 +13,15 @@ import qualified Data.Text as T
import Data.Text.Encoding (encodeUtf8) import Data.Text.Encoding (encodeUtf8)
import Database.SQLite.Simple (SQLError (..)) import Database.SQLite.Simple (SQLError (..))
import qualified Database.SQLite.Simple as DB import qualified Database.SQLite.Simple as DB
import Simplex.Chat (_defaultNtfServers, defaultChatConfig, operatorSimpleXChat) import Simplex.Chat (defaultChatConfig)
import Simplex.Chat.Controller import Simplex.Chat.Controller
import Simplex.Chat.Core import Simplex.Chat.Core
import Simplex.Chat.Help (chatWelcome) import Simplex.Chat.Help (chatWelcome)
import Simplex.Chat.Operators
import Simplex.Chat.Options import Simplex.Chat.Options
import Simplex.Chat.Terminal.Input import Simplex.Chat.Terminal.Input
import Simplex.Chat.Terminal.Output import Simplex.Chat.Terminal.Output
import Simplex.FileTransfer.Client.Presets (defaultXFTPServers) import Simplex.FileTransfer.Client.Presets (defaultXFTPServers)
import Simplex.Messaging.Agent.Env.SQLite (presetServerCfg)
import Simplex.Messaging.Client (NetworkConfig (..), SMPProxyFallback (..), SMPProxyMode (..), defaultNetworkConfig) import Simplex.Messaging.Client (NetworkConfig (..), SMPProxyFallback (..), SMPProxyMode (..), defaultNetworkConfig)
import Simplex.Messaging.Util (raceAny_) import Simplex.Messaging.Util (raceAny_)
import System.IO (hFlush, hSetEcho, stdin, stdout) import System.IO (hFlush, hSetEcho, stdin, stdout)
@@ -30,24 +29,20 @@ import System.IO (hFlush, hSetEcho, stdin, stdout)
terminalChatConfig :: ChatConfig terminalChatConfig :: ChatConfig
terminalChatConfig = terminalChatConfig =
defaultChatConfig defaultChatConfig
{ presetServers = { defaultServers =
PresetServers DefaultAgentServers
{ operators = { smp =
[ PresetOperator L.fromList $
{ operator = Just operatorSimpleXChat, map
smp = (presetServerCfg True)
map [ "smp://u2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU=@smp4.simplex.im,o5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion",
(presetServer True) "smp://hpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg=@smp5.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion",
[ "smp://u2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU=@smp4.simplex.im,o5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion", "smp://PQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo=@smp6.simplex.im,bylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion"
"smp://hpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg=@smp5.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion", ],
"smp://PQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo=@smp6.simplex.im,bylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion" useSMP = 3,
], ntf = ["ntf://FB-Uop7RTaZZEG0ZLD2CIaTjsPh-Fw0zFAnb7QyA8Ks=@ntf2.simplex.im,ntg7jdjy2i3qbib3sykiho3enekwiaqg3icctliqhtqcg6jmoh6cxiad.onion"],
useSMP = 3, xftp = L.map (presetServerCfg True) defaultXFTPServers,
xftp = map (presetServer True) $ L.toList defaultXFTPServers, useXFTP = L.length defaultXFTPServers,
useXFTP = 3
}
],
ntf = _defaultNtfServers,
netCfg = netCfg =
defaultNetworkConfig defaultNetworkConfig
{ smpProxyMode = SPMUnknown, { smpProxyMode = SPMUnknown,
+2 -2
View File
@@ -10,7 +10,7 @@ import Data.Maybe (fromMaybe)
import Data.Time.Clock (getCurrentTime) import Data.Time.Clock (getCurrentTime)
import Data.Time.LocalTime (getCurrentTimeZone) import Data.Time.LocalTime (getCurrentTimeZone)
import Network.Socket import Network.Socket
import Simplex.Chat.Controller (ChatConfig (..), ChatController (..), ChatResponse (..), PresetServers (..), SimpleNetCfg (..), currentRemoteHost, versionNumber, versionString) import Simplex.Chat.Controller (ChatConfig (..), ChatController (..), ChatResponse (..), DefaultAgentServers (DefaultAgentServers, netCfg), SimpleNetCfg (..), currentRemoteHost, versionNumber, versionString)
import Simplex.Chat.Core import Simplex.Chat.Core
import Simplex.Chat.Options import Simplex.Chat.Options
import Simplex.Chat.Terminal import Simplex.Chat.Terminal
@@ -56,7 +56,7 @@ simplexChatCLI' cfg opts@ChatOpts {chatCmd, chatCmdLog, chatCmdDelay, chatServer
putStrLn $ serializeChatResponse (rh, Just user) ts tz rh r putStrLn $ serializeChatResponse (rh, Just user) ts tz rh r
welcome :: ChatConfig -> ChatOpts -> IO () welcome :: ChatConfig -> ChatOpts -> IO ()
welcome ChatConfig {presetServers = PresetServers {netCfg}} ChatOpts {coreOptions = CoreChatOpts {dbFilePrefix, simpleNetCfg = SimpleNetCfg {socksProxy, socksMode, smpProxyMode_, smpProxyFallback_}}} = welcome ChatConfig {defaultServers = DefaultAgentServers {netCfg}} ChatOpts {coreOptions = CoreChatOpts {dbFilePrefix, simpleNetCfg = SimpleNetCfg {socksProxy, socksMode, smpProxyMode_, smpProxyFallback_}}} =
mapM_ mapM_
putStrLn putStrLn
[ versionString versionNumber, [ versionString versionNumber,
+25 -36
View File
@@ -53,7 +53,7 @@ import Simplex.Chat.Types.Shared
import Simplex.Chat.Types.UITheme import Simplex.Chat.Types.UITheme
import qualified Simplex.FileTransfer.Transport as XFTP import qualified Simplex.FileTransfer.Transport as XFTP
import Simplex.Messaging.Agent.Client (ProtocolTestFailure (..), ProtocolTestStep (..), SubscriptionsInfo (..)) import Simplex.Messaging.Agent.Client (ProtocolTestFailure (..), ProtocolTestStep (..), SubscriptionsInfo (..))
import Simplex.Messaging.Agent.Env.SQLite (NetworkConfig (..)) import Simplex.Messaging.Agent.Env.SQLite (NetworkConfig (..), ServerCfg (..))
import Simplex.Messaging.Agent.Protocol import Simplex.Messaging.Agent.Protocol
import Simplex.Messaging.Agent.Store.SQLite.DB (SlowQueryStats (..)) import Simplex.Messaging.Agent.Store.SQLite.DB (SlowQueryStats (..))
import Simplex.Messaging.Client (SMPProxyFallback, SMPProxyMode (..), SocksMode (..)) import Simplex.Messaging.Client (SMPProxyFallback, SMPProxyMode (..), SocksMode (..))
@@ -95,16 +95,8 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
CRChats chats -> viewChats ts tz chats CRChats chats -> viewChats ts tz chats
CRApiChat u chat -> ttyUser u $ if testView then testViewChat chat else [viewJSON chat] CRApiChat u chat -> ttyUser u $ if testView then testViewChat chat else [viewJSON chat]
CRApiParsedMarkdown ft -> [viewJSON ft] CRApiParsedMarkdown ft -> [viewJSON ft]
-- CRUserProtoServers u userServers operators -> ttyUser u $ viewUserServers userServers operators testView CRUserProtoServers u userServers -> ttyUser u $ viewUserServers userServers testView
CRServerTestResult u srv testFailure -> ttyUser u $ viewServerTestResult srv testFailure CRServerTestResult u srv testFailure -> ttyUser u $ viewServerTestResult srv testFailure
CRTestOperator _ -> []
CRTestUsageConditionsAction _ -> []
CRTestConditionsAcceptance _ -> []
CRTestServerRoles _ -> []
CRServerOperators {} -> []
CRUserServers {} -> []
CRUserServersValidation _ -> []
CRUsageConditions {} -> []
CRChatItemTTL u ttl -> ttyUser u $ viewChatItemTTL ttl CRChatItemTTL u ttl -> ttyUser u $ viewChatItemTTL ttl
CRNetworkConfig cfg -> viewNetworkConfig cfg CRNetworkConfig cfg -> viewNetworkConfig cfg
CRContactInfo u ct cStats customUserProfile -> ttyUser u $ viewContactInfo ct cStats customUserProfile CRContactInfo u ct cStats customUserProfile -> ttyUser u $ viewContactInfo ct cStats customUserProfile
@@ -1217,27 +1209,27 @@ viewUserPrivacy User {userId} User {userId = userId', localDisplayName = n', sho
"profile is " <> if isJust viewPwdHash then "hidden" else "visible" "profile is " <> if isJust viewPwdHash then "hidden" else "visible"
] ]
-- viewUserServers :: AUserProtoServers -> [ServerOperator] -> Bool -> [StyledString] viewUserServers :: AUserProtoServers -> Bool -> [StyledString]
-- viewUserServers (AUPS UserProtoServers {serverProtocol = p, protoServers, presetServers}) operators testView = viewUserServers (AUPS UserProtoServers {serverProtocol = p, protoServers, presetServers}) testView =
-- customServers customServers
-- <> if testView <> if testView
-- then [] then []
-- else else
-- [ "", [ "",
-- "use " <> highlight (srvCmd <> " test <srv>") <> " to test " <> pName <> " server connection", "use " <> highlight (srvCmd <> " test <srv>") <> " to test " <> pName <> " server connection",
-- "use " <> highlight (srvCmd <> " <srv1[,srv2,...]>") <> " to configure " <> pName <> " servers", "use " <> highlight (srvCmd <> " <srv1[,srv2,...]>") <> " to configure " <> pName <> " servers",
-- "use " <> highlight (srvCmd <> " default") <> " to remove configured " <> pName <> " servers and use presets" "use " <> highlight (srvCmd <> " default") <> " to remove configured " <> pName <> " servers and use presets"
-- ] ]
-- <> case p of <> case p of
-- SPSMP -> ["(chat option " <> highlight' "-s" <> " (" <> highlight' "--server" <> ") has precedence over saved SMP servers for chat session)"] SPSMP -> ["(chat option " <> highlight' "-s" <> " (" <> highlight' "--server" <> ") has precedence over saved SMP servers for chat session)"]
-- SPXFTP -> ["(chat option " <> highlight' "-xftp-servers" <> " has precedence over saved XFTP servers for chat session)"] SPXFTP -> ["(chat option " <> highlight' "-xftp-servers" <> " has precedence over saved XFTP servers for chat session)"]
-- where where
-- srvCmd = "/" <> strEncode p srvCmd = "/" <> strEncode p
-- pName = protocolName p pName = protocolName p
-- customServers = customServers =
-- if null protoServers if null protoServers
-- then ("no " <> pName <> " servers saved, using presets: ") : viewServers operators presetServers then ("no " <> pName <> " servers saved, using presets: ") : viewServers presetServers
-- else viewServers operators protoServers else viewServers protoServers
protocolName :: ProtocolTypeI p => SProtocolType p -> StyledString protocolName :: ProtocolTypeI p => SProtocolType p -> StyledString
protocolName = plain . map toUpper . T.unpack . decodeLatin1 . strEncode protocolName = plain . map toUpper . T.unpack . decodeLatin1 . strEncode
@@ -1334,11 +1326,8 @@ viewConnectionStats ConnectionStats {rcvQueuesInfo, sndQueuesInfo} =
["receiving messages via: " <> viewRcvQueuesInfo rcvQueuesInfo | not $ null rcvQueuesInfo] ["receiving messages via: " <> viewRcvQueuesInfo rcvQueuesInfo | not $ null rcvQueuesInfo]
<> ["sending messages via: " <> viewSndQueuesInfo sndQueuesInfo | not $ null sndQueuesInfo] <> ["sending messages via: " <> viewSndQueuesInfo sndQueuesInfo | not $ null sndQueuesInfo]
-- viewServers :: ProtocolTypeI p => [ServerOperator] -> NonEmpty (ServerCfg p) -> [StyledString] viewServers :: ProtocolTypeI p => NonEmpty (ServerCfg p) -> [StyledString]
-- viewServers operators = map (plain . (\ServerCfg {server, operator} -> B.unpack (strEncode server) <> viewOperator operator)) . L.toList viewServers = map (plain . B.unpack . strEncode . (\ServerCfg {server} -> server)) . L.toList
-- where
-- ops :: Map (Maybe DBEntityId) Text = foldl' (\m ServerOperator {operatorId, tradeName} -> M.insert (Just operatorId) tradeName m) M.empty operators
-- viewOperator = maybe "" $ \op -> " (operator " <> maybe (show op) T.unpack (M.lookup (Just op) ops) <> ")"
viewRcvQueuesInfo :: [RcvQueueInfo] -> StyledString viewRcvQueuesInfo :: [RcvQueueInfo] -> StyledString
viewRcvQueuesInfo = plain . intercalate ", " . map showQueueInfo viewRcvQueuesInfo = plain . intercalate ", " . map showQueueInfo
+10 -29
View File
@@ -10,8 +10,7 @@ import ChatTests.Utils
import Control.Concurrent (forkIO, killThread, threadDelay) import Control.Concurrent (forkIO, killThread, threadDelay)
import Control.Exception (finally) import Control.Exception (finally)
import Control.Monad (forM_) import Control.Monad (forM_)
import qualified Data.Text as T import Directory.Events (viewName)
import qualified Directory.Events as DE
import Directory.Options import Directory.Options
import Directory.Service import Directory.Service
import Directory.Store import Directory.Store
@@ -28,7 +27,7 @@ import Test.Hspec hiding (it)
directoryServiceTests :: SpecWith FilePath directoryServiceTests :: SpecWith FilePath
directoryServiceTests = do directoryServiceTests = do
it "should register group" testDirectoryService it "should register group" testDirectoryService
it "should suspend and resume group, send message to owner" testSuspendResume it "should suspend and resume group" testSuspendResume
it "should delete group registration" testDeleteGroup it "should delete group registration" testDeleteGroup
it "should change initial member role" testSetRole it "should change initial member role" testSetRole
it "should join found group via link" testJoinGroup it "should join found group via link" testJoinGroup
@@ -68,7 +67,6 @@ mkDirectoryOpts :: FilePath -> [KnownContact] -> DirectoryOpts
mkDirectoryOpts tmp superUsers = mkDirectoryOpts tmp superUsers =
DirectoryOpts DirectoryOpts
{ coreOptions = testCoreOpts {dbFilePrefix = tmp </> serviceDbPrefix}, { coreOptions = testCoreOpts {dbFilePrefix = tmp </> serviceDbPrefix},
adminUsers = [],
superUsers, superUsers,
directoryLog = Just $ tmp </> "directory_service.log", directoryLog = Just $ tmp </> "directory_service.log",
serviceName = "SimpleX-Directory", serviceName = "SimpleX-Directory",
@@ -79,9 +77,6 @@ mkDirectoryOpts tmp superUsers =
serviceDbPrefix :: FilePath serviceDbPrefix :: FilePath
serviceDbPrefix = "directory_service" serviceDbPrefix = "directory_service"
viewName :: String -> String
viewName = T.unpack . DE.viewName . T.pack
testDirectoryService :: HasCallStack => FilePath -> IO () testDirectoryService :: HasCallStack => FilePath -> IO ()
testDirectoryService tmp = testDirectoryService tmp =
withDirectoryService tmp $ \superUser dsLink -> withDirectoryService tmp $ \superUser dsLink ->
@@ -116,7 +111,7 @@ testDirectoryService tmp =
-- putStrLn "*** update profile so that it has link" -- putStrLn "*** update profile so that it has link"
updateGroupProfile bob welcomeWithLink updateGroupProfile bob welcomeWithLink
bob <# "SimpleX-Directory> Thank you! The group link for ID 1 (PSA) is added to the welcome message." bob <# "SimpleX-Directory> Thank you! The group link for ID 1 (PSA) is added to the welcome message."
bob <## "You will be notified once the group is added to the directory - it may take up to 48 hours." bob <## "You will be notified once the group is added to the directory - it may take up to 24 hours."
approvalRequested superUser welcomeWithLink (1 :: Int) approvalRequested superUser welcomeWithLink (1 :: Int)
-- putStrLn "*** update profile so that it still has link" -- putStrLn "*** update profile so that it still has link"
let welcomeWithLink' = "Welcome! " <> welcomeWithLink let welcomeWithLink' = "Welcome! " <> welcomeWithLink
@@ -144,7 +139,7 @@ testDirectoryService tmp =
-- putStrLn "*** update profile so that it has link again" -- putStrLn "*** update profile so that it has link again"
updateGroupProfile bob welcomeWithLink' updateGroupProfile bob welcomeWithLink'
bob <# "SimpleX-Directory> Thank you! The group link for ID 1 (PSA) is added to the welcome message." bob <# "SimpleX-Directory> Thank you! The group link for ID 1 (PSA) is added to the welcome message."
bob <## "You will be notified once the group is added to the directory - it may take up to 48 hours." bob <## "You will be notified once the group is added to the directory - it may take up to 24 hours."
approvalRequested superUser welcomeWithLink' (1 :: Int) approvalRequested superUser welcomeWithLink' (1 :: Int)
superUser #> "@SimpleX-Directory /pending" superUser #> "@SimpleX-Directory /pending"
superUser <# "SimpleX-Directory> > /pending" superUser <# "SimpleX-Directory> > /pending"
@@ -212,17 +207,6 @@ testSuspendResume tmp =
superUser <## " Group listing resumed!" superUser <## " Group listing resumed!"
bob <# "SimpleX-Directory> The group ID 1 (privacy) is listed in the directory again!" bob <# "SimpleX-Directory> The group ID 1 (privacy) is listed in the directory again!"
groupFound bob "privacy" groupFound bob "privacy"
superUser #> "@SimpleX-Directory privacy"
groupFoundN_ (Just 1) 2 superUser "privacy"
superUser #> "@SimpleX-Directory /link 1:privacy"
superUser <# "SimpleX-Directory> > /link 1:privacy"
superUser <## " The link to join the group ID 1 (privacy):"
superUser <##. "https://simplex.chat/contact"
superUser <## "New member role: member"
superUser #> "@SimpleX-Directory /owner 1:privacy hello there"
superUser <# "SimpleX-Directory> > /owner 1:privacy hello there"
superUser <## " Forwarded to @bob, the owner of the group ID 1 (privacy)"
bob <# "SimpleX-Directory> hello there"
testDeleteGroup :: HasCallStack => FilePath -> IO () testDeleteGroup :: HasCallStack => FilePath -> IO ()
testDeleteGroup tmp = testDeleteGroup tmp =
@@ -666,7 +650,7 @@ testRegOwnerRemovedLink tmp =
bob <## "description changed to:" bob <## "description changed to:"
bob <## welcomeWithLink bob <## welcomeWithLink
bob <# "SimpleX-Directory> Thank you! The group link for ID 1 (privacy) is added to the welcome message." bob <# "SimpleX-Directory> Thank you! The group link for ID 1 (privacy) is added to the welcome message."
bob <## "You will be notified once the group is added to the directory - it may take up to 48 hours." bob <## "You will be notified once the group is added to the directory - it may take up to 24 hours."
cath <## "bob updated group #privacy:" cath <## "bob updated group #privacy:"
cath <## "description changed to:" cath <## "description changed to:"
cath <## welcomeWithLink cath <## welcomeWithLink
@@ -708,7 +692,7 @@ testAnotherOwnerRemovedLink tmp =
bob <## "description changed to:" bob <## "description changed to:"
bob <## (welcomeWithLink <> " - welcome!") bob <## (welcomeWithLink <> " - welcome!")
bob <# "SimpleX-Directory> Thank you! The group link for ID 1 (privacy) is added to the welcome message." bob <# "SimpleX-Directory> Thank you! The group link for ID 1 (privacy) is added to the welcome message."
bob <## "You will be notified once the group is added to the directory - it may take up to 48 hours." bob <## "You will be notified once the group is added to the directory - it may take up to 24 hours."
cath <## "bob updated group #privacy:" cath <## "bob updated group #privacy:"
cath <## "description changed to:" cath <## "description changed to:"
cath <## (welcomeWithLink <> " - welcome!") cath <## (welcomeWithLink <> " - welcome!")
@@ -790,7 +774,7 @@ testDuplicateProhibitWhenUpdated tmp =
cath ##> "/gp privacy security Security" cath ##> "/gp privacy security Security"
cath <## "changed to #security (Security)" cath <## "changed to #security (Security)"
cath <# "SimpleX-Directory> Thank you! The group link for ID 2 (security) is added to the welcome message." cath <# "SimpleX-Directory> Thank you! The group link for ID 2 (security) is added to the welcome message."
cath <## "You will be notified once the group is added to the directory - it may take up to 48 hours." cath <## "You will be notified once the group is added to the directory - it may take up to 24 hours."
notifySuperUser superUser cath "security" "Security" welcomeWithLink' 2 notifySuperUser superUser cath "security" "Security" welcomeWithLink' 2
approveRegistration superUser cath "security" 2 approveRegistration superUser cath "security" 2
groupFound bob "security" groupFound bob "security"
@@ -1051,7 +1035,7 @@ updateProfileWithLink u n welcomeWithLink ugId = do
u <## "description changed to:" u <## "description changed to:"
u <## welcomeWithLink u <## welcomeWithLink
u <# ("SimpleX-Directory> Thank you! The group link for ID " <> show ugId <> " (" <> n <> ") is added to the welcome message.") u <# ("SimpleX-Directory> Thank you! The group link for ID " <> show ugId <> " (" <> n <> ") is added to the welcome message.")
u <## "You will be notified once the group is added to the directory - it may take up to 48 hours." u <## "You will be notified once the group is added to the directory - it may take up to 24 hours."
notifySuperUser :: TestCC -> TestCC -> String -> String -> String -> Int -> IO () notifySuperUser :: TestCC -> TestCC -> String -> String -> String -> Int -> IO ()
notifySuperUser su u n fn welcomeWithLink gId = do notifySuperUser su u n fn welcomeWithLink gId = do
@@ -1128,13 +1112,10 @@ groupFoundN count u name = do
groupFoundN' count u name groupFoundN' count u name
groupFoundN' :: Int -> TestCC -> String -> IO () groupFoundN' :: Int -> TestCC -> String -> IO ()
groupFoundN' = groupFoundN_ Nothing groupFoundN' count u name = do
groupFoundN_ :: Maybe Int -> Int -> TestCC -> String -> IO ()
groupFoundN_ shownId_ count u name = do
u <# ("SimpleX-Directory> > " <> name) u <# ("SimpleX-Directory> > " <> name)
u <## " Found 1 group(s)." u <## " Found 1 group(s)."
u <#. ("SimpleX-Directory> " <> maybe "" (\gId -> show gId <> ". ") shownId_ <> name) u <#. ("SimpleX-Directory> " <> name)
u <## "Welcome message:" u <## "Welcome message:"
u <##. "Link to join the group " u <##. "Link to join the group "
u <## (show count <> " members") u <## (show count <> " members")
+7 -19
View File
@@ -25,10 +25,9 @@ import Data.Maybe (isNothing)
import qualified Data.Text as T import qualified Data.Text as T
import Network.Socket import Network.Socket
import Simplex.Chat import Simplex.Chat
import Simplex.Chat.Controller (ChatCommand (..), ChatConfig (..), ChatController (..), ChatDatabase (..), ChatLogLevel (..), PresetServers (..), defaultSimpleNetCfg) import Simplex.Chat.Controller (ChatCommand (..), ChatConfig (..), ChatController (..), ChatDatabase (..), ChatLogLevel (..), defaultSimpleNetCfg)
import Simplex.Chat.Core import Simplex.Chat.Core
import Simplex.Chat.Options import Simplex.Chat.Options
import Simplex.Chat.Operators (PresetOperator (..), presetServer)
import Simplex.Chat.Protocol (currentChatVersion, pqEncryptionCompressionVersion) import Simplex.Chat.Protocol (currentChatVersion, pqEncryptionCompressionVersion)
import Simplex.Chat.Store import Simplex.Chat.Store
import Simplex.Chat.Store.Profiles import Simplex.Chat.Store.Profiles
@@ -95,8 +94,8 @@ testCoreOpts =
{ dbFilePrefix = "./simplex_v1", { dbFilePrefix = "./simplex_v1",
dbKey = "", dbKey = "",
-- dbKey = "this is a pass-phrase to encrypt the database", -- dbKey = "this is a pass-phrase to encrypt the database",
smpServers = [], smpServers = ["smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=:server_password@localhost:7001"],
xftpServers = [], xftpServers = ["xftp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=:server_password@localhost:7002"],
simpleNetCfg = defaultSimpleNetCfg, simpleNetCfg = defaultSimpleNetCfg,
logLevel = CLLImportant, logLevel = CLLImportant,
logConnections = False, logConnections = False,
@@ -150,18 +149,6 @@ testCfg :: ChatConfig
testCfg = testCfg =
defaultChatConfig defaultChatConfig
{ agentConfig = testAgentCfg, { agentConfig = testAgentCfg,
presetServers =
(presetServers defaultChatConfig)
{ operators =
[ PresetOperator
{ operator = Just operatorSimpleXChat,
smp = map (presetServer True) ["smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=:server_password@localhost:7001"],
useSMP = 1,
xftp = map (presetServer True) ["xftp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=:server_password@localhost:7002"],
useXFTP = 1
}
]
},
showReceipts = False, showReceipts = False,
testView = True, testView = True,
tbqSize = 16 tbqSize = 16
@@ -436,10 +423,11 @@ smpServerCfg =
ServerConfig ServerConfig
{ transports = [(serverPort, transport @TLS, False)], { transports = [(serverPort, transport @TLS, False)],
tbqSize = 1, tbqSize = 1,
msgStoreType = AMSType SMSMemory, -- serverTbqSize = 1,
msgQueueQuota = 16, msgQueueQuota = 16,
maxJournalMsgCount = 24, msgStoreType = AMSType SMSMemory,
maxJournalStateLines = 4, maxJournalMsgCount = 1000,
maxJournalStateLines = 1000,
queueIdBytes = 12, queueIdBytes = 12,
msgIdBytes = 6, msgIdBytes = 6,
storeLogFile = Nothing, storeLogFile = Nothing,
+3 -3
View File
@@ -25,7 +25,7 @@ import Database.SQLite.Simple (Only (..))
import Simplex.Chat.AppSettings (defaultAppSettings) import Simplex.Chat.AppSettings (defaultAppSettings)
import qualified Simplex.Chat.AppSettings as AS import qualified Simplex.Chat.AppSettings as AS
import Simplex.Chat.Call import Simplex.Chat.Call
import Simplex.Chat.Controller (ChatConfig (..), PresetServers (..)) import Simplex.Chat.Controller (ChatConfig (..), DefaultAgentServers (..))
import Simplex.Chat.Messages (ChatItemId) import Simplex.Chat.Messages (ChatItemId)
import Simplex.Chat.Options import Simplex.Chat.Options
import Simplex.Chat.Protocol (supportedChatVRange) import Simplex.Chat.Protocol (supportedChatVRange)
@@ -332,8 +332,8 @@ testRetryConnectingClientTimeout tmp = do
{ quotaExceededTimeout = 1, { quotaExceededTimeout = 1,
messageRetryInterval = RetryInterval2 {riFast = fastRetryInterval, riSlow = fastRetryInterval} messageRetryInterval = RetryInterval2 {riFast = fastRetryInterval, riSlow = fastRetryInterval}
}, },
presetServers = defaultServers =
let def@PresetServers {netCfg} = presetServers testCfg let def@DefaultAgentServers {netCfg} = defaultServers testCfg
in def {netCfg = (netCfg :: NetworkConfig) {tcpTimeout = 10}} in def {netCfg = (netCfg :: NetworkConfig) {tcpTimeout = 10}}
} }
opts' = opts' =
-2
View File
@@ -1,10 +1,8 @@
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PostfixOperators #-} {-# LANGUAGE PostfixOperators #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
module ChatTests.Groups where module ChatTests.Groups where
-1
View File
@@ -2,7 +2,6 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PostfixOperators #-} {-# LANGUAGE PostfixOperators #-}
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
module ChatTests.Profiles where module ChatTests.Profiles where
+20 -33
View File
@@ -1,64 +1,51 @@
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE StandaloneDeriving #-}
{-# OPTIONS_GHC -Wno-orphans #-} {-# OPTIONS_GHC -Wno-orphans #-}
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
module RandomServers where module RandomServers where
import Control.Monad (replicateM) import Control.Monad (replicateM)
import Data.Foldable (foldMap')
import Data.List (sortOn)
import Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as L import qualified Data.List.NonEmpty as L
import Data.Monoid (Sum (..)) import Simplex.Chat (cfgServers, cfgServersToUse, defaultChatConfig, randomServers)
import Simplex.Chat (defaultChatConfig, randomPresetServers) import Simplex.Chat.Controller (ChatConfig (..))
import Simplex.Chat.Controller (ChatConfig (..), PresetServers (..)) import Simplex.Messaging.Agent.Env.SQLite (ServerCfg (..))
import Simplex.Chat.Operators (DBEntityId' (..), NewUserServer, UserServer' (..), operatorServers, operatorServersToUse)
import Simplex.Messaging.Agent.Env.SQLite (ServerRoles (..))
import Simplex.Messaging.Protocol (ProtoServerWithAuth (..), SProtocolType (..), UserProtocol) import Simplex.Messaging.Protocol (ProtoServerWithAuth (..), SProtocolType (..), UserProtocol)
import Test.Hspec import Test.Hspec
randomServersTests :: Spec randomServersTests :: Spec
randomServersTests = describe "choosig random servers" $ do randomServersTests = describe "choosig random servers" $ do
it "should choose 4 + 3 random SMP servers and keep the rest disabled" testRandomSMPServers it "should choose 4 random SMP servers and keep the rest disabled" testRandomSMPServers
it "should choose 3 + 3 random XFTP servers and keep the rest disabled" testRandomXFTPServers it "should keep all 6 XFTP servers" testRandomXFTPServers
deriving instance Eq ServerRoles deriving instance Eq (ServerCfg p)
deriving instance Eq (DBEntityId' s)
deriving instance Eq (UserServer' s p)
testRandomSMPServers :: IO () testRandomSMPServers :: IO ()
testRandomSMPServers = do testRandomSMPServers = do
[srvs1, srvs2, srvs3] <- [srvs1, srvs2, srvs3] <-
replicateM 3 $ replicateM 3 $
checkEnabled SPSMP 7 False =<< randomPresetServers SPSMP (presetServers defaultChatConfig) checkEnabled SPSMP 4 False =<< randomServers SPSMP defaultChatConfig
(srvs1 == srvs2 && srvs2 == srvs3) `shouldBe` False -- && to avoid rare failures (srvs1 == srvs2 && srvs2 == srvs3) `shouldBe` False -- && to avoid rare failures
testRandomXFTPServers :: IO () testRandomXFTPServers :: IO ()
testRandomXFTPServers = do testRandomXFTPServers = do
[srvs1, srvs2, srvs3] <- [srvs1, srvs2, srvs3] <-
replicateM 3 $ replicateM 3 $
checkEnabled SPXFTP 6 False =<< randomPresetServers SPXFTP (presetServers defaultChatConfig) checkEnabled SPXFTP 6 True =<< randomServers SPXFTP defaultChatConfig
(srvs1 == srvs2 && srvs2 == srvs3) `shouldBe` False -- && to avoid rare failures (srvs1 == srvs2 && srvs2 == srvs3) `shouldBe` True
checkEnabled :: UserProtocol p => SProtocolType p -> Int -> Bool -> NonEmpty (NewUserServer p) -> IO [NewUserServer p] checkEnabled :: UserProtocol p => SProtocolType p -> Int -> Bool -> (L.NonEmpty (ServerCfg p), [ServerCfg p]) -> IO [ServerCfg p]
checkEnabled p n allUsed srvs = do checkEnabled p n allUsed (srvs, _) = do
let srvs' = sortOn server' $ L.toList srvs let def = defaultServers defaultChatConfig
PresetServers {operators = presetOps} = presetServers defaultChatConfig cfgSrvs = L.sortWith server' $ cfgServers p def
presetSrvs = sortOn server' $ concatMap (operatorServers p) presetOps toUse = cfgServersToUse p def
Sum toUse = foldMap' (Sum . operatorServersToUse p) presetOps srvs == cfgSrvs `shouldBe` allUsed
srvs' == presetSrvs `shouldBe` allUsed L.map enable srvs `shouldBe` L.map enable cfgSrvs
map enable srvs' `shouldBe` map enable presetSrvs let enbldSrvs = L.filter (\ServerCfg {enabled} -> enabled) srvs
let enbldSrvs = filter (\UserServer {enabled} -> enabled) srvs'
toUse `shouldBe` n toUse `shouldBe` n
length enbldSrvs `shouldBe` n length enbldSrvs `shouldBe` n
pure enbldSrvs pure enbldSrvs
where where
server' UserServer {server = ProtoServerWithAuth srv _} = srv server' ServerCfg {server = ProtoServerWithAuth srv _} = srv
enable :: forall p. NewUserServer p -> NewUserServer p enable :: forall p. ServerCfg p -> ServerCfg p
enable srv = (srv :: NewUserServer p) {enabled = False} enable srv = (srv :: ServerCfg p) {enabled = False}