Large API Refactor in prep for autobindings #494
111
app/app.go
111
app/app.go
|
@ -35,14 +35,24 @@ type application struct {
|
|||
settings *GlobalSettingsFile
|
||||
}
|
||||
|
||||
func (app *application) IsFeatureEnabled(experiment string) bool {
|
||||
settings := app.ReadSettings()
|
||||
if settings.ExperimentsEnabled {
|
||||
if status, exists := settings.Experiments[experiment]; exists {
|
||||
|
||||
return status
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Application is a full cwtch peer application. It allows management, usage and storage of multiple peers
|
||||
type Application interface {
|
||||
LoadProfiles(password string)
|
||||
CreatePeer(name string, password string, attributes map[attr.ZonedPath]string)
|
||||
// Deprecated in 1.10
|
||||
CreateTaggedPeer(name string, password string, tag string)
|
||||
CreateProfile(name string, password string, autostart bool)
|
||||
|
||||
ImportProfile(exportedCwtchFile string, password string) (peer.CwtchPeer, error)
|
||||
DeletePeer(onion string, currentPassword string)
|
||||
EnhancedImportProfile(exportedCwtchFile string, password string) string
|
||||
dan
commented
why are we dropping why are we dropping `doListen, doPeers, doServers`? they were added with the intention of furthering support for various profile run modes, like "invisible" that isn't listening
|
||||
DeleteProfile(onion string, currentPassword string)
|
||||
AddPeerPlugin(onion string, pluginID plugins.PluginID)
|
||||
|
||||
GetPrimaryBus() event.Manager
|
||||
|
@ -51,11 +61,12 @@ type Application interface {
|
|||
QueryACNVersion()
|
||||
|
||||
ActivateEngines(doListn, doPeers, doServers bool)
|
||||
ActivatePeerEngine(onion string, doListen, doPeers, doServers bool)
|
||||
ActivatePeerEngine(onion string)
|
||||
DeactivatePeerEngine(onion string)
|
||||
|
||||
ReadSettings() GlobalSettings
|
||||
UpdateSettings(settings GlobalSettings)
|
||||
IsFeatureEnabled(experiment string) bool
|
||||
|
||||
sarah marked this conversation as resolved
Outdated
dan
commented
what's the invocation of InitApp look like? what's the invocation of InitApp look like?
dan
commented
nevermind, probably just rename ReadSettings nevermind, probably just rename ReadSettings
|
||||
ShutdownPeer(string)
|
||||
Shutdown()
|
||||
|
@ -67,8 +78,7 @@ type Application interface {
|
|||
// LoadProfileFn is the function signature for a function in an app that loads a profile
|
||||
type LoadProfileFn func(profile peer.CwtchPeer)
|
||||
|
||||
// NewApp creates a new app with some environment awareness and initializes a Tor Manager
|
||||
func NewApp(acn connectivity.ACN, appDirectory string) Application {
|
||||
func LoadAppSettings(appDirectory string) *GlobalSettingsFile {
|
||||
log.Debugf("NewApp(%v)\n", appDirectory)
|
||||
os.MkdirAll(path.Join(appDirectory, "profiles"), 0700)
|
||||
|
||||
|
@ -79,6 +89,11 @@ func NewApp(acn connectivity.ACN, appDirectory string) Application {
|
|||
if err != nil {
|
||||
log.Errorf("error initializing global settings file %. Global settings might not be loaded or saves", err)
|
||||
}
|
||||
return settings
|
||||
}
|
||||
|
||||
// NewApp creates a new app with some environment awareness and initializes a Tor Manager
|
||||
func NewApp(acn connectivity.ACN, appDirectory string, settings *GlobalSettingsFile) Application {
|
||||
|
||||
app := &application{engines: make(map[string]connections.Engine), eventBuses: make(map[string]event.Manager), directory: appDirectory, appBus: event.NewEventManager(), settings: settings}
|
||||
app.peers = make(map[string]peer.CwtchPeer)
|
||||
|
@ -110,6 +125,14 @@ func (app *application) UpdateSettings(settings GlobalSettings) {
|
|||
defer app.peerLock.Unlock()
|
||||
for _, profile := range app.peers {
|
||||
profile.UpdateExperiments(settings.ExperimentsEnabled, settings.Experiments)
|
||||
|
||||
// Explicitly toggle blocking/unblocking of unknown connections for profiles
|
||||
// that have been loaded.
|
||||
if settings.BlockUnknownConnections {
|
||||
profile.BlockUnknownConnections()
|
||||
} else {
|
||||
profile.AllowUnknownConnections()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,11 +182,45 @@ func (ap *application) AddPlugin(peerid string, id plugins.PluginID, bus event.M
|
|||
}
|
||||
}
|
||||
|
||||
func (app *application) CreateProfile(name string, password string, autostart bool) {
|
||||
autostartVal := constants.True
|
||||
if !autostart {
|
||||
autostartVal = constants.False
|
||||
}
|
||||
tagVal := constants.ProfileTypeV1Password
|
||||
if password == DefactoPasswordForUnencryptedProfiles {
|
||||
tagVal = constants.ProfileTypeV1DefaultPassword
|
||||
}
|
||||
|
||||
app.CreatePeer(name, password, map[attr.ZonedPath]string{
|
||||
attr.ProfileZone.ConstructZonedPath(constants.Tag): tagVal,
|
||||
attr.ProfileZone.ConstructZonedPath(constants.PeerAutostart): autostartVal,
|
||||
})
|
||||
}
|
||||
|
||||
// Deprecated in 1.10
|
||||
func (app *application) CreateTaggedPeer(name string, password string, tag string) {
|
||||
app.CreatePeer(name, password, map[attr.ZonedPath]string{attr.ProfileZone.ConstructZonedPath(constants.Tag): tag})
|
||||
}
|
||||
|
||||
func (app *application) setupPeer(profile peer.CwtchPeer) {
|
||||
eventBus := event.NewEventManager()
|
||||
app.eventBuses[profile.GetOnion()] = eventBus
|
||||
|
||||
// Initialize the Peer with the Given Event Bus
|
||||
app.peers[profile.GetOnion()] = profile
|
||||
profile.Init(app.eventBuses[profile.GetOnion()])
|
||||
|
||||
// Update the Peer with the Most Recent Experiment State...
|
||||
settings := app.settings.ReadGlobalSettings()
|
||||
profile.UpdateExperiments(settings.ExperimentsEnabled, settings.Experiments)
|
||||
app.registerHooks(profile)
|
||||
|
||||
// Register the Peer With Application Plugins..
|
||||
app.AddPeerPlugin(profile.GetOnion(), plugins.CONNECTIONRETRY) // Now Mandatory
|
||||
|
||||
}
|
||||
|
||||
func (app *application) CreatePeer(name string, password string, attributes map[attr.ZonedPath]string) {
|
||||
app.appmutex.Lock()
|
||||
defer app.appmutex.Unlock()
|
||||
|
@ -177,26 +234,26 @@ func (app *application) CreatePeer(name string, password string, attributes map[
|
|||
return
|
||||
}
|
||||
|
||||
eventBus := event.NewEventManager()
|
||||
app.eventBuses[profile.GetOnion()] = eventBus
|
||||
profile.Init(app.eventBuses[profile.GetOnion()])
|
||||
app.registerHooks(profile)
|
||||
app.peers[profile.GetOnion()] = profile
|
||||
app.setupPeer(profile)
|
||||
|
||||
for zp, val := range attributes {
|
||||
zone, key := attr.ParseZone(zp.ToString())
|
||||
profile.SetScopedZonedAttribute(attr.LocalScope, zone, key, val)
|
||||
}
|
||||
|
||||
app.AddPeerPlugin(profile.GetOnion(), plugins.CONNECTIONRETRY) // Now Mandatory
|
||||
app.appBus.Publish(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: profile.GetOnion(), event.Created: event.True}))
|
||||
}
|
||||
|
||||
func (app *application) DeletePeer(onion string, password string) {
|
||||
log.Debugf("DeletePeer called on %v\n", onion)
|
||||
func (app *application) DeleteProfile(onion string, password string) {
|
||||
log.Debugf("DeleteProfile called on %v\n", onion)
|
||||
app.appmutex.Lock()
|
||||
defer app.appmutex.Unlock()
|
||||
|
||||
// allow a blank password to delete "unencrypted" accounts...
|
||||
if password == "" {
|
||||
password = DefactoPasswordForUnencryptedProfiles
|
||||
}
|
||||
|
||||
if app.peers[onion].CheckPassword(password) {
|
||||
// soft-shutdown
|
||||
app.peers[onion].Shutdown()
|
||||
|
@ -226,6 +283,14 @@ func (app *application) ImportProfile(exportedCwtchFile string, password string)
|
|||
return profile, err
|
||||
}
|
||||
|
||||
func (app *application) EnhancedImportProfile(exportedCwtchFile string, password string) string {
|
||||
_, err := app.ImportProfile(exportedCwtchFile, password)
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
// LoadProfiles takes a password and attempts to load any profiles it can from storage with it and create Peers for them
|
||||
func (app *application) LoadProfiles(password string) {
|
||||
count := 0
|
||||
|
@ -291,14 +356,8 @@ func (app *application) installProfile(profile peer.CwtchPeer) bool {
|
|||
|
||||
// Only attempt to finalize the profile if we don't have one loaded...
|
||||
if app.peers[profile.GetOnion()] == nil {
|
||||
eventBus := event.NewEventManager()
|
||||
app.eventBuses[profile.GetOnion()] = eventBus
|
||||
profile.Init(app.eventBuses[profile.GetOnion()])
|
||||
app.registerHooks(profile)
|
||||
app.peers[profile.GetOnion()] = profile
|
||||
|
||||
app.AddPeerPlugin(profile.GetOnion(), plugins.CONNECTIONRETRY) // Now Mandatory
|
||||
|
||||
app.setupPeer(profile)
|
||||
// Finalize the Creation of Peer / Notify any Interfaces..
|
||||
app.appBus.Publish(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: profile.GetOnion(), event.Created: event.False}))
|
||||
return true
|
||||
}
|
||||
|
@ -333,7 +392,7 @@ func (app *application) ActivateEngines(doListen, doPeers, doServers bool) {
|
|||
}
|
||||
|
||||
// ActivePeerEngine creates a peer engine for use with an ACN, should be called once the underlying ACN is online
|
||||
func (app *application) ActivatePeerEngine(onion string, doListen, doPeers, doServers bool) {
|
||||
func (app *application) ActivatePeerEngine(onion string) {
|
||||
profile := app.GetPeer(onion)
|
||||
if profile != nil {
|
||||
if _, exists := app.engines[onion]; !exists {
|
||||
|
@ -341,10 +400,10 @@ func (app *application) ActivatePeerEngine(onion string, doListen, doPeers, doSe
|
|||
|
||||
app.eventBuses[profile.GetOnion()].Publish(event.NewEventList(event.ProtocolEngineCreated))
|
||||
app.QueryACNStatus()
|
||||
if doListen {
|
||||
if true {
|
||||
profile.Listen()
|
||||
}
|
||||
profile.StartConnections(doPeers, doServers)
|
||||
profile.StartConnections(true, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -228,7 +228,8 @@ type Field string
|
|||
const (
|
||||
|
||||
// A peers local onion address
|
||||
Onion = Field("Onion")
|
||||
Onion = Field("Onion")
|
||||
ProfileOnion = Field("ProfileOnion")
|
||||
|
||||
RemotePeer = Field("RemotePeer")
|
||||
LastSeen = Field("LastSeen")
|
||||
|
|
|
@ -178,6 +178,65 @@ func (om *OverlayMessage) ShouldAutoDL() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (f *Functionality) VerifyOrResumeDownload(profile peer.CwtchPeer, conversation int, fileKey string) {
|
||||
if manifestFilePath, exists := profile.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.manifest", fileKey)); exists {
|
||||
if downloadfilepath, exists := profile.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.path", fileKey)); exists {
|
||||
log.Debugf("resuming %s", fileKey)
|
||||
f.DownloadFile(profile, conversation, downloadfilepath, manifestFilePath, fileKey, files.MaxManifestSize*files.DefaultChunkSize)
|
||||
} else {
|
||||
log.Errorf("found manifest path but not download path for %s", fileKey)
|
||||
}
|
||||
} else {
|
||||
log.Errorf("no stored manifest path found for %s", fileKey)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Functionality) CheckDownloadStatus(profile peer.CwtchPeer, fileKey string) {
|
||||
path, _ := profile.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.path", fileKey))
|
||||
if value, exists := profile.GetScopedZonedAttribute(attr.LocalScope, attr.FilesharingZone, fmt.Sprintf("%s.complete", fileKey)); exists && value == event.True {
|
||||
profile.PublishEvent(event.NewEvent(event.FileDownloaded, map[event.Field]string{
|
||||
event.ProfileOnion: profile.GetOnion(),
|
||||
event.FileKey: fileKey,
|
||||
event.FilePath: path,
|
||||
event.TempFile: "",
|
||||
}))
|
||||
} else {
|
||||
log.Debugf("CheckDownloadStatus found .path but not .complete")
|
||||
profile.PublishEvent(event.NewEvent(event.FileDownloadProgressUpdate, map[event.Field]string{
|
||||
event.ProfileOnion: profile.GetOnion(),
|
||||
event.FileKey: fileKey,
|
||||
event.Progress: "-1",
|
||||
event.FileSizeInChunks: "-1",
|
||||
event.FilePath: path,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Functionality) EnhancedShareFile(profile peer.CwtchPeer, conversationID int, sharefilepath string) string {
|
||||
fileKey, overlay, err := f.ShareFile(sharefilepath, profile)
|
||||
if err != nil {
|
||||
log.Errorf("error sharing file: %v", err)
|
||||
} else if conversationID == -1 {
|
||||
// FIXME: At some point we might want to allow arbitrary public files, but for now this API will assume
|
||||
// there is only one, and it is the custom profile image...
|
||||
profile.SetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.CustomProfileImageKey, fileKey)
|
||||
} else {
|
||||
// Set a new attribute so we can associate this download with this conversation...
|
||||
profile.SetConversationAttribute(conversationID, attr.ConversationScope.ConstructScopedZonedPath(attr.FilesharingZone.ConstructZonedPath(fileKey)), "")
|
||||
id, err := profile.SendMessage(conversationID, overlay)
|
||||
if err == nil {
|
||||
return profile.EnhancedGetMessageById(conversationID, id)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// DownloadFileDefaultLimit given a profile, a conversation handle and a file sharing key, start off a download process
|
||||
// to downloadFilePath with a default filesize limit
|
||||
func (f *Functionality) DownloadFileDefaultLimit(profile peer.CwtchPeer, conversationID int, downloadFilePath string, manifestFilePath string, key string) error {
|
||||
return f.DownloadFile(profile, conversationID, downloadFilePath, manifestFilePath, key, files.MaxManifestSize*files.DefaultChunkSize)
|
||||
}
|
||||
|
||||
// DownloadFile given a profile, a conversation handle and a file sharing key, start off a download process
|
||||
// to downloadFilePath
|
||||
func (f *Functionality) DownloadFile(profile peer.CwtchPeer, conversationID int, downloadFilePath string, manifestFilePath string, key string, limit uint64) error {
|
||||
|
@ -409,6 +468,14 @@ type SharedFile struct {
|
|||
Expired bool
|
||||
}
|
||||
|
||||
func (f *Functionality) EnhancedGetSharedFiles(profile peer.CwtchPeer, conversationID int) string {
|
||||
data, err := json.Marshal(f.GetSharedFiles(profile, conversationID))
|
||||
if err == nil {
|
||||
return string(data)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetSharedFiles returns all file shares associated with a given conversation
|
||||
func (f *Functionality) GetSharedFiles(profile peer.CwtchPeer, conversationID int) []SharedFile {
|
||||
sharedFiles := []SharedFile{}
|
||||
|
|
|
@ -56,3 +56,5 @@ const SyncPreLastMessageTime = "SyncPreLastMessageTime"
|
|||
const SyncMostRecentMessageTime = "SyncMostRecentMessageTime"
|
||||
|
||||
const AttrLastConnectionTime = "last-connection-time"
|
||||
const PeerAutostart = "autostart"
|
||||
const Archived = "archived"
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
package model
|
||||
|
||||
import "sync"
|
||||
|
||||
// Experiments are optional functionality that can be enabled/disabled by an application either completely or individually.
|
||||
// examples of experiments include File Sharing, Profile Images and Groups.
|
||||
type Experiments struct {
|
||||
enabled bool
|
||||
experiments map[string]bool
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// InitExperiments encapsulates a set of experiments separate from their storage in GlobalSettings.
|
||||
|
@ -24,6 +27,10 @@ func (e *Experiments) IsEnabled(experiment string) bool {
|
|||
// todo handle default-enabled functionality
|
||||
return false
|
||||
}
|
||||
|
||||
// go will sometimes panic if we do not lock this read-only map...
|
||||
e.lock.Lock()
|
||||
defer e.lock.Unlock()
|
||||
enabled, exists := e.experiments[experiment]
|
||||
if !exists {
|
||||
return false
|
||||
|
|
|
@ -75,6 +75,94 @@ type cwtchPeer struct {
|
|||
experimentsLock sync.Mutex
|
||||
}
|
||||
|
||||
func (cp *cwtchPeer) EnhancedImportBundle(importString string) string {
|
||||
sarah marked this conversation as resolved
dan
commented
what's this for? Seems a weird "enhancement" that doesnt return an error just the string of the error? what's this for? Seems a weird "enhancement" that doesnt return an error just the string of the error?
|
||||
return cp.ImportBundle(importString).Error()
|
||||
}
|
||||
|
||||
func (cp *cwtchPeer) EnhancedGetMessages(conversation int, index int, count int) string {
|
||||
var emessages []EnhancedMessage = make([]EnhancedMessage, count)
|
||||
|
||||
messages, err := cp.GetMostRecentMessages(conversation, 0, index, count)
|
||||
if err == nil {
|
||||
|
||||
for i, message := range messages {
|
||||
|
||||
time, _ := time.Parse(time.RFC3339Nano, message.Attr[constants.AttrSentTimestamp])
|
||||
emessages[i].Message = model.Message{
|
||||
Message: message.Body,
|
||||
Acknowledged: message.Attr[constants.AttrAck] == constants.True,
|
||||
Error: message.Attr[constants.AttrErr],
|
||||
PeerID: message.Attr[constants.AttrAuthor],
|
||||
Timestamp: time,
|
||||
}
|
||||
emessages[i].ID = message.ID
|
||||
emessages[i].Attributes = message.Attr
|
||||
emessages[i].ContentHash = model.CalculateContentHash(message.Attr[constants.AttrAuthor], message.Body)
|
||||
}
|
||||
}
|
||||
|
||||
bytes, _ := json.Marshal(emessages)
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
func (cp *cwtchPeer) EnhancedGetMessageById(conversation int, messageID int) string {
|
||||
var message EnhancedMessage
|
||||
dbmessage, attr, err := cp.GetChannelMessage(conversation, 0, messageID)
|
||||
if err == nil {
|
||||
time, _ := time.Parse(time.RFC3339Nano, attr[constants.AttrSentTimestamp])
|
||||
message.Message = model.Message{
|
||||
Message: dbmessage,
|
||||
Acknowledged: attr[constants.AttrAck] == constants.True,
|
||||
Error: attr[constants.AttrErr],
|
||||
PeerID: attr[constants.AttrAuthor],
|
||||
Timestamp: time,
|
||||
}
|
||||
message.ID = messageID
|
||||
message.Attributes = attr
|
||||
message.ContentHash = model.CalculateContentHash(attr[constants.AttrAuthor], dbmessage)
|
||||
}
|
||||
bytes, _ := json.Marshal(message)
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
func (cp *cwtchPeer) EnhancedGetMessageByContentHash(conversation int, contentHash string) string {
|
||||
var message EnhancedMessage
|
||||
offset, err := cp.GetChannelMessageByContentHash(conversation, 0, contentHash)
|
||||
if err == nil {
|
||||
messages, err := cp.GetMostRecentMessages(conversation, 0, offset, 1)
|
||||
if err == nil {
|
||||
time, _ := time.Parse(time.RFC3339Nano, messages[0].Attr[constants.AttrSentTimestamp])
|
||||
message.Message = model.Message{
|
||||
Message: messages[0].Body,
|
||||
Acknowledged: messages[0].Attr[constants.AttrAck] == constants.True,
|
||||
Error: messages[0].Attr[constants.AttrErr],
|
||||
PeerID: messages[0].Attr[constants.AttrAuthor],
|
||||
Timestamp: time,
|
||||
}
|
||||
message.ID = messages[0].ID
|
||||
message.Attributes = messages[0].Attr
|
||||
message.LocalIndex = offset
|
||||
message.ContentHash = contentHash
|
||||
} else {
|
||||
log.Errorf("error fetching local index {} ", err)
|
||||
}
|
||||
}
|
||||
bytes, _ := json.Marshal(message)
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
func (cp *cwtchPeer) EnhancedSendMessage(conversation int, message string) string {
|
||||
mid, err := cp.SendMessage(conversation, message)
|
||||
if err == nil {
|
||||
return cp.EnhancedGetMessageById(conversation, mid)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (cp *cwtchPeer) ArchiveConversation(conversationID int) {
|
||||
cp.SetConversationAttribute(conversationID, attr.LocalScope.ConstructScopedZonedPath(attr.ProfileZone.ConstructZonedPath(constants.Archived)), constants.True)
|
||||
}
|
||||
|
||||
// IsFeatureEnabled returns true if the functionality defined by featureName has been enabled by the application, false otherwise.
|
||||
// this function is intended to be used by ProfileHooks to determine if they should execute experimental functionality.
|
||||
func (cp *cwtchPeer) IsFeatureEnabled(featureName string) bool {
|
||||
|
@ -119,7 +207,7 @@ func (cp *cwtchPeer) StoreCachedTokens(tokenServer string, tokens []*privacypass
|
|||
}
|
||||
}
|
||||
|
||||
func (cp *cwtchPeer) Export(file string) error {
|
||||
func (cp *cwtchPeer) ExportProfile(file string) error {
|
||||
cp.mutex.Lock()
|
||||
defer cp.mutex.Unlock()
|
||||
return cp.storage.Export(file)
|
||||
|
|
|
@ -48,6 +48,10 @@ type ModifyServers interface {
|
|||
// SendMessages enables a caller to sender messages to a contact
|
||||
type SendMessages interface {
|
||||
SendMessage(conversation int, message string) (int, error)
|
||||
|
||||
// EnhancedSendMessage Attempts to Send a Message and Immediately Attempts to Lookup the Message in the Database
|
||||
EnhancedSendMessage(conversation int, message string) string
|
||||
|
||||
SendInviteToConversation(conversationID int, inviteConversationID int) (int, error)
|
||||
SendScopedZonedGetValToContact(conversationID int, scope attr.Scope, zone attr.Zone, key string)
|
||||
}
|
||||
|
@ -101,10 +105,12 @@ type CwtchPeer interface {
|
|||
|
||||
// Import Bundle
|
||||
ImportBundle(string) error
|
||||
EnhancedImportBundle(string) string
|
||||
|
||||
// New Unified Conversation Interfaces
|
||||
NewContactConversation(handle string, acl model.AccessControl, accepted bool) (int, error)
|
||||
FetchConversations() ([]*model.Conversation, error)
|
||||
ArchiveConversation(conversation int)
|
||||
GetConversationInfo(conversation int) (*model.Conversation, error)
|
||||
FetchConversationInfo(handle string) (*model.Conversation, error)
|
||||
AcceptConversation(conversation int) error
|
||||
|
@ -121,6 +127,15 @@ type CwtchPeer interface {
|
|||
GetMostRecentMessages(conversation int, channel int, offset int, limit int) ([]model.ConversationMessage, error)
|
||||
UpdateMessageAttribute(conversation int, channel int, id int, key string, value string) error
|
||||
|
||||
// EnhancedGetMessageById returns a json-encoded enhanced message, suitable for rendering in a UI
|
||||
EnhancedGetMessageById(conversation int, mid int) string
|
||||
sarah marked this conversation as resolved
dan
commented
ah by enhanced you mean with all the extra stuff lg was injecting? i slightly like the term "enriched" or possibly something else? but enhanced more or less conveys the same too, to preference or taste i guess :) ah by enhanced you mean with all the extra stuff lg was injecting? i slightly like the term "enriched" or possibly something else? but enhanced more or less conveys the same too, to preference or taste i guess :)
|
||||
|
||||
// EnhancedGetMessageByContentHash returns a json-encoded enhanced message, suitable for rendering in a UI
|
||||
EnhancedGetMessageByContentHash(conversation int, hash string) string
|
||||
|
||||
// EnhancedGetMessages returns a set of json-encoded enhanced messages, suitable for rendering in a UI
|
||||
EnhancedGetMessages(conversation int, index int, count int) string
|
||||
|
||||
// Server Token APIS
|
||||
// TODO move these to feature protected interfaces
|
||||
StoreCachedTokens(tokenServer string, tokens []*privacypass.Token)
|
||||
|
@ -128,10 +143,20 @@ type CwtchPeer interface {
|
|||
// Profile Management
|
||||
CheckPassword(password string) bool
|
||||
ChangePassword(oldpassword string, newpassword string, newpasswordAgain string) error
|
||||
Export(file string) error
|
||||
ExportProfile(file string) error
|
||||
Delete()
|
||||
PublishEvent(resp event.Event)
|
||||
RegisterHook(hook ProfileHooks)
|
||||
UpdateExperiments(enabled bool, experiments map[string]bool)
|
||||
IsFeatureEnabled(featureName string) bool
|
||||
}
|
||||
|
||||
// EnhancedMessage wraps a Cwtch model.Message with some additional data to reduce calls from the UI.
|
||||
type EnhancedMessage struct {
|
||||
model.Message
|
||||
ID int // the actual ID of the message in the database (not the row number)
|
||||
LocalIndex int // local index in the DB (row #). Can be empty (most calls supply it) but lookup by hash will fill it
|
||||
ContentHash string
|
||||
ContactImage string
|
||||
Attributes map[string]string
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ func (fsss *FileSharingSubSystem) ShareFile(fileKey string, serializedManifest s
|
|||
log.Errorf("could not share file %v", err)
|
||||
return
|
||||
}
|
||||
log.Debugf("sharing file: %v %v", fileKey, serializedManifest)
|
||||
log.Infof("sharing file: %v %v", fileKey, serializedManifest)
|
||||
sarah marked this conversation as resolved
dan
commented
debug? debug?
|
||||
fsss.activeShares.Store(fileKey, &manifest)
|
||||
}
|
||||
|
||||
|
|
|
@ -139,7 +139,7 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
|||
const ServerAddr = "nfhxzvzxinripgdh4t2m4xcy3crf6p4cbhectgckuj3idsjsaotgowad"
|
||||
serverKeyBundle, _ := base64.StdEncoding.DecodeString(ServerKeyBundleBase64)
|
||||
|
||||
app := app2.NewApp(acn, "./storage")
|
||||
app := app2.NewApp(acn, "./storage", app2.InitApp("./storage"))
|
||||
|
||||
usr, _ := user.Current()
|
||||
cwtchDir := path.Join(usr.HomeDir, ".cwtch")
|
||||
|
@ -152,31 +152,31 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
|||
// ***** cwtchPeer setup *****
|
||||
|
||||
log.Infoln("Creating Alice...")
|
||||
app.CreateTaggedPeer("Alice", "asdfasdf", "test")
|
||||
app.CreateProfile("Alice", "asdfasdf", true)
|
||||
|
||||
log.Infoln("Creating Bob...")
|
||||
app.CreateTaggedPeer("Bob", "asdfasdf", "test")
|
||||
app.CreateProfile("Bob", "asdfasdf", true)
|
||||
|
||||
log.Infoln("Creating Carol...")
|
||||
app.CreateTaggedPeer("Carol", "asdfasdf", "test")
|
||||
app.CreateProfile("Carol", "asdfasdf", true)
|
||||
|
||||
alice := app2.WaitGetPeer(app, "Alice")
|
||||
aliceBus := app.GetEventBus(alice.GetOnion())
|
||||
app.ActivatePeerEngine(alice.GetOnion(), true, true, true)
|
||||
app.ActivatePeerEngine(alice.GetOnion())
|
||||
log.Infoln("Alice created:", alice.GetOnion())
|
||||
alice.SetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.Name, "Alice")
|
||||
alice.AutoHandleEvents([]event.Type{event.PeerStateChange, event.ServerStateChange, event.NewGroupInvite, event.NewRetValMessageFromPeer})
|
||||
|
||||
bob := app2.WaitGetPeer(app, "Bob")
|
||||
bobBus := app.GetEventBus(bob.GetOnion())
|
||||
app.ActivatePeerEngine(bob.GetOnion(), true, true, true)
|
||||
app.ActivatePeerEngine(bob.GetOnion())
|
||||
log.Infoln("Bob created:", bob.GetOnion())
|
||||
bob.SetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.Name, "Bob")
|
||||
bob.AutoHandleEvents([]event.Type{event.PeerStateChange, event.ServerStateChange, event.NewGroupInvite, event.NewRetValMessageFromPeer})
|
||||
|
||||
carol := app2.WaitGetPeer(app, "Carol")
|
||||
carolBus := app.GetEventBus(carol.GetOnion())
|
||||
app.ActivatePeerEngine(carol.GetOnion(), true, true, true)
|
||||
app.ActivatePeerEngine(carol.GetOnion())
|
||||
log.Infoln("Carol created:", carol.GetOnion())
|
||||
carol.SetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.Name, "Carol")
|
||||
carol.AutoHandleEvents([]event.Type{event.PeerStateChange, event.ServerStateChange, event.NewGroupInvite, event.NewRetValMessageFromPeer})
|
||||
|
|
|
@ -59,9 +59,9 @@ func TestEncryptedStorage(t *testing.T) {
|
|||
|
||||
defer acn.Close()
|
||||
acn.WaitTillBootstrapped()
|
||||
app := app2.NewApp(acn, cwtchDir)
|
||||
app.CreateTaggedPeer("alice", "password", constants.ProfileTypeV1Password)
|
||||
app.CreateTaggedPeer("bob", "password", constants.ProfileTypeV1Password)
|
||||
app := app2.NewApp(acn, cwtchDir, app2.InitApp(cwtchDir))
|
||||
app.CreateProfile("alice", "password", true)
|
||||
app.CreateProfile("bob", "password", true)
|
||||
|
||||
alice := app2.WaitGetPeer(app, "alice")
|
||||
bob := app2.WaitGetPeer(app, "bob")
|
||||
|
@ -130,7 +130,7 @@ func TestEncryptedStorage(t *testing.T) {
|
|||
t.Fatalf("expeced GetMostRecentMessages to return 1, instead returned: %v %v", len(messages), messages)
|
||||
}
|
||||
|
||||
err = alice.Export("alice.tar.gz")
|
||||
err = alice.ExportProfile("alice.tar.gz")
|
||||
if err != nil {
|
||||
t.Fatalf("could not export profile: %v", err)
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ func TestEncryptedStorage(t *testing.T) {
|
|||
t.Fatal("profile is already imported...this should fail")
|
||||
}
|
||||
|
||||
app.DeletePeer(alice.GetOnion(), "password")
|
||||
app.DeleteProfile(alice.GetOnion(), "password")
|
||||
alice, err = app.ImportProfile("alice.tar.gz", "password")
|
||||
if err != nil {
|
||||
t.Fatalf("profile should have successfully imported: %s", err)
|
||||
|
|
|
@ -97,7 +97,7 @@ func TestFileSharing(t *testing.T) {
|
|||
acn.WaitTillBootstrapped()
|
||||
defer acn.Close()
|
||||
|
||||
app := app2.NewApp(acn, "./storage")
|
||||
app := app2.NewApp(acn, "./storage", app2.InitApp("./storage"))
|
||||
|
||||
usr, _ := user.Current()
|
||||
cwtchDir := path.Join(usr.HomeDir, ".cwtch")
|
||||
|
@ -106,16 +106,16 @@ func TestFileSharing(t *testing.T) {
|
|||
os.Mkdir(path.Join(cwtchDir, "testing"), 0700)
|
||||
|
||||
t.Logf("Creating Alice...")
|
||||
app.CreateTaggedPeer("alice", "asdfasdf", "testing")
|
||||
app.CreateProfile("alice", "asdfasdf", true)
|
||||
|
||||
t.Logf("Creating Bob...")
|
||||
app.CreateTaggedPeer("bob", "asdfasdf", "testing")
|
||||
app.CreateProfile("bob", "asdfasdf", true)
|
||||
|
||||
t.Logf("** Waiting for Alice, Bob...")
|
||||
alice := app2.WaitGetPeer(app, "alice")
|
||||
app.ActivatePeerEngine(alice.GetOnion(), true, true, true)
|
||||
app.ActivatePeerEngine(alice.GetOnion())
|
||||
bob := app2.WaitGetPeer(app, "bob")
|
||||
app.ActivatePeerEngine(bob.GetOnion(), true, true, true)
|
||||
app.ActivatePeerEngine(bob.GetOnion())
|
||||
|
||||
alice.AutoHandleEvents([]event.Type{event.PeerStateChange, event.NewRetValMessageFromPeer})
|
||||
bob.AutoHandleEvents([]event.Type{event.PeerStateChange, event.NewRetValMessageFromPeer})
|
||||
|
@ -178,7 +178,7 @@ func TestFileSharing(t *testing.T) {
|
|||
testBobDownloadFile(t, bob, filesharingFunctionality, queueOracle)
|
||||
|
||||
// test that we can delete bob...
|
||||
app.DeletePeer(bob.GetOnion(), "asdfasdf")
|
||||
app.DeleteProfile(bob.GetOnion(), "asdfasdf")
|
||||
|
||||
queueOracle.Shutdown()
|
||||
app.Shutdown()
|
||||
|
|
Loading…
Reference in New Issue
should take the
attributes map[attr.ZonedPath]string
we've had to extend this twice, once for tags on default or no default password, and then autostart or not, so to future proof, should take the attribute map or else it's likely as we continue to build we'll have to extend the API again