forked from cwtch.im/cwtch
Merge pull request 'Support Save History Default + Delete Server' (#529) from stable-blockers into master
Reviewed-on: cwtch.im/cwtch#529 Reviewed-by: Dan Ballard <dan@openprivacy.ca>
This commit is contained in:
commit
95527f8978
|
@ -327,19 +327,25 @@ const (
|
||||||
ContextSendFile = "im.cwtch.file.send.chunk"
|
ContextSendFile = "im.cwtch.file.send.chunk"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Define Default Attribute Keys
|
// Define Attribute Keys related to history preservation
|
||||||
const (
|
const (
|
||||||
SaveHistoryKey = "SavePeerHistory"
|
PreserveHistoryDefaultSettingKey = "SaveHistoryDefault" // profile level default
|
||||||
|
SaveHistoryKey = "SavePeerHistory" // peer level setting
|
||||||
)
|
)
|
||||||
|
|
||||||
// Define Default Attribute Values
|
// Define Default Attribute Values
|
||||||
const (
|
const (
|
||||||
// Save History has 3 distinct states. By default we don't save history (DefaultDeleteHistory), if the peer confirms this
|
// Save History has 3 distinct states. By default we refer to the profile level
|
||||||
// we change to DeleteHistoryConfirmed, if they confirm they want to save then this becomes SaveHistoryConfirmed
|
// attribute PreserveHistoryDefaultSettingKey ( default: false i.e. DefaultDeleteHistory),
|
||||||
// We use this distinction between default and confirmed to drive UI
|
// For each contact, if the profile owner confirms deletion we change to DeleteHistoryConfirmed,
|
||||||
DeleteHistoryDefault = "DefaultDeleteHistory"
|
// 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"
|
SaveHistoryConfirmed = "SaveHistory"
|
||||||
DeleteHistoryConfirmed = "DeleteHistoryConfirmed"
|
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
|
// Bool strings
|
||||||
|
|
|
@ -101,6 +101,21 @@ func (f *Functionality) GetServerInfoList(profile peer.CwtchPeer) []Server {
|
||||||
return servers
|
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
|
// GetServerInfo compiles all the information the UI might need regarding a particular server including any verified
|
||||||
// cryptographic keys
|
// cryptographic keys
|
||||||
func (f *Functionality) GetServerInfo(profile peer.CwtchPeer, serverOnion string) Server {
|
func (f *Functionality) GetServerInfo(profile peer.CwtchPeer, serverOnion string) Server {
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -4,7 +4,7 @@ go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.openprivacy.ca/cwtch.im/tapir v0.6.0
|
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
|
git.openprivacy.ca/openprivacy/log v1.0.3
|
||||||
github.com/gtank/ristretto255 v0.1.3-0.20210930101514-6bb39798585c
|
github.com/gtank/ristretto255 v0.1.3-0.20210930101514-6bb39798585c
|
||||||
github.com/mutecomm/go-sqlcipher/v4 v4.4.2
|
github.com/mutecomm/go-sqlcipher/v4 v4.4.2
|
||||||
|
@ -15,7 +15,7 @@ require (
|
||||||
|
|
||||||
require (
|
require (
|
||||||
filippo.io/edwards25519 v1.0.0 // indirect
|
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/google/go-cmp v0.5.8 // indirect
|
||||||
github.com/gtank/merlin v0.1.1 // indirect
|
github.com/gtank/merlin v0.1.1 // indirect
|
||||||
github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // 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=
|
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 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/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/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.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 h1:E/PMm4LY+Q9s3aDpfySfEDq/vYQontlvNj/scrPaga0=
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.3/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
git.openprivacy.ca/openprivacy/log v1.0.3/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
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)
|
cp.experiments = model.InitExperiments(enabled, experiments)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifySettingsUpdate notifies a Cwtch profile of a change in the nature of global experiments. The Cwtch Profile uses
|
// NotifySettingsUpdate notifies a Cwtch profile of a change in the nature of global settings.
|
||||||
// this information to update registered extensions.
|
// The Cwtch Profile uses this information to update registered extensions in addition
|
||||||
|
// to updating internal settings.
|
||||||
func (cp *cwtchPeer) NotifySettingsUpdate(settings settings.GlobalSettings) {
|
func (cp *cwtchPeer) NotifySettingsUpdate(settings settings.GlobalSettings) {
|
||||||
log.Debugf("Cwtch Profile Settings Update: %v", settings)
|
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()
|
cp.extensionLock.Lock()
|
||||||
defer cp.extensionLock.Unlock()
|
defer cp.extensionLock.Unlock()
|
||||||
for _, extension := range cp.extensions {
|
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
|
// 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
|
// 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
|
// server with no conflicting keys. So we are going to save all the keys
|
||||||
for k, v := range ab {
|
for k, v := range ab {
|
||||||
cp.SetConversationAttribute(conversationInfo.ID, attr.PublicScope.ConstructScopedZonedPath(attr.ServerKeyZone.ConstructZonedPath(k)), v)
|
cp.SetConversationAttribute(conversationInfo.ID, attr.PublicScope.ConstructScopedZonedPath(attr.ServerKeyZone.ConstructZonedPath(k)), v)
|
||||||
}
|
}
|
||||||
|
@ -1537,6 +1543,13 @@ func (cp *cwtchPeer) eventHandler() {
|
||||||
}
|
}
|
||||||
case event.PeerStateChange:
|
case event.PeerStateChange:
|
||||||
handle := ev.Data[event.RemotePeer]
|
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 {
|
if connections.ConnectionStateToType()[ev.Data[event.ConnectionState]] == connections.AUTHENTICATED {
|
||||||
ci, err := cp.FetchConversationInfo(handle)
|
ci, err := cp.FetchConversationInfo(handle)
|
||||||
var cid int
|
var cid int
|
||||||
|
@ -1577,9 +1590,6 @@ func (cp *cwtchPeer) eventHandler() {
|
||||||
}
|
}
|
||||||
cp.extensionLock.Unlock()
|
cp.extensionLock.Unlock()
|
||||||
|
|
||||||
cp.mutex.Lock()
|
|
||||||
cp.state[ev.Data[event.RemotePeer]] = connections.ConnectionStateToType()[ev.Data[event.ConnectionState]]
|
|
||||||
cp.mutex.Unlock()
|
|
||||||
case event.ServerStateChange:
|
case event.ServerStateChange:
|
||||||
cp.mutex.Lock()
|
cp.mutex.Lock()
|
||||||
prevState := cp.state[ev.Data[event.GroupServer]]
|
prevState := cp.state[ev.Data[event.GroupServer]]
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
@ -835,12 +836,30 @@ func (cps *CwtchProfileStorage) PurgeConversationChannel(conversation int, chann
|
||||||
|
|
||||||
// PurgeNonSavedMessages deletes all message conversations that are not explicitly set to saved.
|
// PurgeNonSavedMessages deletes all message conversations that are not explicitly set to saved.
|
||||||
func (cps *CwtchProfileStorage) PurgeNonSavedMessages() {
|
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()
|
ci, err := cps.FetchConversations()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
for _, conversation := range ci {
|
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.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...")
|
log.Debugf("purging conversation...")
|
||||||
// TODO: At some point in the future this needs to iterate over channels and make a decision for each on..
|
// 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)
|
cps.PurgeConversationChannel(conversation.ID, 0)
|
||||||
|
|
|
@ -464,6 +464,10 @@ func (e *engine) peerWithTokenServer(onion string, tokenServerOnion string, toke
|
||||||
e.ignoreOnShutdown(e.serverAuthed)(onion)
|
e.ignoreOnShutdown(e.serverAuthed)(onion)
|
||||||
return
|
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
|
TorCacheDir string
|
||||||
BlodeuweddPath string
|
BlodeuweddPath string
|
||||||
FontScaling float64
|
FontScaling float64
|
||||||
|
DefaultSaveHistory bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultGlobalSettings = GlobalSettings{
|
var DefaultGlobalSettings = GlobalSettings{
|
||||||
|
@ -83,6 +84,7 @@ var DefaultGlobalSettings = GlobalSettings{
|
||||||
TorCacheDir: "",
|
TorCacheDir: "",
|
||||||
BlodeuweddPath: "",
|
BlodeuweddPath: "",
|
||||||
FontScaling: 1.0, // use the system pixel scaling default
|
FontScaling: 1.0, // use the system pixel scaling default
|
||||||
|
DefaultSaveHistory: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitGlobalSettingsFile(directory string, password string) (*GlobalSettingsFile, error) {
|
func InitGlobalSettingsFile(directory string, password string) (*GlobalSettingsFile, error) {
|
||||||
|
@ -131,6 +133,8 @@ func (globalSettingsFile *GlobalSettingsFile) ReadGlobalSettings() GlobalSetting
|
||||||
return settings //firstTime = true
|
return settings //firstTime = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// note: by giving json.Unmarshal settings we are providing it defacto defaults
|
||||||
|
// from DefaultGlobalSettings
|
||||||
err = json.Unmarshal(settingsBytes, &settings)
|
err = json.Unmarshal(settingsBytes, &settings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Could not parse global ui settings: %v\n", err)
|
log.Errorf("Could not parse global ui settings: %v\n", err)
|
||||||
|
|
Loading…
Reference in New Issue