package app import ( "crypto/rand" "cwtch.im/cwtch/peer" "cwtch.im/cwtch/storage" "encoding/hex" "fmt" "git.openprivacy.ca/openprivacy/libricochet-go/connectivity" "git.openprivacy.ca/openprivacy/libricochet-go/log" "io/ioutil" "os" "path" "path/filepath" "sync" ) type application struct { peers map[string]peer.CwtchPeer acn connectivity.ACN directory string mutex sync.Mutex primaryonion string storage map[string]storage.ProfileStore } // Application is a full cwtch peer application. It allows management, usage and storage of multiple peers type Application interface { SaveProfile(cwtchPeer peer.CwtchPeer) LoadProfiles(password string) error CreatePeer(name string, password string) (peer.CwtchPeer, error) PrimaryIdentity() peer.CwtchPeer GetPeer(onion string) peer.CwtchPeer ListPeers() map[string]string LaunchPeers() //GetTorStatus() (map[string]string, error) Shutdown() } // NewApp creates a new app with some environment awareness and initializes a Tor Manager func NewApp(acn connectivity.ACN, appDirectory string) Application { log.Debugf("NewApp(%v)\n", appDirectory) app := &application{peers: make(map[string]peer.CwtchPeer), storage: make(map[string]storage.ProfileStore), directory: appDirectory, acn: acn} os.Mkdir(path.Join(app.directory, "profiles"), 0700) return app } func generateRandomFilename() string { randBytes := make([]byte, 16) rand.Read(randBytes) return filepath.Join(hex.EncodeToString(randBytes)) } func (app *application) SaveProfile(p peer.CwtchPeer) { app.mutex.Lock() defer app.mutex.Unlock() app.peers[p.GetProfile().Onion] = p app.storage[p.GetProfile().Onion].Save(p) } // NewProfile creates a new cwtchPeer with a given name. func (app *application) CreatePeer(name string, password string) (peer.CwtchPeer, error) { log.Debugf("CreatePeer(%v)\n", name) randomFileName := generateRandomFilename() fileStore := storage.CreateFileProfileStore(path.Join(app.directory, "profiles", randomFileName), password) p := peer.NewCwtchPeer(name) err := fileStore.Save(p) if err != nil { return nil, err } p.Init(app.acn) _, 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.storage[p.GetProfile().Onion] = fileStore 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 { fileStore := storage.CreateFileProfileStore(path.Join(app.directory, "profiles", file.Name()), password) p, err := fileStore.Load() if err != nil { continue } _, exists := app.peers[p.GetProfile().Onion] if exists { p.Shutdown() log.Errorf("profile for onion %v already exists", p.GetProfile().Onion) continue } p.Init(app.acn) app.mutex.Lock() app.peers[p.GetProfile().Onion] = p app.storage[p.GetProfile().Onion] = fileStore if app.primaryonion == "" { app.primaryonion = p.GetProfile().Onion } app.mutex.Unlock() } return nil } func (app *application) LaunchPeers() { for _, p := range app.peers { if !p.IsStarted() { p.Listen() } } } // 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 } // PrimaryIdentity returns a cwtchPeer for a given onion address func (app *application) PrimaryIdentity() peer.CwtchPeer { return app.peers[app.primaryonion] } // GetPeer returns a cwtchPeer for a given onion address func (app *application) GetPeer(onion string) peer.CwtchPeer { if peer, ok := app.peers[onion]; ok { return peer } return nil } /* // 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() } }