2018-04-30 21:47:21 +00:00
|
|
|
package app
|
|
|
|
|
|
|
|
import (
|
2019-08-02 01:09:01 +00:00
|
|
|
"cwtch.im/cwtch/app/plugins"
|
2019-01-04 21:44:21 +00:00
|
|
|
"cwtch.im/cwtch/event"
|
2021-11-23 22:45:25 +00:00
|
|
|
"cwtch.im/cwtch/model"
|
2021-10-15 19:38:22 +00:00
|
|
|
"cwtch.im/cwtch/model/attr"
|
2021-10-15 19:43:28 +00:00
|
|
|
"cwtch.im/cwtch/model/constants"
|
2018-05-28 18:05:06 +00:00
|
|
|
"cwtch.im/cwtch/peer"
|
2019-05-15 20:12:11 +00:00
|
|
|
"cwtch.im/cwtch/protocol/connections"
|
2018-10-06 03:50:55 +00:00
|
|
|
"cwtch.im/cwtch/storage"
|
2020-02-10 22:09:24 +00:00
|
|
|
"git.openprivacy.ca/openprivacy/connectivity"
|
|
|
|
"git.openprivacy.ca/openprivacy/log"
|
2018-09-21 18:53:10 +00:00
|
|
|
"io/ioutil"
|
2018-07-01 18:43:05 +00:00
|
|
|
"os"
|
2021-10-07 22:40:25 +00:00
|
|
|
path "path/filepath"
|
2019-08-02 01:09:01 +00:00
|
|
|
"strconv"
|
2018-09-21 18:53:10 +00:00
|
|
|
"sync"
|
2018-04-30 21:47:21 +00:00
|
|
|
)
|
|
|
|
|
2018-09-21 18:53:10 +00:00
|
|
|
type application struct {
|
2021-12-17 19:42:55 +00:00
|
|
|
eventBuses map[string]event.Manager
|
|
|
|
directory string
|
|
|
|
coremutex sync.Mutex
|
2019-07-10 20:30:24 +00:00
|
|
|
appletPeers
|
|
|
|
appletACN
|
2019-08-02 01:09:01 +00:00
|
|
|
appletPlugins
|
2019-12-12 20:21:14 +00:00
|
|
|
engines map[string]connections.Engine
|
|
|
|
appBus event.Manager
|
|
|
|
appmutex sync.Mutex
|
2018-09-21 18:53:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Application is a full cwtch peer application. It allows management, usage and storage of multiple peers
|
|
|
|
type Application interface {
|
2019-06-05 20:40:55 +00:00
|
|
|
LoadProfiles(password string)
|
2019-12-10 23:45:43 +00:00
|
|
|
CreateTaggedPeer(name string, password string, tag string)
|
2021-06-14 23:50:35 +00:00
|
|
|
DeletePeer(onion string, currentPassword string)
|
2019-08-02 01:09:01 +00:00
|
|
|
AddPeerPlugin(onion string, pluginID plugins.PluginID)
|
2018-11-22 00:08:47 +00:00
|
|
|
LaunchPeers()
|
2018-09-21 18:53:10 +00:00
|
|
|
|
2019-06-05 20:40:55 +00:00
|
|
|
GetPrimaryBus() event.Manager
|
|
|
|
GetEventBus(onion string) event.Manager
|
2019-09-26 23:43:34 +00:00
|
|
|
QueryACNStatus()
|
2020-12-01 03:25:17 +00:00
|
|
|
QueryACNVersion()
|
2019-06-05 20:40:55 +00:00
|
|
|
|
2019-05-15 20:12:11 +00:00
|
|
|
ShutdownPeer(string)
|
2018-09-21 18:53:10 +00:00
|
|
|
Shutdown()
|
2019-06-05 20:40:55 +00:00
|
|
|
|
|
|
|
GetPeer(onion string) peer.CwtchPeer
|
2021-10-15 19:38:22 +00:00
|
|
|
ListProfiles() []string
|
2019-06-05 20:40:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LoadProfileFn is the function signature for a function in an app that loads a profile
|
2021-11-09 23:47:33 +00:00
|
|
|
type LoadProfileFn func(profile peer.CwtchPeer)
|
2019-06-05 20:40:55 +00:00
|
|
|
|
2018-07-10 20:42:47 +00:00
|
|
|
// NewApp creates a new app with some environment awareness and initializes a Tor Manager
|
2018-11-22 18:01:04 +00:00
|
|
|
func NewApp(acn connectivity.ACN, appDirectory string) Application {
|
2018-12-04 02:52:11 +00:00
|
|
|
log.Debugf("NewApp(%v)\n", appDirectory)
|
2021-12-17 19:42:55 +00:00
|
|
|
os.MkdirAll(path.Join(appDirectory, "profiles"), 0700)
|
|
|
|
|
|
|
|
app := &application{engines: make(map[string]connections.Engine), eventBuses: make(map[string]event.Manager), directory: appDirectory, appBus: event.NewEventManager()}
|
2019-07-10 20:30:24 +00:00
|
|
|
app.appletPeers.init()
|
|
|
|
|
2019-09-26 23:43:34 +00:00
|
|
|
app.appletACN.init(acn, app.getACNStatusHandler())
|
2018-11-10 22:14:12 +00:00
|
|
|
return app
|
2018-04-30 21:47:21 +00:00
|
|
|
}
|
|
|
|
|
2019-12-10 23:45:43 +00:00
|
|
|
func (app *application) CreateTaggedPeer(name string, password string, tag string) {
|
2021-11-19 22:31:44 +00:00
|
|
|
app.appmutex.Lock()
|
|
|
|
defer app.appmutex.Unlock()
|
2021-11-09 23:47:33 +00:00
|
|
|
|
2021-11-23 22:45:25 +00:00
|
|
|
profileDirectory := path.Join(app.directory, "profiles", model.GenerateRandomID())
|
2021-11-09 23:47:33 +00:00
|
|
|
|
|
|
|
profile, err := peer.CreateEncryptedStorePeer(profileDirectory, name, password)
|
2019-06-05 20:40:55 +00:00
|
|
|
if err != nil {
|
2021-11-09 23:47:33 +00:00
|
|
|
log.Errorf("Error Creating Peer: %v", err)
|
2019-06-05 20:40:55 +00:00
|
|
|
app.appBus.Publish(event.NewEventList(event.PeerError, event.Error, err.Error()))
|
|
|
|
return
|
2018-05-01 20:44:45 +00:00
|
|
|
}
|
2019-06-05 20:40:55 +00:00
|
|
|
|
2021-11-09 23:47:33 +00:00
|
|
|
eventBus := event.NewEventManager()
|
|
|
|
app.eventBuses[profile.GetOnion()] = eventBus
|
|
|
|
profile.Init(app.eventBuses[profile.GetOnion()])
|
|
|
|
app.peers[profile.GetOnion()] = profile
|
2021-11-11 00:41:43 +00:00
|
|
|
app.engines[profile.GetOnion()], _ = profile.GenerateProtocolEngine(app.acn, app.eventBuses[profile.GetOnion()])
|
2018-09-21 18:53:10 +00:00
|
|
|
|
2019-12-10 23:45:43 +00:00
|
|
|
if tag != "" {
|
2021-11-09 23:47:33 +00:00
|
|
|
profile.SetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants.Tag, tag)
|
2019-12-10 23:45:43 +00:00
|
|
|
}
|
|
|
|
|
2021-11-09 23:47:33 +00:00
|
|
|
app.appBus.Publish(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: profile.GetOnion(), event.Created: event.True}))
|
2019-12-10 23:45:43 +00:00
|
|
|
}
|
|
|
|
|
2021-06-14 23:50:35 +00:00
|
|
|
func (app *application) DeletePeer(onion string, password string) {
|
2019-12-10 23:45:43 +00:00
|
|
|
log.Infof("DeletePeer called on %v\n", onion)
|
2019-12-12 20:21:14 +00:00
|
|
|
app.appmutex.Lock()
|
2019-12-13 19:34:59 +00:00
|
|
|
defer app.appmutex.Unlock()
|
2019-12-10 23:45:43 +00:00
|
|
|
|
2021-11-19 19:49:04 +00:00
|
|
|
if app.peers[onion].CheckPassword(password) {
|
|
|
|
app.appletPlugins.ShutdownPeer(onion)
|
|
|
|
app.plugins.Delete(onion)
|
|
|
|
|
|
|
|
// Shutdown and Remove the Engine
|
|
|
|
app.engines[onion].Shutdown()
|
|
|
|
delete(app.engines, onion)
|
|
|
|
|
|
|
|
app.peers[onion].Shutdown()
|
|
|
|
app.peers[onion].Delete()
|
|
|
|
delete(app.peers, onion)
|
|
|
|
app.eventBuses[onion].Publish(event.NewEventList(event.ShutdownPeer, event.Identity, onion))
|
2021-12-17 19:42:55 +00:00
|
|
|
|
|
|
|
app.coremutex.Lock()
|
|
|
|
defer app.coremutex.Unlock()
|
|
|
|
app.eventBuses[onion].Shutdown()
|
|
|
|
delete(app.eventBuses, onion)
|
2021-11-19 19:49:04 +00:00
|
|
|
|
|
|
|
log.Debugf("Delete peer for %v Done\n", onion)
|
|
|
|
app.appBus.Publish(event.NewEventList(event.PeerDeleted, event.Identity, onion))
|
|
|
|
return
|
|
|
|
}
|
2021-06-14 23:50:35 +00:00
|
|
|
app.appBus.Publish(event.NewEventList(event.AppError, event.Error, event.PasswordMatchError, event.Identity, onion))
|
2019-12-10 23:45:43 +00:00
|
|
|
}
|
|
|
|
|
2019-08-02 01:09:01 +00:00
|
|
|
func (app *application) AddPeerPlugin(onion string, pluginID plugins.PluginID) {
|
2019-10-31 23:05:01 +00:00
|
|
|
app.AddPlugin(onion, pluginID, app.eventBuses[onion], app.acn)
|
2019-08-02 01:09:01 +00:00
|
|
|
}
|
|
|
|
|
2019-06-05 20:40:55 +00:00
|
|
|
// LoadProfiles takes a password and attempts to load any profiles it can from storage with it and create Peers for them
|
2021-12-17 19:42:55 +00:00
|
|
|
func (app *application) LoadProfiles(password string) {
|
|
|
|
count := 0
|
|
|
|
migrating := false
|
|
|
|
|
|
|
|
files, err := ioutil.ReadDir(path.Join(app.directory, "profiles"))
|
2018-09-21 18:53:10 +00:00
|
|
|
if err != nil {
|
2021-12-17 19:42:55 +00:00
|
|
|
log.Errorf("error: cannot read profiles directory: %v", err)
|
|
|
|
return
|
2018-09-21 18:53:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, file := range files {
|
2021-11-09 23:47:33 +00:00
|
|
|
// Attempt to load an encrypted database
|
2021-12-17 19:42:55 +00:00
|
|
|
profileDirectory := path.Join(app.directory, "profiles", file.Name())
|
2021-11-09 23:47:33 +00:00
|
|
|
profile, err := peer.FromEncryptedDatabase(profileDirectory, password)
|
2021-12-17 19:42:55 +00:00
|
|
|
loaded := false
|
2021-11-09 23:47:33 +00:00
|
|
|
if err == nil {
|
|
|
|
// return the load the profile...
|
2021-11-18 23:43:58 +00:00
|
|
|
log.Infof("loading profile from new-type storage database...")
|
2021-12-17 19:42:55 +00:00
|
|
|
loaded = app.installProfile(profile)
|
2021-11-18 23:43:58 +00:00
|
|
|
} else { // On failure attempt to load a legacy profile
|
|
|
|
profileStore, err := storage.LoadProfileWriterStore(profileDirectory, password)
|
2021-11-09 23:47:33 +00:00
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
2021-11-18 23:43:58 +00:00
|
|
|
log.Infof("found legacy profile. importing to new database structure...")
|
2021-12-17 19:42:55 +00:00
|
|
|
legacyProfile := profileStore.GetProfileCopy(true)
|
|
|
|
if !migrating {
|
|
|
|
migrating = true
|
|
|
|
app.appBus.Publish(event.NewEventList(event.StartingStorageMiragtion))
|
|
|
|
}
|
2021-11-09 23:47:33 +00:00
|
|
|
|
2021-11-17 23:34:14 +00:00
|
|
|
cps, err := peer.CreateEncryptedStore(profileDirectory, password)
|
2021-11-17 23:59:52 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Errorf("error creating encrypted store: %v", err)
|
|
|
|
}
|
2021-11-17 23:34:14 +00:00
|
|
|
profile := peer.ImportLegacyProfile(legacyProfile, cps)
|
2021-12-17 19:42:55 +00:00
|
|
|
loaded = app.installProfile(profile)
|
2021-11-09 23:47:33 +00:00
|
|
|
}
|
2021-12-17 19:42:55 +00:00
|
|
|
if loaded {
|
2021-11-17 23:34:14 +00:00
|
|
|
count++
|
|
|
|
}
|
2021-12-17 19:42:55 +00:00
|
|
|
}
|
2019-07-10 20:30:24 +00:00
|
|
|
if count == 0 {
|
|
|
|
message := event.NewEventList(event.AppError, event.Error, event.AppErrLoaded0)
|
|
|
|
app.appBus.Publish(message)
|
|
|
|
}
|
2021-12-17 19:42:55 +00:00
|
|
|
if migrating {
|
|
|
|
app.appBus.Publish(event.NewEventList(event.DoneStorageMigration))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// installProfile takes a profile and if it isn't loaded in the app, installs it and returns true
|
|
|
|
func (app *application) installProfile(profile peer.CwtchPeer) bool {
|
|
|
|
app.appmutex.Lock()
|
|
|
|
defer app.appmutex.Unlock()
|
|
|
|
|
|
|
|
// Only attempt to finalize the profile if we don't have one loaded...
|
|
|
|
if app.peers[profile.GetOnion()] == nil {
|
|
|
|
eventBus := event.NewEventManager()
|
|
|
|
app.eventBuses[profile.GetOnion()] = eventBus
|
|
|
|
profile.Init(app.eventBuses[profile.GetOnion()])
|
|
|
|
app.peers[profile.GetOnion()] = profile
|
|
|
|
app.engines[profile.GetOnion()], _ = profile.GenerateProtocolEngine(app.acn, app.eventBuses[profile.GetOnion()])
|
|
|
|
app.appBus.Publish(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: profile.GetOnion(), event.Created: event.False}))
|
|
|
|
return true
|
|
|
|
}
|
2021-12-18 03:55:25 +00:00
|
|
|
// Otherwise shutdown the connections
|
|
|
|
profile.Shutdown()
|
|
|
|
return false
|
2019-06-05 20:40:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetPrimaryBus returns the bus the Application uses for events that aren't peer specific
|
|
|
|
func (app *application) GetPrimaryBus() event.Manager {
|
|
|
|
return app.appBus
|
2018-04-30 21:47:21 +00:00
|
|
|
}
|
|
|
|
|
2019-05-15 20:12:11 +00:00
|
|
|
// GetEventBus returns a cwtchPeer's event bus
|
2021-12-17 19:42:55 +00:00
|
|
|
func (app *application) GetEventBus(onion string) event.Manager {
|
|
|
|
if manager, ok := app.eventBuses[onion]; ok {
|
2019-05-15 20:12:11 +00:00
|
|
|
return manager
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2018-10-05 23:27:57 +00:00
|
|
|
|
2019-09-26 23:43:34 +00:00
|
|
|
func (app *application) getACNStatusHandler() func(int, string) {
|
|
|
|
return func(progress int, status string) {
|
|
|
|
progStr := strconv.Itoa(progress)
|
2021-11-19 23:10:22 +00:00
|
|
|
app.appmutex.Lock()
|
2021-04-13 22:12:12 +00:00
|
|
|
app.appBus.Publish(event.NewEventList(event.ACNStatus, event.Progress, progStr, event.Status, status))
|
2019-09-26 23:43:34 +00:00
|
|
|
for _, bus := range app.eventBuses {
|
2021-04-13 22:12:12 +00:00
|
|
|
bus.Publish(event.NewEventList(event.ACNStatus, event.Progress, progStr, event.Status, status))
|
2019-09-26 23:43:34 +00:00
|
|
|
}
|
2021-11-19 23:10:22 +00:00
|
|
|
app.appmutex.Unlock()
|
2019-09-26 23:43:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (app *application) QueryACNStatus() {
|
|
|
|
prog, status := app.acn.GetBootstrapStatus()
|
|
|
|
app.getACNStatusHandler()(prog, status)
|
|
|
|
}
|
|
|
|
|
2020-12-01 03:25:17 +00:00
|
|
|
func (app *application) QueryACNVersion() {
|
|
|
|
version := app.acn.GetVersion()
|
|
|
|
app.appBus.Publish(event.NewEventList(event.ACNVersion, event.Data, version))
|
|
|
|
}
|
|
|
|
|
2019-05-15 20:12:11 +00:00
|
|
|
// ShutdownPeer shuts down a peer and removes it from the app's management
|
|
|
|
func (app *application) ShutdownPeer(onion string) {
|
2019-12-12 20:21:14 +00:00
|
|
|
app.appmutex.Lock()
|
|
|
|
defer app.appmutex.Unlock()
|
2019-05-15 20:12:11 +00:00
|
|
|
app.eventBuses[onion].Shutdown()
|
|
|
|
delete(app.eventBuses, onion)
|
|
|
|
app.peers[onion].Shutdown()
|
|
|
|
delete(app.peers, onion)
|
|
|
|
app.engines[onion].Shutdown()
|
|
|
|
delete(app.engines, onion)
|
2019-10-31 23:05:01 +00:00
|
|
|
app.appletPlugins.Shutdown()
|
2019-01-19 23:16:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-21 18:53:10 +00:00
|
|
|
// Shutdown shutsdown all peers of an app and then the tormanager
|
|
|
|
func (app *application) Shutdown() {
|
2019-05-15 20:12:11 +00:00
|
|
|
for id, peer := range app.peers {
|
2018-09-21 18:53:10 +00:00
|
|
|
peer.Shutdown()
|
2021-06-24 01:29:23 +00:00
|
|
|
log.Debugf("Shutting Down Peer %v", id)
|
2019-10-31 23:05:01 +00:00
|
|
|
app.appletPlugins.ShutdownPeer(id)
|
2021-06-24 01:29:23 +00:00
|
|
|
log.Debugf("Shutting Down Engines for %v", id)
|
2019-05-15 20:12:11 +00:00
|
|
|
app.engines[id].Shutdown()
|
2021-06-24 01:29:23 +00:00
|
|
|
log.Debugf("Shutting Down Bus for %v", id)
|
2019-05-15 20:12:11 +00:00
|
|
|
app.eventBuses[id].Shutdown()
|
2018-09-21 18:53:10 +00:00
|
|
|
}
|
2021-06-24 01:29:23 +00:00
|
|
|
log.Debugf("Shutting Down App")
|
2019-06-05 20:40:55 +00:00
|
|
|
app.appBus.Shutdown()
|
2021-06-24 01:29:23 +00:00
|
|
|
log.Debugf("Shut Down Complete")
|
2018-04-30 21:47:21 +00:00
|
|
|
}
|