From 91c182b880de3321314d05c517fc10a477774f06 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Thu, 29 Oct 2020 16:12:04 -0700 Subject: [PATCH 1/2] Gate Group Functionality --- go.mod | 4 +- go.sum | 4 + go/features/groups/group_functionality.go | 77 +++++++++++++++++++ .../groups/group_functionality_test.go | 40 ++++++++++ go/{ => features}/servers/server_manager.go | 8 +- go/handlers/appHandler.go | 4 + go/os/kill_unix.go | 3 +- go/ui/gcd.go | 76 +++++++++--------- main.go | 2 +- 9 files changed, 169 insertions(+), 49 deletions(-) create mode 100644 go/features/groups/group_functionality.go create mode 100644 go/features/groups/group_functionality_test.go rename go/{ => features}/servers/server_manager.go (81%) diff --git a/go.mod b/go.mod index dee60d83..7d571761 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module cwtch.im/ui go 1.12 require ( - cwtch.im/cwtch v0.4.3 + cwtch.im/cwtch v0.4.4 git.openprivacy.ca/openprivacy/connectivity v1.3.1 git.openprivacy.ca/openprivacy/log v1.0.1 github.com/c-bata/go-prompt v0.2.3 // indirect @@ -20,4 +20,4 @@ require ( golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect golang.org/x/net v0.0.0-20201022231255-08b38378de70 // indirect golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd // indirect -) +) \ No newline at end of file diff --git a/go.sum b/go.sum index 0df9051c..fe42a757 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,12 @@ cwtch.im/cwtch v0.4.2-0.20201016053957-1933fb703fb0 h1:8d2hJyb6qupb9wS6px3734Hy1 cwtch.im/cwtch v0.4.2-0.20201016053957-1933fb703fb0/go.mod h1:EvZQDbvXNu38m785dWF0MMljqJzwWrNTFT40HvoEAhI= cwtch.im/cwtch v0.4.3 h1:xf/jMW4+UJckzbYm5g9rPJKTP7fr6O6JC5pH1xjTs/A= cwtch.im/cwtch v0.4.3/go.mod h1:10gBkMSqAH95Pz4jTx5mpIHE+dkn+4kRC4BFTxWuQK8= +cwtch.im/cwtch v0.4.4 h1:LC9SngDx+iLXFiDK8GUkSxaWqloXQcPijvMPmwt9h80= +cwtch.im/cwtch v0.4.4/go.mod h1:10gBkMSqAH95Pz4jTx5mpIHE+dkn+4kRC4BFTxWuQK8= cwtch.im/tapir v0.2.0 h1:7MkoR5+uEuPW34/O0GZRidnIjq/01Cfm8nl5IRuqpGc= cwtch.im/tapir v0.2.0/go.mod h1:xzzZ28adyUXNkYL1YodcHsAiTt3IJ8Loc29YVn9mIEQ= +cwtch.im/tapir v0.2.1 h1:t1YJB9q5sV1A9xwiiwL6WVfw3dwQWLoecunuzT1PQtw= +cwtch.im/tapir v0.2.1/go.mod h1:xzzZ28adyUXNkYL1YodcHsAiTt3IJ8Loc29YVn9mIEQ= git.openprivacy.ca/openprivacy/bine v0.0.3 h1:PSHUmNqaW7BZUX8n2eTDeNbjsuRe+t5Ae0Og+P+jDM0= git.openprivacy.ca/openprivacy/bine v0.0.3/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU= git.openprivacy.ca/openprivacy/connectivity v1.2.0/go.mod h1:B7vzuVmChJtSKoh0ezph5vu6DQ0gIk0zHUNG6IgXCcA= diff --git a/go/features/groups/group_functionality.go b/go/features/groups/group_functionality.go new file mode 100644 index 00000000..c74a1190 --- /dev/null +++ b/go/features/groups/group_functionality.go @@ -0,0 +1,77 @@ +package groups + +import ( + "cwtch.im/ui/go/the" + "encoding/base64" + "errors" + "fmt" + "git.openprivacy.ca/openprivacy/log" + "strings" +) + +const ServerPrefix = "server:" +const TofuBundlePrefix = "tofubundle:" +const GroupPrefix = "torv3" +const GroupExperiment = "tapir-groups-experiment" + + +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) +} + +func (gf *GroupFunctionality) SendMessage(handle string, message string) error { + // TODO this auto accepting behaviour needs some thinking through + if !the.Peer.GetGroup(handle).Accepted { + err := the.Peer.AcceptInvite(handle) + if err != nil { + log.Errorf("tried to mark a nonexistent group as existed. bad!") + return err + } + } + + _, err := the.Peer.SendMessageToGroupTracked(handle, message) + + return err +} + +// 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) +} + +// HandleImportString handles import strings for groups and servers +func (gf * GroupFunctionality) HandleImportString(importString string) error { + + if strings.HasPrefix(importString, TofuBundlePrefix) { + bundle := strings.Split(importString,"||") + gf.HandleImportString(bundle[0][11:]) + gf.HandleImportString(bundle[1]) + return nil + } + + // Server Key Bundles are prefixed with + if strings.HasPrefix(importString, ServerPrefix) { + bundle, err := base64.StdEncoding.DecodeString(importString[7:]) + if err == nil { + return the.Peer.AddServer(string(bundle)) + } + return err + } + + //eg: torv3JFDWkXExBsZLkjvfkkuAxHsiLGZBk0bvoeJID9ItYnU=EsEBCiBhOWJhZDU1OTQ0NWI3YmM2N2YxYTM5YjkzMTNmNTczNRIgpHeNaG+6jy750eDhwLO39UX4f2xs0irK/M3P6mDSYQIaOTJjM2ttb29ibnlnaGoyenc2cHd2N2Q1N3l6bGQ3NTNhdW8zdWdhdWV6enB2ZmFrM2FoYzRiZHlkCiJAdVSSVgsksceIfHe41OJu9ZFHO8Kwv3G6F5OK3Hw4qZ6hn6SiZjtmJlJezoBH0voZlCahOU7jCOg+dsENndZxAA== + if strings.HasPrefix(importString, GroupPrefix){ + return the.Peer.ImportGroup(importString) + } + + + return errors.New("invalid_group_invite_prefix") +} \ No newline at end of file diff --git a/go/features/groups/group_functionality_test.go b/go/features/groups/group_functionality_test.go new file mode 100644 index 00000000..86b08693 --- /dev/null +++ b/go/features/groups/group_functionality_test.go @@ -0,0 +1,40 @@ +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") + } +} \ No newline at end of file diff --git a/go/servers/server_manager.go b/go/features/servers/server_manager.go similarity index 81% rename from go/servers/server_manager.go rename to go/features/servers/server_manager.go index 27ada919..a2c94563 100644 --- a/go/servers/server_manager.go +++ b/go/features/servers/server_manager.go @@ -22,7 +22,7 @@ func LaunchServiceManager(gcd *ui.GrandCentralDispatcher) { } -func (sm * ServerManager) Init(gcd *ui.GrandCentralDispatcher) { +func (sm *ServerManager) Init(gcd *ui.GrandCentralDispatcher) { sm.servers = make(map[string]server.Server) q := event.NewQueue() @@ -41,17 +41,17 @@ func (sm * ServerManager) Init(gcd *ui.GrandCentralDispatcher) { // TODO Replace with details from actual hosted servers. Right now these values are used to sketch / test out the // UI QML -func (sm * ServerManager) ListServers(gcd *ui.GrandCentralDispatcher) { +func (sm *ServerManager) ListServers(gcd *ui.GrandCentralDispatcher) { log.Debugf("Listing Servers...") gcd.AddServer("Server 1","Server 1","server",0) gcd.AddServer("Server 2","Server 2","server",4) gcd.AddServer("Server 3","Server 3","server",4) } -func (sm * ServerManager) StartServer(handle string) { +func (sm *ServerManager) StartServer(handle string) { // TODO Start the server with the given handle config } -func (sm * ServerManager) StopServer(handle string) { +func (sm *ServerManager) StopServer(handle string) { // TODO Stop the given server } \ No newline at end of file diff --git a/go/handlers/appHandler.go b/go/handlers/appHandler.go index 43414c91..c999e888 100644 --- a/go/handlers/appHandler.go +++ b/go/handlers/appHandler.go @@ -5,6 +5,7 @@ import ( "cwtch.im/cwtch/app/plugins" "cwtch.im/cwtch/event" "cwtch.im/ui/go/constants" + "cwtch.im/ui/go/features/groups" "cwtch.im/ui/go/the" "cwtch.im/ui/go/ui" "git.openprivacy.ca/openprivacy/log" @@ -87,6 +88,9 @@ func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool, reloadingAccounts if e.Data[event.Status] != "running" { p.Listen() p.StartPeersConnections() + if _,err := groups.ExperimentGate(gcd.GlobalSettings.Experiments); err == nil { + p.StartServerConnections() + } } blockUnkownPeers, exists := p.GetAttribute(constants.BlockUnknownPeersSetting) diff --git a/go/os/kill_unix.go b/go/os/kill_unix.go index acfaaa5c..4856bf1e 100644 --- a/go/os/kill_unix.go +++ b/go/os/kill_unix.go @@ -17,7 +17,8 @@ func CheckProcessAndKill(pid uint64, processName string) { bytes,err := exec.Command("ps", "-p", strconv.Itoa(int(pid)), "-o", "command").Output() if err == nil { // check for a binary @ "/" - if strings.HasSuffix(string(bytes),"/"+processName) { + lines := strings.Split(string(bytes), "\n") + if len(lines) >= 2 && strings.Contains(lines[1],"/"+processName+" ") { Kill(pid) return } diff --git a/go/ui/gcd.go b/go/ui/gcd.go index 9ff82a0d..85c208a4 100644 --- a/go/ui/gcd.go +++ b/go/ui/gcd.go @@ -7,7 +7,7 @@ import ( "cwtch.im/cwtch/model/attr" "cwtch.im/cwtch/protocol/connections" "cwtch.im/ui/go/constants" - "encoding/base64" + "cwtch.im/ui/go/features/groups" "github.com/therecipe/qt/qml" "strconv" "sync" @@ -246,20 +246,18 @@ func (this *GrandCentralDispatcher) sendMessage(message string) { } if isGroup(this.SelectedConversation()) { - if !the.Peer.GetGroup(this.SelectedConversation()).Accepted { - err := the.Peer.AcceptInvite(this.SelectedConversation()) + if gf,err := groups.ExperimentGate(this.GlobalSettings.Experiments); err == nil { + groupHandle := this.SelectedConversation() + this.TimelineInterface.AddMessage(this.TimelineInterface.num()) + err := gf.SendMessage(groupHandle, message) + this.TimelineInterface.RequestEIR() + if err != nil { - log.Errorf("tried to mark a nonexistent group as existed. bad!") + this.InvokePopup("failed to send message " + err.Error()) return } - } - - this.TimelineInterface.AddMessage(this.TimelineInterface.num()) - _, err := the.Peer.SendMessageToGroupTracked(this.SelectedConversation(), message) - this.TimelineInterface.RequestEIR() - - if err != nil { - this.InvokePopup("failed to send message " + err.Error()) + } else { + this.InvokePopup("Groups are currently disabled by an experiment gate. turn it on in Settings") return } } else { @@ -442,27 +440,18 @@ func (this *GrandCentralDispatcher) importString(str string) { return } - if strings.HasPrefix(str, "tofubundle:") { - bundle := strings.Split(str,"||") - this.importString(bundle[0][11:]) - this.importString(bundle[1]) - return - } - - // Server Key Bundles are prefixed with - if strings.HasPrefix(str, "server:") { - bundle, err := base64.StdEncoding.DecodeString(str[7:]) - if err == nil { - err := the.Peer.AddServer(string(bundle)) + if gf,err := groups.ExperimentGate(this.GlobalSettings.Experiments); err == nil { + if gf.ValidPrefix(str) { + err = gf.HandleImportString(str) if err == nil { - this.InvokePopup("Successfully Imported Server Key Bundle") + // TODO: We need a better way of signaling the success of "invisible" actions like adding server bundles + this.InvokePopup("successfully imported") return } - this.InvokePopup("Error Importing Server Key Bundle: " + err.Error()) + this.InvokePopup("failed import: " + err.Error()) return } - this.InvokePopup("Invalid Server Key Bundle: " + err.Error()) - return + // drop through to peer import strings } log.Debugf("importing: %s\n", str) @@ -470,16 +459,7 @@ func (this *GrandCentralDispatcher) importString(str string) { name := onion str = strings.TrimSpace(str) - //eg: torv3JFDWkXExBsZLkjvfkkuAxHsiLGZBk0bvoeJID9ItYnU=EsEBCiBhOWJhZDU1OTQ0NWI3YmM2N2YxYTM5YjkzMTNmNTczNRIgpHeNaG+6jy750eDhwLO39UX4f2xs0irK/M3P6mDSYQIaOTJjM2ttb29ibnlnaGoyenc2cHd2N2Q1N3l6bGQ3NTNhdW8zdWdhdWV6enB2ZmFrM2FoYzRiZHlkCiJAdVSSVgsksceIfHe41OJu9ZFHO8Kwv3G6F5OK3Hw4qZ6hn6SiZjtmJlJezoBH0voZlCahOU7jCOg+dsENndZxAA== - if str[0:5] == "torv3" { // GROUP INVITE - err := the.Peer.ImportGroup(str) - if err != nil { - this.InvokePopup("not a valid group invite") - return - } - return - } if strings.Contains(str, " ") { // usually people prepend spaces and we don't want it going into the name (use ~ for that) parts := strings.Split(strings.TrimSpace(str), " ") @@ -708,10 +688,24 @@ func (this *GrandCentralDispatcher) loadProfile(onion string) { } } - groups := the.Peer.GetGroups() - for i := range groups { - // Only join servers for active and explicitly accepted groups. - this.GetUiManager(this.selectedProfile()).AddContact(groups[i]) + + // Groups Gating + if _,err := groups.ExperimentGate(this.GlobalSettings.Experiments); err == nil { + the.Peer.StartServerConnections() + groups := the.Peer.GetGroups() + for i := range groups { + // Only join servers for active and explicitly accepted groups. + this.GetUiManager(this.selectedProfile()).AddContact(groups[i]) + } + } else { + // Leave all active groups for this Peer. + groups := the.Peer.GetGroups() + for i := range groups { + group := the.Peer.GetGroup(groups[i]) + the.EventBus.Publish(event.NewEvent(event.LeaveServer, map[event.Field]string{ + event.GroupServer: group.GroupServer, + })) + } } } diff --git a/main.go b/main.go index 40aae889..303d72fd 100644 --- a/main.go +++ b/main.go @@ -6,9 +6,9 @@ import ( "cwtch.im/cwtch/event" "cwtch.im/cwtch/event/bridge" "cwtch.im/cwtch/peer" + "cwtch.im/ui/go/features/servers" "cwtch.im/ui/go/handlers" os2 "cwtch.im/ui/go/os" - "cwtch.im/ui/go/servers" "cwtch.im/ui/go/the" "cwtch.im/ui/go/ui" "cwtch.im/ui/go/ui/android" From 143dd9693ca395dd460b19933d70abed76e592cf Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Thu, 29 Oct 2020 16:17:41 -0700 Subject: [PATCH 2/2] Update Cwtch Release --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 7d571761..8964e3a8 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module cwtch.im/ui go 1.12 require ( - cwtch.im/cwtch v0.4.4 + cwtch.im/cwtch v0.4.5 git.openprivacy.ca/openprivacy/connectivity v1.3.1 git.openprivacy.ca/openprivacy/log v1.0.1 github.com/c-bata/go-prompt v0.2.3 // indirect diff --git a/go.sum b/go.sum index fe42a757..83fc3264 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ cwtch.im/cwtch v0.4.3 h1:xf/jMW4+UJckzbYm5g9rPJKTP7fr6O6JC5pH1xjTs/A= cwtch.im/cwtch v0.4.3/go.mod h1:10gBkMSqAH95Pz4jTx5mpIHE+dkn+4kRC4BFTxWuQK8= cwtch.im/cwtch v0.4.4 h1:LC9SngDx+iLXFiDK8GUkSxaWqloXQcPijvMPmwt9h80= cwtch.im/cwtch v0.4.4/go.mod h1:10gBkMSqAH95Pz4jTx5mpIHE+dkn+4kRC4BFTxWuQK8= +cwtch.im/cwtch v0.4.5 h1:BK4IMqCMf9xNmeLzaVDUbl2bnXdw5fOWXvEGBMTOjXM= +cwtch.im/cwtch v0.4.5/go.mod h1:Mh7vQQ3z55+prpX6EuUkg4QNQkBACMoDcgCNBeAH2EY= cwtch.im/tapir v0.2.0 h1:7MkoR5+uEuPW34/O0GZRidnIjq/01Cfm8nl5IRuqpGc= cwtch.im/tapir v0.2.0/go.mod h1:xzzZ28adyUXNkYL1YodcHsAiTt3IJ8Loc29YVn9mIEQ= cwtch.im/tapir v0.2.1 h1:t1YJB9q5sV1A9xwiiwL6WVfw3dwQWLoecunuzT1PQtw=