package connections import ( "cwtch.im/cwtch/event" "cwtch.im/tapir" "cwtch.im/tapir/applications" "encoding/json" "git.openprivacy.ca/openprivacy/libricochet-go/log" ) // PeerApp encapsulates the behaviour of a Cwtch Peer type PeerApp struct { applications.AuthApp connection tapir.Connection MessageHandler func(string, string, []byte) IsBlocked func(string) bool OnAcknowledgement func(string, string) OnAuth func(string) OnClose func(string) OnConnecting func(string) } // PeerMessage is an encapsulation that can be used by higher level applications type PeerMessage struct { ID string // A unique Message ID (primarily used for acknowledgments) Context string // A unique context identifier i.e. im.cwtch.chat Data []byte // The serialized data packet. } // 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.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 if pa.IsBlocked(connection.Hostname()) { pa.connection.Close() pa.OnClose(connection.Hostname()) } else { pa.OnAuth(connection.Hostname()) go pa.listen() } } else { pa.OnClose(connection.Hostname()) } } func (pa PeerApp) listen() { for { message := pa.connection.Expect() if len(message) == 0 { log.Errorf("0 byte read, socket has likely failed. Closing the listen goroutine") pa.OnClose(pa.connection.Hostname()) return } var peerMessage PeerMessage err := json.Unmarshal(message, &peerMessage) if err == nil { if peerMessage.Context == event.ContextAck { pa.OnAcknowledgement(pa.connection.Hostname(), peerMessage.ID) } else { pa.MessageHandler(pa.connection.Hostname(), peerMessage.Context, peerMessage.Data) // Acknowledge the message // TODO Should this be in the ui? pa.SendMessage(PeerMessage{peerMessage.ID, event.ContextAck, []byte{}}) } } 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 PeerMessage) { serialized, _ := json.Marshal(message) pa.connection.Send(serialized) }