cwtch/app/app.go

183 lines
4.7 KiB
Go
Raw Normal View History

2018-04-30 21:47:21 +00:00
package app
import (
"crypto/rand"
"cwtch.im/cwtch/connectivity/tor"
2018-05-28 18:05:06 +00:00
"cwtch.im/cwtch/peer"
"encoding/hex"
2018-07-01 18:43:05 +00:00
"fmt"
"io/ioutil"
"log"
2018-07-01 18:43:05 +00:00
"os"
"path"
"path/filepath"
"sync"
2018-04-30 21:47:21 +00:00
)
type application struct {
2018-10-04 19:15:03 +00:00
peers map[string]peer.CwtchPeer
torManager *tor.Manager
directory string
mutex sync.Mutex
2018-10-04 04:35:30 +00:00
primaryonion string
}
// Application is a full cwtch peer application. It allows management, usage and storage of multiple peers
type Application interface {
LoadProfiles(password string) error
CreatePeer(name string, password string) (peer.CwtchPeer, error)
2018-10-04 04:35:30 +00:00
PrimaryIdentity() peer.CwtchPeer
GetPeer(onion string) peer.CwtchPeer
ListPeers() map[string]string
2018-10-05 23:27:57 +00:00
GetTorStatus() (map[string]string, error)
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)
os.Mkdir(path.Join(app.directory, "profiles"), 0700)
err := app.startTor(torPath)
if err != nil {
return nil, err
}
return app, nil
2018-04-30 21:47:21 +00:00
}
func generateRandomFilename() string {
randBytes := make([]byte, 16)
rand.Read(randBytes)
return filepath.Join(hex.EncodeToString(randBytes))
}
// NewProfile creates a new cwtchPeer with a given name.
func (app *application) CreatePeer(name string, password string) (peer.CwtchPeer, error) {
log.Printf("CreatePeer(%v)\n", name)
randomFileName := generateRandomFilename()
p, err := peer.NewCwtchPeer(name, password, path.Join(app.directory, "profiles", randomFileName))
if err != nil {
return nil, err
}
err = p.Save()
if err != nil {
p.Shutdown() //attempt
return nil, fmt.Errorf("Error attempting to save new profile: %v", err)
}
app.startPeer(p)
_, exists := app.peers[p.GetProfile().Onion]
if exists {
p.Shutdown()
return nil, fmt.Errorf("Error: profile for onion %v already exists", p.GetProfile().Onion)
}
app.mutex.Lock()
app.peers[p.GetProfile().Onion] = p
app.mutex.Unlock()
return p, nil
}
func (app *application) LoadProfiles(password string) error {
files, err := ioutil.ReadDir(path.Join(app.directory, "profiles"))
if err != nil {
return fmt.Errorf("Error: cannot read profiles directory: %v", err)
}
for _, file := range files {
p, err := peer.LoadCwtchPeer(path.Join(app.directory, "profiles", file.Name()), password)
if err != nil {
continue
}
_, exists := app.peers[p.GetProfile().Onion]
if exists {
p.Shutdown()
log.Printf("Error: profile for onion %v already exists", p.GetProfile().Onion)
continue
}
app.startPeer(p)
app.mutex.Lock()
app.peers[p.GetProfile().Onion] = p
2018-10-04 04:35:30 +00:00
if app.primaryonion == "" {
app.primaryonion = p.GetProfile().Onion
}
app.mutex.Unlock()
}
return nil
2018-04-30 21:47:21 +00:00
}
// startTor will create a local torrc if needed
func (app *application) startTor(torPath string) error {
2018-07-01 18:43:05 +00:00
// Creating a local cwtch tor server config for the user
// creating $app.directory/torrc file
2018-07-01 18:43:05 +00:00
// SOCKSPort socksPort
// ControlPort controlPort
torrc := path.Join(app.directory, "tor", "torrc")
2018-07-01 18:43:05 +00:00
if _, err := os.Stat(torrc); os.IsNotExist(err) {
log.Printf("writing torrc to: %v\n", torrc)
2018-07-01 18:43:05 +00:00
file, err := os.Create(torrc)
if err != nil {
return err
}
fmt.Fprintf(file, "SOCKSPort %d\nControlPort %d\nCookieAuthentication 0\nSafeSocks 1\n", 9050, 9051)
2018-07-01 18:43:05 +00:00
file.Close()
}
tm, err := tor.NewTorManager(9050, 9051, torPath, torrc)
2018-07-01 18:43:05 +00:00
if err != nil {
return err
}
app.torManager = tm
2018-07-01 18:43:05 +00:00
return nil
}
func (app *application) startPeer(peer peer.CwtchPeer) {
go func() {
e := peer.Listen()
if e != nil {
log.Fatalf("ERROR: peer %v has crashed with: %v\n", peer.GetProfile().Onion, 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
}
2018-10-04 04:35:30 +00:00
// PrimaryIdentity returns a Peer for a given onion address
func (app *application) PrimaryIdentity() peer.CwtchPeer {
return app.peers[app.primaryonion]
}
// 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
2018-04-30 21:47:21 +00:00
}
2018-10-05 23:27:57 +00:00
// GetTorStatus returns tor control port bootstrap-phase status info in a map
func (app *application) GetTorStatus() (map[string]string, error) {
return app.torManager.GetStatus()
}
// 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()
2018-04-30 21:47:21 +00:00
}