package v0 import ( "cwtch.im/cwtch/event" "cwtch.im/cwtch/model" "encoding/json" "fmt" "os" "time" ) const groupIDLen = 32 const peerIDLen = 56 const profileFilename = "profile" // ProfileStoreV0 is a legacy profile store used now for upgrading legacy profile stores to newer versions type ProfileStoreV0 struct { fs FileStore streamStores map[string]StreamStore // map [groupId|onion] StreamStore directory string password string profile *model.Profile } // NewProfileWriterStore returns a profile store backed by a filestore listening for events and saving them // directory should be $appDir/profiles/$rand func NewProfileWriterStore(eventManager event.Manager, directory, password string, profile *model.Profile) *ProfileStoreV0 { os.Mkdir(directory, 0700) ps := &ProfileStoreV0{fs: NewFileStore(directory, profileFilename, password), password: password, directory: directory, profile: profile, streamStores: map[string]StreamStore{}} if profile != nil { ps.save() } return ps } // ReadProfile reads a profile from storqage and returns the profile // directory should be $appDir/profiles/$rand func ReadProfile(directory, password string) (*model.Profile, error) { os.Mkdir(directory, 0700) ps := &ProfileStoreV0{fs: NewFileStore(directory, profileFilename, password), password: password, directory: directory, profile: nil, streamStores: map[string]StreamStore{}} err := ps.Load() if err != nil { return nil, err } profile := ps.getProfileCopy(true) return profile, nil } /********************************************************************************************/ // AddGroup For testing, adds a group to the profile (and startsa stream store) func (ps *ProfileStoreV0) AddGroup(invite []byte, peer string) { gid, err := ps.profile.ProcessInvite(string(invite), peer) if err == nil { ps.save() group := ps.profile.Groups[gid] ps.streamStores[group.GroupID] = NewStreamStore(ps.directory, group.LocalID, ps.password) } } // AddGroupMessage for testing, adds a group message func (ps *ProfileStoreV0) AddGroupMessage(groupid string, timeSent, timeRecvied string, remotePeer, data string) { received, _ := time.Parse(time.RFC3339Nano, timeRecvied) sent, _ := time.Parse(time.RFC3339Nano, timeSent) message := model.Message{Received: received, Timestamp: sent, Message: data, PeerID: remotePeer, Signature: []byte("signature"), PreviousMessageSig: []byte("PreviousSignature")} ss, exists := ps.streamStores[groupid] if exists { ss.Write(message) } else { fmt.Println("ERROR") } } // GetNewPeerMessage is for AppService to call on Reload events, to reseed the AppClient with the loaded peers func (ps *ProfileStoreV0) GetNewPeerMessage() *event.Event { message := event.NewEventList(event.NewPeer, event.Identity, ps.profile.LocalID, event.Password, ps.password, event.Status, "running") return &message } // Load instantiates a cwtchPeer from the file store func (ps *ProfileStoreV0) Load() error { decrypted, err := ps.fs.Read() if err != nil { return err } cp := new(model.Profile) err = json.Unmarshal(decrypted, &cp) if err == nil { ps.profile = cp for gid, group := range cp.Groups { ss := NewStreamStore(ps.directory, group.LocalID, ps.password) cp.Groups[gid].Timeline.SetMessages(ss.Read()) ps.streamStores[group.GroupID] = ss } } return err } func (ps *ProfileStoreV0) getProfileCopy(timeline bool) *model.Profile { return ps.profile.GetCopy(timeline) } // Shutdown saves the storage system func (ps *ProfileStoreV0) Shutdown() { ps.save() } /************* Writing *************/ func (ps *ProfileStoreV0) save() error { bytes, _ := json.Marshal(ps.profile) return ps.fs.Write(bytes) }