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.
 
 
 
 

288 lines
9.1 KiB

  1. package app
  2. import (
  3. "cwtch.im/cwtch/app/plugins"
  4. "cwtch.im/cwtch/event"
  5. "cwtch.im/cwtch/model"
  6. "cwtch.im/cwtch/peer"
  7. "cwtch.im/cwtch/protocol/connections"
  8. "cwtch.im/cwtch/storage"
  9. "cwtch.im/tapir/primitives"
  10. "fmt"
  11. "git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
  12. "git.openprivacy.ca/openprivacy/libricochet-go/log"
  13. "io/ioutil"
  14. "os"
  15. "path"
  16. "strconv"
  17. "sync"
  18. )
  19. // AttributeTag is a const name for a peer attribute that can be set at creation time, for example for versioning info
  20. const AttributeTag = "tag"
  21. type applicationCore struct {
  22. eventBuses map[string]event.Manager
  23. directory string
  24. coremutex sync.Mutex
  25. }
  26. type application struct {
  27. applicationCore
  28. appletPeers
  29. appletACN
  30. appletPlugins
  31. storage map[string]storage.ProfileStore
  32. engines map[string]connections.Engine
  33. appBus event.Manager
  34. appmutex sync.Mutex
  35. }
  36. // Application is a full cwtch peer application. It allows management, usage and storage of multiple peers
  37. type Application interface {
  38. LoadProfiles(password string)
  39. CreatePeer(name string, password string)
  40. CreateTaggedPeer(name string, password string, tag string)
  41. DeletePeer(onion string)
  42. AddPeerPlugin(onion string, pluginID plugins.PluginID)
  43. ChangePeerPassword(onion, oldpass, newpass string)
  44. LaunchPeers()
  45. GetPrimaryBus() event.Manager
  46. GetEventBus(onion string) event.Manager
  47. QueryACNStatus()
  48. ShutdownPeer(string)
  49. Shutdown()
  50. GetPeer(onion string) peer.CwtchPeer
  51. ListPeers() map[string]string
  52. }
  53. // LoadProfileFn is the function signature for a function in an app that loads a profile
  54. type LoadProfileFn func(profile *model.Profile, store storage.ProfileStore)
  55. func newAppCore(appDirectory string) *applicationCore {
  56. appCore := &applicationCore{eventBuses: make(map[string]event.Manager), directory: appDirectory}
  57. os.MkdirAll(path.Join(appCore.directory, "profiles"), 0700)
  58. return appCore
  59. }
  60. // NewApp creates a new app with some environment awareness and initializes a Tor Manager
  61. func NewApp(acn connectivity.ACN, appDirectory string) Application {
  62. log.Debugf("NewApp(%v)\n", appDirectory)
  63. app := &application{storage: make(map[string]storage.ProfileStore), engines: make(map[string]connections.Engine), applicationCore: *newAppCore(appDirectory), appBus: event.NewEventManager()}
  64. app.appletPeers.init()
  65. app.appletACN.init(acn, app.getACNStatusHandler())
  66. return app
  67. }
  68. // CreatePeer creates a new Peer with a given name and core required accessories (eventbus)
  69. func (ac *applicationCore) CreatePeer(name string) (*model.Profile, error) {
  70. log.Debugf("CreatePeer(%v)\n", name)
  71. profile := storage.NewProfile(name)
  72. ac.coremutex.Lock()
  73. defer ac.coremutex.Unlock()
  74. _, exists := ac.eventBuses[profile.Onion]
  75. if exists {
  76. return nil, fmt.Errorf("Error: profile for onion %v already exists", profile.Onion)
  77. }
  78. eventBus := event.NewEventManager()
  79. ac.eventBuses[profile.Onion] = eventBus
  80. return profile, nil
  81. }
  82. func (ac *applicationCore) DeletePeer(onion string) {
  83. ac.coremutex.Lock()
  84. defer ac.coremutex.Unlock()
  85. ac.eventBuses[onion].Shutdown()
  86. delete(ac.eventBuses, onion)
  87. }
  88. func (app *application) CreateTaggedPeer(name string, password string, tag string) {
  89. profile, err := app.applicationCore.CreatePeer(name)
  90. if err != nil {
  91. app.appBus.Publish(event.NewEventList(event.PeerError, event.Error, err.Error()))
  92. return
  93. }
  94. profileStore := storage.NewProfileWriterStore(app.eventBuses[profile.Onion], path.Join(app.directory, "profiles", profile.LocalID), password, profile)
  95. app.storage[profile.Onion] = profileStore
  96. pc := app.storage[profile.Onion].GetProfileCopy(true)
  97. peer := peer.FromProfile(pc)
  98. peer.Init(app.eventBuses[profile.Onion])
  99. blockedPeers := profile.BlockedPeers()
  100. // TODO: Would be nice if ProtocolEngine did not need to explicitly be given the Private Key.
  101. identity := primitives.InitializeIdentity(profile.Name, &profile.Ed25519PrivateKey, &profile.Ed25519PublicKey)
  102. engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, app.acn, app.eventBuses[profile.Onion], profile.GetContacts(), blockedPeers)
  103. app.peers[profile.Onion] = peer
  104. app.engines[profile.Onion] = engine
  105. if tag != "" {
  106. peer.SetAttribute(AttributeTag, tag)
  107. }
  108. app.appBus.Publish(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: profile.Onion}))
  109. }
  110. // CreatePeer creates a new Peer with the given name and required accessories (eventbus, storage, protocol engine)
  111. func (app *application) CreatePeer(name string, password string) {
  112. app.CreateTaggedPeer(name, password, "")
  113. }
  114. func (app *application) DeletePeer(onion string) {
  115. log.Infof("DeletePeer called on %v\n", onion)
  116. app.appmutex.Lock()
  117. defer app.appmutex.Unlock()
  118. app.appletPlugins.ShutdownPeer(onion)
  119. app.plugins.Delete(onion)
  120. app.peers[onion].Shutdown()
  121. delete(app.peers, onion)
  122. app.engines[onion].Shutdown()
  123. delete(app.engines, onion)
  124. app.storage[onion].Shutdown()
  125. app.storage[onion].Delete()
  126. delete(app.storage, onion)
  127. app.eventBuses[onion].Publish(event.NewEventList(event.ShutdownPeer, event.Identity, onion))
  128. app.applicationCore.DeletePeer(onion)
  129. log.Debugf("Delete peer for %v Done\n", onion)
  130. }
  131. func (app *application) ChangePeerPassword(onion, oldpass, newpass string) {
  132. app.eventBuses[onion].Publish(event.NewEventList(event.ChangePassword, event.Password, oldpass, event.NewPassword, newpass))
  133. }
  134. func (app *application) AddPeerPlugin(onion string, pluginID plugins.PluginID) {
  135. app.AddPlugin(onion, pluginID, app.eventBuses[onion], app.acn)
  136. }
  137. // LoadProfiles takes a password and attempts to load any profiles it can from storage with it and create Peers for them
  138. func (ac *applicationCore) LoadProfiles(password string, timeline bool, loadProfileFn LoadProfileFn) error {
  139. files, err := ioutil.ReadDir(path.Join(ac.directory, "profiles"))
  140. if err != nil {
  141. return fmt.Errorf("Error: cannot read profiles directory: %v", err)
  142. }
  143. for _, file := range files {
  144. eventBus := event.NewEventManager()
  145. profileStore := storage.NewProfileWriterStore(eventBus, path.Join(ac.directory, "profiles", file.Name()), password, nil)
  146. err = profileStore.Load()
  147. if err != nil {
  148. continue
  149. }
  150. profile := profileStore.GetProfileCopy(timeline)
  151. _, exists := ac.eventBuses[profile.Onion]
  152. if exists {
  153. profileStore.Shutdown()
  154. eventBus.Shutdown()
  155. log.Errorf("profile for onion %v already exists", profile.Onion)
  156. continue
  157. }
  158. ac.coremutex.Lock()
  159. ac.eventBuses[profile.Onion] = eventBus
  160. ac.coremutex.Unlock()
  161. loadProfileFn(profile, profileStore)
  162. }
  163. return nil
  164. }
  165. // LoadProfiles takes a password and attempts to load any profiles it can from storage with it and create Peers for them
  166. func (app *application) LoadProfiles(password string) {
  167. count := 0
  168. app.applicationCore.LoadProfiles(password, true, func(profile *model.Profile, profileStore storage.ProfileStore) {
  169. peer := peer.FromProfile(profile)
  170. peer.Init(app.eventBuses[profile.Onion])
  171. blockedPeers := profile.BlockedPeers()
  172. identity := primitives.InitializeIdentity(profile.Name, &profile.Ed25519PrivateKey, &profile.Ed25519PublicKey)
  173. engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, app.acn, app.eventBuses[profile.Onion], profile.GetContacts(), blockedPeers)
  174. app.appmutex.Lock()
  175. app.peers[profile.Onion] = peer
  176. app.storage[profile.Onion] = profileStore
  177. app.engines[profile.Onion] = engine
  178. app.appmutex.Unlock()
  179. app.appBus.Publish(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: profile.Onion}))
  180. count++
  181. })
  182. if count == 0 {
  183. message := event.NewEventList(event.AppError, event.Error, event.AppErrLoaded0)
  184. app.appBus.Publish(message)
  185. }
  186. }
  187. // GetPrimaryBus returns the bus the Application uses for events that aren't peer specific
  188. func (app *application) GetPrimaryBus() event.Manager {
  189. return app.appBus
  190. }
  191. // GetEventBus returns a cwtchPeer's event bus
  192. func (ac *applicationCore) GetEventBus(onion string) event.Manager {
  193. if manager, ok := ac.eventBuses[onion]; ok {
  194. return manager
  195. }
  196. return nil
  197. }
  198. func (app *application) getACNStatusHandler() func(int, string) {
  199. return func(progress int, status string) {
  200. progStr := strconv.Itoa(progress)
  201. app.appBus.Publish(event.NewEventList(event.ACNStatus, event.Progreess, progStr, event.Status, status))
  202. for _, bus := range app.eventBuses {
  203. bus.Publish(event.NewEventList(event.ACNStatus, event.Progreess, progStr, event.Status, status))
  204. }
  205. }
  206. }
  207. func (app *application) QueryACNStatus() {
  208. prog, status := app.acn.GetBootstrapStatus()
  209. app.getACNStatusHandler()(prog, status)
  210. }
  211. // ShutdownPeer shuts down a peer and removes it from the app's management
  212. func (app *application) ShutdownPeer(onion string) {
  213. app.appmutex.Lock()
  214. defer app.appmutex.Unlock()
  215. app.eventBuses[onion].Shutdown()
  216. delete(app.eventBuses, onion)
  217. app.peers[onion].Shutdown()
  218. delete(app.peers, onion)
  219. app.engines[onion].Shutdown()
  220. delete(app.engines, onion)
  221. app.storage[onion].Shutdown()
  222. delete(app.storage, onion)
  223. app.appletPlugins.Shutdown()
  224. }
  225. // Shutdown shutsdown all peers of an app and then the tormanager
  226. func (app *application) Shutdown() {
  227. for id, peer := range app.peers {
  228. peer.Shutdown()
  229. app.appletPlugins.ShutdownPeer(id)
  230. app.engines[id].Shutdown()
  231. app.storage[id].Shutdown()
  232. app.eventBuses[id].Shutdown()
  233. }
  234. app.appBus.Shutdown()
  235. }