cwtch/app/app.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()
}