2020-01-13 22:11:00 +00:00
|
|
|
package v1
|
|
|
|
|
|
|
|
import (
|
|
|
|
"cwtch.im/cwtch/event"
|
|
|
|
"cwtch.im/cwtch/model"
|
|
|
|
"encoding/json"
|
2020-02-10 22:09:24 +00:00
|
|
|
"git.openprivacy.ca/openprivacy/log"
|
2022-09-06 19:41:52 +00:00
|
|
|
"os"
|
2020-01-13 22:11:00 +00:00
|
|
|
"path"
|
|
|
|
)
|
|
|
|
|
|
|
|
const profileFilename = "profile"
|
|
|
|
const saltFile = "SALT"
|
|
|
|
|
2022-09-06 19:41:52 +00:00
|
|
|
// ProfileStoreV1 storage for profiles and message streams that uses in memory key and fs stored salt instead of in memory password
|
2020-01-13 22:11:00 +00:00
|
|
|
type ProfileStoreV1 struct {
|
2021-11-11 00:41:43 +00:00
|
|
|
fs FileStore
|
|
|
|
directory string
|
|
|
|
profile *model.Profile
|
|
|
|
key [32]byte
|
|
|
|
salt [128]byte
|
2020-01-13 22:11:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LoadProfileWriterStore loads a profile store from filestore listening for events and saving them
|
|
|
|
// directory should be $appDir/profiles/$rand
|
2021-11-18 23:43:58 +00:00
|
|
|
func LoadProfileWriterStore(directory, password string) (*ProfileStoreV1, error) {
|
2022-09-06 19:41:52 +00:00
|
|
|
salt, err := os.ReadFile(path.Join(directory, saltFile))
|
2020-01-13 22:11:00 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-06-25 18:21:10 +00:00
|
|
|
key := CreateKey(password, salt)
|
2020-01-13 22:11:00 +00:00
|
|
|
|
2021-11-11 00:41:43 +00:00
|
|
|
ps := &ProfileStoreV1{fs: NewFileStore(directory, profileFilename, key), key: key, directory: directory, profile: nil}
|
2020-01-13 22:11:00 +00:00
|
|
|
copy(ps.salt[:], salt)
|
|
|
|
|
2020-03-18 23:10:03 +00:00
|
|
|
err = ps.load()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-01-13 22:11:00 +00:00
|
|
|
|
|
|
|
return ps, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// load instantiates a cwtchPeer from the file store
|
|
|
|
func (ps *ProfileStoreV1) load() error {
|
|
|
|
decrypted, err := ps.fs.Read()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
cp := new(model.Profile)
|
|
|
|
err = json.Unmarshal(decrypted, &cp)
|
2020-09-28 18:18:18 +00:00
|
|
|
|
2020-01-13 22:11:00 +00:00
|
|
|
if err == nil {
|
|
|
|
ps.profile = cp
|
|
|
|
|
2020-06-16 00:16:04 +00:00
|
|
|
// TODO 2020.06.09: v1 update, Remove on v2
|
|
|
|
// if we already have the contact it can be assumed "approved" unless blocked
|
|
|
|
for _, contact := range cp.Contacts {
|
|
|
|
if contact.Authorization == "" {
|
|
|
|
if contact.DeprecatedBlocked {
|
|
|
|
contact.Authorization = model.AuthBlocked
|
|
|
|
} else {
|
|
|
|
contact.Authorization = model.AuthApproved
|
|
|
|
}
|
|
|
|
}
|
2020-07-08 18:29:33 +00:00
|
|
|
|
2021-11-16 23:14:34 +00:00
|
|
|
if contact.Attributes[event.SaveHistoryKey] == event.SaveHistoryConfirmed {
|
2020-07-08 18:29:33 +00:00
|
|
|
ss := NewStreamStore(ps.directory, contact.LocalID, ps.key)
|
2023-11-18 19:49:52 +00:00
|
|
|
if contact, exists := cp.Contacts[contact.Onion]; exists {
|
|
|
|
contact.Timeline.SetMessages(ss.Read())
|
|
|
|
}
|
2020-07-08 18:29:33 +00:00
|
|
|
}
|
2020-06-16 00:16:04 +00:00
|
|
|
}
|
|
|
|
|
2020-01-13 22:11:00 +00:00
|
|
|
for gid, group := range cp.Groups {
|
2020-09-28 18:18:18 +00:00
|
|
|
if group.Version == 0 {
|
2023-01-25 20:32:26 +00:00
|
|
|
log.Debugf("group %v is of unsupported version 0. dropping group...\n", group.GroupID)
|
2020-09-28 18:18:18 +00:00
|
|
|
delete(cp.Groups, gid)
|
|
|
|
continue
|
|
|
|
}
|
2020-01-13 22:11:00 +00:00
|
|
|
ss := NewStreamStore(ps.directory, group.LocalID, ps.key)
|
2023-11-18 19:49:52 +00:00
|
|
|
if group, exists := cp.Groups[gid]; exists {
|
|
|
|
group.Timeline.SetMessages(ss.Read())
|
|
|
|
group.Timeline.Sort()
|
|
|
|
}
|
2020-01-13 22:11:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetProfileCopy returns a copy of the stored profile
|
|
|
|
func (ps *ProfileStoreV1) GetProfileCopy(timeline bool) *model.Profile {
|
|
|
|
return ps.profile.GetCopy(timeline)
|
|
|
|
}
|