Compare commits

...

188 Commits

Author SHA1 Message Date
Dan Ballard 3964348dd0 Merge pull request 'Expose Profile Tags for Handling Unencrypted Profiles' (#66) from fixees into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #66
2021-06-24 10:42:40 -07:00
Sarah Jamie Lewis ae16c7c6a5 Expose Profile Tags for Handling Unencrypted Profiles
continuous-integration/drone/pr Build is passing Details
2021-06-24 10:38:51 -07:00
Sarah Jamie Lewis 69f14f9bb1 Merge pull request 'dualpane settings wiring' (#65) from dualpane into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #65
2021-06-24 08:49:03 -07:00
erinn f1775eb975 dualpane settings wiring
continuous-integration/drone/pr Build is passing Details
2021-06-24 00:34:06 -07:00
erinn c1b7e4c75d Merge pull request 'Remove Unused Map' (#64) from fixees into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #64
2021-06-22 16:58:34 -07:00
Sarah Jamie Lewis a460b900b6 Reset
continuous-integration/drone/pr Build is passing Details
2021-06-22 16:58:01 -07:00
Sarah Jamie Lewis b2d8f3f34a Reconnect Fix
continuous-integration/drone/pr Build is passing Details
2021-06-22 16:56:53 -07:00
Sarah Jamie Lewis 6c5b48311d Only launch 1 profile handling thread per profile (on reload)
continuous-integration/drone/pr Build is passing Details
2021-06-22 15:46:28 -07:00
Sarah Jamie Lewis b33c6c77dd Remove Unused Map
continuous-integration/drone/pr Build is passing Details
2021-06-22 15:34:46 -07:00
Sarah Jamie Lewis c864bccbb9 Merge pull request 'stale process detection on android' (#63) from rrgtj into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #63
2021-06-21 17:51:49 -07:00
erinn 5dddb16f2b Merge branch 'trunk' of git.openprivacy.ca:flutter/libcwtch-go into rrgtj
continuous-integration/drone/pr Build is passing Details
2021-06-21 17:47:55 -07:00
erinn 001470257a process fixes for android 2021-06-21 17:47:43 -07:00
Sarah Jamie Lewis b37f283fe3 Merge pull request 'tag unpassworded users' (#62) from tagPass into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #62
2021-06-21 15:14:46 -07:00
Dan Ballard 2f495dfd62 tag unpassworded users
continuous-integration/drone/pr Build is passing Details
2021-06-21 15:11:56 -07:00
erinn e09e9c09a7 Merge branch 'trunk' of git.openprivacy.ca:flutter/libcwtch-go into rrgtj 2021-06-17 18:11:35 -07:00
erinn ae3aef0518 idk 2021-06-17 18:11:30 -07:00
Sarah Jamie Lewis 0f53ae0062 Merge pull request 'if no remote name, reask' (#61) from getNameAfterApproved into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #61
2021-06-17 17:23:08 -07:00
Sarah Jamie Lewis f6eaa2d691 Merge branch 'trunk' into getNameAfterApproved
continuous-integration/drone/pr Build is passing Details
2021-06-17 17:23:03 -07:00
Sarah Jamie Lewis 8f1b1b8630 Merge pull request 'check acn status on reconnect' (#60) from reconnACNStatus into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #60
2021-06-17 17:21:57 -07:00
Dan Ballard e2cf1d25e3 if no remote name, reask
continuous-integration/drone/pr Build is passing Details
2021-06-17 17:05:07 -07:00
Dan Ballard 7b3ff2c1af check acn status on reconnect
continuous-integration/drone/pr Build is passing Details
2021-06-17 16:58:11 -07:00
Sarah Jamie Lewis c6f1c4d0f9 Merge pull request 'counter sync events' (#59) from countersync into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #59
2021-06-17 15:45:23 -07:00
erinn e7ef2fef4a counter sync events
continuous-integration/drone/pr Build is passing Details
2021-06-17 15:41:41 -07:00
erinn ffaca8d876 Merge pull request 'Fix import bundle for groups to auto accept' (#58) from beta_fixes into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #58
2021-06-17 13:53:14 -07:00
Sarah Jamie Lewis 6070d378c5 Merge branch 'trunk' into beta_fixes
continuous-integration/drone/pr Build is passing Details
2021-06-17 13:50:58 -07:00
Sarah Jamie Lewis fafdb8132a Merge pull request 'remove QueryACNVersion API, just do it on app launch; removed unused GetProfiles API' (#57) from fixReconnect into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #57
2021-06-17 13:50:50 -07:00
Sarah Jamie Lewis 9a764b5b35 Fix import bundle for groups to auto accept
continuous-integration/drone/pr Build is passing Details
2021-06-17 13:49:20 -07:00
Dan Ballard 3261d18118 remove QueryACNVersion API, just do it on app launch; removed unused GetProfiles API
continuous-integration/drone/pr Build is passing Details
2021-06-17 11:53:41 -07:00
Sarah Jamie Lewis 08b1b19f7a Merge pull request 'add group message popup notifications' (#56) from groupnotifs into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #56
2021-06-16 17:00:11 -07:00
erinn 204cc35e0d switch-ffi
continuous-integration/drone/pr Build is passing Details
2021-06-16 16:46:27 -07:00
erinn 28011ae674 add group message popup notifications 2021-06-16 16:45:15 -07:00
erinn eb4150c0be Merge branch 'trunk' of git.openprivacy.ca:flutter/libcwtch-go into groupnotifs 2021-06-16 15:59:00 -07:00
erinn d9f9489fa2 add group message popup notifications 2021-06-16 15:58:54 -07:00
erinn 854dc1946f Merge pull request 'Shutdown Event' (#55) from beta_fixes into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #55
2021-06-16 14:55:53 -07:00
Sarah Jamie Lewis 26b0151e79 Shutdown Event
continuous-integration/drone/pr Build is passing Details
2021-06-16 14:11:40 -07:00
Sarah Jamie Lewis abe06912a1 Merge pull request 'add mutex on StartCwtch and ReconnectCwtchForeground' (#54) from fixReconnect into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #54
2021-06-16 12:03:23 -07:00
Dan Ballard 11f09c95d2 remove mutex 'fix', instead just don't reconnect, assume whole stale thread and abandon
continuous-integration/drone/pr Build is passing Details
2021-06-16 11:41:36 -07:00
Dan Ballard 9b18bef47a add mutex on StartCwtch and ReconnectCwtchForeground
continuous-integration/drone/pr Build is passing Details
2021-06-16 08:58:53 -07:00
Sarah Jamie Lewis c5f36a9480 Merge pull request 'goStart' (#53) from goStart into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #53
2021-06-15 11:07:03 -07:00
Dan Ballard 192d98a89b Cleaned out more dead code; fixed bug where remote attrs could override local attrs in UI
continuous-integration/drone/pr Build is passing Details
2021-06-15 11:03:53 -07:00
Dan Ballard 00c5412551 finish removing unused code (getACNEvents, getContactEvents, getRepaintEvent, SelectProfile
continuous-integration/drone/pr Build is passing Details
2021-06-15 10:28:19 -07:00
Dan Ballard d921612d25 move acn and cwtch app creation into a go routine, report success or error by message 2021-06-15 10:28:19 -07:00
erinn 39187a7db7 Merge pull request 'Delete Peer and Leave Conversation' (#52) from beta_fixes into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #52
2021-06-14 17:28:27 -07:00
erinn 6fabfa507d Merge branch 'trunk' into beta_fixes
continuous-integration/drone/pr Build is passing Details
2021-06-14 17:28:21 -07:00
Sarah Jamie Lewis 6560fcef49 Delete Peer and Leave Conversation
continuous-integration/drone/pr Build is passing Details
2021-06-14 17:23:47 -07:00
Sarah Jamie Lewis 033de73b6a Merge pull request 'android service and notifications' (#51) from androidservice into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #51
2021-06-11 14:41:20 -07:00
erinn 370e6090ce loglevel oops
continuous-integration/drone/pr Build is passing Details
2021-06-11 14:26:55 -07:00
erinn 61d078f645 ffi switch oops 2021-06-11 14:25:00 -07:00
erinn af4d994d6a Merge branch 'trunk' of git.openprivacy.ca:flutter/libcwtch-go into androidservice 2021-06-11 14:21:16 -07:00
erinn 78a2e033dc android service and notification support 2021-06-11 14:21:09 -07:00
Dan Ballard fddfd41fbf Merge pull request 'Upgrade cwtch' (#50) from update_flags into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #50
2021-06-10 11:36:27 -07:00
Sarah Jamie Lewis 936c45f234 Upgrade cwtch
continuous-integration/drone/pr Build is passing Details
2021-06-10 10:59:22 -07:00
Dan Ballard 08fe76bf30 Merge pull request 'Use int64 for UpdateMessageFlags api so gomobile can generate it' (#49) from update_flags into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #49
2021-06-09 12:45:50 -07:00
Sarah Jamie Lewis 2b86f7b282 Use int64 for UpdateMessageFlags api so gomobile can generate it
continuous-integration/drone/pr Build is passing Details
2021-06-09 11:58:23 -07:00
Dan Ballard 33945af046 Merge pull request 'Add UpdateMessageFlags' (#48) from update_flags into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #48
2021-06-09 11:31:59 -07:00
Sarah Jamie Lewis 9b1814e43d Add UpdateMessageFlags
continuous-integration/drone/pr Build is passing Details
2021-06-09 11:27:51 -07:00
Sarah Jamie Lewis 4dd8d74bfb Merge pull request 'api call for QueryACNVersion' (#47) from acnver into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #47
2021-06-07 09:13:58 -07:00
Sarah Jamie Lewis 772cf7949a Merge branch 'trunk' into acnver
continuous-integration/drone/pr Build is passing Details
2021-06-07 09:13:52 -07:00
Dan Ballard 62a55a6fec api call for QueryACNVersion
continuous-integration/drone/pr Build is passing Details
2021-06-06 10:40:58 -07:00
erinn 6a0e839bb6 Merge pull request 'ui-fixes' (#46) from ui-fixes into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #46
2021-06-02 12:40:33 -07:00
Sarah Jamie Lewis 53d11dc45d spelling
continuous-integration/drone/pr Build is passing Details
2021-06-02 12:36:26 -07:00
Sarah Jamie Lewis ee47798714 staticcheck fixes 2021-06-02 12:36:26 -07:00
Sarah Jamie Lewis 6f01c992af Fix Tor Crashes, Upgrade Cwtch/Tapir 2021-06-02 12:36:26 -07:00
Dan Ballard 4f625c7f8e Merge pull request 'Optimistically Sync Servers on Import Bundle' (#45) from ui-fixes into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #45
2021-05-31 16:30:47 -07:00
Sarah Jamie Lewis ed868c140b Optimistically Sync Servers on Import Bundle
continuous-integration/drone/pr Build is passing Details
2021-05-31 16:26:51 -07:00
erinn a98b5deb02 Merge pull request 'Create & Leave Groups' (#44) from ui-fixes into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #44
2021-05-28 14:20:54 -07:00
Sarah Jamie Lewis af338e7e95 Updae Cwtch
continuous-integration/drone/pr Build is passing Details
2021-05-28 11:27:59 -07:00
Sarah Jamie Lewis 1787f87607 Creat & Leave Groups
continuous-integration/drone/pr Build is failing Details
2021-05-28 02:12:41 -07:00
Dan Ballard 47f8cf2f42 Merge pull request 'Upgrade Cwtch + Send Message to Peer Error Propagation.' (#43) from ui-fixes into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #43
2021-05-26 15:58:42 -07:00
Sarah Jamie Lewis b4681241bc go.sum
continuous-integration/drone/pr Build is passing Details
2021-05-26 15:48:01 -07:00
Sarah Jamie Lewis 30490f7b9d Upgrade Cwtch
continuous-integration/drone/pr Build is passing Details
2021-05-26 15:46:24 -07:00
Sarah Jamie Lewis 406b3510b8 IndexedError + Default Theme to Dark 2021-05-26 15:21:01 -07:00
erinn 84d85b7694 Merge pull request 'Turn off old groups + Upgrade Cwtch' (#42) from groups into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #42
2021-05-19 18:14:21 -07:00
Sarah Jamie Lewis c9d589a27a Upgrade Cwtch
continuous-integration/drone/pr Build is passing Details
2021-05-19 16:42:39 -07:00
Sarah Jamie Lewis 66d7c63c08 Turn off old groups in UI 2021-05-19 16:42:39 -07:00
erinn 83c3ad21c3 Merge pull request 'Using new group API to avoid replicated code' (#41) from groups into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #41
2021-05-18 16:00:20 -07:00
erinn 299595180b Merge branch 'trunk' into groups
continuous-integration/drone/pr Build is passing Details
2021-05-18 16:00:12 -07:00
Sarah Jamie Lewis dd0fc13b89 Using new group API to avoid replicated code
continuous-integration/drone/pr Build is passing Details
2021-05-18 13:42:38 -07:00
erinn 2788c068dd Merge pull request 'Upgrade groups' (#40) from groups into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #40
2021-05-18 13:00:17 -07:00
Sarah Jamie Lewis 2c76504e0b Upgrade groups
continuous-integration/drone/pr Build is passing Details
2021-05-18 12:55:25 -07:00
Sarah Jamie Lewis bef54c6091 Merge pull request 'add sendinvitation' (#38) from sendinvite into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #38
2021-05-11 15:37:53 -07:00
erinn 54c2048f58 addressing comments on #38
continuous-integration/drone/pr Build is passing Details
2021-05-11 15:35:58 -07:00
erinn 33c42f32d2 Merge branch 'trunk' of git.openprivacy.ca:flutter/libcwtch-go into sendinvite
continuous-integration/drone/pr Build is passing Details
2021-05-10 18:57:59 -07:00
erinn a1095b7c35 Merge pull request 'Version Bump to 0.7.6' (#37) from groups into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #37
2021-05-10 17:12:03 -07:00
Sarah Jamie Lewis 62db995842 Version Bump to 0.7.6
continuous-integration/drone/pr Build was killed Details
2021-05-10 17:11:23 -07:00
erinn f6d4708501 adding sendinvitation 2021-05-10 16:58:25 -07:00
erinn dbf12007c7 Merge pull request 'Upgrade Cwtch for SendMessageToGroupError' (#36) from groups into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #36
2021-05-10 16:57:28 -07:00
Sarah Jamie Lewis 8a67b88f2e Upgrade Cwtch for SendMessageToGroupError
continuous-integration/drone/pr Build is passing Details
2021-05-10 16:54:37 -07:00
erinn 7ad9c25b19 Merge pull request 'Support partially syncing groups (faster syncing)' (#35) from groups into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #35
2021-05-07 16:49:39 -07:00
erinn f4ea408b7b Merge branch 'trunk' into groups
continuous-integration/drone/pr Build is passing Details
2021-05-07 16:49:31 -07:00
Sarah Jamie Lewis c1121f13a1 Support partially syncing groups (faster syncing)
continuous-integration/drone/pr Build is passing Details
Also properly handle server connection issues for groups
2021-05-07 16:35:07 -07:00
erinn df414ed23f Merge pull request 'New Cwtch Group API' (#34) from groups into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #34
2021-05-05 15:21:57 -07:00
Sarah Jamie Lewis 699d249ad7 Switch FFI
continuous-integration/drone/pr Build is passing Details
2021-05-05 15:19:07 -07:00
Sarah Jamie Lewis c2e3488a45 Remove Debug
continuous-integration/drone/pr Build is passing Details
2021-05-05 15:17:35 -07:00
Sarah Jamie Lewis 4df9a22075 New Cwtch Group API
continuous-integration/drone/pr Build is passing Details
2021-05-05 13:35:14 -07:00
Sarah Jamie Lewis 8f493b8fbb Merge pull request 'subscribe to indexed acks' (#33) from indexedacks into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #33
2021-05-03 11:51:25 -07:00
erinn 7f5e25d55a update cwtch.im dep
continuous-integration/drone/pr Build is failing Details
2021-05-03 11:47:07 -07:00
erinn b5f4e5c0e7 subscribe to indexed acks
continuous-integration/drone/pr Build is failing Details
2021-05-03 11:37:34 -07:00
erinn d5e9907ca4 Merge pull request 'Return Enhanced Peer Message' (#32) from groups into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #32
2021-05-02 20:49:50 -07:00
Sarah Jamie Lewis aef23fde74 Return Enhanced Peer Message
continuous-integration/drone/pr Build is passing Details
2021-05-02 20:45:18 -07:00
erinn 3f07d9a134 Merge pull request 'Accept/Reject Group Invite' (#31) from groups into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #31
2021-04-28 15:46:27 -07:00
erinn f4e8bb3e64 Merge branch 'trunk' into groups
continuous-integration/drone/pr Build is passing Details
2021-04-28 15:46:14 -07:00
Sarah Jamie Lewis e679b5f47b Accept/Reject Group Invite
continuous-integration/drone/pr Build is passing Details
2021-04-28 15:24:40 -07:00
erinn 75dcf71c02 Merge pull request 'bugfixes from android testing' (#30) from groups into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #30
2021-04-28 14:11:26 -07:00
erinn 7a1cc55781 Merge branch 'trunk' into groups
continuous-integration/drone/pr Build is passing Details
2021-04-28 14:11:17 -07:00
Dan Ballard d7e6af917c drone.yml: drone gitea release: reenable trigger restrictions
continuous-integration/drone/push Build is passing Details
2021-04-27 16:33:23 -07:00
Dan Ballard 31b5b4b13e drone.yml: drone gitea release try to get triggerable rm all restrictions
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2021-04-27 16:20:52 -07:00
Dan Ballard 3637ea6875 drone.yml: drone gitea release try to get triggerable
continuous-integration/drone/push Build is passing Details
2021-04-27 14:14:43 -07:00
Dan Ballard 4b95bbfe3e drone.yml: drone gitea release try to get triggerable
continuous-integration/drone/push Build is passing Details
2021-04-27 13:34:05 -07:00
Dan Ballard 8c6156385a drone.yml: react to tag and publish results
continuous-integration/drone/push Build is passing Details
2021-04-27 12:16:03 -07:00
erinn 5ccbb5b7d4 Merge branch 'trunk' into groups
continuous-integration/drone/pr Build is passing Details
2021-04-27 12:14:08 -07:00
Sarah Jamie Lewis 3d2c8c9fbf bugfixes from android testing
continuous-integration/drone/pr Build is passing Details
2021-04-27 12:12:26 -07:00
erinn c6c9f034ae Merge pull request 'Upgrade Cwtch and Enhance New Group Invite' (#29) from groups into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #29
2021-04-23 14:29:19 -07:00
Sarah Jamie Lewis a99a7c659e Upgrade Cwtch and Enhance New Group Invite
continuous-integration/drone/pr Build is passing Details
2021-04-23 14:18:45 -07:00
erinn 65fd80b21a Merge pull request 'Set Group Attribute' (#28) from groups into trunk
continuous-integration/drone/push Build is failing Details
Reviewed-on: #28
2021-04-23 13:02:24 -07:00
Sarah Jamie Lewis d5f8dbc48b SetGroupAttribute
continuous-integration/drone/pr Build is passing Details
2021-04-23 12:56:20 -07:00
Sarah Jamie Lewis 3985b350f8 BUGFIX: Don't send Server contacts to the UI
continuous-integration/drone/pr Build is passing Details
2021-04-23 12:31:35 -07:00
erinn b05c734ab7 Merge pull request 'Import Group Bundles and Sending Group Messages' (#26) from server-lists into trunk
continuous-integration/drone/push Build is failing Details
Reviewed-on: #26
2021-04-22 14:30:01 -07:00
erinn 996052cb13 Merge branch 'trunk' into server-lists
continuous-integration/drone/pr Build is passing Details
2021-04-22 14:29:48 -07:00
Sarah Jamie Lewis 624f56f6f8 Import Group Bundles and Sending Group Messages
continuous-integration/drone/pr Build is passing Details
2021-04-22 14:28:58 -07:00
erinn f21ded32c4 Merge pull request 'Group Functionality Experiments with Server Lists' (#25) from server-lists into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #25
2021-04-20 16:04:10 -07:00
Sarah Jamie Lewis 347bb27310 Push Group Information to UI, Fix bug with fetching messages for groups
continuous-integration/drone/pr Build is passing Details
2021-04-20 15:24:00 -07:00
Sarah Jamie Lewis f9b4e1179e Group Functionality Experiments with Server Lists
continuous-integration/drone/pr Build is passing Details
2021-04-15 15:18:16 -07:00
erinn 154ba4610d Merge pull request 'ACNRestart API and Auto Re-listen on Tor Setup' (#24) from acn into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #24
2021-04-13 15:33:15 -07:00
Sarah Jamie Lewis 92402c08f5 ACNRestart API and Auto Re-listen on Tor Setup
continuous-integration/drone/pr Build is passing Details
2021-04-13 15:26:48 -07:00
Sarah Jamie Lewis 32de6272c1 Merge pull request 'sort and update contact list' (#23) from clsort into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #23
2021-04-13 14:01:23 -07:00
erinn c3973ecd92 Merge branch 'trunk' into clsort
continuous-integration/drone/pr Build is passing Details
2021-04-12 17:06:10 -07:00
erinn 0224372a66 sort and update contact list
continuous-integration/drone/pr Build is passing Details
2021-04-12 17:04:21 -07:00
Sarah Jamie Lewis bd7bccb9d2 Merge pull request 'update ui on block' (#22) from erinn237 into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #22
2021-04-12 15:44:37 -07:00
erinn 91b3d19cf0 fixes #237
continuous-integration/drone/pr Build is passing Details
2021-04-12 15:27:53 -07:00
Sarah Jamie Lewis 17bd2da4c4 Upgrade Cwtch / Tapir / Connectivity / Logging + API Call
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2021-04-09 20:05:59 -07:00
Sarah Jamie Lewis b3cbd5905b Merge pull request 'approve contacts, send messages' (#20) from sendagain into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #20
2021-04-09 19:51:48 -07:00
erinn c648f067af Merge branch 'trunk' of git.openprivacy.ca:flutter/libcwtch-go into sendagain
continuous-integration/drone/pr Build is passing Details
2021-04-09 19:41:37 -07:00
erinn 6e1cb20d66 mo wirin
continuous-integration/drone/pr Build is running Details
2021-04-09 19:31:05 -07:00
erinn 0c963172ff restoring sendmessage yay 2021-04-07 22:06:21 -07:00
Dan Ballard d8e21ee2c9 Merge pull request 'Adding Block/Allow Unknown Contact Global Setting' (#19) from peersettings into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #19
2021-04-06 15:13:22 -07:00
Sarah Jamie Lewis fe84c6ac47 Adding Block/Allow Unknown Contact Global Setting
continuous-integration/drone/pr Build is passing Details
2021-04-06 15:08:52 -07:00
Dan Ballard 6e9d423d58 drone.yml new drone format settings for email
continuous-integration/drone/push Build is passing Details
2021-03-30 14:04:17 -07:00
Dan Ballard 6e826691fa drone.yml new drone format settings for drone-gogs
continuous-integration/drone/push Build is passing Details
2021-03-29 16:55:42 -07:00
Dan Ballard 3ea5121998 Merge pull request 'Add Contact Flow' (#18) from peersettings into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #18
2021-03-29 15:39:35 -07:00
Sarah Jamie Lewis fd53fadef9 Add Contact Flow
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
2021-03-29 15:28:32 -07:00
Dan Ballard 2bfaaada54 drone.yml: update sha256 to exclude itself
continuous-integration/drone/push Build is passing Details
2021-03-26 18:01:35 -07:00
Dan Ballard 951a9cf10e gogs step use from_secret
continuous-integration/drone/push Build is passing Details
2021-03-25 17:40:28 -07:00
Dan Ballard 4967f5f3f8 drone.yml: deploy stage working and sha256
continuous-integration/drone/push Build is passing Details
2021-03-25 15:53:03 -07:00
Dan Ballard 3efb31e9f3 drone.yml: deploy stage debug
continuous-integration/drone/push Build is passing Details
2021-03-25 15:33:45 -07:00
Dan Ballard 3e6eaca789 drone.yml: deploy stage + notify-gogs on PR only
continuous-integration/drone/push Build is failing Details
2021-03-25 14:00:52 -07:00
Dan Ballard 77247bc4b9 drone.yml rely on trigger, rm most when
continuous-integration/drone/push Build is failing Details
2021-03-24 18:34:03 -07:00
Dan Ballard a0a836ebc6 drone.yml triggers to filter
continuous-integration/drone/push Build is passing Details
2021-03-24 18:32:00 -07:00
Dan Ballard d032211f27 drone.yml windows set GOPATH
continuous-integration/drone/push Build is passing Details
2021-03-24 18:14:49 -07:00
Dan Ballard 36e344053a Makefile: windows build. drone.yml: attempt windows build
continuous-integration/drone/push Build is passing Details
2021-03-24 16:57:53 -07:00
Dan Ballard da82e25575 drone.yml use official openpriv/android-go-mobile:2021.03
continuous-integration/drone/push Build is passing Details
2021-03-23 11:24:29 -07:00
Dan Ballard 15be89ac13 drone.yml: using hte new drone gomobile image we can cut all the needless instructions
continuous-integration/drone/push Build is passing Details
2021-03-22 18:14:21 -07:00
Dan Ballard f1c2b3094a drone.yml gomobile explode /go from GOPATH
continuous-integration/drone/push Build is passing Details
2021-03-22 18:04:00 -07:00
Dan Ballard f9d7230427 less gomobile related cleaning
continuous-integration/drone/push Build is passing Details
2021-03-22 11:48:17 -07:00
Dan Ballard bfafbbcd4a drone.yml: even more gomobile commands to make it work
continuous-integration/drone/push Build is passing Details
2021-03-19 14:56:02 -07:00
Dan Ballard a741aea975 Merge pull request 'Launch Peers on NewPeer + Set Name' (#16) from peersettings into trunk
continuous-integration/drone/push Build is failing Details
Reviewed-on: #16
2021-03-19 14:52:36 -07:00
Sarah Jamie Lewis 1fa86ca0ef Merge branch 'trunk' into peersettings
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
2021-03-19 14:51:07 -07:00
Dan Ballard 3b2c8a3d7b drone.yml: more gomobile commands to make it work
continuous-integration/drone/push Build is failing Details
2021-03-19 14:50:45 -07:00
Sarah Jamie Lewis faffb3e042 Dan Comments + Upgrade to Cwtch 0.6.0
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
2021-03-19 14:49:44 -07:00
Sarah Jamie Lewis 8072496afe Launch Peers on NewPeer + Set Name 2021-03-19 12:58:07 -07:00
Dan Ballard b678b3b77d drone.yml gomobile try gets
continuous-integration/drone/push Build is failing Details
2021-03-17 17:36:16 -07:00
Dan Ballard dac7d145d2 drone.yml wrong images
continuous-integration/drone/push Build is failing Details
2021-03-17 16:59:02 -07:00
Dan Ballard 3d1de1cd6e drone.yml new gomobile image
continuous-integration/drone/push Build is failing Details
2021-03-17 16:53:46 -07:00
Sarah Jamie Lewis e1d4eed7ac Merge pull request 'initialize message count' (#15) from rinnmar17 into trunk
continuous-integration/drone/push Build is failing Details
Reviewed-on: #15
2021-03-17 16:00:08 -07:00
erinn f3870163db Merge branch 'trunk' of git.openprivacy.ca:flutter/libcwtch-go into rinnmar17
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
2021-03-17 15:51:48 -07:00
erinn cf23b04f2d Merge pull request 'Save Peer History Settings' (#14) from peersettings into trunk
continuous-integration/drone/push Build is failing Details
Reviewed-on: #14
2021-03-17 15:49:39 -07:00
erinn e439c0954b initialize message counter 2021-03-17 15:42:09 -07:00
Sarah Jamie Lewis c18844a0d5 Save Peer History Settings
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
2021-03-17 15:39:59 -07:00
erinn 0677dc308b Merge pull request 'Peer Settings Pane Changes' (#13) from logging into trunk
continuous-integration/drone/push Build is failing Details
Reviewed-on: #13
2021-03-17 15:33:18 -07:00
erinn 2edaff73b5 Merge branch 'trunk' into logging
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
2021-03-17 15:24:45 -07:00
Sarah Jamie Lewis a7524d6c55 Peer Settings Pane Changes 2021-03-17 14:51:43 -07:00
Dan Ballard e9b6e10795 drone.yml keep vendors
continuous-integration/drone/push Build is failing Details
2021-03-17 13:41:52 -07:00
Dan Ballard d53d81f8d0 .drone.yml go get golint... not go1.16 yet
continuous-integration/drone/push Build is failing Details
2021-03-17 13:22:02 -07:00
Dan Ballard 68e60d438d fix govet 'error' and disable .drone.yml quality linting
continuous-integration/drone/push Build is failing Details
2021-03-17 13:16:27 -07:00
Dan Ballard 94792c5953 .drone.yml syntax fix
continuous-integration/drone/push Build is failing Details
2021-03-17 12:47:32 -07:00
Dan Ballard 8837a62808 Merge branch 'trunk' of git.openprivacy.ca:flutter/libcwtch-go into trunk 2021-03-17 12:43:36 -07:00
Dan Ballard f0870e4407 drone pipeline file test 2021-03-17 12:35:55 -07:00
erinn 1e983fadd4 Merge pull request 'Set default logging level and allow event bus configuration' (#12) from logging into trunk
Reviewed-on: #12
2021-03-17 12:22:17 -07:00
Sarah Jamie Lewis 64aeaad23a Set default logging level and allow event bus configuration 2021-03-17 12:14:44 -07:00
Dan Ballard eb06bdd19a Merge pull request 'unwrap eventprofileenvelope' (#11) from rinnmar16 into trunk
Reviewed-on: #11
2021-03-16 15:16:36 -07:00
erinn 1902944bc7 unwrap eventprofileenvelope 2021-03-16 15:16:04 -07:00
erinn 6e4547c8bb unwrap eventprofileenvelope 2021-03-16 15:05:44 -07:00
erinn b61094f5d4 Merge pull request 'Experimental Settings' (#10) from settings into trunk
Reviewed-on: #10
2021-03-16 13:58:29 -07:00
erinn d110e870ec Merge branch 'trunk' into settings 2021-03-16 13:58:01 -07:00
Sarah Jamie Lewis eb570745a5 Formatt + if/else 2021-03-16 13:55:50 -07:00
Sarah Jamie Lewis 3960244756 Fix condition on Accept 2021-03-16 13:54:31 -07:00
Dan Ballard 2359d8a2e4 Merge pull request 'contacts rewire' (#9) from contacts into trunk
Reviewed-on: #9
2021-03-16 13:49:11 -07:00
Sarah Jamie Lewis c230746546 Experimental Settings 2021-03-16 12:54:25 -07:00
18 changed files with 1417 additions and 377 deletions

137
.drone.yml Normal file
View File

@ -0,0 +1,137 @@
---
kind: pipeline
type: docker
name: default
steps:
- name: fetch
image: golang
volumes:
- name: deps
path: /go
commands:
- wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/tor
- wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/torrc
- chmod a+x tor
- go get -u golang.org/x/lint/golint
- git fetch --tags
#- export GO111MODULE=on
#- go mod vendor
- go get
# TODO: upgrade to go1.16, remove mod/vendor, add go install for 1.16
- echo `git describe --tags` > VERSION
- echo `date +%G-%m-%d-%H-%M` > BUILDDATE
- name: quality
image: golang
volumes:
- name: deps
path: /go
commands:
- go list ./... | xargs go vet
- go list ./... | xargs golint
#Todo: fix all the lint errors and add `-set_exit_status` above to enforce linting
- name: build-linux
image: golang
volumes:
- name: deps
path: /go
commands:
- make linux
- name: build-android
image: openpriv/android-go-mobile:2021.03
volumes:
- name: deps
path: /go
commands:
- go mod download
- gomobile init
- make android
- name: build-windows
image: openpriv/mingw-go:2021.03
environment:
GOPATH: /go
volumes:
- name: deps
path: /go
commands:
- make windows
- name: deploy-buildfiles
image: kroniak/ssh-client
environment:
BUILDFILES_KEY:
from_secret: buildfiles_key
secrets: [gogs_account_token]
when:
event:
- push
- tag
status: [ success ]
commands:
- echo $BUILDFILES_KEY > ~/id_rsab64
- base64 -d ~/id_rsab64 > ~/id_rsa
- chmod 400 ~/id_rsa
- export DIR=libCwtch-go-`cat VERSION`-`cat BUILDDATE`
- mkdir $DIR
- mv libCwtch.so libCwtch.dll cwtch.aar cwtch-sources.jar libCwtch.h $DIR/
- cd $DIR
- find . -type f -exec sha256sum {} \; > ./../sha256s.txt
- mv ./../sha256s.txt .
- cd ..
- scp -r -o StrictHostKeyChecking=no -i ~/id_rsa $DIR buildfiles@openprivacy.ca:/home/buildfiles/buildfiles/
- name: gitea-release
image: plugins/gitea-release
when:
event: tag
settings:
api_key:
from_secret: gogs_account_token
base_url: https://git.openprivacy.ca
files:
- libCwtch.so
- libCwtch.dll
- cwtch.aar
- cwtch-sources.jar
- libCwtch.h
checksum:
- sha256
- sha512
- name: notify-email
image: drillster/drone-email
settings:
host: build.openprivacy.ca
port: 25
skip_verify: true
from: drone@openprivacy.ca
when:
status: [ failure ]
- name: notify-gogs
image: openpriv/drone-gogs
when:
event: pull_request
status: [ success, changed, failure ]
environment:
GOGS_ACCOUNT_TOKEN:
from_secret: gogs_account_token
settings:
gogs_url: https://git.openprivacy.ca
volumes:
# gopath where bin and pkg lives to persist across steps
- name: deps
temp: {}
trigger:
repo: flutter/libcwtch-go
branch: trunk
event:
- push
- pull_request
- tag

1
.gitignore vendored
View File

@ -3,3 +3,4 @@ cwtch-sources.jar
cwtch.aar
libCwtch.h
libCwtch.so
libCwtch.dll

View File

@ -8,6 +8,8 @@ linux: libCwtch.so
android: cwtch.aar
windows: libCwtch.dll
libCwtch.so: lib.go
./switch-ffi.sh
go build -buildmode c-shared -o libCwtch.so
@ -16,5 +18,9 @@ cwtch.aar: lib.go
./switch-gomobile.sh
gomobile bind -target android
libCwtch.dll: lib.go
./switch-ffi.sh
GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc-win32 go build -buildmode c-shared -o libCwtch.dll
clean:
rm -f cwtch.aar cwtch_go.apk libCwtch.h libCwtch.so cwtch-sources.jar
rm -f cwtch.aar cwtch_go.apk libCwtch.h libCwtch.so cwtch-sources.jar libCwtch.dll

5
constants/globals.go Normal file
View File

@ -0,0 +1,5 @@
package constants
// We offer "un-passworded" profiles but our storage encrypts everything with a password. We need an agreed upon
// password to use in that case, that the app case use behind the scenes to password and unlock with
const DefactoPasswordForUnencryptedProfiles = "be gay do crime"

View File

@ -0,0 +1,41 @@
package contact
import (
"cwtch.im/cwtch/model"
"cwtch.im/cwtch/peer"
"git.openprivacy.ca/flutter/libcwtch-go/features"
"git.openprivacy.ca/openprivacy/connectivity/tor"
)
// Functionality groups some common UI triggered functions for contacts...
type Functionality struct {
}
const addContactPrefix = "addcontact"
const sendMessagePrefix = "sendmessage"
// FunctionalityGate returns contact.Functionality always
func FunctionalityGate(experimentMap map[string]bool) (*Functionality, error) {
return new(Functionality), nil
}
// SendMessage handles sending messages to contacts
func (pf *Functionality) SendMessage(peer peer.SendMessages, handle string, message string) features.Response {
eventID := peer.SendMessageToPeer(handle, message)
return features.ConstructResponse(sendMessagePrefix, eventID)
}
// HandleImportString handles contact import strings
func (pf *Functionality) HandleImportString(peer peer.ModifyContactsAndPeers, importString string) features.Response {
if tor.IsValidHostname(importString) {
if peer.GetContact(importString) == nil {
peer.AddContact(importString, importString, model.AuthApproved)
// Implicit Peer Attempt
peer.PeerWithOnion(importString)
return features.ConstructResponse(addContactPrefix, "success")
}
return features.ConstructResponse(addContactPrefix, "contact_already_exists")
}
return features.ConstructResponse(addContactPrefix, "invalid_import_string")
}

View File

@ -0,0 +1,124 @@
package contact
import (
"cwtch.im/cwtch/model"
"git.openprivacy.ca/flutter/libcwtch-go/features"
"testing"
)
const ValidHostname = "openpravyvc6spbd4flzn4g2iqu4sxzsizbtb5aqec25t76dnoo5w7yd"
type MockPeer struct {
hasContact bool
addContact bool
peerRequest bool
}
func (m MockPeer) BlockUnknownConnections() {
panic("should never be called")
}
func (m MockPeer) AllowUnknownConnections() {
panic("should never be called")
}
func (m MockPeer) GetContacts() []string {
panic("should never be called")
}
func (m MockPeer) GetContact(s string) *model.PublicProfile {
if m.hasContact {
return &(model.GenerateNewProfile("").PublicProfile)
}
return nil
}
func (m MockPeer) GetContactAttribute(s string, s2 string) (string, bool) {
panic("should never be called")
}
func (m *MockPeer) AddContact(nick, onion string, authorization model.Authorization) {
m.addContact = true
}
func (m MockPeer) SetContactAuthorization(s string, authorization model.Authorization) error {
panic("should never be called")
}
func (m MockPeer) SetContactAttribute(s string, s2 string, s3 string) {
panic("should never be called")
}
func (m MockPeer) DeleteContact(s string) {
panic("should never be called")
}
func (m *MockPeer) PeerWithOnion(s string) {
m.peerRequest = true
}
func (m MockPeer) JoinServer(s string) error {
panic("should never be called")
}
func TestContactFunctionality_InValidHostname(t *testing.T) {
cf, _ := FunctionalityGate(map[string]bool{})
peer := &MockPeer{
hasContact: false,
addContact: false,
peerRequest: false,
}
response := cf.HandleImportString(peer, "")
if peer.addContact || peer.peerRequest {
t.Fatalf("HandleImportString for a malformed import string should have no resulted in addContact or a peerRequest: %v", peer)
}
if response.Error() != features.ConstructResponse(addContactPrefix, "invalid_import_string").Error() {
t.Fatalf("Response to a successful import is malformed: %v", response)
}
}
func TestContactFunctionality_ValidHostnameExistingContact(t *testing.T) {
cf, _ := FunctionalityGate(map[string]bool{})
peer := &MockPeer{
hasContact: true,
addContact: false,
peerRequest: false,
}
response := cf.HandleImportString(peer, ValidHostname)
if peer.addContact || peer.peerRequest {
t.Fatalf("HandleImportString for a valid string should not call addContact or a peerRequest when the contact already exists: %v", peer)
}
if response.Error() != features.ConstructResponse(addContactPrefix, "contact_already_exists").Error() {
t.Fatalf("Response to a successful import is malformed: %v", response)
}
}
func TestContactFunctionality_ValidHostnameUnknownContact(t *testing.T) {
cf, _ := FunctionalityGate(map[string]bool{})
peer := &MockPeer{
hasContact: false,
addContact: false,
peerRequest: false,
}
response := cf.HandleImportString(peer, ValidHostname)
if peer.addContact && peer.peerRequest {
if response.Error() != features.ConstructResponse(addContactPrefix, "success").Error() {
t.Fatalf("Response to a successful import is malformed: %v", response)
}
} else {
t.Fatalf("HandleImportString for a valid import string should have resulted in addContact or a peerRequest: %v", peer)
}
}

View File

@ -0,0 +1,127 @@
package groups
import (
"cwtch.im/cwtch/event"
"cwtch.im/cwtch/model"
"cwtch.im/cwtch/peer"
"encoding/base64"
"fmt"
"git.openprivacy.ca/flutter/libcwtch-go/features"
"git.openprivacy.ca/openprivacy/log"
"strings"
)
const serverPrefix = "server:"
const tofuBundlePrefix = "tofubundle:"
const groupPrefix = "torv3"
const groupExperiment = "tapir-groups-experiment"
const importBundlePrefix = "importBundle"
const (
// ServerList is a json encoded list of servers
ServerList = event.Field("ServerList")
)
const (
// UpdateServerInfo is an event containing a ProfileOnion and a ServerList
UpdateServerInfo = event.Type("UpdateServerInfo")
)
// ReadServerInfo is a meta-interface for reading information about servers..
type ReadServerInfo interface {
peer.ReadContacts
peer.ReadServers
}
// GroupFunctionality provides experiment gated server functionality
type GroupFunctionality struct {
}
// ExperimentGate returns GroupFunctionality if the experiment is enabled, and an error otherwise.
func ExperimentGate(experimentMap map[string]bool) (*GroupFunctionality, error) {
if experimentMap[groupExperiment] {
return new(GroupFunctionality), nil
}
return nil, fmt.Errorf("gated by %v", groupExperiment)
}
// SendMessage is a deprecated api
func (gf *GroupFunctionality) SendMessage(peer peer.CwtchPeer, handle string, message string) (string, error) {
// TODO this auto accepting behaviour needs some thinking through
if !peer.GetGroup(handle).Accepted {
err := peer.AcceptInvite(handle)
if err != nil {
log.Errorf("tried to mark a nonexistent group as existed. bad!")
return "", err
}
}
return peer.SendMessageToGroupTracked(handle, message)
}
// ValidPrefix returns true if an import string contains a prefix that indicates it contains information about a
// server or a group
func (gf *GroupFunctionality) ValidPrefix(importString string) bool {
return strings.HasPrefix(importString, tofuBundlePrefix) || strings.HasPrefix(importString, serverPrefix) || strings.HasPrefix(importString, groupPrefix)
}
// GetServerInfoList compiles all the information the UI might need regarding all servers..
func (gf *GroupFunctionality) GetServerInfoList(profile ReadServerInfo) []Server {
var servers []Server
for _, server := range profile.GetServers() {
servers = append(servers, gf.GetServerInfo(server, profile))
}
return servers
}
// GetServerInfo compiles all the information the UI might need regarding a particular server including any verified
// cryptographic keys
func (gf *GroupFunctionality) GetServerInfo(serverOnion string, profile peer.ReadContacts) Server {
serverInfo := profile.GetContact(serverOnion)
keyTypes := []model.KeyType{model.KeyTypeServerOnion, model.KeyTypeTokenOnion, model.KeyTypePrivacyPass}
var serverKeys []ServerKey
for _, keyType := range keyTypes {
if key, has := serverInfo.GetAttribute(string(keyType)); has {
serverKeys = append(serverKeys, ServerKey{Type: string(keyType), Key: key})
}
}
return Server{Onion: serverOnion, Status: serverInfo.State, Keys: serverKeys}
}
// HandleImportString handles import strings for groups and servers
func (gf *GroupFunctionality) HandleImportString(peer peer.CwtchPeer, importString string) error {
if strings.HasPrefix(importString, tofuBundlePrefix) {
bundle := strings.Split(importString, "||")
if len(bundle) == 2 {
err := gf.HandleImportString(peer, bundle[0][len(tofuBundlePrefix):])
// if the server import failed then abort the whole process..
if !strings.HasSuffix(err.Error(), "success") {
return features.ConstructResponse(importBundlePrefix, err.Error())
}
return gf.HandleImportString(peer, bundle[1])
}
} else if strings.HasPrefix(importString, serverPrefix) {
// Server Key Bundles are prefixed with
bundle, err := base64.StdEncoding.DecodeString(importString[len(serverPrefix):])
if err == nil {
if err = peer.AddServer(string(bundle)); err != nil {
return features.ConstructResponse(importBundlePrefix, err.Error())
}
return features.ConstructResponse(importBundlePrefix, "success")
}
return features.ConstructResponse(importBundlePrefix, err.Error())
} else if strings.HasPrefix(importString, groupPrefix) {
//eg: torv3JFDWkXExBsZLkjvfkkuAxHsiLGZBk0bvoeJID9ItYnU=EsEBCiBhOWJhZDU1OTQ0NWI3YmM2N2YxYTM5YjkzMTNmNTczNRIgpHeNaG+6jy750eDhwLO39UX4f2xs0irK/M3P6mDSYQIaOTJjM2ttb29ibnlnaGoyenc2cHd2N2Q1N3l6bGQ3NTNhdW8zdWdhdWV6enB2ZmFrM2FoYzRiZHlkCiJAdVSSVgsksceIfHe41OJu9ZFHO8Kwv3G6F5OK3Hw4qZ6hn6SiZjtmJlJezoBH0voZlCahOU7jCOg+dsENndZxAA==
if gid, err := peer.ImportGroup(importString); err != nil {
return features.ConstructResponse(importBundlePrefix, err.Error())
} else {
// Auto accept the group here.
if peer.AcceptInvite(gid) != nil {
log.Errorf("Error accepting invite: %v", err)
}
return features.ConstructResponse(importBundlePrefix, "success")
}
}
return features.ConstructResponse(importBundlePrefix, "invalid_group_invite_prefix")
}

View File

@ -0,0 +1,39 @@
package groups
import "testing"
func TestGroupFunctionality_ValidPrefix(t *testing.T) {
gf, _ := ExperimentGate(map[string]bool{groupExperiment: true})
if gf.ValidPrefix("torv3blahblahblah") == false {
t.Fatalf("torv3 should be a valid prefix")
}
if gf.ValidPrefix("tofubundle:32432423||3242342") == false {
t.Fatalf("tofubundle should be a valid prefix")
}
if gf.ValidPrefix("server:23541233t") == false {
t.Fatalf("server should be a valid prefix")
}
if gf.ValidPrefix("alice!24234") == true {
t.Fatalf("alice should be an invalid predix")
}
}
func TestGroupFunctionality_IsEnabled(t *testing.T) {
_, err := ExperimentGate(map[string]bool{})
if err == nil {
t.Fatalf("group functionality should be disabled")
}
_, err = ExperimentGate(map[string]bool{groupExperiment: true})
if err != nil {
t.Fatalf("group functionality should be enabled")
}
_, err = ExperimentGate(map[string]bool{groupExperiment: false})
if err == nil {
t.Fatalf("group functionality should be disabled")
}
}

12
features/groups/server.go Normal file
View File

@ -0,0 +1,12 @@
package groups
type ServerKey struct {
Type string `json:"type"`
Key string `json:"key"`
}
type Server struct {
Onion string `json:"onion"`
Status string `json:"status"`
Keys []ServerKey `json:"keys"`
}

13
features/response.go Normal file
View File

@ -0,0 +1,13 @@
package features
import "errors"
// Response is a wrapper to better semantically convey the response type...
type Response error
const errorSeparator = "."
// ConstructResponse is a helper function for creating Response structures.
func ConstructResponse(prefix string, error string) Response {
return errors.New(prefix + errorSeparator + error)
}

6
go.mod
View File

@ -3,8 +3,8 @@ module git.openprivacy.ca/flutter/libcwtch-go
go 1.15
require (
cwtch.im/cwtch v0.5.1
git.openprivacy.ca/openprivacy/connectivity v1.3.3
cwtch.im/cwtch v0.8.11
git.openprivacy.ca/openprivacy/connectivity v1.4.4
git.openprivacy.ca/openprivacy/log v1.0.2
golang.org/x/mobile v0.0.0-20210208171126-f462b3930c8f // indirect
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect
)

73
go.sum
View File

@ -1,93 +1,74 @@
cwtch.im v0.4.11 h1:2FdbstCo8a2pSoM9+FnH9H8IL8TYpNgEDA6+vcstqWI=
cwtch.im/cwtch v0.5.1 h1:84foD/HBebPbA4gUEwp+feakeHkD3Di53Q3FnSbqDMM=
cwtch.im/cwtch v0.5.1/go.mod h1:snHZIZwRQPAZG2LRZsN5SpAIbeR597VJoDS+KHm7q9w=
cwtch.im/tapir v0.2.1 h1:t1YJB9q5sV1A9xwiiwL6WVfw3dwQWLoecunuzT1PQtw=
cwtch.im/tapir v0.2.1/go.mod h1:xzzZ28adyUXNkYL1YodcHsAiTt3IJ8Loc29YVn9mIEQ=
cwtch.im/cwtch v0.8.11 h1:nUrd6srjLxInSJ0q1JdmRBhN4RRlZRL+2vIyE/AXkfY=
cwtch.im/cwtch v0.8.11/go.mod h1:D9dtO+WnKqdmufKSfFeFlUYaxLTfE/RtqVe1OD0kiKc=
git.openprivacy.ca/cwtch.im/tapir v0.4.3 h1:sctSfUXHDIqaHfJPDl+5lHtmoEJolQiHTcHZGAe5Qc4=
git.openprivacy.ca/cwtch.im/tapir v0.4.3/go.mod h1:10qEaib5x021zgyZ/97JKWsEpedH5+Vfy2CvB2V+08E=
git.openprivacy.ca/openprivacy/bine v0.0.4 h1:CO7EkGyz+jegZ4ap8g5NWRuDHA/56KKvGySR6OBPW+c=
git.openprivacy.ca/openprivacy/bine v0.0.4/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU=
git.openprivacy.ca/openprivacy/connectivity v1.2.0/go.mod h1:B7vzuVmChJtSKoh0ezph5vu6DQ0gIk0zHUNG6IgXCcA=
git.openprivacy.ca/openprivacy/connectivity v1.3.3 h1:OKHZ/pzY95+UNOhF74DisSYPh7lULtjbxFQnK9r6cAk=
git.openprivacy.ca/openprivacy/connectivity v1.3.3/go.mod h1:DL9QitHjpyNspMUe3wjIej9gFgDK2FdRKP2JE4+7T90=
git.openprivacy.ca/openprivacy/log v1.0.0/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
git.openprivacy.ca/openprivacy/log v1.0.1/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
git.openprivacy.ca/openprivacy/connectivity v1.4.4 h1:11M3akVCyy/luuhMpZTM1r9Jayl7IHD944Bxsn2FDpU=
git.openprivacy.ca/openprivacy/connectivity v1.4.4/go.mod h1:JVRCIdL+lAG6ohBFWiKeC/MN42nnC0sfFszR9XG6vPQ=
git.openprivacy.ca/openprivacy/log v1.0.2 h1:HLP4wsw4ljczFAelYnbObIs821z+jgMPCe8uODPnGQM=
git.openprivacy.ca/openprivacy/log v1.0.2/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cretz/bine v0.1.1-0.20200124154328-f9f678b84cca/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc=
github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0=
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/struCoder/pidusage v0.1.3/go.mod h1:pWBlW3YuSwRl6h7R5KbvA4N8oOqe9LjaKW5CwT1SPjI=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200206161412-a0c6ece9d31a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee h1:4yd7jl+vXjalO5ztz6Vc1VADv+S/80LGJmyl1ROJ2AI=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f h1:kgfVkAEEQXXQ0qc6dH7n6y37NAYmTFmz0YRwrRjgxKw=
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mobile v0.0.0-20210208171126-f462b3930c8f h1:aEcjdTsycgPqO/caTgnxfR9xwWOltP/21vtJyFztEy0=
golang.org/x/mobile v0.0.0-20210208171126-f462b3930c8f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb h1:mUVeFHoDKis5nxCAzoAi7E8Ghb86EXh/RK6wtvJIqRY=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200625195345-7480c7b4547d h1:V1BGE5ZHrUIYZYNEm0i7jrPwSo3ks0HSn1TrartSqME=
golang.org/x/tools v0.0.0-20200625195345-7480c7b4547d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

645
lib.go
View File

@ -1,4 +1,5 @@
//package cwtch
package main
import "C"
@ -6,14 +7,19 @@ import (
"crypto/rand"
"cwtch.im/cwtch/app"
"cwtch.im/cwtch/event"
"cwtch.im/cwtch/model"
"cwtch.im/cwtch/model/attr"
"cwtch.im/cwtch/peer"
"encoding/json"
"fmt"
"git.openprivacy.ca/flutter/libcwtch-go/constants"
contact "git.openprivacy.ca/flutter/libcwtch-go/features/contacts"
"git.openprivacy.ca/flutter/libcwtch-go/features/groups"
"git.openprivacy.ca/flutter/libcwtch-go/utils"
"git.openprivacy.ca/openprivacy/connectivity"
"runtime"
"strconv"
"strings"
"encoding/base64"
"git.openprivacy.ca/openprivacy/connectivity/tor"
@ -25,23 +31,78 @@ import (
"time"
)
const (
// ProfileOnion is an event field that contains the handle for a given profile.
// todo: this should probably be moved back into Cwtch, and renamed ProfileHandle (onions are too tor-specific)
ProfileOnion = event.Field("ProfileOnion")
)
var application app.Application
var eventHandler *utils.EventHandler
var acnQueue event.Queue
var contactEventsQueue event.Queue
var globalACN connectivity.ACN
//export c_StartCwtch
func c_StartCwtch(dir_c *C.char, len C.int, tor_c *C.char, torLen C.int) {
dir := C.GoStringN(dir_c, len)
tor := C.GoStringN(tor_c, torLen)
StartCwtch(dir, tor)
// ChatMessage API currently not officially documented, see
// https://git.openprivacy.ca/cwtch.im/secure-development-handbook/issues/3
// for latest updates for now
//
// A ChatMessage is the application-layer Cwtch message, delivered to the UI
// as serialized json.
type ChatMessage struct {
O int `json:"o"`
D string `json:"d"`
}
func StartCwtch(appDir string, torPath string) {
log.SetLevel(log.LevelDebug)
//log.ExcludeFromPattern("service.go")
//export c_StartCwtch
func c_StartCwtch(dir_c *C.char, len C.int, tor_c *C.char, torLen C.int) int8 {
dir := C.GoStringN(dir_c, len)
tor := C.GoStringN(tor_c, torLen)
return int8(StartCwtch(dir, tor))
}
utils.InitGlobalSettingsFile(appDir, "be gay do crime")
// StartCwtch starts cwtch in the library and initlaizes all data structures
// GetAppbusEvents is always safe to use
// the rest of functions are unsafe until the CwtchStarted event has been received indicating StartCwtch has completed
// returns:
// message: CwtchStarted when start up is complete and app is safe to use
// CwtchStartError message when start up fails (includes event.Error data field)
func StartCwtch(appDir string, torPath string) int {
log.SetLevel(log.LevelInfo)
log.Infof("StartCwtch(...)")
// Quick hack check that we're being called with the correct params
// On android a stale worker could be calling us with "last apps" directory. Best to abort fast so the app can make a new worker
if runtime.GOOS == "android" {
fh, err := os.Open(torPath)
if err != nil {
log.Errorf("%v", err)
log.Errorf("failed to stat tor, skipping StartCwtch(). potentially normal if the app was reinstalled or the device was restarted; this workorder should get canceled soon")
return 1
}
_ = fh.Close()
}
go _startCwtch(appDir, torPath)
return 0
}
func _startCwtch(appDir string, torPath string) {
log.Infof("application: %v eventHandler: %v acn: %v", application, eventHandler, globalACN)
if application != nil {
log.Infof("_startCwtch detected existing application; resuming instead of relaunching")
ReconnectCwtchForeground()
return
}
// Exclude Tapir wire Messages
//(We need a TRACE level)
log.ExcludeFromPattern("service.go")
// Ensure that the application directory exists...and then initialize settings..
os.MkdirAll(path.Join(appDir), 0700)
utils.InitGlobalSettingsFile(appDir, constants.DefactoPasswordForUnencryptedProfiles)
log.Infof("Loading Cwtch Directory %v and tor path: %v", appDir, torPath)
@ -56,57 +117,104 @@ func StartCwtch(appDir string, torPath string) {
panic(err)
}
log.Infof("Creating new EventHandler()")
eventHandler = utils.NewEventHandler()
log.Infof("making directory %v", appDir)
os.MkdirAll(path.Join(appDir, "/.tor", "tor"), 0700)
tor.NewTorrc().WithSocksPort(port).WithOnionTrafficOnly().WithControlPort(controlPort).WithHashedPassword(base64.StdEncoding.EncodeToString(key)).Build(filepath.Join(appDir, ".tor", "tor", "torrc"))
acn, err := tor.NewTorACNWithAuth(path.Join(appDir, "/.tor"), torPath, controlPort, tor.HashedPasswordAuthenticator{base64.StdEncoding.EncodeToString(key)})
acn, err := tor.NewTorACNWithAuth(path.Join(appDir, "/.tor"), torPath, controlPort, tor.HashedPasswordAuthenticator{Password: base64.StdEncoding.EncodeToString(key)})
if err != nil {
log.Errorf("\nError connecting to Tor: %v\n", err)
log.Errorf("\nError connecting to Tor replacing with ErrorACN: %v\n", err)
eventHandler.PublishAppEvent(event.NewEventList(utils.CwtchStartError, event.Error, err))
return
}
//acn.WaitTillBootstrapped()
globalACN = acn
newApp := app.NewApp(acn, appDir)
acnQueue = event.NewQueue()
newApp.GetPrimaryBus().Subscribe(event.ACNStatus, acnQueue)
newApp.GetPrimaryBus().Subscribe(utils.UpdateGlobalSettings, acnQueue)
newApp.GetPrimaryBus().Subscribe(utils.SetLoggingLevel, acnQueue)
newApp.GetPrimaryBus().Subscribe(event.AppError, acnQueue)
eventHandler = utils.NewEventHandler(newApp)
eventHandler.HandleApp(newApp)
peer.DefaultEventsToHandle = []event.Type{
event.EncryptedGroupMessage,
event.NewMessageFromPeer,
event.PeerAcknowledgement,
event.NewGroupInvite,
event.PeerError,
event.SendMessageToPeerError,
event.SendMessageToGroupError,
event.NewGetValMessageFromPeer,
event.PeerStateChange,
event.NewRetValMessageFromPeer,
event.NewGroupInvite,
event.ServerStateChange,
event.ProtocolEngineStopped,
event.RetryServerRequest,
}
settings := utils.ReadGlobalSettings()
settingsJson, _ := json.Marshal(settings)
newApp.LoadProfiles("be gay do crime")
newApp.LaunchPeers()
newApp.LoadProfiles(constants.DefactoPasswordForUnencryptedProfiles)
application = newApp
// Send global settings to the UI...
application.GetPrimaryBus().Publish(event.NewEvent(utils.UpdateGlobalSettings, map[event.Field]string{event.Data: string(settingsJson)}))
log.Infof("libcwtch-go application SET\n")
log.Infof("libcwtch-go application launched")
application.GetPrimaryBus().Publish(event.NewEvent(utils.CwtchStarted, map[event.Field]string{}))
application.QueryACNVersion()
}
//export c_ACNEvents
func c_ACNEvents() *C.char {
return C.CString(ACNEvents())
//export c_ReconnectCwtchForeground
func c_ReconnectCwtchForeground() {
ReconnectCwtchForeground()
}
func ACNEvents() string {
select {
case myevent := <-acnQueue.OutChan():
return fmt.Sprintf("%v", myevent)
default:
return ""
// Like StartCwtch, but StartCwtch has already been called so we don't need to restart Tor etc (probably)
// Do need to re-send initial state tho, eg profiles that are already loaded
func ReconnectCwtchForeground() {
log.Infof("Reconnecting cwtchforeground")
if application == nil {
log.Errorf("ReconnectCwtchForeground: Application is nil, presuming stale thread, EXITING Reconnect\n")
return
}
// populate profile list
peerList := application.ListPeers()
for onion := range peerList {
eventHandler.Push(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: onion, event.Created: event.False, "Reload": event.True}))
}
for onion := range peerList {
// fix peerpeercontact message counts
contactList := application.GetPeer(onion).GetContacts()
for _, handle := range contactList {
totalMessages := application.GetPeer(onion).GetContact(handle).Timeline.Len() + len(application.GetPeer(onion).GetContact(handle).UnacknowledgedMessages)
eventHandler.Push(event.NewEvent(event.MessageCounterResync, map[event.Field]string{
event.Identity: onion,
event.RemotePeer: handle,
event.Data: strconv.Itoa(totalMessages),
}))
}
// fix peergroupcontact message counts
groupList := application.GetPeer(onion).GetGroups()
for _, groupID := range groupList {
totalMessages := application.GetPeer(onion).GetGroup(groupID).Timeline.Len() + len(application.GetPeer(onion).GetGroup(groupID).UnacknowledgedMessages)
eventHandler.Push(event.NewEvent(event.MessageCounterResync, map[event.Field]string{
event.Identity: onion,
event.GroupID: groupID,
event.Data: strconv.Itoa(totalMessages),
}))
}
}
application.GetPrimaryBus().Publish(event.NewEvent(utils.CwtchStarted, map[event.Field]string{}))
application.QueryACNStatus()
application.QueryACNVersion()
}
//export c_SendAppEvent
@ -135,6 +243,45 @@ func SendAppEvent(eventJson string) {
}
log.Debugf("New Settings %v", globalSettings)
utils.WriteGlobalSettings(globalSettings)
// Group Experiment Refresh
groupHandler, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
for profileOnion := range application.ListPeers() {
serverListForOnion := groupHandler.GetServerInfoList(application.GetPeer(profileOnion))
serversListBytes, _ := json.Marshal(serverListForOnion)
eventHandler.Push(event.NewEvent(groups.UpdateServerInfo, map[event.Field]string{"ProfileOnion": profileOnion, groups.ServerList: string(serversListBytes)}))
}
}
// Explicitly toggle blocking/unblocking of unknown connections for profiles
// that have been loaded.
if utils.ReadGlobalSettings().BlockUnknownConnections {
for onion := range application.ListPeers() {
application.GetPeer(onion).BlockUnknownConnections()
}
} else {
for onion := range application.ListPeers() {
application.GetPeer(onion).AllowUnknownConnections()
}
}
case utils.SetLoggingLevel:
_, warn := new_event.Data[utils.Warn]
_, error := new_event.Data[utils.Error]
_, debug := new_event.Data[utils.Debug]
_, info := new_event.Data[utils.Info]
// Assign logging level in priority order. The highest logging level wins in the
// event of multiple fields.
if info {
log.SetLevel(log.LevelInfo)
} else if warn {
log.SetLevel(log.LevelWarn)
} else if error {
log.SetLevel(log.LevelError)
} else if debug {
log.SetLevel(log.LevelDebug)
}
default: // do nothing
}
}
@ -147,6 +294,11 @@ func c_SendProfileEvent(onion_ptr *C.char, onion_len C.int, json_ptr *C.char, js
SendProfileEvent(onion, eventJson)
}
const (
AddContact = event.Type("AddContact")
ImportString = event.Field("ImportString")
)
// SendProfileEvent is a generic method for Rebroadcasting Profile Events from a UI
func SendProfileEvent(onion string, eventJson string) {
// Convert the Event Json back to a typed Event Struct, this will make the
@ -164,8 +316,23 @@ func SendProfileEvent(onion string, eventJson string) {
// We need to update the local cache
// Ideally I think this would be pushed back into Cwtch
switch new_event.EventType {
case AddContact:
// Peer Functionality is Always Enabled, so we forgo the existence check...
// TODO: Combine with GroupFunctionality to make a meta-handleimportstring that can do both!
pf, _ := contact.FunctionalityGate(utils.ReadGlobalSettings().Experiments)
err := pf.HandleImportString(peer, new_event.Data[ImportString])
eventHandler.Push(event.NewEvent(event.AppError, map[event.Field]string{event.Data: err.Error()}))
case event.SetAttribute:
peer.SetAttribute(new_event.Data[event.Key], new_event.Data[event.Data])
case event.SetPeerAttribute:
peer.SetContactAttribute(new_event.Data[event.RemotePeer], new_event.Data[event.Key], new_event.Data[event.Data])
case event.SetPeerAuthorization:
peer.SetContactAuthorization(new_event.Data[event.RemotePeer], model.Authorization(new_event.Data[event.Authorization]))
// If approved (e.g. after an unblock) we want to kick off peering again...
if model.Authorization(new_event.Data[event.Authorization]) == model.AuthApproved {
peer.PeerWithOnion(new_event.Data[event.RemotePeer])
}
default:
// rebroadcast catch all
log.Infof("Received Event %v for %v but no libCwtch handler found, relaying the event directly", new_event, onion)
@ -180,97 +347,38 @@ func c_GetAppBusEvent() *C.char {
// GetAppBusEvent blocks until an event
func GetAppBusEvent() string {
log.Debugf("appbusevent called")
for eventHandler == nil {
log.Debugf("waiting for eventHandler != nil")
time.Sleep(time.Second)
}
var json = ""
for json == "" {
log.Debugf("waiting for json != ''")
json = eventHandler.GetNextEvent()
}
log.Debugf("appbusevent: %v", json)
return json
}
//export c_GetProfileRepaintEvent
func c_GetProfileRepaintEvent() int8 {
if GetProfileRepaintEvent() {
return 1
} else {
return 0
}
}
func GetProfileRepaintEvent() bool {
<-acnQueue.OutChan()
return true
}
type Profile struct {
Name string `json:"name"`
Onion string `json:"onion"`
ImagePath string `json:"imagePath"`
}
//export c_GetProfiles
func c_GetProfiles() *C.char {
return C.CString(GetProfiles())
}
func GetProfiles() string {
peerList := application.ListPeers()
profiles := make([]Profile, len(peerList))
i := 0
for onion := range peerList {
name, _ := application.GetPeer(onion).GetAttribute(attr.GetPublicScope(constants.Name))
profiles[i] = Profile{
Name: name,
Onion: onion,
ImagePath: "",
}
i += 1
}
jsonBytes, _ := json.Marshal(profiles)
return string(jsonBytes)
}
//export c_GetContacts
func c_GetContacts(onion_ptr *C.char, onion_len C.int) *C.char {
return C.CString(GetContacts(C.GoStringN(onion_ptr, onion_len)))
}
func GetContacts(onion string) string {
log.Infof("Get Contacts for %v", onion)
mypeer := application.GetPeer(onion)
contactEventsQueue = event.NewQueue()
application.GetEventBus(onion).Subscribe(event.PeerStateChange, contactEventsQueue)
var contacts []utils.Contact
for _, contact := range mypeer.GetContacts() {
contactInfo := mypeer.GetContact(contact)
log.Infof("contactInfo %v", contactInfo)
contacts = append(contacts, utils.Contact{Name: contactInfo.Name, Onion: contactInfo.Onion, Status: contactInfo.State})
}
bytes, _ := json.Marshal(contacts)
return string(bytes)
}
//export c_CreateProfile
func c_CreateProfile(nick_ptr *C.char, nick_len C.int, pass_ptr *C.char, pass_len C.int) {
CreateProfile(C.GoStringN(nick_ptr, nick_len), C.GoStringN(pass_ptr, pass_len))
}
func CreateProfile(nick, pass string) {
application.CreatePeer(nick, pass)
}
//export c_SelectProfile
func c_SelectProfile(onion_ptr *C.char, onion_len C.int) *C.char {
return C.CString(SelectProfile(C.GoStringN(onion_ptr, onion_len)))
}
func SelectProfile(onion string) string {
log.Infof("Select Profile: %v", onion)
contactEventsQueue = event.NewQueue()
application.GetEventBus(onion).Subscribe(event.PeerStateChange, contactEventsQueue)
return ""
if pass == constants.DefactoPasswordForUnencryptedProfiles {
application.CreateTaggedPeer(nick, pass, constants.ProfileTypeV1DefaultPassword)
} else {
application.CreateTaggedPeer(nick, pass, constants.ProfileTypeV1Password)
}
}
//export c_LoadProfiles
@ -296,17 +404,82 @@ func ContactEvents() string {
}
}
//export c_NumMessages
func c_NumMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int) (n int) {
profile := C.GoStringN(profile_ptr, profile_len)
handle := C.GoStringN(handle_ptr, handle_len)
return (NumMessages(profile, handle))
//export c_AcceptContact
func c_AcceptContact(profilePtr *C.char, profileLen C.int, handlePtr *C.char, handleLen C.int) {
AcceptContact(C.GoStringN(profilePtr, profileLen), C.GoStringN(handlePtr, handleLen))
}
func NumMessages(profile, handle string) (n int) {
n = len(application.GetPeer(profile).GetContact(handle).Timeline.Messages)
log.Infof("NumMessagse(%s, %s) = %d", profile, handle, n)
return
// AcceptContact takes in a profileOnion and a handle to either a group or a peer and authorizes the handle
// for further action (e.g. messaging / connecting to the server / joining the group etc.)
func AcceptContact(profileOnion string, handle string) {
profile := application.GetPeer(profileOnion)
profileHandler := utils.NewPeerHelper(profile)
if profileHandler.IsGroup(handle) {
profile.AcceptInvite(handle)
} else {
err := profile.SetContactAuthorization(handle, model.AuthApproved)
if err == nil {
eventHandler.Push(event.NewEvent(event.PeerStateChange, map[event.Field]string{
ProfileOnion: profileOnion,
event.RemotePeer: handle,
"authorization": string(model.AuthApproved),
}))
} else {
log.Errorf("error accepting contact: %s", err.Error())
}
}
}
//export c_RejectInvite
func c_RejectInvite(profilePtr *C.char, profileLen C.int, handlePtr *C.char, handleLen C.int) {
RejectInvite(C.GoStringN(profilePtr, profileLen), C.GoStringN(handlePtr, handleLen))
}
// RejectInvite rejects a group invite
func RejectInvite(profileOnion string, handle string) {
log.Debugf("rejecting invite %v for %v", handle, profileOnion)
profile := application.GetPeer(profileOnion)
profileHandler := utils.NewPeerHelper(profile)
if profileHandler.IsGroup(handle) {
profile.RejectInvite(handle)
log.Debugf("successfully rejected invite %v for %v", handle, profileOnion)
}
}
//export c_BlockContact
func c_BlockContact(profilePtr *C.char, profileLen C.int, handlePtr *C.char, handleLen C.int) {
BlockContact(C.GoStringN(profilePtr, profileLen), C.GoStringN(handlePtr, handleLen))
}
func BlockContact(profile, handle string) {
err := application.GetPeer(profile).SetContactAuthorization(handle, model.AuthBlocked)
if err == nil {
eventHandler.Push(event.NewEvent(event.PeerStateChange, map[event.Field]string{
ProfileOnion: profile,
event.RemotePeer: handle,
"authorization": string(model.AuthBlocked),
}))
} else {
log.Errorf("error blocking contact: %s", err.Error())
}
}
//export c_UpdateMessageFlags
func c_UpdateMessageFlags(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, mIdx C.int, message_flags C.ulong) {
profile := C.GoStringN(profile_ptr, profile_len)
handle := C.GoStringN(handle_ptr, handle_len)
UpdateMessageFlags(profile, handle, int(mIdx), int64(message_flags))
}
// UpdateMessageFlags sets the messages flags on a given message for a given profile.
// gomobile doesn't support uint64...so here we are....
func UpdateMessageFlags(profileOnion, handle string, mIdx int, flags int64) {
profile := application.GetPeer(profileOnion)
if profile != nil {
profile.UpdateMessageFlags(handle, mIdx, uint64(flags))
} else {
log.Errorf("called updatemessageflags with invalid profile onion")
}
}
//export c_GetMessage
@ -316,25 +489,255 @@ func c_GetMessage(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, ha
return C.CString(GetMessage(profile, handle, int(message_index)))
}
// Deprecate - 2021.01.14 - not used
func GetMessage(profile, handle string, message_index int) string {
message := application.GetPeer(profile).GetContact(handle).Timeline.Messages[message_index]
// EnhancedMessage wraps a Cwtch model.Message with some additional data to reduce calls from the UI.
type EnhancedMessage struct {
model.Message
ContactImage string
}
func GetMessage(profileOnion, handle string, message_index int) string {
profile := application.GetPeer(profileOnion)
ph := utils.NewPeerHelper(profile)
var message EnhancedMessage
if ph.IsGroup(handle) {
if len(profile.GetGroup(handle).Timeline.Messages) > message_index {
message.Message = profile.GetGroup(handle).Timeline.Messages[message_index]
message.ContactImage = ph.GetProfilePic(message.Message.PeerID)
} else {
// Message Index Request exceeded Timeline, most likely reason is this is a request for an
// unacknowledged sent message (it can take a many seconds for a message to be confirmed in the worst
// case).
offset := message_index - len(profile.GetGroup(handle).Timeline.Messages)
if len(profile.GetGroup(handle).UnacknowledgedMessages) > offset {
message.Message = profile.GetGroup(handle).UnacknowledgedMessages[offset]
message.ContactImage = ph.GetProfilePic(message.Message.PeerID)
} else {
log.Errorf("Couldn't find message in timeline or unacked messages, probably transient threading issue, but logging for visibility..")
}
}
} else {
if message_index < len(profile.GetContact(handle).Timeline.Messages) {
message.Message = profile.GetContact(handle).Timeline.Messages[message_index]
message.ContactImage = ph.GetProfilePic(handle)
} else {
log.Errorf("peerpeercontact getmessage out of range; sending counter resync just in case")
eventHandler.Push(event.NewEvent(event.MessageCounterResync, map[event.Field]string{
event.Identity: profileOnion,
event.RemotePeer: handle,
event.Data: strconv.Itoa(len(profile.GetContact(handle).Timeline.Messages)),
}))
}
}
bytes, _ := json.Marshal(message)
log.Infof("GetMessage(%s, %s, %d) = %s", profile, handle, message_index, string(bytes))
return string(bytes)
}
//export c_GetMessages
func c_GetMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, start C.int, end C.int) *C.char {
//export c_SendMessage
func c_SendMessage(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, msg_ptr *C.char, msg_len C.int) {
profile := C.GoStringN(profile_ptr, profile_len)
handle := C.GoStringN(handle_ptr, handle_len)
return C.CString(GetMessages(profile, handle, int(start), int(end)))
msg := C.GoStringN(msg_ptr, msg_len)
SendMessage(profile, handle, msg)
}
func GetMessages(profile, handle string, start, end int) string {
messages := application.GetPeer(profile).GetContact(handle).Timeline.Messages[start:end]
bytes, _ := json.Marshal(messages)
return string(bytes)
func SendMessage(profileOnion, handle, msg string) {
profile := application.GetPeer(profileOnion)
ph := utils.NewPeerHelper(profile)
if ph.IsGroup(handle) {
groupHandler, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
groupHandler.SendMessage(profile, handle, msg)
}
} else {
contactHandler, _ := contact.FunctionalityGate(utils.ReadGlobalSettings().Experiments)
contactHandler.SendMessage(profile, handle, msg)
}
}
//export c_SendInvitation
func c_SendInvitation(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, target_ptr *C.char, target_len C.int) {
profile := C.GoStringN(profile_ptr, profile_len)
handle := C.GoStringN(handle_ptr, handle_len)
target := C.GoStringN(target_ptr, target_len)
SendInvitation(profile, handle, target)
}
// Send an invitation from `profileOnion` to contact `handle` (peer or group)
// asking them to add the contact `target` (also peer or group).
// For groups, the profile must already have `target` as a contact.
func SendInvitation(profileOnion, handle, target string) {
profile := application.GetPeer(profileOnion)
ph := utils.NewPeerHelper(profile)
var invite ChatMessage
if ph.IsGroup(target) {
bundle, _ := profile.GetContact(profile.GetGroup(target).GroupServer).GetAttribute(string(model.BundleType))
inviteStr, err := profile.GetGroup(target).Invite()
if err == nil {
invite = ChatMessage{O: 101, D: fmt.Sprintf("tofubundle:server:%s||%s", base64.StdEncoding.EncodeToString([]byte(bundle)), inviteStr)}
}
} else {
invite = ChatMessage{O: 100, D: target}
}
inviteBytes, err := json.Marshal(invite)
if err != nil {
log.Errorf("malformed invite: %v", err)
} else {
SendMessage(profileOnion, handle, string(inviteBytes))
}
}
//export c_ResetTor
func c_ResetTor() {
ResetTor()
}
func ResetTor() {
globalACN.Restart()
}
//export c_CreateGroup
func c_CreateGroup(profile_ptr *C.char, profile_len C.int, server_ptr *C.char, server_len C.int, name_ptr *C.char, name_len C.int) {
profile := C.GoStringN(profile_ptr, profile_len)
server := C.GoStringN(server_ptr, server_len)
name := C.GoStringN(name_ptr, name_len)
CreateGroup(profile, server, name)
}
// CreateGroup takes in a profile and server in addition to a name and creates a new group.
func CreateGroup(profile string, server string, name string) {
peer := application.GetPeer(profile)
_, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
gid, _, err := peer.StartGroup(server)
if err == nil {
log.Debugf("created group %v on %v: $v", profile, server, gid)
// set the group name
peer.SetGroupAttribute(gid, attr.GetLocalScope("name"), name)
} else {
log.Errorf("error creating group or %v on server %v: %v", profile, server, err)
}
}
}
//export c_DeleteProfile
func c_DeleteProfile(profile_ptr *C.char, profile_len C.int, password_ptr *C.char, password_len C.int) {
profile := C.GoStringN(profile_ptr, profile_len)
password := C.GoStringN(password_ptr, password_len)
DeleteProfile(profile, password)
}
// DeleteProfile deletes a profile given the right password
func DeleteProfile(profile string, password string) {
// allow a blank password to delete "unencrypted" accounts...
if password == "" {
password = constants.DefactoPasswordForUnencryptedProfiles
}
application.DeletePeer(profile, password)
}
//export c_LeaveConversation
func c_LeaveConversation(profile_ptr *C.char, profile_len C.int, contact_ptr *C.char, contact_len C.int) {
profile := C.GoStringN(profile_ptr, profile_len)
contact := C.GoStringN(contact_ptr, contact_len)
LeaveConversation(profile, contact)
}
// LeaveConversation forces profile to leave the peer
func LeaveConversation(profile string, contact string) {
peer := application.GetPeer(profile)
peer.DeleteContact(contact)
}
//export c_LeaveGroup
func c_LeaveGroup(profile_ptr *C.char, profile_len C.int, group_ptr *C.char, group_len C.int) {
profile := C.GoStringN(profile_ptr, profile_len)
groupID := C.GoStringN(group_ptr, group_len)
LeaveGroup(profile, groupID)
}
// LeaveGroup forces profile to leave the group groupID
func LeaveGroup(profile string, groupID string) {
peer := application.GetPeer(profile)
_, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
peer.DeleteGroup(groupID)
}
}
//export c_ImportBundle
func c_ImportBundle(profile_ptr *C.char, profile_len C.int, bundle_ptr *C.char, bundle_len C.int) {
profile := C.GoStringN(profile_ptr, profile_len)
name := C.GoStringN(bundle_ptr, bundle_len)
ImportBundle(profile, name)
}
// ImportBundle takes in a handle to a profile and an invite string which could have one of many
// different formats (e.g. a peer address, a group invite, a server key bundle, or a combination)
func ImportBundle(profileOnion string, bundle string) {
profile := application.GetPeer(profileOnion)
peerHandler, _ := contact.FunctionalityGate(utils.ReadGlobalSettings().Experiments)
response := peerHandler.HandleImportString(profile, bundle)
if strings.Contains(response.Error(), "invalid_import_string") {
groupHandler, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
response = groupHandler.HandleImportString(profile, bundle)
eventHandler.Push(event.NewEvent(event.AppError, map[event.Field]string{event.Data: response.Error()}))
// We might have added a new server, so refresh the server list...
serverListForOnion := groupHandler.GetServerInfoList(profile)
serversListBytes, _ := json.Marshal(serverListForOnion)
eventHandler.Push(event.NewEvent(groups.UpdateServerInfo, map[event.Field]string{"ProfileOnion": profileOnion, groups.ServerList: string(serversListBytes)}))
return
}
}
eventHandler.Push(event.NewEvent(event.AppError, map[event.Field]string{event.Data: response.Error()}))
}
//export c_SetGroupAttribute
func c_SetGroupAttribute(profile_ptr *C.char, profile_len C.int, group_ptr *C.char, group_len C.int, key_ptr *C.char, key_len C.int, val_ptr *C.char, val_len C.int) {
profileOnion := C.GoStringN(profile_ptr, profile_len)
groupHandle := C.GoStringN(group_ptr, group_len)
key := C.GoStringN(key_ptr, key_len)
value := C.GoStringN(val_ptr, val_len)
SetGroupAttribute(profileOnion, groupHandle, key, value)
}
// SetGroupAttribute provides a wrapper around profile.SetGroupAttribute, gated by global experiments...
func SetGroupAttribute(profileOnion string, groupHandle string, key string, value string) {
profile := application.GetPeer(profileOnion)
_, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
profile.SetGroupAttribute(groupHandle, key, value)
}
}
//export c_ShutdownCwtch
func c_ShutdownCwtch() {
ShutdownCwtch()
}
// ShutdownCwtch is a safe way to shutdown any active cwtch applications and associated ACNs
func ShutdownCwtch() {
if application != nil && globalACN != nil {
// Kill the isolate
eventHandler.Push(event.NewEvent(event.Shutdown, map[event.Field]string{}))
// Allow for the shutdown events to go through and then purge everything else...
log.Infof("Shutting Down Application...")
application.Shutdown()
log.Infof("Shutting Down ACN...")
globalACN.Close()
log.Infof("Library Shutdown Complete!")
// do not remove - important for state checks elsewhere
application = nil
globalACN = nil
eventHandler = nil
}
}
// Leave as is, needed by ffi

View File

@ -1,8 +1,15 @@
package utils
type Contact struct {
Name string `json:"name"`
Onion string `json:"onion"`
Status string `json:"status"`
Picture string `json:"picture"`
Name string `json:"name"`
Onion string `json:"onion"`
Status string `json:"status"`
Picture string `json:"picture"`
Authorization string `json:"authorization"`
SaveHistory string `json:"saveConversationHistory"`
Messages int `json:"numMessages"`
Unread int `json:"numUnread"`
LastMessage string `json:"lastMsgTime"`
IsGroup bool `json:"isGroup"`
GroupServer string `json:"groupServer"`
}

View File

@ -8,7 +8,9 @@ import (
"cwtch.im/cwtch/protocol/connections"
"encoding/json"
"git.openprivacy.ca/flutter/libcwtch-go/constants"
"git.openprivacy.ca/flutter/libcwtch-go/features/groups"
"git.openprivacy.ca/openprivacy/log"
"strconv"
)
import "cwtch.im/cwtch/event"
@ -21,20 +23,31 @@ type EventHandler struct {
app app.Application
appBusQueue event.Queue
profileEvents chan EventProfileEnvelope
profileQueues map[string]event.Queue
}
func NewEventHandler(application app.Application) *EventHandler {
appBusQueue := event.NewQueue()
application.GetPrimaryBus().Subscribe(event.NewPeer, appBusQueue)
application.GetPrimaryBus().Subscribe(event.PeerError, appBusQueue)
application.GetPrimaryBus().Subscribe(event.AppError, appBusQueue)
application.GetPrimaryBus().Subscribe(event.ACNStatus, appBusQueue)
application.GetPrimaryBus().Subscribe(event.ReloadDone, appBusQueue)
application.GetPrimaryBus().Subscribe(event.ACNVersion, appBusQueue)
application.GetPrimaryBus().Subscribe(UpdateGlobalSettings, appBusQueue)
return &EventHandler{app: application, appBusQueue: appBusQueue, profileQueues: make(map[string]event.Queue), profileEvents: make(chan EventProfileEnvelope)}
func NewEventHandler() *EventHandler {
eh := &EventHandler{app: nil, appBusQueue: event.NewQueue(), profileEvents: make(chan EventProfileEnvelope)}
return eh
}
// PublishAppEvent is a way for libCwtch-go to publish an event for consumption by a UI before a Cwtch app has been initialized
// Main use: to signal an error before a cwtch app could be created
func (eh *EventHandler) PublishAppEvent(event event.Event) {
eh.appBusQueue.Publish(event)
}
func (eh *EventHandler) HandleApp(application app.Application) {
eh.app = application
application.GetPrimaryBus().Subscribe(event.NewPeer, eh.appBusQueue)
application.GetPrimaryBus().Subscribe(event.PeerError, eh.appBusQueue)
application.GetPrimaryBus().Subscribe(event.PeerDeleted, eh.appBusQueue)
application.GetPrimaryBus().Subscribe(event.Shutdown, eh.appBusQueue)
application.GetPrimaryBus().Subscribe(event.AppError, eh.appBusQueue)
application.GetPrimaryBus().Subscribe(event.ACNStatus, eh.appBusQueue)
application.GetPrimaryBus().Subscribe(event.ReloadDone, eh.appBusQueue)
application.GetPrimaryBus().Subscribe(event.ACNVersion, eh.appBusQueue)
application.GetPrimaryBus().Subscribe(UpdateGlobalSettings, eh.appBusQueue)
application.GetPrimaryBus().Subscribe(CwtchStarted, eh.appBusQueue)
}
func (eh *EventHandler) GetNextEvent() string {
@ -50,68 +63,164 @@ func (eh *EventHandler) GetNextEvent() string {
// handleAppBusEvent enriches AppBus events so they are usable with out further data fetches
func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
switch e.EventType {
case event.NewPeer:
onion := e.Data[event.Identity]
profile := eh.app.GetPeer(e.Data[event.Identity])
log.Debugf("New AppBus Event to Handle: %v", e)
if eh.app != nil {
switch e.EventType {
case event.ACNStatus:
if e.Data[event.Progress] == "100" {
for onion := range eh.app.ListPeers() {
// launch a listen thread (internally this does a check that the protocol engine is not listening)
// and as such is safe to call.
eh.app.GetPeer(onion).Listen()
}
}
case event.NewPeer:
onion := e.Data[event.Identity]
profile := eh.app.GetPeer(e.Data[event.Identity])
log.Debug("New Peer Event: %v", e)
eh.startHandlingPeer(onion)
if e.Data[event.Created] == event.True {
profile.SetAttribute(attr.GetPublicScope(constants.Name), profile.GetName())
profile.SetAttribute(attr.GetPublicScope(constants.Picture), ImageToString(NewImage(RandomProfileImage(onion), TypeImageDistro)))
}
if e.Data[event.Status] != event.StorageRunning || e.Data[event.Created] == event.True {
profile.SetAttribute(attr.GetLocalScope(constants.PeerOnline), event.False)
eh.app.AddPeerPlugin(onion, plugins.CONNECTIONRETRY)
eh.app.AddPeerPlugin(onion, plugins.NETWORKCHECK)
}
nick, exists := profile.GetAttribute(attr.GetPublicScope(constants.Name))
if !exists {
nick = onion
}
picVal, ok := profile.GetAttribute(attr.GetPublicScope(constants.Picture))
if !ok {
picVal = ImageToString(NewImage(RandomProfileImage(onion), TypeImageDistro))
}
pic, err := StringToImage(picVal)
if err != nil {
pic = NewImage(RandomProfileImage(onion), TypeImageDistro)
}
picPath := GetPicturePath(pic)
//tag, _ := profile.GetAttribute(app.AttributeTag)
online, _ := profile.GetAttribute(attr.GetLocalScope(constants.PeerOnline))
e.Data[constants.Name] = nick
e.Data[constants.Picture] = picPath
e.Data["Online"] = online
var contacts []Contact
for _, contact := range profile.GetContacts() {
if profile.GetContact(contact).IsServer() {
continue
if e.Data["Reload"] != event.True {
eh.startHandlingPeer(onion)
}
cpicVal, ok := profile.GetContactAttribute(contact, attr.GetPeerScope(constants.Picture))
tag,isTagged := profile.GetAttribute(app.AttributeTag)
if isTagged {
e.Data[app.AttributeTag] = tag
} else {
// Assume encrypted for non-tagged profiles - this isn't always true, but all post-beta profiles
// are tagged on creation.
e.Data[app.AttributeTag] = constants.ProfileTypeV1Password
}
if e.Data[event.Created] == event.True {
name, _ := profile.GetAttribute(attr.GetLocalScope(constants.Name))
profile.SetAttribute(attr.GetPublicScope(constants.Name), name)
profile.SetAttribute(attr.GetPublicScope(constants.Picture), ImageToString(NewImage(RandomProfileImage(onion), TypeImageDistro)))
}
if e.Data[event.Status] != event.StorageRunning || e.Data[event.Created] == event.True {
profile.SetAttribute(attr.GetLocalScope(constants.PeerOnline), event.False)
eh.app.AddPeerPlugin(onion, plugins.CONNECTIONRETRY)
eh.app.AddPeerPlugin(onion, plugins.NETWORKCHECK)
// If the user has chosen to block unknown profiles
// then explicitly configure the protocol engine to do so..
if ReadGlobalSettings().BlockUnknownConnections {
profile.BlockUnknownConnections()
} else {
// For completeness
profile.AllowUnknownConnections()
}
// Start up the Profile
profile.Listen()
profile.StartPeersConnections()
if _, err := groups.ExperimentGate(ReadGlobalSettings().Experiments); err == nil {
profile.StartServerConnections()
}
}
nick, exists := profile.GetAttribute(attr.GetPublicScope(constants.Name))
if !exists {
nick = onion
}
picVal, ok := profile.GetAttribute(attr.GetPublicScope(constants.Picture))
if !ok {
cpicVal = ImageToString(NewImage(RandomProfileImage(contact), TypeImageDistro))
picVal = ImageToString(NewImage(RandomProfileImage(onion), TypeImageDistro))
}
cpic, err := StringToImage(cpicVal)
pic, err := StringToImage(picVal)
if err != nil {
cpic = NewImage(RandomProfileImage(contact), TypeImageDistro)
pic = NewImage(RandomProfileImage(onion), TypeImageDistro)
}
cpicPath := GetPicturePath(cpic)
contactInfo := profile.GetContact(contact)
contacts = append(contacts, Contact{Name: contactInfo.Name, Onion: contactInfo.Onion, Status: contactInfo.State, Picture: cpicPath,})
}
picPath := GetPicturePath(pic)
bytes, _ := json.Marshal(contacts)
e.Data["ContactsJson"] = string(bytes)
log.Infof("contactsJson %v", e.Data["ContactsJson"])
//tag, _ := profile.GetAttribute(app.AttributeTag)
online, _ := profile.GetAttribute(attr.GetLocalScope(constants.PeerOnline))
e.Data[constants.Name] = nick
e.Data[constants.Picture] = picPath
e.Data["Online"] = online
var contacts []Contact
var servers []groups.Server
for _, contact := range profile.GetContacts() {
// Only compile the server info if we have enabled the experiment...
// Note that this means that this info can become stale if when first loaded the experiment
// has been disabled and then is later re-enabled. As such we need to ensure that this list is
// re-fetched when the group experiment is enabled via a dedicated ListServerInfo event...
if profile.GetContact(contact).IsServer() {
groupHandler, err := groups.ExperimentGate(ReadGlobalSettings().Experiments)
if err == nil {
servers = append(servers, groupHandler.GetServerInfo(contact, profile))
}
continue
}
contactInfo := profile.GetContact(contact)
ph := NewPeerHelper(profile)
name := ph.GetNick(contact)
cpicPath := ph.GetProfilePic(contact)
saveHistory, set := contactInfo.GetAttribute(event.SaveHistoryKey)
if !set {
saveHistory = event.DeleteHistoryDefault
}
contacts = append(contacts, Contact{
Name: name,
Onion: contactInfo.Onion,
Status: contactInfo.State,
Picture: cpicPath,
Authorization: string(contactInfo.Authorization),
SaveHistory: saveHistory,
Messages: contactInfo.Timeline.Len(),
Unread: 0,
LastMessage: strconv.Itoa(getLastMessageTime(&contactInfo.Timeline)),
IsGroup: false,
})
}
// We compile and send the groups regardless of the experiment flag, and hide them in the UI
for _, groupId := range profile.GetGroups() {
group := profile.GetGroup(groupId)
// Check that the group is cryptographically valid
if !group.CheckGroup() {
continue
}
ph := NewPeerHelper(profile)
cpicPath := ph.GetProfilePic(groupId)
authorization := model.AuthUnknown
if group.Accepted {
authorization = model.AuthApproved
}
contacts = append(contacts, Contact{
Name: ph.GetNick(groupId),
Onion: group.GroupID,
Status: group.State,
Picture: cpicPath,
Authorization: string(authorization),
SaveHistory: event.SaveHistoryConfirmed,
Messages: group.Timeline.Len(),
Unread: 0,
LastMessage: strconv.Itoa(getLastMessageTime(&group.Timeline)),
IsGroup: true,
GroupServer: group.GroupServer,
})
}
bytes, _ := json.Marshal(contacts)
e.Data["ContactsJson"] = string(bytes)
// Marshal the server list into the new peer event...
serversListBytes, _ := json.Marshal(servers)
e.Data[groups.ServerList] = string(serversListBytes)
log.Debugf("contactsJson %v", e.Data["ContactsJson"])
}
}
json, _ := json.Marshal(e)
@ -120,143 +229,123 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
// handleProfileEvent enriches Profile events so they are usable with out further data fetches
func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string {
if eh.app == nil {
log.Errorf("eh.app == nil in handleProfileEvent... this shouldnt happen?")
} else {
peer := eh.app.GetPeer(ev.Profile)
ph := NewPeerHelper(peer)
log.Debugf("New Profile Event to Handle: %v", ev)
switch ev.Event.EventType {
peer := eh.app.GetPeer(ev.Profile)
ph := NewPeerHelper(peer)
/*
TODO: still handle this somewhere - network info from plugin Network check
case event.NetworkStatus:
online, _ := peer.GetAttribute(attr.GetLocalScope(constants.PeerOnline))
if e.Data[event.Status] == plugins.NetworkCheckSuccess && online == event.False {
peer.SetAttribute(attr.GetLocalScope(constants.PeerOnline), event.True)
uiManager.UpdateNetworkStatus(true)
// TODO we may have to reinitialize the peer
} else if e.Data[event.Status] == plugins.NetworkCheckError && online == event.True {
peer.SetAttribute(attr.GetLocalScope(constants.PeerOnline), event.False)
uiManager.UpdateNetworkStatus(false)
}*/
switch ev.Event.EventType {
/*case event.NetworkStatus:
online, _ := peer.GetAttribute(attr.GetLocalScope(constants.PeerOnline))
if e.Data[event.Status] == plugins.NetworkCheckSuccess && online == event.False {
peer.SetAttribute(attr.GetLocalScope(constants.PeerOnline), event.True)
uiManager.UpdateNetworkStatus(true)
// TODO we may have to reinitialize the peer
} else if e.Data[event.Status] == plugins.NetworkCheckError && online == event.True {
peer.SetAttribute(attr.GetLocalScope(constants.PeerOnline), event.False)
uiManager.UpdateNetworkStatus(false)
}*/
case event.NewMessageFromPeer: //event.TimestampReceived, event.RemotePeer, event.Data
// I Don't think we need to enrich
// TODO: deprecate and move to dart
/*func (p *PeerHelper) updateLastReadTime(id string) {
lastRead, _ := time.Now().MarshalText()
if p.IsGroup(id) {
p.peer.SetGroupAttribute(id, attr.GetLocalScope(constants.LastRead), string(lastRead))
} else {
p.peer.SetContactAttribute(id, attr.GetLocalScope(constants.LastRead), string(lastRead))
}
}*/
// legacy
//ts, _ := time.Parse(time.RFC3339Nano, e.Data[event.TimestampReceived])
case event.PeerAcknowledgement:
// No enrichement required
//Acknowledge(ev.Event.Data[event.RemotePeer], ev.Event.Data[event.EventID])
/*
case event.NewMessageFromGroup: //event.TimestampReceived, event.TimestampSent, event.Data, event.GroupID, event.RemotePeer
ts, _ := time.Parse(time.RFC3339Nano, e.Data[event.TimestampSent])
uiManager.AddMessage(e.Data[event.GroupID], e.Data[event.RemotePeer], e.Data[event.Data], e.Data[event.RemotePeer] == peer.GetOnion(), hex.EncodeToString([]byte(e.Data[event.Signature])), ts, true)
case event.NewGroupInvite:
gid, err := peer.ProcessInvite(e.Data[event.GroupInvite], e.Data[event.RemotePeer])
group := peer.GetGroup(gid)
if err == nil && group != nil {
uiManager.AddContact(gid)
}
*/
case event.PeerCreated:
handle := ev.Event.Data[event.RemotePeer]
err := EnrichNewPeer(handle, ph, ev)
if err != nil {
return ""
}
/*
case event.SendMessageToGroupError:
uiManager.AddSendMessageError(e.Data[event.GroupServer], e.Data[event.Signature], e.Data[event.Error])
case event.SendMessageToPeerError:
uiManager.AddSendMessageError(e.Data[event.RemotePeer], e.Data[event.EventID], e.Data[event.Error])
*/
case event.PeerStateChange:
cxnState := connections.ConnectionStateToType[ev.Event.Data[event.ConnectionState]]
contact := peer.GetContact(ev.Event.Data[event.RemotePeer])
if cxnState == connections.AUTHENTICATED && contact == nil {
// Contact does not exist, change event to NewPeer
peer.AddContact(ev.Event.Data[event.RemotePeer], ev.Event.Data[event.RemotePeer], model.AuthUnknown)
contact = peer.GetContact(ev.Event.Data[event.RemotePeer])
ev.Event.EventType = event.PeerCreated
err := EnrichNewPeer(ev.Event.Data[event.RemotePeer], ph, ev)
case event.NewMessageFromPeer: //event.TimestampReceived, event.RemotePeer, event.Data
// only needs contact nickname and picture, for displaying on popup notifications
ev.Event.Data["Nick"] = ph.GetNick(ev.Event.Data["RemotePeer"])
ev.Event.Data["Picture"] = ph.GetProfilePic(ev.Event.Data["RemotePeer"])
case event.NewMessageFromGroup:
// only needs contact nickname and picture, for displaying on popup notifications
ev.Event.Data["Nick"] = ph.GetNick(ev.Event.Data[event.GroupID])
ev.Event.Data["Picture"] = ph.GetProfilePic(ev.Event.Data[event.GroupID])
case event.PeerAcknowledgement:
// No enrichement required
case event.PeerCreated:
handle := ev.Event.Data[event.RemotePeer]
err := EnrichNewPeer(handle, ph, ev)
if err != nil {
return ""
}
}
case event.GroupCreated:
// This event should only happen after we have validated the invite, as such the error
// condition *should* never happen.
if contact != nil {
// No enrichment needed
//uiManager.UpdateContactStatus(contact.Onion, int(cxnState), false)
if cxnState == connections.AUTHENTICATED {
// if known and authed, get vars
peer.SendGetValToPeer(ev.Event.Data[event.RemotePeer], attr.PublicScope, constants.Name)
peer.SendGetValToPeer(ev.Event.Data[event.RemotePeer], attr.PublicScope, constants.Picture)
groupPic := ph.GetProfilePic(ev.Event.Data[event.GroupID])
ev.Event.Data["PicturePath"] = groupPic
ev.Event.Data["GroupName"] = ph.GetNick(ev.Event.Data[event.GroupID])
case event.NewGroup:
// This event should only happen after we have validated the invite, as such the error
// condition *should* never happen.
serializedInvite := ev.Event.Data[event.GroupInvite]
if invite, err := model.ValidateInvite(serializedInvite); err == nil {
groupPic := ph.GetProfilePic(invite.GroupID)
ev.Event.Data["PicturePath"] = groupPic
} else {
log.Errorf("received a new group event which contained an invalid invite %v. this should never happen and likely means there is a bug in cwtch. Please file a ticket @ https://git.openprivcy.ca/cwtch.im/cwtch", err)
return ""
}
}
case event.PeerStateChange:
cxnState := connections.ConnectionStateToType()[ev.Event.Data[event.ConnectionState]]
contact := peer.GetContact(ev.Event.Data[event.RemotePeer])
/*case event.NewRetValMessageFromPeer:
onion := e.Data[event.RemotePeer]
scope := e.Data[event.Scope]
path := e.Data[event.Path]
val := e.Data[event.Data]
exists, _ := strconv.ParseBool(e.Data[event.Exists])
if cxnState == connections.AUTHENTICATED && contact == nil {
peer.AddContact(ev.Event.Data[event.RemotePeer], ev.Event.Data[event.RemotePeer], model.AuthUnknown)
return ""
}
if contact != nil {
// No enrichment needed
//uiManager.UpdateContactStatus(contact.Onion, int(cxnState), false)
if cxnState == connections.AUTHENTICATED {
// if known and authed, get vars
peer.SendGetValToPeer(ev.Event.Data[event.RemotePeer], attr.PublicScope, constants.Name)
peer.SendGetValToPeer(ev.Event.Data[event.RemotePeer], attr.PublicScope, constants.Picture)
}
}
case event.NewRetValMessageFromPeer:
// auto handled event means the setting is already done, we're just deciding if we need to tell the UI
onion := ev.Event.Data[event.RemotePeer]
scope := ev.Event.Data[event.Scope]
path := ev.Event.Data[event.Path]
//val := ev.Event.Data[event.Data]
exists, _ := strconv.ParseBool(ev.Event.Data[event.Exists])
if exists && scope == attr.PublicScope {
switch path {
case constants.Name:
peer.SetContactAttribute(onion, attr.GetPeerScope(constants.Name), val)
uiManager.UpdateContactDisplayName(onion)
case constants.Picture:
peer.SetContactAttribute(onion, attr.GetPeerScope(constants.Picture), val)
uiManager.UpdateContactPicture(onion)
if _, exists := peer.GetContactAttribute(onion, attr.GetLocalScope(path)); exists {
// we have a locally set ovverride, don't pass this remote set public scope update to UI
return ""
}
}
case event.ServerStateChange:
serverOnion := e.Data[event.GroupServer]
state := connections.ConnectionStateToType[e.Data[event.ConnectionState]]
groups := peer.GetGroups()
for _, groupID := range groups {
group := peer.GetGroup(groupID)
if group != nil && group.GroupServer == serverOnion {
group.State = e.Data[event.ConnectionState]
loading := false
if state == connections.AUTHENTICATED {
loading = true
}
uiManager.UpdateContactStatus(groupID, int(state), loading)
uiManager.UpdateContactStatus(serverOnion, int(state), loading)
}
}
case event.DeletePeer:
log.Infof("PeerHandler got DeletePeer, SHUTTING down!\n")
uiManager.ReloadProfiles()
return
*/
}
}
json, _ := json.Marshal(ev)
json, _ := json.Marshal(unwrap(ev))
return string(json)
}
func unwrap(original *EventProfileEnvelope) *event.Event {
unwrapped := &original.Event
unwrapped.Data["ProfileOnion"] = original.Profile
return unwrapped
}
func (eh *EventHandler) startHandlingPeer(onion string) {
eventBus := eh.app.GetEventBus(onion)
q := event.NewQueue()
eventBus.Subscribe(event.NewMessageFromPeer, q)
eventBus.Subscribe(event.PeerAcknowledgement, q)
eventBus.Subscribe(event.DeleteContact, q)
eventBus.Subscribe(event.AppError, q)
eventBus.Subscribe(event.IndexedAcknowledgement, q)
eventBus.Subscribe(event.IndexedFailure, q)
eventBus.Subscribe(event.NewMessageFromGroup, q)
eventBus.Subscribe(event.NewGroupInvite, q)
eventBus.Subscribe(event.GroupCreated, q)
eventBus.Subscribe(event.NewGroup, q)
eventBus.Subscribe(event.AcceptGroupInvite, q)
eventBus.Subscribe(event.SetGroupAttribute, q)
eventBus.Subscribe(event.DeleteGroup, q)
eventBus.Subscribe(event.SendMessageToGroupError, q)
eventBus.Subscribe(event.SendMessageToPeerError, q)
eventBus.Subscribe(event.ServerStateChange, q)
@ -266,18 +355,25 @@ func (eh *EventHandler) startHandlingPeer(onion string) {
eventBus.Subscribe(event.ChangePasswordSuccess, q)
eventBus.Subscribe(event.ChangePasswordError, q)
eventBus.Subscribe(event.NewRetValMessageFromPeer, q)
eh.profileQueues[onion] = q
eventBus.Subscribe(event.SetAttribute, q)
go eh.forwardProfileMessages(onion, q)
}
func (eh *EventHandler) forwardProfileMessages(onion string, q event.Queue) {
log.Infof("Launching Forwarding Goroutine for %v", onion)
// TODO: graceful shutdown, via an injected event of special QUIT type exiting loop/go routine
for {
e := q.Next()
ev := EventProfileEnvelope{Event: *e, Profile: onion}
ev := EventProfileEnvelope{Event: e, Profile: onion}
eh.profileEvents <- ev
if ev.Event.EventType == event.Shutdown {
return
}
}
}
func (eh *EventHandler) Push(newEvent event.Event) {
eh.appBusQueue.Publish(newEvent)
}

18
utils/logging.go Normal file
View File

@ -0,0 +1,18 @@
package utils
import "cwtch.im/cwtch/event"
// An event to set the logging level dynamically from the UI
const (
SetLoggingLevel = event.Type("SetLoggingLevel")
)
// Logging Levels as Event Fields. Note: Unlike most event we don't cae about
// the *value* of the field, only the presence. If more than one of these fields is
// present in a single SetLoggingLevel event then the highest logging level is used. INFO < WARN < ERROR < DEBUG
const (
Warn = event.Field("Warn")
Error = event.Field("Error")
Debug = event.Field("Debug")
Info = event.Field("Info")
)

View File

@ -87,6 +87,8 @@ func (p *PeerHelper) GetNick(id string) string {
nick, exists = p.peer.GetContactAttribute(id, attr.GetPeerScope(constants.Name))
if !exists {
nick = "[" + id + "]"
// re-request
p.peer.SendGetValToPeer(id, attr.PublicScope, constants.Name)
}
}
return nick
@ -245,6 +247,7 @@ func NewManager(profile string, gcd *GrandCentralDispatcher) Manager {
// uiManager.AddContact(onion)
// (handle string, displayName string, image string, badge int, status int, authorization string, loading bool, lastMsgTime int)
func EnrichNewPeer(handle string, ph *PeerHelper, ev *EventProfileEnvelope) error {
log.Infof("Enriching New Peer %v", handle)
if ph.IsGroup(handle) {
group := ph.peer.GetGroup(handle)
if group != nil {
@ -253,7 +256,7 @@ func EnrichNewPeer(handle string, ph *PeerHelper, ev *EventProfileEnvelope) erro
ev.Event.Data["picture"] = ph.GetProfilePic(handle)
ev.Event.Data["nick"] = ph.GetNick(handle)
ev.Event.Data["status"] = strconv.Itoa(int(connections.ConnectionStateToType[group.State]))
ev.Event.Data["status"] = strconv.Itoa(int(connections.ConnectionStateToType()[group.State]))
ev.Event.Data["authorization"] = string(model.AuthApproved)
ev.Event.Data["loading"] = "false"
ev.Event.Data["lastMsgTime"] = strconv.Itoa(getLastMessageTime(&group.Timeline))
@ -263,19 +266,28 @@ func EnrichNewPeer(handle string, ph *PeerHelper, ev *EventProfileEnvelope) erro
if contact != nil {
lastRead := ph.InitLastReadTime(contact.Onion)
ev.Event.Data["unread"] = strconv.Itoa(ph.CountUnread(contact.Timeline.GetMessages(), lastRead))
ev.Event.Data["numMessages"] = strconv.Itoa(contact.Timeline.Len())
ev.Event.Data["picture"] = ph.GetProfilePic(handle)
ev.Event.Data["nick"] = ph.GetNick(handle)
ev.Event.Data["status"] = strconv.Itoa(int(connections.ConnectionStateToType[contact.State]))
// TODO Replace this if with a better flow that separates New Contacts and Peering Updates
if contact.State == "" {
// Will be disconnected to start
ev.Event.Data["status"] = connections.ConnectionStateName[connections.DISCONNECTED]
} else {
ev.Event.Data["status"] = contact.State
}
ev.Event.Data["authorization"] = string(contact.Authorization)
ev.Event.Data["loading"] = "false"
ev.Event.Data["lastMsgTime"] = strconv.Itoa(getLastMessageTime(&contact.Timeline))
} else {
log.Errorf("Failed to find contact: %v", handle)
}
} else {
// could be a server?
log.Errorf("sorry, unable to handle AddContact(%v)", handle)
return errors.New("Not a peer or group")
log.Debugf("sorry, unable to handle AddContact(%v)", handle)
return errors.New("not a peer or group")
}
return nil
}

View File

@ -12,6 +12,8 @@ import (
)
const (
CwtchStarted = event.Type("CwtchStarted")
CwtchStartError = event.Type("CwtchStartError")
UpdateGlobalSettings = event.Type("UpdateGlobalSettings")
)
@ -21,37 +23,46 @@ const GlobalSettingsFilename = "ui.globals"
const saltFile = "SALT"
type GlobalSettings struct {
Locale string
Theme string
PreviousPid int64
ExperimentsEnabled bool
Experiments map[string]bool
StateRootPane int
FirstTime bool
Locale string
Theme string
PreviousPid int64
ExperimentsEnabled bool
Experiments map[string]bool
BlockUnknownConnections bool
StateRootPane int
FirstTime bool
UIColumnModePortrait string
UIColumnModeLandscape string
}
var DefaultGlobalSettings = GlobalSettings{
Locale: "en",
Theme: "light",
PreviousPid: -1,
ExperimentsEnabled: false,
Experiments: make(map[string]bool),
StateRootPane: 0,
FirstTime: true,
Locale: "en",
Theme: "dark",
PreviousPid: -1,
ExperimentsEnabled: false,
Experiments: make(map[string]bool),
StateRootPane: 0,
FirstTime: true,
BlockUnknownConnections: false,
UIColumnModePortrait: "DualpaneMode.Single",
UIColumnModeLandscape: "DualpaneMode.CopyPortrait",
}
func InitGlobalSettingsFile(directory string, password string) error {
var key [32]byte
salt, err := ioutil.ReadFile(path.Join(directory, saltFile))
if err != nil {
log.Infof("Could not find salt file: %v (creating a new settings file)", err)
var newSalt [128]byte
key, newSalt, err = v1.CreateKeySalt(password)
if err != nil {
log.Errorf("Could not initialize salt: %v", err)
return err
}
os.Mkdir(directory, 0700)
err := ioutil.WriteFile(path.Join(directory, saltFile), newSalt[:], 0600)
if err != nil {
log.Errorf("Could not write salt file: %v", err)
return err
}
} else {
@ -59,21 +70,28 @@ func InitGlobalSettingsFile(directory string, password string) error {
}
GlobalSettingsFile = v1.NewFileStore(directory, GlobalSettingsFilename, key)
log.Infof("initialized global settings file: %v", GlobalSettingsFile)
return nil
}
func ReadGlobalSettings() *GlobalSettings {
settings := DefaultGlobalSettings
if GlobalSettingsFile == nil {
log.Errorf("Global Settings File was not Initialized Properly")
return &settings
}
settingsBytes, err := GlobalSettingsFile.Read()
if err != nil {
log.Errorf("Could not read global ui settings: %v\n", err)
log.Infof("Could not read global ui settings: %v (assuming this is a first time app deployment...)", err)
return &settings //firstTime = true
}
err = json.Unmarshal(settingsBytes, &settings)
if err != nil {
log.Errorf("Could not parse global ui settings: %v\n", err)
// TODO if settings is corrupted, we probably want to alert the UI.
return &settings //firstTime = true
}