this is the main cwtch gui with the pretty interface https://cwtch.im
Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

368 linhas
12 KiB

  1. package main
  2. import (
  3. "crypto/rand"
  4. libapp "cwtch.im/cwtch/app"
  5. "cwtch.im/cwtch/event"
  6. "cwtch.im/cwtch/event/bridge"
  7. "cwtch.im/cwtch/peer"
  8. "cwtch.im/ui/go/features/servers"
  9. "cwtch.im/ui/go/handlers"
  10. os2 "cwtch.im/ui/go/os"
  11. "cwtch.im/ui/go/the"
  12. "cwtch.im/ui/go/ui"
  13. "encoding/base64"
  14. "flag"
  15. "git.openprivacy.ca/openprivacy/connectivity/tor"
  16. "git.openprivacy.ca/openprivacy/log"
  17. "github.com/therecipe/qt/androidextras"
  18. "github.com/therecipe/qt/core"
  19. "github.com/therecipe/qt/gui"
  20. "github.com/therecipe/qt/network"
  21. "github.com/therecipe/qt/qml"
  22. "github.com/therecipe/qt/quickcontrols2"
  23. mrand "math/rand"
  24. "os"
  25. "os/user"
  26. "path"
  27. "path/filepath"
  28. "runtime"
  29. "time"
  30. )
  31. const androidBaseDir = "/data/data/ca.openprivacy.cwtch.ui/"
  32. var (
  33. buildVer string
  34. buildDate string
  35. )
  36. func init() {
  37. // make go-defined types available in qml
  38. ui.GrandCentralDispatcher_QmlRegisterType2("CustomQmlTypes", 1, 0, "GrandCentralDispatcher")
  39. ui.MessageWrapper_QmlRegisterType2("CustomQmlTypes", 1, 0, "MessageWrapper")
  40. }
  41. func main() {
  42. if os.Getenv("CWTCH_FOLDER") != "" {
  43. the.CwtchDir = os.Getenv("CWTCH_FOLDER")
  44. } else if runtime.GOOS == "android" {
  45. the.CwtchDir = filepath.Join(androidBaseDir, "files")
  46. } else {
  47. usr, err := user.Current()
  48. if err != nil {
  49. log.Errorf("\nerror: could not load current user: %v\n", err)
  50. os.Exit(1)
  51. }
  52. the.CwtchDir = filepath.Join(usr.HomeDir, ".cwtch")
  53. }
  54. // suppress event.NewMessageFromPeer so we can handle it ourselves
  55. peer.DefaultEventsToHandle = []event.Type{
  56. event.EncryptedGroupMessage,
  57. event.PeerAcknowledgement,
  58. event.NewGroupInvite,
  59. event.PeerError,
  60. event.SendMessageToGroupError,
  61. event.NewGetValMessageFromPeer,
  62. }
  63. logfileDefault := "cwtch_log.txt"
  64. flagDebug := flag.Bool("debug", false, "turn on extra debug level logging. WARNING: THIS MAY EXPOSE PRIVATE INFORMATION IN CONSOLE OUTPUT!")
  65. flagLogFile := flag.Bool("logfile", false, "instead of console output, log to $HOME/.cwtch/"+logfileDefault)
  66. flagLocal := flag.Bool("local", false, "load user interface from the local folder \"qml\" instead of the built-in UI")
  67. flagService := flag.Bool("service", false, "run this process as an android service")
  68. flagClientUI := flag.Bool("clientui", false, "start the UI as a client of a service app instead of a full app")
  69. flag.Parse()
  70. if *flagLogFile {
  71. filelogger, err := log.NewFile(log.LevelInfo, filepath.Join(the.CwtchDir, logfileDefault))
  72. if err == nil {
  73. log.SetStd(filelogger)
  74. }
  75. }
  76. if *flagDebug {
  77. log.SetLevel(log.LevelDebug)
  78. } else {
  79. log.SetLevel(log.LevelInfo)
  80. }
  81. if !(*flagDebug && buildVer == "") {
  82. // PF Onions
  83. log.AddPrivacyFilter(func(s string) bool {
  84. return len(s) == 56
  85. })
  86. // PF Group ids
  87. log.AddPrivacyFilter(func(s string) bool {
  88. return len(s) == 32
  89. })
  90. // Replace in prod
  91. if buildVer != "" {
  92. log.SetPrivacyFilterReplace(true)
  93. }
  94. }
  95. log.ExcludeFromPattern("connection/connection")
  96. log.ExcludeFromPattern("event/eventmanager")
  97. log.ExcludeFromPattern("service.go")
  98. log.ExcludeFromPattern("tor/BaseOnionService.go")
  99. log.ExcludeFromPattern("applications/auth.go")
  100. log.ExcludeFromPattern("connections/engine.go")
  101. log.ExcludeFromPattern("bridge/pipeBridge.go")
  102. log.ExcludeFromPattern("app/appBridge.go")
  103. log.Infoln("ui main()")
  104. if buildVer == "" && os.Getenv("CWTCH_FOLDER") == "" {
  105. log.Infoln("Development build: using dev directory for dev profiles")
  106. the.CwtchDir = filepath.Join(the.CwtchDir, "dev")
  107. }
  108. the.ACN = nil
  109. the.Peer = nil
  110. the.IPCBridge = nil
  111. the.CwtchApp = nil
  112. the.CwtchService = nil
  113. os.MkdirAll(the.CwtchDir, 0700)
  114. os.MkdirAll(path.Join(the.CwtchDir, "tor"), 0700)
  115. if *flagService {
  116. mainService()
  117. } else {
  118. clientUI := *flagClientUI || runtime.GOOS == "android"
  119. mainUi(*flagLocal, clientUI)
  120. }
  121. if the.ACN != nil {
  122. the.ACN.Close()
  123. }
  124. }
  125. // QRunnable is a shim for QAndroidService and QGuiApplication
  126. type QRunnable interface {
  127. Exec() int
  128. }
  129. func mainService() {
  130. log.Infoln("I am the service")
  131. log.Infoln("Starting a cwtch app...")
  132. go loadNetworkingAndFiles(nil, true, false)
  133. var app QRunnable
  134. if runtime.GOOS == "android" {
  135. log.Infoln("Making QAndroidService...")
  136. app = androidextras.NewQAndroidService(len(os.Args), os.Args)
  137. } else {
  138. log.Infoln("Making QGuiApplication...")
  139. app = gui.NewQGuiApplication(len(os.Args), os.Args)
  140. }
  141. log.Infoln("Cwtch Service starting app.Exec")
  142. app.Exec()
  143. }
  144. func mainUi(flagLocal bool, flagClientUI bool) {
  145. log.Infof("I am the UI (client:%v)\n", flagClientUI)
  146. app := gui.NewQGuiApplication(len(os.Args), os.Args)
  147. // our globals
  148. err := ui.InitGlobalSettingsFile(filepath.Join(the.CwtchDir, "global"), the.AppPassword)
  149. if err != nil {
  150. log.Errorf("Could not access global ui config: %v\n", err)
  151. os.Exit(-1)
  152. }
  153. gcd := ui.NewGrandCentralDispatcher(nil)
  154. gcd.SetOs(runtime.GOOS)
  155. dir := core.QCoreApplication_ApplicationDirPath()
  156. log.Infof("core.QCoreApplication_ApplicationDirPath(): %v\n", dir)
  157. if runtime.GOOS == "android" {
  158. gcd.SetAssetPath("assets:/")
  159. } else if runtime.GOOS == "windows" {
  160. // all of these access are QML based, and QML takes URIs which use forward slashes and translates them to local OS sperators
  161. // also windows paths need to be like /c:/PATH
  162. dir = "/" + dir
  163. // QML uses '/' regardless of platform (so we use path.Join here not filepath.Join)
  164. gcd.SetAssetPath("file://" + path.Join(dir, "assets") + "/")
  165. } else {
  166. if buildVer == "" || flagLocal {
  167. if _, err := os.Stat(path.Join(dir, "assets")); !os.IsNotExist(err) {
  168. gcd.SetAssetPath("file://" + path.Join(dir, "assets") + "/")
  169. }
  170. } else {
  171. usr, err := user.Current()
  172. if err != nil {
  173. log.Errorf("\nerror: could not load current user: %v\n", err)
  174. os.Exit(1)
  175. }
  176. localCwtch := path.Join(usr.HomeDir, ".local/share/cwtch")
  177. if _, err := os.Stat(localCwtch); !os.IsNotExist(err) {
  178. gcd.SetAssetPath("file://" + path.Join(localCwtch, "assets") + "/")
  179. } else if _, err := os.Stat("/usr/share/cwtch"); !os.IsNotExist(err) {
  180. gcd.SetAssetPath("file://" + "/usr/share/cwtch/assets/")
  181. } else if _, err := os.Stat("/usr/local/share/cwtch/"); !os.IsNotExist(err) {
  182. gcd.SetAssetPath("file://" + "/usr/local/share/cwtch/assets/")
  183. } else if _, err := os.Stat(path.Join(dir, "assets")); !os.IsNotExist(err) {
  184. gcd.SetAssetPath("file://" + path.Join(dir, "assets") + "/")
  185. }
  186. }
  187. if gcd.AssetPath() == "" {
  188. log.Errorf("Could not find assets folder")
  189. os.Exit(-1)
  190. }
  191. }
  192. log.Infof("gcd.assetPath = '%v'\n", gcd.AssetPath())
  193. if buildVer != "" {
  194. gcd.SetVersion(buildVer)
  195. gcd.SetBuildDate(buildDate)
  196. } else {
  197. gcd.SetVersion("development")
  198. gcd.SetBuildDate("now")
  199. }
  200. // this is to load local qml files quickly when developing
  201. var qmlSource *core.QUrl
  202. if flagLocal {
  203. qmlSource = core.QUrl_FromLocalFile("./qml/main.qml")
  204. } else {
  205. qmlSource = core.NewQUrl3("qrc:/qml/main.qml", 0)
  206. }
  207. app.SetWindowIcon(gui.NewQIcon5(":/qml/images/cwtch-icon.png"))
  208. // 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
  209. translator := core.NewQTranslator(nil)
  210. translator.Load("translation_en", ":/i18n/", "", "")
  211. core.QCoreApplication_InstallTranslator(translator)
  212. opaqueTranslator := core.NewQTranslator(nil)
  213. opaqueTranslator.Load("translation_en", ":/qml/opaque/i18n/", "", "")
  214. core.QCoreApplication_InstallTranslator(opaqueTranslator)
  215. core.QCoreApplication_SetAttribute(core.Qt__AA_EnableHighDpiScaling, true)
  216. quickcontrols2.QQuickStyle_SetStyle("Universe")
  217. engine := qml.NewQQmlApplicationEngine(nil)
  218. gcd.QMLEngine = engine
  219. gcd.SetLocale(gcd.GlobalSettings.Locale)
  220. // prevent qt from initiating network connections (possible deanon attempts!)
  221. factory := qml.NewQQmlNetworkAccessManagerFactory()
  222. factory.ConnectCreate(func(parent *core.QObject) *network.QNetworkAccessManager {
  223. nam := network.NewQNetworkAccessManager(parent)
  224. nam.SetNetworkAccessible(network.QNetworkAccessManager__NotAccessible)
  225. proxy := network.NewQNetworkProxy()
  226. proxy.SetHostName("0.0.0.0")
  227. nam.SetProxy(proxy)
  228. return nam
  229. })
  230. engine.SetNetworkAccessManagerFactory(factory)
  231. engine.RootContext().SetContextProperty("gcd", gcd)
  232. gcd.TimelineInterface = ui.NewMessageModel(nil)
  233. engine.RootContext().SetContextProperty("mm", gcd.TimelineInterface)
  234. engine.RootContext().SetContextProperty("androidCwtchActivity", gcd.AndroidCwtchActivity)
  235. engine.Load(qmlSource)
  236. go loadNetworkingAndFiles(gcd, false, flagClientUI)
  237. log.Infoln("Cwtch App starting app.Exec")
  238. app.Exec()
  239. }
  240. func loadACN() {
  241. torpath := "tor"
  242. if runtime.GOOS == "android" {
  243. torpath = filepath.Join(androidBaseDir, "lib/libtor.so")
  244. } else if runtime.GOOS == "windows" {
  245. ex, err := os.Executable()
  246. if err != nil {
  247. ex = ""
  248. }
  249. exPath := filepath.Dir(ex)
  250. torpath = filepath.Join(exPath, "tor-0.4.4.6", "Tor", "tor.exe")
  251. } else {
  252. dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
  253. if _, err := os.Stat(filepath.Join(dir, "tor")); os.IsNotExist(err) {
  254. if _, err := os.Stat(filepath.Join(dir, "deploy", "linux", "tor")); os.IsNotExist(err) {
  255. log.Warnln("Cannot find bundled Tor")
  256. } else {
  257. torpath = filepath.Join(dir, "deploy", "linux", "tor")
  258. }
  259. } else {
  260. torpath = filepath.Join(dir, "tor")
  261. }
  262. }
  263. // generate a random socks and control port (not real random...these are port numbers...)
  264. mrand.Seed(int64(time.Now().Nanosecond()))
  265. port := mrand.Intn(1000) + 9600
  266. controlPort := port + 1
  267. // generate a random password (actually random, stored in memory, for the control port)
  268. key := make([]byte, 64)
  269. _, err := rand.Read(key)
  270. if err != nil {
  271. panic(err)
  272. }
  273. // generate torrc on the fly
  274. // TODO if we have been configured for it, use system tor (like orbot) - we need a way to config this in the UI first
  275. tor.NewTorrc().WithSocksPort(port).WithOnionTrafficOnly().WithControlPort(controlPort).WithHashedPassword(base64.StdEncoding.EncodeToString(key)).Build(filepath.Join(the.CwtchDir, "tor", "torrc"))
  276. the.ACN, err = tor.NewTorACNWithAuth(the.CwtchDir, torpath, controlPort, tor.HashedPasswordAuthenticator{base64.StdEncoding.EncodeToString(key)})
  277. if err != nil {
  278. // TODO: turn into UI error: status panel?
  279. log.Errorf("Could not start Tor: %v", err)
  280. os.Exit(1)
  281. }
  282. }
  283. func loadNetworkingAndFiles(gcd *ui.GrandCentralDispatcher, service bool, clientUI bool) {
  284. if service || clientUI || runtime.GOOS == "android" {
  285. clientIn := filepath.Join(the.CwtchDir, "clientIn")
  286. serviceIn := filepath.Join(the.CwtchDir, "serviceIn")
  287. if service {
  288. loadACN()
  289. serviceBridge := bridge.NewPipeBridgeService(serviceIn, clientIn)
  290. log.Infoln("Creating New App Service")
  291. the.CwtchService = libapp.NewAppService(the.ACN, the.CwtchDir, serviceBridge)
  292. } else {
  293. clientBridge := bridge.NewPipeBridgeClient(clientIn, serviceIn)
  294. log.Infoln("Creating New App Client")
  295. the.CwtchApp = libapp.NewAppClient(the.CwtchDir, clientBridge)
  296. }
  297. } else {
  298. if gcd.GlobalSettings.PreviousPid != -1 {
  299. // Todo: Check if there is an older version of the ACN running...
  300. log.Debugf("checking to see if we shutdown the previous ACN (%v)", gcd.GlobalSettings.PreviousPid)
  301. os2.CheckProcessAndKill(uint64(gcd.GlobalSettings.PreviousPid), "tor")
  302. }
  303. loadACN()
  304. pid, err := the.ACN.GetPID()
  305. if err == nil {
  306. gcd.GlobalSettings.PreviousPid = int64(pid)
  307. ui.WriteGlobalSettings(gcd.GlobalSettings)
  308. } else {
  309. log.Errorf("error fetching pid from ACN %v", err)
  310. }
  311. log.Infoln("Creating New App")
  312. the.CwtchApp = libapp.NewApp(the.ACN, the.CwtchDir)
  313. }
  314. if !service {
  315. the.AppBus = the.CwtchApp.GetPrimaryBus()
  316. subscribed := make(chan bool)
  317. go handlers.App(gcd, subscribed, clientUI)
  318. go servers.LaunchServiceManager(gcd, the.ACN, path.Join(the.CwtchDir, "servers"))
  319. <-subscribed
  320. }
  321. }