diff --git a/constants/server_manager_events.go b/constants/server_manager_events.go deleted file mode 100644 index 75982c3..0000000 --- a/constants/server_manager_events.go +++ /dev/null @@ -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") -) diff --git a/features/servers/servers_functionality.go b/features/servers/servers_functionality.go new file mode 100644 index 0000000..ff9cdbe --- /dev/null +++ b/features/servers/servers_functionality.go @@ -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" + } +} \ No newline at end of file diff --git a/go.mod b/go.mod index 1bf45ba..2168528 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.15 require ( 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 diff --git a/go.sum b/go.sum index df5c7a2..678cfdb 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +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.0 h1:hEMee2/2s4kUwukGCTBpGww/KfrsE84e9tOLnM8lM78= -cwtch.im/cwtch v0.12.0/go.mod h1:QpTkQK7MqNt0dQK9/pBk5VpkvFhy6xuoxJIn401B8fM= -cwtch.im/cwtch v0.12.1 h1:3+OZtzZ9Kg+3Es/ntyPeg7Ku9XzOlSXcvC6rdezmuIM= -cwtch.im/cwtch v0.12.1/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= @@ -17,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= @@ -32,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= @@ -42,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= @@ -52,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= @@ -71,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= @@ -90,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= diff --git a/lib.go b/lib.go index 4a9a58a..2db08a3 100644 --- a/lib.go +++ b/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 @@ -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) } @@ -181,8 +186,10 @@ func _startCwtch(appDir string, torPath string) { 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) @@ -207,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)})) @@ -267,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{})) @@ -277,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) { @@ -304,8 +327,19 @@ func SendAppEvent(eventJson string) { log.Debugf("New Settings %v", globalSettings) utils.WriteGlobalSettings(globalSettings) + settings := utils.ReadGlobalSettings() + + _, err = servers.ExperimentGate(settings.Experiments) + if err == nil { + servers.InitServers(globalACN, globalAppDir) + publishLoadedServers() + LaunchServers() + } else { + servers.DeactivateServers() + } + // Group Experiment Refresh - groupHandler, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments) + groupHandler, err := groups.ExperimentGate(settings.Experiments) if err == nil { for _, profileOnion := range application.ListProfiles() { serverListForOnion := groupHandler.GetServerInfoList(application.GetPeer(profileOnion)) @@ -316,7 +350,7 @@ func SendAppEvent(eventJson string) { // Explicitly toggle blocking/unblocking of unknown connections for profiles // that have been loaded. - if utils.ReadGlobalSettings().BlockUnknownConnections { + if settings.BlockUnknownConnections { for _, onion := range application.ListProfiles() { application.GetPeer(onion).BlockUnknownConnections() } @@ -938,5 +972,171 @@ 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) + } + } + } +} + +//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() {} diff --git a/utils/eventHandler.go b/utils/eventHandler.go index db5baa5..8657202 100644 --- a/utils/eventHandler.go +++ b/utils/eventHandler.go @@ -10,6 +10,7 @@ import ( "encoding/json" 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" ) @@ -49,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 { diff --git a/utils/manager.go b/utils/manager.go index 4e2ea8e..056c23a 100644 --- a/utils/manager.go +++ b/utils/manager.go @@ -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) -} -*/ +} \ No newline at end of file