forked from cwtch.im/cwtch
166 lines
4.5 KiB
Go
166 lines
4.5 KiB
Go
package app
|
|
|
|
import (
|
|
"cwtch.im/cwtch/connectivity/tor"
|
|
"cwtch.im/cwtch/model"
|
|
"cwtch.im/cwtch/peer"
|
|
"cwtch.im/cwtch/storage"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path"
|
|
"sync"
|
|
)
|
|
|
|
const (
|
|
// ProfileFileName is the file used by app to store profiles. Clients of app can use this to check if it has
|
|
// been initialized or not. Once app has been run once, it will exist (from ProfileStore)
|
|
ProfileFileName = "profiles"
|
|
)
|
|
|
|
// Application is a facade over a cwtchPeer that provides some wrapping logic.
|
|
type application struct {
|
|
peers map[string]peer.CwtchPeer
|
|
profileStore storage.ProfileStore
|
|
torManager *tor.Manager
|
|
directory string
|
|
mutex sync.Mutex
|
|
}
|
|
|
|
// Application is a full cwtch peer application. It allows management, usage and storage of multiple peers
|
|
type Application interface {
|
|
InitProfiles(password string) (int, error)
|
|
InitDeniableProfiles(password string) (int, error)
|
|
CreatePeer(name string) (peer.CwtchPeer, error)
|
|
|
|
GetPeer(onion string) peer.CwtchPeer
|
|
ListPeers() map[string]string
|
|
|
|
Shutdown()
|
|
}
|
|
|
|
// NewApp creates a new app with some environment awareness and initializes a Tor Manager
|
|
func NewApp(appDirectory string, torPath string) (Application, error) {
|
|
log.Printf("NewApp(%v, %v)\n", appDirectory, torPath)
|
|
app := &application{peers: make(map[string]peer.CwtchPeer), directory: appDirectory}
|
|
os.MkdirAll(path.Join(appDirectory, "tor"), 0700)
|
|
err := app.startTor(torPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
app.profileStore = storage.NewProfileStore(path.Join(appDirectory, ProfileFileName), storage.DataBlockSizeMedium, storage.NumProfileBlocks, 2)
|
|
return app, nil
|
|
}
|
|
|
|
// startTor will create a local torrc if needed
|
|
func (app *application) startTor(torPath string) error {
|
|
// Creating a local cwtch tor server config for the user
|
|
// creating $app.directory/torrc file
|
|
// SOCKSPort socksPort
|
|
// ControlPort controlPort
|
|
torrc := path.Join(app.directory, "tor", "torrc")
|
|
if _, err := os.Stat(torrc); os.IsNotExist(err) {
|
|
log.Printf("writing torrc to: %v\n", torrc)
|
|
file, err := os.Create(torrc)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Fprintf(file, "SOCKSPort %d\nControlPort %d\nCookieAuthentication 0\nSafeSocks 1\n", 9050, 9051)
|
|
file.Close()
|
|
}
|
|
|
|
tm, err := tor.NewTorManager(9050, 9051, torPath, torrc)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
app.torManager = tm
|
|
return nil
|
|
}
|
|
|
|
// InitProfiles will attempt to load the normal portion of profile storage with the supplied password, returning number of profiles loaded
|
|
func (app *application) InitProfiles(password string) (int, error) {
|
|
profiles, err := app.profileStore.InitializeProfileGroup(storage.GroupMaster, password)
|
|
if err != nil {
|
|
return 0, nil
|
|
}
|
|
|
|
n := app.loadProfiles(profiles)
|
|
return n, nil
|
|
}
|
|
|
|
// InitDeniableProfiles will attempt to load the deniable portion of profile storage with the supplied password, returning number of profiles loaded
|
|
func (app *application) InitDeniableProfiles(password string) (int, error) {
|
|
profiles, err := app.profileStore.InitializeProfileGroup(storage.GroupDeniable1, password)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
n := app.loadProfiles(profiles)
|
|
return n, nil
|
|
}
|
|
|
|
func (app *application) loadProfiles(profiles []*model.Profile) int {
|
|
app.mutex.Lock()
|
|
count := 0
|
|
for _, profile := range profiles {
|
|
fmt.Printf("init from profile: %v\n", profile.Name)
|
|
peer := peer.InitFromProfile(profile)
|
|
app.peers[peer.GetProfile().Onion] = peer
|
|
app.startPeer(peer)
|
|
count++
|
|
}
|
|
|
|
app.mutex.Unlock()
|
|
return count
|
|
}
|
|
|
|
// NewProfile creates a new cwtchPeer with a given name.
|
|
func (app *application) CreatePeer(name string) (peer.CwtchPeer, error) {
|
|
app.mutex.Lock()
|
|
p := peer.NewCwtchPeer(name)
|
|
|
|
err := app.profileStore.AddProfile(storage.GroupMaster, p.GetProfile())
|
|
if err != nil {
|
|
log.Printf("Could not add profile to profile store: %v", err)
|
|
return nil, err
|
|
}
|
|
app.peers[p.GetProfile().Onion] = p
|
|
|
|
app.startPeer(p)
|
|
return p, nil
|
|
}
|
|
|
|
func (app *application) startPeer(peer peer.CwtchPeer) {
|
|
go func() {
|
|
e := peer.Listen()
|
|
if e != nil {
|
|
log.Panic(e)
|
|
}
|
|
}()
|
|
}
|
|
|
|
// ListPeers returns a map of onions to their profile's Name
|
|
func (app *application) ListPeers() map[string]string {
|
|
keys := map[string]string{}
|
|
for k, p := range app.peers {
|
|
keys[k] = p.GetProfile().Name
|
|
}
|
|
return keys
|
|
}
|
|
|
|
// GetPeer returns a Peer for a given onion address
|
|
func (app *application) GetPeer(onion string) peer.CwtchPeer {
|
|
if peer, ok := app.peers[onion]; ok {
|
|
return peer
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Shutdown shutsdown all peers of an app and then the tormanager
|
|
func (app *application) Shutdown() {
|
|
for _, peer := range app.peers {
|
|
peer.Shutdown()
|
|
}
|
|
app.torManager.Shutdown()
|
|
}
|