forked from cwtch.im/cwtch
new profile storeage on eventbus
This commit is contained in:
parent
ffc4254f18
commit
84e31f02fe
37
app/app.go
37
app/app.go
|
@ -29,7 +29,6 @@ type application struct {
|
|||
|
||||
// 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)
|
||||
|
||||
|
@ -59,21 +58,16 @@ func generateRandomFilename() string {
|
|||
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.NewFileStore(path.Join(app.directory, "profiles", randomFileName), password)
|
||||
// TODO: eventBus per profile
|
||||
profileStore, err := storage.NewProfileStore(app.eventBus, path.Join(app.directory, "profiles", randomFileName), password)
|
||||
profileStore.Init(name)
|
||||
p := peer.NewCwtchPeer(name)
|
||||
err := fileStore.Save(p)
|
||||
app.eventBus.Publish(event.NewEvent(event.SetProfileName, map[string]string{"Name": name}))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -85,7 +79,7 @@ func (app *application) CreatePeer(name string, password string) (peer.CwtchPeer
|
|||
}
|
||||
app.mutex.Lock()
|
||||
app.peers[p.GetProfile().Onion] = p
|
||||
app.storage[p.GetProfile().Onion] = fileStore
|
||||
app.storage[p.GetProfile().Onion] = profileStore
|
||||
app.mutex.Unlock()
|
||||
|
||||
return p, nil
|
||||
|
@ -99,27 +93,30 @@ func (app *application) LoadProfiles(password string) error {
|
|||
|
||||
for _, file := range files {
|
||||
|
||||
fileStore := storage.NewFileStore(path.Join(app.directory, "profiles", file.Name()), password)
|
||||
// TODO: Per profile eventBus
|
||||
profileStore, err := storage.NewProfileStore(app.eventBus, path.Join(app.directory, "profiles", file.Name()), password)
|
||||
|
||||
p, err := fileStore.Load()
|
||||
err = profileStore.Load()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
_, exists := app.peers[p.GetProfile().Onion]
|
||||
profile := profileStore.GetProfileCopy()
|
||||
_, exists := app.peers[profile.Onion]
|
||||
if exists {
|
||||
p.Shutdown()
|
||||
log.Errorf("profile for onion %v already exists", p.GetProfile().Onion)
|
||||
profileStore.Shutdown()
|
||||
log.Errorf("profile for onion %v already exists", profile.Onion)
|
||||
continue
|
||||
}
|
||||
|
||||
p.Init(app.acn, app.eventBus)
|
||||
peer := peer.FromProfile(profile)
|
||||
peer.Init(app.acn, app.eventBus)
|
||||
|
||||
app.mutex.Lock()
|
||||
app.peers[p.GetProfile().Onion] = p
|
||||
app.storage[p.GetProfile().Onion] = fileStore
|
||||
app.peers[profile.Onion] = peer
|
||||
app.storage[profile.Onion] = profileStore
|
||||
if app.primaryonion == "" {
|
||||
app.primaryonion = p.GetProfile().Onion
|
||||
app.primaryonion = profile.Onion
|
||||
}
|
||||
app.mutex.Unlock()
|
||||
}
|
||||
|
|
|
@ -548,8 +548,6 @@ func main() {
|
|||
fmt.Printf("%v", commands)
|
||||
fmt.Printf("Error importing group, usage: %s\n", usages[commands[0]])
|
||||
}
|
||||
case "/save":
|
||||
app.SaveProfile(peer)
|
||||
case "/help":
|
||||
for _, command := range suggestions {
|
||||
fmt.Printf("%-18s%-56s%s\n", command.Text, command.Description, usages[command.Text])
|
||||
|
@ -599,13 +597,6 @@ func main() {
|
|||
}
|
||||
}
|
||||
}
|
||||
if peer != nil {
|
||||
app.SaveProfile(peer)
|
||||
}
|
||||
}
|
||||
|
||||
if peer != nil {
|
||||
app.SaveProfile(peer)
|
||||
}
|
||||
|
||||
app.Shutdown()
|
||||
|
|
|
@ -1,54 +1,14 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/rand"
|
||||
libpeer "cwtch.im/cwtch/peer"
|
||||
storage2 "cwtch.im/cwtch/storage"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
|
||||
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
//"bufio"
|
||||
//"cwtch.im/cwtch/storage"
|
||||
)
|
||||
|
||||
func convertCwtchFile(filename string, password string) error {
|
||||
fileStore := storage2.NewFileStore(filename, password)
|
||||
peer, err := fileStore.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := []byte("== ed25519v1-secret: type0 ==")
|
||||
b = append(b, peer.GetProfile().Ed25519PrivateKey...)
|
||||
err = ioutil.WriteFile("hs_ed25519_secret_key", b, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b = []byte("== ed25519v1-public: type0 ==")
|
||||
b = append(b, peer.GetProfile().Ed25519PublicKey...)
|
||||
err = ioutil.WriteFile("hs_ed25519_public_key", b, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b = []byte(peer.GetProfile().Onion + ".onion\n")
|
||||
err = ioutil.WriteFile("hostname", b, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infoln("success!")
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertTorFile(filename string, password string) error {
|
||||
return errors.New("this code doesn't work and can never work :( it's a math thing")
|
||||
|
||||
|
@ -87,6 +47,7 @@ func convertTorFile(filename string, password string) error {
|
|||
return nil*/
|
||||
}
|
||||
|
||||
/*
|
||||
func vanity() error {
|
||||
for {
|
||||
pk, sk, err := ed25519.GenerateKey(rand.Reader)
|
||||
|
@ -100,16 +61,14 @@ func vanity() error {
|
|||
peer.GetProfile().Ed25519PrivateKey = sk
|
||||
peer.GetProfile().Ed25519PublicKey = pk
|
||||
peer.GetProfile().Onion = onion
|
||||
fileStore := storage2.NewFileStore(os.Args[3], onion+".cwtch")
|
||||
err := fileStore.Save(peer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
profileStore, _ := storage2.NewProfileStore(nil, os.Args[3], onion+".cwtch")
|
||||
profileStore.Init("")
|
||||
// need to signal new onion? impossible
|
||||
log.Infof("found %s.onion\n", onion)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
func printHelp() {
|
||||
log.Infoln("usage: cwtchutil {help, convert-cwtch-file, convert-tor-file, changepw, vanity}")
|
||||
|
@ -127,15 +86,6 @@ func main() {
|
|||
printHelp()
|
||||
case "help":
|
||||
printHelp()
|
||||
case "convert-cwtch-file":
|
||||
if len(os.Args) != 4 {
|
||||
fmt.Println("example: cwtchutil convert-cwtch-file ~/.cwtch/profiles/11ddd78a9918c064e742d5e36a8b8fd4 passw0rd")
|
||||
os.Exit(1)
|
||||
}
|
||||
err := convertCwtchFile(os.Args[2], os.Args[3])
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
case "convert-tor-file":
|
||||
if len(os.Args) != 4 {
|
||||
fmt.Println("example: cwtchutil convert-tor-file /var/lib/tor/hs1 passw0rd")
|
||||
|
@ -145,7 +95,7 @@ func main() {
|
|||
if err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
case "vanity":
|
||||
/*case "vanity":
|
||||
if len(os.Args) < 5 {
|
||||
fmt.Println("example: cwtchutil vanity 4 passw0rd erinn openpriv")
|
||||
os.Exit(1)
|
||||
|
@ -163,8 +113,8 @@ func main() {
|
|||
|
||||
for { // run until ctrl+c
|
||||
time.Sleep(time.Hour * 24)
|
||||
}
|
||||
case "changepw":
|
||||
}*/
|
||||
/*case "changepw":
|
||||
if len(os.Args) != 3 {
|
||||
fmt.Println("example: cwtch changepw ~/.cwtch/profiles/XXX")
|
||||
os.Exit(1)
|
||||
|
@ -179,9 +129,9 @@ func main() {
|
|||
}
|
||||
pw = pw[:len(pw)-1]
|
||||
|
||||
fileStore := storage2.NewFileStore(os.Args[2], pw)
|
||||
profileStore, _ := storage.NewProfileStore(nil, os.Args[2], pw)
|
||||
|
||||
peer, err := fileStore.Load()
|
||||
err = profileStore.Load()
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
os.Exit(1)
|
||||
|
@ -195,7 +145,8 @@ func main() {
|
|||
}
|
||||
newpw1 = newpw1[:len(newpw1)-1] // fuck go with this linebreak shit ^ea
|
||||
|
||||
fileStore2 := storage2.NewFileStore(os.Args[2], newpw1)
|
||||
fileStore2, _ := storage.NewProfileStore(nil, os.Args[2], newpw1)
|
||||
// No way to copy, populate this method
|
||||
err = fileStore2.Save(peer)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
|
@ -203,5 +154,6 @@ func main() {
|
|||
}
|
||||
|
||||
log.Infoln("success!")
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,4 +24,6 @@ const (
|
|||
|
||||
SendMessageToPeer = Type("SendMessageToPeer")
|
||||
NewMessageFromPeer = Type("NewMessageFromPeer")
|
||||
|
||||
SetProfileName = Type("SetProfileName")
|
||||
)
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"crypto/rand"
|
||||
"cwtch.im/cwtch/protocol"
|
||||
"encoding/base32"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
@ -32,8 +33,6 @@ type Profile struct {
|
|||
Contacts map[string]*PublicProfile
|
||||
Ed25519PrivateKey ed25519.PrivateKey
|
||||
Groups map[string]*Group
|
||||
Custom map[string]string
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// MaxGroupMessageLength is the maximum length of a message posted to a server group.
|
||||
|
@ -62,6 +61,7 @@ func (p *PublicProfile) GetAttribute(name string) (value string, exists bool) {
|
|||
// GenerateNewProfile creates a new profile, with new encryption and signing keys, and a profile name.
|
||||
func GenerateNewProfile(name string) *Profile {
|
||||
p := new(Profile)
|
||||
p.init()
|
||||
p.Name = name
|
||||
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
|
||||
p.Ed25519PublicKey = pub
|
||||
|
@ -71,7 +71,6 @@ func GenerateNewProfile(name string) *Profile {
|
|||
p.Contacts = make(map[string]*PublicProfile)
|
||||
p.Contacts[p.Onion] = &p.PublicProfile
|
||||
p.Groups = make(map[string]*Group)
|
||||
p.Custom = make(map[string]string)
|
||||
return p
|
||||
}
|
||||
|
||||
|
@ -149,21 +148,6 @@ func (p *Profile) GetGroups() []string {
|
|||
return keys
|
||||
}
|
||||
|
||||
// SetCustomAttribute allows applications to store arbitrary configuration info at the profile level.
|
||||
func (p *Profile) SetCustomAttribute(name string, value string) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
p.Custom[name] = value
|
||||
}
|
||||
|
||||
// GetCustomAttribute returns the value of a value set with SetCustomAttribute. If no such value has been set exists is set to false.
|
||||
func (p *Profile) GetCustomAttribute(name string) (value string, exists bool) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
value, exists = p.Custom[name]
|
||||
return
|
||||
}
|
||||
|
||||
// GetContacts returns an unordered list of contact onions associated with this profile.
|
||||
func (p *Profile) GetContacts() []string {
|
||||
p.lock.Lock()
|
||||
|
@ -370,3 +354,14 @@ func (p *Profile) EncryptMessageToGroup(message string, groupID string) ([]byte,
|
|||
}
|
||||
return nil, nil, errors.New("group does not exist")
|
||||
}
|
||||
|
||||
// GetCopy returns a full deep copy of the Profile struct and its members
|
||||
func (p *Profile) GetCopy() *Profile {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
newp := new(Profile)
|
||||
bytes, _ := json.Marshal(p)
|
||||
json.Unmarshal(bytes, &newp)
|
||||
return newp
|
||||
}
|
||||
|
|
|
@ -50,8 +50,8 @@ func TestProfileIdentity(t *testing.T) {
|
|||
t.Errorf("alice should be only contact: %v", alice.GetContacts())
|
||||
}
|
||||
|
||||
alice.SetCustomAttribute("test", "hello world")
|
||||
value, _ := alice.GetCustomAttribute("test")
|
||||
alice.SetAttribute("test", "hello world")
|
||||
value, _ := alice.GetAttribute("test")
|
||||
if value != "hello world" {
|
||||
t.Errorf("value from custom attribute should have been 'hello world', instead was: %v", value)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"cwtch.im/cwtch/server/listen"
|
||||
"cwtch.im/cwtch/server/metrics"
|
||||
"cwtch.im/cwtch/server/send"
|
||||
"cwtch.im/cwtch/storage"
|
||||
"cwtch.im/cwtch/server/storage"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/application"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
|
|
|
@ -3,7 +3,7 @@ package server
|
|||
import (
|
||||
"cwtch.im/cwtch/protocol"
|
||||
"cwtch.im/cwtch/server/listen"
|
||||
"cwtch.im/cwtch/storage"
|
||||
"cwtch.im/cwtch/server/storage"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/application"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
|
||||
)
|
||||
|
|
|
@ -3,7 +3,7 @@ package server
|
|||
import (
|
||||
"cwtch.im/cwtch/protocol"
|
||||
"cwtch.im/cwtch/server/metrics"
|
||||
"cwtch.im/cwtch/storage"
|
||||
"cwtch.im/cwtch/server/storage"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/application"
|
||||
"os"
|
||||
"testing"
|
||||
|
|
|
@ -17,6 +17,7 @@ type fileStore struct {
|
|||
password string
|
||||
}
|
||||
|
||||
// FileStore is a primitive around storing encrypted files
|
||||
type FileStore interface {
|
||||
Save([]byte) error
|
||||
Load() ([]byte, error)
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/peer"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFileProfileStore(t *testing.T) {
|
||||
fileStore := NewFileStore(".test.json", "password")
|
||||
alice := peer.NewCwtchPeer("alice")
|
||||
fileStore.Save(alice)
|
||||
|
||||
aliceLoaded, err := fileStore.Load()
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("alice profile should have been loaded from store instead %v", err)
|
||||
}
|
||||
|
||||
if aliceLoaded.GetProfile().Name != "alice" {
|
||||
t.Errorf("alice profile should have been loaded from store instead %v", aliceLoaded)
|
||||
}
|
||||
}
|
|
@ -1,39 +1,86 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/event"
|
||||
"cwtch.im/cwtch/model"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type profileStore struct {
|
||||
fs FileStore
|
||||
fs FileStore
|
||||
profile *model.Profile
|
||||
eventManager *event.Manager
|
||||
queue *event.Queue
|
||||
}
|
||||
|
||||
// ProfileStore is an interface to managing the storage of Cwtch Profiles
|
||||
type ProfileStore interface {
|
||||
Save(profile *model.Profile) error
|
||||
Load() (*model.Profile, error)
|
||||
Save() error
|
||||
Init(name string)
|
||||
Load() error
|
||||
Shutdown()
|
||||
GetProfileCopy() *model.Profile
|
||||
}
|
||||
|
||||
func NewProfileStore(filename string, password string) ProfileStore {
|
||||
return &profileStore{NewFileStore(filename, password)}
|
||||
// NewProfileStore returns a profile store backed by a filestore listening for events and saving them
|
||||
func NewProfileStore(eventManager *event.Manager, filename string, password string) (ProfileStore, error) {
|
||||
ps := &profileStore{fs: NewFileStore(filename, password), profile: nil, eventManager: eventManager}
|
||||
err := ps.Load()
|
||||
if err == nil {
|
||||
ps.queue = event.NewEventQueue(100)
|
||||
go ps.eventHandler()
|
||||
|
||||
ps.eventManager.Subscribe(event.BlockPeer, ps.queue.EventChannel)
|
||||
}
|
||||
return ps, err
|
||||
}
|
||||
|
||||
func (ps *profileStore) Save(profile *model.Profile) error {
|
||||
bytes, _ := json.Marshal(profile)
|
||||
func (ps *profileStore) Init(name string) {
|
||||
ps.profile = model.GenerateNewProfile(name)
|
||||
ps.Save()
|
||||
}
|
||||
|
||||
func (ps *profileStore) Save() error {
|
||||
bytes, _ := json.Marshal(ps.profile)
|
||||
return ps.fs.Save(bytes)
|
||||
}
|
||||
|
||||
// Load instantiates a cwtchPeer from the file store
|
||||
func (ps *profileStore) Load() (*model.Profile, error) {
|
||||
func (ps *profileStore) Load() error {
|
||||
decrypted, err := ps.fs.Load()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
cp := new(model.Profile)
|
||||
err = json.Unmarshal(decrypted, &cp)
|
||||
if err == nil {
|
||||
return cp, nil
|
||||
ps.profile = cp
|
||||
return nil
|
||||
}
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
func (ps *profileStore) GetProfileCopy() *model.Profile {
|
||||
return ps.profile.GetCopy()
|
||||
}
|
||||
|
||||
func (ps *profileStore) eventHandler() {
|
||||
for {
|
||||
ev := ps.queue.Next()
|
||||
switch ev.EventType {
|
||||
case event.BlockPeer:
|
||||
contact, exists := ps.profile.GetContact(ev.Data["Onion"])
|
||||
if exists {
|
||||
contact.Blocked = true
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
ps.Save()
|
||||
}
|
||||
}
|
||||
|
||||
func (ps *profileStore) Shutdown() {
|
||||
ps.queue.Shutdown()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue