2018-10-23 18:52:13 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2018-11-22 00:01:17 +00:00
|
|
|
libapp "cwtch.im/cwtch/app"
|
2019-06-21 21:58:40 +00:00
|
|
|
"cwtch.im/cwtch/event/bridge"
|
2018-11-28 22:14:02 +00:00
|
|
|
"cwtch.im/ui/go/characters"
|
|
|
|
"cwtch.im/ui/go/gobjects"
|
|
|
|
"cwtch.im/ui/go/gothings"
|
2019-05-03 18:28:11 +00:00
|
|
|
"cwtch.im/ui/go/gothings/android"
|
2018-11-28 22:14:02 +00:00
|
|
|
"cwtch.im/ui/go/the"
|
2019-03-25 19:10:46 +00:00
|
|
|
"flag"
|
2018-11-28 22:14:02 +00:00
|
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
2018-12-05 16:05:39 +00:00
|
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
2019-08-02 04:45:58 +00:00
|
|
|
"github.com/therecipe/qt/androidextras"
|
2018-11-28 22:14:02 +00:00
|
|
|
"github.com/therecipe/qt/core"
|
2019-03-18 23:52:46 +00:00
|
|
|
"github.com/therecipe/qt/gui"
|
2019-01-28 22:00:46 +00:00
|
|
|
"github.com/therecipe/qt/network"
|
|
|
|
"github.com/therecipe/qt/qml"
|
2018-10-25 00:13:03 +00:00
|
|
|
"github.com/therecipe/qt/quickcontrols2"
|
|
|
|
"os"
|
2019-03-18 23:52:46 +00:00
|
|
|
"os/user"
|
2018-10-25 00:13:03 +00:00
|
|
|
"path"
|
2019-02-14 18:57:44 +00:00
|
|
|
"path/filepath"
|
2019-03-18 23:52:46 +00:00
|
|
|
"runtime"
|
2018-10-28 02:49:14 +00:00
|
|
|
)
|
2018-10-23 18:52:13 +00:00
|
|
|
|
2019-04-08 21:01:53 +00:00
|
|
|
const androidBaseDir = "/data/data/ca.openprivacy.cwtch.ui/"
|
2019-02-19 21:32:52 +00:00
|
|
|
|
2019-03-27 19:57:11 +00:00
|
|
|
var (
|
|
|
|
buildVer string
|
|
|
|
buildDate string
|
|
|
|
)
|
|
|
|
|
2018-11-22 00:01:17 +00:00
|
|
|
func init() {
|
|
|
|
// make go-defined types available in qml
|
|
|
|
gothings.GrandCentralDispatcher_QmlRegisterType2("CustomQmlTypes", 1, 0, "GrandCentralDispatcher")
|
2018-10-23 18:52:13 +00:00
|
|
|
}
|
|
|
|
|
2018-11-22 00:01:17 +00:00
|
|
|
func main() {
|
2019-08-12 21:23:50 +00:00
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
filelogger, err := log.NewFile(log.LevelInfo, "cwtch_log.txt")
|
|
|
|
if err == nil {
|
|
|
|
log.SetStd(filelogger)
|
|
|
|
}
|
|
|
|
}
|
2019-05-09 21:45:02 +00:00
|
|
|
log.Infoln("ui main()\n")
|
2019-03-25 19:10:46 +00:00
|
|
|
flagDebug := flag.Bool("debug", false, "turn on extra logging. WARNING: THIS MAY EXPOSE PRIVATE INFORMATION IN CONSOLE OUTPUT!")
|
|
|
|
flagLocal := flag.Bool("local", false, "load user interface from the local folder \"qml\" instead of the built-in UI")
|
2019-07-06 00:50:03 +00:00
|
|
|
flagService := flag.Bool("service", false, "run this process as an android service")
|
|
|
|
flagClientUI := flag.Bool("clientui", false, "start the UI as a client of a service app instead of a full app")
|
2019-03-25 19:10:46 +00:00
|
|
|
flag.Parse()
|
2019-02-14 02:42:13 +00:00
|
|
|
|
2019-03-25 19:10:46 +00:00
|
|
|
if *flagDebug {
|
2019-02-14 02:42:13 +00:00
|
|
|
log.SetLevel(log.LevelDebug)
|
|
|
|
} else {
|
|
|
|
log.SetLevel(log.LevelInfo)
|
|
|
|
}
|
|
|
|
|
2019-06-21 21:58:40 +00:00
|
|
|
// TESTING
|
2019-07-06 00:50:03 +00:00
|
|
|
//log.SetLevel(log.LevelDebug)
|
|
|
|
//log.ExcludeFromPattern("connection/connection")
|
|
|
|
//log.ExcludeFromPattern("outbound/3dhauthchannel")
|
2019-08-12 21:23:50 +00:00
|
|
|
//log.AddNothingExceptFilter("event/eventmanager")
|
2019-06-21 21:58:40 +00:00
|
|
|
|
2019-05-13 21:01:08 +00:00
|
|
|
if os.Getenv("CWTCH_FOLDER") != "" {
|
|
|
|
the.CwtchDir = os.Getenv("CWTCH_FOLDER")
|
|
|
|
} else if runtime.GOOS == "android" {
|
|
|
|
the.CwtchDir = path.Join(androidBaseDir, "files")
|
2019-02-14 02:53:36 +00:00
|
|
|
} else {
|
2019-05-13 21:01:08 +00:00
|
|
|
usr, err := user.Current()
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("\nerror: could not load current user: %v\n", err)
|
|
|
|
os.Exit(1)
|
2019-05-09 21:45:02 +00:00
|
|
|
}
|
2019-05-13 21:01:08 +00:00
|
|
|
the.CwtchDir = path.Join(usr.HomeDir, ".cwtch")
|
|
|
|
}
|
|
|
|
the.ACN = nil
|
2019-06-21 21:58:40 +00:00
|
|
|
the.Peer = nil
|
|
|
|
the.IPCBridge = nil
|
|
|
|
the.CwtchApp = nil
|
|
|
|
the.CwtchService = nil
|
|
|
|
os.MkdirAll(the.CwtchDir, 0700)
|
2019-05-09 21:45:02 +00:00
|
|
|
|
2019-05-13 21:01:08 +00:00
|
|
|
if *flagService {
|
|
|
|
mainService()
|
|
|
|
} else {
|
2019-09-10 23:36:45 +00:00
|
|
|
clientUI := *flagClientUI || runtime.GOOS == "android"
|
|
|
|
mainUi(*flagLocal, clientUI)
|
2019-05-13 21:01:08 +00:00
|
|
|
}
|
2019-05-09 21:45:02 +00:00
|
|
|
|
2019-05-13 21:01:08 +00:00
|
|
|
if the.ACN != nil {
|
|
|
|
the.ACN.Close()
|
|
|
|
}
|
|
|
|
}
|
2019-05-09 21:45:02 +00:00
|
|
|
|
2019-08-02 04:45:58 +00:00
|
|
|
// QRunnable is a shim for QAndroidService and QGuiApplication
|
|
|
|
type QRunnable interface {
|
|
|
|
Exec() int
|
|
|
|
}
|
|
|
|
|
2019-05-13 21:01:08 +00:00
|
|
|
func mainService() {
|
|
|
|
log.Infoln("I am the service")
|
|
|
|
log.Infoln("Starting a cwtch app...")
|
2019-07-06 00:50:03 +00:00
|
|
|
go loadNetworkingAndFiles(nil, true, false)
|
2019-08-02 04:45:58 +00:00
|
|
|
var app QRunnable
|
|
|
|
if runtime.GOOS == "android" {
|
|
|
|
log.Infoln("Making QAndroidService...")
|
|
|
|
app = androidextras.NewQAndroidService(len(os.Args), os.Args)
|
|
|
|
} else {
|
|
|
|
log.Infoln("Making QGuiApplication...")
|
|
|
|
app = gui.NewQGuiApplication(len(os.Args), os.Args)
|
|
|
|
}
|
2019-06-21 21:58:40 +00:00
|
|
|
log.Infoln("Cwtch Service starting app.Exec")
|
|
|
|
app.Exec()
|
2019-05-13 21:01:08 +00:00
|
|
|
}
|
2019-05-09 21:45:02 +00:00
|
|
|
|
2019-07-06 00:50:03 +00:00
|
|
|
func mainUi(flagLocal bool, flagClientUI bool) {
|
2019-09-10 23:36:45 +00:00
|
|
|
log.Infof("I am the UI (client:%v)\n", flagClientUI)
|
2019-05-09 21:45:02 +00:00
|
|
|
|
2019-05-13 21:01:08 +00:00
|
|
|
app := gui.NewQGuiApplication(len(os.Args), os.Args)
|
2019-05-09 21:45:02 +00:00
|
|
|
|
2019-05-13 21:01:08 +00:00
|
|
|
// our globals
|
|
|
|
gcd := gothings.NewGrandCentralDispatcher(nil)
|
|
|
|
gcd.SetOs(runtime.GOOS)
|
|
|
|
if buildVer != "" {
|
|
|
|
gcd.SetVersion(buildVer)
|
|
|
|
gcd.SetBuildDate(buildDate)
|
|
|
|
} else {
|
|
|
|
gcd.SetVersion("development")
|
|
|
|
gcd.SetBuildDate("now")
|
2019-05-09 21:45:02 +00:00
|
|
|
}
|
2019-05-13 21:01:08 +00:00
|
|
|
gcd.UIState = gothings.NewUIState(gcd)
|
|
|
|
gcd.OutgoingMessages = make(chan gobjects.Letter, 1000)
|
2019-04-16 21:11:29 +00:00
|
|
|
|
2019-05-13 21:01:08 +00:00
|
|
|
//TODO: put theme stuff somewhere better
|
|
|
|
gcd.SetThemeScale(1.0)
|
|
|
|
|
|
|
|
// this is to load local qml files quickly when developing
|
|
|
|
var qmlSource *core.QUrl
|
|
|
|
if flagLocal {
|
|
|
|
qmlSource = core.QUrl_FromLocalFile("./qml/main.qml")
|
|
|
|
} else {
|
|
|
|
qmlSource = core.NewQUrl3("qrc:/qml/main.qml", 0)
|
2019-04-16 21:11:29 +00:00
|
|
|
}
|
2018-10-23 18:52:13 +00:00
|
|
|
|
2019-05-13 21:01:08 +00:00
|
|
|
app.SetWindowIcon(gui.NewQIcon5(":/qml/images/cwtch-icon.png"))
|
|
|
|
|
|
|
|
// 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
|
|
|
|
translator := core.NewQTranslator(nil)
|
|
|
|
translator.Load("translation_en", ":/i18n/", "", "")
|
|
|
|
core.QCoreApplication_InstallTranslator(translator)
|
|
|
|
|
|
|
|
gcd.Translator = core.NewQTranslator(nil)
|
|
|
|
gcd.Translator.Load("translation_"+core.QLocale_System().Name(), ":/i18n/", "", "")
|
|
|
|
core.QCoreApplication_InstallTranslator(gcd.Translator)
|
|
|
|
|
|
|
|
core.QCoreApplication_SetAttribute(core.Qt__AA_EnableHighDpiScaling, true)
|
|
|
|
quickcontrols2.QQuickStyle_SetStyle("Universe")
|
|
|
|
engine := qml.NewQQmlApplicationEngine(nil)
|
|
|
|
gcd.QMLEngine = engine
|
|
|
|
|
|
|
|
// prevent qt from initiating network connections (possible deanon attempts!)
|
|
|
|
factory := qml.NewQQmlNetworkAccessManagerFactory()
|
|
|
|
factory.ConnectCreate(func(parent *core.QObject) *network.QNetworkAccessManager {
|
|
|
|
nam := network.NewQNetworkAccessManager(parent)
|
|
|
|
nam.SetNetworkAccessible(network.QNetworkAccessManager__NotAccessible)
|
|
|
|
proxy := network.NewQNetworkProxy()
|
|
|
|
proxy.SetHostName("0.0.0.0")
|
|
|
|
nam.SetProxy(proxy)
|
|
|
|
//nam.ConnectCreateRequest(func(op network.QNetworkAccessManager__Operation, originalReq *network.QNetworkRequest, outgoingData *core.QIODevice) *network.QNetworkReply {
|
|
|
|
// log.Errorf("network access request detected - possible remote content insertion bug!!!")
|
|
|
|
// return nil
|
|
|
|
//})
|
|
|
|
return nam
|
|
|
|
})
|
|
|
|
engine.SetNetworkAccessManagerFactory(factory)
|
|
|
|
|
|
|
|
// variables we want to access from inside qml
|
|
|
|
if runtime.GOOS == "android" {
|
|
|
|
gcd.SetThemeScale(2.9)
|
2019-03-27 19:57:11 +00:00
|
|
|
} else {
|
2019-05-13 21:01:08 +00:00
|
|
|
gcd.SetThemeScale(1.0)
|
2019-02-14 19:17:48 +00:00
|
|
|
}
|
2019-05-13 21:01:08 +00:00
|
|
|
engine.RootContext().SetContextProperty("gcd", gcd)
|
|
|
|
|
|
|
|
var androidCwtchActivity = android.NewCwtchActivity(nil)
|
|
|
|
engine.RootContext().SetContextProperty("androidCwtchActivity", androidCwtchActivity)
|
|
|
|
|
|
|
|
engine.Load(qmlSource)
|
|
|
|
|
2019-07-06 00:50:03 +00:00
|
|
|
go loadNetworkingAndFiles(gcd, false, flagClientUI)
|
2019-05-13 21:01:08 +00:00
|
|
|
|
2019-06-21 21:58:40 +00:00
|
|
|
log.Infoln("Cwtch App starting app.Exec")
|
2019-05-13 21:01:08 +00:00
|
|
|
app.Exec()
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadACN() {
|
2019-02-14 02:53:36 +00:00
|
|
|
torpath := "tor"
|
|
|
|
if runtime.GOOS == "android" {
|
2019-02-19 21:32:52 +00:00
|
|
|
torpath = path.Join(androidBaseDir, "lib/libtor.so")
|
2019-08-10 00:31:22 +00:00
|
|
|
} else if runtime.GOOS == "windows" {
|
|
|
|
ex, err := os.Executable()
|
|
|
|
if err != nil {
|
|
|
|
ex = ""
|
|
|
|
}
|
|
|
|
exPath := filepath.Dir(ex)
|
|
|
|
torpath = path.Join(exPath, "tor-0.3.5.7", "Tor", "tor.exe")
|
2019-02-14 18:57:44 +00:00
|
|
|
} else {
|
|
|
|
dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
|
|
|
|
if _, err := os.Stat(path.Join(dir, "tor")); os.IsNotExist(err) {
|
|
|
|
if _, err := os.Stat(path.Join(dir, "deploy", "linux", "tor")); os.IsNotExist(err) {
|
2019-08-10 00:31:22 +00:00
|
|
|
log.Warnln("Cannot find bundled Tor")
|
2019-02-14 18:57:44 +00:00
|
|
|
} else {
|
|
|
|
torpath = path.Join(dir, "deploy", "linux", "tor")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
torpath = path.Join(dir, "tor")
|
|
|
|
}
|
2019-02-14 02:53:36 +00:00
|
|
|
}
|
2019-04-16 21:11:29 +00:00
|
|
|
var err error
|
|
|
|
the.ACN, err = connectivity.StartTor(the.CwtchDir, torpath)
|
2019-02-04 22:34:16 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Errorf("Could not start Tor: %v", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2019-05-13 21:01:08 +00:00
|
|
|
}
|
|
|
|
|
2019-07-06 00:50:03 +00:00
|
|
|
func loadNetworkingAndFiles(gcd *gothings.GrandCentralDispatcher, service bool, clientUI bool) {
|
|
|
|
if service || clientUI || runtime.GOOS == "android" {
|
2019-06-21 21:58:40 +00:00
|
|
|
clientIn := path.Join(the.CwtchDir, "clientIn")
|
|
|
|
serviceIn := path.Join(the.CwtchDir, "serviceIn")
|
|
|
|
if service {
|
|
|
|
loadACN()
|
2019-07-23 20:40:54 +00:00
|
|
|
serviceBridge := bridge.NewPipeBridgeService(serviceIn, clientIn)
|
2019-06-21 21:58:40 +00:00
|
|
|
log.Infoln("Creating New App Service")
|
|
|
|
the.CwtchService = libapp.NewAppService(the.ACN, the.CwtchDir, serviceBridge)
|
|
|
|
} else {
|
2019-07-10 20:38:56 +00:00
|
|
|
clientBridge := bridge.NewPipeBridgeClient(clientIn, serviceIn)
|
2019-06-21 21:58:40 +00:00
|
|
|
log.Infoln("Creating New App Client")
|
|
|
|
the.CwtchApp = libapp.NewAppClient(the.CwtchDir, clientBridge)
|
|
|
|
}
|
|
|
|
} else {
|
2019-05-13 21:01:08 +00:00
|
|
|
loadACN()
|
2019-06-21 21:58:40 +00:00
|
|
|
log.Infoln("Creating New App")
|
|
|
|
the.CwtchApp = libapp.NewApp(the.ACN, the.CwtchDir)
|
2019-05-13 21:01:08 +00:00
|
|
|
}
|
2019-02-04 22:34:16 +00:00
|
|
|
|
2019-06-21 21:58:40 +00:00
|
|
|
if !service {
|
|
|
|
the.AppBus = the.CwtchApp.GetPrimaryBus()
|
2019-07-24 20:51:20 +00:00
|
|
|
subscribed := make(chan bool)
|
|
|
|
go characters.AppEventListener(gcd, subscribed)
|
|
|
|
<-subscribed
|
2019-07-23 20:40:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if !service && !clientUI {
|
|
|
|
the.CwtchApp.LoadProfiles(the.AppPassword)
|
2019-06-21 21:58:40 +00:00
|
|
|
}
|
2018-11-28 22:14:02 +00:00
|
|
|
}
|