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.

app.go 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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. type applicationCore struct {
  20. eventBuses map[string]event.Manager
  21. directory string
  22. mutex sync.Mutex
  23. }
  24. type application struct {
  25. applicationCore
  26. appletPeers
  27. appletACN
  28. appletPlugins
  29. storage map[string]storage.ProfileStore
  30. engines map[string]connections.Engine
  31. appBus event.Manager
  32. }
  33. // Application is a full cwtch peer application. It allows management, usage and storage of multiple peers
  34. type Application interface {
  35. LoadProfiles(password string)
  36. CreatePeer(name string, password string)
  37. AddPeerPlugin(onion string, pluginID plugins.PluginID)
  38. LaunchPeers()
  39. GetPrimaryBus() event.Manager
  40. GetEventBus(onion string) event.Manager
  41. ShutdownPeer(string)
  42. Shutdown()
  43. GetPeer(onion string) peer.CwtchPeer
  44. ListPeers() map[string]string
  45. }
  46. // LoadProfileFn is the function signature for a function in an app that loads a profile
  47. type LoadProfileFn func(profile *model.Profile, store storage.ProfileStore)
  48. func newAppCore(appDirectory string) *applicationCore {
  49. appCore := &applicationCore{eventBuses: make(map[string]event.Manager), directory: appDirectory}
  50. os.MkdirAll(path.Join(appCore.directory, "profiles"), 0700)
  51. return appCore
  52. }
  53. // NewApp creates a new app with some environment awareness and initializes a Tor Manager
  54. func NewApp(acn connectivity.ACN, appDirectory string) Application {
  55. log.Debugf("NewApp(%v)\n", appDirectory)
  56. app := &application{storage: make(map[string]storage.ProfileStore), engines: make(map[string]connections.Engine), applicationCore: *newAppCore(appDirectory), appBus: event.NewEventManager()}
  57. app.appletPeers.init()
  58. fn := func(progress int, status string) {
  59. progStr := strconv.Itoa(progress)
  60. app.appBus.Publish(event.NewEventList(event.ACNStatus, event.Progreess, progStr, event.Status, status))
  61. }
  62. app.appletACN.init(acn, fn)
  63. return app
  64. }
  65. // CreatePeer creates a new Peer with a given name and core required accessories (eventbus)
  66. func (ac *applicationCore) CreatePeer(name string, password string) (*model.Profile, error) {
  67. log.Debugf("CreatePeer(%v)\n", name)
  68. profile := storage.NewProfile(name)
  69. ac.mutex.Lock()
  70. defer ac.mutex.Unlock()
  71. _, exists := ac.eventBuses[profile.Onion]
  72. if exists {
  73. return nil, fmt.Errorf("Error: profile for onion %v already exists", profile.Onion)
  74. }
  75. eventBus := event.NewEventManager()
  76. ac.eventBuses[profile.Onion] = eventBus
  77. return profile, nil
  78. }
  79. // CreatePeer creates a new Peer with the given name and required accessories (eventbus, storage, protocol engine)
  80. func (app *application) CreatePeer(name string, password string) {
  81. profile, err := app.applicationCore.CreatePeer(name, password)
  82. if err != nil {
  83. app.appBus.Publish(event.NewEventList(event.PeerError, event.Error, err.Error()))
  84. return
  85. }
  86. profileStore := storage.NewProfileWriterStore(app.eventBuses[profile.Onion], path.Join(app.directory, "profiles", profile.LocalID), password, profile)
  87. app.storage[profile.Onion] = profileStore
  88. pc := app.storage[profile.Onion].GetProfileCopy(true)
  89. peer := peer.FromProfile(pc)
  90. peer.Init(app.eventBuses[profile.Onion])
  91. blockedPeers := profile.BlockedPeers()
  92. // TODO: Would be nice if ProtocolEngine did not need to explicitly be given the Private Key.
  93. identity := primitives.InitializeIdentity(profile.Name, &profile.Ed25519PrivateKey, &profile.Ed25519PublicKey)
  94. engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, app.acn, app.eventBuses[profile.Onion], blockedPeers)
  95. app.peers[profile.Onion] = peer
  96. app.engines[profile.Onion] = engine
  97. app.appBus.Publish(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: profile.Onion}))
  98. }
  99. func (app *application) AddPeerPlugin(onion string, pluginID plugins.PluginID) {
  100. app.AddPlugin(onion, pluginID, app.eventBuses[onion])
  101. }
  102. // LoadProfiles takes a password and attempts to load any profiles it can from storage with it and create Peers for them
  103. func (ac *applicationCore) LoadProfiles(password string, timeline bool, loadProfileFn LoadProfileFn) error {
  104. files, err := ioutil.ReadDir(path.Join(ac.directory, "profiles"))
  105. if err != nil {
  106. return fmt.Errorf("Error: cannot read profiles directory: %v", err)
  107. }
  108. for _, file := range files {
  109. eventBus := event.NewEventManager()
  110. profileStore := storage.NewProfileWriterStore(eventBus, path.Join(ac.directory, "profiles", file.Name()), password, nil)
  111. err = profileStore.Load()
  112. if err != nil {
  113. continue
  114. }
  115. profile := profileStore.GetProfileCopy(timeline)
  116. _, exists := ac.eventBuses[profile.Onion]
  117. if exists {
  118. profileStore.Shutdown()
  119. eventBus.Shutdown()
  120. log.Errorf("profile for onion %v already exists", profile.Onion)
  121. continue
  122. }
  123. ac.mutex.Lock()
  124. ac.eventBuses[profile.Onion] = eventBus
  125. ac.mutex.Unlock()
  126. loadProfileFn(profile, profileStore)
  127. }
  128. return nil
  129. }
  130. // LoadProfiles takes a password and attempts to load any profiles it can from storage with it and create Peers for them
  131. func (app *application) LoadProfiles(password string) {
  132. count := 0
  133. app.applicationCore.LoadProfiles(password, true, func(profile *model.Profile, profileStore storage.ProfileStore) {
  134. peer := peer.FromProfile(profile)
  135. peer.Init(app.eventBuses[profile.Onion])
  136. blockedPeers := profile.BlockedPeers()
  137. identity := primitives.InitializeIdentity(profile.Name, &profile.Ed25519PrivateKey, &profile.Ed25519PublicKey)
  138. engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, app.acn, app.eventBuses[profile.Onion], blockedPeers)
  139. app.mutex.Lock()
  140. app.peers[profile.Onion] = peer
  141. app.storage[profile.Onion] = profileStore
  142. app.engines[profile.Onion] = engine
  143. app.mutex.Unlock()
  144. app.appBus.Publish(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: profile.Onion}))
  145. count++
  146. })
  147. if count == 0 {
  148. message := event.NewEventList(event.AppError, event.Error, event.AppErrLoaded0)
  149. app.appBus.Publish(message)
  150. }
  151. }
  152. // GetPrimaryBus returns the bus the Application uses for events that aren't peer specific
  153. func (app *application) GetPrimaryBus() event.Manager {
  154. return app.appBus
  155. }
  156. // GetEventBus returns a cwtchPeer's event bus
  157. func (ac *applicationCore) GetEventBus(onion string) event.Manager {
  158. if manager, ok := ac.eventBuses[onion]; ok {
  159. return manager
  160. }
  161. return nil
  162. }
  163. // ShutdownPeer shuts down a peer and removes it from the app's management
  164. func (app *application) ShutdownPeer(onion string) {
  165. app.mutex.Lock()
  166. defer app.mutex.Unlock()
  167. app.eventBuses[onion].Shutdown()
  168. delete(app.eventBuses, onion)
  169. app.peers[onion].Shutdown()
  170. delete(app.peers, onion)
  171. app.engines[onion].Shutdown()
  172. delete(app.engines, onion)
  173. app.storage[onion].Shutdown()
  174. delete(app.storage, onion)
  175. }
  176. // Shutdown shutsdown all peers of an app and then the tormanager
  177. func (app *application) Shutdown() {
  178. for id, peer := range app.peers {
  179. peer.Shutdown()
  180. app.engines[id].Shutdown()
  181. app.storage[id].Shutdown()
  182. app.appletPlugins.Shutdown()
  183. app.eventBuses[id].Shutdown()
  184. }
  185. app.appBus.Shutdown()
  186. }