From e9f986cc2eac3bdcc9a2c44229b5b47d0d31cdc5 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Thu, 25 Nov 2021 14:34:47 -0800 Subject: [PATCH] Update Server Attribute. Fix Profile Attribute Updates. Add UNIQUE constraint to type/key in profile attributes --- event/common.go | 3 +++ peer/cwtch_peer.go | 19 ++++++++++++++----- peer/cwtchprofilestorage.go | 4 ++-- peer/sql_statements.go | 2 +- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/event/common.go b/event/common.go index fdf5cd6..c682651 100644 --- a/event/common.go +++ b/event/common.go @@ -252,6 +252,9 @@ const ( FileDownloadProgressUpdate = Type("FileDownloadProgressUpdate") FileDownloaded = Type("FileDownloaded") FileVerificationFailed = Type("FileVerificationFailed") + + // Profile Attribute Event + UpdatedProfileAttribute = Type("UpdatedProfileAttribute") ) // Field defines common event attributes diff --git a/peer/cwtch_peer.go b/peer/cwtch_peer.go index 53dc782..e7620bb 100644 --- a/peer/cwtch_peer.go +++ b/peer/cwtch_peer.go @@ -133,10 +133,8 @@ func (cp *cwtchPeer) GetScopedZonedAttribute(scope attr.Scope, zone attr.Zone, k defer cp.mutex.Unlock() scopedZonedKey := scope.ConstructScopedZonedPath(zone.ConstructZonedPath(key)) - log.Debugf("looking up attribute %v %v %v (%v)", scope, zone, key, scopedZonedKey) value, err := cp.storage.LoadProfileKeyValue(TypeAttribute, scopedZonedKey.ToString()) - log.Debugf("found [%v] %v", string(value), err) if err != nil { return "", false } @@ -151,12 +149,18 @@ func (cp *cwtchPeer) SetScopedZonedAttribute(scope attr.Scope, zone attr.Zone, k defer cp.mutex.Unlock() scopedZonedKey := scope.ConstructScopedZonedPath(zone.ConstructZonedPath(key)) - log.Debugf("storing attribute: %v = %v", scopedZonedKey, value) + err := cp.storage.StoreProfileKeyValue(TypeAttribute, scopedZonedKey.ToString(), []byte(value)) if err != nil { log.Errorf("error setting attribute %v") + return } + + // We always want to publish profile level attributes to the ui + // This should be low traffic. + cp.eventBus.Publish(event.NewEvent(event.UpdatedProfileAttribute, map[event.Field]string{event.Key: scopedZonedKey.ToString(), event.Data: value})) + } // SendMessage is a higher level that merges sending messages to contacts and group handles @@ -275,7 +279,6 @@ func ImportLegacyProfile(profile *model.Profile, cps *CwtchProfileStorage) Cwtch 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) cp.storage.StoreProfileKeyValue(TypeAttribute, attr.PublicScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.Onion)).ToString(), []byte(tor.GetTorV3Hostname(profile.Ed25519PublicKey))) cp.storage.StoreProfileKeyValue(TypePrivateKey, "Ed25519PrivateKey", profile.Ed25519PrivateKey) @@ -923,8 +926,14 @@ func (cp *cwtchPeer) eventHandler() { // The security of cwtch groups are also not dependent on the servers inability to uniquely tag connections (as long as // it learns nothing else about each connection). // store the base64 encoded signature for later use - //cp.SetConversationAttribute(ev.Data[event.GroupServer], lastKnownSignature, ev.Data[event.Signature]) + // TODO Server Connections should sent Connection ID + ci, err := cp.FetchConversationInfo(ev.Data[event.GroupServer]) + if ci == nil || err != nil { + log.Errorf("no server connection count") + return + } + cp.SetConversationAttribute(ci.ID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(lastReceivedSignature)), ev.Data[event.Signature]) conversations, err := cp.FetchConversations() if err == nil { for _, conversationInfo := range conversations { diff --git a/peer/cwtchprofilestorage.go b/peer/cwtchprofilestorage.go index 1725384..80f160c 100644 --- a/peer/cwtchprofilestorage.go +++ b/peer/cwtchprofilestorage.go @@ -63,7 +63,7 @@ type ChannelID struct { Channel int } -const insertProfileKeySQLStmt = `insert into profile_kv(KeyType, KeyName, KeyValue) values(?,?,?);` +const insertProfileKeySQLStmt = `insert or replace into profile_kv(KeyType, KeyName, KeyValue) values(?,?,?);` const selectProfileKeySQLStmt = `select KeyValue from profile_kv where KeyType=(?) and KeyName=(?);` const insertConversationSQLStmt = `insert into conversations(Handle, Attributes, ACL, Accepted) values(?,?,?,?);` @@ -701,7 +701,7 @@ func (cps *CwtchProfileStorage) Close() { if err == nil { for _, conversation := range ci { if !conversation.IsGroup() && !conversation.IsServer() { - if conversation.Attributes[event.SaveHistoryKey] != event.SaveHistoryConfirmed { + if conversation.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(event.SaveHistoryKey)).ToString()] != event.SaveHistoryConfirmed { log.Infof("purging conversation...") // TODO: At some point in the future this needs to iterate over channels and make a decision for each on.. cps.PurgeConversationChannel(conversation.ID, 0) diff --git a/peer/sql_statements.go b/peer/sql_statements.go index 42ae396..f6ea511 100644 --- a/peer/sql_statements.go +++ b/peer/sql_statements.go @@ -6,7 +6,7 @@ import ( ) // SQLCreateTableProfileKeyValue creates the Profile Key Value Table -const SQLCreateTableProfileKeyValue = `create table if not exists profile_kv (KeyType text, KeyName text, KeyValue blob);` +const SQLCreateTableProfileKeyValue = `create table if not exists profile_kv (KeyType text, KeyName text, KeyValue blob, UNIQUE (KeyType,KeyName));` // SQLCreateTableConversations creates the Profile Key Value Table const SQLCreateTableConversations = `create table if not exists conversations (ID integer unique primary key autoincrement, Handle text, Attributes blob, ACL blob, Accepted bool);`