file resumption support #42
|
@ -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")
|
||||
)
|
|
@ -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
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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
5
go.mod
|
@ -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
19
go.sum
|
@ -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
374
lib.go
|
@ -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() {}
|
||||
|
|
|
@ -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 ""
|
||||
}
|
||||
}
|
||||
|
|
148
utils/manager.go
148
utils/manager.go
|
@ -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)
|
||||
}
|
||||
*/
|
||||
}
|
Reference in New Issue