Fixes from Cwtch UI Integration
This commit is contained in:
parent
2caaa7eb87
commit
72ac4099d5
13
app/app.go
13
app/app.go
|
@ -160,21 +160,16 @@ func (ac *applicationCore) LoadProfiles(password string, timeline bool, loadProf
|
|||
// Attempt to load an encrypted database
|
||||
profileDirectory := path.Join(ac.directory, "profiles", file.Name())
|
||||
profile, err := peer.FromEncryptedDatabase(profileDirectory, password)
|
||||
|
||||
if err == nil {
|
||||
// return the load the profile...
|
||||
log.Infof("loading profile from new-type storage database...")
|
||||
loadProfileFn(profile)
|
||||
}
|
||||
|
||||
// On failure attempt to load a legacy profile
|
||||
if err != nil {
|
||||
eventBus := event.NewEventManager()
|
||||
|
||||
profileStore, err := storage.LoadProfileWriterStore(eventBus, profileDirectory, password)
|
||||
} else { // On failure attempt to load a legacy profile
|
||||
profileStore, err := storage.LoadProfileWriterStore(profileDirectory, password)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Infof("found legacy profile. importing to new database structure...")
|
||||
legacyProfile := profileStore.GetProfileCopy(timeline)
|
||||
|
||||
cps, err := peer.CreateEncryptedStore(profileDirectory, password)
|
||||
|
|
|
@ -30,6 +30,9 @@ const True = "true"
|
|||
// False - false
|
||||
const False = "false"
|
||||
|
||||
// AttrAuthor - conversation attribute for author of the message - referenced by pub key rather than conversation id because of groups.
|
||||
const AttrAuthor = "author"
|
||||
|
||||
// AttrAck - conversation attribute for acknowledgement status
|
||||
const AttrAck = "ack"
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ type Group struct {
|
|||
GroupName string
|
||||
GroupKey [32]byte
|
||||
GroupServer string
|
||||
Attributes map[string]string //legacy to not use
|
||||
Version int
|
||||
Timeline Timeline `json:"-"`
|
||||
LocalID string
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
)
|
||||
|
||||
const lastKnownSignature = "LastKnowSignature"
|
||||
const lastReceivedSignature = "LastReceivedSignature"
|
||||
|
||||
var autoHandleableEvents = map[event.Type]bool{event.EncryptedGroupMessage: true, event.PeerStateChange: true,
|
||||
event.ServerStateChange: true, event.NewGroupInvite: true, event.NewMessageFromPeer: true,
|
||||
|
@ -160,7 +161,7 @@ func (cp *cwtchPeer) SendMessage(conversation int, message string) error {
|
|||
onion, _ := cp.storage.LoadProfileKeyValue(TypeAttribute, attr.PublicScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.Onion)).ToString())
|
||||
|
||||
// For p2p messages we store the event id of the message as the "signature" we can then look this up in the database later for acks
|
||||
err := cp.storage.InsertMessage(conversationInfo.ID, 0, message, model.Attributes{constants.AttrAck: event.False, constants.AttrSentTimestamp: time.Now().Format(time.RFC3339Nano)}, ev.EventID, model.CalculateContentHash(string(onion), message))
|
||||
err := cp.storage.InsertMessage(conversationInfo.ID, 0, message, model.Attributes{constants.AttrAuthor: string(onion), constants.AttrAck: event.False, constants.AttrSentTimestamp: time.Now().Format(time.RFC3339Nano)}, ev.EventID, model.CalculateContentHash(string(onion), message))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -192,7 +193,7 @@ func (cp *cwtchPeer) SendMessage(conversation int, message string) error {
|
|||
}
|
||||
|
||||
// Insert the Group Message
|
||||
err = cp.storage.InsertMessage(conversationInfo.ID, 0, dm.Text, model.Attributes{constants.AttrAck: constants.False, "PreviousSignature": base64.StdEncoding.EncodeToString(dm.PreviousMessageSig), "Author": dm.Onion, constants.AttrSentTimestamp: strconv.Itoa(int(dm.Timestamp))}, base64.StdEncoding.EncodeToString(sig), model.CalculateContentHash(dm.Onion, dm.Text))
|
||||
err = cp.storage.InsertMessage(conversationInfo.ID, 0, dm.Text, model.Attributes{constants.AttrAck: constants.False, "PreviousSignature": base64.StdEncoding.EncodeToString(dm.PreviousMessageSig), constants.AttrAuthor: dm.Onion, constants.AttrSentTimestamp: time.Now().Format(time.RFC3339Nano)}, base64.StdEncoding.EncodeToString(sig), model.CalculateContentHash(dm.Onion, dm.Text))
|
||||
if err == nil {
|
||||
ev := event.NewEvent(event.SendMessageToGroup, map[event.Field]string{event.ConversationID: strconv.Itoa(conversationInfo.ID), event.GroupID: conversationInfo.Handle, event.GroupServer: group.GroupServer, event.Ciphertext: base64.StdEncoding.EncodeToString(ct), event.Signature: base64.StdEncoding.EncodeToString(sig)})
|
||||
cp.eventBus.Publish(ev)
|
||||
|
@ -264,7 +265,9 @@ func ImportLegacyProfile(profile *model.Profile, cps *CwtchProfileStorage) Cwtch
|
|||
cp := new(cwtchPeer)
|
||||
cp.shutdown = false
|
||||
cp.storage = cps
|
||||
cp.eventBus = event.NewEventManager()
|
||||
cp.queue = event.NewQueue()
|
||||
cp.state = make(map[string]connections.ConnectionState)
|
||||
// Store all the Necessary Base Attributes In The Database
|
||||
cp.SetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, constants.Name, profile.Name)
|
||||
cp.SetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.Name, profile.Name)
|
||||
|
@ -274,7 +277,7 @@ func ImportLegacyProfile(profile *model.Profile, cps *CwtchProfileStorage) Cwtch
|
|||
|
||||
for k, v := range profile.Attributes {
|
||||
parts := strings.SplitN(k, ".", 2)
|
||||
if len(parts) != 2 {
|
||||
if len(parts) == 2 {
|
||||
scope := attr.IntoScope(parts[0])
|
||||
zone, path := attr.ParseZone(parts[1])
|
||||
cp.SetScopedZonedAttribute(scope, zone, path, v)
|
||||
|
@ -297,10 +300,6 @@ func ImportLegacyProfile(profile *model.Profile, cps *CwtchProfileStorage) Cwtch
|
|||
if err == nil {
|
||||
for key, value := range contact.Attributes {
|
||||
switch key {
|
||||
case "name":
|
||||
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.Name)), value)
|
||||
case "local.profile.name":
|
||||
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.Name)), value)
|
||||
case event.SaveHistoryKey:
|
||||
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(event.SaveHistoryKey)), value)
|
||||
case string(model.BundleType):
|
||||
|
@ -311,13 +310,23 @@ func ImportLegacyProfile(profile *model.Profile, cps *CwtchProfileStorage) Cwtch
|
|||
// ignore
|
||||
case string(model.KeyTypePrivacyPass):
|
||||
// ignore
|
||||
case lastKnownSignature:
|
||||
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(lastReceivedSignature)), value)
|
||||
default:
|
||||
log.Errorf("could not import conversation attribute %v %v", key, value)
|
||||
}
|
||||
}
|
||||
|
||||
if name, exists := contact.Attributes["local.name"]; exists {
|
||||
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.Name)), name)
|
||||
}
|
||||
if name, exists := contact.Attributes["peer.name"]; exists {
|
||||
cp.SetConversationAttribute(conversationID, attr.PublicScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.Name)), name)
|
||||
}
|
||||
|
||||
for _, message := range contact.Timeline.GetMessages() {
|
||||
// By definition anything stored in legacy timelines in acknowledged
|
||||
attr := model.Attributes{constants.AttrAck: event.True, constants.AttrSentTimestamp: message.Timestamp.Format(time.RFC3339Nano)}
|
||||
attr := model.Attributes{constants.AttrAuthor: message.PeerID, constants.AttrAck: event.True, constants.AttrSentTimestamp: message.Timestamp.Format(time.RFC3339Nano)}
|
||||
if message.Flags&0x01 == 0x01 {
|
||||
attr[constants.AttrRejected] = event.True
|
||||
}
|
||||
|
@ -330,15 +339,15 @@ func ImportLegacyProfile(profile *model.Profile, cps *CwtchProfileStorage) Cwtch
|
|||
}
|
||||
|
||||
for _, group := range profile.Groups {
|
||||
group.GroupName = group.Attributes["local.name"]
|
||||
invite, err := group.Invite()
|
||||
if err == nil {
|
||||
// Automatically grab all the important fields...
|
||||
// Including Name...
|
||||
conversationID, err := cp.ImportGroup(invite)
|
||||
if err == nil {
|
||||
for _, message := range group.Timeline.GetMessages() {
|
||||
// By definition anything stored in legacy timelines in acknowledged
|
||||
attr := model.Attributes{constants.AttrAck: event.True, constants.AttrSentTimestamp: message.Timestamp.Format(time.RFC3339Nano)}
|
||||
attr := model.Attributes{constants.AttrAuthor: message.PeerID, constants.AttrAck: event.True, constants.AttrSentTimestamp: message.Timestamp.Format(time.RFC3339Nano)}
|
||||
if message.Flags&0x01 == 0x01 {
|
||||
attr[constants.AttrRejected] = event.True
|
||||
}
|
||||
|
@ -350,7 +359,7 @@ func ImportLegacyProfile(profile *model.Profile, cps *CwtchProfileStorage) Cwtch
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
cp.eventBus.Shutdown() // We disregard all events from profile...
|
||||
return cp
|
||||
}
|
||||
|
||||
|
@ -446,7 +455,7 @@ func (cp *cwtchPeer) ImportGroup(exportedInvite string) (int, error) {
|
|||
cp.SetConversationAttribute(groupConversationID, attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.GroupID)), gci.GroupID)
|
||||
cp.SetConversationAttribute(groupConversationID, attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.GroupServer)), gci.ServerHost)
|
||||
cp.SetConversationAttribute(groupConversationID, attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.GroupKey)), base64.StdEncoding.EncodeToString(gci.SharedKey))
|
||||
cp.SetConversationAttribute(groupConversationID, attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.Name)), gci.GroupName)
|
||||
cp.SetConversationAttribute(groupConversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.Name)), gci.GroupName)
|
||||
cp.eventBus.Publish(event.NewEvent(event.NewGroup, map[event.Field]string{event.ConversationID: strconv.Itoa(groupConversationID), event.GroupServer: gci.ServerHost, event.GroupInvite: exportedInvite}))
|
||||
}
|
||||
return groupConversationID, err
|
||||
|
@ -570,7 +579,7 @@ func (cp *cwtchPeer) StartGroup(name string, server string) (int, error) {
|
|||
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.GroupID)), group.GroupID)
|
||||
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.GroupServer)), group.GroupServer)
|
||||
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.GroupKey)), base64.StdEncoding.EncodeToString(group.GroupKey[:]))
|
||||
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.Name)), name)
|
||||
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.Name)), name)
|
||||
|
||||
cp.eventBus.Publish(event.NewEvent(event.GroupCreated, map[event.Field]string{
|
||||
event.ConversationID: strconv.Itoa(conversationID),
|
||||
|
@ -710,7 +719,7 @@ func (cp *cwtchPeer) SendInviteToConversation(conversationID int, inviteConversa
|
|||
if !ok {
|
||||
return errors.New("group structure is malformed - no key")
|
||||
}
|
||||
groupName, ok := inviteConversationInfo.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.Name)).ToString()]
|
||||
groupName, ok := inviteConversationInfo.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.Name)).ToString()]
|
||||
if !ok {
|
||||
return errors.New("group structure is malformed - no name")
|
||||
}
|
||||
|
@ -804,7 +813,7 @@ func (cp *cwtchPeer) JoinServer(onion string) error {
|
|||
tokenY, yExists := ci.Attributes[attr.PublicScope.ConstructScopedZonedPath(attr.ServerKeyZone.ConstructZonedPath(string(model.KeyTypePrivacyPass))).ToString()]
|
||||
tokenOnion, onionExists := ci.Attributes[attr.PublicScope.ConstructScopedZonedPath(attr.ServerKeyZone.ConstructZonedPath(string(model.KeyTypeTokenOnion))).ToString()]
|
||||
if yExists && onionExists {
|
||||
signature, exists := ci.Attributes[attr.PublicScope.ConstructScopedZonedPath(attr.ServerKeyZone.ConstructZonedPath(lastKnownSignature)).ToString()]
|
||||
signature, exists := ci.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(lastReceivedSignature)).ToString()]
|
||||
if !exists {
|
||||
signature = base64.StdEncoding.EncodeToString([]byte{})
|
||||
}
|
||||
|
@ -908,7 +917,7 @@ func (cp *cwtchPeer) storeMessage(handle string, message string, sent time.Time)
|
|||
|
||||
// Generate a random number and use it as the signature
|
||||
signature := event.GetRandNumber().String()
|
||||
return cp.storage.InsertMessage(ci.ID, 0, message, model.Attributes{constants.AttrAck: event.True, constants.AttrSentTimestamp: sent.Format(time.RFC3339Nano)}, signature, model.CalculateContentHash(handle, message))
|
||||
return cp.storage.InsertMessage(ci.ID, 0, message, model.Attributes{constants.AttrAuthor: handle, constants.AttrAck: event.True, constants.AttrSentTimestamp: sent.Format(time.RFC3339Nano)}, signature, model.CalculateContentHash(handle, message))
|
||||
}
|
||||
|
||||
// ShareFile begins hosting the given serialized manifest
|
||||
|
@ -1138,9 +1147,9 @@ func (cp *cwtchPeer) attemptInsertOrAcknowledgeLegacyGroupConversation(conversat
|
|||
}
|
||||
} else {
|
||||
cp.mutex.Lock()
|
||||
cp.storage.InsertMessage(conversationID, 0, dm.Text, model.Attributes{constants.AttrAck: constants.True, "PreviousSignature": base64.StdEncoding.EncodeToString(dm.PreviousMessageSig), "Author": dm.Onion, constants.AttrSentTimestamp: strconv.Itoa(int(dm.Timestamp))}, signature, model.CalculateContentHash(dm.Onion, dm.Text))
|
||||
cp.storage.InsertMessage(conversationID, 0, dm.Text, model.Attributes{constants.AttrAck: constants.True, "PreviousSignature": base64.StdEncoding.EncodeToString(dm.PreviousMessageSig), constants.AttrAuthor: dm.Onion, constants.AttrSentTimestamp: time.Unix(int64(dm.Timestamp), 0).Format(time.RFC3339Nano)}, signature, model.CalculateContentHash(dm.Onion, dm.Text))
|
||||
cp.mutex.Unlock()
|
||||
cp.eventBus.Publish(event.NewEvent(event.NewMessageFromGroup, map[event.Field]string{event.ConversationID: strconv.Itoa(conversationID), event.Index: strconv.Itoa(messageID)}))
|
||||
cp.eventBus.Publish(event.NewEvent(event.NewMessageFromGroup, map[event.Field]string{event.ConversationID: strconv.Itoa(conversationID), event.RemotePeer: dm.Onion, event.Index: strconv.Itoa(messageID)}))
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
|
@ -1204,6 +1213,12 @@ func (cp *cwtchPeer) GetChannelMessageBySignature(conversationID int, channelID
|
|||
return cp.storage.GetChannelMessageBySignature(conversationID, channelID, signature)
|
||||
}
|
||||
|
||||
func (cp *cwtchPeer) GetChannelMessageByContentHash(conversationID int, channelID int, contenthash string) (int, error) {
|
||||
cp.mutex.Lock()
|
||||
defer cp.mutex.Unlock()
|
||||
return cp.storage.GetChannelMessageByContentHash(conversationID, channelID, contenthash)
|
||||
}
|
||||
|
||||
func (cp *cwtchPeer) constructGroupFromConversation(conversationInfo *model.Conversation) (*model.Group, error) {
|
||||
key := conversationInfo.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.GroupKey)).ToString()]
|
||||
groupKey, err := base64.StdEncoding.DecodeString(key)
|
||||
|
|
|
@ -49,6 +49,7 @@ type CwtchProfileStorage struct {
|
|||
channelGetMessageBySignatureStmts map[ChannelID]*sql.Stmt
|
||||
channelGetCountStmts map[ChannelID]*sql.Stmt
|
||||
channelGetMostRecentMessagesStmts map[ChannelID]*sql.Stmt
|
||||
channelGetMessageByContentHashStmts map[ChannelID]*sql.Stmt
|
||||
|
||||
db *sql.DB
|
||||
}
|
||||
|
@ -87,7 +88,7 @@ const getMessageFromConversationSQLStmt = `select Body, Attributes from channel_
|
|||
const getMessageBySignatureFromConversationSQLStmt = `select ID from channel_%d_%d_chat where Signature=(?);`
|
||||
|
||||
// getMessageByContentHashFromConversationSQLStmt is a template for selecting conversation messages by content hash
|
||||
const getMessageByContentHashFromConversationSQLStmt = `select ID from channel_%d_%d_chat where ContentHash=(?);`
|
||||
const getMessageByContentHashFromConversationSQLStmt = `select ID from channel_%d_%d_chat where ContentHash=(?) order by ID desc limit 1;`
|
||||
|
||||
// getMessageCountFromConversationSQLStmt is a template for fetching the count of a messages in a conversation channel
|
||||
const getMessageCountFromConversationSQLStmt = `select count(*) from channel_%d_%d_chat;`
|
||||
|
@ -178,6 +179,7 @@ func NewCwtchProfileStorage(db *sql.DB) (*CwtchProfileStorage, error) {
|
|||
channelUpdateMessageStmts: map[ChannelID]*sql.Stmt{},
|
||||
channelGetMessageStmts: map[ChannelID]*sql.Stmt{},
|
||||
channelGetMessageBySignatureStmts: map[ChannelID]*sql.Stmt{},
|
||||
channelGetMessageByContentHashStmts: map[ChannelID]*sql.Stmt{},
|
||||
channelGetMostRecentMessagesStmts: map[ChannelID]*sql.Stmt{},
|
||||
channelGetCountStmts: map[ChannelID]*sql.Stmt{}},
|
||||
nil
|
||||
|
@ -423,7 +425,6 @@ func (cps *CwtchProfileStorage) InsertMessage(conversation int, channel int, bod
|
|||
return err
|
||||
}
|
||||
|
||||
log.Infof("inserted message with signature: %v", signature)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -489,6 +490,43 @@ func (cps *CwtchProfileStorage) GetChannelMessageBySignature(conversation int, c
|
|||
return id, nil
|
||||
}
|
||||
|
||||
// GetChannelMessageByContentHash looks up a conversation message by hash instead of identifier.
|
||||
func (cps *CwtchProfileStorage) GetChannelMessageByContentHash(conversation int, channel int, hash string) (int, error) {
|
||||
channelID := ChannelID{Conversation: conversation, Channel: channel}
|
||||
|
||||
_, exists := cps.channelGetMessageByContentHashStmts[channelID]
|
||||
if !exists {
|
||||
conversationStmt, err := cps.db.Prepare(fmt.Sprintf(getMessageByContentHashFromConversationSQLStmt, conversation, channel))
|
||||
if err != nil {
|
||||
log.Errorf("error executing transaction: %v", err)
|
||||
return -1, err
|
||||
}
|
||||
cps.channelGetMessageByContentHashStmts[channelID] = conversationStmt
|
||||
}
|
||||
|
||||
rows, err := cps.channelGetMessageByContentHashStmts[channelID].Query(hash)
|
||||
if err != nil {
|
||||
log.Errorf("error executing query: %v", err)
|
||||
return -1, err
|
||||
}
|
||||
|
||||
result := rows.Next()
|
||||
|
||||
if !result {
|
||||
return -1, errors.New("no result found")
|
||||
}
|
||||
|
||||
var id int
|
||||
err = rows.Scan(&id)
|
||||
if err != nil {
|
||||
log.Errorf("error fetching rows: %v", err)
|
||||
rows.Close()
|
||||
return -1, err
|
||||
}
|
||||
rows.Close()
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// GetChannelMessage looks up a channel message by conversation, channel and message id. On success it
|
||||
// returns the message body and the attributes associated with the message. Otherwise an error is returned.
|
||||
func (cps *CwtchProfileStorage) GetChannelMessage(conversation int, channel int, messageID int) (string, model.Attributes, error) {
|
||||
|
|
|
@ -114,6 +114,7 @@ type CwtchPeer interface {
|
|||
// New Unified Conversation Channel Interfaces
|
||||
GetChannelMessage(conversation int, channel int, id int) (string, model.Attributes, error)
|
||||
GetChannelMessageCount(conversation int, channel int) (int, error)
|
||||
GetChannelMessageByContentHash(conversation int, channel int, contenthash string) (int, error)
|
||||
GetMostRecentMessages(conversation int, channel int, offset int, limit int) ([]model.ConversationMessage, error)
|
||||
|
||||
ShareFile(fileKey string, serializedManifest string)
|
||||
|
|
|
@ -3,6 +3,7 @@ package peer
|
|||
import (
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/openprivacy/log"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
|
@ -63,7 +64,7 @@ func initV2Directory(directory, password string) ([32]byte, [128]byte, error) {
|
|||
return key, salt, nil
|
||||
}
|
||||
|
||||
func openEncryptedDatabase(profileDirectory string, password string) (*sql.DB, error) {
|
||||
func openEncryptedDatabase(profileDirectory string, password string, createIfNotExists bool) (*sql.DB, error) {
|
||||
salt, err := ioutil.ReadFile(path.Join(profileDirectory, saltFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -71,6 +72,13 @@ func openEncryptedDatabase(profileDirectory string, password string) (*sql.DB, e
|
|||
|
||||
key := createKey(password, salt)
|
||||
dbPath := filepath.Join(profileDirectory, "db")
|
||||
|
||||
if !createIfNotExists {
|
||||
if _, err := os.Stat(dbPath); errors.Is(err, os.ErrNotExist) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
dbname := fmt.Sprintf("%v?_pragma_key=x'%x'&_pragma_cipher_page_size=8192", dbPath, key)
|
||||
db, err := sql.Open("sqlite3", dbname)
|
||||
if err != nil {
|
||||
|
@ -89,7 +97,7 @@ func CreateEncryptedStorePeer(profileDirectory string, name string, password str
|
|||
}
|
||||
|
||||
log.Debugf("Opening Encrypted Database")
|
||||
db, err := openEncryptedDatabase(profileDirectory, password)
|
||||
db, err := openEncryptedDatabase(profileDirectory, password, true)
|
||||
if db == nil || err != nil {
|
||||
return nil, fmt.Errorf("unable to open encrypted database: error: %v", err)
|
||||
}
|
||||
|
@ -115,14 +123,9 @@ func CreateEncryptedStorePeer(profileDirectory string, name string, password str
|
|||
|
||||
// CreateEncryptedStore creates a encrypted datastore
|
||||
func CreateEncryptedStore(profileDirectory string, password string) (*CwtchProfileStorage, error) {
|
||||
log.Debugf("Initializing Encrypted Storage Directory")
|
||||
_, _, err := initV2Directory(profileDirectory, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debugf("Opening Encrypted Database")
|
||||
db, err := openEncryptedDatabase(profileDirectory, password)
|
||||
log.Debugf("Creating Encrypted Database")
|
||||
db, err := openEncryptedDatabase(profileDirectory, password, true)
|
||||
if db == nil || err != nil {
|
||||
return nil, fmt.Errorf("unable to open encrypted database: error: %v", err)
|
||||
}
|
||||
|
@ -148,8 +151,8 @@ func CreateEncryptedStore(profileDirectory string, password string) (*CwtchProfi
|
|||
|
||||
// FromEncryptedDatabase constructs a Cwtch Profile from an existing Encrypted Database
|
||||
func FromEncryptedDatabase(profileDirectory string, password string) (CwtchPeer, error) {
|
||||
log.Debugf("Loading Encrypted Profile")
|
||||
db, err := openEncryptedDatabase(profileDirectory, password)
|
||||
log.Infof("Loading Encrypted Profile: %v", profileDirectory)
|
||||
db, err := openEncryptedDatabase(profileDirectory, password, false)
|
||||
if db == nil || err != nil {
|
||||
return nil, fmt.Errorf("unable to open encrypted database: error: %v", err)
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ func CreateProfileWriterStore(eventManager event.Manager, directory, password st
|
|||
|
||||
// LoadProfileWriterStore loads a profile store from filestore listening for events and saving them
|
||||
// directory should be $appDir/profiles/$rand
|
||||
func LoadProfileWriterStore(eventManager event.Manager, directory, password string) (ProfileStore, error) {
|
||||
return v1.LoadProfileWriterStore(eventManager, directory, password)
|
||||
func LoadProfileWriterStore(directory, password string) (ProfileStore, error) {
|
||||
return v1.LoadProfileWriterStore(directory, password)
|
||||
}
|
||||
|
||||
// ReadProfile reads a profile from storage and returns the profile
|
||||
|
|
|
@ -68,7 +68,7 @@ func CreateProfileWriterStore(eventManager event.Manager, directory, password st
|
|||
|
||||
// LoadProfileWriterStore loads a profile store from filestore listening for events and saving them
|
||||
// directory should be $appDir/profiles/$rand
|
||||
func LoadProfileWriterStore(eventManager event.Manager, directory, password string) (*ProfileStoreV1, error) {
|
||||
func LoadProfileWriterStore(directory, password string) (*ProfileStoreV1, error) {
|
||||
salt, err := ioutil.ReadFile(path.Join(directory, saltFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
Loading…
Reference in New Issue