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.

main.go 7.8KB

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