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.
 
 
 
 

407 lines
13 KiB

  1. package v1
  2. import (
  3. "cwtch.im/cwtch/event"
  4. "cwtch.im/cwtch/model"
  5. "encoding/json"
  6. "git.openprivacy.ca/openprivacy/log"
  7. "io/ioutil"
  8. "os"
  9. "path"
  10. "time"
  11. )
  12. const groupIDLen = 32
  13. const peerIDLen = 56
  14. const profileFilename = "profile"
  15. const version = "1"
  16. const versionFile = "VERSION"
  17. const saltFile = "SALT"
  18. //ProfileStoreV1 storage for profiles and message streams that uses in memory key and fs stored salt instead of in memory password
  19. type ProfileStoreV1 struct {
  20. fs FileStore
  21. streamStores map[string]StreamStore // map [groupId|onion] StreamStore
  22. directory string
  23. profile *model.Profile
  24. key [32]byte
  25. salt [128]byte
  26. eventManager event.Manager
  27. queue event.Queue
  28. writer bool
  29. }
  30. func initV1Directory(directory, password string) ([32]byte, [128]byte, error) {
  31. key, salt, err := createKeySalt(password)
  32. if err != nil {
  33. log.Errorf("Could not create key for profile store from password: %v\n", err)
  34. return [32]byte{}, [128]byte{}, err
  35. }
  36. ioutil.WriteFile(path.Join(directory, versionFile), []byte(version), 0600)
  37. ioutil.WriteFile(path.Join(directory, saltFile), salt[:], 0600)
  38. return key, salt, nil
  39. }
  40. // CreateProfileWriterStore creates a profile store backed by a filestore listening for events and saving them
  41. // directory should be $appDir/profiles/$rand
  42. func CreateProfileWriterStore(eventManager event.Manager, directory, password string, profile *model.Profile) *ProfileStoreV1 {
  43. os.Mkdir(directory, 0700)
  44. key, salt, err := initV1Directory(directory, password)
  45. if err != nil {
  46. return nil
  47. }
  48. ps := &ProfileStoreV1{fs: NewFileStore(directory, profileFilename, key), key: key, salt: salt, directory: directory, profile: profile, eventManager: eventManager, streamStores: map[string]StreamStore{}, writer: true}
  49. ps.save()
  50. ps.initProfileWriterStore()
  51. return ps
  52. }
  53. func (ps *ProfileStoreV1) initProfileWriterStore() {
  54. ps.queue = event.NewQueue()
  55. go ps.eventHandler()
  56. ps.eventManager.Subscribe(event.BlockPeer, ps.queue)
  57. ps.eventManager.Subscribe(event.UnblockPeer, ps.queue)
  58. ps.eventManager.Subscribe(event.PeerCreated, ps.queue)
  59. ps.eventManager.Subscribe(event.GroupCreated, ps.queue)
  60. ps.eventManager.Subscribe(event.SetProfileName, ps.queue)
  61. ps.eventManager.Subscribe(event.SetAttribute, ps.queue)
  62. ps.eventManager.Subscribe(event.SetPeerAttribute, ps.queue)
  63. ps.eventManager.Subscribe(event.SetGroupAttribute, ps.queue)
  64. ps.eventManager.Subscribe(event.AcceptGroupInvite, ps.queue)
  65. ps.eventManager.Subscribe(event.NewGroupInvite, ps.queue)
  66. ps.eventManager.Subscribe(event.NewMessageFromGroup, ps.queue)
  67. ps.eventManager.Subscribe(event.PeerStateChange, ps.queue)
  68. ps.eventManager.Subscribe(event.ServerStateChange, ps.queue)
  69. ps.eventManager.Subscribe(event.DeleteContact, ps.queue)
  70. ps.eventManager.Subscribe(event.DeleteGroup, ps.queue)
  71. ps.eventManager.Subscribe(event.ChangePassword, ps.queue)
  72. }
  73. // LoadProfileWriterStore loads a profile store from filestore listening for events and saving them
  74. // directory should be $appDir/profiles/$rand
  75. func LoadProfileWriterStore(eventManager event.Manager, directory, password string) (*ProfileStoreV1, error) {
  76. salt, err := ioutil.ReadFile(path.Join(directory, saltFile))
  77. if err != nil {
  78. return nil, err
  79. }
  80. key := createKey(password, salt)
  81. ps := &ProfileStoreV1{fs: NewFileStore(directory, profileFilename, key), key: key, directory: directory, profile: nil, eventManager: eventManager, streamStores: map[string]StreamStore{}, writer: true}
  82. copy(ps.salt[:], salt)
  83. ps.initProfileWriterStore()
  84. ps.load()
  85. return ps, nil
  86. }
  87. // ReadProfile reads a profile from storqage and returns the profile
  88. // directory should be $appDir/profiles/$rand
  89. func ReadProfile(directory string, key [32]byte, salt [128]byte) (*model.Profile, error) {
  90. os.Mkdir(directory, 0700)
  91. ps := &ProfileStoreV1{fs: NewFileStore(directory, profileFilename, key), key: key, salt: salt, directory: directory, profile: nil, eventManager: nil, streamStores: map[string]StreamStore{}, writer: true}
  92. err := ps.load()
  93. if err != nil {
  94. return nil, err
  95. }
  96. profile := ps.GetProfileCopy(true)
  97. return profile, nil
  98. }
  99. // UpgradeV0Profile takes a profile (presumably from a V0 store) and creates and writes a V1 store
  100. func UpgradeV0Profile(profile *model.Profile, directory, password string) error {
  101. key, salt, err := initV1Directory(directory, password)
  102. if err != nil {
  103. return err
  104. }
  105. ps := &ProfileStoreV1{fs: NewFileStore(directory, profileFilename, key), key: key, salt: salt, directory: directory, profile: profile, eventManager: nil, streamStores: map[string]StreamStore{}, writer: true}
  106. ps.save()
  107. for gid, group := range ps.profile.Groups {
  108. ss := NewStreamStore(ps.directory, group.LocalID, ps.key)
  109. ss.WriteN(ps.profile.Groups[gid].Timeline.Messages)
  110. }
  111. return nil
  112. }
  113. // NewProfile creates a new profile for use in the profile store.
  114. func NewProfile(name string) *model.Profile {
  115. profile := model.GenerateNewProfile(name)
  116. return profile
  117. }
  118. // GetNewPeerMessage is for AppService to call on Reload events, to reseed the AppClient with the loaded peers
  119. func (ps *ProfileStoreV1) GetNewPeerMessage() *event.Event {
  120. message := event.NewEventList(event.NewPeer, event.Identity, ps.profile.LocalID, event.Key, string(ps.key[:]), event.Salt, string(ps.salt[:]))
  121. return &message
  122. }
  123. // GetStatusMessages creates an array of status messages for all peers and group servers from current information
  124. func (ps *ProfileStoreV1) GetStatusMessages() []*event.Event {
  125. messages := []*event.Event{}
  126. for _, contact := range ps.profile.Contacts {
  127. message := event.NewEvent(event.PeerStateChange, map[event.Field]string{
  128. event.RemotePeer: string(contact.Onion),
  129. event.ConnectionState: contact.State,
  130. })
  131. messages = append(messages, &message)
  132. }
  133. doneServers := make(map[string]bool)
  134. for _, group := range ps.profile.Groups {
  135. if _, exists := doneServers[group.GroupServer]; !exists {
  136. message := event.NewEvent(event.ServerStateChange, map[event.Field]string{
  137. event.GroupServer: string(group.GroupServer),
  138. event.ConnectionState: group.State,
  139. })
  140. messages = append(messages, &message)
  141. doneServers[group.GroupServer] = true
  142. }
  143. }
  144. return messages
  145. }
  146. // ChangePassword restores all data under a new password's encryption
  147. func (ps *ProfileStoreV1) ChangePassword(oldpass, newpass, eventID string) {
  148. oldkey := createKey(oldpass, ps.salt[:])
  149. if oldkey != ps.key {
  150. ps.eventManager.Publish(event.NewEventList(event.ChangePasswordError, event.Error, "Supplied current password does not match", event.EventID, eventID))
  151. return
  152. }
  153. newkey := createKey(newpass, ps.salt[:])
  154. newStreamStores := map[string]StreamStore{}
  155. idToNewLocalID := map[string]string{}
  156. // Generate all new StreamStores with the new password and write all the old StreamStore data into these ones
  157. for ssid, ss := range ps.streamStores {
  158. // New ss with new pass and new localID
  159. newlocalID := model.GenerateRandomID()
  160. idToNewLocalID[ssid] = newlocalID
  161. newSS := NewStreamStore(ps.directory, newlocalID, newkey)
  162. newStreamStores[ssid] = newSS
  163. // write whole store
  164. messages := ss.Read()
  165. newSS.WriteN(messages)
  166. }
  167. // Switch over
  168. oldStreamStores := ps.streamStores
  169. ps.streamStores = newStreamStores
  170. for ssid, newLocalID := range idToNewLocalID {
  171. if len(ssid) == groupIDLen {
  172. ps.profile.Groups[ssid].LocalID = newLocalID
  173. } else {
  174. ps.profile.Contacts[ssid].LocalID = newLocalID
  175. }
  176. }
  177. ps.key = newkey
  178. ps.fs.ChangeKey(newkey)
  179. ps.save()
  180. // Clean up
  181. for _, oldss := range oldStreamStores {
  182. oldss.Delete()
  183. }
  184. ps.eventManager.Publish(event.NewEventList(event.ChangePasswordSuccess, event.EventID, eventID))
  185. return
  186. }
  187. func (ps *ProfileStoreV1) save() error {
  188. if ps.writer {
  189. bytes, _ := json.Marshal(ps.profile)
  190. return ps.fs.Write(bytes)
  191. }
  192. return nil
  193. }
  194. // load instantiates a cwtchPeer from the file store
  195. func (ps *ProfileStoreV1) load() error {
  196. decrypted, err := ps.fs.Read()
  197. if err != nil {
  198. return err
  199. }
  200. cp := new(model.Profile)
  201. err = json.Unmarshal(decrypted, &cp)
  202. if err == nil {
  203. ps.profile = cp
  204. for gid, group := range cp.Groups {
  205. ss := NewStreamStore(ps.directory, group.LocalID, ps.key)
  206. cp.Groups[gid].Timeline.SetMessages(ss.Read())
  207. ps.streamStores[group.GroupID] = ss
  208. }
  209. }
  210. return err
  211. }
  212. // GetProfileCopy returns a copy of the stored profile
  213. func (ps *ProfileStoreV1) GetProfileCopy(timeline bool) *model.Profile {
  214. return ps.profile.GetCopy(timeline)
  215. }
  216. func (ps *ProfileStoreV1) eventHandler() {
  217. log.Debugln("eventHandler()!")
  218. for {
  219. ev := ps.queue.Next()
  220. log.Debugf("eventHandler event %v %v\n", ev.EventType, ev.EventID)
  221. switch ev.EventType {
  222. case event.BlockPeer:
  223. contact, exists := ps.profile.GetContact(ev.Data[event.RemotePeer])
  224. if exists {
  225. contact.Blocked = true
  226. ps.save()
  227. }
  228. case event.UnblockPeer:
  229. contact, exists := ps.profile.GetContact(ev.Data[event.RemotePeer])
  230. if exists {
  231. contact.Blocked = false
  232. ps.save()
  233. }
  234. case event.PeerCreated:
  235. var pp *model.PublicProfile
  236. json.Unmarshal([]byte(ev.Data[event.Data]), &pp)
  237. ps.profile.AddContact(ev.Data[event.RemotePeer], pp)
  238. // TODO: configure - allow peers to be configured to turn on limited storage
  239. /*ss := NewStreamStore(ps.directory, pp.LocalID, ps.password)
  240. pp.Timeline.SetMessages(ss.Read())
  241. ps.streamStores[pp.Onion] = ss
  242. ps.save()*/
  243. case event.GroupCreated:
  244. var group *model.Group
  245. json.Unmarshal([]byte(ev.Data[event.Data]), &group)
  246. ps.profile.AddGroup(group)
  247. ps.streamStores[group.GroupID] = NewStreamStore(ps.directory, group.LocalID, ps.key)
  248. ps.save()
  249. case event.SetProfileName:
  250. ps.profile.Name = ev.Data[event.ProfileName]
  251. ps.profile.SetAttribute("name", ev.Data[event.ProfileName])
  252. ps.save()
  253. case event.SetAttribute:
  254. ps.profile.SetAttribute(ev.Data[event.Key], ev.Data[event.Data])
  255. ps.save()
  256. case event.SetPeerAttribute:
  257. contact, exists := ps.profile.GetContact(ev.Data[event.RemotePeer])
  258. if exists {
  259. contact.SetAttribute(ev.Data[event.Key], ev.Data[event.Data])
  260. ps.save()
  261. } else {
  262. log.Errorf("error setting attribute on peer %v peer does not exist", ev)
  263. }
  264. case event.SetGroupAttribute:
  265. group := ps.profile.GetGroup(ev.Data[event.GroupID])
  266. if group != nil {
  267. group.SetAttribute(ev.Data[event.Key], ev.Data[event.Data])
  268. ps.save()
  269. } else {
  270. log.Errorf("error setting attribute on group %v group does not exist", ev)
  271. }
  272. case event.AcceptGroupInvite:
  273. err := ps.profile.AcceptInvite(ev.Data[event.GroupID])
  274. if err == nil {
  275. ps.save()
  276. } else {
  277. log.Errorf("error accepting group invite")
  278. }
  279. case event.NewGroupInvite:
  280. gid, err := ps.profile.ProcessInvite(ev.Data[event.GroupInvite], ev.Data[event.RemotePeer])
  281. log.Errorf("gid: %v err:%v\n", gid, err)
  282. if err == nil {
  283. ps.save()
  284. group := ps.profile.Groups[gid]
  285. ps.streamStores[group.GroupID] = NewStreamStore(ps.directory, group.LocalID, ps.key)
  286. } else {
  287. log.Errorf("error storing new group invite: %v (%v)", err, ev)
  288. }
  289. case event.NewMessageFromGroup:
  290. groupid := ev.Data[event.GroupID]
  291. received, _ := time.Parse(time.RFC3339Nano, ev.Data[event.TimestampReceived])
  292. sent, _ := time.Parse(time.RFC3339Nano, ev.Data[event.TimestampSent])
  293. message := model.Message{Received: received, Timestamp: sent, Message: ev.Data[event.Data], PeerID: ev.Data[event.RemotePeer], Signature: []byte(ev.Data[event.Signature]), PreviousMessageSig: []byte(ev.Data[event.PreviousSignature])}
  294. ss, exists := ps.streamStores[groupid]
  295. if exists {
  296. ss.Write(message)
  297. } else {
  298. log.Errorf("error storing new group message: %v stream store does not exist", ev)
  299. }
  300. case event.PeerStateChange:
  301. if _, exists := ps.profile.Contacts[ev.Data[event.RemotePeer]]; exists {
  302. ps.profile.Contacts[ev.Data[event.RemotePeer]].State = ev.Data[event.ConnectionState]
  303. }
  304. case event.ServerStateChange:
  305. for _, group := range ps.profile.Groups {
  306. if group.GroupServer == ev.Data[event.GroupServer] {
  307. group.State = ev.Data[event.ConnectionState]
  308. }
  309. }
  310. case event.DeleteContact:
  311. onion := ev.Data[event.RemotePeer]
  312. ps.profile.DeleteContact(onion)
  313. ps.save()
  314. case event.DeleteGroup:
  315. groupID := ev.Data[event.GroupID]
  316. ps.profile.DeleteGroup(groupID)
  317. ps.save()
  318. ss, exists := ps.streamStores[groupID]
  319. if exists {
  320. ss.Delete()
  321. delete(ps.streamStores, groupID)
  322. }
  323. case event.ChangePassword:
  324. oldpass := ev.Data[event.Password]
  325. newpass := ev.Data[event.NewPassword]
  326. ps.ChangePassword(oldpass, newpass, ev.EventID)
  327. default:
  328. return
  329. }
  330. }
  331. }
  332. // Shutdown shuts down the queue / thread
  333. func (ps *ProfileStoreV1) Shutdown() {
  334. if ps.queue != nil {
  335. ps.queue.Shutdown()
  336. }
  337. }
  338. // Delete removes all stored files for this stored profile
  339. func (ps *ProfileStoreV1) Delete() {
  340. log.Debugf("Delete ProfileStore for %v\n", ps.profile.Onion)
  341. for _, ss := range ps.streamStores {
  342. ss.Delete()
  343. }
  344. ps.fs.Delete()
  345. err := os.RemoveAll(ps.directory)
  346. if err != nil {
  347. log.Errorf("ProfileStore Delete error on RemoveAll on %v was %v\n", ps.directory, err)
  348. }
  349. }