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"
|
2019-06-05 20:40:55 +00:00
|
|
|
"cwtch.im/cwtch/model"
|
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"
|
2019-08-08 18:39:38 +00:00
|
|
|
"cwtch.im/tapir/primitives"
|
2018-07-01 18:43:05 +00:00
|
|
|
"fmt"
|
2018-11-10 22:14:12 +00:00
|
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
2018-12-04 02:52:11 +00:00
|
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
2018-09-21 18:53:10 +00:00
|
|
|
"io/ioutil"
|
2018-07-01 18:43:05 +00:00
|
|
|
"os"
|
|
|
|
"path"
|
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
|
|
|
)
|
|
|
|
|
2019-12-12 20:21:14 +00:00
|
|
|
// AttributeTag is a const name for a peer attribute that can be set at creation time, for example for versioning info
|
2019-12-10 23:45:43 +00:00
|
|
|
const AttributeTag = "tag"
|
|
|
|
|
2019-06-05 20:40:55 +00:00
|
|
|
type applicationCore struct {
|
|
|
|
eventBuses map[string]event.Manager
|
|
|
|
|
|
|
|
directory string
|
2019-12-13 19:34:59 +00:00
|
|
|
coremutex sync.Mutex
|
2019-06-05 20:40:55 +00:00
|
|
|
}
|
|
|
|
|
2018-09-21 18:53:10 +00:00
|
|
|
type application struct {
|
2019-06-05 20:40:55 +00:00
|
|
|
applicationCore
|
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
|
|
|
storage map[string]storage.ProfileStore
|
|
|
|
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)
|
|
|
|
CreatePeer(name string, password string)
|
2019-12-10 23:45:43 +00:00
|
|
|
CreateTaggedPeer(name string, password string, tag string)
|
|
|
|
DeletePeer(onion string)
|
2019-08-02 01:09:01 +00:00
|
|
|
AddPeerPlugin(onion string, pluginID plugins.PluginID)
|
2019-12-12 20:21:14 +00:00
|
|
|
ChangePeerPassword(onion, oldpass, newpass string)
|
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()
|
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
|
|
|
|
ListPeers() map[string]string
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadProfileFn is the function signature for a function in an app that loads a profile
|
|
|
|
type LoadProfileFn func(profile *model.Profile, store storage.ProfileStore)
|
|
|
|
|
2019-06-21 21:50:43 +00:00
|
|
|
func newAppCore(appDirectory string) *applicationCore {
|
|
|
|
appCore := &applicationCore{eventBuses: make(map[string]event.Manager), directory: appDirectory}
|
2019-06-05 20:40:55 +00:00
|
|
|
os.MkdirAll(path.Join(appCore.directory, "profiles"), 0700)
|
|
|
|
return appCore
|
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)
|
2019-07-10 20:30:24 +00:00
|
|
|
app := &application{storage: make(map[string]storage.ProfileStore), engines: make(map[string]connections.Engine), applicationCore: *newAppCore(appDirectory), appBus: event.NewEventManager()}
|
|
|
|
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-06-05 20:40:55 +00:00
|
|
|
// CreatePeer creates a new Peer with a given name and core required accessories (eventbus)
|
2019-11-15 13:54:44 +00:00
|
|
|
func (ac *applicationCore) CreatePeer(name string) (*model.Profile, error) {
|
2018-12-04 02:52:11 +00:00
|
|
|
log.Debugf("CreatePeer(%v)\n", name)
|
2018-09-21 18:53:10 +00:00
|
|
|
|
2019-02-04 18:25:58 +00:00
|
|
|
profile := storage.NewProfile(name)
|
2019-06-05 20:40:55 +00:00
|
|
|
|
2019-12-13 19:34:59 +00:00
|
|
|
ac.coremutex.Lock()
|
|
|
|
defer ac.coremutex.Unlock()
|
2019-06-05 20:40:55 +00:00
|
|
|
|
|
|
|
_, exists := ac.eventBuses[profile.Onion]
|
2018-09-21 18:53:10 +00:00
|
|
|
if exists {
|
2019-06-05 20:40:55 +00:00
|
|
|
return nil, fmt.Errorf("Error: profile for onion %v already exists", profile.Onion)
|
|
|
|
}
|
|
|
|
|
|
|
|
eventBus := event.NewEventManager()
|
|
|
|
ac.eventBuses[profile.Onion] = eventBus
|
|
|
|
|
|
|
|
return profile, nil
|
|
|
|
}
|
|
|
|
|
2019-12-10 23:45:43 +00:00
|
|
|
func (ac *applicationCore) DeletePeer(onion string) {
|
2019-12-13 19:34:59 +00:00
|
|
|
ac.coremutex.Lock()
|
|
|
|
defer ac.coremutex.Unlock()
|
2019-12-10 23:45:43 +00:00
|
|
|
|
|
|
|
ac.eventBuses[onion].Shutdown()
|
|
|
|
delete(ac.eventBuses, onion)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (app *application) CreateTaggedPeer(name string, password string, tag string) {
|
2019-11-30 02:11:12 +00:00
|
|
|
profile, err := app.applicationCore.CreatePeer(name)
|
2019-06-05 20:40:55 +00:00
|
|
|
if err != nil {
|
|
|
|
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
|
|
|
|
2020-01-13 22:11:00 +00:00
|
|
|
profileStore := storage.CreateProfileWriterStore(app.eventBuses[profile.Onion], path.Join(app.directory, "profiles", profile.LocalID), password, profile)
|
2019-06-05 20:40:55 +00:00
|
|
|
app.storage[profile.Onion] = profileStore
|
|
|
|
|
2019-07-19 17:27:50 +00:00
|
|
|
pc := app.storage[profile.Onion].GetProfileCopy(true)
|
2019-06-05 20:40:55 +00:00
|
|
|
peer := peer.FromProfile(pc)
|
2019-06-21 21:50:43 +00:00
|
|
|
peer.Init(app.eventBuses[profile.Onion])
|
2019-05-15 20:12:11 +00:00
|
|
|
|
|
|
|
blockedPeers := profile.BlockedPeers()
|
|
|
|
// TODO: Would be nice if ProtocolEngine did not need to explicitly be given the Private Key.
|
2019-08-08 18:39:38 +00:00
|
|
|
identity := primitives.InitializeIdentity(profile.Name, &profile.Ed25519PrivateKey, &profile.Ed25519PublicKey)
|
2019-08-21 19:25:26 +00:00
|
|
|
engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, app.acn, app.eventBuses[profile.Onion], profile.GetContacts(), blockedPeers)
|
2019-05-15 20:12:11 +00:00
|
|
|
|
2019-06-05 20:40:55 +00:00
|
|
|
app.peers[profile.Onion] = peer
|
|
|
|
app.engines[profile.Onion] = engine
|
2018-09-21 18:53:10 +00:00
|
|
|
|
2019-12-10 23:45:43 +00:00
|
|
|
if tag != "" {
|
|
|
|
peer.SetAttribute(AttributeTag, tag)
|
|
|
|
}
|
|
|
|
|
2019-06-05 20:40:55 +00:00
|
|
|
app.appBus.Publish(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: profile.Onion}))
|
2018-09-21 18:53:10 +00:00
|
|
|
}
|
|
|
|
|
2019-12-10 23:45:43 +00:00
|
|
|
// CreatePeer creates a new Peer with the given name and required accessories (eventbus, storage, protocol engine)
|
|
|
|
func (app *application) CreatePeer(name string, password string) {
|
|
|
|
app.CreateTaggedPeer(name, password, "")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (app *application) DeletePeer(onion string) {
|
|
|
|
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
|
|
|
|
|
|
|
app.appletPlugins.ShutdownPeer(onion)
|
|
|
|
app.plugins.Delete(onion)
|
|
|
|
|
|
|
|
app.peers[onion].Shutdown()
|
|
|
|
delete(app.peers, onion)
|
|
|
|
|
|
|
|
app.engines[onion].Shutdown()
|
|
|
|
delete(app.engines, onion)
|
|
|
|
|
|
|
|
app.storage[onion].Shutdown()
|
|
|
|
app.storage[onion].Delete()
|
|
|
|
delete(app.storage, onion)
|
|
|
|
|
|
|
|
app.eventBuses[onion].Publish(event.NewEventList(event.ShutdownPeer, event.Identity, onion))
|
|
|
|
|
|
|
|
app.applicationCore.DeletePeer(onion)
|
|
|
|
log.Debugf("Delete peer for %v Done\n", onion)
|
|
|
|
}
|
|
|
|
|
2019-12-12 20:21:14 +00:00
|
|
|
func (app *application) ChangePeerPassword(onion, oldpass, newpass string) {
|
|
|
|
app.eventBuses[onion].Publish(event.NewEventList(event.ChangePassword, event.Password, oldpass, event.NewPassword, newpass))
|
|
|
|
}
|
|
|
|
|
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
|
2019-07-19 17:27:50 +00:00
|
|
|
func (ac *applicationCore) LoadProfiles(password string, timeline bool, loadProfileFn LoadProfileFn) error {
|
2019-06-05 20:40:55 +00:00
|
|
|
files, err := ioutil.ReadDir(path.Join(ac.directory, "profiles"))
|
2018-09-21 18:53:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error: cannot read profiles directory: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, file := range files {
|
2019-06-05 20:40:55 +00:00
|
|
|
eventBus := event.NewEventManager()
|
2020-01-13 22:11:00 +00:00
|
|
|
profileStore, err := storage.LoadProfileWriterStore(eventBus, path.Join(ac.directory, "profiles", file.Name()), password)
|
2018-09-21 18:53:10 +00:00
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
2018-10-06 03:50:55 +00:00
|
|
|
|
2019-07-19 17:27:50 +00:00
|
|
|
profile := profileStore.GetProfileCopy(timeline)
|
2019-02-03 01:18:33 +00:00
|
|
|
|
2019-06-05 20:40:55 +00:00
|
|
|
_, exists := ac.eventBuses[profile.Onion]
|
2018-09-21 18:53:10 +00:00
|
|
|
if exists {
|
2019-01-21 20:11:40 +00:00
|
|
|
profileStore.Shutdown()
|
2019-05-15 20:12:11 +00:00
|
|
|
eventBus.Shutdown()
|
2019-01-21 20:11:40 +00:00
|
|
|
log.Errorf("profile for onion %v already exists", profile.Onion)
|
2018-09-21 18:53:10 +00:00
|
|
|
continue
|
|
|
|
}
|
2018-11-22 00:08:47 +00:00
|
|
|
|
2019-12-13 19:34:59 +00:00
|
|
|
ac.coremutex.Lock()
|
2019-06-05 20:40:55 +00:00
|
|
|
ac.eventBuses[profile.Onion] = eventBus
|
2019-12-13 19:34:59 +00:00
|
|
|
ac.coremutex.Unlock()
|
2019-06-05 20:40:55 +00:00
|
|
|
|
|
|
|
loadProfileFn(profile, profileStore)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadProfiles takes a password and attempts to load any profiles it can from storage with it and create Peers for them
|
|
|
|
func (app *application) LoadProfiles(password string) {
|
2019-07-10 20:30:24 +00:00
|
|
|
count := 0
|
2019-07-19 17:27:50 +00:00
|
|
|
app.applicationCore.LoadProfiles(password, true, func(profile *model.Profile, profileStore storage.ProfileStore) {
|
2019-01-21 20:11:40 +00:00
|
|
|
peer := peer.FromProfile(profile)
|
2019-06-21 21:50:43 +00:00
|
|
|
peer.Init(app.eventBuses[profile.Onion])
|
2019-05-15 20:12:11 +00:00
|
|
|
|
|
|
|
blockedPeers := profile.BlockedPeers()
|
2019-08-08 18:39:38 +00:00
|
|
|
identity := primitives.InitializeIdentity(profile.Name, &profile.Ed25519PrivateKey, &profile.Ed25519PublicKey)
|
2019-08-21 19:25:26 +00:00
|
|
|
engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, app.acn, app.eventBuses[profile.Onion], profile.GetContacts(), blockedPeers)
|
2019-12-12 20:21:14 +00:00
|
|
|
app.appmutex.Lock()
|
2019-01-21 20:11:40 +00:00
|
|
|
app.peers[profile.Onion] = peer
|
|
|
|
app.storage[profile.Onion] = profileStore
|
2019-05-15 20:12:11 +00:00
|
|
|
app.engines[profile.Onion] = engine
|
2019-12-12 20:21:14 +00:00
|
|
|
app.appmutex.Unlock()
|
2019-06-05 20:40:55 +00:00
|
|
|
app.appBus.Publish(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: profile.Onion}))
|
2019-07-10 20:30:24 +00:00
|
|
|
count++
|
2019-06-05 20:40: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)
|
|
|
|
}
|
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
|
2019-06-05 20:40:55 +00:00
|
|
|
func (ac *applicationCore) GetEventBus(onion string) event.Manager {
|
|
|
|
if manager, ok := ac.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)
|
|
|
|
app.appBus.Publish(event.NewEventList(event.ACNStatus, event.Progreess, progStr, event.Status, status))
|
|
|
|
for _, bus := range app.eventBuses {
|
|
|
|
bus.Publish(event.NewEventList(event.ACNStatus, event.Progreess, progStr, event.Status, status))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (app *application) QueryACNStatus() {
|
|
|
|
prog, status := app.acn.GetBootstrapStatus()
|
|
|
|
app.getACNStatusHandler()(prog, status)
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
app.storage[onion].Shutdown()
|
|
|
|
delete(app.storage, 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()
|
2019-10-31 23:05:01 +00:00
|
|
|
app.appletPlugins.ShutdownPeer(id)
|
2019-05-15 20:12:11 +00:00
|
|
|
app.engines[id].Shutdown()
|
|
|
|
app.storage[id].Shutdown()
|
|
|
|
app.eventBuses[id].Shutdown()
|
2018-09-21 18:53:10 +00:00
|
|
|
}
|
2019-06-05 20:40:55 +00:00
|
|
|
app.appBus.Shutdown()
|
2018-04-30 21:47:21 +00:00
|
|
|
}
|