this is the main cwtch gui with the pretty interface
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.

221 lines
6.8KB

  1. package main
  2. import (
  3. libapp "cwtch.im/cwtch/app"
  4. "cwtch.im/ui/go/characters"
  5. "cwtch.im/ui/go/cwutil"
  6. "cwtch.im/ui/go/gobjects"
  7. "cwtch.im/ui/go/gothings"
  8. "cwtch.im/ui/go/the"
  9. "git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
  10. "git.openprivacy.ca/openprivacy/libricochet-go/log"
  11. "github.com/therecipe/qt/gui"
  12. "github.com/therecipe/qt/core"
  13. "github.com/therecipe/qt/network"
  14. "github.com/therecipe/qt/qml"
  15. "github.com/therecipe/qt/quickcontrols2"
  16. "os"
  17. "path"
  18. "runtime"
  19. "path/filepath"
  20. "os/user"
  21. "fmt"
  22. )
  23. const androidBaseDir = "/data/data/org.qtproject.example.go/"
  24. func init() {
  25. // make go-defined types available in qml
  26. gothings.GrandCentralDispatcher_QmlRegisterType2("CustomQmlTypes", 1, 0, "GrandCentralDispatcher")
  27. }
  28. func main() {
  29. if len(os.Args) >= 3 && os.Args[2] == "-debug" {
  30. log.SetLevel(log.LevelDebug)
  31. } else {
  32. log.SetLevel(log.LevelInfo)
  33. }
  34. // our globals
  35. gcd := gothings.NewGrandCentralDispatcher(nil)
  36. gcd.UIState = gothings.NewUIState(gcd)
  37. the.AcknowledgementIDs = make(map[string][]*the.AckId)
  38. gcd.OutgoingMessages = make(chan gobjects.Letter, 1000)
  39. //TODO: put theme stuff somewhere better
  40. gcd.SetThemeScale(1.0)
  41. // this is to load local qml files quickly when developing
  42. var qmlSource *core.QUrl
  43. if len(os.Args) >= 2 && os.Args[1] == "-local" {
  44. qmlSource = core.QUrl_FromLocalFile("./qml/main.qml")
  45. } else {
  46. qmlSource = core.NewQUrl3("qrc:/qml/main.qml", 0)
  47. }
  48. app := gui.NewQGuiApplication(len(os.Args), os.Args)
  49. app.SetWindowIcon(gui.NewQIcon5(":/qml/images/cwtch-icon.png"))
  50. var translator = core.NewQTranslator(nil)
  51. translator.Load("translation_"+core.QLocale_System().Name(), ":/i18n/", "", "")
  52. core.QCoreApplication_InstallTranslator(translator)
  53. core.QCoreApplication_SetAttribute(core.Qt__AA_EnableHighDpiScaling, true)
  54. quickcontrols2.QQuickStyle_SetStyle("Universe")
  55. engine := qml.NewQQmlApplicationEngine(nil)
  56. // variables we want to access from inside qml
  57. if runtime.GOOS == "android" {
  58. gcd.SetThemeScale(2.9)
  59. } else {
  60. gcd.SetThemeScale(1.0)
  61. }
  62. engine.RootContext().SetContextProperty("gcd", gcd)
  63. engine.Load(qmlSource)
  64. if os.Getenv("CWTCH_FOLDER") != "" {
  65. the.CwtchDir = os.Getenv("CWTCH_FOLDER")
  66. } else if runtime.GOOS == "android" {
  67. the.CwtchDir = path.Join(androidBaseDir, "files")
  68. } else {
  69. usr, err := user.Current()
  70. if err != nil {
  71. fmt.Printf("\nerror: could not load current user: %v\n", err)
  72. os.Exit(1)
  73. }
  74. the.CwtchDir = path.Join(usr.HomeDir, ".cwtch")
  75. }
  76. torpath := "tor"
  77. if runtime.GOOS == "android" {
  78. torpath = path.Join(androidBaseDir, "lib/libtor.so")
  79. } else {
  80. dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
  81. if _, err := os.Stat(path.Join(dir, "tor")); os.IsNotExist(err) {
  82. if _, err := os.Stat(path.Join(dir, "deploy", "linux", "tor")); os.IsNotExist(err) {
  83. if _, err := os.Stat(path.Join(dir, "deploy", "windows", "tor")); os.IsNotExist(err) {
  84. log.Warnln("Cannot find bundled Tor")
  85. } else {
  86. torpath = path.Join(dir, "deploy", "windows", "tor")
  87. }
  88. } else {
  89. torpath = path.Join(dir, "deploy", "linux", "tor")
  90. }
  91. } else {
  92. torpath = path.Join(dir, "tor")
  93. }
  94. }
  95. acn, err := connectivity.StartTor(the.CwtchDir, torpath)
  96. if err != nil {
  97. log.Errorf("Could not start Tor: %v", err)
  98. os.Exit(1)
  99. }
  100. // these are long-lived pollers/listeners for incoming messages and status changes
  101. loadCwtchData(gcd, acn)
  102. go characters.IncomingListener(gcd.UIState.AddMessage, gcd.UIState.AddSendMessageError)
  103. go characters.TorStatusPoller(gcd.TorStatus, acn)
  104. go characters.PresencePoller(gcd.UIState.GetContact, gcd.UIState.AddContact, gcd.UIState.UpdateContact)
  105. go characters.GroupPoller(gcd.UIState.GetContact, gcd.UIState.UpdateContact)
  106. // prevent qt from initiating network connections (possible deanon attempts!)
  107. factory := qml.NewQQmlNetworkAccessManagerFactory()
  108. factory.ConnectCreate(func(parent *core.QObject) *network.QNetworkAccessManager {
  109. nam := network.NewQNetworkAccessManager(parent)
  110. nam.SetNetworkAccessible(network.QNetworkAccessManager__NotAccessible)
  111. proxy := network.NewQNetworkProxy()
  112. proxy.SetHostName("0.0.0.0")
  113. nam.SetProxy(proxy)
  114. //nam.ConnectCreateRequest(func(op network.QNetworkAccessManager__Operation, originalReq *network.QNetworkRequest, outgoingData *core.QIODevice) *network.QNetworkReply {
  115. // log.Errorf("network access request detected - possible remote content insertion bug!!!")
  116. // return nil
  117. //})
  118. return nam
  119. })
  120. engine.SetNetworkAccessManagerFactory(factory)
  121. app.Exec()
  122. acn.Close()
  123. }
  124. // this is mostly going to get factored out when we add profile support
  125. // for now, it loads a single peer and fills the ui with its data
  126. func loadCwtchData(gcd *gothings.GrandCentralDispatcher, acn connectivity.ACN) {
  127. var err error
  128. /*_, err := app2.NewApp(dirname, "/data/data/org.qtproject.example.go/lib/libtor.so")
  129. if err != nil {
  130. log.Errorf("ERROR CREATING CWTCH APP: %v", err)
  131. }
  132. time.Sleep(time.Second * 10)
  133. */
  134. os.MkdirAll(the.CwtchDir, 0700)
  135. the.CwtchApp = libapp.NewApp(acn, the.CwtchDir)
  136. err = the.CwtchApp.LoadProfiles("be gay do crime")
  137. if err != nil {
  138. //TODO no more fatalfs
  139. log.Errorf("couldn't load profiles: %v", err)
  140. os.Exit(1)
  141. }
  142. if len(the.CwtchApp.ListPeers()) == 0 {
  143. log.Infoln("couldn't load your config file. attempting to create one now")
  144. the.Peer, err = the.CwtchApp.CreatePeer("alice", "be gay do crime")
  145. if err != nil {
  146. log.Errorf("couldn't create one. is your cwtch folder writable?")
  147. os.Exit(1)
  148. }
  149. } else {
  150. the.Peer = the.CwtchApp.PrimaryIdentity()
  151. }
  152. gcd.UpdateMyProfile(the.Peer.GetProfile().Name, the.Peer.GetProfile().Onion, cwutil.RandomProfileImage(the.Peer.GetProfile().Onion))
  153. the.CwtchApp.LaunchPeers()
  154. contacts := the.Peer.GetContacts()
  155. for i := range contacts {
  156. contact, _ := the.Peer.GetProfile().GetContact(contacts[i])
  157. displayName, _ := contact.GetAttribute("nick")
  158. deleted, _ := contact.GetAttribute("deleted")
  159. if deleted != "deleted" {
  160. gcd.UIState.AddContact(&gobjects.Contact{
  161. Handle: contacts[i],
  162. DisplayName: displayName,
  163. Image: cwutil.RandomProfileImage(contacts[i]),
  164. Trusted: contact.Trusted,
  165. })
  166. }
  167. }
  168. groups := the.Peer.GetGroups()
  169. for i := range groups {
  170. group := the.Peer.GetGroup(groups[i])
  171. nick, exists := group.GetAttribute("nick")
  172. if !exists {
  173. nick = group.GroupID[:12]
  174. }
  175. deleted,_ := group.GetAttribute("deleted")
  176. // Only join servers for active and explicitly accepted groups.
  177. if deleted != "deleted"{
  178. gcd.UIState.AddContact(&gobjects.Contact{
  179. Handle: group.GroupID,
  180. DisplayName: nick,
  181. Image: cwutil.RandomGroupImage(group.GroupID),
  182. Server: group.GroupServer,
  183. Trusted: group.Accepted,
  184. })
  185. }
  186. // Only send a join server packet if we haven't joined this server yet...
  187. _,connecting := the.Peer.GetServers()[group.GroupServer]
  188. if group.Accepted && !connecting {
  189. the.Peer.JoinServer(group.GroupServer)
  190. }
  191. }
  192. }