cwtch/protocol/connections/peerapp.go

101 lines
2.7 KiB
Go

package connections
import (
model2 "cwtch.im/cwtch/protocol/model"
"encoding/json"
"git.openprivacy.ca/cwtch.im/tapir"
"git.openprivacy.ca/cwtch.im/tapir/applications"
"git.openprivacy.ca/openprivacy/log"
)
const cwtchCapability = tapir.Capability("cwtchCapability")
// PeerApp encapsulates the behaviour of a Cwtch Peer
type PeerApp struct {
applications.AuthApp
connection tapir.Connection
MessageHandler func(string, string, string, []byte)
IsBlocked func(string) bool
IsAllowed func(string) bool
OnAcknowledgement func(string, string)
OnAuth func(string)
OnClose func(string)
OnConnecting func(string)
}
type peerGetVal struct {
Scope, Path string
}
type peerRetVal struct {
Val string
Exists bool
}
// NewInstance should always return a new instantiation of the application.
func (pa *PeerApp) NewInstance() tapir.Application {
newApp := new(PeerApp)
newApp.MessageHandler = pa.MessageHandler
newApp.IsBlocked = pa.IsBlocked
newApp.IsAllowed = pa.IsAllowed
newApp.OnAcknowledgement = pa.OnAcknowledgement
newApp.OnAuth = pa.OnAuth
newApp.OnClose = pa.OnClose
newApp.OnConnecting = pa.OnConnecting
return newApp
}
// Init is run when the connection is first started.
func (pa *PeerApp) Init(connection tapir.Connection) {
// First run the Authentication App
pa.AuthApp.Init(connection)
if connection.HasCapability(applications.AuthCapability) {
pa.connection = connection
connection.SetCapability(cwtchCapability)
if pa.IsBlocked(connection.Hostname()) {
pa.connection.Close()
pa.OnClose(connection.Hostname())
} else {
pa.OnAuth(connection.Hostname())
go pa.listen()
}
} else {
// The auth protocol wasn't completed, we can safely shutdown the connection
connection.Close()
}
}
func (pa *PeerApp) listen() {
for {
message := pa.connection.Expect()
if len(message) == 0 {
log.Debugf("0 byte read, socket has likely failed. Closing the listen goroutine")
pa.OnClose(pa.connection.Hostname())
return
}
var peerMessage model2.PeerMessage
err := json.Unmarshal(message, &peerMessage)
if err == nil {
if pa.IsAllowed(pa.connection.Hostname()) {
pa.MessageHandler(pa.connection.Hostname(), peerMessage.ID, peerMessage.Context, peerMessage.Data)
}
} else {
log.Errorf("Error unmarshalling PeerMessage package: %x %v", message, err)
}
}
}
// SendMessage sends the peer a preformatted message
// NOTE: This is a stub, we will likely want to extend this to better reflect the desired protocol
func (pa *PeerApp) SendMessage(message model2.PeerMessage) error {
serialized, err := json.Marshal(message)
if err == nil {
pa.connection.Send(serialized)
return nil
}
return err
}