196 lines
6.8 KiB
Go
196 lines
6.8 KiB
Go
package app
|
|
|
|
import (
|
|
"cwtch.im/cwtch/app/plugins"
|
|
"cwtch.im/cwtch/event"
|
|
"cwtch.im/cwtch/model"
|
|
"cwtch.im/cwtch/protocol/connections"
|
|
"cwtch.im/cwtch/storage"
|
|
"cwtch.im/tapir/primitives"
|
|
"git.openprivacy.ca/openprivacy/connectivity"
|
|
"git.openprivacy.ca/openprivacy/log"
|
|
"path"
|
|
"strconv"
|
|
"sync"
|
|
)
|
|
|
|
type applicationService struct {
|
|
applicationBridge
|
|
appletACN
|
|
appletPlugins
|
|
|
|
storage map[string]storage.ProfileStore
|
|
engines map[string]connections.Engine
|
|
asmutex sync.Mutex
|
|
}
|
|
|
|
// ApplicationService is the back end of an application that manages engines and writing storage and communicates to an ApplicationClient by an IPCBridge
|
|
type ApplicationService interface {
|
|
Shutdown()
|
|
}
|
|
|
|
// NewAppService returns an ApplicationService that runs the backend of an app and communicates with a client by the supplied IPCBridge
|
|
func NewAppService(acn connectivity.ACN, appDirectory string, bridge event.IPCBridge) ApplicationService {
|
|
appService := &applicationService{storage: make(map[string]storage.ProfileStore), engines: make(map[string]connections.Engine), applicationBridge: applicationBridge{applicationCore: *newAppCore(appDirectory), bridge: bridge}}
|
|
|
|
appService.appletACN.init(acn, appService.getACNStatusHandler())
|
|
appService.handle = appService.handleEvent
|
|
|
|
go appService.listen()
|
|
|
|
log.Infoln("Created new App Service")
|
|
return appService
|
|
}
|
|
|
|
func (as *applicationService) handleEvent(ev *event.Event) {
|
|
log.Infof("app Service handleEvent %v\n", ev.EventType)
|
|
switch ev.EventType {
|
|
case event.CreatePeer:
|
|
profileName := ev.Data[event.ProfileName]
|
|
password := ev.Data[event.Password]
|
|
tag := ev.Data[event.Data]
|
|
as.createPeer(profileName, password, tag)
|
|
case event.DeletePeer:
|
|
onion := ev.Data[event.Identity]
|
|
as.deletePeer(onion)
|
|
|
|
message := event.IPCMessage{Dest: DestApp, Message: *ev}
|
|
as.bridge.Write(&message)
|
|
case event.AddPeerPlugin:
|
|
onion := ev.Data[event.Identity]
|
|
pluginID, _ := strconv.Atoi(ev.Data[event.Data])
|
|
as.AddPlugin(onion, plugins.PluginID(pluginID), as.eventBuses[onion], as.acn)
|
|
case event.LoadProfiles:
|
|
password := ev.Data[event.Password]
|
|
as.loadProfiles(password)
|
|
case event.ReloadClient:
|
|
for _, storage := range as.storage {
|
|
peerMsg := *storage.GetNewPeerMessage()
|
|
peerMsg.Data[event.Status] = event.StorageRunning
|
|
message := event.IPCMessage{Dest: DestApp, Message: peerMsg}
|
|
as.bridge.Write(&message)
|
|
}
|
|
|
|
message := event.IPCMessage{Dest: DestApp, Message: event.NewEventList(event.ReloadDone)}
|
|
as.bridge.Write(&message)
|
|
case event.ReloadPeer:
|
|
onion := ev.Data[event.Identity]
|
|
events := as.storage[onion].GetStatusMessages()
|
|
|
|
for _, ev := range events {
|
|
message := event.IPCMessage{Dest: onion, Message: *ev}
|
|
as.bridge.Write(&message)
|
|
}
|
|
case event.GetACNStatus:
|
|
prog, status := as.acn.GetBootstrapStatus()
|
|
as.getACNStatusHandler()(prog, status)
|
|
case event.ShutdownPeer:
|
|
onion := ev.Data[event.Identity]
|
|
as.ShutdownPeer(onion)
|
|
}
|
|
}
|
|
|
|
func (as *applicationService) createPeer(name, password, tag string) {
|
|
log.Infof("app Service create peer %v %v\n", name, password)
|
|
profile, err := as.applicationCore.CreatePeer(name)
|
|
as.eventBuses[profile.Onion] = event.IPCEventManagerFrom(as.bridge, profile.Onion, as.eventBuses[profile.Onion])
|
|
if err != nil {
|
|
log.Errorf("Could not create Peer: %v\n", err)
|
|
message := event.IPCMessage{Dest: DestApp, Message: event.NewEventList(event.PeerError, event.Error, err.Error())}
|
|
as.bridge.Write(&message)
|
|
return
|
|
}
|
|
|
|
if tag != "" {
|
|
profile.SetAttribute(AttributeTag, tag)
|
|
}
|
|
|
|
profileStore := storage.CreateProfileWriterStore(as.eventBuses[profile.Onion], path.Join(as.directory, "profiles", profile.LocalID), password, profile)
|
|
|
|
blockedPeers := profile.BlockedPeers()
|
|
// TODO: Would be nice if ProtocolEngine did not need to explicitly be given the Private Key.
|
|
identity := primitives.InitializeIdentity(profile.Name, &profile.Ed25519PrivateKey, &profile.Ed25519PublicKey)
|
|
engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, as.acn, as.eventBuses[profile.Onion], profile.GetContacts(), blockedPeers)
|
|
|
|
as.storage[profile.Onion] = profileStore
|
|
as.engines[profile.Onion] = engine
|
|
|
|
peerMsg := *profileStore.GetNewPeerMessage()
|
|
peerMsg.Data[event.Status] = event.StorageNew
|
|
message := event.IPCMessage{Dest: DestApp, Message: peerMsg}
|
|
as.bridge.Write(&message)
|
|
}
|
|
|
|
func (as *applicationService) loadProfiles(password string) {
|
|
count := 0
|
|
as.applicationCore.LoadProfiles(password, false, func(profile *model.Profile, profileStore storage.ProfileStore) {
|
|
as.eventBuses[profile.Onion] = event.IPCEventManagerFrom(as.bridge, profile.Onion, as.eventBuses[profile.Onion])
|
|
|
|
blockedPeers := profile.BlockedPeers()
|
|
identity := primitives.InitializeIdentity(profile.Name, &profile.Ed25519PrivateKey, &profile.Ed25519PublicKey)
|
|
engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, as.acn, as.eventBuses[profile.Onion], profile.GetContacts(), blockedPeers)
|
|
as.asmutex.Lock()
|
|
as.storage[profile.Onion] = profileStore
|
|
as.engines[profile.Onion] = engine
|
|
as.asmutex.Unlock()
|
|
|
|
peerMsg := *profileStore.GetNewPeerMessage()
|
|
peerMsg.Data[event.Status] = event.StorageNew
|
|
message := event.IPCMessage{Dest: DestApp, Message: peerMsg}
|
|
as.bridge.Write(&message)
|
|
count++
|
|
})
|
|
if count == 0 {
|
|
message := event.IPCMessage{Dest: DestApp, Message: event.NewEventList(event.AppError, event.Error, event.AppErrLoaded0)}
|
|
as.bridge.Write(&message)
|
|
}
|
|
}
|
|
|
|
func (as *applicationService) getACNStatusHandler() func(int, string) {
|
|
return func(progress int, status string) {
|
|
progStr := strconv.Itoa(progress)
|
|
as.bridge.Write(&event.IPCMessage{Dest: DestApp, Message: event.NewEventList(event.ACNStatus, event.Progreess, progStr, event.Status, status)})
|
|
as.applicationCore.coremutex.Lock()
|
|
defer as.applicationCore.coremutex.Unlock()
|
|
for _, bus := range as.eventBuses {
|
|
bus.Publish(event.NewEventList(event.ACNStatus, event.Progreess, progStr, event.Status, status))
|
|
}
|
|
}
|
|
}
|
|
|
|
func (as *applicationService) deletePeer(onion string) {
|
|
as.asmutex.Lock()
|
|
defer as.asmutex.Unlock()
|
|
|
|
as.appletPlugins.ShutdownPeer(onion)
|
|
as.plugins.Delete(onion)
|
|
|
|
as.engines[onion].Shutdown()
|
|
delete(as.engines, onion)
|
|
|
|
as.storage[onion].Shutdown()
|
|
as.storage[onion].Delete()
|
|
delete(as.storage, onion)
|
|
|
|
as.applicationCore.DeletePeer(onion)
|
|
}
|
|
|
|
func (as *applicationService) ShutdownPeer(onion string) {
|
|
as.engines[onion].Shutdown()
|
|
delete(as.engines, onion)
|
|
as.storage[onion].Shutdown()
|
|
delete(as.storage, onion)
|
|
as.eventBuses[onion].Shutdown()
|
|
delete(as.eventBuses, onion)
|
|
}
|
|
|
|
// Shutdown shuts down the application Service and all peer related backend parts
|
|
func (as *applicationService) Shutdown() {
|
|
log.Debugf("shutting down application service...")
|
|
as.appletPlugins.Shutdown()
|
|
for id := range as.engines {
|
|
log.Debugf("shutting down application service peer engine %v", id)
|
|
as.ShutdownPeer(id)
|
|
}
|
|
}
|