forked from Silverfish/proton-bridge
GODT-1554 / 1555: Implement gRPC go service and Qt 5 frontend C++ app.
WIP: updates WIP: cache on disk and autostart. WIP: mail, keychain and more. WIP: updated grpc version in go mod file. WIP: user list. WIP: RPC service placeholder WIP: test C++ RPC client skeleton. Other: missing license script update. WIP: use Qt test framework. WIP: test for app and login calls. WIP: test for update & cache on disk calls. WIP: tests for mail settings calls. WIP: all client tests. WIP: linter fixes. WIP: fix missing license link. WIP: update dependency_license script for gRPC and protobuf. WIP: removed unused file. WIP: app & login event streaming tests. WIP: update event stream tests. WIP: completed event streaming tests. GODT-1554: qt C++ frontend skeleton. WIP: C++ backend declaration. wip: started drafting user model. WIP: users. not functional. WIP: invokable methods WIP: Exception class + backend 'injection' into QML. WIP: switch to VCPKG to ease multi-arch compilation, C++ RPC client skeleton. WIP: Renaming and reorganisation WIP:introduced new 'grpc' go frontend. WIP: Worker & Oveerseer for thread management. WIP: added log to C++ app. WIP: event stream architecture on Go side. WIP: event parsing and streamer stopping. WIP: Moved grpc to frontend subfolder + use vcpkg for gRPC and protobuf. WIP: windows building ok WIP: wired a few messages WIP: more wiring. WIP: Fixed imports after rebase on top of devel. WIP: wired some bool and string properties. WIP: more properties. WIP: wired cache on disk stuff WIP: connect event watcher. WIP: login WIP: fix showSplashScreen WIP: Wired login calls. WIP: user list. WIP: Refactored main(). WIP: User retrieval . WIP: no shared pointer in user model. WIP: fixed user count. WIP: cached goos. WIP: Wired autostart WIP: beta channel toggle wired. WIP: User removal WIP: wired theme WIP: implemented configure apple mail. WIP: split mode. WIP: fixed user updates. WIP: fixed Quit from tray icon WIP: wired CurrentEmailClient WIP: wired UseSSLForSMTP WIP: wired change ports . WIP: wired DoH. . WIP: wired keychain calls. WIP: wired autoupdate option. WIP: QML Backend clean-up. WIP: cleanup. WIP: moved user related files in subfolder. . WIP: User are managed using smart pointers. WIP: cleanup. WIP: more cleanup. WIP: mail events forwarding WIP: code inspection tweaks from CLion. WIP: moved QML, cleanup, and missing copyright notices. WIP: Backend is not QMLBackend. Other: fixed issues reported by Leander. [skip ci]
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@ -34,4 +34,6 @@ proton-bridge
|
|||||||
|
|
||||||
# Jetbrains (CLion, Golang) cmake build dirs
|
# Jetbrains (CLion, Golang) cmake build dirs
|
||||||
cmake-build-*/
|
cmake-build-*/
|
||||||
cmake-build-*/
|
|
||||||
|
# Doxygen doc files
|
||||||
|
_doc/
|
||||||
|
|||||||
@ -33,12 +33,10 @@ Proton Mail Bridge includes the following 3rd party software:
|
|||||||
* [go-srp](https://github.com/ProtonMail/go-srp) available under [license](https://github.com/ProtonMail/go-srp/blob/master/LICENSE)
|
* [go-srp](https://github.com/ProtonMail/go-srp) available under [license](https://github.com/ProtonMail/go-srp/blob/master/LICENSE)
|
||||||
* [go-vcard](https://github.com/ProtonMail/go-vcard) available under [license](https://github.com/ProtonMail/go-vcard/blob/master/LICENSE)
|
* [go-vcard](https://github.com/ProtonMail/go-vcard) available under [license](https://github.com/ProtonMail/go-vcard/blob/master/LICENSE)
|
||||||
* [gopenpgp](https://github.com/ProtonMail/gopenpgp/v2) available under [license](https://github.com/ProtonMail/gopenpgp/v2/blob/master/LICENSE)
|
* [gopenpgp](https://github.com/ProtonMail/gopenpgp/v2) available under [license](https://github.com/ProtonMail/gopenpgp/v2/blob/master/LICENSE)
|
||||||
|
* [proton-bridge](https://github.com/ProtonMail/proton-bridge) available under [license](https://github.com/ProtonMail/proton-bridge/blob/master/LICENSE)
|
||||||
* [goquery](https://github.com/PuerkitoBio/goquery) available under [license](https://github.com/PuerkitoBio/goquery/blob/master/LICENSE)
|
* [goquery](https://github.com/PuerkitoBio/goquery) available under [license](https://github.com/PuerkitoBio/goquery/blob/master/LICENSE)
|
||||||
* [ishell](https://github.com/abiosoft/ishell) available under [license](https://github.com/abiosoft/ishell/blob/master/LICENSE)
|
* [ishell](https://github.com/abiosoft/ishell) available under [license](https://github.com/abiosoft/ishell/blob/master/LICENSE)
|
||||||
* [readline](https://github.com/abiosoft/readline) available under [license](https://github.com/abiosoft/readline/blob/master/LICENSE)
|
|
||||||
* [go-singleinstance](https://github.com/allan-simon/go-singleinstance) available under [license](https://github.com/allan-simon/go-singleinstance/blob/master/LICENSE)
|
* [go-singleinstance](https://github.com/allan-simon/go-singleinstance) available under [license](https://github.com/allan-simon/go-singleinstance/blob/master/LICENSE)
|
||||||
* [logex](https://github.com/chzyer/logex) available under [license](https://github.com/chzyer/logex/blob/master/LICENSE)
|
|
||||||
* [test](https://github.com/chzyer/test) available under [license](https://github.com/chzyer/test/blob/master/LICENSE)
|
|
||||||
* [godog](https://github.com/cucumber/godog) available under [license](https://github.com/cucumber/godog/blob/master/LICENSE)
|
* [godog](https://github.com/cucumber/godog) available under [license](https://github.com/cucumber/godog/blob/master/LICENSE)
|
||||||
* [messages-go](https://github.com/cucumber/messages-go/v16) available under [license](https://github.com/cucumber/messages-go/v16/blob/master/LICENSE)
|
* [messages-go](https://github.com/cucumber/messages-go/v16) available under [license](https://github.com/cucumber/messages-go/v16/blob/master/LICENSE)
|
||||||
* [go-sysinfo](https://github.com/elastic/go-sysinfo) available under [license](https://github.com/elastic/go-sysinfo/blob/master/LICENSE)
|
* [go-sysinfo](https://github.com/elastic/go-sysinfo) available under [license](https://github.com/elastic/go-sysinfo/blob/master/LICENSE)
|
||||||
@ -51,9 +49,7 @@ Proton Mail Bridge includes the following 3rd party software:
|
|||||||
* [go-sasl](https://github.com/emersion/go-sasl) available under [license](https://github.com/emersion/go-sasl/blob/master/LICENSE)
|
* [go-sasl](https://github.com/emersion/go-sasl) available under [license](https://github.com/emersion/go-sasl/blob/master/LICENSE)
|
||||||
* [go-smtp](https://github.com/emersion/go-smtp) available under [license](https://github.com/emersion/go-smtp/blob/master/LICENSE)
|
* [go-smtp](https://github.com/emersion/go-smtp) available under [license](https://github.com/emersion/go-smtp/blob/master/LICENSE)
|
||||||
* [go-textwrapper](https://github.com/emersion/go-textwrapper) available under [license](https://github.com/emersion/go-textwrapper/blob/master/LICENSE)
|
* [go-textwrapper](https://github.com/emersion/go-textwrapper) available under [license](https://github.com/emersion/go-textwrapper/blob/master/LICENSE)
|
||||||
* [go-vcard](https://github.com/emersion/go-vcard) available under [license](https://github.com/emersion/go-vcard/blob/master/LICENSE)
|
|
||||||
* [color](https://github.com/fatih/color) available under [license](https://github.com/fatih/color/blob/master/LICENSE)
|
* [color](https://github.com/fatih/color) available under [license](https://github.com/fatih/color/blob/master/LICENSE)
|
||||||
* [go-shlex](https://github.com/flynn-archive/go-shlex) available under [license](https://github.com/flynn-archive/go-shlex/blob/master/LICENSE)
|
|
||||||
* [sentry-go](https://github.com/getsentry/sentry-go) available under [license](https://github.com/getsentry/sentry-go/blob/master/LICENSE)
|
* [sentry-go](https://github.com/getsentry/sentry-go) available under [license](https://github.com/getsentry/sentry-go/blob/master/LICENSE)
|
||||||
* [resty](https://github.com/go-resty/resty/v2) available under [license](https://github.com/go-resty/resty/v2/blob/master/LICENSE)
|
* [resty](https://github.com/go-resty/resty/v2) available under [license](https://github.com/go-resty/resty/v2/blob/master/LICENSE)
|
||||||
* [dbus](https://github.com/godbus/dbus) available under [license](https://github.com/godbus/dbus/blob/master/LICENSE)
|
* [dbus](https://github.com/godbus/dbus) available under [license](https://github.com/godbus/dbus/blob/master/LICENSE)
|
||||||
@ -66,15 +62,12 @@ Proton Mail Bridge includes the following 3rd party software:
|
|||||||
* [go-keychain](https://github.com/keybase/go-keychain) available under [license](https://github.com/keybase/go-keychain/blob/master/LICENSE)
|
* [go-keychain](https://github.com/keybase/go-keychain) available under [license](https://github.com/keybase/go-keychain/blob/master/LICENSE)
|
||||||
* [text](https://github.com/kr/text) available under [license](https://github.com/kr/text/blob/master/LICENSE)
|
* [text](https://github.com/kr/text) available under [license](https://github.com/kr/text/blob/master/LICENSE)
|
||||||
* [aurora](https://github.com/logrusorgru/aurora) available under [license](https://github.com/logrusorgru/aurora/blob/master/LICENSE)
|
* [aurora](https://github.com/logrusorgru/aurora) available under [license](https://github.com/logrusorgru/aurora/blob/master/LICENSE)
|
||||||
* [go-runewidth](https://github.com/mattn/go-runewidth) available under [license](https://github.com/mattn/go-runewidth/blob/master/LICENSE)
|
|
||||||
* [dns](https://github.com/miekg/dns) available under [license](https://github.com/miekg/dns/blob/master/LICENSE)
|
* [dns](https://github.com/miekg/dns) available under [license](https://github.com/miekg/dns/blob/master/LICENSE)
|
||||||
* [jsondiff](https://github.com/nsf/jsondiff) available under [license](https://github.com/nsf/jsondiff/blob/master/LICENSE)
|
* [jsondiff](https://github.com/nsf/jsondiff) available under [license](https://github.com/nsf/jsondiff/blob/master/LICENSE)
|
||||||
* [tablewriter](https://github.com/olekukonko/tablewriter) available under [license](https://github.com/olekukonko/tablewriter/blob/master/LICENSE)
|
|
||||||
* [errors](https://github.com/pkg/errors) available under [license](https://github.com/pkg/errors/blob/master/LICENSE)
|
* [errors](https://github.com/pkg/errors) available under [license](https://github.com/pkg/errors/blob/master/LICENSE)
|
||||||
* [procfs](https://github.com/prometheus/procfs) available under [license](https://github.com/prometheus/procfs/blob/master/LICENSE)
|
* [procfs](https://github.com/prometheus/procfs) available under [license](https://github.com/prometheus/procfs/blob/master/LICENSE)
|
||||||
* [du](https://github.com/ricochet2200/go-disk-usage/du) available under [license](https://github.com/ricochet2200/go-disk-usage/du/blob/master/LICENSE)
|
* [du](https://github.com/ricochet2200/go-disk-usage/du) available under [license](https://github.com/ricochet2200/go-disk-usage/du/blob/master/LICENSE)
|
||||||
* [logrus](https://github.com/sirupsen/logrus) available under [license](https://github.com/sirupsen/logrus/blob/master/LICENSE)
|
* [logrus](https://github.com/sirupsen/logrus) available under [license](https://github.com/sirupsen/logrus/blob/master/LICENSE)
|
||||||
* [bom](https://github.com/ssor/bom) available under [license](https://github.com/ssor/bom/blob/master/LICENSE)
|
|
||||||
* [testify](https://github.com/stretchr/testify) available under [license](https://github.com/stretchr/testify/blob/master/LICENSE)
|
* [testify](https://github.com/stretchr/testify) available under [license](https://github.com/stretchr/testify/blob/master/LICENSE)
|
||||||
* [qt](https://github.com/therecipe/qt) available under [license](https://github.com/therecipe/qt/blob/master/LICENSE)
|
* [qt](https://github.com/therecipe/qt) available under [license](https://github.com/therecipe/qt/blob/master/LICENSE)
|
||||||
* [cli](https://github.com/urfave/cli/v2) available under [license](https://github.com/urfave/cli/v2/blob/master/LICENSE)
|
* [cli](https://github.com/urfave/cli/v2) available under [license](https://github.com/urfave/cli/v2/blob/master/LICENSE)
|
||||||
|
|||||||
11
go.mod
11
go.mod
@ -20,12 +20,10 @@ require (
|
|||||||
github.com/ProtonMail/go-srp v0.0.5
|
github.com/ProtonMail/go-srp v0.0.5
|
||||||
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5
|
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.4.7
|
github.com/ProtonMail/gopenpgp/v2 v2.4.7
|
||||||
|
github.com/ProtonMail/proton-bridge v1.8.12
|
||||||
github.com/PuerkitoBio/goquery v1.5.1
|
github.com/PuerkitoBio/goquery v1.5.1
|
||||||
github.com/abiosoft/ishell v2.0.0+incompatible
|
github.com/abiosoft/ishell v2.0.0+incompatible
|
||||||
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect
|
|
||||||
github.com/allan-simon/go-singleinstance v0.0.0-20160830203053-79edcfdc2dfc
|
github.com/allan-simon/go-singleinstance v0.0.0-20160830203053-79edcfdc2dfc
|
||||||
github.com/chzyer/logex v1.1.10 // indirect
|
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
|
|
||||||
github.com/cucumber/godog v0.12.1
|
github.com/cucumber/godog v0.12.1
|
||||||
github.com/cucumber/messages-go/v16 v16.0.1
|
github.com/cucumber/messages-go/v16 v16.0.1
|
||||||
github.com/elastic/go-sysinfo v1.7.1
|
github.com/elastic/go-sysinfo v1.7.1
|
||||||
@ -38,9 +36,7 @@ require (
|
|||||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
|
||||||
github.com/emersion/go-smtp v0.14.0
|
github.com/emersion/go-smtp v0.14.0
|
||||||
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594
|
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594
|
||||||
github.com/emersion/go-vcard v0.0.0-20190105225839-8856043f13c5 // indirect
|
|
||||||
github.com/fatih/color v1.9.0
|
github.com/fatih/color v1.9.0
|
||||||
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
|
|
||||||
github.com/getsentry/sentry-go v0.12.0
|
github.com/getsentry/sentry-go v0.12.0
|
||||||
github.com/go-resty/resty/v2 v2.6.0
|
github.com/go-resty/resty/v2 v2.6.0
|
||||||
github.com/godbus/dbus v4.1.0+incompatible
|
github.com/godbus/dbus v4.1.0+incompatible
|
||||||
@ -53,15 +49,12 @@ require (
|
|||||||
github.com/keybase/go-keychain v0.0.0
|
github.com/keybase/go-keychain v0.0.0
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
|
||||||
github.com/miekg/dns v1.1.41
|
github.com/miekg/dns v1.1.41
|
||||||
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce
|
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce
|
||||||
github.com/olekukonko/tablewriter v0.0.4 // indirect
|
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/prometheus/procfs v0.7.3 // indirect
|
github.com/prometheus/procfs v0.7.3 // indirect
|
||||||
github.com/ricochet2200/go-disk-usage/du v0.0.0-20210707232629-ac9918953285
|
github.com/ricochet2200/go-disk-usage/du v0.0.0-20210707232629-ac9918953285
|
||||||
github.com/sirupsen/logrus v1.7.0
|
github.com/sirupsen/logrus v1.7.0
|
||||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/therecipe/qt v0.0.0-20200701200531-7f61353ee73e
|
github.com/therecipe/qt v0.0.0-20200701200531-7f61353ee73e
|
||||||
github.com/urfave/cli/v2 v2.2.0
|
github.com/urfave/cli/v2 v2.2.0
|
||||||
@ -73,7 +66,7 @@ require (
|
|||||||
golang.org/x/text v0.3.7
|
golang.org/x/text v0.3.7
|
||||||
google.golang.org/grpc v1.46.2
|
google.golang.org/grpc v1.46.2
|
||||||
google.golang.org/protobuf v1.28.0
|
google.golang.org/protobuf v1.28.0
|
||||||
howett.net/plist v1.0.0
|
howett.net/plist v1.0.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace (
|
replace (
|
||||||
|
|||||||
16
go.sum
16
go.sum
@ -31,6 +31,8 @@ github.com/ProtonMail/docker-credential-helpers v1.1.0/go.mod h1:mK0aBveCxhnQ756
|
|||||||
github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a h1:fXK2KsfnkBV9Nh+9SKzHchYjuE9s0vI20JG1mbtEAcc=
|
github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a h1:fXK2KsfnkBV9Nh+9SKzHchYjuE9s0vI20JG1mbtEAcc=
|
||||||
github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4=
|
github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4=
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||||
|
github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||||
|
github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20211221144345-a4f6767435ab h1:5FiL/TCaiKCss/BLMIACDxxadYrx767l9kh0qYX+sLQ=
|
github.com/ProtonMail/go-crypto v0.0.0-20211221144345-a4f6767435ab h1:5FiL/TCaiKCss/BLMIACDxxadYrx767l9kh0qYX+sLQ=
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20211221144345-a4f6767435ab/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
github.com/ProtonMail/go-crypto v0.0.0-20211221144345-a4f6767435ab/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20220113124808-70ae35bab23f/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
github.com/ProtonMail/go-crypto v0.0.0-20220113124808-70ae35bab23f/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||||
@ -54,8 +56,11 @@ github.com/ProtonMail/go-srp v0.0.5 h1:xhUioxZgDbCnpo9JehyFhwwsn9JLWkUGfB0oiKXgi
|
|||||||
github.com/ProtonMail/go-srp v0.0.5/go.mod h1:06iYHtLXW8vjLtccWj++x3MKy65sIT8yZd7nrJF49rs=
|
github.com/ProtonMail/go-srp v0.0.5/go.mod h1:06iYHtLXW8vjLtccWj++x3MKy65sIT8yZd7nrJF49rs=
|
||||||
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5 h1:Uga1DHFN4GUxuDQr0F71tpi8I9HqPIlZodZAI1lR6VQ=
|
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5 h1:Uga1DHFN4GUxuDQr0F71tpi8I9HqPIlZodZAI1lR6VQ=
|
||||||
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5/go.mod h1:oeP9CMN+ajWp5jKp1kue5daJNwMMxLF+ujPaUIoJWlA=
|
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5/go.mod h1:oeP9CMN+ajWp5jKp1kue5daJNwMMxLF+ujPaUIoJWlA=
|
||||||
|
github.com/ProtonMail/gopenpgp/v2 v2.2.2/go.mod h1:ajUlBGvxMH1UBZnaYO3d1FSVzjiC6kK9XlZYGiDCvpM=
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.4.1 h1:b3El0zabaKi73u4sRnb3hOOUczuKuYpN8wnp7wRsZSc=
|
github.com/ProtonMail/gopenpgp/v2 v2.4.1 h1:b3El0zabaKi73u4sRnb3hOOUczuKuYpN8wnp7wRsZSc=
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.4.1/go.mod h1:RFjoVjfhV8f78tjz/fLrp/OXkugL3QmWsiJq/fsQYA4=
|
github.com/ProtonMail/gopenpgp/v2 v2.4.1/go.mod h1:RFjoVjfhV8f78tjz/fLrp/OXkugL3QmWsiJq/fsQYA4=
|
||||||
|
github.com/ProtonMail/proton-bridge v1.8.12 h1:wR/WguXfVT2qiL+4xcSIlN1XqH49zl81hD0CdVnQdfw=
|
||||||
|
github.com/ProtonMail/proton-bridge v1.8.12/go.mod h1:ZeGTC11l/KTGLP5aASnS7dxu3T1z5HORA048DpwbVoI=
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.4.7 h1:V3xeelvXgJiZXZuPtSSE+uYbtPw4RmbmyPqXDAESPhg=
|
github.com/ProtonMail/gopenpgp/v2 v2.4.7 h1:V3xeelvXgJiZXZuPtSSE+uYbtPw4RmbmyPqXDAESPhg=
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.4.7/go.mod h1:ZW1KxHNG6q5LMgFKf9Ap/d2eVYeyGf5+fAUEAjJWtmo=
|
github.com/ProtonMail/gopenpgp/v2 v2.4.7/go.mod h1:ZW1KxHNG6q5LMgFKf9Ap/d2eVYeyGf5+fAUEAjJWtmo=
|
||||||
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
|
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
|
||||||
@ -150,6 +155,7 @@ github.com/emersion/go-imap-quota v0.0.0-20210203125329-619074823f3c h1:khcEdu1y
|
|||||||
github.com/emersion/go-imap-quota v0.0.0-20210203125329-619074823f3c/go.mod h1:iApyhIQBiU4XFyr+3kdJyyGqle82TbQyuP2o+OZHrV0=
|
github.com/emersion/go-imap-quota v0.0.0-20210203125329-619074823f3c/go.mod h1:iApyhIQBiU4XFyr+3kdJyyGqle82TbQyuP2o+OZHrV0=
|
||||||
github.com/emersion/go-imap-unselect v0.0.0-20171113212723-b985794e5f26 h1:FiSb8+XBQQSkcX3ubr+1tAtlRJBYaFmRZqOAweZ9Wy8=
|
github.com/emersion/go-imap-unselect v0.0.0-20171113212723-b985794e5f26 h1:FiSb8+XBQQSkcX3ubr+1tAtlRJBYaFmRZqOAweZ9Wy8=
|
||||||
github.com/emersion/go-imap-unselect v0.0.0-20171113212723-b985794e5f26/go.mod h1:+gnnZx3Mg3MnCzZrv0eZdp5puxXQUgGT/6N6L7ShKfM=
|
github.com/emersion/go-imap-unselect v0.0.0-20171113212723-b985794e5f26/go.mod h1:+gnnZx3Mg3MnCzZrv0eZdp5puxXQUgGT/6N6L7ShKfM=
|
||||||
|
github.com/emersion/go-mbox v1.0.2/go.mod h1:Yp9IVuuOYLEuMv4yjgDHvhb5mHOcYH6x92Oas3QqEZI=
|
||||||
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
|
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
|
||||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
|
||||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||||
@ -175,6 +181,7 @@ github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BMXYYRWT
|
|||||||
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M=
|
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
|
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
|
||||||
|
github.com/getsentry/sentry-go v0.8.0/go.mod h1:kELm/9iCblqUYh+ZRML7PNdCvEuw24wBvJPYyi86cws=
|
||||||
github.com/getsentry/sentry-go v0.12.0 h1:era7g0re5iY13bHSdN/xMkyV+5zZppjRVQhZrXCaEIk=
|
github.com/getsentry/sentry-go v0.12.0 h1:era7g0re5iY13bHSdN/xMkyV+5zZppjRVQhZrXCaEIk=
|
||||||
github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c=
|
github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
@ -231,6 +238,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||||
@ -241,13 +249,15 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
|
|||||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e h1:XWcjeEtTFTOVA9Fs1w7n2XBftk5ib4oZrhzWk0B+3eA=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
@ -328,6 +338,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
|
||||||
github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
|
github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
|
||||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||||
@ -429,6 +440,7 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
|
|||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||||
@ -461,6 +473,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/therecipe/qt v0.0.0-20200701200531-7f61353ee73e h1:G0DQ/TRQyrEZjtLlLwevFjaRiG8eeCMlq9WXQ2OO2bk=
|
github.com/therecipe/qt v0.0.0-20200701200531-7f61353ee73e h1:G0DQ/TRQyrEZjtLlLwevFjaRiG8eeCMlq9WXQ2OO2bk=
|
||||||
github.com/therecipe/qt v0.0.0-20200701200531-7f61353ee73e/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us=
|
github.com/therecipe/qt v0.0.0-20200701200531-7f61353ee73e/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us=
|
||||||
|
github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20200904063919-c0c124a5770d/go.mod h1:7m8PDYDEtEVqfjoUQc2UrFqhG0CDmoVJjRlQxexndFc=
|
||||||
|
github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20200904063919-c0c124a5770d/go.mod h1:mH55Ek7AZcdns5KPp99O0bg+78el64YCYWHiQKrOdt4=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
|
|||||||
@ -31,7 +31,6 @@ import (
|
|||||||
"github.com/ProtonMail/proton-bridge/v2/internal/frontend"
|
"github.com/ProtonMail/proton-bridge/v2/internal/frontend"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/types"
|
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/types"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/imap"
|
"github.com/ProtonMail/proton-bridge/v2/internal/imap"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/rpc"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/smtp"
|
"github.com/ProtonMail/proton-bridge/v2/internal/smtp"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/store"
|
"github.com/ProtonMail/proton-bridge/v2/internal/store"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/store/cache"
|
"github.com/ProtonMail/proton-bridge/v2/internal/store/cache"
|
||||||
@ -139,11 +138,6 @@ func mailLoop(b *base.Base, c *cli.Context) error { //nolint:funlen
|
|||||||
smtpPort, useSSL, tlsConfig, smtpBackend, b.Listener).ListenAndServe()
|
smtpPort, useSSL, tlsConfig, smtpBackend, b.Listener).ListenAndServe()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer b.CrashHandler.HandlePanic()
|
|
||||||
rpc.NewServer().ListenAndServe()
|
|
||||||
}()
|
|
||||||
|
|
||||||
// We want to remove old versions if the app exits successfully.
|
// We want to remove old versions if the app exits successfully.
|
||||||
b.AddTeardownAction(b.Versioner.RemoveOldVersions)
|
b.AddTeardownAction(b.Versioner.RemoveOldVersions)
|
||||||
|
|
||||||
@ -158,7 +152,7 @@ func mailLoop(b *base.Base, c *cli.Context) error { //nolint:funlen
|
|||||||
case c.Bool(flagNonInteractive):
|
case c.Bool(flagNonInteractive):
|
||||||
return <-(make(chan error)) // Block forever.
|
return <-(make(chan error)) // Block forever.
|
||||||
default:
|
default:
|
||||||
frontendMode = "qt"
|
frontendMode = "grpc"
|
||||||
}
|
}
|
||||||
|
|
||||||
f := frontend.New(
|
f := frontend.New(
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import (
|
|||||||
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/config/useragent"
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/useragent"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/cli"
|
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/cli"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/qt"
|
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/grpc"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/types"
|
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/types"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/locations"
|
"github.com/ProtonMail/proton-bridge/v2/internal/locations"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
||||||
@ -39,7 +39,7 @@ type Frontend interface {
|
|||||||
WaitUntilFrontendIsReady()
|
WaitUntilFrontendIsReady()
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns initialized frontend based on `frontendType`, which can be `cli` or `qt`.
|
// New returns initialized frontend based on `frontendType`, which can be `cli` or `grpc`.
|
||||||
func New(
|
func New(
|
||||||
version,
|
version,
|
||||||
buildVersion,
|
buildVersion,
|
||||||
@ -58,10 +58,9 @@ func New(
|
|||||||
) Frontend {
|
) Frontend {
|
||||||
bridgeWrap := types.NewBridgeWrap(bridge)
|
bridgeWrap := types.NewBridgeWrap(bridge)
|
||||||
switch frontendType {
|
switch frontendType {
|
||||||
case "qt":
|
case "grpc":
|
||||||
return qt.New(
|
return grpc.NewService(
|
||||||
version,
|
version,
|
||||||
buildVersion,
|
|
||||||
programName,
|
programName,
|
||||||
showWindowOnStart,
|
showWindowOnStart,
|
||||||
panicHandler,
|
panicHandler,
|
||||||
@ -74,6 +73,7 @@ func New(
|
|||||||
noEncConfirmator,
|
noEncConfirmator,
|
||||||
restarter,
|
restarter,
|
||||||
)
|
)
|
||||||
|
|
||||||
case "cli":
|
case "cli":
|
||||||
return cli.New(
|
return cli.New(
|
||||||
panicHandler,
|
panicHandler,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -20,27 +20,21 @@ syntax = "proto3";
|
|||||||
import "google/protobuf/empty.proto";
|
import "google/protobuf/empty.proto";
|
||||||
import "google/protobuf/wrappers.proto";
|
import "google/protobuf/wrappers.proto";
|
||||||
|
|
||||||
option go_package = "github.com/ProtonMail/proton-bridge/internal/rpc";
|
option go_package = "github.com/ProtonMail/proton-bridge/internal/grpc";
|
||||||
|
|
||||||
package bridgerpc; // ignored by Go, used as namespace name in C++.
|
package grpc; // ignored by Go, used as namespace name in C++.
|
||||||
|
|
||||||
//**********************************************************************************************************************
|
//**********************************************************************************************************************
|
||||||
// Service Declaration
|
// Service Declaration
|
||||||
//**********************************************************************************************************************
|
//**********************************************************************************************************************
|
||||||
service BridgeRpc {
|
service Bridge {
|
||||||
|
|
||||||
// App related calls
|
// App related calls
|
||||||
rpc GetCursorPos (google.protobuf.Empty) returns (PointResponse); // May be unnecessary
|
|
||||||
rpc GuiReady (google.protobuf.Empty) returns (google.protobuf.Empty);
|
rpc GuiReady (google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||||
rpc Quit (google.protobuf.Empty) returns (google.protobuf.Empty);
|
rpc Quit (google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||||
rpc Restart (google.protobuf.Empty) returns (google.protobuf.Empty);
|
rpc Restart (google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||||
rpc SetShowOnStartup(google.protobuf.BoolValue) returns (google.protobuf.Empty);
|
|
||||||
rpc ShowOnStartup(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
rpc ShowOnStartup(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
||||||
rpc SetShowSplashScreen(google.protobuf.BoolValue) returns (google.protobuf.Empty);
|
|
||||||
rpc ShowSplashScreen(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
rpc ShowSplashScreen(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
||||||
rpc SetDockIconVisible(google.protobuf.BoolValue) returns (google.protobuf.Empty);
|
|
||||||
rpc DockIconVisible(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
|
||||||
rpc SetIsFirstGuiStart(google.protobuf.BoolValue) returns (google.protobuf.Empty);
|
|
||||||
rpc IsFirstGuiStart(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
rpc IsFirstGuiStart(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
||||||
rpc SetIsAutostartOn(google.protobuf.BoolValue) returns (google.protobuf.Empty);
|
rpc SetIsAutostartOn(google.protobuf.BoolValue) returns (google.protobuf.Empty);
|
||||||
rpc IsAutostartOn(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
rpc IsAutostartOn(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
||||||
@ -49,13 +43,13 @@ service BridgeRpc {
|
|||||||
rpc GoOs(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
rpc GoOs(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
||||||
rpc TriggerReset(google.protobuf.Empty) returns (google.protobuf.Empty);
|
rpc TriggerReset(google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||||
rpc Version(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
rpc Version(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
||||||
rpc LogPath(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
rpc LogsPath(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
||||||
rpc LicensePath(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
rpc LicensePath(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
||||||
rpc ReleaseNotesLink(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
// rpc ReleaseNotesLink(google.protobuf.Empty) returns (google.protobuf.StringValue); // TODO GODT-1670 Apparently cannot be polled for now, will be sent as update.
|
||||||
rpc LandingPageLink(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
rpc DependencyLicensesLink(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
||||||
|
// rpc LandingPageLink(google.protobuf.Empty) returns (google.protobuf.StringValue); // TODO GODT-1670 Apparently cannot be polled for now, will be sent as update.
|
||||||
rpc SetColorSchemeName(google.protobuf.StringValue) returns (google.protobuf.Empty);
|
rpc SetColorSchemeName(google.protobuf.StringValue) returns (google.protobuf.Empty);
|
||||||
rpc ColorSchemeName(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
rpc ColorSchemeName(google.protobuf.Empty) returns (google.protobuf.StringValue); // TODO Color scheme should probably entirely be managed by the client.
|
||||||
rpc SetCurrentEmailClient(google.protobuf.StringValue) returns (google.protobuf.Empty);
|
|
||||||
rpc CurrentEmailClient(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
rpc CurrentEmailClient(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
||||||
rpc ReportBug(ReportBugRequest) returns (google.protobuf.Empty);
|
rpc ReportBug(ReportBugRequest) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
@ -72,9 +66,7 @@ service BridgeRpc {
|
|||||||
rpc IsAutomaticUpdateOn(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
rpc IsAutomaticUpdateOn(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
||||||
|
|
||||||
// cache
|
// cache
|
||||||
rpc SetIsCacheOnDiskEnabled (google.protobuf.BoolValue) returns (google.protobuf.Empty);
|
|
||||||
rpc IsCacheOnDiskEnabled (google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
rpc IsCacheOnDiskEnabled (google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
||||||
rpc SetDiskCachePath (google.protobuf.StringValue) returns (google.protobuf.Empty);
|
|
||||||
rpc DiskCachePath(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
rpc DiskCachePath(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
||||||
rpc ChangeLocalCache(ChangeLocalCacheRequest) returns (google.protobuf.Empty);
|
rpc ChangeLocalCache(ChangeLocalCacheRequest) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
@ -84,9 +76,7 @@ service BridgeRpc {
|
|||||||
rpc SetUseSslForSmtp(google.protobuf.BoolValue) returns (google.protobuf.Empty);
|
rpc SetUseSslForSmtp(google.protobuf.BoolValue) returns (google.protobuf.Empty);
|
||||||
rpc UseSslForSmtp(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
rpc UseSslForSmtp(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
||||||
rpc Hostname(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
rpc Hostname(google.protobuf.Empty) returns (google.protobuf.StringValue);
|
||||||
rpc SetImapPort(google.protobuf.Int32Value) returns (google.protobuf.Empty);
|
|
||||||
rpc ImapPort(google.protobuf.Empty) returns (google.protobuf.Int32Value);
|
rpc ImapPort(google.protobuf.Empty) returns (google.protobuf.Int32Value);
|
||||||
rpc SetSmtpPort(google.protobuf.Int32Value) returns (google.protobuf.Empty);
|
|
||||||
rpc SmtpPort(google.protobuf.Empty) returns (google.protobuf.Int32Value);
|
rpc SmtpPort(google.protobuf.Empty) returns (google.protobuf.Int32Value);
|
||||||
rpc ChangePorts(ChangePortsRequest) returns (google.protobuf.Empty);
|
rpc ChangePorts(ChangePortsRequest) returns (google.protobuf.Empty);
|
||||||
rpc IsPortFree(google.protobuf.Int32Value) returns (google.protobuf.BoolValue);
|
rpc IsPortFree(google.protobuf.Int32Value) returns (google.protobuf.BoolValue);
|
||||||
@ -98,28 +88,21 @@ service BridgeRpc {
|
|||||||
|
|
||||||
// User & user list
|
// User & user list
|
||||||
rpc GetUserList(google.protobuf.Empty) returns (UserListResponse);
|
rpc GetUserList(google.protobuf.Empty) returns (UserListResponse);
|
||||||
rpc GetUser(google.protobuf.Empty) returns (User);
|
rpc GetUser(google.protobuf.StringValue) returns (User);
|
||||||
rpc SetUserSplitMode(UserSplitModeRequest) returns (google.protobuf.Empty);
|
rpc SetUserSplitMode(UserSplitModeRequest) returns (google.protobuf.Empty);
|
||||||
rpc LogoutUser(google.protobuf.StringValue) returns (google.protobuf.Empty);
|
rpc LogoutUser(google.protobuf.StringValue) returns (google.protobuf.Empty);
|
||||||
rpc RemoveUser(google.protobuf.StringValue) returns (google.protobuf.Empty);
|
rpc RemoveUser(google.protobuf.StringValue) returns (google.protobuf.Empty);
|
||||||
rpc ConfigureUserAppleMail(ConfigureAppleMailRequest) returns (google.protobuf.Empty);
|
rpc ConfigureUserAppleMail(ConfigureAppleMailRequest) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
// Server -> Client event stream
|
// Server -> Client event stream
|
||||||
rpc GetEvents(google.protobuf.Empty) returns (stream StreamEvent);
|
rpc StartEventStream(google.protobuf.Empty) returns (stream StreamEvent); // Keep streaming until StopEventStream is called.
|
||||||
|
rpc StopEventStream(google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
//**********************************************************************************************************************
|
//**********************************************************************************************************************
|
||||||
// RPC calls requests and replies messages
|
// RPC calls requests and replies messages
|
||||||
//**********************************************************************************************************************
|
//**********************************************************************************************************************
|
||||||
|
|
||||||
//**********************************************************
|
|
||||||
// GUI related messages
|
|
||||||
//**********************************************************
|
|
||||||
message PointResponse {
|
|
||||||
int32 x = 1;
|
|
||||||
int32 y = 2;
|
|
||||||
};
|
|
||||||
|
|
||||||
message ReportBugRequest {
|
message ReportBugRequest {
|
||||||
string description = 1;
|
string description = 1;
|
||||||
string address = 2;
|
string address = 2;
|
||||||
@ -214,7 +197,7 @@ message StreamEvent {
|
|||||||
message AppEvent {
|
message AppEvent {
|
||||||
oneof event {
|
oneof event {
|
||||||
InternetStatusEvent internetStatus = 1;
|
InternetStatusEvent internetStatus = 1;
|
||||||
AutostartFinishedEvent autostartFinished = 2;
|
ToggleAutostartFinishedEvent toggleAutostartFinished = 2;
|
||||||
ResetFinishedEvent resetFinished = 3;
|
ResetFinishedEvent resetFinished = 3;
|
||||||
ReportBugFinishedEvent reportBugFinished = 4;
|
ReportBugFinishedEvent reportBugFinished = 4;
|
||||||
ReportBugSuccessEvent reportBugSuccess = 5;
|
ReportBugSuccessEvent reportBugSuccess = 5;
|
||||||
@ -227,7 +210,7 @@ message InternetStatusEvent {
|
|||||||
bool connected = 1;
|
bool connected = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AutostartFinishedEvent {}
|
message ToggleAutostartFinishedEvent {}
|
||||||
message ResetFinishedEvent {}
|
message ResetFinishedEvent {}
|
||||||
message ReportBugFinishedEvent {}
|
message ReportBugFinishedEvent {}
|
||||||
message ReportBugSuccessEvent {}
|
message ReportBugSuccessEvent {}
|
||||||
@ -243,6 +226,7 @@ message LoginEvent {
|
|||||||
LoginTfaRequestedEvent tfaRequested = 2;
|
LoginTfaRequestedEvent tfaRequested = 2;
|
||||||
LoginTwoPasswordsRequestedEvent twoPasswordRequested = 3;
|
LoginTwoPasswordsRequestedEvent twoPasswordRequested = 3;
|
||||||
LoginFinishedEvent finished = 4;
|
LoginFinishedEvent finished = 4;
|
||||||
|
LoginFinishedEvent alreadyLoggedIn = 5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,7 +252,7 @@ message LoginTfaRequestedEvent {
|
|||||||
message LoginTwoPasswordsRequestedEvent {}
|
message LoginTwoPasswordsRequestedEvent {}
|
||||||
|
|
||||||
message LoginFinishedEvent {
|
message LoginFinishedEvent {
|
||||||
bool wasAlreadyLoggedIn = 1;
|
string userID = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//**********************************************************
|
//**********************************************************
|
||||||
@ -320,6 +304,8 @@ message CacheEvent {
|
|||||||
CacheErrorEvent error = 1;
|
CacheErrorEvent error = 1;
|
||||||
CacheLocationChangeSuccessEvent locationChangedSuccess = 2;
|
CacheLocationChangeSuccessEvent locationChangedSuccess = 2;
|
||||||
ChangeLocalCacheFinishedEvent changeLocalCacheFinished = 3;
|
ChangeLocalCacheFinishedEvent changeLocalCacheFinished = 3;
|
||||||
|
IsCacheOnDiskEnabledChanged isCacheOnDiskEnabledChanged = 4;
|
||||||
|
DiskCachePathChanged diskCachePathChanged = 5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,6 +323,16 @@ message CacheLocationChangeSuccessEvent {};
|
|||||||
|
|
||||||
message ChangeLocalCacheFinishedEvent {};
|
message ChangeLocalCacheFinishedEvent {};
|
||||||
|
|
||||||
|
|
||||||
|
message IsCacheOnDiskEnabledChanged {
|
||||||
|
bool enabled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DiskCachePathChanged {
|
||||||
|
string path = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//**********************************************************
|
//**********************************************************
|
||||||
// Mail settings related events
|
// Mail settings related events
|
||||||
//**********************************************************
|
//**********************************************************
|
||||||
@ -393,11 +389,11 @@ message NoActiveKeyForRecipientEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message AddressChangedEvent {
|
message AddressChangedEvent {
|
||||||
string address = 1; // TODO: user event ?
|
string address = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AddressChangedLogoutEvent {
|
message AddressChangedLogoutEvent {
|
||||||
string address = 1; // TODO: user event ?
|
string address = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ApiCertIssueEvent {}
|
message ApiCertIssueEvent {}
|
||||||
@ -419,10 +415,10 @@ message ToggleSplitModeFinishedEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message UserDisconnectedEvent {
|
message UserDisconnectedEvent {
|
||||||
string username = 1; // TODO: isn't it userID ?
|
string username = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UserChangedEvent {
|
message UserChangedEvent {
|
||||||
User user = 1;
|
string userID = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
1955
internal/frontend/grpc/bridge_grpc.pb.go
Normal file
1955
internal/frontend/grpc/bridge_grpc.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
69
internal/frontend/grpc/certs.go
Normal file
69
internal/frontend/grpc/certs.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package grpc
|
||||||
|
|
||||||
|
//goland:noinspection SpellCheckingInspection
|
||||||
|
const (
|
||||||
|
serverCert = `-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC5TCCAc2gAwIBAgIJAMUQK0VGexMsMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
|
||||||
|
BAMMCWxvY2FsaG9zdDAeFw0yMjA2MTQxNjUyNTVaFw0yMjA3MTQxNjUyNTVaMBQx
|
||||||
|
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
||||||
|
ggEBAL6T1JQ0jptq512PBLASpCLFB0px7KIzEml0oMUCkVgUF+2cayrvdBXJZnaO
|
||||||
|
SG+/JPnHDcQ/ecgqkh2Ii6a2x2kWA5KqWiV+bSHp0drXyUGJfM85muLsnrhYwJ83
|
||||||
|
HHtweoUVebRZvHn66KjaH8nBJ+YVWyYbSUhJezcg6nBSEtkW+I/XUHu4S2C7FUc5
|
||||||
|
DXPO3yWWZuZ22OZz70DY3uYE/9COuilotuKdj7XgeKDyKIvRXjPFyqGxwnnp6bXC
|
||||||
|
vWvrQdcxy0wM+vZxew3QtA/Ag9uKJU9owP6noauXw95l49lEVIA5KXVNtdaldVht
|
||||||
|
MO/QoelLZC7h79PK22zbii3x930CAwEAAaM6MDgwFAYDVR0RBA0wC4IJbG9jYWxo
|
||||||
|
b3N0MAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0B
|
||||||
|
AQsFAAOCAQEAW/9PE8dcAN+0C3K96Xd6Y3qOOtQhRw+WlZXhtiqMtlJfTjvuGKs9
|
||||||
|
58xuKcTvU5oobxLv+i5+4gpqLjUZZ9FBnYXZIACNVzq4PEXf+YdzcA+y6RS/rqT4
|
||||||
|
dUjsuYrScAmdXK03Duw3HWYrTp8gsJzIaYGTltUrOn0E4k/TsZb/tZ6z+oH7Fi+p
|
||||||
|
wdsI6Ut6Zwm3Z7WLn5DDk8KvFjHjZkdsCb82SFSAUVrzWo5EtbLIY/7y3A5rGp9D
|
||||||
|
t0AVpuGPo5Vn+MW1WA9HT8lhjz0v5wKGMOBi3VYW+Yx8FWHDpacvbZwVM0MjMSAd
|
||||||
|
M7SXYbNDiLF4LwPLsunoLsW133Ky7s99MA==
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
serverKey = `-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC+k9SUNI6baudd
|
||||||
|
jwSwEqQixQdKceyiMxJpdKDFApFYFBftnGsq73QVyWZ2jkhvvyT5xw3EP3nIKpId
|
||||||
|
iIumtsdpFgOSqlolfm0h6dHa18lBiXzPOZri7J64WMCfNxx7cHqFFXm0Wbx5+uio
|
||||||
|
2h/JwSfmFVsmG0lISXs3IOpwUhLZFviP11B7uEtguxVHOQ1zzt8llmbmdtjmc+9A
|
||||||
|
2N7mBP/QjropaLbinY+14Hig8iiL0V4zxcqhscJ56em1wr1r60HXMctMDPr2cXsN
|
||||||
|
0LQPwIPbiiVPaMD+p6Grl8PeZePZRFSAOSl1TbXWpXVYbTDv0KHpS2Qu4e/Tytts
|
||||||
|
24ot8fd9AgMBAAECggEBAJFkGpOOnRU4s5YO3BavwgS8p9lFnLAJooxNa7GhSd0W
|
||||||
|
R0MBSEkTMU7FvaPI3L5T5xOfpoMHohLxV1Osrk3bt7oWD1e/GtLr5routejtIx8a
|
||||||
|
kttNKTriJhyhqSJOWy5ZGz+YqKbMpxuwLftTnVjAQX4o4MbrnjbFyHjAZdqW4sY2
|
||||||
|
jLulfEdOave6nxaEocmIkoXEjuX90LB+yNG6ncSYM3GV+IyCVw7DsoU4dLd/IRDa
|
||||||
|
4iJVF7tVdAsZqN6/EVYXpGqG0t1HI8ddacHa1qWgCG3kBB+3faxXZcDJdlRrXLUQ
|
||||||
|
4jLH8oEfXOb5YgCwyYzW2EynXEpG5vjsPmsCWJY/mIECgYEA52av81+lui97KLg+
|
||||||
|
T07XtR8zJPMkHnBNfc6ooWku/+0NuQPpUq14vqzRVut9jBHUDP3xSvrPnXsp15ZA
|
||||||
|
/mipLQLNKssTYtk90cyGqLUkrd/NPLFZLXToBfWBlfazdcJQQRIxZ2dTy5MH+HIU
|
||||||
|
Oio3LZi+iDIbdzzSlmL8PaLit20CgYEA0tYsswhq6OaWx25iu4hBMRlt6hr9qGVW
|
||||||
|
jlzCFjBhlh3YtoBti2w2fsJdU+hUpeXU327fhFmdCQFXtf+Om5CSHihmJ+mHj9O1
|
||||||
|
5Jd6zn4o8szdg5je9T4gt7KG6QdXaFJ2aMuq+SxZl1NIE+9qnf/qom4GHHZ/Nj41
|
||||||
|
vwlQu+zS5lECgYAOzSK0DoorPp5CHIbfy8tAap563pKQ394VDgL7UB8Rf7hA/V8P
|
||||||
|
SslOaP9679U4AGvv6M5mXWSqThZ/E71UiJ1Jo8Q72IGE8SBjKxHx+KQ/+vDF0RJD
|
||||||
|
NhchSnLfhMg14BgCEYfXdWSGwQDhg2qHzet5nyuQyqO3HMzbkblQt/qIgQKBgHLv
|
||||||
|
nPiQmy+SHRplO9+93MQ2d6wKwMNfUztSp9/OyjQ62xxKkO1TtbWOobAPVK4Hx+9y
|
||||||
|
EtmkvK3fFIC763M08eMM5PvXHDa1FFCkn6cYMZyDQDLwUINjNhTOdytr/CN76N8i
|
||||||
|
QHeLzN9o4D814mp1y+R2lFBJ7PmWGlilbGS2KxaxAoGAFMsb1MER+eTOUO3z05Di
|
||||||
|
lts4VRWQhq2frd/on6AcTv4idQox1RcOrKWQbRVgeQVY1SkkHhg8lN0jX3W3EfuQ
|
||||||
|
aOfyky04GbLiwO8NRHZMlORWLxlCkrUrb6Va+LQlT0JvpQbqdbu6Ix8NomG9K697
|
||||||
|
aScKmY7bGC0ki2IIdt2YZ5I=
|
||||||
|
-----END PRIVATE KEY-----`
|
||||||
|
)
|
||||||
199
internal/frontend/grpc/event_factory.go
Normal file
199
internal/frontend/grpc/event_factory.go
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package grpc
|
||||||
|
|
||||||
|
func NewInternetStatusEvent(connected bool) *StreamEvent {
|
||||||
|
return appEvent(&AppEvent{Event: &AppEvent_InternetStatus{InternetStatus: &InternetStatusEvent{Connected: connected}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewToggleAutostartFinishedEvent() *StreamEvent {
|
||||||
|
return appEvent(&AppEvent{Event: &AppEvent_ToggleAutostartFinished{ToggleAutostartFinished: &ToggleAutostartFinishedEvent{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewResetFinishedEvent() *StreamEvent {
|
||||||
|
return appEvent(&AppEvent{Event: &AppEvent_ResetFinished{ResetFinished: &ResetFinishedEvent{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReportBugFinishedEvent() *StreamEvent {
|
||||||
|
return appEvent(&AppEvent{Event: &AppEvent_ReportBugFinished{ReportBugFinished: &ReportBugFinishedEvent{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReportBugSuccessEvent() *StreamEvent {
|
||||||
|
return appEvent(&AppEvent{Event: &AppEvent_ReportBugSuccess{ReportBugSuccess: &ReportBugSuccessEvent{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReportBugErrorEvent() *StreamEvent {
|
||||||
|
return appEvent(&AppEvent{Event: &AppEvent_ReportBugError{ReportBugError: &ReportBugErrorEvent{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewShowMainWindowEvent() *StreamEvent {
|
||||||
|
return appEvent(&AppEvent{Event: &AppEvent_ShowMainWindow{ShowMainWindow: &ShowMainWindowEvent{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoginError(err LoginErrorType, message string) *StreamEvent {
|
||||||
|
return loginEvent(&LoginEvent{Event: &LoginEvent_Error{Error: &LoginErrorEvent{Type: err, Message: message}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoginTfaRequestedEvent(username string) *StreamEvent {
|
||||||
|
return loginEvent(&LoginEvent{Event: &LoginEvent_TfaRequested{TfaRequested: &LoginTfaRequestedEvent{Username: username}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoginTwoPasswordsRequestedEvent() *StreamEvent {
|
||||||
|
return loginEvent(&LoginEvent{Event: &LoginEvent_TwoPasswordRequested{}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoginFinishedEvent(userID string) *StreamEvent {
|
||||||
|
return loginEvent(&LoginEvent{Event: &LoginEvent_Finished{Finished: &LoginFinishedEvent{UserID: userID}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoginAlreadyLoggedInEvent(userID string) *StreamEvent {
|
||||||
|
return loginEvent(&LoginEvent{Event: &LoginEvent_AlreadyLoggedIn{AlreadyLoggedIn: &LoginFinishedEvent{UserID: userID}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUpdateErrorEvent(errorType UpdateErrorType) *StreamEvent {
|
||||||
|
return updateEvent(&UpdateEvent{Event: &UpdateEvent_Error{Error: &UpdateErrorEvent{Type: errorType}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUpdateManualReadyEvent(version string) *StreamEvent {
|
||||||
|
return updateEvent(&UpdateEvent{Event: &UpdateEvent_ManualReady{ManualReady: &UpdateManualReadyEvent{Version: version}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUpdateManualRestartNeededEvent() *StreamEvent {
|
||||||
|
return updateEvent(&UpdateEvent{Event: &UpdateEvent_ManualRestartNeeded{ManualRestartNeeded: &UpdateManualRestartNeededEvent{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUpdateForceEvent(version string) *StreamEvent {
|
||||||
|
return updateEvent(&UpdateEvent{Event: &UpdateEvent_Force{Force: &UpdateForceEvent{Version: version}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUpdateSilentRestartNeededEvent() *StreamEvent {
|
||||||
|
return updateEvent(&UpdateEvent{Event: &UpdateEvent_SilentRestartNeeded{SilentRestartNeeded: &UpdateSilentRestartNeeded{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUpdateIsLatestVersionEvent() *StreamEvent {
|
||||||
|
return updateEvent(&UpdateEvent{Event: &UpdateEvent_IsLatestVersion{IsLatestVersion: &UpdateIsLatestVersion{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUpdateCheckFinishedEvent() *StreamEvent {
|
||||||
|
return updateEvent(&UpdateEvent{Event: &UpdateEvent_CheckFinished{CheckFinished: &UpdateCheckFinished{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCacheErrorEvent(err CacheErrorType) *StreamEvent {
|
||||||
|
return cacheEvent(&CacheEvent{Event: &CacheEvent_Error{Error: &CacheErrorEvent{Type: err}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCacheLocationChangeSuccessEvent() *StreamEvent {
|
||||||
|
return cacheEvent(&CacheEvent{Event: &CacheEvent_LocationChangedSuccess{LocationChangedSuccess: &CacheLocationChangeSuccessEvent{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCacheChangeLocalCacheFinishedEvent() *StreamEvent {
|
||||||
|
return cacheEvent(&CacheEvent{Event: &CacheEvent_ChangeLocalCacheFinished{ChangeLocalCacheFinished: &ChangeLocalCacheFinishedEvent{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIsCacheOnDiskEnabledChanged(enabled bool) *StreamEvent {
|
||||||
|
return cacheEvent(&CacheEvent{Event: &CacheEvent_IsCacheOnDiskEnabledChanged{IsCacheOnDiskEnabledChanged: &IsCacheOnDiskEnabledChanged{Enabled: enabled}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDiskCachePathChanged(path string) *StreamEvent {
|
||||||
|
return cacheEvent(&CacheEvent{Event: &CacheEvent_DiskCachePathChanged{DiskCachePathChanged: &DiskCachePathChanged{Path: path}}})
|
||||||
|
}
|
||||||
|
func NewMailSettingsErrorEvent(err MailSettingsErrorType) *StreamEvent {
|
||||||
|
return mailSettingsEvent(&MailSettingsEvent{Event: &MailSettingsEvent_Error{Error: &MailSettingsErrorEvent{Type: err}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMailSettingsUseSslForSmtpFinishedEvent() *StreamEvent { //nolint:revive,stylecheck
|
||||||
|
return mailSettingsEvent(&MailSettingsEvent{Event: &MailSettingsEvent_UseSslForSmtpFinished{UseSslForSmtpFinished: &UseSslForSmtpFinishedEvent{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMailSettingsChangePortFinishedEvent() *StreamEvent {
|
||||||
|
return mailSettingsEvent(&MailSettingsEvent{Event: &MailSettingsEvent_ChangePortsFinished{ChangePortsFinished: &ChangePortsFinishedEvent{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKeychainChangeKeychainFinishedEvent() *StreamEvent {
|
||||||
|
return keychainEvent(&KeychainEvent{Event: &KeychainEvent_ChangeKeychainFinished{ChangeKeychainFinished: &ChangeKeychainFinishedEvent{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKeychainHasNoKeychainEvent() *StreamEvent {
|
||||||
|
return keychainEvent(&KeychainEvent{Event: &KeychainEvent_HasNoKeychain{HasNoKeychain: &HasNoKeychainEvent{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKeychainRebuildKeychainEvent() *StreamEvent {
|
||||||
|
return keychainEvent(&KeychainEvent{Event: &KeychainEvent_RebuildKeychain{RebuildKeychain: &RebuildKeychainEvent{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMailNoActiveKeyForRecipientEvent(email string) *StreamEvent {
|
||||||
|
return mailEvent(&MailEvent{Event: &MailEvent_NoActiveKeyForRecipientEvent{NoActiveKeyForRecipientEvent: &NoActiveKeyForRecipientEvent{Email: email}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMailAddressChangeEvent(email string) *StreamEvent {
|
||||||
|
return mailEvent(&MailEvent{Event: &MailEvent_AddressChanged{AddressChanged: &AddressChangedEvent{Address: email}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMailAddressChangeLogoutEvent(email string) *StreamEvent {
|
||||||
|
return mailEvent(&MailEvent{Event: &MailEvent_AddressChangedLogout{AddressChangedLogout: &AddressChangedLogoutEvent{Address: email}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMailApiCertIssue() *StreamEvent { //nolint:revive,stylecheck
|
||||||
|
return mailEvent(&MailEvent{Event: &MailEvent_ApiCertIssue{ApiCertIssue: &ApiCertIssueEvent{}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserToggleSplitModeFinishedEvent(userID string) *StreamEvent {
|
||||||
|
return userEvent(&UserEvent{Event: &UserEvent_ToggleSplitModeFinished{ToggleSplitModeFinished: &ToggleSplitModeFinishedEvent{UserID: userID}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserDisconnectedEvent(email string) *StreamEvent {
|
||||||
|
return userEvent(&UserEvent{Event: &UserEvent_UserDisconnected{UserDisconnected: &UserDisconnectedEvent{Username: email}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserChangedEvent(userID string) *StreamEvent {
|
||||||
|
return userEvent(&UserEvent{Event: &UserEvent_UserChanged{UserChanged: &UserChangedEvent{UserID: userID}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event category factory functions.
|
||||||
|
|
||||||
|
func appEvent(appEvent *AppEvent) *StreamEvent {
|
||||||
|
return &StreamEvent{Event: &StreamEvent_App{App: appEvent}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loginEvent(event *LoginEvent) *StreamEvent {
|
||||||
|
return &StreamEvent{Event: &StreamEvent_Login{Login: event}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateEvent(event *UpdateEvent) *StreamEvent {
|
||||||
|
return &StreamEvent{Event: &StreamEvent_Update{Update: event}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cacheEvent(event *CacheEvent) *StreamEvent {
|
||||||
|
return &StreamEvent{Event: &StreamEvent_Cache{Cache: event}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mailSettingsEvent(event *MailSettingsEvent) *StreamEvent {
|
||||||
|
return &StreamEvent{Event: &StreamEvent_MailSettings{MailSettings: event}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func keychainEvent(event *KeychainEvent) *StreamEvent {
|
||||||
|
return &StreamEvent{Event: &StreamEvent_Keychain{Keychain: event}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mailEvent(event *MailEvent) *StreamEvent {
|
||||||
|
return &StreamEvent{Event: &StreamEvent_Mail{Mail: event}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func userEvent(event *UserEvent) *StreamEvent {
|
||||||
|
return &StreamEvent{Event: &StreamEvent_User{User: event}}
|
||||||
|
}
|
||||||
329
internal/frontend/grpc/service.go
Normal file
329
internal/frontend/grpc/service.go
Normal file
@ -0,0 +1,329 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package grpc
|
||||||
|
|
||||||
|
//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative bridge.proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/bridge"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/useragent"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/events"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/types"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/locations"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/users"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/pkg/keychain"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/pkg/listener"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Service is the RPC service struct.
|
||||||
|
type Service struct { // nolint:structcheck
|
||||||
|
UnimplementedBridgeServer
|
||||||
|
grpcServer *grpc.Server // the gGRPC server
|
||||||
|
listener net.Listener
|
||||||
|
eventStreamCh chan *StreamEvent
|
||||||
|
eventStreamDoneCh chan struct{}
|
||||||
|
|
||||||
|
programName string
|
||||||
|
programVersion string
|
||||||
|
panicHandler types.PanicHandler
|
||||||
|
locations *locations.Locations
|
||||||
|
settings *settings.Settings
|
||||||
|
eventListener listener.Listener
|
||||||
|
updater types.Updater
|
||||||
|
userAgent *useragent.UserAgent
|
||||||
|
bridge types.Bridger
|
||||||
|
restarter types.Restarter
|
||||||
|
showOnStartup bool
|
||||||
|
authClient pmapi.Client
|
||||||
|
auth *pmapi.Auth
|
||||||
|
password []byte
|
||||||
|
// newVersionInfo updater.VersionInfo // TO-DO GODT-1670 Implement version check
|
||||||
|
log *logrus.Entry
|
||||||
|
initializing sync.WaitGroup
|
||||||
|
initializationDone sync.Once
|
||||||
|
firstTimeAutostart sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewService returns a new instance of the service.
|
||||||
|
func NewService(
|
||||||
|
version,
|
||||||
|
programName string,
|
||||||
|
showOnStartup bool,
|
||||||
|
panicHandler types.PanicHandler,
|
||||||
|
locations *locations.Locations,
|
||||||
|
settings *settings.Settings,
|
||||||
|
eventListener listener.Listener,
|
||||||
|
updater types.Updater,
|
||||||
|
userAgent *useragent.UserAgent,
|
||||||
|
bridge types.Bridger,
|
||||||
|
_ types.NoEncConfirmator,
|
||||||
|
restarter types.Restarter,
|
||||||
|
|
||||||
|
) *Service {
|
||||||
|
s := Service{
|
||||||
|
UnimplementedBridgeServer: UnimplementedBridgeServer{},
|
||||||
|
programName: programName,
|
||||||
|
programVersion: version,
|
||||||
|
panicHandler: panicHandler,
|
||||||
|
locations: locations,
|
||||||
|
settings: settings,
|
||||||
|
eventListener: eventListener,
|
||||||
|
updater: updater,
|
||||||
|
userAgent: userAgent,
|
||||||
|
bridge: bridge,
|
||||||
|
restarter: restarter,
|
||||||
|
showOnStartup: showOnStartup,
|
||||||
|
|
||||||
|
log: logrus.WithField("pkg", "grpc"),
|
||||||
|
initializing: sync.WaitGroup{},
|
||||||
|
initializationDone: sync.Once{},
|
||||||
|
firstTimeAutostart: sync.Once{},
|
||||||
|
}
|
||||||
|
|
||||||
|
s.userAgent.SetPlatform(runtime.GOOS) // TO-DO GODT-1672 In the previous Qt frontend, this routine used QSysInfo::PrettyProductName to return a more accurate description, e.g. "Windows 10" or "MacOS 10.12"
|
||||||
|
|
||||||
|
cert, err := tls.X509KeyPair([]byte(serverCert), []byte(serverKey))
|
||||||
|
if err != nil {
|
||||||
|
s.log.WithError(err).Error("could not create key pair")
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.initAutostart()
|
||||||
|
|
||||||
|
s.grpcServer = grpc.NewServer(grpc.Creds(credentials.NewTLS(&tls.Config{
|
||||||
|
Certificates: []tls.Certificate{cert},
|
||||||
|
MinVersion: tls.VersionTLS13,
|
||||||
|
})))
|
||||||
|
|
||||||
|
RegisterBridgeServer(s.grpcServer, &s)
|
||||||
|
|
||||||
|
s.listener, err = net.Listen("tcp", "127.0.0.1:9292") // Port should be configurable from the command-line.
|
||||||
|
if err != nil {
|
||||||
|
s.log.WithError(err).Error("could not create listener")
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) initAutostart() {
|
||||||
|
// GODT-1507 Windows: autostart needs to be created after Qt is initialized.
|
||||||
|
// GODT-1206: if preferences file says it should be on enable it here.
|
||||||
|
|
||||||
|
// TO-DO GODT-1681 Autostart needs to be properly implement for gRPC approach.
|
||||||
|
|
||||||
|
s.firstTimeAutostart.Do(func() {
|
||||||
|
shouldAutostartBeOn := s.settings.GetBool(settings.AutostartKey)
|
||||||
|
if s.bridge.IsFirstStart() || shouldAutostartBeOn {
|
||||||
|
if err := s.bridge.EnableAutostart(); err != nil {
|
||||||
|
s.log.WithField("prefs", shouldAutostartBeOn).WithError(err).Error("Failed to enable first autostart")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Loop() error {
|
||||||
|
defer func() {
|
||||||
|
s.settings.SetBool(settings.FirstStartGUIKey, false)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer s.panicHandler.HandlePanic()
|
||||||
|
s.watchEvents()
|
||||||
|
}()
|
||||||
|
|
||||||
|
err := s.grpcServer.Serve(s.listener)
|
||||||
|
if err != nil {
|
||||||
|
s.log.WithError(err).Error("error serving RPC")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// frontend interface functions TODO GODT-1670 Implement
|
||||||
|
|
||||||
|
func (s *Service) NotifyManualUpdate( /* update */ _ updater.VersionInfo /*canInstall */, _ bool) {}
|
||||||
|
func (s *Service) SetVersion( /* update */ updater.VersionInfo) {}
|
||||||
|
func (s *Service) NotifySilentUpdateInstalled() {}
|
||||||
|
func (s *Service) NotifySilentUpdateError(error) {}
|
||||||
|
func (s *Service) WaitUntilFrontendIsReady() {}
|
||||||
|
|
||||||
|
func (s *Service) watchEvents() { // nolint:funlen
|
||||||
|
if s.bridge.HasError(bridge.ErrLocalCacheUnavailable) {
|
||||||
|
_ = s.SendEvent(NewCacheErrorEvent(CacheErrorType_CACHE_UNAVAILABLE_ERROR))
|
||||||
|
}
|
||||||
|
|
||||||
|
errorCh := s.eventListener.ProvideChannel(events.ErrorEvent)
|
||||||
|
credentialsErrorCh := s.eventListener.ProvideChannel(events.CredentialsErrorEvent)
|
||||||
|
noActiveKeyForRecipientCh := s.eventListener.ProvideChannel(events.NoActiveKeyForRecipientEvent)
|
||||||
|
internetConnChangedCh := s.eventListener.ProvideChannel(events.InternetConnChangedEvent)
|
||||||
|
secondInstanceCh := s.eventListener.ProvideChannel(events.SecondInstanceEvent)
|
||||||
|
restartBridgeCh := s.eventListener.ProvideChannel(events.RestartBridgeEvent)
|
||||||
|
addressChangedCh := s.eventListener.ProvideChannel(events.AddressChangedEvent)
|
||||||
|
addressChangedLogoutCh := s.eventListener.ProvideChannel(events.AddressChangedLogoutEvent)
|
||||||
|
logoutCh := s.eventListener.ProvideChannel(events.LogoutEvent)
|
||||||
|
updateApplicationCh := s.eventListener.ProvideChannel(events.UpgradeApplicationEvent)
|
||||||
|
userChangedCh := s.eventListener.ProvideChannel(events.UserRefreshEvent)
|
||||||
|
certIssue := s.eventListener.ProvideChannel(events.TLSCertIssue)
|
||||||
|
|
||||||
|
// we forward events to the GUI/frontend via the gRPC event stream.
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case errorDetails := <-errorCh:
|
||||||
|
if strings.Contains(errorDetails, "IMAP failed") {
|
||||||
|
_ = s.SendEvent(NewMailSettingsErrorEvent(MailSettingsErrorType_IMAP_PORT_ISSUE))
|
||||||
|
}
|
||||||
|
if strings.Contains(errorDetails, "SMTP failed") {
|
||||||
|
_ = s.SendEvent(NewMailSettingsErrorEvent(MailSettingsErrorType_SMTP_PORT_ISSUE))
|
||||||
|
}
|
||||||
|
case reason := <-credentialsErrorCh:
|
||||||
|
if reason == keychain.ErrMacKeychainRebuild.Error() {
|
||||||
|
_ = s.SendEvent(NewKeychainRebuildKeychainEvent())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_ = s.SendEvent(NewKeychainHasNoKeychainEvent())
|
||||||
|
case email := <-noActiveKeyForRecipientCh:
|
||||||
|
_ = s.SendEvent(NewMailNoActiveKeyForRecipientEvent(email))
|
||||||
|
case stat := <-internetConnChangedCh:
|
||||||
|
if stat == events.InternetOff {
|
||||||
|
_ = s.SendEvent(NewInternetStatusEvent(false))
|
||||||
|
}
|
||||||
|
if stat == events.InternetOn {
|
||||||
|
_ = s.SendEvent(NewInternetStatusEvent(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-secondInstanceCh:
|
||||||
|
_ = s.SendEvent(NewShowMainWindowEvent())
|
||||||
|
case <-restartBridgeCh:
|
||||||
|
s.restart()
|
||||||
|
case address := <-addressChangedCh:
|
||||||
|
_ = s.SendEvent(NewMailAddressChangeEvent(address))
|
||||||
|
case address := <-addressChangedLogoutCh:
|
||||||
|
_ = s.SendEvent(NewMailAddressChangeLogoutEvent(address))
|
||||||
|
case userID := <-logoutCh:
|
||||||
|
user, err := s.bridge.GetUser(userID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = s.SendEvent(NewUserDisconnectedEvent(user.Username()))
|
||||||
|
case <-updateApplicationCh:
|
||||||
|
s.updateForce()
|
||||||
|
case userID := <-userChangedCh:
|
||||||
|
_ = s.SendEvent(NewUserChangedEvent(userID))
|
||||||
|
case <-certIssue:
|
||||||
|
_ = s.SendEvent(NewMailApiCertIssue())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) loginAbort() {
|
||||||
|
s.loginClean()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) loginClean() {
|
||||||
|
s.auth = nil
|
||||||
|
s.authClient = nil
|
||||||
|
for i := range s.password {
|
||||||
|
s.password[i] = '\x00'
|
||||||
|
}
|
||||||
|
s.password = s.password[0:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) finishLogin() {
|
||||||
|
defer s.loginClean()
|
||||||
|
|
||||||
|
if len(s.password) == 0 || s.auth == nil || s.authClient == nil {
|
||||||
|
s.log.
|
||||||
|
WithField("hasPass", len(s.password) != 0).
|
||||||
|
WithField("hasAuth", s.auth != nil).
|
||||||
|
WithField("hasClient", s.authClient != nil).
|
||||||
|
Error("Finish login: authentication incomplete")
|
||||||
|
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_TWO_PASSWORDS_ABORT, "Missing authentication, try again."))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
done := make(chan string)
|
||||||
|
s.eventListener.Add(events.UserChangeDone, done)
|
||||||
|
defer s.eventListener.Remove(events.UserChangeDone, done)
|
||||||
|
|
||||||
|
user, err := s.bridge.FinishLogin(s.authClient, s.auth, s.password)
|
||||||
|
|
||||||
|
if err != nil && err != users.ErrUserAlreadyConnected {
|
||||||
|
s.log.WithError(err).Errorf("Finish login failed")
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_TWO_PASSWORDS_ABORT, err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The user changed should be triggered by FinishLogin, but it is not
|
||||||
|
// guaranteed when this is going to happen. Therefor we should wait
|
||||||
|
// until we receive the signal from userChanged function.
|
||||||
|
s.waitForUserChangeDone(done, user.ID())
|
||||||
|
|
||||||
|
s.log.WithField("userID", user.ID()).Debug("Login finished")
|
||||||
|
_ = s.SendEvent(NewLoginFinishedEvent(user.ID()))
|
||||||
|
|
||||||
|
if err == users.ErrUserAlreadyConnected {
|
||||||
|
s.log.WithError(err).Error("User already logged in")
|
||||||
|
_ = s.SendEvent(NewLoginAlreadyLoggedInEvent(user.ID()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) waitForUserChangeDone(done <-chan string, userID string) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case changedID := <-done:
|
||||||
|
if changedID == userID {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-time.After(2 * time.Second):
|
||||||
|
s.log.WithField("ID", userID).Warning("Login finished but user not added within 2 seconds")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) restart() {
|
||||||
|
s.log.Error("Restart is not implemented") // TO-DO GODT-1671 implement restart.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) checkUpdate() {
|
||||||
|
s.log.Error("checkUpdate is not implemented") // TO-DO GODT-1670 implement update check.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) updateForce() {
|
||||||
|
s.log.Error("updateForce is not implemented") // TO-DO GODT-1670 implement update.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) checkUpdateAndNotify() {
|
||||||
|
s.log.Error("checkUpdateAndNotify is not implemented") // TO-DO GODT-1670 implement update check.
|
||||||
|
}
|
||||||
543
internal/frontend/grpc/service_methods.go
Normal file
543
internal/frontend/grpc/service_methods.go
Normal file
@ -0,0 +1,543 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver/v3"
|
||||||
|
"github.com/ProtonMail/proton-bridge/pkg/ports"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/bridge"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/theme"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/pkg/keychain"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrNotImplemented = status.Errorf(codes.Unimplemented, "Not implemented")
|
||||||
|
|
||||||
|
// GuiReady implement the GuiReady gRPC service call.
|
||||||
|
func (s *Service) GuiReady(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
s.log.Info("GuiReady")
|
||||||
|
// Note nothing to be done. old Qt frontend had a sync.one
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quit implement the Quit gRPC service call.
|
||||||
|
func (s *Service) Quit(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
s.log.Info("Quit")
|
||||||
|
var err error
|
||||||
|
if s.eventStreamCh != nil {
|
||||||
|
if _, err = s.StopEventStream(ctx, empty); err != nil {
|
||||||
|
s.log.WithError(err).Error("Quit failed.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following call is launched as a goroutine, as it will wait for current calls to end, including this one.
|
||||||
|
go func() { s.grpcServer.GracefulStop() }()
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart implement the Restart gRPC service call.
|
||||||
|
func (s *Service) Restart(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
s.log.Info("Restart") // TO-DO-GODT-1671 handle restart.
|
||||||
|
|
||||||
|
s.restart()
|
||||||
|
|
||||||
|
return nil, ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ShowOnStartup(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) {
|
||||||
|
s.log.Info("ShowOnStartup")
|
||||||
|
|
||||||
|
return wrapperspb.Bool(s.showOnStartup), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ShowSplashScreen(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) {
|
||||||
|
s.log.Info("ShowSplashScreen")
|
||||||
|
|
||||||
|
if s.bridge.IsFirstStart() {
|
||||||
|
return wrapperspb.Bool(false), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ver, err := semver.NewVersion(s.bridge.GetLastVersion())
|
||||||
|
if err != nil {
|
||||||
|
s.log.WithError(err).WithField("last", s.bridge.GetLastVersion()).Debug("Cannot parse last version")
|
||||||
|
return wrapperspb.Bool(false), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current splash screen contains update on rebranding. Therefore, it
|
||||||
|
// should be shown only if the last used version was less than 2.2.0.
|
||||||
|
return wrapperspb.Bool(ver.LessThan(semver.MustParse("2.2.0"))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) IsFirstGuiStart(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) {
|
||||||
|
s.log.Info("IsFirstGuiStart")
|
||||||
|
|
||||||
|
return wrapperspb.Bool(s.settings.GetBool(settings.FirstStartGUIKey)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) SetIsAutostartOn(_ context.Context, isOn *wrapperspb.BoolValue) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("show", isOn.Value).Info("SetIsAutostartOn")
|
||||||
|
|
||||||
|
defer func() { _ = s.SendEvent(NewToggleAutostartFinishedEvent()) }()
|
||||||
|
|
||||||
|
if isOn.Value == s.bridge.IsAutostartEnabled() {
|
||||||
|
s.initAutostart()
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if isOn.Value {
|
||||||
|
err = s.bridge.EnableAutostart()
|
||||||
|
} else {
|
||||||
|
err = s.bridge.DisableAutostart()
|
||||||
|
}
|
||||||
|
|
||||||
|
s.initAutostart()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
s.log.WithField("makeItEnabled", isOn.Value).WithError(err).Error("Autostart change failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) IsAutostartOn(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) {
|
||||||
|
s.log.Info("IsAutostartOn")
|
||||||
|
|
||||||
|
return wrapperspb.Bool(s.bridge.IsAutostartEnabled()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) SetIsBetaEnabled(_ context.Context, isEnabled *wrapperspb.BoolValue) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("isEnabled", isEnabled.Value).Info("SetIsBetaEnabled")
|
||||||
|
|
||||||
|
channel := updater.StableChannel
|
||||||
|
if isEnabled.Value {
|
||||||
|
channel = updater.EarlyChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
s.bridge.SetUpdateChannel(channel)
|
||||||
|
s.checkUpdate()
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) IsBetaEnabled(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) {
|
||||||
|
s.log.Info("IsBetaEnabled")
|
||||||
|
|
||||||
|
return wrapperspb.Bool(s.bridge.GetUpdateChannel() == updater.EarlyChannel), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GoOs(context.Context, *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||||
|
s.log.Info("GoOs") // TO-DO We can probably get rid of this and use QSysInfo::product name
|
||||||
|
return wrapperspb.String(runtime.GOOS), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) TriggerReset(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
s.log.Info("TriggerReset")
|
||||||
|
return nil, ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Version(context.Context, *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||||
|
s.log.Info("Version")
|
||||||
|
return nil, ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) LogsPath(context.Context, *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||||
|
s.log.Info("LogsPath")
|
||||||
|
path, err := s.locations.ProvideLogsPath()
|
||||||
|
if err != nil {
|
||||||
|
s.log.WithError(err).Error("Cannot determine logs path")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return wrapperspb.String(path), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) LicensePath(context.Context, *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||||
|
s.log.Info("LicensePath")
|
||||||
|
return wrapperspb.String(s.locations.GetLicenseFilePath()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) DependencyLicensesLink(context.Context, *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||||
|
return wrapperspb.String(s.locations.GetDependencyLicensesLink()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) SetColorSchemeName(_ context.Context, name *wrapperspb.StringValue) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("ColorSchemeName", name.Value).Info("SetColorSchemeName")
|
||||||
|
|
||||||
|
if !theme.IsAvailable(theme.Theme(name.Value)) {
|
||||||
|
s.log.WithField("scheme", name.Value).Warn("Color scheme not available")
|
||||||
|
return nil, status.Error(codes.NotFound, "Color scheme not available")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.settings.Set(settings.ColorScheme, name.Value)
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ColorSchemeName(context.Context, *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||||
|
s.log.Info("ColorSchemeName")
|
||||||
|
|
||||||
|
current := s.settings.Get(settings.ColorScheme)
|
||||||
|
if !theme.IsAvailable(theme.Theme(current)) {
|
||||||
|
current = string(theme.DefaultTheme())
|
||||||
|
s.settings.Set(settings.ColorScheme, current)
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapperspb.String(current), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) CurrentEmailClient(context.Context, *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||||
|
s.log.Info("CurrentEmailClient")
|
||||||
|
|
||||||
|
return wrapperspb.String(s.userAgent.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ReportBug(_ context.Context, report *ReportBugRequest) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("description", report.Description).
|
||||||
|
WithField("address", report.Address).
|
||||||
|
WithField("emailClient", report.EmailClient).
|
||||||
|
WithField("includeLogs", report.IncludeLogs).
|
||||||
|
Info("ReportBug")
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Login(_ context.Context, login *LoginRequest) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("username", login.Username).Info("Login")
|
||||||
|
go func() {
|
||||||
|
defer s.panicHandler.HandlePanic()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
s.password, err = base64.StdEncoding.DecodeString(login.Password)
|
||||||
|
if err != nil {
|
||||||
|
s.log.WithError(err).Error("Cannot decode password")
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, "Cannot decode password"))
|
||||||
|
s.loginClean()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.authClient, s.auth, err = s.bridge.Login(login.Username, s.password)
|
||||||
|
if err != nil {
|
||||||
|
if err == pmapi.ErrPasswordWrong {
|
||||||
|
// Remove error message since it is hardcoded in QML.
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, ""))
|
||||||
|
s.loginClean()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err == pmapi.ErrPaidPlanRequired {
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_FREE_USER, ""))
|
||||||
|
s.loginClean()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, err.Error()))
|
||||||
|
s.loginClean()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.auth.HasTwoFactor() {
|
||||||
|
_ = s.SendEvent(NewLoginTfaRequestedEvent(login.Username))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if s.auth.HasMailboxPassword() {
|
||||||
|
_ = s.SendEvent(NewLoginTwoPasswordsRequestedEvent())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.finishLogin()
|
||||||
|
}()
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Login2FA(_ context.Context, login *LoginRequest) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("username", login.Username).Info("Login2FA")
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer s.panicHandler.HandlePanic()
|
||||||
|
|
||||||
|
if s.auth == nil || s.authClient == nil {
|
||||||
|
s.log.Errorf("Login 2FA: authethication incomplete %p %p", s.auth, s.authClient)
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_TFA_ABORT, "Missing authentication, try again."))
|
||||||
|
s.loginClean()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
twoFA, err := base64.StdEncoding.DecodeString(login.Password)
|
||||||
|
if err != nil {
|
||||||
|
s.log.WithError(err).Error("Cannot decode 2fa code")
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, "Cannot decode 2fa code"))
|
||||||
|
s.loginClean()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.authClient.Auth2FA(context.Background(), string(twoFA))
|
||||||
|
if err == pmapi.ErrBad2FACodeTryAgain {
|
||||||
|
s.log.Warn("Login 2FA: retry 2fa")
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_TFA_ERROR, ""))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == pmapi.ErrBad2FACode {
|
||||||
|
s.log.Warn("Login 2FA: abort 2fa")
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_TFA_ABORT, ""))
|
||||||
|
s.loginClean()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
s.log.WithError(err).Warn("Login 2FA: failed.")
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_TFA_ABORT, err.Error()))
|
||||||
|
s.loginClean()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.auth.HasMailboxPassword() {
|
||||||
|
_ = s.SendEvent(NewLoginTwoPasswordsRequestedEvent())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.finishLogin()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Login2Passwords(_ context.Context, login *LoginRequest) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("username", login.Username).Info("Login2Passwords")
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer s.panicHandler.HandlePanic()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
s.password, err = base64.StdEncoding.DecodeString(login.Password)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
s.log.WithError(err).Error("Cannot decode mbox password")
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, "Cannot decode mbox password"))
|
||||||
|
s.loginClean()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.finishLogin()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) LoginAbort(_ context.Context, loginAbort *LoginAbortRequest) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("username", loginAbort.Username).Info("LoginAbort")
|
||||||
|
go func() {
|
||||||
|
defer s.panicHandler.HandlePanic()
|
||||||
|
|
||||||
|
s.loginAbort()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) CheckUpdate(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
s.log.Info("CheckUpdate")
|
||||||
|
// TO-DO GODT-1670 Implement update check
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) InstallUpdate(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
s.log.Info("InstallUpdate")
|
||||||
|
// TO-DO GODT-1670 Implement update install
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) SetIsAutomaticUpdateOn(_ context.Context, isOn *wrapperspb.BoolValue) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("isOn", isOn.Value).Info("SetIsAutomaticUpdateOn")
|
||||||
|
|
||||||
|
currentlyOn := s.settings.GetBool(settings.AutoUpdateKey)
|
||||||
|
if currentlyOn == isOn.Value {
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s.settings.SetBool(settings.AutoUpdateKey, isOn.Value)
|
||||||
|
s.checkUpdateAndNotify()
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) IsAutomaticUpdateOn(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) {
|
||||||
|
s.log.Info("IsAutomaticUpdateOn")
|
||||||
|
|
||||||
|
return wrapperspb.Bool(s.settings.GetBool(settings.AutoUpdateKey)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) IsCacheOnDiskEnabled(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) {
|
||||||
|
s.log.Info("IsCacheOnDiskEnabled")
|
||||||
|
return wrapperspb.Bool(s.settings.GetBool(settings.CacheEnabledKey)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) DiskCachePath(context.Context, *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||||
|
s.log.Info("DiskCachePath")
|
||||||
|
return wrapperspb.String(s.settings.Get(settings.CacheLocationKey)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ChangeLocalCache(_ context.Context, change *ChangeLocalCacheRequest) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("enableDiskCache", change.EnableDiskCache).
|
||||||
|
WithField("diskCachePath", change.DiskCachePath).
|
||||||
|
Info("DiskCachePath")
|
||||||
|
|
||||||
|
defer func() { _ = s.SendEvent(NewCacheChangeLocalCacheFinishedEvent()) }()
|
||||||
|
defer func() { _ = s.SendEvent(NewIsCacheOnDiskEnabledChanged(s.settings.GetBool(settings.CacheEnabledKey))) }()
|
||||||
|
defer func() { _ = s.SendEvent(NewDiskCachePathChanged(s.settings.Get(settings.CacheCompressionKey))) }()
|
||||||
|
|
||||||
|
if change.EnableDiskCache != s.settings.GetBool(settings.CacheEnabledKey) {
|
||||||
|
if change.EnableDiskCache {
|
||||||
|
if err := s.bridge.EnableCache(); err != nil {
|
||||||
|
s.log.WithError(err).Error("Cannot enable disk cache")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := s.bridge.DisableCache(); err != nil {
|
||||||
|
s.log.WithError(err).Error("Cannot disable disk cache")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
path := change.DiskCachePath
|
||||||
|
//goland:noinspection GoBoolExpressions
|
||||||
|
if (runtime.GOOS == "windows") && (path[0] == '/') {
|
||||||
|
path = path[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if change.EnableDiskCache && path != s.settings.Get(settings.CacheLocationKey) {
|
||||||
|
if err := s.bridge.MigrateCache(s.settings.Get(settings.CacheLocationKey), path); err != nil {
|
||||||
|
s.log.WithError(err).Error("The local cache location could not be changed.")
|
||||||
|
_ = s.SendEvent(NewCacheErrorEvent(CacheErrorType_CACHE_CANT_MOVE_ERROR))
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
s.settings.Set(settings.CacheLocationKey, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = s.SendEvent(NewCacheLocationChangeSuccessEvent())
|
||||||
|
s.restart()
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) SetIsDoHEnabled(_ context.Context, isEnabled *wrapperspb.BoolValue) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("isEnabled", isEnabled.Value).Info("SetIsDohEnabled")
|
||||||
|
|
||||||
|
s.bridge.SetProxyAllowed(isEnabled.Value)
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) IsDoHEnabled(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) {
|
||||||
|
s.log.Info("IsDohEnabled")
|
||||||
|
|
||||||
|
return wrapperspb.Bool(s.bridge.GetProxyAllowed()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) SetUseSslForSmtp(_ context.Context, useSsl *wrapperspb.BoolValue) (*emptypb.Empty, error) { //nolint:revive,stylecheck
|
||||||
|
s.log.WithField("useSsl", useSsl.Value).Info("SetUseSslForSmtp")
|
||||||
|
|
||||||
|
if s.settings.GetBool(settings.SMTPSSLKey) == useSsl.Value {
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() { _ = s.SendEvent(NewMailSettingsUseSslForSmtpFinishedEvent()) }()
|
||||||
|
|
||||||
|
s.settings.SetBool(settings.SMTPSSLKey, useSsl.Value)
|
||||||
|
s.restart()
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) UseSslForSmtp(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) { //nolint:revive,stylecheck
|
||||||
|
s.log.Info("UseSslForSmtp")
|
||||||
|
|
||||||
|
return wrapperspb.Bool(s.settings.GetBool(settings.SMTPSSLKey)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Hostname(context.Context, *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||||
|
s.log.Info("Hostname")
|
||||||
|
|
||||||
|
return wrapperspb.String(bridge.Host), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ImapPort(context.Context, *emptypb.Empty) (*wrapperspb.Int32Value, error) {
|
||||||
|
s.log.Info("ImapPort")
|
||||||
|
|
||||||
|
return wrapperspb.Int32(int32(s.settings.GetInt(settings.IMAPPortKey))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) SmtpPort(context.Context, *emptypb.Empty) (*wrapperspb.Int32Value, error) { //nolint:revive,stylecheck
|
||||||
|
s.log.Info("SmtpPort")
|
||||||
|
|
||||||
|
return wrapperspb.Int32(int32(s.settings.GetInt(settings.SMTPPortKey))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ChangePorts(_ context.Context, ports *ChangePortsRequest) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("imapPort", ports.ImapPort).WithField("smtpPort", ports.SmtpPort).Info("ChangePorts")
|
||||||
|
|
||||||
|
defer func() { _ = s.SendEvent(NewMailSettingsChangePortFinishedEvent()) }()
|
||||||
|
|
||||||
|
s.settings.SetInt(settings.IMAPPortKey, int(ports.ImapPort))
|
||||||
|
s.settings.SetInt(settings.SMTPPortKey, int(ports.SmtpPort))
|
||||||
|
|
||||||
|
s.restart()
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) IsPortFree(_ context.Context, port *wrapperspb.Int32Value) (*wrapperspb.BoolValue, error) {
|
||||||
|
s.log.Info("IsPortFree")
|
||||||
|
return wrapperspb.Bool(ports.IsPortFree(int(port.Value))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) AvailableKeychains(context.Context, *emptypb.Empty) (*AvailableKeychainsResponse, error) {
|
||||||
|
s.log.Info("AvailableKeychains")
|
||||||
|
|
||||||
|
keychains := make([]string, 0, len(keychain.Helpers))
|
||||||
|
for chain := range keychain.Helpers {
|
||||||
|
keychains = append(keychains, chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &AvailableKeychainsResponse{Keychains: keychains}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) SetCurrentKeychain(_ context.Context, keychain *wrapperspb.StringValue) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("keychain", keychain.Value).Info("SetCurrentKeyChain") // we do not check validity.
|
||||||
|
defer func() { _ = s.SendEvent(NewKeychainChangeKeychainFinishedEvent()) }()
|
||||||
|
|
||||||
|
if s.bridge.GetKeychainApp() == keychain.Value {
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s.bridge.SetKeychainApp(keychain.Value)
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) CurrentKeychain(context.Context, *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||||
|
s.log.Info("CurrentKeychain")
|
||||||
|
|
||||||
|
return wrapperspb.String(s.bridge.GetKeychainApp()), nil
|
||||||
|
}
|
||||||
151
internal/frontend/grpc/service_stream.go
Normal file
151
internal/frontend/grpc/service_stream.go
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StartEventStream implement the gRPC server->Client event stream.
|
||||||
|
func (s *Service) StartEventStream(_ *emptypb.Empty, server Bridge_StartEventStreamServer) error {
|
||||||
|
s.log.Info("Starting Event stream")
|
||||||
|
|
||||||
|
if s.eventStreamCh != nil {
|
||||||
|
return status.Errorf(codes.AlreadyExists, "the service is already streaming") // TO-DO GODT-1667 decide if we want to kill the existing stream.
|
||||||
|
}
|
||||||
|
|
||||||
|
s.eventStreamCh = make(chan *StreamEvent)
|
||||||
|
s.eventStreamDoneCh = make(chan struct{})
|
||||||
|
|
||||||
|
// TO-DO GODT-1667 We should have a safer we to close this channel? What if an event occur while we are closing?
|
||||||
|
defer func() {
|
||||||
|
close(s.eventStreamCh)
|
||||||
|
s.eventStreamCh = nil
|
||||||
|
close(s.eventStreamDoneCh)
|
||||||
|
s.eventStreamDoneCh = nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.eventStreamDoneCh:
|
||||||
|
s.log.Info("Stop Event stream")
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case event := <-s.eventStreamCh:
|
||||||
|
s.log.WithField("event", event).Info("Sending event")
|
||||||
|
if err := server.Send(event); err != nil {
|
||||||
|
s.log.Info("Stop Event stream")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopEventStream stops the event stream.
|
||||||
|
func (s *Service) StopEventStream(_ context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
if s.eventStreamCh == nil {
|
||||||
|
return nil, status.Errorf(codes.NotFound, "The service is not streaming")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.eventStreamDoneCh <- struct{}{}
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendEvent sends an event to the via the gRPC event stream.
|
||||||
|
func (s *Service) SendEvent(event *StreamEvent) error {
|
||||||
|
if s.eventStreamCh == nil {
|
||||||
|
return errors.New("gRPC service is not streaming")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.eventStreamCh <- event
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartEventTest sends all the known event via gRPC.
|
||||||
|
func (s *Service) StartEventTest() error { //nolint:funlen
|
||||||
|
const dummyAddress = "dummy@proton.me"
|
||||||
|
events := []*StreamEvent{
|
||||||
|
// app
|
||||||
|
NewInternetStatusEvent(true),
|
||||||
|
NewToggleAutostartFinishedEvent(),
|
||||||
|
NewResetFinishedEvent(),
|
||||||
|
NewReportBugFinishedEvent(),
|
||||||
|
NewReportBugSuccessEvent(),
|
||||||
|
NewReportBugErrorEvent(),
|
||||||
|
NewShowMainWindowEvent(),
|
||||||
|
|
||||||
|
// login
|
||||||
|
NewLoginError(LoginErrorType_FREE_USER, "error"),
|
||||||
|
NewLoginTfaRequestedEvent(dummyAddress),
|
||||||
|
NewLoginTwoPasswordsRequestedEvent(),
|
||||||
|
NewLoginFinishedEvent("userID"),
|
||||||
|
NewLoginAlreadyLoggedInEvent("userID"),
|
||||||
|
|
||||||
|
// update
|
||||||
|
NewUpdateErrorEvent(UpdateErrorType_UPDATE_SILENT_ERROR),
|
||||||
|
NewUpdateManualReadyEvent("2.0"),
|
||||||
|
NewUpdateManualRestartNeededEvent(),
|
||||||
|
NewUpdateForceEvent("2.0"),
|
||||||
|
NewUpdateSilentRestartNeededEvent(),
|
||||||
|
NewUpdateIsLatestVersionEvent(),
|
||||||
|
NewUpdateCheckFinishedEvent(),
|
||||||
|
|
||||||
|
// cache
|
||||||
|
NewCacheErrorEvent(CacheErrorType_CACHE_UNAVAILABLE_ERROR),
|
||||||
|
NewCacheLocationChangeSuccessEvent(),
|
||||||
|
NewCacheChangeLocalCacheFinishedEvent(),
|
||||||
|
NewIsCacheOnDiskEnabledChanged(true),
|
||||||
|
NewDiskCachePathChanged("/dummy/path"),
|
||||||
|
|
||||||
|
// mail settings
|
||||||
|
NewMailSettingsErrorEvent(MailSettingsErrorType_IMAP_PORT_ISSUE),
|
||||||
|
NewMailSettingsUseSslForSmtpFinishedEvent(),
|
||||||
|
NewMailSettingsChangePortFinishedEvent(),
|
||||||
|
|
||||||
|
// keychain
|
||||||
|
NewKeychainChangeKeychainFinishedEvent(),
|
||||||
|
NewKeychainHasNoKeychainEvent(),
|
||||||
|
NewKeychainRebuildKeychainEvent(),
|
||||||
|
|
||||||
|
// mail
|
||||||
|
NewMailNoActiveKeyForRecipientEvent(dummyAddress),
|
||||||
|
NewMailAddressChangeEvent(dummyAddress),
|
||||||
|
NewMailAddressChangeLogoutEvent(dummyAddress),
|
||||||
|
NewMailApiCertIssue(),
|
||||||
|
|
||||||
|
// user
|
||||||
|
NewUserToggleSplitModeFinishedEvent("userID"),
|
||||||
|
NewUserDisconnectedEvent("username"),
|
||||||
|
NewUserChangedEvent("userID"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, event := range events {
|
||||||
|
if err := s.SendEvent(event); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
132
internal/frontend/grpc/service_user.go
Normal file
132
internal/frontend/grpc/service_user.go
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/clientconfig"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Service) GetUserList(context.Context, *emptypb.Empty) (*UserListResponse, error) {
|
||||||
|
s.log.Info("GetUserList")
|
||||||
|
|
||||||
|
users := s.bridge.GetUsers()
|
||||||
|
|
||||||
|
userList := make([]*User, len(users))
|
||||||
|
for i, user := range users {
|
||||||
|
userList[i] = grpcUserFromBridge(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no active accounts.
|
||||||
|
if len(userList) == 0 {
|
||||||
|
s.log.Info("No active accounts")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &UserListResponse{Users: userList}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetUser(_ context.Context, userID *wrapperspb.StringValue) (*User, error) {
|
||||||
|
s.log.WithField("userID", userID).Info("GetUser")
|
||||||
|
|
||||||
|
user, err := s.bridge.GetUser(userID.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.NotFound, "user not found %v", userID.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return grpcUserFromBridge(user), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) SetUserSplitMode(_ context.Context, splitMode *UserSplitModeRequest) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("UserID", splitMode.UserID).WithField("Active", splitMode.Active).Info("SetUserSplitMode")
|
||||||
|
|
||||||
|
user, err := s.bridge.GetUser(splitMode.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.NotFound, "user not found %v", splitMode.UserID)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer s.panicHandler.HandlePanic()
|
||||||
|
defer func() { _ = s.SendEvent(NewUserToggleSplitModeFinishedEvent(splitMode.UserID)) }()
|
||||||
|
if splitMode.Active == user.IsCombinedAddressMode() {
|
||||||
|
_ = user.SwitchAddressMode() // check for errors
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) LogoutUser(_ context.Context, userID *wrapperspb.StringValue) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("UserID", userID.Value).Info("LogoutUser")
|
||||||
|
|
||||||
|
user, err := s.bridge.GetUser(userID.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.NotFound, "user not found %v", userID.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer s.panicHandler.HandlePanic()
|
||||||
|
_ = user.Logout()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) RemoveUser(_ context.Context, userID *wrapperspb.StringValue) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("UserID", userID.Value).Info("RemoveUser")
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer s.panicHandler.HandlePanic()
|
||||||
|
|
||||||
|
// remove preferences
|
||||||
|
if err := s.bridge.DeleteUser(userID.Value, false); err != nil {
|
||||||
|
s.log.WithError(err).Error("Failed to remove user")
|
||||||
|
// notification
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ConfigureUserAppleMail(_ context.Context, request *ConfigureAppleMailRequest) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("UserID", request.UserID).WithField("Address", request.Address).Info("ConfigureUserAppleMail")
|
||||||
|
|
||||||
|
user, err := s.bridge.GetUser(request.UserID)
|
||||||
|
if err != nil {
|
||||||
|
s.log.WithField("userID", request.UserID).Error("Cannot configure AppleMail for user")
|
||||||
|
return nil, status.Error(codes.NotFound, "Cannot configure AppleMail for user")
|
||||||
|
}
|
||||||
|
|
||||||
|
needRestart, err := clientconfig.ConfigureAppleMail(user, request.Address, s.settings)
|
||||||
|
if err != nil {
|
||||||
|
s.log.WithError(err).Error("Apple Mail config failed")
|
||||||
|
return nil, status.Error(codes.Internal, "Apple Mail config failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if needRestart {
|
||||||
|
// There is delay needed for external window to open
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
s.restart()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
@ -15,29 +15,15 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//go:build build_qt
|
package grpc
|
||||||
// +build build_qt
|
|
||||||
|
|
||||||
package qt
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/therecipe/qt/core"
|
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/types"
|
||||||
"github.com/therecipe/qt/gui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// getCursorPos returns current mouse position to be able to use in QML
|
|
||||||
func getCursorPos() *core.QPoint {
|
|
||||||
return gui.QCursor_Pos()
|
|
||||||
}
|
|
||||||
|
|
||||||
// newQByteArrayFromString is a wrapper for new QByteArray from string.
|
|
||||||
func newQByteArrayFromString(name string) *core.QByteArray {
|
|
||||||
return core.NewQByteArray2(name, len(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
reMultiSpaces = regexp.MustCompile(`\s{2,}`)
|
reMultiSpaces = regexp.MustCompile(`\s{2,}`)
|
||||||
reStartWithSymbol = regexp.MustCompile(`^[.,/#!$@%^&*;:{}=\-_` + "`" + `~()]`)
|
reStartWithSymbol = regexp.MustCompile(`^[.,/#!$@%^&*;:{}=\-_` + "`" + `~()]`)
|
||||||
@ -69,3 +55,19 @@ func getInitials(fullName string) string {
|
|||||||
}
|
}
|
||||||
return strings.ToUpper(initials)
|
return strings.ToUpper(initials)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// grpcUserFromBridge converts a bridge user to a gRPC user.
|
||||||
|
func grpcUserFromBridge(user types.User) *User {
|
||||||
|
return &User{
|
||||||
|
Id: user.ID(),
|
||||||
|
Username: user.Username(),
|
||||||
|
AvatarText: getInitials(user.Username()),
|
||||||
|
LoggedIn: user.IsConnected(),
|
||||||
|
SplitMode: user.IsCombinedAddressMode(),
|
||||||
|
SetupGuideSeen: true, // users listed have already seen the setup guide.
|
||||||
|
UsedBytes: user.UsedBytes(),
|
||||||
|
TotalBytes: user.TotalBytes(),
|
||||||
|
Password: user.GetBridgePassword(),
|
||||||
|
Addresses: user.GetAddresses(),
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,162 +0,0 @@
|
|||||||
// Copyright (c) 2022 Proton AG
|
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//go:build build_qt
|
|
||||||
// +build build_qt
|
|
||||||
|
|
||||||
// Package qt provides communication between Qt/QML frontend and Go backend
|
|
||||||
package qt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/config/useragent"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/types"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/locations"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/listener"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/therecipe/qt/core"
|
|
||||||
"github.com/therecipe/qt/qml"
|
|
||||||
"github.com/therecipe/qt/widgets"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FrontendQt struct {
|
|
||||||
programName, programVersion string
|
|
||||||
|
|
||||||
panicHandler types.PanicHandler
|
|
||||||
locations *locations.Locations
|
|
||||||
settings *settings.Settings
|
|
||||||
eventListener listener.Listener
|
|
||||||
updater types.Updater
|
|
||||||
userAgent *useragent.UserAgent
|
|
||||||
bridge types.Bridger
|
|
||||||
noEncConfirmator types.NoEncConfirmator
|
|
||||||
restarter types.Restarter
|
|
||||||
showOnStartup bool
|
|
||||||
|
|
||||||
authClient pmapi.Client
|
|
||||||
auth *pmapi.Auth
|
|
||||||
password []byte
|
|
||||||
|
|
||||||
newVersionInfo updater.VersionInfo
|
|
||||||
|
|
||||||
log *logrus.Entry
|
|
||||||
initializing sync.WaitGroup
|
|
||||||
initializationDone sync.Once
|
|
||||||
firstTimeAutostart sync.Once
|
|
||||||
|
|
||||||
app *widgets.QApplication
|
|
||||||
engine *qml.QQmlEngine
|
|
||||||
qml *QMLBackend
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(
|
|
||||||
version,
|
|
||||||
buildVersion,
|
|
||||||
programName string,
|
|
||||||
showWindowOnStart bool,
|
|
||||||
panicHandler types.PanicHandler,
|
|
||||||
locations *locations.Locations,
|
|
||||||
settings *settings.Settings,
|
|
||||||
eventListener listener.Listener,
|
|
||||||
updater types.Updater,
|
|
||||||
userAgent *useragent.UserAgent,
|
|
||||||
bridge types.Bridger,
|
|
||||||
_ types.NoEncConfirmator,
|
|
||||||
restarter types.Restarter,
|
|
||||||
) *FrontendQt {
|
|
||||||
userAgent.SetPlatform(core.QSysInfo_PrettyProductName())
|
|
||||||
|
|
||||||
f := &FrontendQt{
|
|
||||||
programName: programName,
|
|
||||||
programVersion: version,
|
|
||||||
log: logrus.WithField("pkg", "frontend/qt"),
|
|
||||||
|
|
||||||
panicHandler: panicHandler,
|
|
||||||
locations: locations,
|
|
||||||
settings: settings,
|
|
||||||
eventListener: eventListener,
|
|
||||||
updater: updater,
|
|
||||||
userAgent: userAgent,
|
|
||||||
bridge: bridge,
|
|
||||||
restarter: restarter,
|
|
||||||
showOnStartup: showWindowOnStart,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initializing.Done is only called sync.Once. Please keep the increment
|
|
||||||
// set to 1
|
|
||||||
f.initializing.Add(1)
|
|
||||||
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) Loop() error {
|
|
||||||
err := f.initiateQtApplication()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer f.panicHandler.HandlePanic()
|
|
||||||
f.watchEvents()
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Set whether this is the first time GUI starts.
|
|
||||||
f.qml.SetIsFirstGUIStart(f.settings.GetBool(settings.FirstStartGUIKey))
|
|
||||||
defer func() {
|
|
||||||
f.settings.SetBool(settings.FirstStartGUIKey, false)
|
|
||||||
}()
|
|
||||||
|
|
||||||
if ret := f.app.Exec(); ret != 0 {
|
|
||||||
err := fmt.Errorf("Event loop ended with return value: %v", ret)
|
|
||||||
f.log.Warn("App exec", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) NotifyManualUpdate(version updater.VersionInfo, canInstall bool) {
|
|
||||||
if canInstall {
|
|
||||||
f.qml.UpdateManualReady(version.Version.String())
|
|
||||||
} else {
|
|
||||||
f.qml.UpdateManualError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) SetVersion(version updater.VersionInfo) {
|
|
||||||
f.newVersionInfo = version
|
|
||||||
f.qml.SetReleaseNotesLink(core.NewQUrl3(version.ReleaseNotesPage, core.QUrl__TolerantMode))
|
|
||||||
f.qml.SetLandingPageLink(core.NewQUrl3(version.LandingPage, core.QUrl__TolerantMode))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) NotifySilentUpdateInstalled() {
|
|
||||||
f.qml.UpdateSilentRestartNeeded()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) NotifySilentUpdateError(err error) {
|
|
||||||
f.log.WithError(err).Warn("In-app update failed, asking for manual.")
|
|
||||||
f.qml.UpdateManualError()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) WaitUntilFrontendIsReady() {
|
|
||||||
f.initializing.Wait()
|
|
||||||
}
|
|
||||||
@ -1,104 +0,0 @@
|
|||||||
// Copyright (c) 2022 Proton AG
|
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//go:build build_qt
|
|
||||||
// +build build_qt
|
|
||||||
|
|
||||||
// Package qt provides communication between Qt/QML frontend and Go backend
|
|
||||||
package qt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/bridge"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/events"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/keychain"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (f *FrontendQt) watchEvents() {
|
|
||||||
f.WaitUntilFrontendIsReady()
|
|
||||||
|
|
||||||
// First we check bridge global errors for any error that should be shown on GUI.
|
|
||||||
if f.bridge.HasError(bridge.ErrLocalCacheUnavailable) {
|
|
||||||
f.qml.CacheUnavailable()
|
|
||||||
}
|
|
||||||
|
|
||||||
errorCh := f.eventListener.ProvideChannel(events.ErrorEvent)
|
|
||||||
credentialsErrorCh := f.eventListener.ProvideChannel(events.CredentialsErrorEvent)
|
|
||||||
noActiveKeyForRecipientCh := f.eventListener.ProvideChannel(events.NoActiveKeyForRecipientEvent)
|
|
||||||
internetConnChangedCh := f.eventListener.ProvideChannel(events.InternetConnChangedEvent)
|
|
||||||
secondInstanceCh := f.eventListener.ProvideChannel(events.SecondInstanceEvent)
|
|
||||||
restartBridgeCh := f.eventListener.ProvideChannel(events.RestartBridgeEvent)
|
|
||||||
addressChangedCh := f.eventListener.ProvideChannel(events.AddressChangedEvent)
|
|
||||||
addressChangedLogoutCh := f.eventListener.ProvideChannel(events.AddressChangedLogoutEvent)
|
|
||||||
logoutCh := f.eventListener.ProvideChannel(events.LogoutEvent)
|
|
||||||
updateApplicationCh := f.eventListener.ProvideChannel(events.UpgradeApplicationEvent)
|
|
||||||
userChangedCh := f.eventListener.ProvideChannel(events.UserRefreshEvent)
|
|
||||||
certIssue := f.eventListener.ProvideChannel(events.TLSCertIssue)
|
|
||||||
|
|
||||||
// This loop is executed outside main Qt application thread. In order
|
|
||||||
// to make sure that all signals are propagated correctly to QML we
|
|
||||||
// must call QMLBackend signals to apply any changes to GUI. The
|
|
||||||
// signals will make sure the changes are executed in main Qt app
|
|
||||||
// thread.
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case errorDetails := <-errorCh:
|
|
||||||
if strings.Contains(errorDetails, "IMAP failed") {
|
|
||||||
f.qml.PortIssueIMAP()
|
|
||||||
}
|
|
||||||
if strings.Contains(errorDetails, "SMTP failed") {
|
|
||||||
f.qml.PortIssueSMTP()
|
|
||||||
}
|
|
||||||
case reason := <-credentialsErrorCh:
|
|
||||||
if reason == keychain.ErrMacKeychainRebuild.Error() {
|
|
||||||
f.qml.NotifyRebuildKeychain()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
f.qml.NotifyHasNoKeychain()
|
|
||||||
case email := <-noActiveKeyForRecipientCh:
|
|
||||||
f.qml.NoActiveKeyForRecipient(email)
|
|
||||||
case stat := <-internetConnChangedCh:
|
|
||||||
if stat == events.InternetOff {
|
|
||||||
f.qml.InternetOff()
|
|
||||||
}
|
|
||||||
if stat == events.InternetOn {
|
|
||||||
f.qml.InternetOn()
|
|
||||||
}
|
|
||||||
case <-secondInstanceCh:
|
|
||||||
f.qml.ShowMainWindow()
|
|
||||||
case <-restartBridgeCh:
|
|
||||||
f.restart()
|
|
||||||
case address := <-addressChangedCh:
|
|
||||||
f.qml.AddressChanged(address)
|
|
||||||
case address := <-addressChangedLogoutCh:
|
|
||||||
f.qml.AddressChangedLogout(address)
|
|
||||||
case userID := <-logoutCh:
|
|
||||||
user, err := f.bridge.GetUser(userID)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
f.qml.UserDisconnected(user.Username())
|
|
||||||
case <-updateApplicationCh:
|
|
||||||
f.updateForce()
|
|
||||||
case userID := <-userChangedCh:
|
|
||||||
f.qml.UserChanged(userID)
|
|
||||||
case <-certIssue:
|
|
||||||
f.qml.ApiCertIssue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
// Copyright (c) 2022 Proton AG
|
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//go:build build_qt
|
|
||||||
// +build build_qt
|
|
||||||
|
|
||||||
package qt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/therecipe/qt/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (f *FrontendQt) setVersion() {
|
|
||||||
f.qml.SetVersion(f.programVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) setLogsPath() {
|
|
||||||
path, err := f.locations.ProvideLogsPath()
|
|
||||||
if err != nil {
|
|
||||||
f.log.WithError(err).Error("Cannot update path folder")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
f.qml.SetLogsPath(core.QUrl_FromLocalFile(path))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) setLicensePath() {
|
|
||||||
f.qml.SetLicensePath(core.QUrl_FromLocalFile(f.locations.GetLicenseFilePath()))
|
|
||||||
f.qml.SetDependencyLicensesLink(core.NewQUrl3(f.locations.GetDependencyLicensesLink(), core.QUrl__TolerantMode))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) setCurrentEmailClient() {
|
|
||||||
f.qml.SetCurrentEmailClient(f.userAgent.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) reportBug(description, address, emailClient string, includeLogs bool) {
|
|
||||||
defer f.qml.ReportBugFinished()
|
|
||||||
|
|
||||||
if err := f.bridge.ReportBug(
|
|
||||||
core.QSysInfo_ProductType(),
|
|
||||||
core.QSysInfo_PrettyProductName(),
|
|
||||||
description,
|
|
||||||
address,
|
|
||||||
address,
|
|
||||||
emailClient,
|
|
||||||
includeLogs,
|
|
||||||
); err != nil {
|
|
||||||
f.log.WithError(err).Error("Failed to report bug")
|
|
||||||
f.qml.BugReportSendError()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
f.qml.BugReportSendSuccess()
|
|
||||||
}
|
|
||||||
@ -1,120 +0,0 @@
|
|||||||
// Copyright (c) 2022 Proton AG
|
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//go:build build_qt
|
|
||||||
// +build build_qt
|
|
||||||
|
|
||||||
package qt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
|
||||||
qmlLog "github.com/ProtonMail/proton-bridge/v2/internal/frontend/qt/log"
|
|
||||||
"github.com/therecipe/qt/core"
|
|
||||||
"github.com/therecipe/qt/qml"
|
|
||||||
"github.com/therecipe/qt/quickcontrols2"
|
|
||||||
"github.com/therecipe/qt/widgets"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (f *FrontendQt) initiateQtApplication() error {
|
|
||||||
qmlLog.InstallMessageHandler()
|
|
||||||
|
|
||||||
f.app = widgets.NewQApplication(len(os.Args), os.Args)
|
|
||||||
|
|
||||||
if os.Getenv("QSG_INFO") != "" && os.Getenv("QSG_INFO") != "0" {
|
|
||||||
core.QLoggingCategory_SetFilterRules("qt.scenegraph.general=true")
|
|
||||||
}
|
|
||||||
|
|
||||||
core.QCoreApplication_SetApplicationName(f.programName)
|
|
||||||
core.QCoreApplication_SetApplicationVersion(f.programVersion)
|
|
||||||
core.QCoreApplication_SetOrganizationName("Proton AG")
|
|
||||||
core.QCoreApplication_SetOrganizationDomain("proton.ch")
|
|
||||||
|
|
||||||
// High DPI scaling for windows.
|
|
||||||
core.QCoreApplication_SetAttribute(core.Qt__AA_EnableHighDpiScaling, false)
|
|
||||||
|
|
||||||
// Use software OpenGL to avoid dedicated GPU on darwin. It cause no
|
|
||||||
// problems on linux, but it can cause initializaion issues on windows
|
|
||||||
// for some specific GPU / driver combination.
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
core.QCoreApplication_SetAttribute(core.Qt__AA_UseSoftwareOpenGL, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bridge runs background, no window is needed to be opened.
|
|
||||||
f.app.SetQuitOnLastWindowClosed(false)
|
|
||||||
|
|
||||||
// QML Engine and path
|
|
||||||
f.engine = qml.NewQQmlEngine(f.app)
|
|
||||||
rootComponent := qml.NewQQmlComponent2(f.engine, f.engine)
|
|
||||||
|
|
||||||
f.qml = NewQMLBackend(f.engine)
|
|
||||||
f.qml.setup(f)
|
|
||||||
|
|
||||||
f.engine.AddImportPath("qrc:/qml/")
|
|
||||||
f.engine.AddPluginPath("qrc:/qml/")
|
|
||||||
|
|
||||||
// Add style: if colorScheme / style is forgotten we should fallback to
|
|
||||||
// default style and should be Proton
|
|
||||||
quickcontrols2.QQuickStyle_AddStylePath("qrc:/qml/")
|
|
||||||
quickcontrols2.QQuickStyle_SetStyle("Proton")
|
|
||||||
|
|
||||||
// Before loading a component we should load translations.
|
|
||||||
// See https://github.com/qt/qtdeclarative/blob/bedef212a74a62452ed31d7f65536a6c67881fc4/src/qml/qml/qqmlapplicationengine.cpp#L69 as example.
|
|
||||||
|
|
||||||
rootComponent.LoadUrl(core.NewQUrl3("qrc:/qml/Bridge.qml", 0))
|
|
||||||
|
|
||||||
if rootComponent.Status() != qml.QQmlComponent__Ready {
|
|
||||||
return errors.New("QML not loaded properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instead of creating component right away we use BeginCreate to stop right before binding evaluation.
|
|
||||||
// That is needed to set backend property so all bindings will be calculated properly.
|
|
||||||
rootObject := rootComponent.BeginCreate(f.engine.RootContext())
|
|
||||||
// Check QML is loaded properly.
|
|
||||||
if rootObject == nil {
|
|
||||||
return errors.New("QML not created properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
rootObject.SetProperty("backend", f.qml.ToVariant())
|
|
||||||
rootComponent.CompleteCreate()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) setShowSplashScreen() {
|
|
||||||
f.qml.SetShowSplashScreen(false)
|
|
||||||
|
|
||||||
// Splash screen should not be shown to new users or after factory reset.
|
|
||||||
if f.bridge.IsFirstStart() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ver, err := semver.NewVersion(f.bridge.GetLastVersion())
|
|
||||||
if err != nil {
|
|
||||||
f.log.WithError(err).WithField("last", f.bridge.GetLastVersion()).Debug("Cannot parse last version")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current splash screen contains update on rebranding. Therefore, it
|
|
||||||
// should be shown only if the last used version was less than 2.2.0.
|
|
||||||
if ver.LessThan(semver.MustParse("2.2.0")) {
|
|
||||||
f.qml.SetShowSplashScreen(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,82 +0,0 @@
|
|||||||
// Copyright (c) 2022 Proton AG
|
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//go:build !build_qt
|
|
||||||
// +build !build_qt
|
|
||||||
|
|
||||||
package qt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/config/useragent"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/types"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/locations"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/listener"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
var log = logrus.WithField("pkg", "frontend-nogui") //nolint:gochecknoglobals
|
|
||||||
|
|
||||||
type FrontendHeadless struct{}
|
|
||||||
|
|
||||||
func New(
|
|
||||||
version,
|
|
||||||
buildVersion,
|
|
||||||
programName string,
|
|
||||||
showWindowOnStart bool,
|
|
||||||
panicHandler types.PanicHandler,
|
|
||||||
locations *locations.Locations,
|
|
||||||
settings *settings.Settings,
|
|
||||||
eventListener listener.Listener,
|
|
||||||
updater types.Updater,
|
|
||||||
userAgent *useragent.UserAgent,
|
|
||||||
bridge types.Bridger,
|
|
||||||
noEncConfirmator types.NoEncConfirmator,
|
|
||||||
restarter types.Restarter,
|
|
||||||
) *FrontendHeadless {
|
|
||||||
return &FrontendHeadless{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *FrontendHeadless) Loop() error {
|
|
||||||
log.Info("Check status on localhost:8081")
|
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
fmt.Fprintf(w, "Bridge is running")
|
|
||||||
})
|
|
||||||
return http.ListenAndServe(":8081", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *FrontendHeadless) NotifyManualUpdate(update updater.VersionInfo, canInstall bool) {
|
|
||||||
// NOTE: Save the update somewhere so that it can be installed when user chooses "install now".
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *FrontendHeadless) WaitUntilFrontendIsReady() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *FrontendHeadless) SetVersion(update updater.VersionInfo) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *FrontendHeadless) NotifySilentUpdateInstalled() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *FrontendHeadless) NotifySilentUpdateError(err error) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *FrontendHeadless) InstanceExistAlert() {}
|
|
||||||
@ -1,218 +0,0 @@
|
|||||||
// Copyright (c) 2022 Proton AG
|
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//go:build build_qt
|
|
||||||
// +build build_qt
|
|
||||||
|
|
||||||
package qt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/clientconfig"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/theme"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/keychain"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/ports"
|
|
||||||
"github.com/therecipe/qt/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (f *FrontendQt) setIsDiskCacheEnabled() {
|
|
||||||
f.qml.SetIsDiskCacheEnabled(f.settings.GetBool(settings.CacheEnabledKey))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) setDiskCachePath() {
|
|
||||||
f.qml.SetDiskCachePath(core.QUrl_FromLocalFile(f.settings.Get(settings.CacheLocationKey)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) changeLocalCache(enableDiskCache bool, diskCachePath *core.QUrl) {
|
|
||||||
defer f.qml.ChangeLocalCacheFinished()
|
|
||||||
defer f.setIsDiskCacheEnabled()
|
|
||||||
defer f.setDiskCachePath()
|
|
||||||
|
|
||||||
if enableDiskCache != f.settings.GetBool(settings.CacheEnabledKey) {
|
|
||||||
if enableDiskCache {
|
|
||||||
if err := f.bridge.EnableCache(); err != nil {
|
|
||||||
f.log.WithError(err).Error("Cannot enable disk cache")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := f.bridge.DisableCache(); err != nil {
|
|
||||||
f.log.WithError(err).Error("Cannot disable disk cache")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_diskCachePath := diskCachePath.Path(core.QUrl__FullyDecoded)
|
|
||||||
if (runtime.GOOS == "windows") && (_diskCachePath[0] == '/') {
|
|
||||||
_diskCachePath = _diskCachePath[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if enableDiskCache && _diskCachePath != f.settings.Get(settings.CacheLocationKey) {
|
|
||||||
if err := f.bridge.MigrateCache(f.settings.Get(settings.CacheLocationKey), _diskCachePath); err != nil {
|
|
||||||
f.log.WithError(err).Error("The local cache location could not be changed.")
|
|
||||||
f.qml.CacheCantMove()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
f.settings.Set(settings.CacheLocationKey, _diskCachePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.qml.CacheLocationChangeSuccess()
|
|
||||||
f.restart()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) setIsAutostartOn() {
|
|
||||||
// GODT-1507 Windows: autostart needs to be created after Qt is initialized.
|
|
||||||
// GODT-1206: if preferences file says it should be on enable it here.
|
|
||||||
f.firstTimeAutostart.Do(func() {
|
|
||||||
shouldAutostartBeOn := f.settings.GetBool(settings.AutostartKey)
|
|
||||||
if f.bridge.IsFirstStart() || shouldAutostartBeOn {
|
|
||||||
if err := f.bridge.EnableAutostart(); err != nil {
|
|
||||||
f.log.WithField("prefs", shouldAutostartBeOn).WithError(err).Error("Failed to enable first autostart")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
f.qml.SetIsAutostartOn(f.bridge.IsAutostartEnabled())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) toggleAutostart(makeItEnabled bool) {
|
|
||||||
defer f.qml.ToggleAutostartFinished()
|
|
||||||
if makeItEnabled == f.bridge.IsAutostartEnabled() {
|
|
||||||
f.setIsAutostartOn()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
if makeItEnabled {
|
|
||||||
err = f.bridge.EnableAutostart()
|
|
||||||
} else {
|
|
||||||
err = f.bridge.DisableAutostart()
|
|
||||||
}
|
|
||||||
f.setIsAutostartOn()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
f.log.
|
|
||||||
WithField("makeItEnabled", makeItEnabled).
|
|
||||||
WithField("isEnabled", f.qml.IsAutostartOn()).
|
|
||||||
WithError(err).
|
|
||||||
Error("Autostart change failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) toggleDoH(makeItEnabled bool) {
|
|
||||||
f.bridge.SetProxyAllowed(makeItEnabled)
|
|
||||||
f.qml.SetIsDoHEnabled(f.bridge.GetProxyAllowed())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) toggleUseSSLforSMTP(makeItEnabled bool) {
|
|
||||||
if f.settings.GetBool(settings.SMTPSSLKey) == makeItEnabled {
|
|
||||||
f.qml.SetUseSSLforSMTP(makeItEnabled)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
f.settings.SetBool(settings.SMTPSSLKey, makeItEnabled)
|
|
||||||
f.restart()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) changePorts(imapPort, smtpPort int) {
|
|
||||||
defer f.qml.ChangePortFinished()
|
|
||||||
f.settings.SetInt(settings.IMAPPortKey, imapPort)
|
|
||||||
f.settings.SetInt(settings.SMTPPortKey, smtpPort)
|
|
||||||
f.restart()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) isPortFree(port int) bool {
|
|
||||||
return ports.IsPortFree(port)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) configureAppleMail(userID, address string) {
|
|
||||||
user, err := f.bridge.GetUser(userID)
|
|
||||||
if err != nil {
|
|
||||||
f.log.WithField("userID", userID).Error("Cannot configure AppleMail for user")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
needRestart, err := clientconfig.ConfigureAppleMail(user, address, f.settings)
|
|
||||||
if err != nil {
|
|
||||||
f.log.WithError(err).Error("Apple Mail config failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
if needRestart {
|
|
||||||
// There is delay needed for external window to open
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
f.restart()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) triggerReset() {
|
|
||||||
defer f.qml.ResetFinished()
|
|
||||||
f.bridge.FactoryReset()
|
|
||||||
f.restart()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) setKeychain() {
|
|
||||||
availableKeychain := []string{}
|
|
||||||
for chain := range keychain.Helpers {
|
|
||||||
availableKeychain = append(availableKeychain, chain)
|
|
||||||
}
|
|
||||||
f.qml.SetAvailableKeychain(availableKeychain)
|
|
||||||
f.qml.SetCurrentKeychain(f.bridge.GetKeychainApp())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) changeKeychain(wantKeychain string) {
|
|
||||||
defer f.qml.ChangeKeychainFinished()
|
|
||||||
|
|
||||||
if f.bridge.GetKeychainApp() == wantKeychain {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
f.bridge.SetKeychainApp(wantKeychain)
|
|
||||||
f.restart()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) restart() {
|
|
||||||
f.log.Info("Restarting bridge")
|
|
||||||
f.restarter.SetToRestart()
|
|
||||||
f.app.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) quit() {
|
|
||||||
f.log.Warn("Your wish is my command.. I quit!")
|
|
||||||
f.app.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) guiReady() {
|
|
||||||
f.initializationDone.Do(f.initializing.Done)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) setColorScheme() {
|
|
||||||
current := f.settings.Get(settings.ColorScheme)
|
|
||||||
if !theme.IsAvailable(theme.Theme(current)) {
|
|
||||||
current = string(theme.DefaultTheme())
|
|
||||||
f.settings.Set(settings.ColorScheme, current)
|
|
||||||
}
|
|
||||||
f.qml.SetColorSchemeName(current)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) changeColorScheme(newScheme string) {
|
|
||||||
if !theme.IsAvailable(theme.Theme(newScheme)) {
|
|
||||||
f.log.WithField("scheme", newScheme).Warn("Color scheme not available")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
f.settings.Set(settings.ColorScheme, newScheme)
|
|
||||||
f.setColorScheme()
|
|
||||||
}
|
|
||||||
@ -1,148 +0,0 @@
|
|||||||
// Copyright (c) 2022 Proton AG
|
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//go:build build_qt
|
|
||||||
// +build build_qt
|
|
||||||
|
|
||||||
package qt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
var checkingUpdates = sync.Mutex{}
|
|
||||||
|
|
||||||
func (f *FrontendQt) checkUpdates() error {
|
|
||||||
version, err := f.updater.Check()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
f.SetVersion(version)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) checkUpdatesAndNotify(isRequestFromUser bool) {
|
|
||||||
checkingUpdates.Lock()
|
|
||||||
defer checkingUpdates.Unlock()
|
|
||||||
defer f.qml.CheckUpdatesFinished()
|
|
||||||
|
|
||||||
if err := f.checkUpdates(); err != nil {
|
|
||||||
f.log.WithError(err).Error("An error occurred while checking updates")
|
|
||||||
if isRequestFromUser {
|
|
||||||
f.qml.UpdateManualError()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !f.updater.IsUpdateApplicable(f.newVersionInfo) {
|
|
||||||
f.log.Debug("No need to update")
|
|
||||||
if isRequestFromUser {
|
|
||||||
f.qml.UpdateIsLatestVersion()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !f.updater.CanInstall(f.newVersionInfo) {
|
|
||||||
f.log.Debug("A manual update is required")
|
|
||||||
f.qml.UpdateManualError()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.settings.GetBool(settings.AutoUpdateKey) {
|
|
||||||
// NOOP will update eventually
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if isRequestFromUser {
|
|
||||||
f.qml.UpdateManualReady(f.newVersionInfo.Version.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) updateForce() {
|
|
||||||
checkingUpdates.Lock()
|
|
||||||
defer checkingUpdates.Unlock()
|
|
||||||
|
|
||||||
version := ""
|
|
||||||
if err := f.checkUpdates(); err == nil {
|
|
||||||
version = f.newVersionInfo.Version.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
f.qml.UpdateForce(version)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) setIsAutomaticUpdateOn() {
|
|
||||||
f.qml.SetIsAutomaticUpdateOn(f.settings.GetBool(settings.AutoUpdateKey))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) toggleAutomaticUpdate(makeItEnabled bool) {
|
|
||||||
f.qml.SetIsAutomaticUpdateOn(makeItEnabled)
|
|
||||||
isEnabled := f.settings.GetBool(settings.AutoUpdateKey)
|
|
||||||
if makeItEnabled == isEnabled {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
f.settings.SetBool(settings.AutoUpdateKey, makeItEnabled)
|
|
||||||
|
|
||||||
f.checkUpdatesAndNotify(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) setIsBetaEnabled() {
|
|
||||||
channel := f.bridge.GetUpdateChannel()
|
|
||||||
f.qml.SetIsBetaEnabled(channel == updater.EarlyChannel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) toggleBeta(makeItEnabled bool) {
|
|
||||||
channel := updater.StableChannel
|
|
||||||
if makeItEnabled {
|
|
||||||
channel = updater.EarlyChannel
|
|
||||||
}
|
|
||||||
|
|
||||||
f.bridge.SetUpdateChannel(channel)
|
|
||||||
|
|
||||||
f.setIsBetaEnabled()
|
|
||||||
|
|
||||||
// Immediately check the updates to set the correct landing page link.
|
|
||||||
f.checkUpdates()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) installUpdate() {
|
|
||||||
checkingUpdates.Lock()
|
|
||||||
defer checkingUpdates.Unlock()
|
|
||||||
|
|
||||||
if !f.updater.CanInstall(f.newVersionInfo) {
|
|
||||||
f.log.Warning("Skipping update installation, current version too old")
|
|
||||||
f.qml.UpdateManualError()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.updater.InstallUpdate(f.newVersionInfo); err != nil {
|
|
||||||
if errors.Cause(err) == updater.ErrDownloadVerify {
|
|
||||||
f.log.WithError(err).Warning("Skipping update installation due to temporary error")
|
|
||||||
} else {
|
|
||||||
f.log.WithError(err).Error("The update couldn't be installed")
|
|
||||||
f.qml.UpdateManualError()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
f.qml.UpdateSilentRestartNeeded()
|
|
||||||
}
|
|
||||||
@ -1,196 +0,0 @@
|
|||||||
// Copyright (c) 2022 Proton AG
|
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//go:build build_qt
|
|
||||||
// +build build_qt
|
|
||||||
|
|
||||||
package qt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/base64"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/events"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/users"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (f *FrontendQt) login(username, password string) {
|
|
||||||
var err error
|
|
||||||
f.password, err = base64.StdEncoding.DecodeString(password)
|
|
||||||
if err != nil {
|
|
||||||
f.log.WithError(err).Error("Cannot decode password")
|
|
||||||
f.qml.LoginUsernamePasswordError("Cannot decode password")
|
|
||||||
f.loginClean()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
f.authClient, f.auth, err = f.bridge.Login(username, f.password)
|
|
||||||
if err != nil {
|
|
||||||
if err == pmapi.ErrPasswordWrong {
|
|
||||||
// Remove error message since it is hardcodded in QML.
|
|
||||||
f.qml.LoginUsernamePasswordError("")
|
|
||||||
f.loginClean()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err == pmapi.ErrPaidPlanRequired {
|
|
||||||
f.qml.LoginFreeUserError()
|
|
||||||
f.loginClean()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
f.qml.LoginUsernamePasswordError(err.Error())
|
|
||||||
f.loginClean()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.auth.HasTwoFactor() {
|
|
||||||
f.qml.Login2FARequested(username)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if f.auth.HasMailboxPassword() {
|
|
||||||
f.qml.Login2PasswordRequested()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
f.finishLogin()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) login2FA(username, code string) {
|
|
||||||
if f.auth == nil || f.authClient == nil {
|
|
||||||
f.log.Errorf("Login 2FA: authethication incomplete %p %p", f.auth, f.authClient)
|
|
||||||
f.qml.Login2FAErrorAbort("Missing authentication, try again.")
|
|
||||||
f.loginClean()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
twoFA, err := base64.StdEncoding.DecodeString(code)
|
|
||||||
if err != nil {
|
|
||||||
f.log.WithError(err).Error("Cannot decode 2fa code")
|
|
||||||
f.qml.LoginUsernamePasswordError("Cannot decode 2fa code")
|
|
||||||
f.loginClean()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = f.authClient.Auth2FA(context.Background(), string(twoFA))
|
|
||||||
if err == pmapi.ErrBad2FACodeTryAgain {
|
|
||||||
f.log.Warn("Login 2FA: retry 2fa")
|
|
||||||
f.qml.Login2FAError("")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == pmapi.ErrBad2FACode {
|
|
||||||
f.log.Warn("Login 2FA: abort 2fa")
|
|
||||||
f.qml.Login2FAErrorAbort("")
|
|
||||||
f.loginClean()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
f.log.WithError(err).Warn("Login 2FA: failed.")
|
|
||||||
f.qml.Login2FAErrorAbort(err.Error())
|
|
||||||
f.loginClean()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.auth.HasMailboxPassword() {
|
|
||||||
f.qml.Login2PasswordRequested()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
f.finishLogin()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) login2Password(username, mboxPassword string) {
|
|
||||||
var err error
|
|
||||||
f.password, err = base64.StdEncoding.DecodeString(mboxPassword)
|
|
||||||
if err != nil {
|
|
||||||
f.log.WithError(err).Error("Cannot decode mbox password")
|
|
||||||
f.qml.LoginUsernamePasswordError("Cannot decode mbox password")
|
|
||||||
f.loginClean()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
f.finishLogin()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) finishLogin() {
|
|
||||||
defer f.loginClean()
|
|
||||||
|
|
||||||
if len(f.password) == 0 || f.auth == nil || f.authClient == nil {
|
|
||||||
f.log.
|
|
||||||
WithField("hasPass", len(f.password) != 0).
|
|
||||||
WithField("hasAuth", f.auth != nil).
|
|
||||||
WithField("hasClient", f.authClient != nil).
|
|
||||||
Error("Finish login: authethication incomplete")
|
|
||||||
f.qml.Login2PasswordErrorAbort("Missing authentication, try again.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
done := make(chan string)
|
|
||||||
f.eventListener.Add(events.UserChangeDone, done)
|
|
||||||
defer f.eventListener.Remove(events.UserChangeDone, done)
|
|
||||||
|
|
||||||
user, err := f.bridge.FinishLogin(f.authClient, f.auth, f.password)
|
|
||||||
|
|
||||||
if err != nil && err != users.ErrUserAlreadyConnected {
|
|
||||||
f.log.WithError(err).Errorf("Finish login failed")
|
|
||||||
f.qml.Login2PasswordErrorAbort(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The user changed should be triggerd by FinishLogin but it is not
|
|
||||||
// guaranteed when this is going to happen. Therefore we should wait
|
|
||||||
// until we receive the signal from userChanged function.
|
|
||||||
f.waitForUserChangeDone(done, user.ID())
|
|
||||||
|
|
||||||
index := f.qml.Users().indexByID(user.ID())
|
|
||||||
f.log.WithField("index", index).Debug("Login finished")
|
|
||||||
defer f.qml.LoginFinished(index)
|
|
||||||
|
|
||||||
if err == users.ErrUserAlreadyConnected {
|
|
||||||
f.log.WithError(err).Error("User already logged in")
|
|
||||||
f.qml.LoginAlreadyLoggedIn(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) waitForUserChangeDone(done <-chan string, userID string) {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case changedID := <-done:
|
|
||||||
if changedID == userID {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case <-time.After(2 * time.Second):
|
|
||||||
f.log.WithField("ID", userID).Warning("Login finished but user not added within 2 seconds")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) loginAbort(username string) {
|
|
||||||
f.loginClean()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FrontendQt) loginClean() {
|
|
||||||
f.auth = nil
|
|
||||||
f.authClient = nil
|
|
||||||
for i := range f.password {
|
|
||||||
f.password[i] = '\x00'
|
|
||||||
}
|
|
||||||
f.password = f.password[0:0]
|
|
||||||
}
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
// Copyright (c) 2022 Proton AG
|
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
// +build build_qt
|
|
||||||
|
|
||||||
#include "log.h"
|
|
||||||
#include "_cgo_export.h"
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QByteArray>
|
|
||||||
#include <QString>
|
|
||||||
#include <QVector>
|
|
||||||
#include <QtGlobal>
|
|
||||||
|
|
||||||
void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
|
||||||
{
|
|
||||||
Q_UNUSED( type )
|
|
||||||
Q_UNUSED( context )
|
|
||||||
QByteArray localMsg = msg.toUtf8().prepend("WHITESPACE");
|
|
||||||
logMsgPacked(
|
|
||||||
const_cast<char*>( (localMsg.constData()) +10 ),
|
|
||||||
localMsg.size()-10
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstallMessageHandler() {
|
|
||||||
qInstallMessageHandler(messageHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
// Copyright (c) 2022 Proton AG
|
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//go:build build_qt
|
|
||||||
// +build build_qt
|
|
||||||
|
|
||||||
// Package log redirects QML logs to logrus
|
|
||||||
package log
|
|
||||||
|
|
||||||
//#include "log.h"
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/therecipe/qt/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
var logQML = logrus.WithField("pkg", "frontent/qml")
|
|
||||||
|
|
||||||
// InstallMessageHandler is registering logQML as logger for QML calls.
|
|
||||||
func InstallMessageHandler() {
|
|
||||||
C.InstallMessageHandler()
|
|
||||||
}
|
|
||||||
|
|
||||||
//export logMsgPacked
|
|
||||||
func logMsgPacked(data *C.char, len C.int) {
|
|
||||||
logQML.Warn(C.GoStringN(data, len))
|
|
||||||
}
|
|
||||||
|
|
||||||
// logDummy is here to trigger qtmoc to create cgo instructions
|
|
||||||
type logDummy struct {
|
|
||||||
core.QObject
|
|
||||||
}
|
|
||||||
@ -1,317 +0,0 @@
|
|||||||
// Copyright (c) 2022 Proton AG
|
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//go:build build_qt
|
|
||||||
// +build build_qt
|
|
||||||
|
|
||||||
package qt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/bridge"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
|
||||||
dockIcon "github.com/ProtonMail/proton-bridge/v2/internal/frontend/qt/dockicon"
|
|
||||||
"github.com/therecipe/qt/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
QMLBackend_QRegisterMetaType()
|
|
||||||
}
|
|
||||||
|
|
||||||
// QMLBackend connects QML frontend with Go backend.
|
|
||||||
type QMLBackend struct {
|
|
||||||
core.QObject
|
|
||||||
|
|
||||||
_ func() *core.QPoint `slot:"getCursorPos"`
|
|
||||||
_ func() `slot:"guiReady"`
|
|
||||||
_ func() `slot:"quit"`
|
|
||||||
_ func() `slot:"restart"`
|
|
||||||
|
|
||||||
_ bool `property:showOnStartup`
|
|
||||||
_ bool `property:showSplashScreen`
|
|
||||||
|
|
||||||
_ bool `property:dockIconVisible`
|
|
||||||
|
|
||||||
_ QMLUserModel `property:"users"`
|
|
||||||
|
|
||||||
// TODO copy stuff from Bridge_test.qml backend object
|
|
||||||
_ string `property:"goos"`
|
|
||||||
|
|
||||||
_ func(username, password string) `slot:"login"`
|
|
||||||
_ func(username, code string) `slot:"login2FA"`
|
|
||||||
_ func(username, password string) `slot:"login2Password"`
|
|
||||||
_ func(username string) `slot:"loginAbort"`
|
|
||||||
_ func(errorMsg string) `signal:"loginUsernamePasswordError"`
|
|
||||||
_ func() `signal:"loginFreeUserError"`
|
|
||||||
_ func(errorMsg string) `signal:"loginConnectionError"`
|
|
||||||
_ func(username string) `signal:"login2FARequested"`
|
|
||||||
_ func(errorMsg string) `signal:"login2FAError"`
|
|
||||||
_ func(errorMsg string) `signal:"login2FAErrorAbort"`
|
|
||||||
_ func() `signal:"login2PasswordRequested"`
|
|
||||||
_ func(errorMsg string) `signal:"login2PasswordError"`
|
|
||||||
_ func(errorMsg string) `signal:"login2PasswordErrorAbort"`
|
|
||||||
_ func(index int) `signal:"loginFinished"`
|
|
||||||
_ func(index int) `signal:"loginAlreadyLoggedIn"`
|
|
||||||
|
|
||||||
_ func() `signal:"internetOff"`
|
|
||||||
_ func() `signal:"internetOn"`
|
|
||||||
|
|
||||||
_ func(version string) `signal:"updateManualReady"`
|
|
||||||
_ func() `signal:"updateManualRestartNeeded"`
|
|
||||||
_ func() `signal:"updateManualError"`
|
|
||||||
_ func(version string) `signal:"updateForce"`
|
|
||||||
_ func() `signal:"updateForceError"`
|
|
||||||
_ func() `signal:"updateSilentRestartNeeded"`
|
|
||||||
_ func() `signal:"updateSilentError"`
|
|
||||||
_ func() `signal:"updateIsLatestVersion"`
|
|
||||||
_ func() `slot:"checkUpdates"`
|
|
||||||
_ func() `signal:"checkUpdatesFinished"`
|
|
||||||
_ func() `slot:"installUpdate"`
|
|
||||||
|
|
||||||
_ bool `property:"isDiskCacheEnabled"`
|
|
||||||
_ core.QUrl `property:"diskCachePath"`
|
|
||||||
_ func() `signal:"cacheUnavailable"`
|
|
||||||
_ func() `signal:"cacheCantMove"`
|
|
||||||
_ func() `signal:"cacheLocationChangeSuccess"`
|
|
||||||
_ func() `signal:"diskFull"`
|
|
||||||
_ func(enableDiskCache bool, diskCachePath core.QUrl) `slot:"changeLocalCache"`
|
|
||||||
_ func() `signal:"changeLocalCacheFinished"`
|
|
||||||
|
|
||||||
_ bool `property:"isFirstGUIStart"`
|
|
||||||
|
|
||||||
_ bool `property:"isAutomaticUpdateOn"`
|
|
||||||
_ func(makeItActive bool) `slot:"toggleAutomaticUpdate"`
|
|
||||||
|
|
||||||
_ bool `property:"isAutostartOn"`
|
|
||||||
_ func(makeItActive bool) `slot:"toggleAutostart"`
|
|
||||||
_ func() `signal:"toggleAutostartFinished"`
|
|
||||||
|
|
||||||
_ bool `property:"isBetaEnabled"`
|
|
||||||
_ func(makeItActive bool) `slot:"toggleBeta"`
|
|
||||||
|
|
||||||
_ bool `property:"isDoHEnabled"`
|
|
||||||
_ func(makeItActive bool) `slot:"toggleDoH"`
|
|
||||||
|
|
||||||
_ bool `property:"useSSLforSMTP"`
|
|
||||||
_ func(makeItActive bool) `slot:"toggleUseSSLforSMTP"`
|
|
||||||
_ func() `signal:"toggleUseSSLFinished"`
|
|
||||||
|
|
||||||
_ string `property:"hostname"`
|
|
||||||
_ int `property:"portIMAP"`
|
|
||||||
_ int `property:"portSMTP"`
|
|
||||||
_ func(imapPort, smtpPort int) `slot:"changePorts"`
|
|
||||||
_ func(port int) bool `slot:"isPortFree"`
|
|
||||||
_ func() `signal:"changePortFinished"`
|
|
||||||
_ func() `signal:"portIssueIMAP"`
|
|
||||||
_ func() `signal:"portIssueSMTP"`
|
|
||||||
|
|
||||||
_ func() `slot:"triggerReset"`
|
|
||||||
_ func() `signal:"resetFinished"`
|
|
||||||
|
|
||||||
_ string `property:"version"`
|
|
||||||
_ core.QUrl `property:"logsPath"`
|
|
||||||
_ core.QUrl `property:"licensePath"`
|
|
||||||
_ core.QUrl `property:"releaseNotesLink"`
|
|
||||||
_ core.QUrl `property:"dependencyLicensesLink"`
|
|
||||||
_ core.QUrl `property:"landingPageLink"`
|
|
||||||
|
|
||||||
_ string `property:"colorSchemeName"`
|
|
||||||
_ func(string) `slot:"changeColorScheme"`
|
|
||||||
_ string `property:"currentEmailClient"`
|
|
||||||
_ func() `slot:"updateCurrentMailClient"`
|
|
||||||
_ func(description, address, emailClient string, includeLogs bool) `slot:"reportBug"`
|
|
||||||
_ func() `signal:"reportBugFinished"`
|
|
||||||
_ func() `signal:"bugReportSendSuccess"`
|
|
||||||
_ func() `signal:"bugReportSendError"`
|
|
||||||
|
|
||||||
_ []string `property:"availableKeychain"`
|
|
||||||
_ string `property:"currentKeychain"`
|
|
||||||
_ func(keychain string) `slot:"changeKeychain"`
|
|
||||||
_ func() `signal:"changeKeychainFinished"`
|
|
||||||
_ func() `signal:"notifyHasNoKeychain"`
|
|
||||||
_ func() `signal:"notifyRebuildKeychain"`
|
|
||||||
|
|
||||||
_ func(email string) `signal:noActiveKeyForRecipient`
|
|
||||||
_ func() `signal:showMainWindow`
|
|
||||||
|
|
||||||
_ func(address string) `signal:addressChanged`
|
|
||||||
_ func(address string) `signal:addressChangedLogout`
|
|
||||||
_ func(username string) `signal:userDisconnected`
|
|
||||||
_ func() `signal:apiCertIssue`
|
|
||||||
|
|
||||||
_ func(userID string) `signal:userChanged`
|
|
||||||
|
|
||||||
_ bool `property:"isAllMailVisible"`
|
|
||||||
_ func(isDisabled bool) `slot:"changeIsAllMailVisible"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *QMLBackend) setup(f *FrontendQt) {
|
|
||||||
q.ConnectGetCursorPos(getCursorPos)
|
|
||||||
q.ConnectQuit(f.quit)
|
|
||||||
q.ConnectRestart(f.restart)
|
|
||||||
q.ConnectGuiReady(f.guiReady)
|
|
||||||
|
|
||||||
q.ConnectIsShowOnStartup(func() bool {
|
|
||||||
return f.showOnStartup
|
|
||||||
})
|
|
||||||
|
|
||||||
q.ConnectIsDockIconVisible(dockIcon.GetDockIconVisibleState)
|
|
||||||
q.ConnectSetDockIconVisible(dockIcon.SetDockIconVisibleState)
|
|
||||||
|
|
||||||
um := NewQMLUserModel(q)
|
|
||||||
um.f = f
|
|
||||||
q.SetUsers(um)
|
|
||||||
um.load()
|
|
||||||
|
|
||||||
q.ConnectUserChanged(um.userChanged)
|
|
||||||
|
|
||||||
q.SetGoos(runtime.GOOS)
|
|
||||||
|
|
||||||
q.ConnectLogin(func(u, p string) {
|
|
||||||
go func() {
|
|
||||||
defer f.panicHandler.HandlePanic()
|
|
||||||
f.login(u, p)
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
q.ConnectLogin2FA(func(u, p string) {
|
|
||||||
go func() {
|
|
||||||
defer f.panicHandler.HandlePanic()
|
|
||||||
f.login2FA(u, p)
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
q.ConnectLogin2Password(func(u, p string) {
|
|
||||||
go func() {
|
|
||||||
defer f.panicHandler.HandlePanic()
|
|
||||||
f.login2Password(u, p)
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
q.ConnectLoginAbort(func(u string) {
|
|
||||||
go func() {
|
|
||||||
defer f.panicHandler.HandlePanic()
|
|
||||||
f.loginAbort(u)
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer f.panicHandler.HandlePanic()
|
|
||||||
f.checkUpdatesAndNotify(false)
|
|
||||||
}()
|
|
||||||
q.ConnectCheckUpdates(func() {
|
|
||||||
go func() {
|
|
||||||
defer f.panicHandler.HandlePanic()
|
|
||||||
f.checkUpdatesAndNotify(true)
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
|
|
||||||
q.ConnectInstallUpdate(func() {
|
|
||||||
go func() {
|
|
||||||
defer f.panicHandler.HandlePanic()
|
|
||||||
f.installUpdate()
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
|
|
||||||
f.setIsDiskCacheEnabled()
|
|
||||||
f.setDiskCachePath()
|
|
||||||
q.ConnectChangeLocalCache(func(e bool, d *core.QUrl) {
|
|
||||||
go func() {
|
|
||||||
defer f.panicHandler.HandlePanic()
|
|
||||||
f.changeLocalCache(e, d)
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
|
|
||||||
f.setIsAutomaticUpdateOn()
|
|
||||||
q.ConnectToggleAutomaticUpdate(func(m bool) {
|
|
||||||
go func() {
|
|
||||||
defer f.panicHandler.HandlePanic()
|
|
||||||
f.toggleAutomaticUpdate(m)
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
|
|
||||||
f.setIsAutostartOn()
|
|
||||||
q.ConnectToggleAutostart(f.toggleAutostart)
|
|
||||||
|
|
||||||
f.setIsBetaEnabled()
|
|
||||||
q.ConnectToggleBeta(func(m bool) {
|
|
||||||
go func() {
|
|
||||||
defer f.panicHandler.HandlePanic()
|
|
||||||
f.toggleBeta(m)
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
|
|
||||||
q.SetIsDoHEnabled(f.bridge.GetProxyAllowed())
|
|
||||||
q.ConnectToggleDoH(f.toggleDoH)
|
|
||||||
|
|
||||||
q.SetUseSSLforSMTP(f.settings.GetBool(settings.SMTPSSLKey))
|
|
||||||
q.ConnectToggleUseSSLforSMTP(f.toggleUseSSLforSMTP)
|
|
||||||
|
|
||||||
q.SetHostname(bridge.Host)
|
|
||||||
q.SetPortIMAP(f.settings.GetInt(settings.IMAPPortKey))
|
|
||||||
q.SetPortSMTP(f.settings.GetInt(settings.SMTPPortKey))
|
|
||||||
q.ConnectChangePorts(f.changePorts)
|
|
||||||
q.ConnectIsPortFree(f.isPortFree)
|
|
||||||
|
|
||||||
q.ConnectTriggerReset(func() {
|
|
||||||
go func() {
|
|
||||||
defer f.panicHandler.HandlePanic()
|
|
||||||
f.triggerReset()
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
|
|
||||||
f.setShowSplashScreen()
|
|
||||||
f.setVersion()
|
|
||||||
f.setLogsPath()
|
|
||||||
// release notes link is set by update
|
|
||||||
f.setLicensePath()
|
|
||||||
|
|
||||||
f.setColorScheme()
|
|
||||||
q.ConnectChangeColorScheme(func(newScheme string) {
|
|
||||||
go func() {
|
|
||||||
defer f.panicHandler.HandlePanic()
|
|
||||||
f.changeColorScheme(newScheme)
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
|
|
||||||
f.setCurrentEmailClient()
|
|
||||||
q.ConnectUpdateCurrentMailClient(func() {
|
|
||||||
go func() {
|
|
||||||
defer f.panicHandler.HandlePanic()
|
|
||||||
f.setCurrentEmailClient()
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
q.ConnectReportBug(func(d, a, e string, i bool) {
|
|
||||||
go func() {
|
|
||||||
defer f.panicHandler.HandlePanic()
|
|
||||||
f.reportBug(d, a, e, i)
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
|
|
||||||
f.setKeychain()
|
|
||||||
q.ConnectChangeKeychain(func(k string) {
|
|
||||||
go func() {
|
|
||||||
defer f.panicHandler.HandlePanic()
|
|
||||||
f.changeKeychain(k)
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
|
|
||||||
q.SetIsAllMailVisible(f.bridge.IsAllMailVisible())
|
|
||||||
q.ConnectChangeIsAllMailVisible(func(isVisible bool) {
|
|
||||||
f.bridge.SetIsAllMailVisible(isVisible)
|
|
||||||
f.qml.SetIsAllMailVisible(isVisible)
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,297 +0,0 @@
|
|||||||
// Copyright (c) 2022 Proton AG
|
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//go:build build_qt
|
|
||||||
// +build build_qt
|
|
||||||
|
|
||||||
package qt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/events"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/types"
|
|
||||||
"github.com/therecipe/qt/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
QMLUser_QRegisterMetaType()
|
|
||||||
QMLUserModel_QRegisterMetaType()
|
|
||||||
}
|
|
||||||
|
|
||||||
// QMLUserModel stores list of of users
|
|
||||||
type QMLUserModel struct {
|
|
||||||
core.QAbstractListModel
|
|
||||||
|
|
||||||
_ map[int]*core.QByteArray `property:"roles"`
|
|
||||||
_ int `property:"count"`
|
|
||||||
_ func() `constructor:"init"`
|
|
||||||
_ func(row int) *core.QVariant `slot:"get"`
|
|
||||||
|
|
||||||
userIDs []string
|
|
||||||
userByID map[string]*QMLUser
|
|
||||||
access sync.RWMutex
|
|
||||||
f *FrontendQt
|
|
||||||
}
|
|
||||||
|
|
||||||
func (um *QMLUserModel) init() {
|
|
||||||
um.access.Lock()
|
|
||||||
defer um.access.Unlock()
|
|
||||||
um.SetCount(0)
|
|
||||||
um.ConnectRowCount(um.rowCount)
|
|
||||||
um.ConnectRoleNames(um.roleNames)
|
|
||||||
um.ConnectData(um.data)
|
|
||||||
um.ConnectGet(um.get)
|
|
||||||
um.ConnectCount(func() int {
|
|
||||||
um.access.RLock()
|
|
||||||
defer um.access.RUnlock()
|
|
||||||
return len(um.userIDs)
|
|
||||||
})
|
|
||||||
um.userIDs = []string{}
|
|
||||||
um.userByID = map[string]*QMLUser{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (um *QMLUserModel) roleNames() map[int]*core.QByteArray {
|
|
||||||
return map[int]*core.QByteArray{
|
|
||||||
int(core.Qt__DisplayRole): core.NewQByteArray2("user", -1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (um *QMLUserModel) data(index *core.QModelIndex, property int) *core.QVariant {
|
|
||||||
if !index.IsValid() {
|
|
||||||
um.f.log.WithField("size", len(um.userIDs)).Info("Trying to get user by invalid index")
|
|
||||||
return core.NewQVariant()
|
|
||||||
}
|
|
||||||
return um.get(index.Row())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (um *QMLUserModel) get(index int) *core.QVariant {
|
|
||||||
um.access.Lock()
|
|
||||||
defer um.access.Unlock()
|
|
||||||
if index < 0 || index >= len(um.userIDs) {
|
|
||||||
um.f.log.WithField("index", index).WithField("size", len(um.userIDs)).Info("Trying to get user by wrong index")
|
|
||||||
return core.NewQVariant()
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := um.getUserByID(um.userIDs[index])
|
|
||||||
if err != nil {
|
|
||||||
um.f.log.WithError(err).Error("Cannot get user from backend")
|
|
||||||
return core.NewQVariant()
|
|
||||||
}
|
|
||||||
return u.ToVariant()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (um *QMLUserModel) getUserByID(userID string) (*QMLUser, error) {
|
|
||||||
u, ok := um.userByID[userID]
|
|
||||||
if ok {
|
|
||||||
return u, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
user, err := um.f.bridge.GetUser(userID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
u = newQMLUserFromBacked(um, user)
|
|
||||||
um.userByID[userID] = u
|
|
||||||
return u, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (um *QMLUserModel) rowCount(*core.QModelIndex) int {
|
|
||||||
um.access.RLock()
|
|
||||||
defer um.access.RUnlock()
|
|
||||||
return len(um.userIDs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (um *QMLUserModel) setCount() {
|
|
||||||
um.SetCount(len(um.userIDs))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (um *QMLUserModel) addUser(userID string) {
|
|
||||||
um.BeginInsertRows(core.NewQModelIndex(), len(um.userIDs), len(um.userIDs))
|
|
||||||
um.access.Lock()
|
|
||||||
if um.indexByIDNotSafe(userID) < 0 {
|
|
||||||
um.userIDs = append(um.userIDs, userID)
|
|
||||||
}
|
|
||||||
um.access.Unlock()
|
|
||||||
um.EndInsertRows()
|
|
||||||
um.setCount()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (um *QMLUserModel) removeUser(row int) {
|
|
||||||
um.BeginRemoveRows(core.NewQModelIndex(), row, row)
|
|
||||||
um.access.Lock()
|
|
||||||
id := um.userIDs[row]
|
|
||||||
um.userIDs = append(um.userIDs[:row], um.userIDs[row+1:]...)
|
|
||||||
delete(um.userByID, id)
|
|
||||||
um.access.Unlock()
|
|
||||||
um.EndRemoveRows()
|
|
||||||
um.setCount()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (um *QMLUserModel) clear() {
|
|
||||||
um.BeginResetModel()
|
|
||||||
um.access.Lock()
|
|
||||||
um.userIDs = []string{}
|
|
||||||
um.userByID = map[string]*QMLUser{}
|
|
||||||
um.SetCount(0)
|
|
||||||
um.access.Unlock()
|
|
||||||
um.EndResetModel()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (um *QMLUserModel) load() {
|
|
||||||
um.clear()
|
|
||||||
|
|
||||||
for _, user := range um.f.bridge.GetUsers() {
|
|
||||||
um.addUser(user.ID())
|
|
||||||
|
|
||||||
// We need mark that all existing users already saw setup
|
|
||||||
// guide. This it is OK to construct QML here because it is in main thread.
|
|
||||||
u, err := um.getUserByID(user.ID())
|
|
||||||
if err != nil {
|
|
||||||
um.f.log.WithError(err).Error("Cannot get QMLUser while loading users")
|
|
||||||
}
|
|
||||||
u.SetSetupGuideSeen(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are no active accounts.
|
|
||||||
if um.Count() == 0 {
|
|
||||||
um.f.log.Info("No active accounts")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (um *QMLUserModel) userChanged(userID string) {
|
|
||||||
defer um.f.eventListener.Emit(events.UserChangeDone, userID)
|
|
||||||
|
|
||||||
index := um.indexByIDNotSafe(userID)
|
|
||||||
user, err := um.f.bridge.GetUser(userID)
|
|
||||||
|
|
||||||
if user == nil || err != nil {
|
|
||||||
if index >= 0 { // delete existing user
|
|
||||||
um.removeUser(index)
|
|
||||||
}
|
|
||||||
// if not exiting do nothing
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if index < 0 { // add non-existing user
|
|
||||||
um.addUser(userID)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// update exiting user
|
|
||||||
um.userByID[userID].update(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (um *QMLUserModel) indexByIDNotSafe(wantID string) int {
|
|
||||||
for i, id := range um.userIDs {
|
|
||||||
if id == wantID {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (um *QMLUserModel) indexByID(id string) int {
|
|
||||||
um.access.RLock()
|
|
||||||
defer um.access.RUnlock()
|
|
||||||
|
|
||||||
return um.indexByIDNotSafe(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// QMLUser holds data, slots and signals and for user.
|
|
||||||
type QMLUser struct {
|
|
||||||
core.QObject
|
|
||||||
|
|
||||||
_ string `property:"username"`
|
|
||||||
_ string `property:"avatarText"`
|
|
||||||
_ bool `property:"loggedIn"`
|
|
||||||
_ bool `property:"splitMode"`
|
|
||||||
_ bool `property:"setupGuideSeen"`
|
|
||||||
_ float32 `property:"usedBytes"`
|
|
||||||
_ float32 `property:"totalBytes"`
|
|
||||||
_ string `property:"password"`
|
|
||||||
_ []string `property:"addresses"`
|
|
||||||
|
|
||||||
_ func(makeItActive bool) `slot:"toggleSplitMode"`
|
|
||||||
_ func() `signal:"toggleSplitModeFinished"`
|
|
||||||
_ func() `slot:"logout"`
|
|
||||||
_ func() `slot:"remove"`
|
|
||||||
_ func(address string) `slot:"configureAppleMail"`
|
|
||||||
|
|
||||||
ID string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newQMLUserFromBacked(um *QMLUserModel, user types.User) *QMLUser {
|
|
||||||
qu := NewQMLUser(um)
|
|
||||||
qu.ID = user.ID()
|
|
||||||
|
|
||||||
qu.update(user)
|
|
||||||
|
|
||||||
qu.ConnectToggleSplitMode(func(activateSplitMode bool) {
|
|
||||||
go func() {
|
|
||||||
defer um.f.panicHandler.HandlePanic()
|
|
||||||
defer qu.ToggleSplitModeFinished()
|
|
||||||
if activateSplitMode == user.IsCombinedAddressMode() {
|
|
||||||
user.SwitchAddressMode()
|
|
||||||
}
|
|
||||||
qu.SetSplitMode(!user.IsCombinedAddressMode())
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
|
|
||||||
qu.ConnectLogout(func() {
|
|
||||||
qu.SetLoggedIn(false)
|
|
||||||
go func() {
|
|
||||||
defer um.f.panicHandler.HandlePanic()
|
|
||||||
user.Logout()
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
|
|
||||||
qu.ConnectRemove(func() {
|
|
||||||
go func() {
|
|
||||||
defer um.f.panicHandler.HandlePanic()
|
|
||||||
|
|
||||||
// TODO: remove preferences
|
|
||||||
if err := um.f.bridge.DeleteUser(qu.ID, false); err != nil {
|
|
||||||
um.f.log.WithError(err).Error("Failed to remove user")
|
|
||||||
// TODO: notification
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
|
|
||||||
qu.ConnectConfigureAppleMail(func(address string) {
|
|
||||||
go func() {
|
|
||||||
defer um.f.panicHandler.HandlePanic()
|
|
||||||
um.f.configureAppleMail(qu.ID, address)
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
|
|
||||||
return qu
|
|
||||||
}
|
|
||||||
|
|
||||||
func (qu *QMLUser) update(user types.User) {
|
|
||||||
username := user.Username()
|
|
||||||
qu.SetAvatarText(getInitials(username))
|
|
||||||
qu.SetUsername(username)
|
|
||||||
qu.SetLoggedIn(user.IsConnected())
|
|
||||||
qu.SetSplitMode(!user.IsCombinedAddressMode())
|
|
||||||
qu.SetSetupGuideSeen(false)
|
|
||||||
qu.SetUsedBytes(float32(user.UsedBytes()))
|
|
||||||
qu.SetTotalBytes(float32(user.TotalBytes()))
|
|
||||||
qu.SetPassword(user.GetBridgePassword())
|
|
||||||
qu.SetAddresses(user.GetAddresses())
|
|
||||||
}
|
|
||||||
45
internal/frontend/qt6/AppController.cpp
Normal file
45
internal/frontend/qt6/AppController.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#include "Pch.h"
|
||||||
|
#include "AppController.h"
|
||||||
|
#include "QMLBackend.h"
|
||||||
|
#include "GRPC/GRPCClient.h"
|
||||||
|
#include "Log.h"
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \return The AppController instance.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
AppController &app()
|
||||||
|
{
|
||||||
|
static AppController app;
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
AppController::AppController()
|
||||||
|
: backend_(std::make_unique<QMLBackend>())
|
||||||
|
, grpc_(std::make_unique<GRPCClient>())
|
||||||
|
, log_(std::make_unique<Log>())
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
59
internal/frontend/qt6/AppController.h
Normal file
59
internal/frontend/qt6/AppController.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BRIDGE_QT6_APP_CONTROLLER_H
|
||||||
|
#define BRIDGE_QT6_APP_CONTROLLER_H
|
||||||
|
|
||||||
|
|
||||||
|
class QMLBackend;
|
||||||
|
class GRPCClient;
|
||||||
|
class Log;
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \brief App controller class.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
class AppController: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
friend AppController& app();
|
||||||
|
|
||||||
|
public: // member functions.
|
||||||
|
AppController(AppController const&) = delete; ///< Disabled copy-constructor.
|
||||||
|
AppController(AppController&&) = delete; ///< Disabled assignment copy-constructor.
|
||||||
|
~AppController() override = default; ///< Destructor.
|
||||||
|
AppController& operator=(AppController const&) = delete; ///< Disabled assignment operator.
|
||||||
|
AppController& operator=(AppController&&) = delete; ///< Disabled move assignment operator.
|
||||||
|
QMLBackend& backend() { return *backend_; } ///< Return a reference to the backend.
|
||||||
|
GRPCClient& grpc() { return *grpc_; } ///< Return a reference to the GRPC client.
|
||||||
|
Log& log() { return *log_; } ///< Return a reference to the log.
|
||||||
|
|
||||||
|
private: // member functions
|
||||||
|
AppController(); ///< Default constructor.
|
||||||
|
|
||||||
|
private: // data members
|
||||||
|
std::unique_ptr<QMLBackend> backend_; ///< The backend.
|
||||||
|
std::unique_ptr<GRPCClient> grpc_; ///< The RPC client.
|
||||||
|
std::unique_ptr<Log> log_; ///< The log.
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
AppController& app(); ///< Return a reference to the app controller.
|
||||||
|
|
||||||
|
|
||||||
|
#endif // BRIDGE_QT6_APP_CONTROLLER_H
|
||||||
136
internal/frontend/qt6/CMakeLists.txt
Normal file
136
internal/frontend/qt6/CMakeLists.txt
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
# Copyright (c) 2022 Proton AG
|
||||||
|
#
|
||||||
|
# This file is part of Proton Mail Bridge.
|
||||||
|
#
|
||||||
|
# Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.22)
|
||||||
|
|
||||||
|
set(CMAKE_OSX_ARCHITECTURES x86_64) # needs to be set before the first project() directive.
|
||||||
|
|
||||||
|
#We rely on vcpkg for to get gRPC+Protobuf
|
||||||
|
if (NOT DEFINED ENV{VCPKG_ROOT})
|
||||||
|
message(FATAL_ERROR "vcpkg is required. Install vcpkg and define VCPKG_ROOT to point the the vcpkg installation folder. (e.g. ~/vcpkg/")
|
||||||
|
endif()
|
||||||
|
if (APPLE)
|
||||||
|
set(VCPKG_TARGET_TRIPLET x64-osx)
|
||||||
|
endif()
|
||||||
|
if (WIN32)
|
||||||
|
set(VCPKG_TARGET_TRIPLET x64-mingw-static)
|
||||||
|
endif()
|
||||||
|
set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "toolchain")
|
||||||
|
|
||||||
|
|
||||||
|
project(bridge_qt6 LANGUAGES CXX)
|
||||||
|
if (APPLE) # On macOS, we have some Objective-C++ code in DockIcon to deal with ... the dock icon.
|
||||||
|
enable_language(OBJC OBJCXX)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT DEFINED ENV{QT5DIR})
|
||||||
|
message(FATAL_ERROR "QT5DIR needs to be defined and point to the root of your Qt5 folder (e.g. /Users/MyName/Qt/5.10.1/clang_64).")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
set(CMAKE_PREFIX_PATH $ENV{QT5DIR} ${CMAKE_PREFIX_PATH})
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
set(CMAKE_AUTORCC ON)
|
||||||
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
find_package(Protobuf CONFIG REQUIRED)
|
||||||
|
message(STATUS "Using protobuf ${Protobuf_VERSION}")
|
||||||
|
|
||||||
|
|
||||||
|
find_package(grpc CONFIG REQUIRED)
|
||||||
|
message(STATUS "Using gRPC ${gRPC_VERSION}")
|
||||||
|
|
||||||
|
|
||||||
|
if (APPLE) # We need to link the Cocoa framework for the dock icon.
|
||||||
|
find_library(COCOA_LIBRARY Cocoa REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
find_package(Qt5 COMPONENTS
|
||||||
|
Core
|
||||||
|
Quick
|
||||||
|
Qml
|
||||||
|
QuickControls2
|
||||||
|
REQUIRED)
|
||||||
|
|
||||||
|
find_program(PROTOC_EXE protoc)
|
||||||
|
find_program(GRPC_CPP_PLUGIN grpc_cpp_plugin)
|
||||||
|
set(PROTO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../grpc")
|
||||||
|
set(PROTO_FILE "${PROTO_DIR}/bridge.proto")
|
||||||
|
set(GRPC_OUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/GRPC")
|
||||||
|
set(PROTO_CPP_FILE "${GRPC_OUT_DIR}/bridge.pb.cc")
|
||||||
|
set(PROTO_H_FILE "${GRPC_OUT_DIR}/bridge.pb.h")
|
||||||
|
set(GRPC_CPP_FILE "${GRPC_OUT_DIR}/bridge.grpc.pb.cc")
|
||||||
|
set(GRPC_H_FILE "${GRPC_OUT_DIR}/bridge.grpc.pb.h")
|
||||||
|
if (APPLE)
|
||||||
|
set(DOCK_ICON_SRC_FILE DockIcon/DockIcon.mm)
|
||||||
|
else()
|
||||||
|
set(DOCK_ICON_SRC_FILE DockIcon/DockIcon.cpp)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT
|
||||||
|
${PROTO_CPP_FILE}
|
||||||
|
${PROTO_H_FILE}
|
||||||
|
${GRPC_CPP_FILE}
|
||||||
|
${GRPC_H_FILE}
|
||||||
|
COMMAND
|
||||||
|
${PROTOC_EXE}
|
||||||
|
ARGS
|
||||||
|
--proto_path=${PROTO_DIR}
|
||||||
|
--plugin=protoc-gen-grpc="${GRPC_CPP_PLUGIN}"
|
||||||
|
--cpp_out=${GRPC_OUT_DIR}
|
||||||
|
--grpc_out=${GRPC_OUT_DIR}
|
||||||
|
${PROTO_FILE}
|
||||||
|
DEPENDS
|
||||||
|
${PROTO_FILE}
|
||||||
|
COMMENT "Generating gPRC/Protobuf C++ code"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(bridge_qt6
|
||||||
|
${PROTO_CPP_FILE} ${PROTO_H_FILE} ${GRPC_CPP_FILE} ${GRPC_H_FILE}
|
||||||
|
AppController.cpp AppController.h
|
||||||
|
EventStreamWorker.cpp EventStreamWorker.h
|
||||||
|
Exception.cpp Exception.h
|
||||||
|
Log.cpp Log.h
|
||||||
|
main.cpp
|
||||||
|
Pch.h
|
||||||
|
QMLBackend.cpp QMLBackend.h
|
||||||
|
${DOCK_ICON_SRC_FILE} DockIcon/DockIcon.h
|
||||||
|
GRPC/GRPCClient.cpp GRPC/GRPCClient.h
|
||||||
|
GRPC/GRPCUtils.cpp GRPC/GRPCUtils.h
|
||||||
|
User/User.cpp User/User.h User/UserList.cpp User/UserList.h
|
||||||
|
Worker/Overseer.cpp Worker/Overseer.h
|
||||||
|
Worker/Worker.h)
|
||||||
|
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
target_precompile_headers(bridge_qt6 PRIVATE Pch.h)
|
||||||
|
|
||||||
|
target_link_libraries(bridge_qt6
|
||||||
|
Qt5::Core
|
||||||
|
Qt5::Quick
|
||||||
|
Qt5::Qml
|
||||||
|
Qt5::QuickControls2
|
||||||
|
protobuf::libprotobuf
|
||||||
|
gRPC::grpc++
|
||||||
|
)
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
target_link_libraries(bridge_qt6 ${COCOA_LIBRARY})
|
||||||
|
endif()
|
||||||
@ -15,13 +15,15 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//go:build !darwin && build_qt
|
|
||||||
// +build !darwin,build_qt
|
|
||||||
|
|
||||||
package dockicon
|
#include "Pch.h"
|
||||||
|
|
||||||
func SetDockIconVisibleState(visible bool) {}
|
|
||||||
|
|
||||||
func GetDockIconVisibleState() bool {
|
#ifndef Q_OS_MACOS
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
void setDockIconVisibleState(bool visible) { Q_UNUSED(visible) }
|
||||||
|
bool getDockIconVisibleState() { return true; }
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -15,22 +15,13 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef LOGRUS_QML_LOG_H
|
#ifndef BRIDGE_QT6_DOCK_ICON_H
|
||||||
#define LOGRUS_QML_LOG_H
|
#define BRIDGE_QT6_DOCK_ICON_H
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
void setDockIconVisibleState(bool visible); ///< Set the DOCK icon visibility state
|
||||||
extern "C" {
|
bool getDockIconVisibleState(); ///< Get the Dock icon visibility state
|
||||||
#endif // C++
|
|
||||||
|
|
||||||
void InstallMessageHandler();
|
|
||||||
;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#endif // #ifndef BRIDGE_QT6_DOCK_ICON_H
|
||||||
}
|
|
||||||
#endif // C++
|
|
||||||
|
|
||||||
#endif // LOGRUS_QML_LOG_H
|
|
||||||
@ -15,13 +15,16 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// +build darwin
|
|
||||||
// +build build_qt
|
|
||||||
|
|
||||||
#include "DockIcon.h"
|
#include "Pch.h"
|
||||||
#include <Cocoa/Cocoa.h>
|
#include <Cocoa/Cocoa.h>
|
||||||
|
#include "DockIcon.h"
|
||||||
|
|
||||||
void SetDockIconVisibleState(bool visible) {
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
|
||||||
|
|
||||||
|
void setDockIconVisibleState(bool visible) {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
[NSApp setActivationPolicy: NSApplicationActivationPolicyRegular];
|
[NSApp setActivationPolicy: NSApplicationActivationPolicyRegular];
|
||||||
return;
|
return;
|
||||||
@ -31,12 +34,16 @@ void SetDockIconVisibleState(bool visible) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetDockIconVisibleState() {
|
|
||||||
|
bool getDockIconVisibleState() {
|
||||||
switch ([NSApp activationPolicy]) {
|
switch ([NSApp activationPolicy]) {
|
||||||
case NSApplicationActivationPolicyAccessory:
|
case NSApplicationActivationPolicyAccessory:
|
||||||
case NSApplicationActivationPolicyProhibited:
|
case NSApplicationActivationPolicyProhibited:
|
||||||
return false;
|
return false;
|
||||||
case NSApplicationActivationPolicyRegular:
|
case NSApplicationActivationPolicyRegular:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // #ifdef Q_OS_MACOS
|
||||||
2656
internal/frontend/qt6/Doxyfile
Normal file
2656
internal/frontend/qt6/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
57
internal/frontend/qt6/EventStreamWorker.cpp
Normal file
57
internal/frontend/qt6/EventStreamWorker.cpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#include "Pch.h"
|
||||||
|
#include "EventStreamWorker.h"
|
||||||
|
#include "GRPC/GRPCClient.h"
|
||||||
|
#include "Log.h"
|
||||||
|
#include "Exception.h"
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] parent The parent object.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
EventStreamReader::EventStreamReader(QObject *parent)
|
||||||
|
: Worker(parent)
|
||||||
|
{
|
||||||
|
connect(this, &EventStreamReader::started, [&]() { app().log().debug("EventStreamReader started");});
|
||||||
|
connect(this, &EventStreamReader::finished, [&]() { app().log().debug("EventStreamReader finished");});
|
||||||
|
connect(this, &EventStreamReader::error, &app().log(), &Log::error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void EventStreamReader::run()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
emit started();
|
||||||
|
|
||||||
|
grpc::Status const status = app().grpc().startEventStream();
|
||||||
|
if (!status.ok())
|
||||||
|
throw Exception(QString::fromStdString(status.error_message()));
|
||||||
|
|
||||||
|
emit finished();
|
||||||
|
}
|
||||||
|
catch (Exception const &e)
|
||||||
|
{
|
||||||
|
emit error(e.qwhat());
|
||||||
|
}
|
||||||
|
}
|
||||||
52
internal/frontend/qt6/EventStreamWorker.h
Normal file
52
internal/frontend/qt6/EventStreamWorker.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BRIDGE_QT6_EVENT_STREAM_WORKER_H
|
||||||
|
#define BRIDGE_QT6_EVENT_STREAM_WORKER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "GRPC/bridge.grpc.pb.h"
|
||||||
|
#include "Worker/Worker.h"
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \brief Stream reader class.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
class EventStreamReader: public Worker
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public: // member functions
|
||||||
|
explicit EventStreamReader(QObject *parent); ///< Default constructor.
|
||||||
|
EventStreamReader(EventStreamReader const&) = delete; ///< Disabled copy-constructor.
|
||||||
|
EventStreamReader(EventStreamReader&&) = delete; ///< Disabled assignment copy-constructor.
|
||||||
|
~EventStreamReader() override = default; ///< Destructor.
|
||||||
|
EventStreamReader& operator=(EventStreamReader const&) = delete; ///< Disabled assignment operator.
|
||||||
|
EventStreamReader& operator=(EventStreamReader&&) = delete; ///< Disabled move assignment operator.
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void run() override; ///< Run the reader.
|
||||||
|
|
||||||
|
signals:
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma ide diagnostic ignored "NotImplementedFunctions"
|
||||||
|
void eventReceived(QString eventString); ///< signal for events.
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //BRIDGE_QT6_EVENT_STREAM_WORKER_H
|
||||||
68
internal/frontend/qt6/Exception.cpp
Normal file
68
internal/frontend/qt6/Exception.cpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#include "Pch.h"
|
||||||
|
#include "Exception.h"
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] what A description of the exception
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
Exception::Exception(QString what) noexcept
|
||||||
|
: std::exception()
|
||||||
|
, what_(std::move(what))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] ref The Exception to copy from
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
Exception::Exception(Exception const& ref) noexcept
|
||||||
|
: std::exception(ref)
|
||||||
|
, what_(ref.what_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] ref The Exception to copy from
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
Exception::Exception(Exception&& ref) noexcept
|
||||||
|
: std::exception(ref)
|
||||||
|
, what_(ref.what_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \return a string describing the exception
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
QString const& Exception::qwhat() const noexcept
|
||||||
|
{
|
||||||
|
return what_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \return A pointer to the description string of the exception.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
const char* Exception::what() const noexcept
|
||||||
|
{
|
||||||
|
return what_.toLocal8Bit().constData();
|
||||||
|
}
|
||||||
46
internal/frontend/qt6/Exception.h
Normal file
46
internal/frontend/qt6/Exception.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BRIDGE_QT6_EXCEPTION_H
|
||||||
|
#define BRIDGE_QT6_EXCEPTION_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \brief Exception class.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
class Exception: public std::exception
|
||||||
|
{
|
||||||
|
public: // member functions
|
||||||
|
explicit Exception(QString what = QString()) noexcept; ///< Constructor
|
||||||
|
Exception(Exception const& ref) noexcept; ///< copy constructor
|
||||||
|
Exception(Exception&& ref) noexcept; ///< copy constructor
|
||||||
|
Exception& operator=(Exception const&) = delete; ///< Disabled assignment operator
|
||||||
|
Exception& operator=(Exception&&) = delete; ///< Disabled assignment operator
|
||||||
|
~Exception() noexcept override = default; ///< Destructor
|
||||||
|
QString const& qwhat() const noexcept; ///< Return the description of the exception as a QString
|
||||||
|
const char* what() const noexcept override; ///< Return the description of the exception as C style string
|
||||||
|
|
||||||
|
private: // data members
|
||||||
|
QString const what_; ///< The description of the exception
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //BRIDGE_QT6_EXCEPTION_H
|
||||||
1183
internal/frontend/qt6/GRPC/GRPCClient.cpp
Normal file
1183
internal/frontend/qt6/GRPC/GRPCClient.cpp
Normal file
File diff suppressed because it is too large
Load Diff
214
internal/frontend/qt6/GRPC/GRPCClient.h
Normal file
214
internal/frontend/qt6/GRPC/GRPCClient.h
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BRIDGE_QT6_RPC_CLIENT_H
|
||||||
|
#define BRIDGE_QT6_RPC_CLIENT_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "GRPC/bridge.grpc.pb.h"
|
||||||
|
#include "grpc++/grpc++.h"
|
||||||
|
#include "User/User.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef grpc::Status (grpc::Bridge::Stub::*SimpleMethod)(grpc::ClientContext*, const google::protobuf::Empty&, google::protobuf::Empty*);
|
||||||
|
typedef grpc::Status (grpc::Bridge::Stub::*BoolSetter)(grpc::ClientContext*, const google::protobuf::BoolValue&, google::protobuf::Empty*);
|
||||||
|
typedef grpc::Status (grpc::Bridge::Stub::*BoolGetter)(grpc::ClientContext*, const google::protobuf::Empty&, google::protobuf::BoolValue*);
|
||||||
|
typedef grpc::Status (grpc::Bridge::Stub::*Int32Setter)(grpc::ClientContext*, const google::protobuf::Int32Value&, google::protobuf::Empty*);
|
||||||
|
typedef grpc::Status (grpc::Bridge::Stub::*Int32Getter)(grpc::ClientContext*, const google::protobuf::Empty&, google::protobuf::Int32Value*);
|
||||||
|
typedef grpc::Status (grpc::Bridge::Stub::*StringGetter)(grpc::ClientContext*, const google::protobuf::Empty&, google::protobuf::StringValue*);
|
||||||
|
typedef grpc::Status (grpc::Bridge::Stub::*StringSetter)(grpc::ClientContext*, const google::protobuf::StringValue&, google::protobuf::Empty*);
|
||||||
|
typedef grpc::Status (grpc::Bridge::Stub::*StringParamMethod)(grpc::ClientContext*, const google::protobuf::StringValue&, google::protobuf::Empty*);
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \brief gRPC client class. This class encapsulate the gRPC service, abstracting all data type conversions.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
class GRPCClient: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public: // member functions.
|
||||||
|
GRPCClient() = default; ///< Default constructor.
|
||||||
|
GRPCClient(GRPCClient const&) = delete; ///< Disabled copy-constructor.
|
||||||
|
GRPCClient(GRPCClient&&) = delete; ///< Disabled assignment copy-constructor.
|
||||||
|
~GRPCClient() override = default; ///< Destructor.
|
||||||
|
GRPCClient& operator=(GRPCClient const&) = delete; ///< Disabled assignment operator.
|
||||||
|
GRPCClient& operator=(GRPCClient&&) = delete; ///< Disabled move assignment operator.
|
||||||
|
bool connectToServer(QString &outError); ///< Establish connection to the gRPC server.
|
||||||
|
|
||||||
|
grpc::Status guiReady(); ///< performs the "GuiReady" gRPC call.
|
||||||
|
grpc::Status isFirstGUIStart(bool &outIsFirst); ///< performs the "IsFirstGUIStart" gRPC call.
|
||||||
|
grpc::Status isAutostartOn(bool &outIsOn); ///< Performs the "isAutostartOn" gRPC call.
|
||||||
|
grpc::Status setIsAutostartOn(bool on); ///< Performs the "setIsAutostartOn" gRPC call.
|
||||||
|
grpc::Status isBetaEnabled(bool &outEnabled); ///< Performs the "isBetaEnabled" gRPC call.
|
||||||
|
grpc::Status setisBetaEnabled(bool enabled); ///< Performs the 'setIsBetaEnabled' gRPC call.
|
||||||
|
grpc::Status colorSchemeName(QString &outName); ///< Performs the "colorSchemeName' gRPC call.
|
||||||
|
grpc::Status setColorSchemeName(QString const &name); ///< Performs the "setColorSchemeName' gRPC call.
|
||||||
|
grpc::Status currentEmailClient(QString &outName); ///< Performs the 'currentEmailClient' gRPC call.
|
||||||
|
|
||||||
|
grpc::Status quit(); ///< Perform the "Quit" gRPC call.
|
||||||
|
grpc::Status restart(); ///< Performs the Restart gRPC call.
|
||||||
|
grpc::Status isPortFree(qint32 port, bool &outFree); ///< Performs the 'IsPortFree' call.
|
||||||
|
grpc::Status showOnStartup(bool &outValue); ///< Performs the 'ShowOnStartup' call.
|
||||||
|
grpc::Status showSplashScreen(bool &outValue); ///< Performs the 'ShowSplashScreen' call.
|
||||||
|
grpc::Status goos(QString &outGoos); ///< Performs the 'GoOs' call.
|
||||||
|
grpc::Status logsPath(QUrl &outPath); ///< Performs the 'LogsPath' call.
|
||||||
|
grpc::Status licensePath(QUrl &outPath); ///< Performs the 'LicensePath' call.
|
||||||
|
grpc::Status dependencyLicensesLink(QUrl &outUrl); ///< Performs the 'DependencyLicensesLink' call.
|
||||||
|
grpc::Status version(QString &outVersion); ///< Performs the 'Version' call.
|
||||||
|
grpc::Status hostname(QString &outHostname); ///< Performs the 'Hostname' call.
|
||||||
|
|
||||||
|
signals: // app related signals
|
||||||
|
void internetStatus(bool isOn);
|
||||||
|
void toggleAutostartFinished();
|
||||||
|
void resetFinished();
|
||||||
|
void reportBugFinished();
|
||||||
|
void reportBugSuccess();
|
||||||
|
void reportBugError();
|
||||||
|
void showMainWindow();
|
||||||
|
|
||||||
|
// cache related calls
|
||||||
|
public:
|
||||||
|
grpc::Status isCacheOnDiskEnabled(bool &outEnabled); ///< Performs the 'isCacheOnDiskEnabled' call.
|
||||||
|
grpc::Status diskCachePath(QUrl &outPath); ///< Performs the 'diskCachePath' call.
|
||||||
|
grpc::Status changeLocalCache(bool enabled, QUrl const &path); ///< Performs the 'ChangeLocalCache' call.
|
||||||
|
signals:
|
||||||
|
void isCacheOnDiskEnabledChanged(bool enabled);
|
||||||
|
void diskCachePathChanged(QUrl const&outPath);
|
||||||
|
void cacheUnavailable(); // _ func() `signal:"cacheUnavailable"`
|
||||||
|
void cacheCantMove(); // _ func() `signal:"cacheCantMove"`
|
||||||
|
void cacheLocationChangeSuccess(); // _ func() `signal:"cacheLocationChangeSuccess"`
|
||||||
|
void diskFull(); // _ func() `signal:"diskFull"`
|
||||||
|
void changeLocalCacheFinished(); // _ func() `signal:"changeLocalCacheFinished"`
|
||||||
|
|
||||||
|
|
||||||
|
// mail settings related calls
|
||||||
|
public:
|
||||||
|
grpc::Status useSSLForSMTP(bool &outUseSSL); ///< Performs the 'useSSLForSMTP' gRPC call
|
||||||
|
grpc::Status setUseSSLForSMTP(bool useSSL); ///< Performs the 'currentEmailClient' gRPC call.
|
||||||
|
grpc::Status portIMAP(int &outPort); ///< Performs the 'portImap' gRPC call.
|
||||||
|
grpc::Status portSMTP(int &outPort); ///< Performs the 'portImap' gRPC call.
|
||||||
|
grpc::Status changePorts(int portIMAP, int portSMTP); ///< Performs the 'changePorts' gRPC call.
|
||||||
|
grpc::Status isDoHEnabled(bool &outEnabled); ///< Performs the 'isDoHEnabled' gRPC call.
|
||||||
|
grpc::Status setIsDoHEnabled(bool enabled); ///< Performs the 'setIsDoHEnabled' gRPC call.
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void portIssueIMAP();
|
||||||
|
void portIssueSMTP();
|
||||||
|
void toggleUseSSLFinished();
|
||||||
|
void changePortFinished();
|
||||||
|
|
||||||
|
public: // login related calls
|
||||||
|
grpc::Status login(QString const &username, QString const& password); ///< Performs the 'login' call.
|
||||||
|
grpc::Status login2FA(QString const &username, QString const& code); ///< Performs the 'login2FA' call.
|
||||||
|
grpc::Status login2Passwords(QString const &username, QString const& password); ///< Performs the 'login2Passwords' call.
|
||||||
|
grpc::Status loginAbort(QString const &username); ///< Performs the 'loginAbort' call.
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void loginUsernamePasswordError(QString const &errMsg); // _ func(errorMsg string) `signal:"loginUsernamePasswordError"`
|
||||||
|
void loginFreeUserError(); // _ func() `signal:"loginFreeUserError"`
|
||||||
|
void loginConnectionError(QString const &errMsg); // _ func(errorMsg string) `signal:"loginConnectionError"`
|
||||||
|
void login2FARequested(QString const &userName); // _ func(username string) `signal:"login2FARequested"`
|
||||||
|
void login2FAError(QString const& errMsg); // _ func(errorMsg string) `signal:"login2FAError"`
|
||||||
|
void login2FAErrorAbort(QString const& errMsg); // _ func(errorMsg string) `signal:"login2FAErrorAbort"`
|
||||||
|
void login2PasswordRequested(); // _ func() `signal:"login2PasswordRequested"`
|
||||||
|
void login2PasswordError(QString const& errMsg); // _ func(errorMsg string) `signal:"login2PasswordError"`
|
||||||
|
void login2PasswordErrorAbort(QString const& errMsg); // _ func(errorMsg string) `signal:"login2PasswordErrorAbort"`
|
||||||
|
void loginFinished(QString const &userID); // _ func(index int) `signal:"loginFinished"`
|
||||||
|
void loginAlreadyLoggedIn(QString const &userID); // _ func(index int) `signal:"loginAlreadyLoggedIn"`
|
||||||
|
|
||||||
|
public: // Update related calls
|
||||||
|
grpc::Status checkUpdate();
|
||||||
|
grpc::Status installUpdate();
|
||||||
|
grpc::Status setIsAutomaticUpdateOn(bool on);
|
||||||
|
grpc::Status isAutomaticUpdateOn(bool &isOn);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void updateManualError();
|
||||||
|
void updateForceError();
|
||||||
|
void updateSilentError();
|
||||||
|
void updateManualReady(QString const &version);
|
||||||
|
void updateManualRestartNeeded();
|
||||||
|
void updateForce(QString const &version);
|
||||||
|
void updateSilentRestartNeeded();
|
||||||
|
void updateIsLatestVersion();
|
||||||
|
void checkUpdatesFinished();
|
||||||
|
|
||||||
|
public: // user related calls
|
||||||
|
grpc::Status getUserList(QList<SPUser>& outUsers);
|
||||||
|
grpc::Status getUser(QString const &userID, SPUser& outUser);
|
||||||
|
grpc::Status logoutUser(QString const &userID); ///< Performs the 'logoutUser' call.
|
||||||
|
grpc::Status removeUser(QString const &userID); ///< Performs the 'removeUser' call.
|
||||||
|
grpc::Status configureAppleMail(QString const& userID, QString const &address); ///< Performs the 'configureAppleMail' call.
|
||||||
|
grpc::Status setUserSplitMode(QString const& userID, bool active); ///< Performs the 'SetUserSplitMode' call.
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void toggleSplitModeFinished(QString const& userID);
|
||||||
|
void userDisconnected(QString const& username);
|
||||||
|
void userChanged(QString const& userID);
|
||||||
|
|
||||||
|
|
||||||
|
public: // keychain related calls
|
||||||
|
grpc::Status availableKeychains(QStringList &outKeychains);
|
||||||
|
grpc::Status currentKeychain(QString &outKeychain);
|
||||||
|
grpc::Status setCurrentKeychain(QString const &keychain);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void changeKeychainFinished();
|
||||||
|
void hasNoKeychain();
|
||||||
|
void rebuildKeychain();
|
||||||
|
|
||||||
|
signals: // mail releated events
|
||||||
|
void noActiveKeyForRecipient(QString const &email); // _ func(email string) `signal:noActiveKeyForRecipient`
|
||||||
|
void addressChanged(QString const &address); // _ func(address string) `signal:addressChanged`
|
||||||
|
void addressChangedLogout(QString const &address); // _ func(address string) `signal:addressChangedLogout`
|
||||||
|
void apiCertIssue();
|
||||||
|
|
||||||
|
public:
|
||||||
|
grpc::Status startEventStream(); ///< Retrieve and signal the events in the event stream.
|
||||||
|
grpc::Status stopEventStream(); ///< Stop the event stream.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
grpc::Status simpleMethod(SimpleMethod method); ///< perform a gRPC call to a bool setter.
|
||||||
|
grpc::Status setBool(BoolSetter setter, bool value); ///< perform a gRPC call to a bool setter.
|
||||||
|
grpc::Status getBool(BoolGetter getter, bool& outValue); ///< perform a gRPC call to a bool getter.
|
||||||
|
grpc::Status setInt32(Int32Setter setter, int value); ///< perform a gRPC call to an int setter.
|
||||||
|
grpc::Status getInt32(Int32Getter getter, int& outValue); ///< perform a gRPC call to an int getter.
|
||||||
|
grpc::Status setString(StringSetter getter, QString const& value); ///< Perform a gRPC call to a string setter.
|
||||||
|
grpc::Status getString(StringGetter getter, QString& outValue); ///< Perform a gRPC call to a string getter.
|
||||||
|
grpc::Status getURLForLocalFile(StringGetter getter, QUrl& outValue); ///< Perform a gRPC call to a string getter, with resulted converted to QUrl for a local file path.
|
||||||
|
grpc::Status getURL(StringGetter getter, QUrl& outValue); ///< Perform a gRPC call to a string getter, with resulted converted to QUrl.
|
||||||
|
grpc::Status methodWithStringParam(StringParamMethod method, QString const& str); ///< Perfom a gRPC call that takes a string as a parameter and returns an Empty.
|
||||||
|
|
||||||
|
void processAppEvent(grpc::AppEvent const &event); ///< Process an 'App' event.
|
||||||
|
void processLoginEvent(grpc::LoginEvent const &event); ///< Process a 'Login' event.
|
||||||
|
void processUpdateEvent(grpc::UpdateEvent const &event); ///< Process an 'Update' event.
|
||||||
|
void processCacheEvent(grpc::CacheEvent const &event); ///< Process a 'Cache' event.
|
||||||
|
void processMailSettingsEvent(grpc::MailSettingsEvent const &event); ///< Process a 'MailSettings' event.
|
||||||
|
void processKeychainEvent(grpc::KeychainEvent const &event); ///< Process a 'Keychain' event.
|
||||||
|
void processMailEvent(grpc::MailEvent const &event); ///< Process a 'Mail' event.
|
||||||
|
void processUserEvent(grpc::UserEvent const &event); ///< Process a 'User' event.
|
||||||
|
|
||||||
|
private: // data members.
|
||||||
|
std::shared_ptr<grpc::Channel> channel_ { nullptr }; ///< The gRPC channel.
|
||||||
|
std::shared_ptr<grpc::Bridge::Stub> stub_ { nullptr }; ///< The gRPC stub (a.k.a. client).
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // BRIDGE_QT6_RPC_CLIENT_H
|
||||||
63
internal/frontend/qt6/GRPC/GRPCUtils.cpp
Normal file
63
internal/frontend/qt6/GRPC/GRPCUtils.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#include "GRPCUtils.h"
|
||||||
|
#include "QMLBackend.h"
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] status The status
|
||||||
|
/// \param[in] callName The call name.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void logGRPCCallStatus(grpc::Status const& status, QString const &callName)
|
||||||
|
{
|
||||||
|
if (status.ok())
|
||||||
|
app().log().debug(QString("%1()").arg(callName));
|
||||||
|
else
|
||||||
|
app().log().error(QString("%1() FAILED").arg(callName));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] grpcUser the gRPC user struct
|
||||||
|
/// \return a user.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
SPUser parsegrpcUser(grpc::User const &grpcUser)
|
||||||
|
{
|
||||||
|
// As we want to use shared pointers here, we do not want to use the Qt ownership system, so we set parent to nil.
|
||||||
|
// But: From https://doc.qt.io/qt-5/qtqml-cppintegration-data.html:
|
||||||
|
// " When data is transferred from C++ to QML, the ownership of the data always remains with C++. The exception to this rule
|
||||||
|
// is when a QObject is returned from an explicit C++ method call: in this case, the QML engine assumes ownership of the object. "
|
||||||
|
// This is the case here, so we explicitely indicate that the object is owned by C++.
|
||||||
|
SPUser user = std::make_shared<User>(nullptr);
|
||||||
|
QQmlEngine::setObjectOwnership(user.get(), QQmlEngine::CppOwnership);
|
||||||
|
user->setProperty("username", QString::fromStdString(grpcUser.username()));
|
||||||
|
user->setProperty("avatarText", QString::fromStdString(grpcUser.avatartext()));
|
||||||
|
user->setProperty("loggedIn", grpcUser.loggedin());
|
||||||
|
user->setProperty("splitMode", grpcUser.splitmode());
|
||||||
|
user->setProperty("setupGuideSeen", grpcUser.setupguideseen());
|
||||||
|
user->setProperty("usedBytes", float(grpcUser.usedbytes()));
|
||||||
|
user->setProperty("totalBytes", float(grpcUser.totalbytes()));
|
||||||
|
user->setProperty("password", QString::fromStdString(grpcUser.password()));
|
||||||
|
QStringList addresses;
|
||||||
|
for (int j = 0; j < grpcUser.addresses_size(); ++j)
|
||||||
|
addresses.append(QString::fromStdString(grpcUser.addresses(j)));
|
||||||
|
user->setProperty("addresses", addresses);
|
||||||
|
user->setProperty("id", QString::fromStdString(grpcUser.id()));
|
||||||
|
return user;
|
||||||
|
}
|
||||||
@ -15,20 +15,16 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//go:build darwin && build_qt
|
|
||||||
// +build darwin,build_qt
|
|
||||||
|
|
||||||
package dockicon
|
#ifndef BRIDGE_QT6_GRPCUTILS_H
|
||||||
|
#define BRIDGE_QT6_GRPCUTILS_H
|
||||||
|
|
||||||
// #cgo CFLAGS: -x objective-c
|
#include "GRPC/bridge.grpc.pb.h"
|
||||||
// #cgo LDFLAGS: -framework Cocoa
|
#include "grpc++/grpc++.h"
|
||||||
// #include "DockIcon.h"
|
#include "User/User.h"
|
||||||
import "C"
|
|
||||||
|
|
||||||
func SetDockIconVisibleState(visible bool) {
|
|
||||||
C.SetDockIconVisibleState(C.bool(visible))
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetDockIconVisibleState() bool {
|
void logGRPCCallStatus(grpc::Status const& status, QString const &callName); ///< Log the status of a gRPC code.
|
||||||
return bool(C.GetDockIconVisibleState())
|
SPUser parsegrpcUser(grpc::User const& grpcUser); ///< Parse a gRPC user struct and return a User.
|
||||||
}
|
|
||||||
|
#endif // BRIDGE_QT6_GRPCUTILS_H
|
||||||
2179
internal/frontend/qt6/GRPC/bridge.grpc.pb.cc
Normal file
2179
internal/frontend/qt6/GRPC/bridge.grpc.pb.cc
Normal file
File diff suppressed because it is too large
Load Diff
8146
internal/frontend/qt6/GRPC/bridge.grpc.pb.h
Normal file
8146
internal/frontend/qt6/GRPC/bridge.grpc.pb.h
Normal file
File diff suppressed because it is too large
Load Diff
12646
internal/frontend/qt6/GRPC/bridge.pb.cc
Normal file
12646
internal/frontend/qt6/GRPC/bridge.pb.cc
Normal file
File diff suppressed because it is too large
Load Diff
14902
internal/frontend/qt6/GRPC/bridge.pb.h
Normal file
14902
internal/frontend/qt6/GRPC/bridge.pb.h
Normal file
File diff suppressed because it is too large
Load Diff
159
internal/frontend/qt6/Log.cpp
Normal file
159
internal/frontend/qt6/Log.cpp
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#include "Pch.h"
|
||||||
|
#include "Log.h"
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
Log::Log()
|
||||||
|
: QObject()
|
||||||
|
, stdout_(stdout)
|
||||||
|
, stderr_(stderr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] level The log level.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void Log::setLevel(Log::Level level)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&mutex_);
|
||||||
|
level_ = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \return The log level.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
Log::Level Log::level() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&mutex_);
|
||||||
|
return Log::Level::Debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] value Should the log entries be sent to STDOUT/STDERR.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void Log::setEchoInConsole(bool value)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&mutex_);
|
||||||
|
echoInConsole_ = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \return true iff the log entries be should sent to STDOUT/STDERR.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
bool Log::echoInConsole() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&mutex_);
|
||||||
|
return echoInConsole_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] message The message
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void Log::debug(QString const &message)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&mutex_);
|
||||||
|
|
||||||
|
if (qint32(level_) > qint32(Level::Debug))
|
||||||
|
return;
|
||||||
|
|
||||||
|
emit debugEntryAdded(message);
|
||||||
|
QString const withPrefix = "[DEBUG] " + message;
|
||||||
|
emit entryAdded(withPrefix);
|
||||||
|
if (echoInConsole_)
|
||||||
|
{
|
||||||
|
stdout_ << withPrefix << "\n";
|
||||||
|
stdout_.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] message The message
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void Log::info(QString const &message)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&mutex_);
|
||||||
|
|
||||||
|
if (qint32(level_) > qint32(Level::Info))
|
||||||
|
return;
|
||||||
|
|
||||||
|
emit infoEntryAdded(message);
|
||||||
|
QString const withPrefix = "[INFO] " + message;
|
||||||
|
emit entryAdded(withPrefix);
|
||||||
|
if (echoInConsole_)
|
||||||
|
{
|
||||||
|
stdout_ << withPrefix << "\n";
|
||||||
|
stdout_.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void Log::warn(QString const &message)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&mutex_);
|
||||||
|
|
||||||
|
if (qint32(level_) > qint32(Level::Warn))
|
||||||
|
return;
|
||||||
|
|
||||||
|
emit infoEntryAdded(message);
|
||||||
|
QString const withPrefix = "[WARNING] " + message;
|
||||||
|
emit entryAdded(withPrefix);
|
||||||
|
if (echoInConsole_)
|
||||||
|
{
|
||||||
|
stdout_ << withPrefix << "\n";
|
||||||
|
stdout_.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// message
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void Log::error(QString const &message)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&mutex_);
|
||||||
|
|
||||||
|
if (qint32(level_) > qint32(Level::Error))
|
||||||
|
return;
|
||||||
|
|
||||||
|
emit infoEntryAdded(message);
|
||||||
|
QString const withPrefix = "[ERROR] " + message;
|
||||||
|
emit entryAdded(withPrefix);
|
||||||
|
if (echoInConsole_)
|
||||||
|
{
|
||||||
|
stderr_ << withPrefix << "\n";
|
||||||
|
stderr_.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
74
internal/frontend/qt6/Log.h
Normal file
74
internal/frontend/qt6/Log.h
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BRIDGE_QT6_LOG_H
|
||||||
|
#define BRIDGE_QT6_LOG_H
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \brief Basic log class. No logging to file. Four levels. Rebroadcast received log entries via Qt signals.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
class Log : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public: // data types.
|
||||||
|
enum class Level
|
||||||
|
{
|
||||||
|
Debug = 0, ///< Debug
|
||||||
|
Info = 1, ///< Info
|
||||||
|
Warn = 2, ///< Warn
|
||||||
|
Error = 3, ///< Error
|
||||||
|
}; ///< Log level class
|
||||||
|
|
||||||
|
public: // member functions.
|
||||||
|
Log(); ///< Default constructor.
|
||||||
|
Log(Log const &) = delete; ///< Disabled copy-constructor.
|
||||||
|
Log(Log &&) = delete; ///< Disabled assignment copy-constructor.
|
||||||
|
~Log() override = default; ///< Destructor.
|
||||||
|
Log &operator=(Log const &) = delete; ///< Disabled assignment operator.
|
||||||
|
Log &operator=(Log &&) = delete; ///< Disabled move assignment operator.
|
||||||
|
|
||||||
|
void setLevel(Level level); ///< Set the log level.
|
||||||
|
Level level() const; ///< Get the log level.
|
||||||
|
void setEchoInConsole(bool value); ///< Set if the log entries should be echoed in STDOUT/STDERR.
|
||||||
|
bool echoInConsole() const; ///< Check if the log entries should be echoed in STDOUT/STDERR.
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void debug(QString const &message); ///< Adds a debug entry in the log.
|
||||||
|
void info(QString const &message); ///< Adds an info entry to the log.
|
||||||
|
void warn(QString const &message); ///< Adds a warning entry to the log.
|
||||||
|
void error(QString const &message); ///< Adds an error entry to the log.
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void debugEntryAdded(QString const &); ///< Signal for debug entries.
|
||||||
|
void infoEntryAdded(QString const &message); ///< Signal for info entries.
|
||||||
|
void warnEntryAdded(QString const &message); ///< Signal for warning entries.
|
||||||
|
void errorEntryAdded(QString const &message); ///< Signal for error entries.
|
||||||
|
void entryAdded(QString const &message); ///< Signal emitted when any type of entry is added.
|
||||||
|
|
||||||
|
private: // data members
|
||||||
|
mutable QMutex mutex_; ///< The mutex.
|
||||||
|
Level level_{Level::Debug}; ///< The log level
|
||||||
|
bool echoInConsole_{false}; ///< Set if the log messages should be sent to STDOUT/STDERR.
|
||||||
|
|
||||||
|
QTextStream stdout_; ///< The stdout stream.
|
||||||
|
QTextStream stderr_; ///< The stderr stream.
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //BRIDGE_QT6_LOG_H
|
||||||
@ -15,10 +15,9 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// +build darwin
|
|
||||||
// +build build_qt
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <QtCore>
|
||||||
|
#include <QtQuick>
|
||||||
void SetDockIconVisibleState(bool visible);
|
#include <QtQml>
|
||||||
bool GetDockIconVisibleState();
|
#include <QtQuickControls2>
|
||||||
|
#include <AppController.h>
|
||||||
316
internal/frontend/qt6/QMLBackend.cpp
Normal file
316
internal/frontend/qt6/QMLBackend.cpp
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#include "Pch.h"
|
||||||
|
#include "QMLBackend.h"
|
||||||
|
#include "Exception.h"
|
||||||
|
#include "GRPC/GRPCClient.h"
|
||||||
|
#include "Worker/Overseer.h"
|
||||||
|
#include "EventStreamWorker.h"
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
QMLBackend::QMLBackend()
|
||||||
|
: QObject()
|
||||||
|
, users_(new UserList(this))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::init()
|
||||||
|
{
|
||||||
|
this->connectGrpcEvents();
|
||||||
|
|
||||||
|
QString error;
|
||||||
|
if (app().grpc().connectToServer(error))
|
||||||
|
app().log().info("Connected to backend via gRPC service.");
|
||||||
|
else
|
||||||
|
throw Exception(QString("Cannot connectToServer to go backend via gRPC: %1").arg(error));
|
||||||
|
|
||||||
|
eventStreamOverseer_ = std::make_unique<Overseer>(new EventStreamReader(nullptr), nullptr);
|
||||||
|
eventStreamOverseer_->startWorker(true);
|
||||||
|
|
||||||
|
this->retrieveUserList();
|
||||||
|
}
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::connectGrpcEvents()
|
||||||
|
{
|
||||||
|
GRPCClient *client = &app().grpc();
|
||||||
|
|
||||||
|
// app events
|
||||||
|
connect(client, &GRPCClient::internetStatus, this, [&](bool isOn) { if (isOn) emit internetOn(); else emit internetOff(); });
|
||||||
|
connect(client, &GRPCClient::toggleAutostartFinished, this, &QMLBackend::toggleAutostartFinished);
|
||||||
|
connect(client, &GRPCClient::resetFinished, this, &QMLBackend::resetFinished);
|
||||||
|
connect(client, &GRPCClient::reportBugFinished, this, &QMLBackend::reportBugFinished);
|
||||||
|
connect(client, &GRPCClient::reportBugSuccess, this, &QMLBackend::bugReportSendSuccess);
|
||||||
|
connect(client, &GRPCClient::reportBugError, this, &QMLBackend::bugReportSendError);
|
||||||
|
|
||||||
|
// cache events
|
||||||
|
connect(client, &GRPCClient::isCacheOnDiskEnabledChanged, this, &QMLBackend::isDiskCacheEnabledChanged);
|
||||||
|
connect(client, &GRPCClient::diskCachePathChanged, this, &QMLBackend::diskCachePathChanged);
|
||||||
|
connect(client, &GRPCClient::cacheUnavailable, this, &QMLBackend::cacheUnavailable); // _ func() `signal:"cacheUnavailable"`
|
||||||
|
connect(client, &GRPCClient::cacheCantMove, this, &QMLBackend::cacheCantMove);
|
||||||
|
connect(client, &GRPCClient::diskFull, this, &QMLBackend::diskFull);
|
||||||
|
connect(client, &GRPCClient::cacheLocationChangeSuccess, this, &QMLBackend::cacheLocationChangeSuccess);
|
||||||
|
connect(client, &GRPCClient::changeLocalCacheFinished, this, &QMLBackend::changeLocalCacheFinished);
|
||||||
|
|
||||||
|
// login events
|
||||||
|
connect(client, &GRPCClient::loginUsernamePasswordError, this, &QMLBackend::loginUsernamePasswordError);
|
||||||
|
connect(client, &GRPCClient::loginFreeUserError, this, &QMLBackend::loginFreeUserError);
|
||||||
|
connect(client, &GRPCClient::loginConnectionError, this, &QMLBackend::loginConnectionError);
|
||||||
|
connect(client, &GRPCClient::login2FARequested, this, &QMLBackend::login2FARequested);
|
||||||
|
connect(client, &GRPCClient::login2FAError, this, &QMLBackend::login2FAError);
|
||||||
|
connect(client, &GRPCClient::login2FAErrorAbort, this, &QMLBackend::login2FAErrorAbort);
|
||||||
|
connect(client, &GRPCClient::login2PasswordRequested, this, &QMLBackend::login2PasswordRequested);
|
||||||
|
connect(client, &GRPCClient::login2PasswordError, this, &QMLBackend::login2PasswordError);
|
||||||
|
connect(client, &GRPCClient::login2PasswordErrorAbort, this, &QMLBackend::login2PasswordErrorAbort);
|
||||||
|
connect(client, &GRPCClient::loginFinished, this, [&](QString const &userID) {
|
||||||
|
qint32 const index = users_->rowOfUserID(userID); emit loginFinished(index); });
|
||||||
|
connect(client, &GRPCClient::loginAlreadyLoggedIn, this, [&](QString const &userID) {
|
||||||
|
qint32 const index = users_->rowOfUserID(userID); emit loginAlreadyLoggedIn(index); });
|
||||||
|
|
||||||
|
// mail settings events
|
||||||
|
connect(client, &GRPCClient::portIssueIMAP, this, &QMLBackend::portIssueIMAP);
|
||||||
|
connect(client, &GRPCClient::portIssueSMTP, this, &QMLBackend::portIssueSMTP);
|
||||||
|
connect(client, &GRPCClient::toggleUseSSLFinished, this, &QMLBackend::toggleUseSSLFinished);
|
||||||
|
connect(client, &GRPCClient::changePortFinished, this, &QMLBackend::changePortFinished);
|
||||||
|
|
||||||
|
// keychain events
|
||||||
|
connect(client, &GRPCClient::changeKeychainFinished, this, &QMLBackend::changeKeychainFinished);
|
||||||
|
connect(client, &GRPCClient::hasNoKeychain, this, &QMLBackend::notifyHasNoKeychain);
|
||||||
|
connect(client, &GRPCClient::rebuildKeychain, this, &QMLBackend::notifyRebuildKeychain);
|
||||||
|
|
||||||
|
// mail events
|
||||||
|
connect(client, &GRPCClient::noActiveKeyForRecipient, this, &QMLBackend::noActiveKeyForRecipient);
|
||||||
|
connect(client, &GRPCClient::addressChanged, this, &QMLBackend::addressChanged);
|
||||||
|
connect(client, &GRPCClient::addressChangedLogout, this, &QMLBackend::addressChangedLogout);
|
||||||
|
connect(client, &GRPCClient::apiCertIssue, this, &QMLBackend::apiCertIssue);
|
||||||
|
|
||||||
|
// user events
|
||||||
|
connect(client, &GRPCClient::userDisconnected, this, &QMLBackend::userDisconnected);
|
||||||
|
users_->connectGRPCEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::retrieveUserList()
|
||||||
|
{
|
||||||
|
QList<SPUser> users;
|
||||||
|
logGRPCCallStatus(app().grpc().getUserList(users), "getUserList");
|
||||||
|
users_->reset(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::clearUserList()
|
||||||
|
{
|
||||||
|
users_->reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
QPoint QMLBackend::getCursorPos()
|
||||||
|
{
|
||||||
|
return QCursor::pos();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
bool QMLBackend::isPortFree(int port)
|
||||||
|
{
|
||||||
|
bool isFree = false;
|
||||||
|
logGRPCCallStatus(app().grpc().isPortFree(port, isFree), "isPortFree");
|
||||||
|
return isFree;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::guiReady()
|
||||||
|
{
|
||||||
|
logGRPCCallStatus(app().grpc().guiReady(), "guiReady");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::quit()
|
||||||
|
{
|
||||||
|
logGRPCCallStatus(app().grpc().quit(), "quit");
|
||||||
|
qApp->exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::restart()
|
||||||
|
{
|
||||||
|
logGRPCCallStatus(app().grpc().restart(), "restart");
|
||||||
|
app().log().error("RESTART is not implemented"); /// \todo GODT-1671 implement restart.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] active Should we activate autostart.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::toggleAutostart(bool active)
|
||||||
|
{
|
||||||
|
logGRPCCallStatus(app().grpc().setIsAutostartOn(active), "setIsAutostartOn");
|
||||||
|
emit isAutostartOnChanged(this->isAutostartOn());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] active The new state for the beta enabled property.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::toggleBeta(bool active)
|
||||||
|
{
|
||||||
|
logGRPCCallStatus(app().grpc().setisBetaEnabled(active), "setIsBetaEnabled");
|
||||||
|
emit isBetaEnabledChanged(this->isBetaEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] scheme the scheme name
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::changeColorScheme(QString const &scheme)
|
||||||
|
{
|
||||||
|
logGRPCCallStatus(app().grpc().setColorSchemeName(scheme), "setIsBetaEnabled");
|
||||||
|
emit colorSchemeNameChanged(this->colorSchemeName());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] makeItActive Should SSL for SMTP be enabled.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::toggleUseSSLforSMTP(bool makeItActive)
|
||||||
|
{
|
||||||
|
grpc::Status status = app().grpc().setUseSSLForSMTP(makeItActive);
|
||||||
|
logGRPCCallStatus(status, "setUseSSLForSMTP");
|
||||||
|
if (status.ok())
|
||||||
|
emit useSSLforSMTPChanged(makeItActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] imapPort The IMAP port.
|
||||||
|
/// \param[in] smtpPort The SMTP port.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::changePorts(int imapPort, int smtpPort)
|
||||||
|
{
|
||||||
|
grpc::Status status = app().grpc().changePorts(imapPort, smtpPort);
|
||||||
|
logGRPCCallStatus(status, "changePorts");
|
||||||
|
if (status.ok())
|
||||||
|
{
|
||||||
|
emit portIMAPChanged(imapPort);
|
||||||
|
emit portSMTPChanged(smtpPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] active Should DoH be active.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::toggleDoH(bool active)
|
||||||
|
{
|
||||||
|
grpc::Status status = app().grpc().setIsDoHEnabled(active);
|
||||||
|
logGRPCCallStatus(status, "toggleDoH");
|
||||||
|
if (status.ok())
|
||||||
|
emit isDoHEnabledChanged(active);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] keychain The new keychain.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::changeKeychain(QString const &keychain)
|
||||||
|
{
|
||||||
|
grpc::Status status = app().grpc().setCurrentKeychain(keychain);
|
||||||
|
logGRPCCallStatus(status, "setCurrentKeychain");
|
||||||
|
if (status.ok())
|
||||||
|
emit currentKeychainChanged(keychain);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] active Should automatic update be turned on.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::toggleAutomaticUpdate(bool active)
|
||||||
|
{
|
||||||
|
grpc::Status status = app().grpc().setIsAutomaticUpdateOn(active);
|
||||||
|
logGRPCCallStatus(status, "toggleAutomaticUpdate");
|
||||||
|
if (status.ok())
|
||||||
|
emit isAutomaticUpdateOnChanged(active);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::checkUpdates()
|
||||||
|
{
|
||||||
|
app().log().error(QString("%1() is not implemented.").arg(__FUNCTION__));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::installUpdate()
|
||||||
|
{
|
||||||
|
app().log().error(QString("%1() is not implemented.").arg(__FUNCTION__));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::triggerReset()
|
||||||
|
{
|
||||||
|
app().log().error(QString("%1() is not implemented.").arg(__FUNCTION__));
|
||||||
|
}
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::reportBug(QString const &description, QString const &address, QString const &emailClient,
|
||||||
|
bool includeLogs)
|
||||||
|
{
|
||||||
|
Q_UNUSED(includeLogs)
|
||||||
|
app().log().error(QString("%1() is not implemented.").arg(__FUNCTION__));
|
||||||
|
}
|
||||||
222
internal/frontend/qt6/QMLBackend.h
Normal file
222
internal/frontend/qt6/QMLBackend.h
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BRIDGE_QT6_QMLBACKEND_H
|
||||||
|
#define BRIDGE_QT6_QMLBACKEND_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <grpcpp/support/status.h>
|
||||||
|
#include "DockIcon/DockIcon.h"
|
||||||
|
#include "GRPC/GRPCClient.h"
|
||||||
|
#include "GRPC/GRPCUtils.h"
|
||||||
|
#include "Worker/Overseer.h"
|
||||||
|
#include "User/UserList.h"
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \brief Bridge C++ backend class.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
class QMLBackend: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public: // member functions.
|
||||||
|
QMLBackend(); ///< Default constructor.
|
||||||
|
QMLBackend(QMLBackend const &) = delete; ///< Disabled copy-constructor.
|
||||||
|
QMLBackend(QMLBackend &&) = delete; ///< Disabled assignment copy-constructor.
|
||||||
|
~QMLBackend() override = default; ///< Destructor.
|
||||||
|
QMLBackend &operator=(QMLBackend const &) = delete; ///< Disabled assignment operator.
|
||||||
|
QMLBackend &operator=(QMLBackend &&) = delete; ///< Disabled move assignment operator.
|
||||||
|
void init(); ///< Initialize the backend.
|
||||||
|
void clearUserList(); ///< Clear the user list.
|
||||||
|
|
||||||
|
// invokable methods can be called from QML. They generally return a value, which slots cannot do.
|
||||||
|
Q_INVOKABLE static QPoint getCursorPos(); // _ func() *core.QPoint `slot:"getCursorPos"`
|
||||||
|
Q_INVOKABLE static bool isPortFree(int port); // _ func(port int) bool `slot:"isPortFree"`
|
||||||
|
|
||||||
|
public: // Qt/QML properties. Note that the NOTIFY-er signal is required even for read-only properties (QML warning otherwise)
|
||||||
|
Q_PROPERTY(bool showOnStartup READ showOnStartup NOTIFY showOnStartupChanged) // _ bool `property:showOnStartup`
|
||||||
|
Q_PROPERTY(bool showSplashScreen READ showSplashScreen NOTIFY showSplashScreenChanged) // _ bool `property:showSplashScreen`
|
||||||
|
Q_PROPERTY(QString goos READ goos NOTIFY goosChanged) // _ string `property:"goos"`
|
||||||
|
Q_PROPERTY(QUrl logsPath READ logsPath NOTIFY logsPathChanged) // _ core.QUrl `property:"logsPath"`
|
||||||
|
Q_PROPERTY(QUrl licensePath READ licensePath NOTIFY licensePathChanged) // _ core.QUrl `property:"licensePath"`
|
||||||
|
Q_PROPERTY(QUrl releaseNotesLink READ releaseNotesLink WRITE setReleaseNotesLink NOTIFY releaseNotesLinkChanged) // _ core.QUrl `property:"releaseNotesLink"`
|
||||||
|
Q_PROPERTY(QUrl dependencyLicensesLink READ dependencyLicensesLink NOTIFY dependencyLicensesLinkChanged) // _ core.QUrl `property:"dependencyLicensesLink"`
|
||||||
|
Q_PROPERTY(QUrl landingPageLink READ landingPageLink WRITE setLandingPageLink NOTIFY landingPageLinkChanged) // _ core.QUrl `property:"landingPageLink"`
|
||||||
|
Q_PROPERTY(QString version READ version NOTIFY versionChanged) // _ string `property:"version"`
|
||||||
|
Q_PROPERTY(QString hostname READ hostname NOTIFY hostnameChanged) // _ string `property:"hostname"`
|
||||||
|
Q_PROPERTY(bool isAutostartOn READ isAutostartOn NOTIFY isAutostartOnChanged) // _ bool `property:"isAutostartOn"`
|
||||||
|
Q_PROPERTY(bool isBetaEnabled READ isBetaEnabled NOTIFY isBetaEnabledChanged) // _ bool `property:"isBetaEnabled"`
|
||||||
|
Q_PROPERTY(QString colorSchemeName READ colorSchemeName NOTIFY colorSchemeNameChanged) // _ string `property:"colorSchemeName"`
|
||||||
|
Q_PROPERTY(bool isDiskCacheEnabled READ isDiskCacheEnabled NOTIFY isDiskCacheEnabledChanged) // _ bool `property:"isDiskCacheEnabled"`
|
||||||
|
Q_PROPERTY(QUrl diskCachePath READ diskCachePath NOTIFY diskCachePathChanged) // _ core.QUrl `property:"diskCachePath"`
|
||||||
|
Q_PROPERTY(bool useSSLforSMTP READ useSSLForSMTP NOTIFY useSSLforSMTPChanged) // _ bool `property:"useSSLforSMTP"`
|
||||||
|
Q_PROPERTY(int portIMAP READ portIMAP NOTIFY portIMAPChanged) // _ int `property:"portIMAP"`
|
||||||
|
Q_PROPERTY(int portSMTP READ portSMTP NOTIFY portSMTPChanged) // _ int `property:"portSMTP"`
|
||||||
|
Q_PROPERTY(bool isDoHEnabled READ isDoHEnabled NOTIFY isDoHEnabledChanged) // _ bool `property:"isDoHEnabled"`
|
||||||
|
Q_PROPERTY(bool isFirstGUIStart READ isFirstGUIStart) // _ bool `property:"isFirstGUIStart"`
|
||||||
|
Q_PROPERTY(bool isAutomaticUpdateOn READ isAutomaticUpdateOn NOTIFY isAutomaticUpdateOnChanged) // _ bool `property:"isAutomaticUpdateOn"`
|
||||||
|
Q_PROPERTY(QString currentEmailClient READ currentEmailClient NOTIFY currentEmailClientChanged) // _ string `property:"currentEmailClient"`
|
||||||
|
Q_PROPERTY(QStringList availableKeychain READ availableKeychain NOTIFY availableKeychainChanged) // _ []string `property:"availableKeychain"`
|
||||||
|
Q_PROPERTY(QString currentKeychain READ currentKeychain NOTIFY currentKeychainChanged) // _ string `property:"currentKeychain"`
|
||||||
|
Q_PROPERTY(UserList* users MEMBER users_ NOTIFY usersChanged)
|
||||||
|
Q_PROPERTY(bool dockIconVisible READ dockIconVisible WRITE setDockIconVisible NOTIFY dockIconVisibleChanged) // _ bool `property:dockIconVisible`
|
||||||
|
|
||||||
|
// Qt Property system setters & getters.
|
||||||
|
bool showOnStartup() const { bool v = false; logGRPCCallStatus(app().grpc().showOnStartup(v), "showOnStartup"); return v; };
|
||||||
|
bool showSplashScreen() { bool show = false; logGRPCCallStatus(app().grpc().showSplashScreen(show), "showSplashScreen"); return show; }
|
||||||
|
QString goos() { QString goos; logGRPCCallStatus(app().grpc().goos(goos), "goos"); return goos; }
|
||||||
|
QUrl logsPath() const { QUrl path; logGRPCCallStatus(app().grpc().logsPath(path), "logsPath"); return path;}
|
||||||
|
QUrl licensePath() const { QUrl path; logGRPCCallStatus(app().grpc().licensePath(path), "licensePath"); return path; }
|
||||||
|
QUrl releaseNotesLink() const { return releaseNotesLink_; }
|
||||||
|
void setReleaseNotesLink(QUrl const& url) { if (url != releaseNotesLink_) { releaseNotesLink_ = url; emit releaseNotesLinkChanged(url); } }
|
||||||
|
QUrl dependencyLicensesLink() const { QUrl link; logGRPCCallStatus(app().grpc().dependencyLicensesLink(link), "dependencyLicensesLink"); return link; }
|
||||||
|
QUrl landingPageLink() const { return landingPageLink_; }
|
||||||
|
void setLandingPageLink(QUrl const& url) { if (url != landingPageLink_) { landingPageLink_ = url; emit landingPageLinkChanged(url); } }
|
||||||
|
QString version() const { QString version; logGRPCCallStatus(app().grpc().version(version), "version"); return version; }
|
||||||
|
QString hostname() const { QString hostname; logGRPCCallStatus(app().grpc().hostname(hostname), "hostname"); return hostname; }
|
||||||
|
bool isAutostartOn() const { bool v; logGRPCCallStatus(app().grpc().isAutostartOn(v), "isAutostartOn"); return v; };
|
||||||
|
bool isBetaEnabled() const { bool v; logGRPCCallStatus(app().grpc().isBetaEnabled(v), "isBetaEnabled"); return v; }
|
||||||
|
QString colorSchemeName() const { QString name; logGRPCCallStatus(app().grpc().colorSchemeName(name), "colorSchemeName"); return name; }
|
||||||
|
bool isDiskCacheEnabled() const { bool enabled; logGRPCCallStatus(app().grpc().isCacheOnDiskEnabled(enabled), "isCacheOnDiskEnabled"); return enabled;}
|
||||||
|
QUrl diskCachePath() const { QUrl path; logGRPCCallStatus(app().grpc().diskCachePath(path), "diskCachePath"); return path; }
|
||||||
|
bool useSSLForSMTP() const{ bool useSSL; logGRPCCallStatus(app().grpc().useSSLForSMTP(useSSL), "useSSLForSMTP"); return useSSL; }
|
||||||
|
int portIMAP() const { int port; logGRPCCallStatus(app().grpc().portIMAP(port), "portIMAP"); return port; }
|
||||||
|
int portSMTP() const { int port; logGRPCCallStatus(app().grpc().portSMTP(port), "portSMTP"); return port; }
|
||||||
|
bool isDoHEnabled() const { bool isEnabled; logGRPCCallStatus(app().grpc().isDoHEnabled(isEnabled), "isDoHEnabled"); return isEnabled;}
|
||||||
|
bool isFirstGUIStart() const { bool v; logGRPCCallStatus(app().grpc().isFirstGUIStart(v), "isFirstGUIStart"); return v; };
|
||||||
|
bool isAutomaticUpdateOn() const { bool isOn = false; logGRPCCallStatus(app().grpc().isAutomaticUpdateOn(isOn), "isAutomaticUpdateOn"); return isOn; }
|
||||||
|
QString currentEmailClient() { QString client; logGRPCCallStatus(app().grpc().currentEmailClient(client), "currentEmailClient"); return client;}
|
||||||
|
QStringList availableKeychain() const { QStringList keychains; logGRPCCallStatus(app().grpc().availableKeychains(keychains), "availableKeychain"); return keychains; }
|
||||||
|
QString currentKeychain() const { QString keychain; logGRPCCallStatus(app().grpc().currentKeychain(keychain), "currentKeychain"); return keychain; }
|
||||||
|
bool dockIconVisible() const { return getDockIconVisibleState(); };
|
||||||
|
void setDockIconVisible(bool visible) { setDockIconVisibleState(visible); emit dockIconVisibleChanged(visible); }
|
||||||
|
|
||||||
|
signals: // Signal used by the Qt property system. Many of them are unused but required to avoir warning from the QML engine.
|
||||||
|
void showSplashScreenChanged(bool value);
|
||||||
|
void showOnStartupChanged(bool value);
|
||||||
|
void goosChanged(QString const &value);
|
||||||
|
void isDiskCacheEnabledChanged(bool value);
|
||||||
|
void diskCachePathChanged(QUrl const &url);
|
||||||
|
void useSSLforSMTPChanged(bool value);
|
||||||
|
void isAutomaticUpdateOnChanged(bool value);
|
||||||
|
void isBetaEnabledChanged(bool value);
|
||||||
|
void colorSchemeNameChanged(QString const &scheme);
|
||||||
|
void isDoHEnabledChanged(bool value);
|
||||||
|
void logsPathChanged(QUrl const &path);
|
||||||
|
void licensePathChanged(QUrl const &path);
|
||||||
|
void releaseNotesLinkChanged(QUrl const &link);
|
||||||
|
void dependencyLicensesLinkChanged(QUrl const &link);
|
||||||
|
void landingPageLinkChanged(QUrl const &link);
|
||||||
|
void versionChanged(QString const &version);
|
||||||
|
void currentEmailClientChanged(QString const &email);
|
||||||
|
void currentKeychainChanged(QString const &keychain);
|
||||||
|
void availableKeychainChanged(QStringList const &keychains);
|
||||||
|
void hostnameChanged(QString const &hostname);
|
||||||
|
void isAutostartOnChanged(bool value);
|
||||||
|
void portIMAPChanged(int port);
|
||||||
|
void portSMTPChanged(int port);
|
||||||
|
void usersChanged(UserList* users);
|
||||||
|
void dockIconVisibleChanged(bool value);
|
||||||
|
|
||||||
|
public slots: // slot for signals received from QML -> To be forwarded to Bridge via RPC Client calls.
|
||||||
|
void toggleAutostart(bool active); // _ func(makeItActive bool) `slot:"toggleAutostart"`
|
||||||
|
void toggleBeta(bool active); // _ func(makeItActive bool) `slot:"toggleBeta"`
|
||||||
|
void changeColorScheme(QString const &scheme); // _ func(string) `slot:"changeColorScheme"`
|
||||||
|
void changeLocalCache(bool enable, QUrl const& path) { logGRPCCallStatus(app().grpc().changeLocalCache(enable, path), "changeLocalCache"); } // _ func(enableDiskCache bool, diskCachePath core.QUrl) `slot:"changeLocalCache"`
|
||||||
|
void login(QString const& username, QString const& password) { logGRPCCallStatus(app().grpc().login(username, password), "login");} // _ func(username, password string) `slot:"login"`
|
||||||
|
void login2FA(QString const& username, QString const& code) { logGRPCCallStatus(app().grpc().login2FA(username, code), "login2FA");} // _ func(username, code string) `slot:"login2FA"`
|
||||||
|
void login2Password(QString const& username, QString const& password) { logGRPCCallStatus(app().grpc().login2Passwords(username, password),
|
||||||
|
"login2Passwords");} // _ func(username, password string) `slot:"login2Password"`
|
||||||
|
void loginAbort(QString const& username){ logGRPCCallStatus(app().grpc().loginAbort(username), "loginAbort");} // _ func(username string) `slot:"loginAbort"`
|
||||||
|
void toggleUseSSLforSMTP(bool makeItActive); // _ func(makeItActive bool) `slot:"toggleUseSSLforSMTP"`
|
||||||
|
void changePorts(int imapPort, int smtpPort); // _ func(imapPort, smtpPort int) `slot:"changePorts"`
|
||||||
|
void toggleDoH(bool active); // _ func(makeItActive bool) `slot:"toggleDoH"`
|
||||||
|
void toggleAutomaticUpdate(bool makeItActive); // _ func(makeItActive bool) `slot:"toggleAutomaticUpdate"`
|
||||||
|
void updateCurrentMailClient() { emit currentEmailClientChanged(currentEmailClient()); } // _ func() `slot:"updateCurrentMailClient"`
|
||||||
|
void changeKeychain(QString const &keychain); // _ func(keychain string) `slot:"changeKeychain"`
|
||||||
|
void guiReady(); // _ func() `slot:"guiReady"`
|
||||||
|
void quit(); // _ func() `slot:"quit"`
|
||||||
|
void restart(); // _ func() `slot:"restart"`
|
||||||
|
void checkUpdates(); // _ func() `slot:"checkUpdates"`
|
||||||
|
void installUpdate(); // _ func() `slot:"installUpdate"`
|
||||||
|
void triggerReset(); // _ func() `slot:"triggerReset"`
|
||||||
|
void reportBug(QString const &description, QString const& address, QString const &emailClient, bool includeLogs); // _ func(description, address, emailClient string, includeLogs bool) `slot:"reportBug"`
|
||||||
|
|
||||||
|
signals: // Signals received from the Go backend, to be forwarded to QML
|
||||||
|
void toggleAutostartFinished(); // _ func() `signal:"toggleAutostartFinished"`
|
||||||
|
void cacheUnavailable(); // _ func() `signal:"cacheUnavailable"`
|
||||||
|
void cacheCantMove(); // _ func() `signal:"cacheCantMove"`
|
||||||
|
void cacheLocationChangeSuccess(); // _ func() `signal:"cacheLocationChangeSuccess"`
|
||||||
|
void diskFull(); // _ func() `signal:"diskFull"`
|
||||||
|
void changeLocalCacheFinished(); // _ func() `signal:"changeLocalCacheFinished"`
|
||||||
|
void loginUsernamePasswordError(QString const &errorMsg); // _ func(errorMsg string) `signal:"loginUsernamePasswordError"`
|
||||||
|
void loginFreeUserError(); // _ func() `signal:"loginFreeUserError"`
|
||||||
|
void loginConnectionError(QString const &errorMsg); // _ func(errorMsg string) `signal:"loginConnectionError"`
|
||||||
|
void login2FARequested(QString const &username); // _ func(username string) `signal:"login2FARequested"`
|
||||||
|
void login2FAError(QString const& errorMsg); // _ func(errorMsg string) `signal:"login2FAError"`
|
||||||
|
void login2FAErrorAbort(QString const& errorMsg); // _ func(errorMsg string) `signal:"login2FAErrorAbort"`
|
||||||
|
void login2PasswordRequested(); // _ func() `signal:"login2PasswordRequested"`
|
||||||
|
void login2PasswordError(QString const& errorMsg); // _ func(errorMsg string) `signal:"login2PasswordError"`
|
||||||
|
void login2PasswordErrorAbort(QString const& errorMsg); // _ func(errorMsg string) `signal:"login2PasswordErrorAbort"`
|
||||||
|
void loginFinished(int index); // _ func(index int) `signal:"loginFinished"`
|
||||||
|
void loginAlreadyLoggedIn(int index); // _ func(index int) `signal:"loginAlreadyLoggedIn"`
|
||||||
|
void updateManualReady(QString const& version); // _ func(version string) `signal:"updateManualReady"`
|
||||||
|
void updateManualRestartNeeded(); // _ func() `signal:"updateManualRestartNeeded"`
|
||||||
|
void updateManualError(); // _ func() `signal:"updateManualError"`
|
||||||
|
void updateForce(QString const& version); // _ func(version string) `signal:"updateForce"`
|
||||||
|
void updateForceError(); // _ func() `signal:"updateForceError"`
|
||||||
|
void updateSilentRestartNeeded(); // _ func() `signal:"updateSilentRestartNeeded"`
|
||||||
|
void updateSilentError(); // _ func() `signal:"updateSilentError"`
|
||||||
|
void updateIsLatestVersion(); // _ func() `signal:"updateIsLatestVersion"`
|
||||||
|
void checkUpdatesFinished(); // _ func() `signal:"checkUpdatesFinished"`
|
||||||
|
void toggleUseSSLFinished(); // _ func() `signal:"toggleUseSSLFinished"`
|
||||||
|
void changePortFinished(); // _ func() `signal:"changePortFinished"`
|
||||||
|
void portIssueIMAP(); // _ func() `signal:"portIssueIMAP"`
|
||||||
|
void portIssueSMTP(); // _ func() `signal:"portIssueSMTP"`
|
||||||
|
void changeKeychainFinished(); // _ func() `signal:"changeKeychainFinished"`
|
||||||
|
void notifyHasNoKeychain(); // _ func() `signal:"notifyHasNoKeychain"`
|
||||||
|
void notifyRebuildKeychain(); // _ func() `signal:"notifyRebuildKeychain"`
|
||||||
|
void noActiveKeyForRecipient(QString const& email); // _ func(email string) `signal:noActiveKeyForRecipient`
|
||||||
|
void addressChanged(QString const& address); // _ func(address string) `signal:addressChanged`
|
||||||
|
void addressChangedLogout(QString const& address); // _ func(address string) `signal:addressChangedLogout`
|
||||||
|
void apiCertIssue(); // _ func() `signal:apiCertIssue`
|
||||||
|
void userDisconnected(QString const& username); // _ func(username string) `signal:userDisconnected`
|
||||||
|
void internetOff(); // _ func() `signal:"internetOff"`
|
||||||
|
void internetOn(); // _ func() `signal:"internetOn"`
|
||||||
|
void resetFinished(); // _ func() `signal:"resetFinished"`
|
||||||
|
void reportBugFinished(); // _ func() `signal:"reportBugFinished"`
|
||||||
|
void bugReportSendSuccess(); // _ func() `signal:"bugReportSendSuccess"`
|
||||||
|
void bugReportSendError(); // _ func() `signal:"bugReportSendError"`
|
||||||
|
void showMainWindow(); // _ func() `signal:showMainWindow`
|
||||||
|
|
||||||
|
private: // member functions
|
||||||
|
void retrieveUserList(); ///< Retrieve the list of users via gRPC.
|
||||||
|
void connectGrpcEvents(); ///< Connect gRPC that need to be forwarded to QML via backend signals
|
||||||
|
|
||||||
|
private: // data members
|
||||||
|
UserList* users_ { nullptr }; ///< The user list. Owned by backend.
|
||||||
|
std::unique_ptr<Overseer> eventStreamOverseer_; ///< The event stream overseer.
|
||||||
|
QUrl releaseNotesLink_; /// Release notes is not stored in the backend, it's pushed by the update check so we keep a local copy of it. \todo GODT-1670 Check this is implemented.
|
||||||
|
QUrl landingPageLink_; /// Landing page link is not stored in the backend, it's pushed by the update check so we keep a local copy of it. \todo GODT-1670 Check this is implemented.
|
||||||
|
|
||||||
|
friend class AppController;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // BRIDGE_QT6_QMLBACKEND_H
|
||||||
95
internal/frontend/qt6/User/User.cpp
Normal file
95
internal/frontend/qt6/User/User.cpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#include "Pch.h"
|
||||||
|
#include "User.h"
|
||||||
|
#include "GRPC/GRPCUtils.h"
|
||||||
|
#include "GRPC/GRPCClient.h"
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] parent The parent object.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
User::User(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] user The user to copy from
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void User::update(User const &user)
|
||||||
|
{
|
||||||
|
this->setProperty("username", user.username_);
|
||||||
|
this->setProperty("avatarText", user.avatarText_);
|
||||||
|
this->setProperty("loggedIn", user.loggedIn_);
|
||||||
|
this->setProperty("splitMode", user.splitMode_);
|
||||||
|
this->setProperty("setupGuideSeen", user.setupGuideSeen_);
|
||||||
|
this->setProperty("usedBytes", user.usedBytes_);
|
||||||
|
this->setProperty("totalBytes", user.totalBytes_);
|
||||||
|
this->setProperty("password", user.password_);
|
||||||
|
this->setProperty("addresses", user.addresses_);
|
||||||
|
this->setProperty("id", user.id_);
|
||||||
|
}
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] makeItActive Should split mode be made active.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void User::toggleSplitMode(bool makeItActive)
|
||||||
|
{
|
||||||
|
logGRPCCallStatus(app().grpc().setUserSplitMode(id_, makeItActive), "toggleSplitMode");
|
||||||
|
}
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void User::logout()
|
||||||
|
{
|
||||||
|
logGRPCCallStatus(app().grpc().logoutUser(id_), "logoutUser");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void User::remove()
|
||||||
|
{
|
||||||
|
logGRPCCallStatus(app().grpc().removeUser(id_), "removeUser");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] address The email address to configure Apple Mail for.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void User::configureAppleMail(QString const &address)
|
||||||
|
{
|
||||||
|
logGRPCCallStatus(app().grpc().configureAppleMail(id_, address), "configureAppleMail");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
// The only purpose of this call is to forward to the QML application the toggleSplitModeFinished(userID) event
|
||||||
|
// that was received by the UserList model.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void User::emitToggleSplitModeFinished()
|
||||||
|
{
|
||||||
|
this->setProperty("splitMode", QVariant::fromValue(!this->property("splitMode").toBool()));
|
||||||
|
emit toggleSplitModeFinished();
|
||||||
|
}
|
||||||
93
internal/frontend/qt6/User/User.h
Normal file
93
internal/frontend/qt6/User/User.h
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BRIDGE_QT6_USER_H
|
||||||
|
#define BRIDGE_QT6_USER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "Log.h"
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \brief User class.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
class User : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public: // member functions.
|
||||||
|
explicit User(QObject *parent = nullptr); ///< Default constructor.
|
||||||
|
User(User const &) = delete; ///< Disabled copy-constructor.
|
||||||
|
User(User &&) = delete; ///< Disabled assignment copy-constructor.
|
||||||
|
~User() override = default; ///< Destructor.
|
||||||
|
User &operator=(User const &) = delete; ///< Disabled assignment operator.
|
||||||
|
User &operator=(User &&) = delete; ///< Disabled move assignment operator.
|
||||||
|
void update(User const &user); ///< Update the user
|
||||||
|
public slots:
|
||||||
|
// slots for QML generated calls
|
||||||
|
void toggleSplitMode(bool makeItActive); // _ func(makeItActive bool) `slot:"toggleSplitMode"`
|
||||||
|
void logout(); // _ func() `slot:"logout"`
|
||||||
|
void remove(); // _ func() `slot:"remove"`
|
||||||
|
void configureAppleMail(QString const &address); // _ func(address string) `slot:"configureAppleMail"`
|
||||||
|
|
||||||
|
// slots for external signals
|
||||||
|
void emitToggleSplitModeFinished();
|
||||||
|
|
||||||
|
public:
|
||||||
|
Q_PROPERTY(QString username MEMBER username_ NOTIFY usernameChanged) // _ string `property:"username"`
|
||||||
|
Q_PROPERTY(QString avatarText MEMBER avatarText_ NOTIFY avatarTextChanged) // _ string `property:"avatarText"`
|
||||||
|
Q_PROPERTY(bool loggedIn MEMBER loggedIn_ NOTIFY loggedInChanged) // _ bool `property:"loggedIn"`
|
||||||
|
Q_PROPERTY(bool splitMode MEMBER splitMode_ NOTIFY splitModeChanged) // _ bool `property:"splitMode"`
|
||||||
|
Q_PROPERTY(bool setupGuideSeen MEMBER setupGuideSeen_ NOTIFY setupGuideSeenChanged) // _ bool `property:"setupGuideSeen"`
|
||||||
|
Q_PROPERTY(float usedBytes MEMBER usedBytes_ NOTIFY usedBytesChanged) // _ float32 `property:"usedBytes"`
|
||||||
|
Q_PROPERTY(float totalBytes MEMBER totalBytes_ NOTIFY totalBytesChanged) // _ float32 `property:"totalBytes"`
|
||||||
|
Q_PROPERTY(QString password MEMBER password_ NOTIFY passwordChanged) // _ string `property:"password"`
|
||||||
|
Q_PROPERTY(QStringList addresses MEMBER addresses_ NOTIFY addressesChanged) // _ []string `property:"addresses"`
|
||||||
|
Q_PROPERTY(QString id MEMBER id_ NOTIFY idChanged) // _ string ID
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// signals used for Qt properties
|
||||||
|
void usernameChanged(QString const &username);
|
||||||
|
void avatarTextChanged(QString const &avatarText);
|
||||||
|
void loggedInChanged(bool loggedIn);
|
||||||
|
void splitModeChanged(bool splitMode);
|
||||||
|
void setupGuideSeenChanged(bool seen);
|
||||||
|
void usedBytesChanged(float byteCount);
|
||||||
|
void totalBytesChanged(float byteCount);
|
||||||
|
void passwordChanged(QString const &);
|
||||||
|
void addressesChanged(QStringList const &);
|
||||||
|
void idChanged(QStringList const &id);
|
||||||
|
void toggleSplitModeFinished();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString id_; ///< The userID.
|
||||||
|
QString username_; ///< The username
|
||||||
|
QString avatarText_; ///< The avatar text (i.e. initials of the user)
|
||||||
|
bool loggedIn_{false}; ///< Is the user logged in.
|
||||||
|
bool splitMode_{false}; ///< Is split mode active.
|
||||||
|
bool setupGuideSeen_{false}; ///< Has the setup guide been seen.
|
||||||
|
float usedBytes_{0.0f}; ///< The storage used by the user.
|
||||||
|
float totalBytes_{0.0f}; ///< The storage quota of the user.
|
||||||
|
QString password_; ///< The IMAP password of the user.
|
||||||
|
QStringList addresses_; ///< The email address list of the user.
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef std::shared_ptr<User> SPUser;
|
||||||
|
|
||||||
|
|
||||||
|
#endif // BRIDGE_QT6_USER_H
|
||||||
220
internal/frontend/qt6/User/UserList.cpp
Normal file
220
internal/frontend/qt6/User/UserList.cpp
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#include "Pch.h"
|
||||||
|
#include "UserList.h"
|
||||||
|
#include "GRPC/GRPCClient.h"
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] parent The parent object of the user list.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
UserList::UserList(QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
/// \todo use mutex to prevent concurrent access
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void UserList::connectGRPCEvents() const
|
||||||
|
{
|
||||||
|
GRPCClient* client = &app().grpc();
|
||||||
|
connect(client, &GRPCClient::userChanged, this, &UserList::onUserChanged);
|
||||||
|
connect(client, &GRPCClient::toggleSplitModeFinished, this, &UserList::onToggleSplitModeFinished);
|
||||||
|
}
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
int UserList::rowCount(QModelIndex const &) const
|
||||||
|
{
|
||||||
|
return users_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] index The index to retrieve data for.
|
||||||
|
/// \param[in] role The role to retrieve data for.
|
||||||
|
/// \return The data at the index for the given role.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
QVariant UserList::data(QModelIndex const &index, int role) const
|
||||||
|
{
|
||||||
|
/// This It does not seem to be used, but the method is required by the base class.
|
||||||
|
/// From the original QtThe recipe QML backend User model, the User is always returned, regardless of the role.
|
||||||
|
Q_UNUSED(role)
|
||||||
|
int const row = index.row();
|
||||||
|
if ((row < 0) || (row >= users_.size()))
|
||||||
|
return QVariant();
|
||||||
|
return QVariant::fromValue(users_[row].get());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] userID The userID.
|
||||||
|
/// \return the row of the user.
|
||||||
|
/// \return -1 if the userID is not in the list
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
int UserList::rowOfUserID(QString const &userID) const
|
||||||
|
{
|
||||||
|
for (qint32 row = 0; row < users_.count(); ++row)
|
||||||
|
if (userID == users_[row]->property("id"))
|
||||||
|
return row;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void UserList::reset()
|
||||||
|
{
|
||||||
|
this->beginResetModel();
|
||||||
|
users_.clear();
|
||||||
|
this->endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] users The new user list.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void UserList::reset(QList<SPUser> const &users)
|
||||||
|
{
|
||||||
|
this->beginResetModel();
|
||||||
|
users_ = users;
|
||||||
|
this->endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] user The user.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void UserList::appendUser(SPUser const& user)
|
||||||
|
{
|
||||||
|
int const size = users_.size();
|
||||||
|
this->beginInsertRows(QModelIndex(), size, size);
|
||||||
|
users_.append(user);
|
||||||
|
this->endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] row The row.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void UserList::removeUserAt(int row)
|
||||||
|
{
|
||||||
|
if ((row < 0) && (row >= users_.size()))
|
||||||
|
return;
|
||||||
|
this->beginRemoveRows(QModelIndex(), row, row);
|
||||||
|
users_.removeAt(row);
|
||||||
|
this->endRemoveRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] row The row.
|
||||||
|
/// \param[in] user The user.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void UserList::updateUserAtRow(int row, User const &user)
|
||||||
|
{
|
||||||
|
if ((row < 0) || (row >= users_.count()))
|
||||||
|
{
|
||||||
|
app().log().error(QString("invalid user at row %2 (user count = %2)").arg(row).arg(users_.count()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
users_[row]->update(user);
|
||||||
|
|
||||||
|
QModelIndex modelIndex = this->index(row);
|
||||||
|
emit dataChanged(modelIndex, modelIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] row The row.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
User *UserList::get(int row) const
|
||||||
|
{
|
||||||
|
if ((row < 0) || (row >= users_.count()))
|
||||||
|
{
|
||||||
|
app().log().error(QString("Requesting invalid user at row %1 (user count = %2)").arg(row).arg(users_.count()));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
app().log().debug(QString("Retrieving user at row %1 (user count = %2)").arg(row).arg(users_.count()));
|
||||||
|
return users_[row].get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] userID The userID.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void UserList::onUserChanged(QString const &userID)
|
||||||
|
{
|
||||||
|
int const index = this->rowOfUserID(userID);
|
||||||
|
SPUser user;
|
||||||
|
grpc::Status status = app().grpc().getUser(userID, user);
|
||||||
|
if ((!user) || (!status.ok()))
|
||||||
|
{
|
||||||
|
if (index >= 0) // user exists here but not in the go backend. we delete it.
|
||||||
|
{
|
||||||
|
app().log().debug(QString("Removing user from userlist: %1").arg(userID));
|
||||||
|
this->removeUserAt(index);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
app().log().debug(QString("Adding user in userlist: %1").arg(userID));
|
||||||
|
this->appendUser(user);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
app().log().debug(QString("Updating user in userlist: %1").arg(userID));
|
||||||
|
this->updateUserAtRow(index, *user);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// The only purpose of this function is to forward the toggleSplitModeFinished event received from gRPC to the
|
||||||
|
/// appropriate user.
|
||||||
|
///
|
||||||
|
/// \param[in] userID the userID.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void UserList::onToggleSplitModeFinished(QString const &userID)
|
||||||
|
{
|
||||||
|
int const index = this->rowOfUserID(userID);
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
app().log().error(QString("Received toggleSplitModeFinished event for unknown userID %1").arg(userID));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
users_[index]->emitToggleSplitModeFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \return THe number of items in the list.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
int UserList::count() const
|
||||||
|
{
|
||||||
|
return users_.size();
|
||||||
|
}
|
||||||
64
internal/frontend/qt6/User/UserList.h
Normal file
64
internal/frontend/qt6/User/UserList.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BRIDGE_QT6_USER_LIST_H
|
||||||
|
#define BRIDGE_QT6_USER_LIST_H
|
||||||
|
|
||||||
|
#include "User.h"
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \brief User list class.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
class UserList: public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public: // member functions.
|
||||||
|
explicit UserList(QObject *parent = nullptr); ///< Default constructor.
|
||||||
|
UserList(UserList const &other) = delete ; ///< Disabled copy-constructor.
|
||||||
|
UserList& operator=(UserList const& other) = delete; ///< Disabled assignment operator.
|
||||||
|
~UserList() override = default; ///< Destructor
|
||||||
|
void connectGRPCEvents() const; ///< Connects gRPC event to the model.
|
||||||
|
int rowCount(QModelIndex const &parent) const override; ///< Return the number of row in the model
|
||||||
|
QVariant data(QModelIndex const &index, int role) const override; ///< Retrieve model data.
|
||||||
|
void reset(); ///< Reset the user list.
|
||||||
|
void reset(QList<SPUser> const &users); ///< Replace the user list.
|
||||||
|
int rowOfUserID(QString const &userID) const;
|
||||||
|
void removeUserAt(int row); ///< Remove the user at a given row
|
||||||
|
void appendUser(SPUser const& user); ///< Add a new user.
|
||||||
|
void updateUserAtRow(int row, User const& user); ///< Update the user at given row.
|
||||||
|
|
||||||
|
// the count property.
|
||||||
|
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||||
|
int count() const; ///< The count property getter.
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void countChanged(int count); ///< Signal for the count property.
|
||||||
|
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE User* get(int row) const;
|
||||||
|
|
||||||
|
public slots: ///< handler for signals coming from the gRPC service
|
||||||
|
void onUserChanged(QString const &userID);
|
||||||
|
void onToggleSplitModeFinished(QString const &userID);
|
||||||
|
|
||||||
|
private: // data members
|
||||||
|
QList<SPUser> users_; ///< The user list.
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // BRIDGE_QT6_USER_LIST_H
|
||||||
103
internal/frontend/qt6/Worker/Overseer.cpp
Normal file
103
internal/frontend/qt6/Worker/Overseer.cpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#include "Pch.h"
|
||||||
|
#include "Overseer.h"
|
||||||
|
#include "Exception.h"
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] worker The worker.
|
||||||
|
/// \param[in] parent The parent object of the worker.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
Overseer::Overseer(Worker *worker, QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, thread_(new QThread(parent))
|
||||||
|
, worker_(worker)
|
||||||
|
{
|
||||||
|
if (!worker_)
|
||||||
|
throw Exception("Overseer cannot accept a nil worker.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
Overseer::~Overseer()
|
||||||
|
{
|
||||||
|
this->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] autorelease Should the overseer automatically release the worker and thread when done.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void Overseer::startWorker(bool autorelease) const
|
||||||
|
{
|
||||||
|
if (!worker_)
|
||||||
|
throw Exception("Cannot start overseer with null worker.");
|
||||||
|
if (!thread_)
|
||||||
|
throw Exception("Cannot start overseer with null thread.");
|
||||||
|
|
||||||
|
worker_->moveToThread(thread_);
|
||||||
|
connect(thread_, &QThread::started, worker_, &Worker::run);
|
||||||
|
connect(worker_, &Worker::finished, thread_, &QThread::quit);
|
||||||
|
connect(worker_, &Worker::error, thread_, &QThread::quit);
|
||||||
|
if (autorelease)
|
||||||
|
{
|
||||||
|
connect(worker_, &Worker::error, this, &Overseer::release);
|
||||||
|
connect(worker_, &Worker::finished, this, &Overseer::release);
|
||||||
|
}
|
||||||
|
thread_->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void Overseer::release()
|
||||||
|
{
|
||||||
|
if (worker_)
|
||||||
|
{
|
||||||
|
worker_->deleteLater();
|
||||||
|
worker_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread_)
|
||||||
|
{
|
||||||
|
if (!thread_->isFinished())
|
||||||
|
{
|
||||||
|
thread_->quit();
|
||||||
|
thread_->wait();
|
||||||
|
}
|
||||||
|
thread_->deleteLater();
|
||||||
|
thread_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \return true iff the worker is finished, release
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
bool Overseer::isFinished() const
|
||||||
|
{
|
||||||
|
if ((!worker_) || (!worker_->thread()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return worker_->thread()->isFinished();
|
||||||
|
}
|
||||||
55
internal/frontend/qt6/Worker/Overseer.h
Normal file
55
internal/frontend/qt6/Worker/Overseer.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BRIDGE_QT6_OVERSEER_H
|
||||||
|
#define BRIDGE_QT6_OVERSEER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "Worker.h"
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \brief Overseer used to manager a worker instance and its associated thread.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
class Overseer: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public: // member functions.
|
||||||
|
explicit Overseer(Worker* worker, QObject* parent); ///< Default constructor.
|
||||||
|
Overseer(Overseer const&) = delete; ///< Disabled copy-constructor.
|
||||||
|
Overseer(Overseer&&) = delete; ///< Disabled assignment copy-constructor.
|
||||||
|
~Overseer() override; ///< Destructor.
|
||||||
|
Overseer& operator=(Overseer const&) = delete; ///< Disabled assignment operator.
|
||||||
|
Overseer& operator=(Overseer&&) = delete; ///< Disabled move assignment operator.
|
||||||
|
bool isFinished() const; ///< Check if the worker is finished.
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void startWorker(bool autorelease) const; ///< Run the worker.
|
||||||
|
void release(); ///< Delete the worker and its thread.
|
||||||
|
|
||||||
|
public: // data members.
|
||||||
|
QThread *thread_ { nullptr }; ///< The thread.
|
||||||
|
Worker *worker_ { nullptr }; ///< The worker.
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef std::unique_ptr<Overseer> UPOverseer; ///< Type definition for unique pointer to Overseer.
|
||||||
|
typedef std::shared_ptr<Overseer> SPOverseer; ///< Type definition for shared pointer to Overseer.
|
||||||
|
|
||||||
|
|
||||||
|
#endif //BRIDGE_QT6_OVERSEER_H
|
||||||
46
internal/frontend/qt6/Worker/Worker.h
Normal file
46
internal/frontend/qt6/Worker/Worker.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BRIDGE_QT6_WORKER_H
|
||||||
|
#define BRIDGE_QT6_WORKER_H
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \brief Pure virtual class for worker intended to perform a threaded operation.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
class Worker: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public: // member functions
|
||||||
|
explicit Worker(QObject *parent) : QObject(parent) {} ///< Default constructor.
|
||||||
|
Worker(Worker const&) = delete; ///< Disabled copy-constructor.
|
||||||
|
Worker(Worker&&) = delete; ///< Disabled assignment copy-constructor.
|
||||||
|
~Worker() override = default; ///< Destructor.
|
||||||
|
Worker& operator=(Worker const&) = delete; ///< Disabled assignment operator.
|
||||||
|
Worker& operator=(Worker&&) = delete; ///< Disabled move assignment operator.
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
virtual void run() = 0; ///< run the worker.
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void started(); ///< Signal for the start of the worker
|
||||||
|
void finished(); ///< Signal for the end of the worker
|
||||||
|
void error(QString const& message); ///< Signal for errors. After an error, worker ends and finished is NOT emitted.
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //BRIDGE_QT6_WORKER_H
|
||||||
22
internal/frontend/qt6/build.sh
Executable file
22
internal/frontend/qt6/build.sh
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
# Copyright (c) 2022 Proton AG
|
||||||
|
#
|
||||||
|
# This file is part of Proton Mail Bridge.
|
||||||
|
#
|
||||||
|
# Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
BUILD_DIR="./cmake-build-debug"
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja -S . -B ${BUILD_DIR}
|
||||||
|
ninja -C ${BUILD_DIR}
|
||||||
135
internal/frontend/qt6/main.cpp
Normal file
135
internal/frontend/qt6/main.cpp
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
// Copyright (c) 2022 Proton AG
|
||||||
|
//
|
||||||
|
// This file is part of Proton Mail Bridge.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#include "Pch.h"
|
||||||
|
#include "Exception.h"
|
||||||
|
#include "QMLBackend.h"
|
||||||
|
#include "Log.h"
|
||||||
|
#include "EventStreamWorker.h"
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// // initialize the Qt application.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
std::shared_ptr<QGuiApplication> initQtApplication(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
// Note the two following attributes must be set before instantiating the QCoreApplication/QGuiApplication class.
|
||||||
|
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, false);
|
||||||
|
if (QSysInfo::productType() != "windows")
|
||||||
|
QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
|
||||||
|
|
||||||
|
QString const qsgInfo = QProcessEnvironment::systemEnvironment().value("QSG_INFO");
|
||||||
|
if ((!qsgInfo.isEmpty()) && (qsgInfo != "0"))
|
||||||
|
QLoggingCategory::setFilterRules("qt.scenegraph.general=true");
|
||||||
|
|
||||||
|
auto app = std::make_shared<QGuiApplication>(argc, argv);
|
||||||
|
|
||||||
|
/// \todo GODT-1670 Get version from go backend.
|
||||||
|
QGuiApplication::setApplicationName("Proton Mail Bridge");
|
||||||
|
QGuiApplication::setApplicationVersion("3.0");
|
||||||
|
QGuiApplication::setOrganizationName("Proton AG");
|
||||||
|
QGuiApplication::setOrganizationDomain("proton.ch");
|
||||||
|
QGuiApplication::setQuitOnLastWindowClosed(false);
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void initLog()
|
||||||
|
{
|
||||||
|
Log &log = app().log();
|
||||||
|
log.setEchoInConsole(true);
|
||||||
|
log.setLevel(Log::Level::Debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] engine The QML engine.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
QQmlComponent *createRootQmlComponent(QQmlApplicationEngine &engine)
|
||||||
|
{
|
||||||
|
/// \todo GODT-1669 pack QML and resources in QRC resource file.
|
||||||
|
QDir qmlDir("qml");
|
||||||
|
|
||||||
|
qmlRegisterType<QMLBackend>("CppBackend", 1, 0, "QMLBackend");
|
||||||
|
qmlRegisterType<UserList>("CppBackend", 1, 0, "UserList");
|
||||||
|
qmlRegisterType<User>("CppBackend", 1, 0, "User");
|
||||||
|
|
||||||
|
auto rootComponent = new QQmlComponent(&engine, &engine);
|
||||||
|
|
||||||
|
engine.addImportPath(qmlDir.absolutePath());
|
||||||
|
engine.addPluginPath(qmlDir.absolutePath());
|
||||||
|
|
||||||
|
QQuickStyle::addStylePath(qmlDir.absolutePath());
|
||||||
|
QQuickStyle::setStyle("Proton");
|
||||||
|
|
||||||
|
rootComponent->loadUrl(qmlDir.absoluteFilePath("Bridge.qml"));
|
||||||
|
if (rootComponent->status() != QQmlComponent::Status::Ready)
|
||||||
|
throw Exception("Could not load QML component");
|
||||||
|
|
||||||
|
return rootComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] argc The number of command-line arguments.
|
||||||
|
/// \param[in] argv The list of command-line arguments.
|
||||||
|
/// \return The exit code for the application.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::shared_ptr<QGuiApplication> guiApp = initQtApplication(argc, argv);
|
||||||
|
initLog();
|
||||||
|
|
||||||
|
/// \todo GODT-1667 Locate & Launch go backend (and wait for it).
|
||||||
|
|
||||||
|
app().backend().init();
|
||||||
|
|
||||||
|
QQmlApplicationEngine engine;
|
||||||
|
QQmlComponent *rootComponent = createRootQmlComponent(engine);
|
||||||
|
|
||||||
|
QObject *rootObject = rootComponent->beginCreate(engine.rootContext());
|
||||||
|
if (!rootObject)
|
||||||
|
throw Exception("Could not create root object.");
|
||||||
|
rootObject->setProperty("backend", QVariant::fromValue(&app().backend()));
|
||||||
|
rootComponent->completeCreate();
|
||||||
|
|
||||||
|
int result = QGuiApplication::exec();
|
||||||
|
|
||||||
|
app().log().info(QString("Exiting app with return code %1").arg(result));
|
||||||
|
|
||||||
|
app().grpc().stopEventStream();
|
||||||
|
app().backend().clearUserList();
|
||||||
|
|
||||||
|
/// \todo GODT-1667 shutdown go backend.
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception const &e)
|
||||||
|
{
|
||||||
|
app().log().error(e.qwhat());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -23,6 +23,7 @@ import QtQuick.Controls 2.12
|
|||||||
|
|
||||||
import Proton 4.0
|
import Proton 4.0
|
||||||
import Notifications 1.0
|
import Notifications 1.0
|
||||||
|
import CppBackend 1.0
|
||||||
|
|
||||||
import "tests"
|
import "tests"
|
||||||
|
|
||||||
@ -32,11 +33,14 @@ ApplicationWindow {
|
|||||||
width: 960
|
width: 960
|
||||||
height: 576
|
height: 576
|
||||||
|
|
||||||
|
visible: true
|
||||||
|
|
||||||
minimumHeight: contentLayout.implicitHeight
|
minimumHeight: contentLayout.implicitHeight
|
||||||
minimumWidth: contentLayout.implicitWidth
|
minimumWidth: contentLayout.implicitWidth
|
||||||
|
|
||||||
colorScheme: ProtonStyle.currentStyle
|
colorScheme: ProtonStyle.currentStyle
|
||||||
|
|
||||||
|
|
||||||
property var backend
|
property var backend
|
||||||
property var notifications
|
property var notifications
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user