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

259 lines
7.5KB

  1. package main
  2. import (
  3. libapp "cwtch.im/cwtch/app"
  4. "cwtch.im/cwtch/event/bridge"
  5. "cwtch.im/ui/go/characters"
  6. "cwtch.im/ui/go/gobjects"
  7. "cwtch.im/ui/go/gothings"
  8. "cwtch.im/ui/go/gothings/android"
  9. "cwtch.im/ui/go/the"
  10. "flag"
  11. "git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
  12. "git.openprivacy.ca/openprivacy/libricochet-go/log"
  13. "github.com/therecipe/qt/androidextras"
  14. "github.com/therecipe/qt/core"
  15. "github.com/therecipe/qt/gui"
  16. "github.com/therecipe/qt/network"
  17. "github.com/therecipe/qt/qml"
  18. "github.com/therecipe/qt/quickcontrols2"
  19. "os"
  20. "os/user"
  21. "path"
  22. "path/filepath"
  23. "runtime"
  24. )
  25. const androidBaseDir = "/data/data/ca.openprivacy.cwtch.ui/"
  26. var (
  27. buildVer string
  28. buildDate string
  29. )
  30. func init() {
  31. // make go-defined types available in qml
  32. gothings.GrandCentralDispatcher_QmlRegisterType2("CustomQmlTypes", 1, 0, "GrandCentralDispatcher")
  33. }
  34. func main() {
  35. if runtime.GOOS == "windows" {
  36. filelogger, err := log.NewFile(log.LevelInfo, "cwtch_log.txt")
  37. if err == nil {
  38. log.SetStd(filelogger)
  39. }
  40. }
  41. log.Infoln("ui main()\n")
  42. flagDebug := flag.Bool("debug", false, "turn on extra logging. WARNING: THIS MAY EXPOSE PRIVATE INFORMATION IN CONSOLE OUTPUT!")
  43. flagLocal := flag.Bool("local", false, "load user interface from the local folder \"qml\" instead of the built-in UI")
  44. flagService := flag.Bool("service", false, "run this process as an android service")
  45. flagClientUI := flag.Bool("clientui", false, "start the UI as a client of a service app instead of a full app")
  46. flag.Parse()
  47. if *flagDebug {
  48. log.SetLevel(log.LevelDebug)
  49. } else {
  50. log.SetLevel(log.LevelInfo)
  51. }
  52. // TESTING
  53. //log.SetLevel(log.LevelDebug)
  54. //log.ExcludeFromPattern("connection/connection")
  55. //log.ExcludeFromPattern("outbound/3dhauthchannel")
  56. //log.AddNothingExceptFilter("event/eventmanager")
  57. if os.Getenv("CWTCH_FOLDER") != "" {
  58. the.CwtchDir = os.Getenv("CWTCH_FOLDER")
  59. } else if runtime.GOOS == "android" {
  60. the.CwtchDir = path.Join(androidBaseDir, "files")
  61. } else {
  62. usr, err := user.Current()
  63. if err != nil {
  64. log.Errorf("\nerror: could not load current user: %v\n", err)
  65. os.Exit(1)
  66. }
  67. the.CwtchDir = path.Join(usr.HomeDir, ".cwtch")
  68. }
  69. the.ACN = nil
  70. the.Peer = nil
  71. the.IPCBridge = nil
  72. the.CwtchApp = nil
  73. the.CwtchService = nil
  74. os.MkdirAll(the.CwtchDir, 0700)
  75. if *flagService {
  76. mainService()
  77. } else {
  78. mainUi(*flagLocal, *flagClientUI)
  79. }
  80. if the.ACN != nil {
  81. the.ACN.Close()
  82. }
  83. }
  84. // QRunnable is a shim for QAndroidService and QGuiApplication
  85. type QRunnable interface {
  86. Exec() int
  87. }
  88. func mainService() {
  89. log.Infoln("I am the service")
  90. log.Infoln("Starting a cwtch app...")
  91. go loadNetworkingAndFiles(nil, true, false)
  92. var app QRunnable
  93. if runtime.GOOS == "android" {
  94. log.Infoln("Making QAndroidService...")
  95. app = androidextras.NewQAndroidService(len(os.Args), os.Args)
  96. } else {
  97. log.Infoln("Making QGuiApplication...")
  98. app = gui.NewQGuiApplication(len(os.Args), os.Args)
  99. }
  100. log.Infoln("Cwtch Service starting app.Exec")
  101. app.Exec()
  102. }
  103. func mainUi(flagLocal bool, flagClientUI bool) {
  104. log.Infoln("I am the application")
  105. app := gui.NewQGuiApplication(len(os.Args), os.Args)
  106. // our globals
  107. gcd := gothings.NewGrandCentralDispatcher(nil)
  108. gcd.SetOs(runtime.GOOS)
  109. if buildVer != "" {
  110. gcd.SetVersion(buildVer)
  111. gcd.SetBuildDate(buildDate)
  112. } else {
  113. gcd.SetVersion("development")
  114. gcd.SetBuildDate("now")
  115. }
  116. gcd.UIState = gothings.NewUIState(gcd)
  117. gcd.OutgoingMessages = make(chan gobjects.Letter, 1000)
  118. //TODO: put theme stuff somewhere better
  119. gcd.SetThemeScale(1.0)
  120. // this is to load local qml files quickly when developing
  121. var qmlSource *core.QUrl
  122. if flagLocal {
  123. qmlSource = core.QUrl_FromLocalFile("./qml/main.qml")
  124. } else {
  125. qmlSource = core.NewQUrl3("qrc:/qml/main.qml", 0)
  126. }
  127. app.SetWindowIcon(gui.NewQIcon5(":/qml/images/cwtch-icon.png"))
  128. // load english first so it becomes the default in case we don't have a .ts for the user's locale, or if it contains unfinished strings
  129. translator := core.NewQTranslator(nil)
  130. translator.Load("translation_en", ":/i18n/", "", "")
  131. core.QCoreApplication_InstallTranslator(translator)
  132. gcd.Translator = core.NewQTranslator(nil)
  133. gcd.Translator.Load("translation_"+core.QLocale_System().Name(), ":/i18n/", "", "")
  134. core.QCoreApplication_InstallTranslator(gcd.Translator)
  135. core.QCoreApplication_SetAttribute(core.Qt__AA_EnableHighDpiScaling, true)
  136. quickcontrols2.QQuickStyle_SetStyle("Universe")
  137. engine := qml.NewQQmlApplicationEngine(nil)
  138. gcd.QMLEngine = engine
  139. // prevent qt from initiating network connections (possible deanon attempts!)
  140. factory := qml.NewQQmlNetworkAccessManagerFactory()
  141. factory.ConnectCreate(func(parent *core.QObject) *network.QNetworkAccessManager {
  142. nam := network.NewQNetworkAccessManager(parent)
  143. nam.SetNetworkAccessible(network.QNetworkAccessManager__NotAccessible)
  144. proxy := network.NewQNetworkProxy()
  145. proxy.SetHostName("0.0.0.0")
  146. nam.SetProxy(proxy)
  147. //nam.ConnectCreateRequest(func(op network.QNetworkAccessManager__Operation, originalReq *network.QNetworkRequest, outgoingData *core.QIODevice) *network.QNetworkReply {
  148. // log.Errorf("network access request detected - possible remote content insertion bug!!!")
  149. // return nil
  150. //})
  151. return nam
  152. })
  153. engine.SetNetworkAccessManagerFactory(factory)
  154. // variables we want to access from inside qml
  155. if runtime.GOOS == "android" {
  156. gcd.SetThemeScale(2.9)
  157. } else {
  158. gcd.SetThemeScale(1.0)
  159. }
  160. engine.RootContext().SetContextProperty("gcd", gcd)
  161. var androidCwtchActivity = android.NewCwtchActivity(nil)
  162. engine.RootContext().SetContextProperty("androidCwtchActivity", androidCwtchActivity)
  163. engine.Load(qmlSource)
  164. go loadNetworkingAndFiles(gcd, false, flagClientUI)
  165. log.Infoln("Cwtch App starting app.Exec")
  166. app.Exec()
  167. }
  168. func loadACN() {
  169. torpath := "tor"
  170. if runtime.GOOS == "android" {
  171. torpath = path.Join(androidBaseDir, "lib/libtor.so")
  172. } else if runtime.GOOS == "windows" {
  173. ex, err := os.Executable()
  174. if err != nil {
  175. ex = ""
  176. }
  177. exPath := filepath.Dir(ex)
  178. torpath = path.Join(exPath, "tor-0.3.5.7", "Tor", "tor.exe")
  179. } else {
  180. dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
  181. if _, err := os.Stat(path.Join(dir, "tor")); os.IsNotExist(err) {
  182. if _, err := os.Stat(path.Join(dir, "deploy", "linux", "tor")); os.IsNotExist(err) {
  183. log.Warnln("Cannot find bundled Tor")
  184. } else {
  185. torpath = path.Join(dir, "deploy", "linux", "tor")
  186. }
  187. } else {
  188. torpath = path.Join(dir, "tor")
  189. }
  190. }
  191. var err error
  192. the.ACN, err = connectivity.StartTor(the.CwtchDir, torpath)
  193. if err != nil {
  194. log.Errorf("Could not start Tor: %v", err)
  195. os.Exit(1)
  196. }
  197. }
  198. func loadNetworkingAndFiles(gcd *gothings.GrandCentralDispatcher, service bool, clientUI bool) {
  199. if service || clientUI || runtime.GOOS == "android" {
  200. clientIn := path.Join(the.CwtchDir, "clientIn")
  201. serviceIn := path.Join(the.CwtchDir, "serviceIn")
  202. if service {
  203. loadACN()
  204. serviceBridge := bridge.NewPipeBridgeService(serviceIn, clientIn)
  205. log.Infoln("Creating New App Service")
  206. the.CwtchService = libapp.NewAppService(the.ACN, the.CwtchDir, serviceBridge)
  207. } else {
  208. clientBridge := bridge.NewPipeBridgeClient(clientIn, serviceIn)
  209. log.Infoln("Creating New App Client")
  210. the.CwtchApp = libapp.NewAppClient(the.CwtchDir, clientBridge)
  211. }
  212. } else {
  213. loadACN()
  214. log.Infoln("Creating New App")
  215. the.CwtchApp = libapp.NewApp(the.ACN, the.CwtchDir)
  216. }
  217. if !service {
  218. the.AppBus = the.CwtchApp.GetPrimaryBus()
  219. subscribed := make(chan bool)
  220. go characters.AppEventListener(gcd, subscribed)
  221. <-subscribed
  222. }
  223. if !service && !clientUI {
  224. the.CwtchApp.LoadProfiles(the.AppPassword)
  225. }
  226. }