gcd.firstTime + SettingsList scrolls #388
|
@ -0,0 +1,10 @@
|
||||||
|
# Settings List / Flickable
|
||||||
|
|
||||||
|
Content not scrolling: Flickable does some reparenting behind the scenes and so
|
||||||
|
in the top level child of the Flickable you will need:
|
||||||
|
|
||||||
|
parent: root.contentItem
|
||||||
|
|
||||||
|
And in the flickable you will need to set the contentHeight:
|
||||||
|
|
||||||
|
contentHeight: <childId>.height + <padding>
|
|
@ -4,23 +4,23 @@ import "cwtch.im/cwtch/event"
|
||||||
|
|
||||||
// The server manager defines its own events, most should be self-explanatory:
|
// The server manager defines its own events, most should be self-explanatory:
|
||||||
const (
|
const (
|
||||||
NewServer = event.Type("NewServer")
|
NewServer = event.Type("NewServer")
|
||||||
|
|
||||||
// Force a UI update
|
// Force a UI update
|
||||||
ListServers = event.Type("ListServers")
|
ListServers = event.Type("ListServers")
|
||||||
|
|
||||||
// Takes an Onion, used to toggle off/on Server availability
|
// Takes an Onion, used to toggle off/on Server availability
|
||||||
StartServer = event.Type("StartServer")
|
StartServer = event.Type("StartServer")
|
||||||
StopServer = event.Type("StopServer")
|
StopServer = event.Type("StopServer")
|
||||||
|
|
||||||
// Takes an Onion and a AutoStartEnabled boolean
|
// Takes an Onion and a AutoStartEnabled boolean
|
||||||
AutoStart = event.Type("AutoStart")
|
AutoStart = event.Type("AutoStart")
|
||||||
|
|
||||||
// Get the status of a particular server (takes an Onion)
|
// Get the status of a particular server (takes an Onion)
|
||||||
CheckServerStatus = event.Type("CheckServerStatus")
|
CheckServerStatus = event.Type("CheckServerStatus")
|
||||||
ServerStatusUpdate = event.Type("ServerStatusUpdate")
|
ServerStatusUpdate = event.Type("ServerStatusUpdate")
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AutoStartEnabled = event.Field("AutoStartEnabled")
|
AutoStartEnabled = event.Field("AutoStartEnabled")
|
||||||
)
|
)
|
||||||
|
|
|
@ -14,13 +14,11 @@ const TofuBundlePrefix = "tofubundle:"
|
||||||
const GroupPrefix = "torv3"
|
const GroupPrefix = "torv3"
|
||||||
const GroupExperiment = "tapir-groups-experiment"
|
const GroupExperiment = "tapir-groups-experiment"
|
||||||
|
|
||||||
|
|
||||||
type GroupFunctionality struct {
|
type GroupFunctionality struct {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExperimentGate returns GroupFunctionality if the experiment is enabled, and an error otherwise.
|
// ExperimentGate returns GroupFunctionality if the experiment is enabled, and an error otherwise.
|
||||||
func ExperimentGate(experimentMap map[string]bool) (*GroupFunctionality,error) {
|
func ExperimentGate(experimentMap map[string]bool) (*GroupFunctionality, error) {
|
||||||
if experimentMap[GroupExperiment] {
|
if experimentMap[GroupExperiment] {
|
||||||
return new(GroupFunctionality), nil
|
return new(GroupFunctionality), nil
|
||||||
}
|
}
|
||||||
|
@ -49,10 +47,10 @@ func (gf *GroupFunctionality) ValidPrefix(importString string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleImportString handles import strings for groups and servers
|
// HandleImportString handles import strings for groups and servers
|
||||||
func (gf * GroupFunctionality) HandleImportString(importString string) error {
|
func (gf *GroupFunctionality) HandleImportString(importString string) error {
|
||||||
|
|
||||||
if strings.HasPrefix(importString, TofuBundlePrefix) {
|
if strings.HasPrefix(importString, TofuBundlePrefix) {
|
||||||
bundle := strings.Split(importString,"||")
|
bundle := strings.Split(importString, "||")
|
||||||
gf.HandleImportString(bundle[0][11:])
|
gf.HandleImportString(bundle[0][11:])
|
||||||
gf.HandleImportString(bundle[1])
|
gf.HandleImportString(bundle[1])
|
||||||
return nil
|
return nil
|
||||||
|
@ -68,10 +66,9 @@ func (gf * GroupFunctionality) HandleImportString(importString string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
//eg: torv3JFDWkXExBsZLkjvfkkuAxHsiLGZBk0bvoeJID9ItYnU=EsEBCiBhOWJhZDU1OTQ0NWI3YmM2N2YxYTM5YjkzMTNmNTczNRIgpHeNaG+6jy750eDhwLO39UX4f2xs0irK/M3P6mDSYQIaOTJjM2ttb29ibnlnaGoyenc2cHd2N2Q1N3l6bGQ3NTNhdW8zdWdhdWV6enB2ZmFrM2FoYzRiZHlkCiJAdVSSVgsksceIfHe41OJu9ZFHO8Kwv3G6F5OK3Hw4qZ6hn6SiZjtmJlJezoBH0voZlCahOU7jCOg+dsENndZxAA==
|
//eg: torv3JFDWkXExBsZLkjvfkkuAxHsiLGZBk0bvoeJID9ItYnU=EsEBCiBhOWJhZDU1OTQ0NWI3YmM2N2YxYTM5YjkzMTNmNTczNRIgpHeNaG+6jy750eDhwLO39UX4f2xs0irK/M3P6mDSYQIaOTJjM2ttb29ibnlnaGoyenc2cHd2N2Q1N3l6bGQ3NTNhdW8zdWdhdWV6enB2ZmFrM2FoYzRiZHlkCiJAdVSSVgsksceIfHe41OJu9ZFHO8Kwv3G6F5OK3Hw4qZ6hn6SiZjtmJlJezoBH0voZlCahOU7jCOg+dsENndZxAA==
|
||||||
if strings.HasPrefix(importString, GroupPrefix){
|
if strings.HasPrefix(importString, GroupPrefix) {
|
||||||
return the.Peer.ImportGroup(importString)
|
return the.Peer.ImportGroup(importString)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return errors.New("invalid_group_invite_prefix")
|
return errors.New("invalid_group_invite_prefix")
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,8 @@ package groups
|
||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
|
|
||||||
func TestGroupFunctionality_ValidPrefix(t *testing.T) {
|
func TestGroupFunctionality_ValidPrefix(t *testing.T) {
|
||||||
gf,_ := ExperimentGate(map[string]bool{GroupExperiment: true})
|
gf, _ := ExperimentGate(map[string]bool{GroupExperiment: true})
|
||||||
if gf.ValidPrefix("torv3blahblahblah") == false {
|
if gf.ValidPrefix("torv3blahblahblah") == false {
|
||||||
t.Fatalf("torv3 should be a valid prefix")
|
t.Fatalf("torv3 should be a valid prefix")
|
||||||
}
|
}
|
||||||
|
@ -21,20 +20,20 @@ func TestGroupFunctionality_ValidPrefix(t *testing.T) {
|
||||||
|
|
||||||
func TestGroupFunctionality_IsEnabled(t *testing.T) {
|
func TestGroupFunctionality_IsEnabled(t *testing.T) {
|
||||||
|
|
||||||
_,err := ExperimentGate(map[string]bool{})
|
_, err := ExperimentGate(map[string]bool{})
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("group functionality should be disabled")
|
t.Fatalf("group functionality should be disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
_,err = ExperimentGate(map[string]bool{GroupExperiment: true})
|
_, err = ExperimentGate(map[string]bool{GroupExperiment: true})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("group functionality should be enabled")
|
t.Fatalf("group functionality should be enabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
_,err = ExperimentGate(map[string]bool{GroupExperiment: false})
|
_, err = ExperimentGate(map[string]bool{GroupExperiment: false})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("group functionality should be disabled")
|
t.Fatalf("group functionality should be disabled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,23 +21,20 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ServerManager is responsible for managing user operated servers
|
// ServerManager is responsible for managing user operated servers
|
||||||
type ServerManager struct {
|
type ServerManager struct {
|
||||||
servers sync.Map
|
servers sync.Map
|
||||||
configDir string
|
configDir string
|
||||||
acn connectivity.ACN
|
acn connectivity.ACN
|
||||||
}
|
}
|
||||||
|
|
||||||
type serverStatusCache struct {
|
type serverStatusCache struct {
|
||||||
online bool
|
online bool
|
||||||
autostart bool
|
autostart bool
|
||||||
messages uint64
|
messages uint64
|
||||||
bundle []byte
|
bundle []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// LaunchServiceManager is responsible for setting up everything relating to managing servers in the UI.
|
// LaunchServiceManager is responsible for setting up everything relating to managing servers in the UI.
|
||||||
func LaunchServiceManager(gcd *ui.GrandCentralDispatcher, acn connectivity.ACN, configDir string) {
|
func LaunchServiceManager(gcd *ui.GrandCentralDispatcher, acn connectivity.ACN, configDir string) {
|
||||||
sm := new(ServerManager)
|
sm := new(ServerManager)
|
||||||
|
@ -48,7 +45,7 @@ func LaunchServiceManager(gcd *ui.GrandCentralDispatcher, acn connectivity.ACN,
|
||||||
|
|
||||||
// initializeServerCache sets up a new cache based on the config. Notably it stores a newly signed keybundle that
|
// initializeServerCache sets up a new cache based on the config. Notably it stores a newly signed keybundle that
|
||||||
// the ui uses to allow people to share server key bundles.
|
// the ui uses to allow people to share server key bundles.
|
||||||
func initializeServerCache(config server.Config) (*server.Server,serverStatusCache) {
|
func initializeServerCache(config server.Config) (*server.Server, serverStatusCache) {
|
||||||
newServer := new(server.Server)
|
newServer := new(server.Server)
|
||||||
newServer.Setup(config)
|
newServer.Setup(config)
|
||||||
newServer.KeyBundle()
|
newServer.KeyBundle()
|
||||||
|
@ -75,7 +72,7 @@ func (sm *ServerManager) Init(gcd *ui.GrandCentralDispatcher) {
|
||||||
// many of risks e.g lack of server diversification / availability of servers.
|
// many of risks e.g lack of server diversification / availability of servers.
|
||||||
// Like many parts of the metadata resistant risk model, it is a compromise
|
// Like many parts of the metadata resistant risk model, it is a compromise
|
||||||
log.Debugf("Reading server directory: %v", sm.configDir)
|
log.Debugf("Reading server directory: %v", sm.configDir)
|
||||||
os.MkdirAll(sm.configDir,0700)
|
os.MkdirAll(sm.configDir, 0700)
|
||||||
items, _ := ioutil.ReadDir(sm.configDir)
|
items, _ := ioutil.ReadDir(sm.configDir)
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
if item.IsDir() {
|
if item.IsDir() {
|
||||||
|
@ -83,7 +80,7 @@ func (sm *ServerManager) Init(gcd *ui.GrandCentralDispatcher) {
|
||||||
config := server.LoadConfig(path.Join(sm.configDir, item.Name()), "serverconfig")
|
config := server.LoadConfig(path.Join(sm.configDir, item.Name()), "serverconfig")
|
||||||
identity := config.Identity()
|
identity := config.Identity()
|
||||||
log.Debugf("Launching Server goroutine for %v", identity.Hostname())
|
log.Debugf("Launching Server goroutine for %v", identity.Hostname())
|
||||||
s,cache := initializeServerCache(config)
|
s, cache := initializeServerCache(config)
|
||||||
sm.servers.Store(identity.Hostname(), cache)
|
sm.servers.Store(identity.Hostname(), cache)
|
||||||
go sm.runServer(s)
|
go sm.runServer(s)
|
||||||
} else {
|
} else {
|
||||||
|
@ -112,24 +109,23 @@ func (sm *ServerManager) Init(gcd *ui.GrandCentralDispatcher) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// NewServer createa a new server
|
// NewServer createa a new server
|
||||||
func (sm *ServerManager) NewServer() {
|
func (sm *ServerManager) NewServer() {
|
||||||
log.Debugf("Adding a new Server")
|
log.Debugf("Adding a new Server")
|
||||||
num, err := rand.Int(rand.Reader, big.NewInt(math.MaxUint32))
|
num, err := rand.Int(rand.Reader, big.NewInt(math.MaxUint32))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
serverDir := path.Join(sm.configDir, num.String());
|
serverDir := path.Join(sm.configDir, num.String())
|
||||||
os.MkdirAll(serverDir,0700)
|
os.MkdirAll(serverDir, 0700)
|
||||||
config := server.LoadConfig(serverDir, "serverconfig")
|
config := server.LoadConfig(serverDir, "serverconfig")
|
||||||
identity := config.Identity()
|
identity := config.Identity()
|
||||||
s, cache := initializeServerCache(config)
|
s, cache := initializeServerCache(config)
|
||||||
sm.servers.Store(identity.Hostname(),cache)
|
sm.servers.Store(identity.Hostname(), cache)
|
||||||
go sm.runServer(s)
|
go sm.runServer(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// runServer sets up an event queue per server to allow them to manage their own state.
|
// runServer sets up an event queue per server to allow them to manage their own state.
|
||||||
func (sm *ServerManager) runServer(s * server.Server) {
|
func (sm *ServerManager) runServer(s *server.Server) {
|
||||||
q := event.NewQueue()
|
q := event.NewQueue()
|
||||||
the.AppBus.Subscribe(constants.StartServer, q)
|
the.AppBus.Subscribe(constants.StartServer, q)
|
||||||
the.AppBus.Subscribe(constants.StopServer, q)
|
the.AppBus.Subscribe(constants.StopServer, q)
|
||||||
|
@ -137,10 +133,9 @@ func (sm *ServerManager) runServer(s * server.Server) {
|
||||||
the.AppBus.Subscribe(constants.AutoStart, q)
|
the.AppBus.Subscribe(constants.AutoStart, q)
|
||||||
the.AppBus.Subscribe(event.Shutdown, q)
|
the.AppBus.Subscribe(event.Shutdown, q)
|
||||||
|
|
||||||
|
|
||||||
identity := s.Identity()
|
identity := s.Identity()
|
||||||
|
|
||||||
cache,ok := sm.servers.Load(identity.Hostname())
|
cache, ok := sm.servers.Load(identity.Hostname())
|
||||||
if ok {
|
if ok {
|
||||||
serverStatusCache := cache.(serverStatusCache)
|
serverStatusCache := cache.(serverStatusCache)
|
||||||
if serverStatusCache.autostart {
|
if serverStatusCache.autostart {
|
||||||
|
@ -148,7 +143,6 @@ func (sm *ServerManager) runServer(s * server.Server) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
log.Debugf("Launching Server %v", identity.Hostname())
|
log.Debugf("Launching Server %v", identity.Hostname())
|
||||||
log.Debugf("Launching Event Bus for Server %v", identity.Hostname())
|
log.Debugf("Launching Event Bus for Server %v", identity.Hostname())
|
||||||
for {
|
for {
|
||||||
|
@ -158,7 +152,7 @@ func (sm *ServerManager) runServer(s * server.Server) {
|
||||||
case constants.StartServer:
|
case constants.StartServer:
|
||||||
onion := e.Data[event.Onion]
|
onion := e.Data[event.Onion]
|
||||||
if onion == identity.Hostname() {
|
if onion == identity.Hostname() {
|
||||||
if running,_ := s.CheckStatus(); running {
|
if running, _ := s.CheckStatus(); running {
|
||||||
// we are already running
|
// we are already running
|
||||||
log.Debugf("Server %v Already Running", onion)
|
log.Debugf("Server %v Already Running", onion)
|
||||||
continue
|
continue
|
||||||
|
@ -175,7 +169,7 @@ func (sm *ServerManager) runServer(s * server.Server) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
fmt.Printf("Secret Debugging Group for Testing: %v\n", "torv3"+base64.StdEncoding.EncodeToString(invite))
|
fmt.Printf("Secret Debugging Group for Testing: %v\n", "torv3"+base64.StdEncoding.EncodeToString(invite))
|
||||||
cache,ok := sm.servers.Load(onion)
|
cache, ok := sm.servers.Load(onion)
|
||||||
if ok {
|
if ok {
|
||||||
serverStatusCache := cache.(serverStatusCache)
|
serverStatusCache := cache.(serverStatusCache)
|
||||||
serverStatusCache.online = true
|
serverStatusCache.online = true
|
||||||
|
@ -187,7 +181,7 @@ func (sm *ServerManager) runServer(s * server.Server) {
|
||||||
onion := e.Data[event.Onion]
|
onion := e.Data[event.Onion]
|
||||||
if onion == identity.Hostname() {
|
if onion == identity.Hostname() {
|
||||||
s.Shutdown()
|
s.Shutdown()
|
||||||
cache,ok := sm.servers.Load(onion)
|
cache, ok := sm.servers.Load(onion)
|
||||||
if ok {
|
if ok {
|
||||||
serverStatusCache := cache.(serverStatusCache)
|
serverStatusCache := cache.(serverStatusCache)
|
||||||
serverStatusCache.online = false
|
serverStatusCache.online = false
|
||||||
|
@ -217,7 +211,7 @@ func (sm *ServerManager) runServer(s * server.Server) {
|
||||||
if onion == identity.Hostname() {
|
if onion == identity.Hostname() {
|
||||||
autostart := e.Data[constants.AutoStartEnabled] == event.True
|
autostart := e.Data[constants.AutoStartEnabled] == event.True
|
||||||
s.ConfigureAutostart(autostart)
|
s.ConfigureAutostart(autostart)
|
||||||
cache,ok := sm.servers.Load(onion)
|
cache, ok := sm.servers.Load(onion)
|
||||||
if ok {
|
if ok {
|
||||||
serverStatusCache := cache.(serverStatusCache)
|
serverStatusCache := cache.(serverStatusCache)
|
||||||
serverStatusCache.autostart = autostart
|
serverStatusCache.autostart = autostart
|
||||||
|
@ -226,8 +220,8 @@ func (sm *ServerManager) runServer(s * server.Server) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case event.Shutdown:
|
case event.Shutdown:
|
||||||
s.Shutdown()
|
s.Shutdown()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,7 +230,7 @@ func (sm *ServerManager) runServer(s * server.Server) {
|
||||||
// cache and sends an update to the UI.
|
// cache and sends an update to the UI.
|
||||||
func (sm *ServerManager) ListServers(gcd *ui.GrandCentralDispatcher) {
|
func (sm *ServerManager) ListServers(gcd *ui.GrandCentralDispatcher) {
|
||||||
log.Debugf("Listing Servers...")
|
log.Debugf("Listing Servers...")
|
||||||
sm.servers.Range(func(k interface{},v interface{}) bool {
|
sm.servers.Range(func(k interface{}, v interface{}) bool {
|
||||||
serverOnion := k.(string)
|
serverOnion := k.(string)
|
||||||
statusCache := v.(serverStatusCache)
|
statusCache := v.(serverStatusCache)
|
||||||
status := 0
|
status := 0
|
||||||
|
@ -249,19 +243,19 @@ func (sm *ServerManager) ListServers(gcd *ui.GrandCentralDispatcher) {
|
||||||
var keyNames []string
|
var keyNames []string
|
||||||
var keys []string
|
var keys []string
|
||||||
|
|
||||||
keybundle,_ := model.DeserializeAndVerify(statusCache.bundle)
|
keybundle, _ := model.DeserializeAndVerify(statusCache.bundle)
|
||||||
|
|
||||||
for _, key_type := range key_types {
|
for _, key_type := range key_types {
|
||||||
log.Debugf("Looking up %v %v", key_type, keyNames)
|
log.Debugf("Looking up %v %v", key_type, keyNames)
|
||||||
if keybundle.HasKeyType(key_type) {
|
if keybundle.HasKeyType(key_type) {
|
||||||
key,_ := keybundle.GetKey(key_type)
|
key, _ := keybundle.GetKey(key_type)
|
||||||
keyNames = append(keyNames, string(key_type))
|
keyNames = append(keyNames, string(key_type))
|
||||||
keys = append(keys, string(key))
|
keys = append(keys, string(key))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Updating Server %v %v %v", serverOnion, status, statusCache.messages)
|
log.Debugf("Updating Server %v %v %v", serverOnion, status, statusCache.messages)
|
||||||
gcd.AddServer(serverOnion,serverOnion,serverOnion, status, statusCache.autostart, "server:"+base64.StdEncoding.EncodeToString(statusCache.bundle), int(statusCache.messages), keyNames, keys)
|
gcd.AddServer(serverOnion, serverOnion, serverOnion, status, statusCache.autostart, "server:"+base64.StdEncoding.EncodeToString(statusCache.bundle), int(statusCache.messages), keyNames, keys)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,11 @@ import (
|
||||||
// On unix systems the command "ps" is practically universal and should suffice for this...
|
// On unix systems the command "ps" is practically universal and should suffice for this...
|
||||||
func CheckProcessAndKill(pid uint64, processName string) {
|
func CheckProcessAndKill(pid uint64, processName string) {
|
||||||
log.Debugf("killing: %v", pid)
|
log.Debugf("killing: %v", pid)
|
||||||
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>"
|
||||||
lines := strings.Split(string(bytes), "\n")
|
lines := strings.Split(string(bytes), "\n")
|
||||||
if len(lines) >= 2 && strings.Contains(lines[1],"/"+processName+" ") {
|
if len(lines) >= 2 && strings.Contains(lines[1], "/"+processName+" ") {
|
||||||
Kill(pid)
|
Kill(pid)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ func CheckProcessAndKill(pid uint64, processName string) {
|
||||||
// Kill a process based on pid
|
// Kill a process based on pid
|
||||||
func Kill(pid uint64) {
|
func Kill(pid uint64) {
|
||||||
log.Debugf("killing: %v", pid)
|
log.Debugf("killing: %v", pid)
|
||||||
bytes,err := exec.Command("kill", strconv.Itoa(int(pid))).Output()
|
bytes, err := exec.Command("kill", strconv.Itoa(int(pid))).Output()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Debugf("kill %v successful: %s", pid, bytes)
|
log.Debugf("kill %v successful: %s", pid, bytes)
|
||||||
} else {
|
} else {
|
||||||
|
@ -40,4 +40,4 @@ func Kill(pid uint64) {
|
||||||
log.Debugf("could not kill pid: %v %v", pid, err)
|
log.Debugf("could not kill pid: %v %v", pid, err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,18 +13,18 @@ import (
|
||||||
// On windows this uses tasklist...
|
// On windows this uses tasklist...
|
||||||
func CheckProcessAndKill(pid uint64, processName string) {
|
func CheckProcessAndKill(pid uint64, processName string) {
|
||||||
log.Debugf("looking up process: %v", pid)
|
log.Debugf("looking up process: %v", pid)
|
||||||
bytes,err := exec.Command("tasklist", "/fi", "pid eq "+strconv.Itoa(int(pid))).Output()
|
bytes, err := exec.Command("tasklist", "/fi", "pid eq "+strconv.Itoa(int(pid))).Output()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Output will be something like this:
|
// Output will be something like this:
|
||||||
//
|
//
|
||||||
// Image Name PID Session Name Session# Mem Usage
|
// Image Name PID Session Name Session# Mem Usage
|
||||||
// ========================= ======== ================ =========== ============
|
// ========================= ======== ================ =========== ============
|
||||||
// process.exe <PID> Services 0 8,936 K
|
// process.exe <PID> Services 0 8,936 K
|
||||||
lines := strings.Split(strings.TrimSpace(string(bytes)),"\n")
|
lines := strings.Split(strings.TrimSpace(string(bytes)), "\n")
|
||||||
log.Debugf("%v\n", lines)
|
log.Debugf("%v\n", lines)
|
||||||
|
|
||||||
// check for "<process>.exe"
|
// check for "<process>.exe"
|
||||||
if len(lines) >= 3 && strings.HasPrefix(strings.ToLower(strings.TrimSpace(lines[2])),processName+".exe") {
|
if len(lines) >= 3 && strings.HasPrefix(strings.ToLower(strings.TrimSpace(lines[2])), processName+".exe") {
|
||||||
Kill(pid)
|
Kill(pid)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ func CheckProcessAndKill(pid uint64, processName string) {
|
||||||
// Kill a process based on pid
|
// Kill a process based on pid
|
||||||
func Kill(pid uint64) {
|
func Kill(pid uint64) {
|
||||||
log.Debugf("killing: %v", pid)
|
log.Debugf("killing: %v", pid)
|
||||||
bytes,err := exec.Command("taskkill", "/F", "/PID", strconv.Itoa(int(pid))).Output()
|
bytes, err := exec.Command("taskkill", "/F", "/PID", strconv.Itoa(int(pid))).Output()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Debugf("kill %v successful: %s", pid, bytes)
|
log.Debugf("kill %v successful: %s", pid, bytes)
|
||||||
} else {
|
} else {
|
||||||
|
@ -45,4 +45,4 @@ func Kill(pid uint64) {
|
||||||
log.Debugf("could not kill pid: %v %v", pid, err)
|
log.Debugf("could not kill pid: %v %v", pid, err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
31
go/ui/gcd.go
31
go/ui/gcd.go
|
@ -36,20 +36,19 @@ type GrandCentralDispatcher struct {
|
||||||
m_selectedProfile string
|
m_selectedProfile string
|
||||||
m_selectedConversation string
|
m_selectedConversation string
|
||||||
|
|
||||||
_ string `property:"os"`
|
_ int `property:"torStatus"`
|
||||||
_ float32 `property:"themeScale,auto,changed"`
|
_ string `property:"os"`
|
||||||
_ string `property:"theme,auto,changed"`
|
_ bool `property:"firstTime"`
|
||||||
_ string `property:"locale,auto,changed"`
|
_ float32 `property:"themeScale,auto,changed"`
|
||||||
_ string `property:"version"`
|
_ string `property:"theme,auto,changed"`
|
||||||
_ string `property:"buildDate"`
|
_ string `property:"locale,auto,changed"`
|
||||||
_ string `property:"assetPath"`
|
_ string `property:"version"`
|
||||||
_ string `property:"selectedProfile,auto"`
|
_ string `property:"buildDate"`
|
||||||
_ string `property:"selectedConversation,auto"`
|
_ string `property:"assetPath"`
|
||||||
|
_ string `property:"selectedProfile,auto"`
|
||||||
_ int `property:"torStatus"`
|
_ string `property:"selectedConversation,auto"`
|
||||||
|
_ bool `property:"experimentsEnabled,auto,changed"`
|
||||||
_ bool `property:experimentsEnabled,auto,changed`
|
_ map[string]bool `property:"experiments,auto,changed"`
|
||||||
_ map[string]bool `property:experiments,auto,changed`
|
|
||||||
|
|
||||||
// profile management stuff
|
// profile management stuff
|
||||||
_ func() `signal:"Loaded"`
|
_ func() `signal:"Loaded"`
|
||||||
|
@ -141,7 +140,9 @@ type GrandCentralDispatcher struct {
|
||||||
|
|
||||||
func (this *GrandCentralDispatcher) init() {
|
func (this *GrandCentralDispatcher) init() {
|
||||||
this.uIManagers = make(map[string]Manager)
|
this.uIManagers = make(map[string]Manager)
|
||||||
this.GlobalSettings = ReadGlobalSettings()
|
firstTime := false
|
||||||
|
this.GlobalSettings, firstTime = ReadGlobalSettings()
|
||||||
|
this.SetFirstTime(firstTime)
|
||||||
this.SetThemeScale(this.GlobalSettings.Zoom)
|
this.SetThemeScale(this.GlobalSettings.Zoom)
|
||||||
this.SetTheme(this.GlobalSettings.Theme)
|
this.SetTheme(this.GlobalSettings.Theme)
|
||||||
this.SetExperimentsEnabled(this.GlobalSettings.ExperimentsEnabled)
|
this.SetExperimentsEnabled(this.GlobalSettings.ExperimentsEnabled)
|
||||||
|
|
|
@ -15,7 +15,7 @@ type MessageModel struct {
|
||||||
|
|
||||||
ackIdx int
|
ackIdx int
|
||||||
handle string
|
handle string
|
||||||
_ func(string) `signal:"setHandle,auto"`
|
_ func(string) `signal:"setHandle,auto"`
|
||||||
|
|
||||||
_ map[int]*core.QByteArray `property:"roles"`
|
_ map[int]*core.QByteArray `property:"roles"`
|
||||||
_ func() `constructor:"init"`
|
_ func() `constructor:"init"`
|
||||||
|
@ -32,17 +32,17 @@ type MessageWrapper struct {
|
||||||
model.Message
|
model.Message
|
||||||
core.QObject
|
core.QObject
|
||||||
|
|
||||||
Timestamp int64
|
Timestamp int64
|
||||||
PeerID string
|
PeerID string
|
||||||
Acknowledged bool
|
Acknowledged bool
|
||||||
RawMessage string
|
RawMessage string
|
||||||
Error string
|
Error string
|
||||||
Day string
|
Day string
|
||||||
Signature string
|
Signature string
|
||||||
_ bool `property:"ackd"`
|
_ bool `property:"ackd"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *MessageModel) Handle() string{
|
func (this *MessageModel) Handle() string {
|
||||||
return this.handle
|
return this.handle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,6 @@ func (this *MessageModel) setHandle(handle string) {
|
||||||
this.handle = handle
|
this.handle = handle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (this *MessageModel) init() {
|
func (this *MessageModel) init() {
|
||||||
mdt := reflect.TypeOf([]MessageWrapper{}).Elem()
|
mdt := reflect.TypeOf([]MessageWrapper{}).Elem()
|
||||||
roles := make(map[int]*core.QByteArray)
|
roles := make(map[int]*core.QByteArray)
|
||||||
|
@ -58,7 +57,7 @@ func (this *MessageModel) init() {
|
||||||
if mdt.Field(i).Name == "Acknowledged" {
|
if mdt.Field(i).Name == "Acknowledged" {
|
||||||
this.ackIdx = int(core.Qt__UserRole) + 1 + i
|
this.ackIdx = int(core.Qt__UserRole) + 1 + i
|
||||||
}
|
}
|
||||||
roles[int(core.Qt__UserRole) + 1 + i] = core.NewQByteArray2(mdt.Field(i).Name, -1)
|
roles[int(core.Qt__UserRole)+1+i] = core.NewQByteArray2(mdt.Field(i).Name, -1)
|
||||||
}
|
}
|
||||||
roles[int(core.Qt__DisplayRole)] = core.NewQByteArray2("display", -1)
|
roles[int(core.Qt__DisplayRole)] = core.NewQByteArray2("display", -1)
|
||||||
this.SetRoles(roles)
|
this.SetRoles(roles)
|
||||||
|
@ -109,13 +108,13 @@ func (this *MessageModel) num() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *MessageModel) getMessage(idx int) *MessageWrapper {
|
func (this *MessageModel) getMessage(idx int) *MessageWrapper {
|
||||||
modelmsg := model.Message{Message:"[an unexpected cwtch error occurred]"}
|
modelmsg := model.Message{Message: "[an unexpected cwtch error occurred]"}
|
||||||
var ackd bool
|
var ackd bool
|
||||||
|
|
||||||
if this.isGroup() {
|
if this.isGroup() {
|
||||||
group := the.Peer.GetGroup(this.Handle())
|
group := the.Peer.GetGroup(this.Handle())
|
||||||
if idx >= len(group.Timeline.Messages) {
|
if idx >= len(group.Timeline.Messages) {
|
||||||
modelmsg = group.UnacknowledgedMessages[idx - len(group.Timeline.Messages)]
|
modelmsg = group.UnacknowledgedMessages[idx-len(group.Timeline.Messages)]
|
||||||
} else {
|
} else {
|
||||||
modelmsg = group.Timeline.Messages[idx]
|
modelmsg = group.Timeline.Messages[idx]
|
||||||
ackd = true
|
ackd = true
|
||||||
|
@ -134,15 +133,15 @@ func (this *MessageModel) getMessage(idx int) *MessageWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &MessageWrapper {
|
return &MessageWrapper{
|
||||||
Message: modelmsg,
|
Message: modelmsg,
|
||||||
Timestamp: modelmsg.Timestamp.Unix(),
|
Timestamp: modelmsg.Timestamp.Unix(),
|
||||||
RawMessage: modelmsg.Message,
|
RawMessage: modelmsg.Message,
|
||||||
PeerID: modelmsg.PeerID,
|
PeerID: modelmsg.PeerID,
|
||||||
Error: modelmsg.Error,
|
Error: modelmsg.Error,
|
||||||
Acknowledged: ackd,
|
Acknowledged: ackd,
|
||||||
Day: modelmsg.Timestamp.Format("January 2, 2006"),
|
Day: modelmsg.Timestamp.Format("January 2, 2006"),
|
||||||
Signature: hex.EncodeToString(modelmsg.Signature),
|
Signature: hex.EncodeToString(modelmsg.Signature),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,16 +21,16 @@ type GlobalSettings struct {
|
||||||
Theme string
|
Theme string
|
||||||
PreviousPid int64
|
PreviousPid int64
|
||||||
ExperimentsEnabled bool
|
ExperimentsEnabled bool
|
||||||
Experiments map[string]bool
|
Experiments map[string]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultGlobalSettings = GlobalSettings{
|
var DefaultGlobalSettings = GlobalSettings{
|
||||||
Zoom: 1.9,
|
Zoom: 1.9,
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
PreviousPid: -1,
|
PreviousPid: -1,
|
||||||
ExperimentsEnabled: false,
|
ExperimentsEnabled: false,
|
||||||
Experiments: make(map[string]bool),
|
Experiments: make(map[string]bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitGlobalSettingsFile(directory string, password string) error {
|
func InitGlobalSettingsFile(directory string, password string) error {
|
||||||
|
@ -55,7 +55,7 @@ func InitGlobalSettingsFile(directory string, password string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadGlobalSettings() *GlobalSettings {
|
func ReadGlobalSettings() (*GlobalSettings, bool) {
|
||||||
settings := DefaultGlobalSettings
|
settings := DefaultGlobalSettings
|
||||||
if runtime.GOOS == "android" {
|
if runtime.GOOS == "android" {
|
||||||
settings.Zoom = 2.9
|
settings.Zoom = 2.9
|
||||||
|
@ -65,17 +65,17 @@ func ReadGlobalSettings() *GlobalSettings {
|
||||||
settingsBytes, err := the.GlobalSettingsFile.Read()
|
settingsBytes, err := the.GlobalSettingsFile.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Could not read global ui settings: %v\n", err)
|
log.Errorf("Could not read global ui settings: %v\n", err)
|
||||||
return &settings
|
return &settings, true //firstTime = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
err = json.Unmarshal(settingsBytes, &settings)
|
err = json.Unmarshal(settingsBytes, &settings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Could not parse global ui settings: %v\n", err)
|
log.Errorf("Could not parse global ui settings: %v\n", err)
|
||||||
|
return &settings, true //firstTime = true
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Settings: %v", settings)
|
log.Debugf("Settings: %v", settings)
|
||||||
return &settings
|
return &settings, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func WriteGlobalSettings(globalSettings *GlobalSettings) {
|
func WriteGlobalSettings(globalSettings *GlobalSettings) {
|
||||||
|
|
3
main.go
3
main.go
|
@ -115,6 +115,7 @@ func main() {
|
||||||
the.CwtchApp = nil
|
the.CwtchApp = nil
|
||||||
the.CwtchService = nil
|
the.CwtchService = nil
|
||||||
os.MkdirAll(the.CwtchDir, 0700)
|
os.MkdirAll(the.CwtchDir, 0700)
|
||||||
|
os.MkdirAll(path.Join(the.CwtchDir, "tor"), 0700)
|
||||||
|
|
||||||
if *flagService {
|
if *flagService {
|
||||||
mainService()
|
mainService()
|
||||||
|
@ -185,6 +186,8 @@ func mainUi(flagLocal bool, flagClientUI bool) {
|
||||||
gcd.SetBuildDate("now")
|
gcd.SetBuildDate("now")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// this is to load local qml files quickly when developing
|
// this is to load local qml files quickly when developing
|
||||||
var qmlSource *core.QUrl
|
var qmlSource *core.QUrl
|
||||||
if flagLocal {
|
if flagLocal {
|
||||||
|
|
15
qml/main.qml
15
qml/main.qml
|
@ -141,18 +141,21 @@ ApplicationWindow {
|
||||||
|
|
||||||
StackLayout {
|
StackLayout {
|
||||||
id: parentStack
|
id: parentStack
|
||||||
currentIndex: managementPane
|
|
||||||
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.bottom: statusbar.top
|
|
||||||
anchors.top: toolbar.bottom
|
|
||||||
|
|
||||||
readonly property int managementPane: 0
|
readonly property int managementPane: 0
|
||||||
readonly property int settingsPane: 1
|
readonly property int settingsPane: 1
|
||||||
readonly property int addEditProfilePane: 2
|
readonly property int addEditProfilePane: 2
|
||||||
readonly property int profilePane: 3
|
readonly property int profilePane: 3
|
||||||
readonly property int addEditServerPane: 4
|
readonly property int addEditServerPane: 4
|
||||||
|
|
||||||
|
currentIndex: gcd.firstTime ? parentStack.settingsPane : parentStack.managementPane
|
||||||
|
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.bottom: statusbar.top
|
||||||
|
anchors.top: toolbar.bottom
|
||||||
|
|
||||||
|
|
||||||
property alias pane: parentStack.currentIndex
|
property alias pane: parentStack.currentIndex
|
||||||
|
|
||||||
Rectangle { // Profile login/management pane
|
Rectangle { // Profile login/management pane
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 3b9675e25917f667ba4b1cf1db09d44ed328f010
|
Subproject commit d6b56d02780c675a0acc36bd0f9ae65a42789077
|
|
@ -20,7 +20,7 @@ Opaque.SettingsList { // groupSettingsPane
|
||||||
property bool connected: false
|
property bool connected: false
|
||||||
property bool synced: false
|
property bool synced: false
|
||||||
|
|
||||||
settings: Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
Opaque.Setting {
|
Opaque.Setting {
|
||||||
|
|
|
@ -16,12 +16,17 @@ Opaque.SettingsList { // settingsPane
|
||||||
id: root
|
id: root
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.topMargin: 20
|
anchors.topMargin: 20
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
contentHeight: peerSettings.height + 20
|
||||||
property string authorization
|
property string authorization
|
||||||
property string saveHistory
|
property string saveHistory
|
||||||
|
|
||||||
settings: Column {
|
Column {
|
||||||
anchors.fill: parent
|
id: peerSettings
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
width:parent.width -20
|
||||||
|
parent: root.contentItem
|
||||||
|
|
||||||
Opaque.Setting {
|
Opaque.Setting {
|
||||||
inline: false
|
inline: false
|
||||||
|
|
|
@ -34,8 +34,8 @@ Opaque.SettingsList { // Add Profile Pane
|
||||||
serverAddEditPane.server_messages = server_messages;
|
serverAddEditPane.server_messages = server_messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
settings: Column {
|
|
||||||
|
|
||||||
|
Column {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
width: 700
|
width: 700
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ Opaque.SettingsList { // groupSettingsPane
|
||||||
property bool connected: false
|
property bool connected: false
|
||||||
property bool synced: false
|
property bool synced: false
|
||||||
|
|
||||||
settings: Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
Opaque.Setting {
|
Opaque.Setting {
|
||||||
|
|
|
@ -16,13 +16,19 @@ import "../utils.js" as Utils
|
||||||
Opaque.SettingsList { // settingsPane
|
Opaque.SettingsList { // settingsPane
|
||||||
id: root
|
id: root
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
anchors.topMargin: 20
|
anchors.topMargin: 20
|
||||||
|
contentHeight: settings.height
|
||||||
|
|
||||||
settings: Column {
|
Column {
|
||||||
|
id: settings
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
width: parent.width - 20
|
width:parent.width -20
|
||||||
|
parent:root.contentItem
|
||||||
|
|
||||||
Opaque.Setting {
|
Opaque.Setting {
|
||||||
|
|
||||||
//: Language
|
//: Language
|
||||||
label: qsTr("setting-language")
|
label: qsTr("setting-language")
|
||||||
|
|
||||||
|
@ -149,6 +155,19 @@ Opaque.SettingsList { // settingsPane
|
||||||
name: "groups_enabled"
|
name: "groups_enabled"
|
||||||
experiment_id: "tapir-groups-experiment"
|
experiment_id: "tapir-groups-experiment"
|
||||||
}
|
}
|
||||||
|
Opaque.ScalingLabel {
|
||||||
|
id: versionLabel
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
//: Version %1
|
||||||
|
text: qsTr("version %1").arg(gcd.version)
|
||||||
|
}
|
||||||
|
|
||||||
|
Opaque.ScalingLabel {
|
||||||
|
id: builddateLabel
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
//: Built on: %2
|
||||||
|
text: qsTr("builddate %2").arg(gcd.buildDate)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,22 +196,7 @@ Opaque.SettingsList { // settingsPane
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Opaque.ScalingLabel {
|
|
||||||
id: versionLabel
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.bottom: builddateLabel.top
|
|
||||||
//: Version %1
|
|
||||||
text: qsTr("version %1").arg(gcd.version)
|
|
||||||
}
|
|
||||||
|
|
||||||
Opaque.ScalingLabel {
|
|
||||||
id: builddateLabel
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: 10
|
|
||||||
//: Built on: %2
|
|
||||||
text: qsTr("builddate %2").arg(gcd.buildDate)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//end of flickable
|
//end of flickable
|
||||||
|
|
|
@ -174,7 +174,7 @@ Rectangle {
|
||||||
|
|
||||||
Image { // ACKNOWLEDGEMENT ICON
|
Image { // ACKNOWLEDGEMENT ICON
|
||||||
id: ack
|
id: ack
|
||||||
source: root.error != "" ? gcd.assetPath + "core/fontawesome/regular/window-close.webp" : (root.ackd ? gcd.assetPath + "core/fontawesome/regular/check-circle.svg" : gcd.assetPath + "core/fontawesome/regular/hourglass.svg")
|
source: root.error != "" ? gcd.assetPath + "core/fontawesome/regular/window-close.webp" : (root.ackd ? gcd.assetPath + "core/fontawesome/regular/check-circle.webp" : gcd.assetPath + "core/fontawesome/regular/hourglass.svg")
|
||||||
height: Theme.chatMetaTextSize * gcd.themeScale
|
height: Theme.chatMetaTextSize * gcd.themeScale
|
||||||
width: Theme.chatMetaTextSize * gcd.themeScale
|
width: Theme.chatMetaTextSize * gcd.themeScale
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
|
Reference in New Issue