1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
|
package client
import (
"sync"
"github.com/twstrike/otr3"
)
// ConversationBuilder represents an entity capable of building Conversations
type ConversationBuilder interface {
// NewConversation returns a new conversation to a peer
NewConversation(peer string) *otr3.Conversation
}
// Sender represents an entity capable of sending messages to peers
//TODO: this assumes there is no more than one simultaneous conversations with a given peer
type Sender interface {
// Send sends a message to a peer
Send(peer, resource, msg string) error
}
// ConversationManager represents an entity capable of managing Conversations
type ConversationManager interface {
// GetConversationWith returns the conversation for the given peer, and
// whether the Conversation exists
GetConversationWith(peer, resource string) (Conversation, bool)
// GetConversationWith returns the conversation for the given peer, and
// creates the conversation if none exists. Additionally, returns whether the
// conversation was created.
EnsureConversationWith(peer, resource string) (Conversation, bool)
// Conversations return all conversations currently managed
Conversations() map[string]Conversation
// TerminateAll terminates all existing conversations
TerminateAll()
}
type conversationManager struct {
// conversations maps from a bare JID to Conversation
conversations map[string]*conversation
sync.RWMutex
builder ConversationBuilder
sender Sender
}
// NewConversationManager returns a new ConversationManager
func NewConversationManager(builder ConversationBuilder, sender Sender) ConversationManager {
return &conversationManager{
conversations: make(map[string]*conversation),
builder: builder,
sender: sender,
}
}
func (m *conversationManager) GetConversationWith(peer, resource string) (Conversation, bool) {
m.RLock()
defer m.RUnlock()
c, ok := m.conversations[peer]
if ok && c.resource != "" && resource != "" && c.resource != resource {
return c, false
}
if ok {
c.resource = resource
}
return c, ok
}
func (m *conversationManager) Conversations() map[string]Conversation {
m.RLock()
defer m.RUnlock()
ret := make(map[string]Conversation)
for to, c := range m.conversations {
ret[to] = c
}
return ret
}
func (m *conversationManager) EnsureConversationWith(peer, resource string) (Conversation, bool) {
m.Lock()
defer m.Unlock()
c, ok := m.conversations[peer]
if ok && (c.resource == "" || resource == "" || c.resource == resource) {
c.resource = resource
return c, true
}
if ok {
m.terminateConversationWith(peer, c.resource)
}
c = &conversation{
to: peer,
resource: resource,
Conversation: m.builder.NewConversation(peer),
}
m.conversations[peer] = c
return c, true
}
func (m *conversationManager) TerminateAll() {
m.RLock()
defer m.RUnlock()
for peer := range m.conversations {
m.terminateConversationWith(peer, "")
}
}
func (m *conversationManager) terminateConversationWith(peer, resource string) error {
if c, ok := m.conversations[peer]; ok {
return c.EndEncryptedChat(m.sender, resource)
}
return nil
}
|