Compare commits

..

1 Commits

Author SHA1 Message Date
Sarah Jamie Lewis d372a15ff9 Remove EventHandler Map that isn't Used Anymore 2021-06-22 15:31:40 -07:00
7 changed files with 328 additions and 338 deletions

View File

@ -1,5 +0,0 @@
package constants
// We offer "un-passworded" profiles but our storage encrypts everything with a password. We need an agreed upon
// password to use in that case, that the app case use behind the scenes to password and unlock with
const DefactoPasswordForUnencryptedProfiles = "be gay do crime"

2
go.mod
View File

@ -3,7 +3,7 @@ module git.openprivacy.ca/flutter/libcwtch-go
go 1.15 go 1.15
require ( require (
cwtch.im/cwtch v0.8.11 cwtch.im/cwtch v0.8.10
git.openprivacy.ca/openprivacy/connectivity v1.4.4 git.openprivacy.ca/openprivacy/connectivity v1.4.4
git.openprivacy.ca/openprivacy/log v1.0.2 git.openprivacy.ca/openprivacy/log v1.0.2
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect

4
go.sum
View File

@ -1,5 +1,5 @@
cwtch.im/cwtch v0.8.11 h1:nUrd6srjLxInSJ0q1JdmRBhN4RRlZRL+2vIyE/AXkfY= cwtch.im/cwtch v0.8.10 h1:y1PAEz9j2wLWahmBLNu7rPAlNpA+9FLwMAzIT/yfXjM=
cwtch.im/cwtch v0.8.11/go.mod h1:D9dtO+WnKqdmufKSfFeFlUYaxLTfE/RtqVe1OD0kiKc= cwtch.im/cwtch v0.8.10/go.mod h1:D9dtO+WnKqdmufKSfFeFlUYaxLTfE/RtqVe1OD0kiKc=
git.openprivacy.ca/cwtch.im/tapir v0.4.3 h1:sctSfUXHDIqaHfJPDl+5lHtmoEJolQiHTcHZGAe5Qc4= git.openprivacy.ca/cwtch.im/tapir v0.4.3 h1:sctSfUXHDIqaHfJPDl+5lHtmoEJolQiHTcHZGAe5Qc4=
git.openprivacy.ca/cwtch.im/tapir v0.4.3/go.mod h1:10qEaib5x021zgyZ/97JKWsEpedH5+Vfy2CvB2V+08E= git.openprivacy.ca/cwtch.im/tapir v0.4.3/go.mod h1:10qEaib5x021zgyZ/97JKWsEpedH5+Vfy2CvB2V+08E=
git.openprivacy.ca/openprivacy/bine v0.0.4 h1:CO7EkGyz+jegZ4ap8g5NWRuDHA/56KKvGySR6OBPW+c= git.openprivacy.ca/openprivacy/bine v0.0.4 h1:CO7EkGyz+jegZ4ap8g5NWRuDHA/56KKvGySR6OBPW+c=

188
lib.go
View File

@ -18,7 +18,6 @@ import (
"git.openprivacy.ca/flutter/libcwtch-go/utils" "git.openprivacy.ca/flutter/libcwtch-go/utils"
"git.openprivacy.ca/openprivacy/connectivity" "git.openprivacy.ca/openprivacy/connectivity"
"runtime" "runtime"
"strconv"
"strings" "strings"
"encoding/base64" "encoding/base64"
@ -68,10 +67,9 @@ func c_StartCwtch(dir_c *C.char, len C.int, tor_c *C.char, torLen C.int) int8 {
// message: CwtchStarted when start up is complete and app is safe to use // message: CwtchStarted when start up is complete and app is safe to use
// CwtchStartError message when start up fails (includes event.Error data field) // CwtchStartError message when start up fails (includes event.Error data field)
func StartCwtch(appDir string, torPath string) int { func StartCwtch(appDir string, torPath string) int {
log.SetLevel(log.LevelInfo) eventHandler = utils.NewEventHandler()
log.SetLevel(log.LevelDebug)
log.Infof("StartCwtch(...)")
// Quick hack check that we're being called with the correct params // Quick hack check that we're being called with the correct params
// On android a stale worker could be calling us with "last apps" directory. Best to abort fast so the app can make a new worker // On android a stale worker could be calling us with "last apps" directory. Best to abort fast so the app can make a new worker
if runtime.GOOS == "android" { if runtime.GOOS == "android" {
@ -88,21 +86,12 @@ func StartCwtch(appDir string, torPath string) int {
} }
func _startCwtch(appDir string, torPath string) { func _startCwtch(appDir string, torPath string) {
log.Infof("application: %v eventHandler: %v acn: %v", application, eventHandler, globalACN) // Exclude Tapir wire Messages (We need a TRACE level)
if application != nil {
log.Infof("_startCwtch detected existing application; resuming instead of relaunching")
ReconnectCwtchForeground()
return
}
// Exclude Tapir wire Messages
//(We need a TRACE level)
log.ExcludeFromPattern("service.go") log.ExcludeFromPattern("service.go")
// Ensure that the application directory exists...and then initialize settings.. // Ensure that the application directory exists...and then initialize settings..
os.MkdirAll(path.Join(appDir), 0700) os.MkdirAll(path.Join(appDir), 0700)
utils.InitGlobalSettingsFile(appDir, constants.DefactoPasswordForUnencryptedProfiles) utils.InitGlobalSettingsFile(appDir, "be gay do crime")
log.Infof("Loading Cwtch Directory %v and tor path: %v", appDir, torPath) log.Infof("Loading Cwtch Directory %v and tor path: %v", appDir, torPath)
@ -117,9 +106,6 @@ func _startCwtch(appDir string, torPath string) {
panic(err) panic(err)
} }
log.Infof("Creating new EventHandler()")
eventHandler = utils.NewEventHandler()
log.Infof("making directory %v", appDir) log.Infof("making directory %v", appDir)
os.MkdirAll(path.Join(appDir, "/.tor", "tor"), 0700) os.MkdirAll(path.Join(appDir, "/.tor", "tor"), 0700)
tor.NewTorrc().WithSocksPort(port).WithOnionTrafficOnly().WithControlPort(controlPort).WithHashedPassword(base64.StdEncoding.EncodeToString(key)).Build(filepath.Join(appDir, ".tor", "tor", "torrc")) tor.NewTorrc().WithSocksPort(port).WithOnionTrafficOnly().WithControlPort(controlPort).WithHashedPassword(base64.StdEncoding.EncodeToString(key)).Build(filepath.Join(appDir, ".tor", "tor", "torrc"))
@ -157,14 +143,13 @@ func _startCwtch(appDir string, torPath string) {
settings := utils.ReadGlobalSettings() settings := utils.ReadGlobalSettings()
settingsJson, _ := json.Marshal(settings) settingsJson, _ := json.Marshal(settings)
newApp.LoadProfiles(constants.DefactoPasswordForUnencryptedProfiles) newApp.LoadProfiles("be gay do crime")
application = newApp application = newApp
// Send global settings to the UI... // Send global settings to the UI...
application.GetPrimaryBus().Publish(event.NewEvent(utils.UpdateGlobalSettings, map[event.Field]string{event.Data: string(settingsJson)})) application.GetPrimaryBus().Publish(event.NewEvent(utils.UpdateGlobalSettings, map[event.Field]string{event.Data: string(settingsJson)}))
log.Infof("libcwtch-go application launched") log.Infof("libcwtch-go application launched")
application.GetPrimaryBus().Publish(event.NewEvent(utils.CwtchStarted, map[event.Field]string{})) application.GetPrimaryBus().Publish(event.NewEvent(utils.CwtchStarted, map[event.Field]string{}))
application.QueryACNVersion()
} }
//export c_ReconnectCwtchForeground //export c_ReconnectCwtchForeground
@ -175,46 +160,15 @@ func c_ReconnectCwtchForeground() {
// Like StartCwtch, but StartCwtch has already been called so we don't need to restart Tor etc (probably) // Like StartCwtch, but StartCwtch has already been called so we don't need to restart Tor etc (probably)
// Do need to re-send initial state tho, eg profiles that are already loaded // Do need to re-send initial state tho, eg profiles that are already loaded
func ReconnectCwtchForeground() { func ReconnectCwtchForeground() {
log.Infof("Reconnecting cwtchforeground")
if application == nil { if application == nil {
log.Errorf("ReconnectCwtchForeground: Application is nil, presuming stale thread, EXITING Reconnect\n") log.Errorf("ReconnectCwtchForeground: Application is nil, presuming stale thread, EXITING Reconnect\n")
return return
} }
// populate profile list
peerList := application.ListPeers() peerList := application.ListPeers()
for onion := range peerList { for onion := range peerList {
eventHandler.Push(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: onion, event.Created: event.False, "Reload": event.True})) eventHandler.Push(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: onion, event.Created: event.False}))
} }
for onion := range peerList {
// fix peerpeercontact message counts
contactList := application.GetPeer(onion).GetContacts()
for _, handle := range contactList {
totalMessages := application.GetPeer(onion).GetContact(handle).Timeline.Len() + len(application.GetPeer(onion).GetContact(handle).UnacknowledgedMessages)
eventHandler.Push(event.NewEvent(event.MessageCounterResync, map[event.Field]string{
event.Identity: onion,
event.RemotePeer: handle,
event.Data: strconv.Itoa(totalMessages),
}))
}
// fix peergroupcontact message counts
groupList := application.GetPeer(onion).GetGroups()
for _, groupID := range groupList {
totalMessages := application.GetPeer(onion).GetGroup(groupID).Timeline.Len() + len(application.GetPeer(onion).GetGroup(groupID).UnacknowledgedMessages)
eventHandler.Push(event.NewEvent(event.MessageCounterResync, map[event.Field]string{
event.Identity: onion,
event.GroupID: groupID,
event.Data: strconv.Itoa(totalMessages),
}))
}
}
application.GetPrimaryBus().Publish(event.NewEvent(utils.CwtchStarted, map[event.Field]string{})) application.GetPrimaryBus().Publish(event.NewEvent(utils.CwtchStarted, map[event.Field]string{}))
application.QueryACNStatus()
application.QueryACNVersion()
} }
//export c_SendAppEvent //export c_SendAppEvent
@ -347,18 +301,10 @@ func c_GetAppBusEvent() *C.char {
// GetAppBusEvent blocks until an event // GetAppBusEvent blocks until an event
func GetAppBusEvent() string { func GetAppBusEvent() string {
log.Debugf("appbusevent called")
for eventHandler == nil {
log.Debugf("waiting for eventHandler != nil")
time.Sleep(time.Second)
}
var json = "" var json = ""
for json == "" { for json == "" {
log.Debugf("waiting for json != ''")
json = eventHandler.GetNextEvent() json = eventHandler.GetNextEvent()
} }
log.Debugf("appbusevent: %v", json)
return json return json
} }
@ -368,17 +314,35 @@ type Profile struct {
ImagePath string `json:"imagePath"` ImagePath string `json:"imagePath"`
} }
//export c_GetProfiles
func c_GetProfiles() *C.char {
return C.CString(GetProfiles())
}
func GetProfiles() string {
peerList := application.ListPeers()
profiles := make([]Profile, len(peerList))
i := 0
for onion := range peerList {
name, _ := application.GetPeer(onion).GetAttribute(attr.GetPublicScope(constants.Name))
profiles[i] = Profile{
Name: name,
Onion: onion,
ImagePath: "",
}
i += 1
}
jsonBytes, _ := json.Marshal(profiles)
return string(jsonBytes)
}
//export c_CreateProfile //export c_CreateProfile
func c_CreateProfile(nick_ptr *C.char, nick_len C.int, pass_ptr *C.char, pass_len C.int) { func c_CreateProfile(nick_ptr *C.char, nick_len C.int, pass_ptr *C.char, pass_len C.int) {
CreateProfile(C.GoStringN(nick_ptr, nick_len), C.GoStringN(pass_ptr, pass_len)) CreateProfile(C.GoStringN(nick_ptr, nick_len), C.GoStringN(pass_ptr, pass_len))
} }
func CreateProfile(nick, pass string) { func CreateProfile(nick, pass string) {
if pass == constants.DefactoPasswordForUnencryptedProfiles { application.CreatePeer(nick, pass)
application.CreateTaggedPeer(nick, pass, constants.ProfileTypeV1DefaultPassword)
} else {
application.CreateTaggedPeer(nick, pass, constants.ProfileTypeV1Password)
}
} }
//export c_LoadProfiles //export c_LoadProfiles
@ -464,6 +428,36 @@ func BlockContact(profile, handle string) {
} }
} }
//export c_DebugResetContact
func c_DebugResetContact(profilePtr *C.char, profileLen C.int, handlePtr *C.char, handleLen C.int) {
DebugResetContact(C.GoStringN(profilePtr, profileLen), C.GoStringN(handlePtr, handleLen))
}
func DebugResetContact(profile, handle string) {
err := application.GetPeer(profile).SetContactAuthorization(handle, model.AuthUnknown)
if err == nil {
application.GetPrimaryBus().Publish(event.NewEvent(event.PeerStateChange, map[event.Field]string{
ProfileOnion: profile,
event.RemotePeer: handle,
"authorization": string(model.AuthUnknown),
}))
} else {
log.Errorf("error resetting contact: %s", err.Error())
}
}
//export c_NumMessages
func c_NumMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int) (n int) {
profile := C.GoStringN(profile_ptr, profile_len)
handle := C.GoStringN(handle_ptr, handle_len)
return (NumMessages(profile, handle))
}
func NumMessages(profile, handle string) (n int) {
n = len(application.GetPeer(profile).GetContact(handle).Timeline.Messages)
return
}
//export c_UpdateMessageFlags //export c_UpdateMessageFlags
func c_UpdateMessageFlags(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, mIdx C.int, message_flags C.ulong) { func c_UpdateMessageFlags(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, mIdx C.int, message_flags C.ulong) {
profile := C.GoStringN(profile_ptr, profile_len) profile := C.GoStringN(profile_ptr, profile_len)
@ -519,19 +513,24 @@ func GetMessage(profileOnion, handle string, message_index int) string {
if message_index < len(profile.GetContact(handle).Timeline.Messages) { if message_index < len(profile.GetContact(handle).Timeline.Messages) {
message.Message = profile.GetContact(handle).Timeline.Messages[message_index] message.Message = profile.GetContact(handle).Timeline.Messages[message_index]
message.ContactImage = ph.GetProfilePic(handle) message.ContactImage = ph.GetProfilePic(handle)
} else {
log.Errorf("peerpeercontact getmessage out of range; sending counter resync just in case")
eventHandler.Push(event.NewEvent(event.MessageCounterResync, map[event.Field]string{
event.Identity: profileOnion,
event.RemotePeer: handle,
event.Data: strconv.Itoa(len(profile.GetContact(handle).Timeline.Messages)),
}))
} }
} }
bytes, _ := json.Marshal(message) bytes, _ := json.Marshal(message)
return string(bytes) return string(bytes)
} }
//export c_GetMessages
func c_GetMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, start C.int, end C.int) *C.char {
profile := C.GoStringN(profile_ptr, profile_len)
handle := C.GoStringN(handle_ptr, handle_len)
return C.CString(GetMessages(profile, handle, int(start), int(end)))
}
func GetMessages(profile, handle string, start, end int) string {
messages := application.GetPeer(profile).GetContact(handle).Timeline.Messages[start:end]
bytes, _ := json.Marshal(messages)
return string(bytes)
}
//export c_SendMessage //export c_SendMessage
func c_SendMessage(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, msg_ptr *C.char, msg_len C.int) { func c_SendMessage(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, msg_ptr *C.char, msg_len C.int) {
@ -598,6 +597,15 @@ func ResetTor() {
globalACN.Restart() globalACN.Restart()
} }
//export c_QueryACNVersion
func c_QueryACNVersion() {
QueryACNVersion()
}
func QueryACNVersion() {
application.QueryACNVersion()
}
//export c_CreateGroup //export c_CreateGroup
func c_CreateGroup(profile_ptr *C.char, profile_len C.int, server_ptr *C.char, server_len C.int, name_ptr *C.char, name_len C.int) { func c_CreateGroup(profile_ptr *C.char, profile_len C.int, server_ptr *C.char, server_len C.int, name_ptr *C.char, name_len C.int) {
profile := C.GoStringN(profile_ptr, profile_len) profile := C.GoStringN(profile_ptr, profile_len)
@ -631,12 +639,6 @@ func c_DeleteProfile(profile_ptr *C.char, profile_len C.int, password_ptr *C.cha
// DeleteProfile deletes a profile given the right password // DeleteProfile deletes a profile given the right password
func DeleteProfile(profile string, password string) { func DeleteProfile(profile string, password string) {
// allow a blank password to delete "unencrypted" accounts...
if password == "" {
password = constants.DefactoPasswordForUnencryptedProfiles
}
application.DeletePeer(profile, password) application.DeletePeer(profile, password)
} }
@ -728,15 +730,31 @@ func ShutdownCwtch() {
eventHandler.Push(event.NewEvent(event.Shutdown, map[event.Field]string{})) eventHandler.Push(event.NewEvent(event.Shutdown, map[event.Field]string{}))
// Allow for the shutdown events to go through and then purge everything else... // Allow for the shutdown events to go through and then purge everything else...
log.Infof("Shutting Down Application...") log.Debugf("Shutting Down Application...")
application.Shutdown() application.Shutdown()
log.Infof("Shutting Down ACN...") log.Debugf("Shutting Down ACN...")
globalACN.Close() globalACN.Close()
log.Infof("Library Shutdown Complete!") log.Debugf("Library Shutdown Complete!")
// do not remove - important for state checks elsewhere }
application = nil }
globalACN = nil
eventHandler = nil //export c_ResyncConversation
func c_ResyncConversation(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int) {
profile := C.GoStringN(profile_ptr, profile_len)
name := C.GoStringN(handle_ptr, handle_len)
ResyncConversation(profile, name)
}
// ResyncConversation forces the protocol engine to retry connecting to the underlying contact, in the case of
// a group it will create completely refresh cached server state and so may cause older messages to appear (bandwidth intensive)
func ResyncConversation(profileOnion string, handle string) {
profile := application.GetPeer(profileOnion)
ph := utils.NewPeerHelper(profile)
if ph.IsPeer(handle) {
profile.PeerWithOnion(handle)
} else if ph.IsGroup(handle) {
group := profile.GetGroup(handle)
profile.ResyncServer(group.GroupServer)
} }
} }

View File

@ -64,163 +64,149 @@ func (eh *EventHandler) GetNextEvent() string {
// handleAppBusEvent enriches AppBus events so they are usable with out further data fetches // handleAppBusEvent enriches AppBus events so they are usable with out further data fetches
func (eh *EventHandler) handleAppBusEvent(e *event.Event) string { func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
log.Debugf("New AppBus Event to Handle: %v", e) log.Debugf("New AppBus Event to Handle: %v", e)
if eh.app != nil { switch e.EventType {
switch e.EventType { case event.ACNStatus:
case event.ACNStatus: if e.Data[event.Progress] == "100" {
if e.Data[event.Progress] == "100" { for onion := range eh.app.ListPeers() {
for onion := range eh.app.ListPeers() { // launch a listen thread (internally this does a check that the protocol engine is not listening)
// launch a listen thread (internally this does a check that the protocol engine is not listening) // and as such is safe to call.
// and as such is safe to call. eh.app.GetPeer(onion).Listen()
eh.app.GetPeer(onion).Listen()
}
} }
case event.NewPeer:
onion := e.Data[event.Identity]
profile := eh.app.GetPeer(e.Data[event.Identity])
log.Debug("New Peer Event: %v", e)
if e.Data["Reload"] != event.True {
eh.startHandlingPeer(onion)
}
tag,isTagged := profile.GetAttribute(app.AttributeTag)
if isTagged {
e.Data[app.AttributeTag] = tag
} else {
// Assume encrypted for non-tagged profiles - this isn't always true, but all post-beta profiles
// are tagged on creation.
e.Data[app.AttributeTag] = constants.ProfileTypeV1Password
}
if e.Data[event.Created] == event.True {
name, _ := profile.GetAttribute(attr.GetLocalScope(constants.Name))
profile.SetAttribute(attr.GetPublicScope(constants.Name), name)
profile.SetAttribute(attr.GetPublicScope(constants.Picture), ImageToString(NewImage(RandomProfileImage(onion), TypeImageDistro)))
}
if e.Data[event.Status] != event.StorageRunning || e.Data[event.Created] == event.True {
profile.SetAttribute(attr.GetLocalScope(constants.PeerOnline), event.False)
eh.app.AddPeerPlugin(onion, plugins.CONNECTIONRETRY)
eh.app.AddPeerPlugin(onion, plugins.NETWORKCHECK)
// If the user has chosen to block unknown profiles
// then explicitly configure the protocol engine to do so..
if ReadGlobalSettings().BlockUnknownConnections {
profile.BlockUnknownConnections()
} else {
// For completeness
profile.AllowUnknownConnections()
}
// Start up the Profile
profile.Listen()
profile.StartPeersConnections()
if _, err := groups.ExperimentGate(ReadGlobalSettings().Experiments); err == nil {
profile.StartServerConnections()
}
}
nick, exists := profile.GetAttribute(attr.GetPublicScope(constants.Name))
if !exists {
nick = onion
}
picVal, ok := profile.GetAttribute(attr.GetPublicScope(constants.Picture))
if !ok {
picVal = ImageToString(NewImage(RandomProfileImage(onion), TypeImageDistro))
}
pic, err := StringToImage(picVal)
if err != nil {
pic = NewImage(RandomProfileImage(onion), TypeImageDistro)
}
picPath := GetPicturePath(pic)
//tag, _ := profile.GetAttribute(app.AttributeTag)
online, _ := profile.GetAttribute(attr.GetLocalScope(constants.PeerOnline))
e.Data[constants.Name] = nick
e.Data[constants.Picture] = picPath
e.Data["Online"] = online
var contacts []Contact
var servers []groups.Server
for _, contact := range profile.GetContacts() {
// Only compile the server info if we have enabled the experiment...
// Note that this means that this info can become stale if when first loaded the experiment
// has been disabled and then is later re-enabled. As such we need to ensure that this list is
// re-fetched when the group experiment is enabled via a dedicated ListServerInfo event...
if profile.GetContact(contact).IsServer() {
groupHandler, err := groups.ExperimentGate(ReadGlobalSettings().Experiments)
if err == nil {
servers = append(servers, groupHandler.GetServerInfo(contact, profile))
}
continue
}
contactInfo := profile.GetContact(contact)
ph := NewPeerHelper(profile)
name := ph.GetNick(contact)
cpicPath := ph.GetProfilePic(contact)
saveHistory, set := contactInfo.GetAttribute(event.SaveHistoryKey)
if !set {
saveHistory = event.DeleteHistoryDefault
}
contacts = append(contacts, Contact{
Name: name,
Onion: contactInfo.Onion,
Status: contactInfo.State,
Picture: cpicPath,
Authorization: string(contactInfo.Authorization),
SaveHistory: saveHistory,
Messages: contactInfo.Timeline.Len(),
Unread: 0,
LastMessage: strconv.Itoa(getLastMessageTime(&contactInfo.Timeline)),
IsGroup: false,
})
}
// We compile and send the groups regardless of the experiment flag, and hide them in the UI
for _, groupId := range profile.GetGroups() {
group := profile.GetGroup(groupId)
// Check that the group is cryptographically valid
if !group.CheckGroup() {
continue
}
ph := NewPeerHelper(profile)
cpicPath := ph.GetProfilePic(groupId)
authorization := model.AuthUnknown
if group.Accepted {
authorization = model.AuthApproved
}
contacts = append(contacts, Contact{
Name: ph.GetNick(groupId),
Onion: group.GroupID,
Status: group.State,
Picture: cpicPath,
Authorization: string(authorization),
SaveHistory: event.SaveHistoryConfirmed,
Messages: group.Timeline.Len(),
Unread: 0,
LastMessage: strconv.Itoa(getLastMessageTime(&group.Timeline)),
IsGroup: true,
GroupServer: group.GroupServer,
})
}
bytes, _ := json.Marshal(contacts)
e.Data["ContactsJson"] = string(bytes)
// Marshal the server list into the new peer event...
serversListBytes, _ := json.Marshal(servers)
e.Data[groups.ServerList] = string(serversListBytes)
log.Debugf("contactsJson %v", e.Data["ContactsJson"])
} }
case event.NewPeer:
onion := e.Data[event.Identity]
profile := eh.app.GetPeer(e.Data[event.Identity])
log.Debug("New Peer Event: %v", e)
eh.startHandlingPeer(onion)
if e.Data[event.Created] == event.True {
name, _ := profile.GetAttribute(attr.GetLocalScope(constants.Name))
profile.SetAttribute(attr.GetPublicScope(constants.Name), name)
profile.SetAttribute(attr.GetPublicScope(constants.Picture), ImageToString(NewImage(RandomProfileImage(onion), TypeImageDistro)))
}
if e.Data[event.Status] != event.StorageRunning || e.Data[event.Created] == event.True {
profile.SetAttribute(attr.GetLocalScope(constants.PeerOnline), event.False)
eh.app.AddPeerPlugin(onion, plugins.CONNECTIONRETRY)
eh.app.AddPeerPlugin(onion, plugins.NETWORKCHECK)
// If the user has chosen to block unknown profiles
// then explicitly configure the protocol engine to do so..
if ReadGlobalSettings().BlockUnknownConnections {
profile.BlockUnknownConnections()
} else {
// For completeness
profile.AllowUnknownConnections()
}
// Start up the Profile
profile.Listen()
profile.StartPeersConnections()
if _, err := groups.ExperimentGate(ReadGlobalSettings().Experiments); err == nil {
profile.StartServerConnections()
}
}
nick, exists := profile.GetAttribute(attr.GetPublicScope(constants.Name))
if !exists {
nick = onion
}
picVal, ok := profile.GetAttribute(attr.GetPublicScope(constants.Picture))
if !ok {
picVal = ImageToString(NewImage(RandomProfileImage(onion), TypeImageDistro))
}
pic, err := StringToImage(picVal)
if err != nil {
pic = NewImage(RandomProfileImage(onion), TypeImageDistro)
}
picPath := GetPicturePath(pic)
//tag, _ := profile.GetAttribute(app.AttributeTag)
online, _ := profile.GetAttribute(attr.GetLocalScope(constants.PeerOnline))
e.Data[constants.Name] = nick
e.Data[constants.Picture] = picPath
e.Data["Online"] = online
var contacts []Contact
var servers []groups.Server
for _, contact := range profile.GetContacts() {
// Only compile the server info if we have enabled the experiment...
// Note that this means that this info can become stale if when first loaded the experiment
// has been disabled and then is later re-enabled. As such we need to ensure that this list is
// re-fetched when the group experiment is enabled via a dedicated ListServerInfo event...
if profile.GetContact(contact).IsServer() {
groupHandler, err := groups.ExperimentGate(ReadGlobalSettings().Experiments)
if err == nil {
servers = append(servers, groupHandler.GetServerInfo(contact, profile))
}
continue
}
contactInfo := profile.GetContact(contact)
ph := NewPeerHelper(profile)
name := ph.GetNick(contact)
cpicPath := ph.GetProfilePic(contact)
saveHistory, set := contactInfo.GetAttribute(event.SaveHistoryKey)
if !set {
saveHistory = event.DeleteHistoryDefault
}
contacts = append(contacts, Contact{
Name: name,
Onion: contactInfo.Onion,
Status: contactInfo.State,
Picture: cpicPath,
Authorization: string(contactInfo.Authorization),
SaveHistory: saveHistory,
Messages: contactInfo.Timeline.Len(),
Unread: 0,
LastMessage: strconv.Itoa(getLastMessageTime(&contactInfo.Timeline)),
IsGroup: false,
})
}
// We compile and send the groups regardless of the experiment flag, and hide them in the UI
for _, groupId := range profile.GetGroups() {
group := profile.GetGroup(groupId)
// Check that the group is cryptographically valid
if !group.CheckGroup() {
continue
}
ph := NewPeerHelper(profile)
cpicPath := ph.GetProfilePic(groupId)
authorization := model.AuthUnknown
if group.Accepted {
authorization = model.AuthApproved
}
contacts = append(contacts, Contact{
Name: ph.GetNick(groupId),
Onion: group.GroupID,
Status: group.State,
Picture: cpicPath,
Authorization: string(authorization),
SaveHistory: event.SaveHistoryConfirmed,
Messages: group.Timeline.Len(),
Unread: 0,
LastMessage: strconv.Itoa(getLastMessageTime(&group.Timeline)),
IsGroup: true,
GroupServer: group.GroupServer,
})
}
bytes, _ := json.Marshal(contacts)
e.Data["ContactsJson"] = string(bytes)
// Marshal the server list into the new peer event...
serversListBytes, _ := json.Marshal(servers)
e.Data[groups.ServerList] = string(serversListBytes)
log.Infof("contactsJson %v", e.Data["ContactsJson"])
} }
json, _ := json.Marshal(e) json, _ := json.Marshal(e)
@ -229,95 +215,92 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
// handleProfileEvent enriches Profile events so they are usable with out further data fetches // handleProfileEvent enriches Profile events so they are usable with out further data fetches
func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string { func (eh *EventHandler) handleProfileEvent(ev *EventProfileEnvelope) string {
if eh.app == nil {
log.Errorf("eh.app == nil in handleProfileEvent... this shouldnt happen?")
} else {
peer := eh.app.GetPeer(ev.Profile)
ph := NewPeerHelper(peer)
log.Debugf("New Profile Event to Handle: %v", ev)
switch ev.Event.EventType {
/* peer := eh.app.GetPeer(ev.Profile)
TODO: still handle this somewhere - network info from plugin Network check ph := NewPeerHelper(peer)
case event.NetworkStatus: log.Debugf("New Profile Event to Handle: %v", ev)
online, _ := peer.GetAttribute(attr.GetLocalScope(constants.PeerOnline)) switch ev.Event.EventType {
if e.Data[event.Status] == plugins.NetworkCheckSuccess && online == event.False {
peer.SetAttribute(attr.GetLocalScope(constants.PeerOnline), event.True)
uiManager.UpdateNetworkStatus(true)
// TODO we may have to reinitialize the peer
} else if e.Data[event.Status] == plugins.NetworkCheckError && online == event.True {
peer.SetAttribute(attr.GetLocalScope(constants.PeerOnline), event.False)
uiManager.UpdateNetworkStatus(false)
}*/
case event.NewMessageFromPeer: //event.TimestampReceived, event.RemotePeer, event.Data /*
// only needs contact nickname and picture, for displaying on popup notifications TODO: still handle this somewhere - network info from plugin Network check
ev.Event.Data["Nick"] = ph.GetNick(ev.Event.Data["RemotePeer"]) case event.NetworkStatus:
ev.Event.Data["Picture"] = ph.GetProfilePic(ev.Event.Data["RemotePeer"]) online, _ := peer.GetAttribute(attr.GetLocalScope(constants.PeerOnline))
case event.NewMessageFromGroup: if e.Data[event.Status] == plugins.NetworkCheckSuccess && online == event.False {
// only needs contact nickname and picture, for displaying on popup notifications peer.SetAttribute(attr.GetLocalScope(constants.PeerOnline), event.True)
ev.Event.Data["Nick"] = ph.GetNick(ev.Event.Data[event.GroupID]) uiManager.UpdateNetworkStatus(true)
ev.Event.Data["Picture"] = ph.GetProfilePic(ev.Event.Data[event.GroupID]) // TODO we may have to reinitialize the peer
case event.PeerAcknowledgement: } else if e.Data[event.Status] == plugins.NetworkCheckError && online == event.True {
// No enrichement required peer.SetAttribute(attr.GetLocalScope(constants.PeerOnline), event.False)
case event.PeerCreated: uiManager.UpdateNetworkStatus(false)
handle := ev.Event.Data[event.RemotePeer] }*/
err := EnrichNewPeer(handle, ph, ev)
if err != nil {
return ""
}
case event.GroupCreated:
// This event should only happen after we have validated the invite, as such the error
// condition *should* never happen.
groupPic := ph.GetProfilePic(ev.Event.Data[event.GroupID]) case event.NewMessageFromPeer: //event.TimestampReceived, event.RemotePeer, event.Data
// only needs contact nickname and picture, for displaying on popup notifications
ev.Event.Data["Nick"] = ph.GetNick(ev.Event.Data["RemotePeer"])
ev.Event.Data["Picture"] = ph.GetProfilePic(ev.Event.Data["RemotePeer"])
case event.NewMessageFromGroup:
// only needs contact nickname and picture, for displaying on popup notifications
ev.Event.Data["Nick"] = ph.GetNick(ev.Event.Data[event.GroupID])
ev.Event.Data["Picture"] = ph.GetProfilePic(ev.Event.Data[event.GroupID])
case event.PeerAcknowledgement:
// No enrichement required
case event.PeerCreated:
handle := ev.Event.Data[event.RemotePeer]
err := EnrichNewPeer(handle, ph, ev)
if err != nil {
return ""
}
case event.GroupCreated:
// This event should only happen after we have validated the invite, as such the error
// condition *should* never happen.
groupPic := ph.GetProfilePic(ev.Event.Data[event.GroupID])
ev.Event.Data["PicturePath"] = groupPic
ev.Event.Data["GroupName"] = ph.GetNick(ev.Event.Data[event.GroupID])
case event.NewGroup:
// This event should only happen after we have validated the invite, as such the error
// condition *should* never happen.
serializedInvite := ev.Event.Data[event.GroupInvite]
if invite, err := model.ValidateInvite(serializedInvite); err == nil {
groupPic := ph.GetProfilePic(invite.GroupID)
ev.Event.Data["PicturePath"] = groupPic ev.Event.Data["PicturePath"] = groupPic
ev.Event.Data["GroupName"] = ph.GetNick(ev.Event.Data[event.GroupID]) } else {
log.Errorf("received a new group event which contained an invalid invite %v. this should never happen and likely means there is a bug in cwtch. Please file a ticket @ https://git.openprivcy.ca/cwtch.im/cwtch", err)
return ""
}
case event.PeerStateChange:
cxnState := connections.ConnectionStateToType()[ev.Event.Data[event.ConnectionState]]
contact := peer.GetContact(ev.Event.Data[event.RemotePeer])
case event.NewGroup: if cxnState == connections.AUTHENTICATED && contact == nil {
// This event should only happen after we have validated the invite, as such the error peer.AddContact(ev.Event.Data[event.RemotePeer], ev.Event.Data[event.RemotePeer], model.AuthUnknown)
// condition *should* never happen. return ""
serializedInvite := ev.Event.Data[event.GroupInvite] }
if invite, err := model.ValidateInvite(serializedInvite); err == nil {
groupPic := ph.GetProfilePic(invite.GroupID) if contact != nil {
ev.Event.Data["PicturePath"] = groupPic // No enrichment needed
} else { //uiManager.UpdateContactStatus(contact.Onion, int(cxnState), false)
log.Errorf("received a new group event which contained an invalid invite %v. this should never happen and likely means there is a bug in cwtch. Please file a ticket @ https://git.openprivcy.ca/cwtch.im/cwtch", err) if cxnState == connections.AUTHENTICATED {
// if known and authed, get vars
peer.SendGetValToPeer(ev.Event.Data[event.RemotePeer], attr.PublicScope, constants.Name)
peer.SendGetValToPeer(ev.Event.Data[event.RemotePeer], attr.PublicScope, constants.Picture)
}
}
case event.NewRetValMessageFromPeer:
// auto handled event means the setting is already done, we're just deciding if we need to tell the UI
onion := ev.Event.Data[event.RemotePeer]
scope := ev.Event.Data[event.Scope]
path := ev.Event.Data[event.Path]
//val := ev.Event.Data[event.Data]
exists, _ := strconv.ParseBool(ev.Event.Data[event.Exists])
if exists && scope == attr.PublicScope {
if _, exists := peer.GetContactAttribute(onion, attr.GetLocalScope(path)); exists {
// we have a locally set ovverride, don't pass this remote set public scope update to UI
return "" return ""
} }
case event.PeerStateChange:
cxnState := connections.ConnectionStateToType()[ev.Event.Data[event.ConnectionState]]
contact := peer.GetContact(ev.Event.Data[event.RemotePeer])
if cxnState == connections.AUTHENTICATED && contact == nil {
peer.AddContact(ev.Event.Data[event.RemotePeer], ev.Event.Data[event.RemotePeer], model.AuthUnknown)
return ""
}
if contact != nil {
// No enrichment needed
//uiManager.UpdateContactStatus(contact.Onion, int(cxnState), false)
if cxnState == connections.AUTHENTICATED {
// if known and authed, get vars
peer.SendGetValToPeer(ev.Event.Data[event.RemotePeer], attr.PublicScope, constants.Name)
peer.SendGetValToPeer(ev.Event.Data[event.RemotePeer], attr.PublicScope, constants.Picture)
}
}
case event.NewRetValMessageFromPeer:
// auto handled event means the setting is already done, we're just deciding if we need to tell the UI
onion := ev.Event.Data[event.RemotePeer]
scope := ev.Event.Data[event.Scope]
path := ev.Event.Data[event.Path]
//val := ev.Event.Data[event.Data]
exists, _ := strconv.ParseBool(ev.Event.Data[event.Exists])
if exists && scope == attr.PublicScope {
if _, exists := peer.GetContactAttribute(onion, attr.GetLocalScope(path)); exists {
// we have a locally set ovverride, don't pass this remote set public scope update to UI
return ""
}
}
} }
} }
@ -356,19 +339,17 @@ func (eh *EventHandler) startHandlingPeer(onion string) {
eventBus.Subscribe(event.ChangePasswordError, q) eventBus.Subscribe(event.ChangePasswordError, q)
eventBus.Subscribe(event.NewRetValMessageFromPeer, q) eventBus.Subscribe(event.NewRetValMessageFromPeer, q)
eventBus.Subscribe(event.SetAttribute, q) eventBus.Subscribe(event.SetAttribute, q)
go eh.forwardProfileMessages(onion, q) go eh.forwardProfileMessages(onion, q)
} }
func (eh *EventHandler) forwardProfileMessages(onion string, q event.Queue) { func (eh *EventHandler) forwardProfileMessages(onion string, q event.Queue) {
log.Infof("Launching Forwarding Goroutine for %v", onion)
// TODO: graceful shutdown, via an injected event of special QUIT type exiting loop/go routine // TODO: graceful shutdown, via an injected event of special QUIT type exiting loop/go routine
for { for {
e := q.Next() e := q.Next()
ev := EventProfileEnvelope{Event: e, Profile: onion} ev := EventProfileEnvelope{Event: e, Profile: onion}
eh.profileEvents <- ev eh.profileEvents <- ev
if ev.Event.EventType == event.Shutdown { if e.EventType == event.Shutdown {
return return
} }
} }

View File

@ -286,7 +286,7 @@ func EnrichNewPeer(handle string, ph *PeerHelper, ev *EventProfileEnvelope) erro
} }
} else { } else {
// could be a server? // could be a server?
log.Debugf("sorry, unable to handle AddContact(%v)", handle) log.Infof("sorry, unable to handle AddContact(%v)", handle)
return errors.New("not a peer or group") return errors.New("not a peer or group")
} }
return nil return nil

View File

@ -31,8 +31,6 @@ type GlobalSettings struct {
BlockUnknownConnections bool BlockUnknownConnections bool
StateRootPane int StateRootPane int
FirstTime bool FirstTime bool
UIColumnModePortrait string
UIColumnModeLandscape string
} }
var DefaultGlobalSettings = GlobalSettings{ var DefaultGlobalSettings = GlobalSettings{
@ -44,8 +42,6 @@ var DefaultGlobalSettings = GlobalSettings{
StateRootPane: 0, StateRootPane: 0,
FirstTime: true, FirstTime: true,
BlockUnknownConnections: false, BlockUnknownConnections: false,
UIColumnModePortrait: "DualpaneMode.Single",
UIColumnModeLandscape: "DualpaneMode.CopyPortrait",
} }
func InitGlobalSettingsFile(directory string, password string) error { func InitGlobalSettingsFile(directory string, password string) error {