Merge pull request 'Gate Group Functionality' (#356) from serverpane into master
the build was successful
Details
the build was successful
Details
Reviewed-on: #356
This commit is contained in:
commit
b80ecc0a7d
2
go.mod
2
go.mod
|
@ -3,7 +3,7 @@ module cwtch.im/ui
|
||||||
go 1.12
|
go 1.12
|
||||||
|
|
||||||
require (
|
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/connectivity v1.3.1
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.1
|
git.openprivacy.ca/openprivacy/log v1.0.1
|
||||||
github.com/c-bata/go-prompt v0.2.3 // indirect
|
github.com/c-bata/go-prompt v0.2.3 // indirect
|
||||||
|
|
6
go.sum
6
go.sum
|
@ -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.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 h1:xf/jMW4+UJckzbYm5g9rPJKTP7fr6O6JC5pH1xjTs/A=
|
||||||
cwtch.im/cwtch v0.4.3/go.mod h1:10gBkMSqAH95Pz4jTx5mpIHE+dkn+4kRC4BFTxWuQK8=
|
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 h1:7MkoR5+uEuPW34/O0GZRidnIjq/01Cfm8nl5IRuqpGc=
|
||||||
cwtch.im/tapir v0.2.0/go.mod h1:xzzZ28adyUXNkYL1YodcHsAiTt3IJ8Loc29YVn9mIEQ=
|
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 h1:PSHUmNqaW7BZUX8n2eTDeNbjsuRe+t5Ae0Og+P+jDM0=
|
||||||
git.openprivacy.ca/openprivacy/bine v0.0.3/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU=
|
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=
|
git.openprivacy.ca/openprivacy/connectivity v1.2.0/go.mod h1:B7vzuVmChJtSKoh0ezph5vu6DQ0gIk0zHUNG6IgXCcA=
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"cwtch.im/cwtch/app/plugins"
|
"cwtch.im/cwtch/app/plugins"
|
||||||
"cwtch.im/cwtch/event"
|
"cwtch.im/cwtch/event"
|
||||||
"cwtch.im/ui/go/constants"
|
"cwtch.im/ui/go/constants"
|
||||||
|
"cwtch.im/ui/go/features/groups"
|
||||||
"cwtch.im/ui/go/the"
|
"cwtch.im/ui/go/the"
|
||||||
"cwtch.im/ui/go/ui"
|
"cwtch.im/ui/go/ui"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
|
@ -87,6 +88,9 @@ func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool, reloadingAccounts
|
||||||
if e.Data[event.Status] != "running" {
|
if e.Data[event.Status] != "running" {
|
||||||
p.Listen()
|
p.Listen()
|
||||||
p.StartPeersConnections()
|
p.StartPeersConnections()
|
||||||
|
if _,err := groups.ExperimentGate(gcd.GlobalSettings.Experiments); err == nil {
|
||||||
|
p.StartServerConnections()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
blockUnkownPeers, exists := p.GetAttribute(constants.BlockUnknownPeersSetting)
|
blockUnkownPeers, exists := p.GetAttribute(constants.BlockUnknownPeersSetting)
|
||||||
|
|
|
@ -17,7 +17,8 @@ func CheckProcessAndKill(pid uint64, processName string) {
|
||||||
bytes,err := exec.Command("ps", "-p", strconv.Itoa(int(pid)), "-o", "command").Output()
|
bytes,err := exec.Command("ps", "-p", strconv.Itoa(int(pid)), "-o", "command").Output()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// check for a binary @ "/<process_name>"
|
// 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)
|
Kill(pid)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
64
go/ui/gcd.go
64
go/ui/gcd.go
|
@ -7,7 +7,7 @@ import (
|
||||||
"cwtch.im/cwtch/model/attr"
|
"cwtch.im/cwtch/model/attr"
|
||||||
"cwtch.im/cwtch/protocol/connections"
|
"cwtch.im/cwtch/protocol/connections"
|
||||||
"cwtch.im/ui/go/constants"
|
"cwtch.im/ui/go/constants"
|
||||||
"encoding/base64"
|
"cwtch.im/ui/go/features/groups"
|
||||||
"github.com/therecipe/qt/qml"
|
"github.com/therecipe/qt/qml"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -246,22 +246,20 @@ func (this *GrandCentralDispatcher) sendMessage(message string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if isGroup(this.SelectedConversation()) {
|
if isGroup(this.SelectedConversation()) {
|
||||||
if !the.Peer.GetGroup(this.SelectedConversation()).Accepted {
|
if gf,err := groups.ExperimentGate(this.GlobalSettings.Experiments); err == nil {
|
||||||
err := the.Peer.AcceptInvite(this.SelectedConversation())
|
groupHandle := this.SelectedConversation()
|
||||||
if err != nil {
|
|
||||||
log.Errorf("tried to mark a nonexistent group as existed. bad!")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.TimelineInterface.AddMessage(this.TimelineInterface.num())
|
this.TimelineInterface.AddMessage(this.TimelineInterface.num())
|
||||||
_, err := the.Peer.SendMessageToGroupTracked(this.SelectedConversation(), message)
|
err := gf.SendMessage(groupHandle, message)
|
||||||
this.TimelineInterface.RequestEIR()
|
this.TimelineInterface.RequestEIR()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.InvokePopup("failed to send message " + err.Error())
|
this.InvokePopup("failed to send message " + err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.InvokePopup("Groups are currently disabled by an experiment gate. turn it on in Settings")
|
||||||
|
return
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.TimelineInterface.AddMessage(this.TimelineInterface.num())
|
this.TimelineInterface.AddMessage(this.TimelineInterface.num())
|
||||||
the.Peer.SendMessageToPeer(this.SelectedConversation(), message)
|
the.Peer.SendMessageToPeer(this.SelectedConversation(), message)
|
||||||
|
@ -442,27 +440,18 @@ func (this *GrandCentralDispatcher) importString(str string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(str, "tofubundle:") {
|
if gf,err := groups.ExperimentGate(this.GlobalSettings.Experiments); err == nil {
|
||||||
bundle := strings.Split(str,"||")
|
if gf.ValidPrefix(str) {
|
||||||
this.importString(bundle[0][11:])
|
err = gf.HandleImportString(str)
|
||||||
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 {
|
if err == nil {
|
||||||
err := the.Peer.AddServer(string(bundle))
|
// TODO: We need a better way of signaling the success of "invisible" actions like adding server bundles
|
||||||
if err == nil {
|
this.InvokePopup("successfully imported")
|
||||||
this.InvokePopup("Successfully Imported Server Key Bundle")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.InvokePopup("Error Importing Server Key Bundle: " + err.Error())
|
this.InvokePopup("failed import: " + err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.InvokePopup("Invalid Server Key Bundle: " + err.Error())
|
// drop through to peer import strings
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("importing: %s\n", str)
|
log.Debugf("importing: %s\n", str)
|
||||||
|
@ -470,16 +459,7 @@ func (this *GrandCentralDispatcher) importString(str string) {
|
||||||
name := onion
|
name := onion
|
||||||
str = strings.TrimSpace(str)
|
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)
|
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), " ")
|
parts := strings.Split(strings.TrimSpace(str), " ")
|
||||||
|
@ -708,11 +688,25 @@ func (this *GrandCentralDispatcher) loadProfile(onion string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Groups Gating
|
||||||
|
if _,err := groups.ExperimentGate(this.GlobalSettings.Experiments); err == nil {
|
||||||
|
the.Peer.StartServerConnections()
|
||||||
groups := the.Peer.GetGroups()
|
groups := the.Peer.GetGroups()
|
||||||
for i := range groups {
|
for i := range groups {
|
||||||
// Only join servers for active and explicitly accepted groups.
|
// Only join servers for active and explicitly accepted groups.
|
||||||
this.GetUiManager(this.selectedProfile()).AddContact(groups[i])
|
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,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *GrandCentralDispatcher) createProfile(nick string, defaultPass bool, password string) {
|
func (this *GrandCentralDispatcher) createProfile(nick string, defaultPass bool, password string) {
|
||||||
|
|
2
main.go
2
main.go
|
@ -6,9 +6,9 @@ import (
|
||||||
"cwtch.im/cwtch/event"
|
"cwtch.im/cwtch/event"
|
||||||
"cwtch.im/cwtch/event/bridge"
|
"cwtch.im/cwtch/event/bridge"
|
||||||
"cwtch.im/cwtch/peer"
|
"cwtch.im/cwtch/peer"
|
||||||
|
"cwtch.im/ui/go/features/servers"
|
||||||
"cwtch.im/ui/go/handlers"
|
"cwtch.im/ui/go/handlers"
|
||||||
os2 "cwtch.im/ui/go/os"
|
os2 "cwtch.im/ui/go/os"
|
||||||
"cwtch.im/ui/go/servers"
|
|
||||||
"cwtch.im/ui/go/the"
|
"cwtch.im/ui/go/the"
|
||||||
"cwtch.im/ui/go/ui"
|
"cwtch.im/ui/go/ui"
|
||||||
"cwtch.im/ui/go/ui/android"
|
"cwtch.im/ui/go/ui/android"
|
||||||
|
|
Reference in New Issue