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.
 
 
 
 
 
 

274 lines
8.0 KiB

  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/connectivity/tor"
  11. "git.openprivacy.ca/openprivacy/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. log.ExcludeFromPattern("service.go")
  59. log.ExcludeFromPattern("tor/BaseOnionService.go")
  60. log.ExcludeFromPattern("applications/auth.go")
  61. log.ExcludeFromPattern("connections/engine.go")
  62. if os.Getenv("CWTCH_FOLDER") != "" {
  63. the.CwtchDir = os.Getenv("CWTCH_FOLDER")
  64. } else if runtime.GOOS == "android" {
  65. the.CwtchDir = path.Join(androidBaseDir, "files")
  66. } else {
  67. usr, err := user.Current()
  68. if err != nil {
  69. log.Errorf("\nerror: could not load current user: %v\n", err)
  70. os.Exit(1)
  71. }
  72. the.CwtchDir = path.Join(usr.HomeDir, ".cwtch")
  73. }
  74. if buildVer == "" && os.Getenv("CWTCH_FOLDER") == "" {
  75. log.Infoln("Development build: using dev directory for dev profiles")
  76. the.CwtchDir = path.Join(the.CwtchDir, "dev")
  77. }
  78. the.ACN = nil
  79. the.Peer = nil
  80. the.IPCBridge = nil
  81. the.CwtchApp = nil
  82. the.CwtchService = nil
  83. os.MkdirAll(the.CwtchDir, 0700)
  84. if *flagService {
  85. mainService()
  86. } else {
  87. clientUI := *flagClientUI || runtime.GOOS == "android"
  88. mainUi(*flagLocal, clientUI)
  89. }
  90. if the.ACN != nil {
  91. the.ACN.Close()
  92. }
  93. }
  94. // QRunnable is a shim for QAndroidService and QGuiApplication
  95. type QRunnable interface {
  96. Exec() int
  97. }
  98. func mainService() {
  99. log.Infoln("I am the service")
  100. log.Infoln("Starting a cwtch app...")
  101. go loadNetworkingAndFiles(nil, true, false)
  102. var app QRunnable
  103. if runtime.GOOS == "android" {
  104. log.Infoln("Making QAndroidService...")
  105. app = androidextras.NewQAndroidService(len(os.Args), os.Args)
  106. } else {
  107. log.Infoln("Making QGuiApplication...")
  108. app = gui.NewQGuiApplication(len(os.Args), os.Args)
  109. }
  110. log.Infoln("Cwtch Service starting app.Exec")
  111. app.Exec()
  112. }
  113. func mainUi(flagLocal bool, flagClientUI bool) {
  114. log.Infof("I am the UI (client:%v)\n", flagClientUI)
  115. app := gui.NewQGuiApplication(len(os.Args), os.Args)
  116. // our globals
  117. err := ui.InitGlobalSettingsFile(path.Join(the.CwtchDir, "global"), the.AppPassword)
  118. if err != nil {
  119. log.Errorf("Could not access global ui config: %v\n", err)
  120. os.Exit(-1)
  121. }
  122. gcd := ui.NewGrandCentralDispatcher(nil)
  123. gcd.SetOs(runtime.GOOS)
  124. dir := core.QCoreApplication_ApplicationDirPath()
  125. log.Infof("core.QCoreApplication_ApplicationDirPath(): %v\n", dir)
  126. if runtime.GOOS == "android" {
  127. gcd.SetAssetPath("assets:/")
  128. } else {
  129. // all of these access are QML based, and QML takes URIs which use forward slashes and translates them to local OS sperators
  130. // also windows paths need to be like /c:/PATH
  131. if runtime.GOOS == "windows" {
  132. dir = "/" + dir
  133. }
  134. gcd.SetAssetPath("file://" + path.Join(dir, "assets") + "/")
  135. }
  136. if buildVer != "" {
  137. gcd.SetVersion(buildVer)
  138. gcd.SetBuildDate(buildDate)
  139. } else {
  140. gcd.SetVersion("development")
  141. gcd.SetBuildDate("now")
  142. }
  143. // this is to load local qml files quickly when developing
  144. var qmlSource *core.QUrl
  145. if flagLocal {
  146. qmlSource = core.QUrl_FromLocalFile("./qml/main.qml")
  147. } else {
  148. qmlSource = core.NewQUrl3("qrc:/qml/main.qml", 0)
  149. }
  150. app.SetWindowIcon(gui.NewQIcon5(":/qml/images/cwtch-icon.png"))
  151. // 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
  152. translator := core.NewQTranslator(nil)
  153. translator.Load("translation_en", ":/i18n/", "", "")
  154. core.QCoreApplication_InstallTranslator(translator)
  155. opaqueTranslator := core.NewQTranslator(nil)
  156. opaqueTranslator.Load("translation_en", ":/qml/opaque/i18n/", "", "")
  157. core.QCoreApplication_InstallTranslator(opaqueTranslator)
  158. core.QCoreApplication_SetAttribute(core.Qt__AA_EnableHighDpiScaling, true)
  159. quickcontrols2.QQuickStyle_SetStyle("Universe")
  160. engine := qml.NewQQmlApplicationEngine(nil)
  161. gcd.QMLEngine = engine
  162. gcd.SetLocale(gcd.GlobalSettings.Locale)
  163. // prevent qt from initiating network connections (possible deanon attempts!)
  164. factory := qml.NewQQmlNetworkAccessManagerFactory()
  165. factory.ConnectCreate(func(parent *core.QObject) *network.QNetworkAccessManager {
  166. nam := network.NewQNetworkAccessManager(parent)
  167. nam.SetNetworkAccessible(network.QNetworkAccessManager__NotAccessible)
  168. proxy := network.NewQNetworkProxy()
  169. proxy.SetHostName("0.0.0.0")
  170. nam.SetProxy(proxy)
  171. return nam
  172. })
  173. engine.SetNetworkAccessManagerFactory(factory)
  174. engine.RootContext().SetContextProperty("gcd", gcd)
  175. var androidCwtchActivity = android.NewCwtchActivity(nil)
  176. engine.RootContext().SetContextProperty("androidCwtchActivity", androidCwtchActivity)
  177. engine.Load(qmlSource)
  178. go loadNetworkingAndFiles(gcd, false, flagClientUI)
  179. log.Infoln("Cwtch App starting app.Exec")
  180. app.Exec()
  181. }
  182. func loadACN() {
  183. torpath := "tor"
  184. if runtime.GOOS == "android" {
  185. torpath = path.Join(androidBaseDir, "lib/libtor.so")
  186. } else if runtime.GOOS == "windows" {
  187. ex, err := os.Executable()
  188. if err != nil {
  189. ex = ""
  190. }
  191. exPath := filepath.Dir(ex)
  192. torpath = path.Join(exPath, "tor-0.3.5.7", "Tor", "tor.exe")
  193. } else {
  194. dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
  195. if _, err := os.Stat(path.Join(dir, "tor")); os.IsNotExist(err) {
  196. if _, err := os.Stat(path.Join(dir, "deploy", "linux", "tor")); os.IsNotExist(err) {
  197. log.Warnln("Cannot find bundled Tor")
  198. } else {
  199. torpath = path.Join(dir, "deploy", "linux", "tor")
  200. }
  201. } else {
  202. torpath = path.Join(dir, "tor")
  203. }
  204. }
  205. var err error
  206. the.ACN, err = tor.NewTorACN(the.CwtchDir, torpath)
  207. if err != nil {
  208. // TODO: turn into UI error: status panel?
  209. log.Errorf("Could not start Tor: %v", err)
  210. os.Exit(1)
  211. }
  212. }
  213. func loadNetworkingAndFiles(gcd *ui.GrandCentralDispatcher, service bool, clientUI bool) {
  214. if service || clientUI || runtime.GOOS == "android" {
  215. clientIn := path.Join(the.CwtchDir, "clientIn")
  216. serviceIn := path.Join(the.CwtchDir, "serviceIn")
  217. if service {
  218. loadACN()
  219. serviceBridge := bridge.NewPipeBridgeService(serviceIn, clientIn)
  220. log.Infoln("Creating New App Service")
  221. the.CwtchService = libapp.NewAppService(the.ACN, the.CwtchDir, serviceBridge)
  222. } else {
  223. clientBridge := bridge.NewPipeBridgeClient(clientIn, serviceIn)
  224. log.Infoln("Creating New App Client")
  225. the.CwtchApp = libapp.NewAppClient(the.CwtchDir, clientBridge)
  226. }
  227. } else {
  228. loadACN()
  229. log.Infoln("Creating New App")
  230. the.CwtchApp = libapp.NewApp(the.ACN, the.CwtchDir)
  231. }
  232. if !service {
  233. the.AppBus = the.CwtchApp.GetPrimaryBus()
  234. subscribed := make(chan bool)
  235. go handlers.App(gcd, subscribed, clientUI)
  236. <-subscribed
  237. }
  238. if !service && !clientUI {
  239. the.CwtchApp.LoadProfiles(the.AppPassword)
  240. }
  241. }