Official cwtch.im peer and server implementations. https://cwtch.im
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

peerapp.go 2.7KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. package connections
  2. import (
  3. "cwtch.im/cwtch/event"
  4. "cwtch.im/tapir"
  5. "cwtch.im/tapir/applications"
  6. "encoding/json"
  7. "git.openprivacy.ca/openprivacy/libricochet-go/log"
  8. )
  9. // PeerApp encapsulates the behaviour of a Cwtch Peer
  10. type PeerApp struct {
  11. applications.AuthApp
  12. connection tapir.Connection
  13. MessageHandler func(string, string, []byte)
  14. IsBlocked func(string) bool
  15. OnAcknowledgement func(string, string)
  16. OnAuth func(string)
  17. OnClose func(string)
  18. OnConnecting func(string)
  19. }
  20. // PeerMessage is an encapsulation that can be used by higher level applications
  21. type PeerMessage struct {
  22. ID string // A unique Message ID (primarily used for acknowledgments)
  23. Context string // A unique context identifier i.e. im.cwtch.chat
  24. Data []byte // The serialized data packet.
  25. }
  26. // NewInstance should always return a new instantiation of the application.
  27. func (pa PeerApp) NewInstance() tapir.Application {
  28. newApp := new(PeerApp)
  29. newApp.MessageHandler = pa.MessageHandler
  30. newApp.IsBlocked = pa.IsBlocked
  31. newApp.OnAcknowledgement = pa.OnAcknowledgement
  32. newApp.OnAuth = pa.OnAuth
  33. newApp.OnClose = pa.OnClose
  34. newApp.OnConnecting = pa.OnConnecting
  35. return newApp
  36. }
  37. // Init is run when the connection is first started.
  38. func (pa *PeerApp) Init(connection tapir.Connection) {
  39. // First run the Authentication App
  40. pa.AuthApp.Init(connection)
  41. if connection.HasCapability(applications.AuthCapability) {
  42. pa.connection = connection
  43. if pa.IsBlocked(connection.Hostname()) {
  44. pa.connection.Close()
  45. pa.OnClose(connection.Hostname())
  46. } else {
  47. pa.OnAuth(connection.Hostname())
  48. go pa.listen()
  49. }
  50. } else {
  51. pa.OnClose(connection.Hostname())
  52. }
  53. }
  54. func (pa PeerApp) listen() {
  55. for {
  56. message := pa.connection.Expect()
  57. if len(message) == 0 {
  58. log.Errorf("0 byte read, socket has likely failed. Closing the listen goroutine")
  59. pa.OnClose(pa.connection.Hostname())
  60. return
  61. }
  62. var peerMessage PeerMessage
  63. err := json.Unmarshal(message, &peerMessage)
  64. if err == nil {
  65. if peerMessage.Context == event.ContextAck {
  66. pa.OnAcknowledgement(pa.connection.Hostname(), peerMessage.ID)
  67. } else {
  68. pa.MessageHandler(pa.connection.Hostname(), peerMessage.Context, peerMessage.Data)
  69. // Acknowledge the message
  70. // TODO Should this be in the ui?
  71. pa.SendMessage(PeerMessage{peerMessage.ID, event.ContextAck, []byte{}})
  72. }
  73. } else {
  74. log.Errorf("Error unmarshalling PeerMessage package: %x %v", message, err)
  75. }
  76. }
  77. }
  78. // SendMessage sends the peer a preformatted message
  79. // NOTE: This is a stub, we will likely want to extend this to better reflect the desired protocol
  80. func (pa PeerApp) SendMessage(message PeerMessage) {
  81. serialized, _ := json.Marshal(message)
  82. pa.connection.Send(serialized)
  83. }