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 }