From 8d7052bb8dc8744b5c2ae8773befafe31c62fb04 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Tue, 25 Jul 2023 11:12:39 -0700 Subject: [PATCH 1/2] Move server handling logic back into Cwtch (from libCwtch-go / autobindings) --- app/app.go | 2 + .../servers/servers_functionality.go | 126 ++++++++++++++++++ model/constants/attributes.go | 3 + model/constants/experiments.go | 2 + 4 files changed, 133 insertions(+) create mode 100644 functionality/servers/servers_functionality.go diff --git a/app/app.go b/app/app.go index 01afb99..23960f7 100644 --- a/app/app.go +++ b/app/app.go @@ -5,6 +5,7 @@ import ( "cwtch.im/cwtch/event" "cwtch.im/cwtch/extensions" "cwtch.im/cwtch/functionality/filesharing" + "cwtch.im/cwtch/functionality/servers" "cwtch.im/cwtch/model" "cwtch.im/cwtch/model/attr" "cwtch.im/cwtch/model/constants" @@ -362,6 +363,7 @@ func (app *application) registerHooks(profile peer.CwtchPeer) { profile.RegisterHook(extensions.ProfileValueExtension{}) profile.RegisterHook(new(filesharing.Functionality)) profile.RegisterHook(new(filesharing.ImagePreviewsFunctionality)) + profile.RegisterHook(new(servers.Functionality)) // Ensure that Profiles have the Most Up to Date Settings... profile.NotifySettingsUpdate(app.settings.ReadGlobalSettings()) } diff --git a/functionality/servers/servers_functionality.go b/functionality/servers/servers_functionality.go new file mode 100644 index 0000000..0e0ab55 --- /dev/null +++ b/functionality/servers/servers_functionality.go @@ -0,0 +1,126 @@ +package servers + + +import ( + "cwtch.im/cwtch/event" + "cwtch.im/cwtch/model" + "cwtch.im/cwtch/model/attr" + "cwtch.im/cwtch/model/constants" + "cwtch.im/cwtch/peer" + "cwtch.im/cwtch/settings" + "cwtch.im/cwtch/protocol/connections" + "encoding/json" +) + +const ( + // ServerList is a json encoded list of servers + ServerList = event.Field("ServerList") +) + +const ( + // UpdateServerInfo is an event containing a ProfileOnion and a ServerList + UpdateServerInfo = event.Type("UpdateServerInfo") +) + +// Functionality groups some common UI triggered functions for contacts... +type Functionality struct { +} + +func (f *Functionality) NotifySettingsUpdate(settings settings.GlobalSettings) { + +} + +func (f *Functionality) EventsToRegister() []event.Type { + return []event.Type{event.ProtocolEngineCreated, event.ManifestReceived, event.FileDownloaded} +} + +func (f *Functionality) ExperimentsToRegister() []string { + return []string{constants.GroupsExperiment} +} + +// OnEvent handles File Sharing Hooks like Manifest Received and FileDownloaded +func (f *Functionality) OnEvent(ev event.Event, profile peer.CwtchPeer) { + if profile.IsFeatureEnabled(constants.GroupsExperiment) { + switch ev.EventType { + // keep the UI in sync with the current backend server updates... + // TODO: do we need a secondary heartbeat for less common updates? + case event.Heartbeat: + f.PublishServerUpdate(profile) + break; + } + } +} + +func (f *Functionality) OnContactRequestValue(profile peer.CwtchPeer, conversation model.Conversation, eventID string, path attr.ScopedZonedPath) { + // nop +} + +func (f *Functionality) OnContactReceiveValue(profile peer.CwtchPeer, conversation model.Conversation, path attr.ScopedZonedPath, value string, exists bool) { + // nopt +} + +// FunctionalityGate returns filesharing functionality - gates now happen on function calls. +func FunctionalityGate() *Functionality { + return new(Functionality) +} + +// ServerKey packages up key information... +// TODO: Can this be merged with KeyBundle? +type ServerKey struct { + Type string `json:"type"` + Key string `json:"key"` +} + +// SyncStatus packages up server sync information... +type SyncStatus struct { + StartTime string `json:"startTime"` + LastMessageTime string `json:"lastMessageTime"` +} + + +// Server encapsulates the information needed to represent a server... +type Server struct { + Onion string `json:"onion"` + Identifier int `json:"identifier"` + Status string `json:"status"` + Description string `json:"description"` + Keys []ServerKey `json:"keys"` + SyncProgress SyncStatus `json:"syncProgress"` +} + +// PublishServerUpdate serializes the current list of group servers and publishes an event with this information +func (f *Functionality) PublishServerUpdate(profile peer.CwtchPeer) { + serverListForOnion := f.GetServerInfoList(profile) + serversListBytes, _ := json.Marshal(serverListForOnion) + profile.PublishEvent(event.NewEvent(UpdateServerInfo, map[event.Field]string{"ProfileOnion": profile.GetOnion(), ServerList: string(serversListBytes)})) +} + +// GetServerInfoList compiles all the information the UI might need regarding all servers.. +func (f *Functionality) GetServerInfoList(profile peer.CwtchPeer) []Server { + var servers []Server + for _, server := range profile.GetServers() { + servers = append(servers, f.GetServerInfo(profile, server)) + } + return servers +} + +// GetServerInfo compiles all the information the UI might need regarding a particular server including any verified +// cryptographic keys +func (f *Functionality) GetServerInfo(profile peer.CwtchPeer, serverOnion string) Server { + serverInfo, _ := profile.FetchConversationInfo(serverOnion) + keyTypes := []model.KeyType{model.KeyTypeServerOnion, model.KeyTypeTokenOnion, model.KeyTypePrivacyPass} + var serverKeys []ServerKey + + for _, keyType := range keyTypes { + if key, has := serverInfo.GetAttribute(attr.PublicScope, attr.ServerKeyZone, string(keyType)); has { + serverKeys = append(serverKeys, ServerKey{Type: string(keyType), Key: key}) + } + } + + description, _ := serverInfo.GetAttribute(attr.LocalScope, attr.ServerZone, constants.Description) + startTimeStr := serverInfo.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.SyncPreLastMessageTime)).ToString()] + recentTimeStr := serverInfo.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.SyncMostRecentMessageTime)).ToString()] + syncStatus := SyncStatus{startTimeStr, recentTimeStr} + + return Server{Onion: serverOnion, Identifier: serverInfo.ID, Status: connections.ConnectionStateName[profile.GetPeerState(serverInfo.Handle)], Keys: serverKeys, Description: description, SyncProgress: syncStatus} +} diff --git a/model/constants/attributes.go b/model/constants/attributes.go index ecdc03a..443f747 100644 --- a/model/constants/attributes.go +++ b/model/constants/attributes.go @@ -63,3 +63,6 @@ const ProfileStatus = "profile-status" const ProfileAttribute1 = "profile-attribute-1" const ProfileAttribute2 = "profile-attribute-2" const ProfileAttribute3 = "profile-attribute-3" + +// Description is used on server contacts, +const Description = "description" diff --git a/model/constants/experiments.go b/model/constants/experiments.go index a9fd358..62cd482 100644 --- a/model/constants/experiments.go +++ b/model/constants/experiments.go @@ -1,5 +1,7 @@ package constants +const GroupsExperiment = "tapir-groups-experiment" + // FileSharingExperiment Allows file sharing const FileSharingExperiment = "filesharing" From f2ad64fe8b0a64702bc4bcc55fcc5d4bc1542d2c Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Tue, 25 Jul 2023 11:18:55 -0700 Subject: [PATCH 2/2] Formatting / Linting --- .../servers/servers_functionality.go | 23 ++++++++----------- peer/cwtch_peer.go | 6 ++--- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/functionality/servers/servers_functionality.go b/functionality/servers/servers_functionality.go index 0e0ab55..40feeac 100644 --- a/functionality/servers/servers_functionality.go +++ b/functionality/servers/servers_functionality.go @@ -1,14 +1,13 @@ package servers - import ( "cwtch.im/cwtch/event" "cwtch.im/cwtch/model" "cwtch.im/cwtch/model/attr" "cwtch.im/cwtch/model/constants" "cwtch.im/cwtch/peer" - "cwtch.im/cwtch/settings" "cwtch.im/cwtch/protocol/connections" + "cwtch.im/cwtch/settings" "encoding/json" ) @@ -28,7 +27,7 @@ type Functionality struct { func (f *Functionality) NotifySettingsUpdate(settings settings.GlobalSettings) { -} +} func (f *Functionality) EventsToRegister() []event.Type { return []event.Type{event.ProtocolEngineCreated, event.ManifestReceived, event.FileDownloaded} @@ -42,11 +41,10 @@ func (f *Functionality) ExperimentsToRegister() []string { func (f *Functionality) OnEvent(ev event.Event, profile peer.CwtchPeer) { if profile.IsFeatureEnabled(constants.GroupsExperiment) { switch ev.EventType { - // keep the UI in sync with the current backend server updates... - // TODO: do we need a secondary heartbeat for less common updates? - case event.Heartbeat: - f.PublishServerUpdate(profile) - break; + // keep the UI in sync with the current backend server updates... + // TODO: do we need a secondary heartbeat for less common updates? + case event.Heartbeat: + f.PublishServerUpdate(profile) } } } @@ -56,7 +54,7 @@ func (f *Functionality) OnContactRequestValue(profile peer.CwtchPeer, conversati } func (f *Functionality) OnContactReceiveValue(profile peer.CwtchPeer, conversation model.Conversation, path attr.ScopedZonedPath, value string, exists bool) { - // nopt + // nopt } // FunctionalityGate returns filesharing functionality - gates now happen on function calls. @@ -77,7 +75,6 @@ type SyncStatus struct { LastMessageTime string `json:"lastMessageTime"` } - // Server encapsulates the information needed to represent a server... type Server struct { Onion string `json:"onion"` @@ -90,9 +87,9 @@ type Server struct { // PublishServerUpdate serializes the current list of group servers and publishes an event with this information func (f *Functionality) PublishServerUpdate(profile peer.CwtchPeer) { - serverListForOnion := f.GetServerInfoList(profile) - serversListBytes, _ := json.Marshal(serverListForOnion) - profile.PublishEvent(event.NewEvent(UpdateServerInfo, map[event.Field]string{"ProfileOnion": profile.GetOnion(), ServerList: string(serversListBytes)})) + serverListForOnion := f.GetServerInfoList(profile) + serversListBytes, _ := json.Marshal(serverListForOnion) + profile.PublishEvent(event.NewEvent(UpdateServerInfo, map[event.Field]string{"ProfileOnion": profile.GetOnion(), ServerList: string(serversListBytes)})) } // GetServerInfoList compiles all the information the UI might need regarding all servers.. diff --git a/peer/cwtch_peer.go b/peer/cwtch_peer.go index 7a427a5..13b6b41 100644 --- a/peer/cwtch_peer.go +++ b/peer/cwtch_peer.go @@ -857,9 +857,9 @@ func (cp *cwtchPeer) doSearch(ctx context.Context, searchID string, pattern stri // in the table (e.g. deleted messages, expired messages, etc.) func (cp *cwtchPeer) SearchConversations(pattern string) string { - // TODO: For now, we simply surround the pattern with the sqlite LIKE syntax for matching any prefix, and any suffix - // At some point we would like to extend this patternt to support e.g. searching a specific conversation, or - // searching for particular types of message. + // TODO: For now, we simply surround the pattern with the sqlite LIKE syntax for matching any prefix, and any suffix + // At some point we would like to extend this patternt to support e.g. searching a specific conversation, or + // searching for particular types of message. pattern = fmt.Sprintf("%%%v%%", pattern) // we need this lock here to prevent weirdness happening when reassigning cp.cancelSearchContext