Support Save History Default + Delete Server #529
|
@ -327,19 +327,25 @@ const (
|
|||
ContextSendFile = "im.cwtch.file.send.chunk"
|
||||
)
|
||||
|
||||
// Define Default Attribute Keys
|
||||
// Define Attribute Keys related to history preservation
|
||||
const (
|
||||
SaveHistoryKey = "SavePeerHistory"
|
||||
PreserveHistoryDefaultSettingKey = "SaveHistoryDefault" // profile level default
|
||||
SaveHistoryKey = "SavePeerHistory" // peer level setting
|
||||
)
|
||||
|
||||
// Define Default Attribute Values
|
||||
const (
|
||||
// Save History has 3 distinct states. By default we don't save history (DefaultDeleteHistory), if the peer confirms this
|
||||
// we change to DeleteHistoryConfirmed, if they confirm they want to save then this becomes SaveHistoryConfirmed
|
||||
// We use this distinction between default and confirmed to drive UI
|
||||
DeleteHistoryDefault = "DefaultDeleteHistory"
|
||||
// Save History has 3 distinct states. By default we refer to the profile level
|
||||
// attribute PreserveHistoryDefaultSettingKey ( default: false i.e. DefaultDeleteHistory),
|
||||
// For each contact, if the profile owner confirms deletion we change to DeleteHistoryConfirmed,
|
||||
// if the profile owner confirms they want to save history then this becomes SaveHistoryConfirmed
|
||||
// These settings are set at the UI level using Get/SetScopeZoneAttribute with scoped zone: local.profile.*
|
||||
SaveHistoryConfirmed = "SaveHistory"
|
||||
DeleteHistoryConfirmed = "DeleteHistoryConfirmed"
|
||||
|
||||
// NOTE: While this says "[DeleteHistory]Default", The actual behaviour will now depend on the
|
||||
// global app/profile value of PreserveHistoryDefaultSettingKey
|
||||
DeleteHistoryDefault = "DefaultDeleteHistory"
|
||||
)
|
||||
|
||||
// Bool strings
|
||||
|
|
|
@ -101,6 +101,21 @@ func (f *Functionality) GetServerInfoList(profile peer.CwtchPeer) []Server {
|
|||
return servers
|
||||
}
|
||||
|
||||
// DeleteServer purges a server and all related keys from a profile
|
||||
func (f *Functionality) DeleteServerInfo(profile peer.CwtchPeer, serverOnion string) error {
|
||||
// Servers are stores as special conversations
|
||||
ci, err := profile.FetchConversationInfo(serverOnion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Purge keys...
|
||||
|
||||
// NOTE: This will leave some groups in the state of being unable to connect to a particular
|
||||
// server.
|
||||
profile.DeleteConversation(ci.ID)
|
||||
f.PublishServerUpdate(profile)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
|
4
go.mod
4
go.mod
|
@ -4,7 +4,7 @@ go 1.17
|
|||
|
||||
require (
|
||||
git.openprivacy.ca/cwtch.im/tapir v0.6.0
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.8.6
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.11.0
|
||||
git.openprivacy.ca/openprivacy/log v1.0.3
|
||||
github.com/gtank/ristretto255 v0.1.3-0.20210930101514-6bb39798585c
|
||||
github.com/mutecomm/go-sqlcipher/v4 v4.4.2
|
||||
|
@ -15,7 +15,7 @@ require (
|
|||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.0.0 // indirect
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.4 // indirect
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.5 // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/gtank/merlin v0.1.1 // indirect
|
||||
github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // indirect
|
||||
|
|
6
go.sum
6
go.sum
|
@ -3,10 +3,12 @@ filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
|
|||
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||
git.openprivacy.ca/cwtch.im/tapir v0.6.0 h1:TtnKjxitkIDMM7Qn0n/u+mOHRLJzuQUYjYRu5n0/QFY=
|
||||
git.openprivacy.ca/cwtch.im/tapir v0.6.0/go.mod h1:iQIq4y7N+DuP3CxyG66WNEC/d6vzh+wXvvOmelB+KoY=
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.4 h1:CO7EkGyz+jegZ4ap8g5NWRuDHA/56KKvGySR6OBPW+c=
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.4/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.8.6 h1:g74PyDGvpMZ3+K0dXy3mlTJh+e0rcwNk0XF8owzkmOA=
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.5 h1:DJs5gqw3SkvLSgRDvroqJxZ7F+YsbxbBRg5t0rU5gYE=
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.5/go.mod h1:fwdeq6RO08WDkV0k7HfArsjRvurVULoUQmT//iaABZM=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.8.6/go.mod h1:Hn1gpOx/bRZp5wvCtPQVJPXrfeUH0EGiG/Aoa0vjGLg=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.11.0 h1:roASjaFtQLu+HdH5fa2wx6F00NL3YsUTlmXBJh8aLZk=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.11.0/go.mod h1:OQO1+7OIz/jLxDrorEMzvZA6SEbpbDyLGpjoFqT3z1Y=
|
||||
git.openprivacy.ca/openprivacy/log v1.0.3 h1:E/PMm4LY+Q9s3aDpfySfEDq/vYQontlvNj/scrPaga0=
|
||||
git.openprivacy.ca/openprivacy/log v1.0.3/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
|
|
|
@ -194,10 +194,16 @@ func (cp *cwtchPeer) UpdateExperiments(enabled bool, experiments map[string]bool
|
|||
cp.experiments = model.InitExperiments(enabled, experiments)
|
||||
}
|
||||
|
||||
// NotifySettingsUpdate notifies a Cwtch profile of a change in the nature of global experiments. The Cwtch Profile uses
|
||||
// this information to update registered extensions.
|
||||
// NotifySettingsUpdate notifies a Cwtch profile of a change in the nature of global settings.
|
||||
// The Cwtch Profile uses this information to update registered extensions in addition
|
||||
// to updating internal settings.
|
||||
func (cp *cwtchPeer) NotifySettingsUpdate(settings settings.GlobalSettings) {
|
||||
log.Debugf("Cwtch Profile Settings Update: %v", settings)
|
||||
|
||||
// update the save history default...
|
||||
cp.SetScopedZonedAttribute(attr.LocalScope, attr.ProfileZone, event.PreserveHistoryDefaultSettingKey, strconv.FormatBool(settings.DefaultSaveHistory))
|
||||
|
||||
// pass these seetings updates
|
||||
cp.extensionLock.Lock()
|
||||
defer cp.extensionLock.Unlock()
|
||||
for _, extension := range cp.extensions {
|
||||
|
@ -981,8 +987,8 @@ func (cp *cwtchPeer) AddServer(serverSpecification string) (string, error) {
|
|||
// we haven't seen this key associated with the server before
|
||||
}
|
||||
|
||||
// // If we have gotten to this point we can assume this is a safe key bundle signed by the
|
||||
// // server with no conflicting keys. So we are going to save all the keys
|
||||
// If we have gotten to this point we can assume this is a safe key bundle signed by the
|
||||
// server with no conflicting keys. So we are going to save all the keys
|
||||
for k, v := range ab {
|
||||
cp.SetConversationAttribute(conversationInfo.ID, attr.PublicScope.ConstructScopedZonedPath(attr.ServerKeyZone.ConstructZonedPath(k)), v)
|
||||
}
|
||||
|
@ -1537,6 +1543,13 @@ func (cp *cwtchPeer) eventHandler() {
|
|||
}
|
||||
case event.PeerStateChange:
|
||||
handle := ev.Data[event.RemotePeer]
|
||||
|
||||
// we need to do this first because calls in the rest of this block may result in
|
||||
// events that result the UI or bindings fetching new data.
|
||||
cp.mutex.Lock()
|
||||
cp.state[handle] = connections.ConnectionStateToType()[ev.Data[event.ConnectionState]]
|
||||
cp.mutex.Unlock()
|
||||
|
||||
if connections.ConnectionStateToType()[ev.Data[event.ConnectionState]] == connections.AUTHENTICATED {
|
||||
ci, err := cp.FetchConversationInfo(handle)
|
||||
var cid int
|
||||
|
@ -1577,9 +1590,6 @@ func (cp *cwtchPeer) eventHandler() {
|
|||
}
|
||||
cp.extensionLock.Unlock()
|
||||
|
||||
cp.mutex.Lock()
|
||||
cp.state[ev.Data[event.RemotePeer]] = connections.ConnectionStateToType()[ev.Data[event.ConnectionState]]
|
||||
cp.mutex.Unlock()
|
||||
case event.ServerStateChange:
|
||||
cp.mutex.Lock()
|
||||
prevState := cp.state[ev.Data[event.GroupServer]]
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
@ -835,12 +836,30 @@ func (cps *CwtchProfileStorage) PurgeConversationChannel(conversation int, chann
|
|||
|
||||
// PurgeNonSavedMessages deletes all message conversations that are not explicitly set to saved.
|
||||
func (cps *CwtchProfileStorage) PurgeNonSavedMessages() {
|
||||
// Purge Messages that are not stored...
|
||||
|
||||
// check to see if the profile global setting has been explicitly set to save (peer) conversations by default.
|
||||
defaultSave := false
|
||||
key, err := cps.LoadProfileKeyValue(TypeAttribute, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(event.PreserveHistoryDefaultSettingKey)).ToString())
|
||||
if err == nil {
|
||||
if defaultSaveSetting, err := strconv.ParseBool(string(key)); err == nil {
|
||||
defaultSave = defaultSaveSetting
|
||||
}
|
||||
}
|
||||
|
||||
// For each conversation, all that is not explicitly saved will be lost...
|
||||
ci, err := cps.FetchConversations()
|
||||
if err == nil {
|
||||
for _, conversation := range ci {
|
||||
// unless this is a server or a group...for which we default save always (for legacy reasons)
|
||||
// FIXME: revisit this for hybrid groups.
|
||||
if !conversation.IsGroup() && !conversation.IsServer() {
|
||||
if conversation.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(event.SaveHistoryKey)).ToString()] != event.SaveHistoryConfirmed {
|
||||
// Note that we only check for confirmed status here...if it is set to any other value we will fallthrough to the default.
|
||||
saveHistoryConfirmed := conversation.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(event.SaveHistoryKey)).ToString()] == event.SaveHistoryConfirmed
|
||||
deleteHistoryConfirmed := conversation.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(event.SaveHistoryKey)).ToString()] == event.DeleteHistoryConfirmed
|
||||
// we purge conversation history in two specific instances...
|
||||
// if the conversation has been explicitly marked as delete history confirmed OR
|
||||
// if save history hasn't been confirmed and default save history is false - i.e. in all other cases
|
||||
if deleteHistoryConfirmed || (!saveHistoryConfirmed && !defaultSave) {
|
||||
log.Debugf("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)
|
||||
|
|
|
@ -464,6 +464,10 @@ func (e *engine) peerWithTokenServer(onion string, tokenServerOnion string, toke
|
|||
e.ignoreOnShutdown(e.serverAuthed)(onion)
|
||||
return
|
||||
}
|
||||
|
||||
// if we are not authed or synced then we are stuck...
|
||||
e.ignoreOnShutdown(e.serverConnecting)(onion)
|
||||
log.Errorf("server connection attempt issued to active connection")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ type GlobalSettings struct {
|
|||
TorCacheDir string
|
||||
BlodeuweddPath string
|
||||
FontScaling float64
|
||||
DefaultSaveHistory bool
|
||||
}
|
||||
|
||||
var DefaultGlobalSettings = GlobalSettings{
|
||||
|
@ -83,6 +84,7 @@ var DefaultGlobalSettings = GlobalSettings{
|
|||
TorCacheDir: "",
|
||||
BlodeuweddPath: "",
|
||||
FontScaling: 1.0, // use the system pixel scaling default
|
||||
DefaultSaveHistory: false,
|
||||
}
|
||||
|
||||
func InitGlobalSettingsFile(directory string, password string) (*GlobalSettingsFile, error) {
|
||||
|
@ -131,6 +133,8 @@ func (globalSettingsFile *GlobalSettingsFile) ReadGlobalSettings() GlobalSetting
|
|||
return settings //firstTime = true
|
||||
}
|
||||
|
||||
// note: by giving json.Unmarshal settings we are providing it defacto defaults
|
||||
// from DefaultGlobalSettings
|
||||
err = json.Unmarshal(settingsBytes, &settings)
|
||||
if err != nil {
|
||||
log.Errorf("Could not parse global ui settings: %v\n", err)
|
||||
|
|
Loading…
Reference in New Issue
what's the ui flow for this? will the UI warn / prompt about groups and delete them first if asked? do we have a way to signal a group is now serverless and will never be online?
The main usecase for this right now is people cleaning up servers they no longer use. To that end we can just disable the button on the UI side if there are active groups.