Merge pull request 'Gate Group Functionality' (#356) from serverpane into master
the build was successful Details

Reviewed-on: #356
This commit is contained in:
erinn 2020-10-29 17:30:16 -07:00
commit b80ecc0a7d
9 changed files with 171 additions and 49 deletions

4
go.mod
View File

@ -3,7 +3,7 @@ module cwtch.im/ui
go 1.12
require (
cwtch.im/cwtch v0.4.3
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
@ -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
)
)

6
go.sum
View File

@ -5,8 +5,14 @@ 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/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=
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=

View File

@ -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")
}

View File

@ -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")
}
}

View File

@ -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
}

View File

@ -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)

View File

@ -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 @ "/<process_name>"
if strings.HasSuffix(string(bytes),"/"+processName) {
lines := strings.Split(string(bytes), "\n")
if len(lines) >= 2 && strings.Contains(lines[1],"/"+processName+" ") {
Kill(pid)
return
}

View File

@ -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,
}))
}
}
}

View File

@ -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"