package extensions import ( "cwtch.im/cwtch/event" "cwtch.im/cwtch/model" "cwtch.im/cwtch/model/attr" "cwtch.im/cwtch/model/constants" "cwtch.im/cwtch/peer" "cwtch.im/cwtch/protocol/connections" "cwtch.im/cwtch/settings" "git.openprivacy.ca/openprivacy/log" "strconv" ) // ProfileValueExtension implements custom Profile Names over Cwtch type ProfileValueExtension struct { } func (pne ProfileValueExtension) NotifySettingsUpdate(_ settings.GlobalSettings) { } func (pne ProfileValueExtension) EventsToRegister() []event.Type { return []event.Type{event.PeerStateChange, event.Heartbeat} } func (pne ProfileValueExtension) ExperimentsToRegister() []string { return nil } func (pne ProfileValueExtension) requestProfileInfo(profile peer.CwtchPeer, ci *model.Conversation) { profile.SendScopedZonedGetValToContact(ci.ID, attr.PublicScope, attr.ProfileZone, constants.Name) profile.SendScopedZonedGetValToContact(ci.ID, attr.PublicScope, attr.ProfileZone, constants.ProfileStatus) profile.SendScopedZonedGetValToContact(ci.ID, attr.PublicScope, attr.ProfileZone, constants.ProfileAttribute1) profile.SendScopedZonedGetValToContact(ci.ID, attr.PublicScope, attr.ProfileZone, constants.ProfileAttribute2) profile.SendScopedZonedGetValToContact(ci.ID, attr.PublicScope, attr.ProfileZone, constants.ProfileAttribute3) } func (pne ProfileValueExtension) OnEvent(ev event.Event, profile peer.CwtchPeer) { switch ev.EventType { case event.Heartbeat: // once every heartbeat, loop through conversations and, if they are online, request an update to any long info.. conversations, err := profile.FetchConversations() if err == nil { for _, ci := range conversations { if profile.GetPeerState(ci.Handle) == connections.AUTHENTICATED { pne.requestProfileInfo(profile, ci) } } } case event.PeerStateChange: ci, err := profile.FetchConversationInfo(ev.Data["RemotePeer"]) if err == nil { // if we have re-authenticated with thie peer then request their profile image... if connections.ConnectionStateToType()[ev.Data[event.ConnectionState]] == connections.AUTHENTICATED { // Request some profile information... pne.requestProfileInfo(profile, ci) } } } } // OnContactReceiveValue for ProfileValueExtension handles saving specific Public Profile Values like Profile Name func (pne ProfileValueExtension) OnContactReceiveValue(profile peer.CwtchPeer, conversation model.Conversation, szp attr.ScopedZonedPath, value string, exists bool) { // Allow public profile parameters to be added as contact specific attributes... scope, zone, _ := szp.GetScopeZonePath() if exists && scope.IsPublic() && zone == attr.ProfileZone { // Check the current value of the attribute currentValue, err := profile.GetConversationAttribute(conversation.ID, szp) if err == nil && currentValue == value { // Value exists and the value is the same, short-circuit return } // Save the new Attribute err = profile.SetConversationAttribute(conversation.ID, szp, value) if err != nil { // Something else wen't wrong.. short-circuit log.Errorf("error setting conversation attribute %v", err) return } // Finally publish an update for listeners to react to. scope, zone, zpath := szp.GetScopeZonePath() profile.PublishEvent(event.NewEvent(event.UpdatedConversationAttribute, map[event.Field]string{ event.Scope: string(scope), event.Path: string(zone.ConstructZonedPath(zpath)), event.Data: value, event.RemotePeer: conversation.Handle, event.ConversationID: strconv.Itoa(conversation.ID), })) } } // OnContactRequestValue for ProfileValueExtension handles returning Public Profile Values func (pne ProfileValueExtension) OnContactRequestValue(profile peer.CwtchPeer, conversation model.Conversation, eventID string, szp attr.ScopedZonedPath) { scope, zone, zpath := szp.GetScopeZonePath() log.Debugf("Looking up public | conversation scope/zone %v", szp.ToString()) if scope.IsPublic() || scope.IsConversation() { val, exists := profile.GetScopedZonedAttribute(scope, zone, zpath) // NOTE: Temporary Override because UI currently wipes names if it can't find them... if !exists && zone == attr.UnknownZone && zpath == constants.Name { val, exists = profile.GetScopedZonedAttribute(attr.PublicScope, attr.ProfileZone, constants.Name) } // NOTE: Cwtch 1.15+ requires that profiles be able to restrict file downloading to specific contacts. As such we need an ACL check here // on the fileshareing zone. // TODO: Split this functionality into FilesharingFunctionality, and restrict this function to only considering Profile zoned attributes? if zone == attr.FilesharingZone { if !conversation.GetPeerAC().ShareFiles { return } } // Construct a Response resp := event.NewEvent(event.SendRetValMessageToPeer, map[event.Field]string{event.ConversationID: strconv.Itoa(conversation.ID), event.RemotePeer: conversation.Handle, event.Exists: strconv.FormatBool(exists)}) resp.EventID = eventID if exists { resp.Data[event.Data] = val } else { resp.Data[event.Data] = "" } log.Debugf("Responding with SendRetValMessageToPeer exists:%v data: %v\n", exists, val) profile.PublishEvent(resp) } }