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.

561 lines
18KB

  1. package peer
  2. import (
  3. "cwtch.im/cwtch/event"
  4. "cwtch.im/cwtch/model"
  5. "cwtch.im/cwtch/protocol/connections"
  6. "encoding/base32"
  7. "encoding/base64"
  8. "encoding/json"
  9. "errors"
  10. "git.openprivacy.ca/openprivacy/libricochet-go/log"
  11. "strings"
  12. "sync"
  13. "time"
  14. )
  15. var autoHandleableEvents = map[event.Type]bool{event.EncryptedGroupMessage: true, event.PeerStateChange: true,
  16. event.ServerStateChange: true, event.NewGroupInvite: true, event.NewMessageFromPeer: true,
  17. event.PeerAcknowledgement: true, event.PeerError: true, event.SendMessageToGroupError: true}
  18. // cwtchPeer manages incoming and outgoing connections and all processing for a Cwtch cwtchPeer
  19. type cwtchPeer struct {
  20. Profile *model.Profile
  21. mutex sync.Mutex
  22. shutdown bool
  23. queue event.Queue
  24. eventBus event.Manager
  25. }
  26. // CwtchPeer provides us with a way of testing systems built on top of cwtch without having to
  27. // directly implement a cwtchPeer.
  28. type CwtchPeer interface {
  29. Init(event.Manager)
  30. AutoHandleEvents(events []event.Type)
  31. PeerWithOnion(string)
  32. InviteOnionToGroup(string, string) error
  33. SendMessageToPeer(string, string) string
  34. TrustPeer(string) error
  35. BlockPeer(string) error
  36. UnblockPeer(string) error
  37. ProcessInvite(string, string) (string, error)
  38. AcceptInvite(string) error
  39. RejectInvite(string)
  40. DeleteContact(string)
  41. DeleteGroup(string)
  42. JoinServer(string)
  43. SendMessageToGroup(string, string) error
  44. SendMessageToGroupTracked(string, string) (string, error)
  45. GetName() string
  46. SetName(string)
  47. GetOnion() string
  48. GetPeerState(string) (connections.ConnectionState, bool)
  49. StartGroup(string) (string, []byte, error)
  50. ImportGroup(string) error
  51. ExportGroup(string) (string, error)
  52. GetGroup(string) *model.Group
  53. GetGroupState(string) (connections.ConnectionState, bool)
  54. GetGroups() []string
  55. AddContact(nick, onion string, trusted bool)
  56. GetContacts() []string
  57. GetContact(string) *model.PublicProfile
  58. SetAttribute(string, string)
  59. GetAttribute(string) (string, bool)
  60. SetContactAttribute(string, string, string)
  61. GetContactAttribute(string, string) (string, bool)
  62. SetGroupAttribute(string, string, string)
  63. GetGroupAttribute(string, string) (string, bool)
  64. Listen()
  65. StartPeersConnections()
  66. StartGroupConnections()
  67. Shutdown()
  68. }
  69. // NewCwtchPeer creates and returns a new cwtchPeer with the given name.
  70. func NewCwtchPeer(name string) CwtchPeer {
  71. cp := new(cwtchPeer)
  72. cp.Profile = model.GenerateNewProfile(name)
  73. cp.shutdown = false
  74. return cp
  75. }
  76. // FromProfile generates a new peer from a profile.
  77. func FromProfile(profile *model.Profile) CwtchPeer {
  78. cp := new(cwtchPeer)
  79. cp.Profile = profile
  80. cp.shutdown = false
  81. return cp
  82. }
  83. // Init instantiates a cwtchPeer
  84. func (cp *cwtchPeer) Init(eventBus event.Manager) {
  85. cp.queue = event.NewQueue()
  86. go cp.eventHandler()
  87. cp.eventBus = eventBus
  88. cp.AutoHandleEvents([]event.Type{event.EncryptedGroupMessage, event.NewMessageFromPeer, event.PeerAcknowledgement, event.PeerError, event.SendMessageToGroupError})
  89. }
  90. // AutoHandleEvents sets an event (if able) to be handled by this peer
  91. func (cp *cwtchPeer) AutoHandleEvents(events []event.Type) {
  92. for _, ev := range events {
  93. if _, exists := autoHandleableEvents[ev]; exists {
  94. cp.eventBus.Subscribe(ev, cp.queue)
  95. } else {
  96. log.Errorf("Peer asked to autohandle event it cannot: %v\n", ev)
  97. }
  98. }
  99. }
  100. // ImportGroup intializes a group from an imported source rather than a peer invite
  101. func (cp *cwtchPeer) ImportGroup(exportedInvite string) (err error) {
  102. if strings.HasPrefix(exportedInvite, "torv3") {
  103. data, err := base64.StdEncoding.DecodeString(exportedInvite[5:])
  104. if err == nil {
  105. cp.eventBus.Publish(event.NewEvent(event.NewGroupInvite, map[event.Field]string{
  106. event.GroupInvite: string(data),
  107. }))
  108. } else {
  109. log.Errorf("error decoding group invite: %v", err)
  110. }
  111. return nil
  112. }
  113. return errors.New("unsupported exported group type")
  114. }
  115. // ExportGroup serializes a group invite so it can be given offline
  116. func (cp *cwtchPeer) ExportGroup(groupID string) (string, error) {
  117. cp.mutex.Lock()
  118. defer cp.mutex.Unlock()
  119. group := cp.Profile.GetGroup(groupID)
  120. if group != nil {
  121. invite, err := group.Invite(group.GetInitialMessage())
  122. if err == nil {
  123. exportedInvite := "torv3" + base64.StdEncoding.EncodeToString(invite)
  124. return exportedInvite, err
  125. }
  126. }
  127. return "", errors.New("group id could not be found")
  128. }
  129. // StartGroup create a new group linked to the given server and returns the group ID, an invite or an error.
  130. func (cp *cwtchPeer) StartGroup(server string) (string, []byte, error) {
  131. return cp.StartGroupWithMessage(server, []byte{})
  132. }
  133. // StartGroupWithMessage create a new group linked to the given server and returns the group ID, an invite or an error.
  134. func (cp *cwtchPeer) StartGroupWithMessage(server string, initialMessage []byte) (groupID string, invite []byte, err error) {
  135. cp.mutex.Lock()
  136. groupID, invite, err = cp.Profile.StartGroupWithMessage(server, initialMessage)
  137. cp.mutex.Unlock()
  138. if err == nil {
  139. group := cp.GetGroup(groupID)
  140. jsobj, err := json.Marshal(group)
  141. if err == nil {
  142. cp.eventBus.Publish(event.NewEvent(event.GroupCreated, map[event.Field]string{
  143. event.Data: string(jsobj),
  144. }))
  145. }
  146. } else {
  147. log.Errorf("error creating group: %v", err)
  148. }
  149. return
  150. }
  151. // GetGroups returns an unordered list of all group IDs.
  152. func (cp *cwtchPeer) GetGroups() []string {
  153. cp.mutex.Lock()
  154. defer cp.mutex.Unlock()
  155. return cp.Profile.GetGroups()
  156. }
  157. // GetGroup returns a pointer to a specific group, nil if no group exists.
  158. func (cp *cwtchPeer) GetGroup(groupID string) *model.Group {
  159. cp.mutex.Lock()
  160. defer cp.mutex.Unlock()
  161. return cp.Profile.GetGroup(groupID)
  162. }
  163. func (cp *cwtchPeer) AddContact(nick, onion string, trusted bool) {
  164. decodedPub, _ := base32.StdEncoding.DecodeString(strings.ToUpper(onion))
  165. pp := &model.PublicProfile{Name: nick, Ed25519PublicKey: decodedPub, Trusted: trusted, Blocked: false, Onion: onion, Attributes: map[string]string{"nick": nick}}
  166. cp.mutex.Lock()
  167. cp.Profile.AddContact(onion, pp)
  168. cp.mutex.Unlock()
  169. pd, _ := json.Marshal(pp)
  170. cp.eventBus.Publish(event.NewEvent(event.PeerCreated, map[event.Field]string{
  171. event.Data: string(pd),
  172. event.RemotePeer: onion,
  173. }))
  174. }
  175. // GetContacts returns an unordered list of onions
  176. func (cp *cwtchPeer) GetContacts() []string {
  177. cp.mutex.Lock()
  178. defer cp.mutex.Unlock()
  179. return cp.Profile.GetContacts()
  180. }
  181. // GetContact returns a given contact, nil is no such contact exists
  182. func (cp *cwtchPeer) GetContact(onion string) *model.PublicProfile {
  183. cp.mutex.Lock()
  184. defer cp.mutex.Unlock()
  185. contact, _ := cp.Profile.GetContact(onion)
  186. return contact
  187. }
  188. func (cp *cwtchPeer) GetName() string {
  189. cp.mutex.Lock()
  190. defer cp.mutex.Unlock()
  191. return cp.Profile.Name
  192. }
  193. func (cp *cwtchPeer) SetName(newName string) {
  194. cp.mutex.Lock()
  195. defer cp.mutex.Unlock()
  196. cp.Profile.Name = newName
  197. }
  198. func (cp *cwtchPeer) GetOnion() string {
  199. cp.mutex.Lock()
  200. defer cp.mutex.Unlock()
  201. return cp.Profile.Onion
  202. }
  203. func (cp *cwtchPeer) GetPeerState(onion string) (connections.ConnectionState, bool) {
  204. cp.mutex.Lock()
  205. defer cp.mutex.Unlock()
  206. if peer, ok := cp.Profile.Contacts[onion]; ok {
  207. return connections.ConnectionStateToType[peer.State], true
  208. }
  209. return connections.DISCONNECTED, false
  210. }
  211. func (cp *cwtchPeer) GetGroupState(groupid string) (connections.ConnectionState, bool) {
  212. cp.mutex.Lock()
  213. defer cp.mutex.Unlock()
  214. if group, ok := cp.Profile.Groups[groupid]; ok {
  215. return connections.ConnectionStateToType[group.State], true
  216. }
  217. return connections.DISCONNECTED, false
  218. }
  219. // PeerWithOnion is the entry point for cwtchPeer relationships
  220. func (cp *cwtchPeer) PeerWithOnion(onion string) {
  221. cp.mutex.Lock()
  222. if _, exists := cp.Profile.GetContact(onion); !exists {
  223. cp.AddContact(onion, onion, false)
  224. }
  225. defer cp.mutex.Unlock()
  226. cp.eventBus.Publish(event.NewEvent(event.PeerRequest, map[event.Field]string{event.RemotePeer: onion}))
  227. }
  228. // DeleteContact deletes a peer from the profile, storage, and handling
  229. func (cp *cwtchPeer) DeleteContact(onion string) {
  230. cp.mutex.Lock()
  231. cp.Profile.DeleteContact(onion)
  232. defer cp.mutex.Unlock()
  233. cp.eventBus.Publish(event.NewEventList(event.DeleteContact, event.RemotePeer, onion))
  234. }
  235. // DeleteGroup deletes a Group from the profile, storage, and handling
  236. func (cp *cwtchPeer) DeleteGroup(groupID string) {
  237. cp.mutex.Lock()
  238. cp.Profile.DeleteGroup(groupID)
  239. defer cp.mutex.Unlock()
  240. cp.eventBus.Publish(event.NewEventList(event.DeleteGroup, event.GroupID, groupID))
  241. }
  242. // InviteOnionToGroup kicks off the invite process
  243. func (cp *cwtchPeer) InviteOnionToGroup(onion string, groupid string) error {
  244. cp.mutex.Lock()
  245. group := cp.Profile.GetGroup(groupid)
  246. defer cp.mutex.Unlock()
  247. if group == nil {
  248. return errors.New("invalid group id")
  249. }
  250. invite, err := group.Invite(group.InitialMessage)
  251. if err == nil {
  252. cp.eventBus.Publish(event.NewEvent(event.InvitePeerToGroup, map[event.Field]string{event.RemotePeer: onion, event.GroupInvite: string(invite)}))
  253. }
  254. return err
  255. }
  256. // JoinServer manages a new server connection with the given onion address
  257. func (cp *cwtchPeer) JoinServer(onion string) {
  258. cp.eventBus.Publish(event.NewEvent(event.JoinServer, map[event.Field]string{event.GroupServer: onion}))
  259. }
  260. // SendMessageToGroup attempts to sent the given message to the given group id.
  261. // TODO: Deprecate in favour of SendMessageToGroupTracked
  262. func (cp *cwtchPeer) SendMessageToGroup(groupid string, message string) error {
  263. _, err := cp.SendMessageToGroupTracked(groupid, message)
  264. return err
  265. }
  266. // SendMessageToGroup attempts to sent the given message to the given group id.
  267. // It returns the signature of the message which can be used to identify it in any UX layer.
  268. func (cp *cwtchPeer) SendMessageToGroupTracked(groupid string, message string) (string, error) {
  269. cp.mutex.Lock()
  270. group := cp.Profile.GetGroup(groupid)
  271. defer cp.mutex.Unlock()
  272. if group == nil {
  273. return "", errors.New("invalid group id")
  274. }
  275. ct, sig, err := cp.Profile.EncryptMessageToGroup(message, groupid)
  276. if err == nil {
  277. cp.eventBus.Publish(event.NewEvent(event.SendMessageToGroup, map[event.Field]string{event.GroupServer: group.GroupServer, event.Ciphertext: string(ct), event.Signature: string(sig)}))
  278. }
  279. return string(sig), err
  280. }
  281. func (cp *cwtchPeer) SendMessageToPeer(onion string, message string) string {
  282. event := event.NewEvent(event.SendMessageToPeer, map[event.Field]string{event.RemotePeer: onion, event.Data: message})
  283. cp.eventBus.Publish(event)
  284. cp.mutex.Lock()
  285. cp.Profile.AddSentMessageToContactTimeline(onion, message, time.Now(), event.EventID)
  286. cp.mutex.Unlock()
  287. return event.EventID
  288. }
  289. // TrustPeer sets an existing peer relationship to trusted
  290. func (cp *cwtchPeer) TrustPeer(peer string) error {
  291. cp.mutex.Lock()
  292. defer cp.mutex.Unlock()
  293. err := cp.Profile.TrustPeer(peer)
  294. if err == nil {
  295. cp.PeerWithOnion(peer)
  296. }
  297. return err
  298. }
  299. // BlockPeer blocks an existing peer relationship.
  300. func (cp *cwtchPeer) BlockPeer(peer string) error {
  301. cp.mutex.Lock()
  302. err := cp.Profile.BlockPeer(peer)
  303. cp.mutex.Unlock()
  304. cp.eventBus.Publish(event.NewEvent(event.BlockPeer, map[event.Field]string{event.RemotePeer: peer}))
  305. return err
  306. }
  307. // UnblockPeer blocks an existing peer relationship.
  308. func (cp *cwtchPeer) UnblockPeer(peer string) error {
  309. cp.mutex.Lock()
  310. err := cp.Profile.UnblockPeer(peer)
  311. cp.mutex.Unlock()
  312. cp.eventBus.Publish(event.NewEvent(event.UnblockPeer, map[event.Field]string{event.RemotePeer: peer}))
  313. return err
  314. }
  315. // ProcessInvite adds a new group invite to the profile. returns the new group ID
  316. func (cp *cwtchPeer) ProcessInvite(invite string, remotePeer string) (string, error) {
  317. cp.mutex.Lock()
  318. defer cp.mutex.Unlock()
  319. return cp.Profile.ProcessInvite(invite, remotePeer)
  320. }
  321. // AcceptInvite accepts a given existing group invite
  322. func (cp *cwtchPeer) AcceptInvite(groupID string) error {
  323. cp.mutex.Lock()
  324. err := cp.Profile.AcceptInvite(groupID)
  325. cp.mutex.Unlock()
  326. if err != nil {
  327. return err
  328. }
  329. cp.eventBus.Publish(event.NewEvent(event.AcceptGroupInvite, map[event.Field]string{event.GroupID: groupID}))
  330. cp.JoinServer(cp.Profile.Groups[groupID].GroupServer)
  331. return nil
  332. }
  333. // RejectInvite rejects a given group invite.
  334. func (cp *cwtchPeer) RejectInvite(groupID string) {
  335. cp.mutex.Lock()
  336. defer cp.mutex.Unlock()
  337. cp.Profile.RejectInvite(groupID)
  338. }
  339. // Listen makes the peer open a listening port to accept incoming connections (and be detactably online)
  340. func (cp *cwtchPeer) Listen() {
  341. log.Debugf("cwtchPeer Listen sending ProtocolEngineStartListen\n")
  342. cp.eventBus.Publish(event.NewEvent(event.ProtocolEngineStartListen, map[event.Field]string{event.Onion: cp.Profile.Onion}))
  343. }
  344. // StartGroupConnections attempts to connect to all group servers (thus initiating reconnect attempts in the conectionsmanager)
  345. func (cp *cwtchPeer) StartPeersConnections() {
  346. for _, contact := range cp.GetContacts() {
  347. cp.PeerWithOnion(contact)
  348. }
  349. }
  350. // StartPeerConnections attempts to connect to all peers (thus initiating reconnect attempts in the conectionsmanager)
  351. func (cp *cwtchPeer) StartGroupConnections() {
  352. joinedServers := map[string]bool{}
  353. for _, groupID := range cp.GetGroups() {
  354. // Only send a join server packet if we haven't joined this server yet...
  355. group := cp.GetGroup(groupID)
  356. cp.mutex.Lock()
  357. if joined := joinedServers[groupID]; group.Accepted && !joined {
  358. log.Infof("Join Server %v (%v)\n", group.GroupServer, joined)
  359. cp.JoinServer(group.GroupServer)
  360. joinedServers[group.GroupServer] = true
  361. }
  362. cp.mutex.Unlock()
  363. }
  364. }
  365. // SetAttribute sets an attribute for this profile and emits an event
  366. func (cp *cwtchPeer) SetAttribute(key string, val string) {
  367. cp.mutex.Lock()
  368. cp.Profile.SetAttribute(key, val)
  369. defer cp.mutex.Unlock()
  370. cp.eventBus.Publish(event.NewEvent(event.SetAttribute, map[event.Field]string{
  371. event.Key: key,
  372. event.Data: val,
  373. }))
  374. }
  375. // GetAttribute gets an attribute for the profile
  376. func (cp *cwtchPeer) GetAttribute(key string) (string, bool) {
  377. cp.mutex.Lock()
  378. defer cp.mutex.Unlock()
  379. return cp.Profile.GetAttribute(key)
  380. }
  381. // SetContactAttribute sets an attribute for the indicated contact and emits an event
  382. func (cp *cwtchPeer) SetContactAttribute(onion string, key string, val string) {
  383. cp.mutex.Lock()
  384. defer cp.mutex.Unlock()
  385. if contact, ok := cp.Profile.GetContact(onion); ok {
  386. contact.SetAttribute(key, val)
  387. cp.eventBus.Publish(event.NewEvent(event.SetPeerAttribute, map[event.Field]string{
  388. event.RemotePeer: onion,
  389. event.Key: key,
  390. event.Data: val,
  391. }))
  392. }
  393. }
  394. // GetContactAttribute gets an attribute for the indicated contact
  395. func (cp *cwtchPeer) GetContactAttribute(onion string, key string) (string, bool) {
  396. cp.mutex.Lock()
  397. defer cp.mutex.Unlock()
  398. if contact, ok := cp.Profile.GetContact(onion); ok {
  399. return contact.GetAttribute(key)
  400. }
  401. return "", false
  402. }
  403. // SetGroupAttribute sets an attribute for the indicated group and emits an event
  404. func (cp *cwtchPeer) SetGroupAttribute(gid string, key string, val string) {
  405. cp.mutex.Lock()
  406. defer cp.mutex.Unlock()
  407. if group := cp.Profile.GetGroup(gid); group != nil {
  408. group.SetAttribute(key, val)
  409. cp.eventBus.Publish(event.NewEvent(event.SetGroupAttribute, map[event.Field]string{
  410. event.GroupID: gid,
  411. event.Key: key,
  412. event.Data: val,
  413. }))
  414. }
  415. }
  416. // GetGroupAttribute gets an attribute for the indicated group
  417. func (cp *cwtchPeer) GetGroupAttribute(gid string, key string) (string, bool) {
  418. cp.mutex.Lock()
  419. defer cp.mutex.Unlock()
  420. if group := cp.Profile.GetGroup(gid); group != nil {
  421. return group.GetAttribute(key)
  422. }
  423. return "", false
  424. }
  425. // Shutdown kills all connections and cleans up all goroutines for the peer
  426. func (cp *cwtchPeer) Shutdown() {
  427. cp.mutex.Lock()
  428. defer cp.mutex.Unlock()
  429. cp.shutdown = true
  430. cp.queue.Shutdown()
  431. }
  432. // eventHandler process events from other subsystems
  433. func (cp *cwtchPeer) eventHandler() {
  434. for {
  435. ev := cp.queue.Next()
  436. switch ev.EventType {
  437. /***** Default auto handled events *****/
  438. case event.EncryptedGroupMessage:
  439. // If successful, a side effect is the message is added to the group's timeline
  440. cp.mutex.Lock()
  441. ok, groupID, message, seen := cp.Profile.AttemptDecryption([]byte(ev.Data[event.Ciphertext]), []byte(ev.Data[event.Signature]))
  442. cp.mutex.Unlock()
  443. if ok && !seen {
  444. cp.eventBus.Publish(event.NewEvent(event.NewMessageFromGroup, map[event.Field]string{event.TimestampReceived: message.Received.Format(time.RFC3339Nano), event.TimestampSent: message.Timestamp.Format(time.RFC3339Nano), event.Data: message.Message, event.GroupID: groupID, event.Signature: string(message.Signature), event.PreviousSignature: string(message.PreviousMessageSig), event.RemotePeer: message.PeerID}))
  445. }
  446. case event.NewMessageFromPeer: //event.TimestampReceived, event.RemotePeer, event.Data
  447. ts, _ := time.Parse(time.RFC3339Nano, ev.Data[event.TimestampReceived])
  448. cp.mutex.Lock()
  449. cp.Profile.AddMessageToContactTimeline(ev.Data[event.RemotePeer], ev.Data[event.Data], ts)
  450. cp.mutex.Unlock()
  451. case event.PeerAcknowledgement:
  452. cp.mutex.Lock()
  453. cp.Profile.AckSentMessageToPeer(ev.Data[event.RemotePeer], ev.Data[event.EventID])
  454. cp.mutex.Unlock()
  455. case event.SendMessageToGroupError:
  456. cp.mutex.Lock()
  457. cp.Profile.AddGroupSentMessageError(ev.Data[event.GroupServer], ev.Data[event.Signature], ev.Data[event.Error])
  458. cp.mutex.Unlock()
  459. case event.SendMessageToPeerError:
  460. cp.mutex.Lock()
  461. cp.Profile.ErrorSentMessageToPeer(ev.Data[event.RemotePeer], ev.Data[event.EventID], ev.Data[event.Error])
  462. cp.mutex.Unlock()
  463. /***** Non default but requestable handlable events *****/
  464. case event.NewGroupInvite:
  465. cp.mutex.Lock()
  466. cp.Profile.ProcessInvite(ev.Data[event.GroupInvite], ev.Data[event.RemotePeer])
  467. cp.mutex.Unlock()
  468. case event.PeerStateChange:
  469. cp.mutex.Lock()
  470. if _, exists := cp.Profile.Contacts[ev.Data[event.RemotePeer]]; exists {
  471. cp.Profile.Contacts[ev.Data[event.RemotePeer]].State = ev.Data[event.ConnectionState]
  472. }
  473. cp.mutex.Unlock()
  474. case event.ServerStateChange:
  475. cp.mutex.Lock()
  476. for _, group := range cp.Profile.Groups {
  477. if group.GroupServer == ev.Data[event.GroupServer] {
  478. group.State = ev.Data[event.ConnectionState]
  479. }
  480. }
  481. cp.mutex.Unlock()
  482. default:
  483. if ev.EventType != "" {
  484. log.Errorf("peer event handler received an event it was not subscribed for: %v", ev.EventType)
  485. }
  486. return
  487. }
  488. }
  489. }