This commit is contained in:
erinn 2021-11-02 15:05:01 -07:00
commit d5231b13db
9 changed files with 482 additions and 292 deletions

View File

@ -1,26 +0,0 @@
package constants
import "cwtch.im/cwtch/event"
// The server manager defines its own events, most should be self-explanatory:
const (
NewServer = event.Type("NewServer")
// Force a UI update
ListServers = event.Type("ListServers")
// Takes an Onion, used to toggle off/on Server availability
StartServer = event.Type("StartServer")
StopServer = event.Type("StopServer")
// Takes an Onion and a AutoStartEnabled boolean
AutoStart = event.Type("AutoStart")
// Get the status of a particular server (takes an Onion)
CheckServerStatus = event.Type("CheckServerStatus")
ServerStatusUpdate = event.Type("ServerStatusUpdate")
)
const (
AutoStartEnabled = event.Field("AutoStartEnabled")
)

View File

@ -22,8 +22,11 @@ func FunctionalityGate(experimentMap map[string]bool) (*Functionality, error) {
// 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)
err := peer.SendMessage(handle, message)
if err == nil {
return features.ConstructResponse(sendMessagePrefix, "success")
}
return features.ConstructResponse(sendMessagePrefix, err.Error())
}
// HandleImportString handles contact import strings

View File

@ -56,7 +56,7 @@ func (gf *GroupFunctionality) SendMessage(peer peer.CwtchPeer, handle string, me
return "", err
}
}
return peer.SendMessageToGroupTracked(handle, message)
return "", peer.SendMessage(handle, message)
}
// ValidPrefix returns true if an import string contains a prefix that indicates it contains information about a
@ -96,7 +96,7 @@ func (gf *GroupFunctionality) HandleImportString(peer peer.CwtchPeer, importStri
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") {
if err != nil && !strings.HasSuffix(err.Error(), "success") {
return features.ConstructResponse(importBundlePrefix, err.Error())
}
return gf.HandleImportString(peer, bundle[1])

View File

@ -0,0 +1,133 @@
package servers
import (
"cwtch.im/cwtch/event"
"fmt"
"git.openprivacy.ca/cwtch.im/libcwtch-go/constants"
"git.openprivacy.ca/cwtch.im/server"
"git.openprivacy.ca/openprivacy/connectivity"
"path"
"sync"
)
const serversExperiment = "servers-experiment"
const (
ZeroServersLoaded = event.Type("ZeroServersLoaded")
NewServer = event.Type("NewServer")
ServerIntentUpdate = event.Type("ServerIntentUpdate")
)
const (
Intent = event.Field("Intent")
)
const (
IntentRunning = "running"
IntentStopped = "stopped"
)
type ServerInfo struct {
Onion string
ServerBundle string
Autostart bool
Running bool
Description string
StorageType string
}
var lock sync.Mutex
var appServers server.Servers
func InitServers(acn connectivity.ACN, appdir string) {
lock.Lock()
defer lock.Unlock()
if appServers == nil {
appServers = server.NewServers(acn, path.Join(appdir, "servers"))
appServers.LoadServers(constants.DefactoPasswordForUnencryptedProfiles)
}
}
func DeactivateServers() {
lock.Lock()
defer lock.Unlock()
if appServers == nil {
appServers.Stop()
}
}
// ServersFunctionality provides experiment gated server functionality
type ServersFunctionality struct {
}
// ExperimentGate returns ServersFunctionality if the experiment is enabled, and an error otherwise.
func ExperimentGate(experimentMap map[string]bool) (*ServersFunctionality, error) {
if experimentMap[serversExperiment] {
lock.Lock()
defer lock.Unlock()
return &ServersFunctionality{}, nil
}
return nil, fmt.Errorf("gated by %v", serversExperiment)
}
func (sf *ServersFunctionality) LoadServers(password string) ([]string, error) {
return appServers.LoadServers(password)
}
func (sf *ServersFunctionality) CreateServer(password string) (server.Server, error) {
return appServers.CreateServer(password)
}
func (sf *ServersFunctionality) GetServer(onion string) server.Server {
return appServers.GetServer(onion)
}
func (sf *ServersFunctionality) ListServers() []string {
return appServers.ListServers()
}
func (sf *ServersFunctionality) DeleteServer(onion string, currentPassword string) error {
return appServers.DeleteServer(onion, currentPassword)
}
func (sf *ServersFunctionality) LaunchServer(onion string) {
appServers.LaunchServer(onion)
}
func (sf *ServersFunctionality) StopServer(onion string) {
appServers.StopServer(onion)
}
func (sf *ServersFunctionality) DestroyServers() {
appServers.Destroy()
}
func (sf *ServersFunctionality) GetServerInfo(onion string) *ServerInfo {
s := sf.GetServer(onion)
var serverInfo ServerInfo
serverInfo.Onion = s.Onion()
serverInfo.ServerBundle = s.ServerBundle()
serverInfo.Autostart = s.GetAttribute(server.AttrAutostart) == "true"
running, _ := s.CheckStatus()
serverInfo.Running = running
serverInfo.Description = s.GetAttribute(server.AttrDescription)
serverInfo.StorageType = s.GetAttribute(server.AttrStorageType)
return &serverInfo
}
func (si *ServerInfo) EnrichEvent(e *event.Event) {
e.Data["Onion"] = si.Onion
e.Data["ServerBundle"] = si.ServerBundle
e.Data["Description"] = si.Description
e.Data["StorageType"] = si.StorageType
if si.Autostart {
e.Data["Autostart"] = "true"
} else {
e.Data["Autostart"] = "false"
}
if si.Running {
e.Data["Running"] = "true"
} else {
e.Data["Running"] = "false"
}
}

5
go.mod
View File

@ -3,10 +3,11 @@ module git.openprivacy.ca/cwtch.im/libcwtch-go
go 1.15
require (
cwtch.im/cwtch v0.11.2
cwtch.im/cwtch v0.13.0
git.openprivacy.ca/cwtch.im/server v1.3.1
git.openprivacy.ca/openprivacy/connectivity v1.5.0
git.openprivacy.ca/openprivacy/log v1.0.3
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 // indirect
golang.org/x/mod v0.5.0 // indirect
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect
)
)

19
go.sum
View File

@ -1,7 +1,11 @@
cwtch.im/cwtch v0.11.2 h1:a38zZLSNsKJzLStBDOIoYKKIL56V9ueQvOZfnIEVc5I=
cwtch.im/cwtch v0.11.2/go.mod h1:QpTkQK7MqNt0dQK9/pBk5VpkvFhy6xuoxJIn401B8fM=
cwtch.im/cwtch v0.12.2 h1:I+ndKadCRCITw4SPbd+1cpRv+z/7iHjjTUv8OzRwTrE=
cwtch.im/cwtch v0.12.2/go.mod h1:QpTkQK7MqNt0dQK9/pBk5VpkvFhy6xuoxJIn401B8fM=
cwtch.im/cwtch v0.13.0 h1:9WhLG9czfyRceZnHSqfTAq0vfmDC/E20mziJb9+Skrg=
cwtch.im/cwtch v0.13.0/go.mod h1:QpTkQK7MqNt0dQK9/pBk5VpkvFhy6xuoxJIn401B8fM=
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
git.openprivacy.ca/cwtch.im/server v1.3.1 h1:Kt4TnlGxGPk1KTjvs1cXtnFWDx+hYqu8w+2eIaqt+F4=
git.openprivacy.ca/cwtch.im/server v1.3.1/go.mod h1:gps6glXDt/ra66Do491csrm0TwatAc2lMLOAKCkh5Vw=
git.openprivacy.ca/cwtch.im/tapir v0.4.9 h1:LXonlztwvI1F1++0IyomIcDH1/Bxzo+oN8YjGonNvjM=
git.openprivacy.ca/cwtch.im/tapir v0.4.9/go.mod h1:p4bHo3DAO8wwimU6JAeZXbfPQ4jnoA2bV+4YvknWTNQ=
git.openprivacy.ca/openprivacy/bine v0.0.4 h1:CO7EkGyz+jegZ4ap8g5NWRuDHA/56KKvGySR6OBPW+c=
@ -11,7 +15,6 @@ git.openprivacy.ca/openprivacy/connectivity v1.5.0/go.mod h1:UjQiGBnWbotmBzIw59B
git.openprivacy.ca/openprivacy/log v1.0.2/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
git.openprivacy.ca/openprivacy/log v1.0.3 h1:E/PMm4LY+Q9s3aDpfySfEDq/vYQontlvNj/scrPaga0=
git.openprivacy.ca/openprivacy/log v1.0.3/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
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=
@ -26,6 +29,8 @@ github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
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/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA=
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
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=
@ -36,7 +41,8 @@ github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
github.com/struCoder/pidusage v0.2.1 h1:dFiEgUDkubeIj0XA1NpQ6+8LQmKrLi7NiIQl86E6BoY=
github.com/struCoder/pidusage v0.2.1/go.mod h1:bewtP2KUA1TBUyza5+/PCpSQ6sc/H6jJbIKAzqW86BA=
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=
@ -46,10 +52,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
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 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
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 h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 h1:3In5TnfvnuXTF/uflgpYxSCEGP2NdYT37KsPh3VjZYU=
@ -65,7 +69,6 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY
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-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
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=
@ -84,11 +87,9 @@ 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.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
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-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
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=

374
lib.go
View File

@ -25,9 +25,12 @@ import (
"git.openprivacy.ca/cwtch.im/libcwtch-go/constants"
contact "git.openprivacy.ca/cwtch.im/libcwtch-go/features/contacts"
"git.openprivacy.ca/cwtch.im/libcwtch-go/features/groups"
"git.openprivacy.ca/cwtch.im/libcwtch-go/utils"
"git.openprivacy.ca/cwtch.im/libcwtch-go/features/servers"
"git.openprivacy.ca/cwtch.im/server"
"git.openprivacy.ca/openprivacy/connectivity"
"git.openprivacy.ca/cwtch.im/libcwtch-go/utils"
"encoding/base64"
mrand "math/rand"
"os"
@ -45,6 +48,7 @@ const (
)
var application app.Application
var globalAppDir string
var eventHandler *utils.EventHandler
var globalACN connectivity.ACN
@ -61,9 +65,9 @@ type ChatMessage struct {
//export c_StartCwtch
func c_StartCwtch(dir_c *C.char, len C.int, tor_c *C.char, torLen C.int) C.int {
dir := C.GoStringN(dir_c, len)
tor := C.GoStringN(tor_c, torLen)
return C.int(StartCwtch(dir, tor))
applicationDirectory := C.GoStringN(dir_c, len)
torDirectory := C.GoStringN(tor_c, torLen)
return C.int(StartCwtch(applicationDirectory, torDirectory))
}
// StartCwtch starts cwtch in the library and initlaizes all data structures
@ -75,8 +79,8 @@ func c_StartCwtch(dir_c *C.char, len C.int, tor_c *C.char, torLen C.int) C.int {
func StartCwtch(appDir string, torPath string) int {
if logfile := os.Getenv("LOG_FILE"); logfile != "" {
filelog, err := log.NewFile(log.LevelInfo, logfile)
filelog.SetUseColor(false)
if err == nil {
filelog.SetUseColor(false)
log.SetStd(filelog)
} else {
// not so likely to be seen since we're usually creating file log in situations we can't access console logs...
@ -86,7 +90,8 @@ func StartCwtch(appDir string, torPath string) int {
if runtime.GOOS == "android" {
log.SetUseColor(false)
}
log.SetLevel(log.LevelInfo)
log.AddEverythingFromPattern("libcwtch-go")
if logLevel := os.Getenv("LOG_LEVEL"); strings.ToLower(logLevel) == "debug" {
log.SetLevel(log.LevelDebug)
}
@ -139,7 +144,11 @@ func _startCwtch(appDir string, torPath string) {
eventHandler.PublishAppEvent(event.NewEventList(utils.CwtchStartError, event.Error, fmt.Sprintf("Error creating appDir %v: %v", appDir, err)))
return
}
utils.InitGlobalSettingsFile(appDir, constants.DefactoPasswordForUnencryptedProfiles)
err = utils.InitGlobalSettingsFile(appDir, constants.DefactoPasswordForUnencryptedProfiles)
if err != nil {
log.Errorf("error initializing global settings file %. Global settings might not be loaded or saves", err)
}
log.Infof("Loading Cwtch Directory %v and tor path: %v", appDir, torPath)
@ -155,16 +164,32 @@ func _startCwtch(appDir string, torPath string) {
}
log.Infof("making directory %v", appDir)
os.MkdirAll(filepath.Join(appDir, "tor"), 0700)
tor.NewTorrc().WithSocksPort(port).WithOnionTrafficOnly().WithControlPort(controlPort).WithHashedPassword(base64.StdEncoding.EncodeToString(key)).Build(filepath.Join(appDir, "tor", "torrc"))
err = os.MkdirAll(filepath.Join(appDir, "tor"), 0700)
if err != nil {
log.Errorf("error creating tor data directory: %v. Aborting app start up", err)
eventHandler.PublishAppEvent(event.NewEventList(utils.CwtchStartError, event.Error, fmt.Sprintf("Error connecting to Tor: %v", err)))
return
}
err = tor.NewTorrc().WithSocksPort(port).WithOnionTrafficOnly().WithControlPort(controlPort).WithHashedPassword(base64.StdEncoding.EncodeToString(key)).Build(filepath.Join(appDir, "tor", "torrc"))
if err != nil {
log.Errorf("error constructing torrc: %v", err)
eventHandler.PublishAppEvent(event.NewEventList(utils.CwtchStartError, event.Error, fmt.Sprintf("Error connecting to Tor: %v", err)))
return
}
acn, err := tor.NewTorACNWithAuth(appDir, torPath, controlPort, tor.HashedPasswordAuthenticator{Password: base64.StdEncoding.EncodeToString(key)})
if err != nil {
log.Errorf("Error connecting to Tor replacing with ErrorACN: %v\n", err)
eventHandler.PublishAppEvent(event.NewEventList(utils.CwtchStartError, event.Error, fmt.Sprintf("Error connecting to Tor: %v", err)))
return
}
globalAppDir = appDir
globalACN = acn
newApp := app.NewApp(acn, appDir)
servers.InitServers(acn, appDir)
eventHandler.HandleApp(newApp)
@ -189,6 +214,8 @@ func _startCwtch(appDir string, torPath string) {
newApp.LoadProfiles(constants.DefactoPasswordForUnencryptedProfiles)
application = newApp
publishLoadedServers()
LaunchServers()
// Send global settings to the UI...
application.GetPrimaryBus().Publish(event.NewEvent(utils.UpdateGlobalSettings, map[event.Field]string{event.Data: string(settingsJson)}))
@ -212,14 +239,14 @@ func ReconnectCwtchForeground() {
}
// populate profile list
peerList := application.ListPeers()
for onion := range peerList {
peerList := application.ListProfiles()
for _, onion := range peerList {
eventHandler.Push(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: onion, event.Created: event.False, "Reload": event.True}))
}
settings := utils.ReadGlobalSettings()
for profileOnion := range peerList {
for _, profileOnion := range peerList {
// fix peerpeercontact message counts
contactList := application.GetPeer(profileOnion).GetContacts()
for _, handle := range contactList {
@ -237,7 +264,7 @@ func ReconnectCwtchForeground() {
// fix peergroupcontact message counts
groupList := application.GetPeer(profileOnion).GetGroups()
for _, groupID := range groupList {
totalMessages := application.GetPeer(profileOnion).GetGroup(groupID).Timeline.Len() + len(application.GetPeer(profileOnion).GetGroup(groupID).UnacknowledgedMessages)
totalMessages := len(application.GetPeer(profileOnion).GetGroup(groupID).GetTimeline())
eventHandler.Push(event.NewEvent(event.MessageCounterResync, map[event.Field]string{
event.Identity: profileOnion,
event.GroupID: groupID,
@ -249,9 +276,10 @@ func ReconnectCwtchForeground() {
serversListBytes, _ := json.Marshal(serverListForOnion)
eventHandler.Push(event.NewEvent(groups.UpdateServerInfo, map[event.Field]string{"ProfileOnion": profileOnion, groups.ServerList: string(serversListBytes)}))
}
}
publishLoadedServers()
settingsJson, _ := json.Marshal(settings)
application.GetPrimaryBus().Publish(event.NewEvent(utils.UpdateGlobalSettings, map[event.Field]string{event.Data: string(settingsJson)}))
application.GetPrimaryBus().Publish(event.NewEvent(utils.CwtchStarted, map[event.Field]string{}))
@ -259,6 +287,19 @@ func ReconnectCwtchForeground() {
application.QueryACNVersion()
}
func publishLoadedServers() {
serversHandler, err := servers.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
serversList := serversHandler.ListServers()
for _, server := range serversList {
serverInfo := serversHandler.GetServerInfo(server)
ev := event.NewEvent(servers.NewServer, make(map[event.Field]string))
serverInfo.EnrichEvent(&ev)
application.GetPrimaryBus().Publish(ev)
}
}
}
//export c_SendAppEvent
// A generic method for Rebroadcasting App Events from a UI
func c_SendAppEvent(json_ptr *C.char, json_len C.int) {
@ -286,10 +327,21 @@ func SendAppEvent(eventJson string) {
log.Debugf("New Settings %v", globalSettings)
utils.WriteGlobalSettings(globalSettings)
// Group Experiment Refresh
groupHandler, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
settings := utils.ReadGlobalSettings()
_, err = servers.ExperimentGate(settings.Experiments)
if err == nil {
for profileOnion := range application.ListPeers() {
servers.InitServers(globalACN, globalAppDir)
publishLoadedServers()
LaunchServers()
} else {
servers.DeactivateServers()
}
// Group Experiment Refresh
groupHandler, err := groups.ExperimentGate(settings.Experiments)
if err == nil {
for _, profileOnion := range application.ListProfiles() {
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)}))
@ -298,19 +350,19 @@ func SendAppEvent(eventJson string) {
// Explicitly toggle blocking/unblocking of unknown connections for profiles
// that have been loaded.
if utils.ReadGlobalSettings().BlockUnknownConnections {
for onion := range application.ListPeers() {
if settings.BlockUnknownConnections {
for _, onion := range application.ListProfiles() {
application.GetPeer(onion).BlockUnknownConnections()
}
} else {
for onion := range application.ListPeers() {
for _, onion := range application.ListProfiles() {
application.GetPeer(onion).AllowUnknownConnections()
}
}
case utils.SetLoggingLevel:
_, warn := new_event.Data[utils.Warn]
_, error := new_event.Data[utils.Error]
_, err := 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
@ -319,7 +371,7 @@ func SendAppEvent(eventJson string) {
log.SetLevel(log.LevelInfo)
} else if warn {
log.SetLevel(log.LevelWarn)
} else if error {
} else if err {
log.SetLevel(log.LevelError)
} else if debug {
log.SetLevel(log.LevelDebug)
@ -366,7 +418,7 @@ func SendProfileEvent(onion string, eventJson string) {
eventHandler.Push(event.NewEvent(event.AppError, map[event.Field]string{event.Data: err.Error()}))
// DEPRECATED: use SetProfileAttribute()
case event.SetAttribute:
peer.SetAttribute(new_event.Data[event.Key], new_event.Data[event.Data])
log.Errorf("SetAttribute is deprecated.")
// DEPRECATED: use SetContactAttribute()
case event.SetPeerAttribute:
peer.SetContactAttribute(new_event.Data[event.RemotePeer], new_event.Data[event.Key], new_event.Data[event.Data])
@ -405,12 +457,6 @@ func GetAppBusEvent() string {
return json
}
type Profile struct {
Name string `json:"name"`
Onion string `json:"onion"`
ImagePath string `json:"imagePath"`
}
//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))
@ -525,7 +571,7 @@ type EnhancedMessage struct {
ContactImage string
}
func GetMessage(profileOnion, handle string, message_index int) string {
func GetMessage(profileOnion, handle string, messageIndex int) string {
var message EnhancedMessage
// There is an edge case that can happen on Android when the app is shutdown while fetching messages...
// The worker threads that are spawned can become activated again when the app is opened attempt to finish their job...
@ -537,28 +583,25 @@ func GetMessage(profileOnion, handle string, message_index int) string {
ph := utils.NewPeerHelper(profile)
if ph.IsGroup(handle) {
if profile.GetGroup(handle) != nil {
// If we are safely within the limits of the timeline just grab the message at the index..
if len(profile.GetGroup(handle).Timeline.Messages) > message_index {
message.Message = profile.GetGroup(handle).Timeline.Messages[message_index]
exists, timelineMessage, length := profile.GetGroup(handle).GetMessage(messageIndex)
if exists {
message.Message = timelineMessage
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..")
}
eventHandler.Push(event.NewEvent(event.MessageCounterResync, map[event.Field]string{
event.Identity: profileOnion,
event.GroupID: handle,
event.Data: strconv.Itoa(length),
}))
log.Errorf("Couldn't find message in timeline %v / %v or unacked messages, probably transient threading issue, but logging for visibility..", messageIndex, length)
}
}
} else {
if profile.GetContact(handle) != nil {
// If we are safely within the limits of the timeline just grab the message at the index..
if len(profile.GetContact(handle).Timeline.Messages) > message_index {
message.Message = profile.GetContact(handle).Timeline.Messages[message_index]
if len(profile.GetContact(handle).Timeline.Messages) > messageIndex {
message.Message = profile.GetContact(handle).Timeline.Messages[messageIndex]
message.ContactImage = ph.GetProfilePic(handle)
} else {
// Otherwise Send a counter resync event...this shouldn't really happen for p2p messages so we
@ -672,17 +715,20 @@ func SendInvitation(profileOnion, handle, target string) {
func c_ShareFile(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, filepath_ptr *C.char, filepath_len C.int) {
profile := C.GoStringN(profile_ptr, profile_len)
handle := C.GoStringN(handle_ptr, handle_len)
filepath := C.GoStringN(filepath_ptr, filepath_len)
ShareFile(profile, handle, filepath)
sharefilepath := C.GoStringN(filepath_ptr, filepath_len)
ShareFile(profile, handle, sharefilepath)
}
func ShareFile(profileOnion, handle, filepath string) {
func ShareFile(profileOnion, handle, sharefilepath string) {
profile := application.GetPeer(profileOnion)
fh, err := filesharing.FunctionalityGate(utils.ReadGlobalSettings().Experiments)
if err != nil {
log.Errorf("file sharing error: %v", err)
} else {
fh.ShareFile(filepath, profile, handle)
err = fh.ShareFile(sharefilepath, profile, handle)
if err != nil {
log.Errorf("error sharing file: %v", err)
}
}
}
@ -690,10 +736,10 @@ func ShareFile(profileOnion, handle, filepath string) {
func c_DownloadFile(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, filepath_ptr *C.char, filepath_len C.int, manifestpath_ptr *C.char, manifestpath_len C.int, filekey_ptr *C.char, filekey_len C.int) {
profile := C.GoStringN(profile_ptr, profile_len)
handle := C.GoStringN(handle_ptr, handle_len)
filepath := C.GoStringN(filepath_ptr, filepath_len)
downloadfilepath := C.GoStringN(filepath_ptr, filepath_len)
manifestpath := C.GoStringN(manifestpath_ptr, manifestpath_len)
filekey := C.GoStringN(filekey_ptr, filekey_len)
DownloadFile(profile, handle, filepath, manifestpath, filekey)
DownloadFile(profile, handle, downloadfilepath, manifestpath, filekey)
}
func DownloadFile(profileOnion, handle, filepath, manifestpath, filekey string) {
@ -713,7 +759,7 @@ func c_CheckDownloadStatus(profilePtr *C.char, profileLen C.int, fileKeyPtr *C.c
func CheckDownloadStatus(profileOnion, fileKey string) {
profile := application.GetPeer(profileOnion)
if path, exists := profile.GetAttribute(attr.GetLocalScope(fmt.Sprintf("%s.complete", fileKey))); exists {
if path, exists := profile.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.complete", fileKey)); exists {
eventHandler.Push(event.NewEvent(event.FileDownloaded, map[event.Field]string{
ProfileOnion: profileOnion,
event.FileKey: fileKey,
@ -748,17 +794,17 @@ func c_CreateGroup(profile_ptr *C.char, profile_len C.int, server_ptr *C.char, s
}
// 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)
func CreateGroup(profileHandle string, server string, name string) {
profile := application.GetPeer(profileHandle)
_, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
gid, _, err := peer.StartGroup(server)
gid, _, err := profile.StartGroup(server)
if err == nil {
log.Debugf("created group %v on %v: $v", profile, server, gid)
log.Debugf("created group %v on %v: $v", profileHandle, server, gid)
// set the group name
peer.SetGroupAttribute(gid, attr.GetLocalScope("name"), name)
profile.SetGroupAttribute(gid, attr.GetLocalScope("name"), name)
} else {
log.Errorf("error creating group or %v on server %v: %v", profile, server, err)
log.Errorf("error creating group or %v on server %v: %v", profileHandle, server, err)
}
}
}
@ -782,20 +828,20 @@ func DeleteProfile(profile string, password string) {
}
//export c_ArchiveConversation
func c_ArchiveConversation(profile_ptr *C.char, profile_len C.int, contact_ptr *C.char, contact_len C.int) {
func c_ArchiveConversation(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int) {
profile := C.GoStringN(profile_ptr, profile_len)
contact := C.GoStringN(contact_ptr, contact_len)
ArchiveConversation(profile, contact)
handle := C.GoStringN(handle_ptr, handle_len)
ArchiveConversation(profile, handle)
}
// ArchiveConversation sets the conversation to archived
func ArchiveConversation(profile string, handle string) {
peer := application.GetPeer(profile)
ph := utils.NewPeerHelper(peer)
func ArchiveConversation(profileHandle string, handle string) {
profile := application.GetPeer(profileHandle)
ph := utils.NewPeerHelper(profile)
if ph.IsGroup(handle) {
peer.SetGroupAttribute(handle, attr.GetLocalScope(constants.Archived), event.True)
profile.SetGroupAttribute(handle, attr.GetLocalScope(constants.Archived), event.True)
} else {
peer.SetContactAttribute(handle, attr.GetLocalScope(constants.Archived), event.True)
profile.SetContactAttribute(handle, attr.GetLocalScope(constants.Archived), event.True)
}
}
@ -807,16 +853,16 @@ func c_DeleteContact(profile_ptr *C.char, profile_len C.int, hanlde_ptr *C.char,
}
// DeleteContact removes all trace of the contact from the profile
func DeleteContact(profile string, handle string) {
peer := application.GetPeer(profile)
ph := utils.NewPeerHelper(peer)
func DeleteContact(profileHandle string, handle string) {
profile := application.GetPeer(profileHandle)
ph := utils.NewPeerHelper(profile)
if ph.IsGroup(handle) {
_, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
peer.DeleteGroup(handle)
profile.DeleteGroup(handle)
}
} else {
peer.DeleteContact(handle)
profile.DeleteContact(handle)
}
}
@ -857,10 +903,23 @@ func c_SetProfileAttribute(profile_ptr *C.char, profile_len C.int, key_ptr *C.ch
SetProfileAttribute(profileOnion, key, value)
}
// SetProfileAttribute provides a wrapper around profile.SetAttribute
// SetProfileAttribute provides a wrapper around profile.SetScopedZonedAttribute
// WARNING: Because this function is potentially dangerous all keys and zones must be added
// explicitly. If you are attempting to added behaviour to the UI that requires the existence of new keys
// you probably want to be building out functionality/subsystem in the UI itself.
// Key must have the format zone.key where Zone is defined in Cwtch. Unknown zones are not permitted.
func SetProfileAttribute(profileOnion string, key string, value string) {
profile := application.GetPeer(profileOnion)
profile.SetAttribute(key, value)
zone, key := attr.ParseZone(key)
// TODO We only allow public.profile.zone to be set for now.
// All other scopes and zones need to be added explicitly or handled by Cwtch.
if zone == attr.ProfileZone && key == constants.Name {
profile.SetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.Name, value)
} else {
log.Errorf("attempted to set an attribute with an unknown zone: %v", key)
}
}
//export c_SetContactAttribute
@ -920,5 +979,174 @@ func ShutdownCwtch() {
}
}
//***** Server APIs *****
//export c_LoadServers
func c_LoadServers(passwordPtr *C.char, passwordLen C.int) {
LoadServers(C.GoStringN(passwordPtr, passwordLen))
}
func LoadServers(password string) {
serversHandler, err := servers.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
serversList, err := serversHandler.LoadServers(password)
if err != nil {
log.Errorf("Error attempting to load servers :%s\n", err)
application.GetPrimaryBus().Publish(event.NewEventList(servers.ZeroServersLoaded))
} else if len(serversList) == 0 {
application.GetPrimaryBus().Publish(event.NewEventList(servers.ZeroServersLoaded))
} else {
for _, serverOnion := range serversList {
serverInfo := serversHandler.GetServerInfo(serverOnion)
log.Debugf("Load Server NewServer event: %s", serverInfo)
ev := event.NewEvent(servers.NewServer, make(map[event.Field]string))
serverInfo.EnrichEvent(&ev)
application.GetPrimaryBus().Publish(ev)
if serverInfo.Autostart {
LaunchServer(serverOnion)
}
}
}
}
}
//export c_CreateServer
func c_CreateServer(passwordPtr *C.char, passwordLen C.int, descPtr *C.char, descLen C.int, autostart C.char) {
CreateServer(C.GoStringN(passwordPtr, passwordLen), C.GoStringN(descPtr, descLen), autostart == 1)
}
func CreateServer(password string, description string, autostart bool) {
serversHandler, err := servers.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
s, err := serversHandler.CreateServer(password)
if err != nil {
log.Errorf("Could not create new server: %s\n", err)
} else {
s.SetAttribute(server.AttrDescription, description)
if autostart {
s.SetAttribute(server.AttrAutostart, "true")
LaunchServer(s.Onion())
} else {
s.SetAttribute(server.AttrAutostart, "false")
}
if password == constants.DefactoPasswordForUnencryptedProfiles {
s.SetAttribute(server.AttrStorageType, server.StorageTypeDefaultPassword)
} else {
s.SetAttribute(server.AttrStorageType, server.StorageTypePassword)
}
serverInfo := serversHandler.GetServerInfo(s.Onion())
log.Debugf("Creating Server NewServer event: %s", serverInfo)
ev := event.NewEvent(servers.NewServer, make(map[event.Field]string))
serverInfo.EnrichEvent(&ev)
application.GetPrimaryBus().Publish(ev)
}
}
}
//export c_DeleteServer
func c_DeleteServer(onionPtr *C.char, onionLen C.int, currentPasswordPtr *C.char, currentPasswordLen C.int) {
DeleteServer(C.GoStringN(onionPtr, onionLen), C.GoStringN(currentPasswordPtr, currentPasswordLen))
}
func DeleteServer(onion string, currentPassword string) {
serversHandler, err := servers.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
serversHandler.StopServer(onion)
application.GetPrimaryBus().Publish(event.NewEventList(servers.ServerIntentUpdate, event.Identity, onion, servers.Intent, servers.IntentStopped))
serversHandler.DeleteServer(onion, currentPassword)
// TODO HANDLE err from DeleteServer?
}
}
//export c_LaunchServers
func c_LaunchServers() {
LaunchServers()
}
func LaunchServers() {
serversHandler, err := servers.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
for _, onion := range serversHandler.ListServers() {
autostart := false
if s := serversHandler.GetServer(onion); s != nil {
autostart = s.GetAttribute(server.AttrAutostart) == "true"
}
if autostart {
LaunchServer(onion)
}
}
}
}
//export c_LaunchServer
func c_LaunchServer(onionPtr *C.char, onionLen C.int) {
LaunchServer(C.GoStringN(onionPtr, onionLen))
}
func LaunchServer(onion string) {
serversHandler, err := servers.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
serversHandler.LaunchServer(onion)
application.GetPrimaryBus().Publish(event.NewEventList(servers.ServerIntentUpdate, event.Identity, onion, servers.Intent, servers.IntentRunning))
}
}
//export c_StopServer
func c_StopServer(onionPtr *C.char, onionLen C.int) {
StopServer(C.GoStringN(onionPtr, onionLen))
}
func StopServer(onion string) {
serversHandler, err := servers.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
serversHandler.StopServer(onion)
application.GetPrimaryBus().Publish(event.NewEventList(servers.ServerIntentUpdate, event.Identity, onion, servers.Intent, servers.IntentStopped))
}
}
//export c_StopServers
func c_StopServers() {
StopServers()
}
func StopServers() {
serversHandler, err := servers.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
for _, onion := range serversHandler.ListServers() {
StopServer(onion)
}
}
}
//export c_DestroyServers
func c_DestroyServers() {
DestroyServers()
}
func DestroyServers() {
serversHandler, err := servers.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
serversHandler.DestroyServers()
}
}
//export c_SetServerAttribute
func c_SetServerAttribute(onionPtr *C.char, onionLen C.int, keyPtr *C.char, keyLen C.int, valPtr *C.char, valLen C.int) {
SetServerAttribute(C.GoStringN(onionPtr, onionLen), C.GoStringN(keyPtr, keyLen), C.GoStringN(valPtr, valLen))
}
func SetServerAttribute(onion string, key string, val string) {
serversHandler, err := servers.ExperimentGate(utils.ReadGlobalSettings().Experiments)
if err == nil {
server := serversHandler.GetServer(onion)
if server != nil {
server.SetAttribute(key, val)
}
}
}
// ***** END Server APIs *****
// Leave as is, needed by ffi
func main() {}

View File

@ -5,10 +5,12 @@ import (
"cwtch.im/cwtch/app/plugins"
"cwtch.im/cwtch/model"
"cwtch.im/cwtch/model/attr"
"cwtch.im/cwtch/model/constants"
"cwtch.im/cwtch/protocol/connections"
"encoding/json"
"git.openprivacy.ca/cwtch.im/libcwtch-go/constants"
constants2 "git.openprivacy.ca/cwtch.im/libcwtch-go/constants"
"git.openprivacy.ca/cwtch.im/libcwtch-go/features/groups"
"git.openprivacy.ca/cwtch.im/libcwtch-go/features/servers"
"git.openprivacy.ca/openprivacy/log"
"strconv"
)
@ -48,6 +50,8 @@ func (eh *EventHandler) HandleApp(application app.Application) {
application.GetPrimaryBus().Subscribe(event.ACNVersion, eh.appBusQueue)
application.GetPrimaryBus().Subscribe(UpdateGlobalSettings, eh.appBusQueue)
application.GetPrimaryBus().Subscribe(CwtchStarted, eh.appBusQueue)
application.GetPrimaryBus().Subscribe(servers.NewServer, eh.appBusQueue)
application.GetPrimaryBus().Subscribe(servers.ServerIntentUpdate, eh.appBusQueue)
}
func (eh *EventHandler) GetNextEvent() string {
@ -68,7 +72,7 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
switch e.EventType {
case event.ACNStatus:
if e.Data[event.Progress] == "100" {
for onion := range eh.app.ListPeers() {
for _, onion := range eh.app.ListProfiles() {
// 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()
@ -83,22 +87,15 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
eh.startHandlingPeer(onion)
}
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
}
// CwtchPeer will always set this now...
tag, _ := profile.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants.Tag)
e.Data[constants.Tag] = tag
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)))
profile.SetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants2.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)
profile.SetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants2.PeerOnline, event.False)
eh.app.AddPeerPlugin(onion, plugins.CONNECTIONRETRY)
eh.app.AddPeerPlugin(onion, plugins.NETWORKCHECK)
@ -119,12 +116,7 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
}
}
nick, exists := profile.GetAttribute(attr.GetPublicScope(constants.Name))
if !exists {
nick = onion
}
picVal, ok := profile.GetAttribute(attr.GetPublicScope(constants.Picture))
picVal, ok := profile.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants2.Picture)
if !ok {
picVal = ImageToString(NewImage(RandomProfileImage(onion), TypeImageDistro))
}
@ -134,12 +126,14 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
}
picPath := GetPicturePath(pic)
//tag, _ := profile.GetAttribute(app.AttributeTag)
// Set publicly scopes attributes
profile.SetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants2.Picture, picPath)
online, _ := profile.GetAttribute(attr.GetLocalScope(constants.PeerOnline))
online, _ := profile.GetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants2.PeerOnline)
e.Data[constants.Name] = nick
e.Data[constants.Picture] = picPath
// Name always exists
e.Data[constants.Name], _ = profile.GetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.Name)
e.Data[constants2.Picture] = picPath
e.Data["Online"] = online
var contacts []Contact
@ -166,7 +160,7 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
if !set {
saveHistory = event.DeleteHistoryDefault
}
isArchived, set := contactInfo.GetAttribute(attr.GetLocalScope(constants.Archived))
isArchived, set := contactInfo.GetAttribute(attr.GetLocalScope(constants2.Archived))
if !set {
isArchived = event.False
}
@ -201,7 +195,7 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
if group.Accepted {
authorization = model.AuthApproved
}
isArchived, set := group.GetAttribute(attr.GetLocalScope(constants.Archived))
isArchived, set := group.GetAttribute(attr.GetLocalScope(constants2.Archived))
if !set {
isArchived = event.False
}
@ -266,12 +260,12 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string {
// 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"])
peer.SetContactAttribute(ev.Event.Data["RemotePeer"], attr.GetLocalScope(constants.Archived), event.False)
peer.SetContactAttribute(ev.Event.Data["RemotePeer"], attr.GetLocalScope(constants2.Archived), event.False)
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])
peer.SetGroupAttribute(ev.Event.Data[event.GroupID], attr.GetLocalScope(constants.Archived), event.False)
peer.SetGroupAttribute(ev.Event.Data[event.GroupID], attr.GetLocalScope(constants2.Archived), event.False)
case event.PeerAcknowledgement:
// No enrichement required
case event.PeerCreated:
@ -313,8 +307,8 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string {
//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)
peer.SendScopedZonedGetValToContact(ev.Event.Data[event.RemotePeer], attr.PublicScope, attr.ProfileZone, constants.Name)
peer.SendScopedZonedGetValToContact(ev.Event.Data[event.RemotePeer], attr.PublicScope, attr.ProfileZone, constants2.Picture)
}
}
@ -326,9 +320,9 @@ func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string {
//val := ev.Event.Data[event.Data]
exists, _ := strconv.ParseBool(ev.Event.Data[event.Exists])
if exists && scope == attr.PublicScope {
if exists && attr.IntoScope(scope) == attr.PublicScope {
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
// we have a locally set override, don't pass this remote set public scope update to UI
return ""
}
}

View File

@ -114,7 +114,7 @@ func (p *PeerHelper) GetNick(id string) string {
// re-request if authenticated
// TODO: This check probably doesn't belong here...
if contact := p.peer.GetContact(id); contact != nil && contact.State == connections.ConnectionStateName[connections.AUTHENTICATED] {
p.peer.SendGetValToPeer(id, attr.PublicScope, constants.Name)
p.peer.SendScopedZonedGetValToContact(id, attr.PublicScope, attr.ProfileZone, constants.Name)
}
}
}
@ -202,74 +202,6 @@ func getLastMessageTime(tl *model.Timeline) int {
return int(tl.Messages[len(tl.Messages)-1].Timestamp.Unix())
}
/*
// AddProfile adds a new profile to the UI
func AddProfile(gcd *GrandCentralDispatcher, handle string) {
p := the.CwtchApp.GetPeer(handle)
if p != nil {
nick, exists := p.GetAttribute(attr.GetPublicScope(constants.Name))
if !exists {
nick = handle
}
picVal, ok := p.GetAttribute(attr.GetPublicScope(constants.Picture))
if !ok {
picVal = ImageToString(NewImage(RandomProfileImage(handle), TypeImageDistro))
}
pic, err := StringToImage(picVal)
if err != nil {
pic = NewImage(RandomProfileImage(handle), TypeImageDistro)
}
picPath := getPicturePath(pic)
tag, _ := p.GetAttribute(app.AttributeTag)
online, _ := p.GetAttribute(attr.GetLocalScope(constants.PeerOnline))
log.Debugf("AddProfile %v %v %v %v %v\n", handle, nick, picPath, tag, online)
gcd.AddProfile(handle, nick, picPath, tag, online == event.True)
}
}*/
/*
type manager struct {
gcd *GrandCentralDispatcher
profile string
}
// Manager is a middleware helper for entities like peer event listeners wishing to trigger ui changes (via the gcd)
// each manager is for one profile/peer
// manager takes minimal arguments and builds the full struct of data (usually pulled from a cwtch peer) required to call the GCD to perform the ui action
// manager also performs call filtering based on UI state: users of manager can safely always call it on events and not have to worry about weather the relevant ui is active
// ie: you can always safely call AddMessage even if in the ui a different profile is selected. manager will check with gcd, and if the correct conditions are not met, it will not call on gcd to update the ui incorrectly
type Manager interface {
Acknowledge(handle, mID string)
AddContact(Handle string)
AddSendMessageError(peer string, signature string, err string)
AddMessage(handle string, from string, message string, fromMe bool, messageID string, timestamp time.Time, Acknowledged bool)
ReloadProfiles()
UpdateContactDisplayName(handle string)
UpdateContactPicture(handle string)
UpdateContactStatus(handle string, status int, loading bool)
UpdateContactAttribute(handle, key, value string)
ChangePasswordResponse(error bool)
AboutToAddMessage()
MessageJustAdded()
StoreAndNotify(peer.CwtchPeer, string, string, time.Time, string)
UpdateNetworkStatus(online bool)
}
// NewManager returns a new Manager interface for a profile to the gcd
func NewManager(profile string, gcd *GrandCentralDispatcher) Manager {
return &manager{gcd: gcd, profile: profile}
}
*/
// EnrichNewPeer populates required data for use by frontend
// uiManager.AddContact(onion)
// (handle string, displayName string, image string, badge int, status int, authorization string, loading bool, lastMsgTime int)
@ -316,80 +248,4 @@ func EnrichNewPeer(handle string, ph *PeerHelper, ev *EventProfileEnvelope) erro
return errors.New("not a peer or group")
}
return nil
}
/*
// AddSendMessageError adds an error not and icon to a message in a conversation in the ui for the message identified by the peer/sig combo
func (this *manager) AddSendMessageError(peer string, signature string, err string) {
this.gcd.DoIfProfile(this.profile, func() {
this.gcd.DoIfConversation(peer, func() {
log.Debugf("Received Error Sending Message: %v", err)
// FIXME: Sometimes, for the first Peer message we send our error beats our message to the UI
time.Sleep(time.Second * 1)
this.gcd.GroupSendError(signature, err)
})
})
}
func (this *manager) AboutToAddMessage() {
this.gcd.TimelineInterface.AddMessage(this.gcd.TimelineInterface.num())
}
func (this *manager) MessageJustAdded() {
this.gcd.TimelineInterface.RequestEIR()
}*/
/*
// AddMessage adds a message to the message pane for the supplied conversation if it is active
func (this *manager) AddMessage(handle string, from string, message string, fromMe bool, messageID string, timestamp time.Time, Acknowledged bool) {
this.gcd.DoIfProfile(this.profile, func() {
this.gcd.DoIfConversation(handle, func() {
updateLastReadTime(handle)
// If the message is not from the user then add it, otherwise, just acknowledge.
if !fromMe || !Acknowledged {
this.gcd.TimelineInterface.AddMessage(this.gcd.TimelineInterface.num() - 1)
this.gcd.TimelineInterface.RequestEIR()
} else {
this.gcd.Acknowledged(messageID)
}
})
this.gcd.IncContactUnreadCount(handle)
})
if !fromMe {
this.gcd.Notify(handle)
}
}
func (this *manager) ReloadProfiles() {
this.gcd.reloadProfileList()
}
// UpdateContactDisplayName updates a contact's display name in the contact list and conversations
func (this *manager) UpdateContactDisplayName(handle string) {
this.gcd.DoIfProfile(this.profile, func() {
this.gcd.UpdateContactDisplayName(handle, GetNick(handle))
})
}
// UpdateContactPicture updates a contact's picture in the contact list and conversations
func (this *manager) UpdateContactPicture(handle string) {
this.gcd.DoIfProfile(this.profile, func() {
this.gcd.UpdateContactPicture(handle, GetProfilePic(handle))
})
}
// UpdateContactAttribute update's a contacts attribute in the ui
func (this *manager) UpdateContactAttribute(handle, key, value string) {
this.gcd.DoIfProfile(this.profile, func() {
this.gcd.UpdateContactAttribute(handle, key, value)
})
}
func (this *manager) ChangePasswordResponse(error bool) {
this.gcd.ChangePasswordResponse(error)
}
func (this *manager) UpdateNetworkStatus(online bool) {
this.gcd.UpdateProfileNetworkStatus(this.profile, online)
}
*/
}