mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2026-02-14 12:48:33 +00:00
chore: merge tmp/Quebec into devel
This commit is contained in:
@ -6,9 +6,12 @@
|
|||||||
* Go 1.18
|
* Go 1.18
|
||||||
* Bash with basic build utils: make, gcc, sed, find, grep, ...
|
* Bash with basic build utils: make, gcc, sed, find, grep, ...
|
||||||
- For Windows, it is recommended to use MinGW 64bit shell from [MSYS2](https://www.msys2.org/)
|
- For Windows, it is recommended to use MinGW 64bit shell from [MSYS2](https://www.msys2.org/)
|
||||||
* GCC (linux), msvc (windows) or Xcode (macOS)
|
* GCC (Linux), msvc (Windows) or Xcode (macOS)
|
||||||
* Windres (windows)
|
* Windres (Windows)
|
||||||
* libglvnd and libsecret development files (linux)
|
* libglvnd and libsecret development files (Linux)
|
||||||
|
* pkg-config (Linux)
|
||||||
|
* cmake, ninja-build and Qt 6 are required to build the graphical user interface. On Linux,
|
||||||
|
the Mesa OpenGL development files are also needed.
|
||||||
|
|
||||||
To enable the sending of crash reports using Sentry please set the
|
To enable the sending of crash reports using Sentry please set the
|
||||||
`DSN_SENTRY` environment variable with the client key of your sentry project before build.
|
`DSN_SENTRY` environment variable with the client key of your sentry project before build.
|
||||||
|
|||||||
35
Changelog.md
35
Changelog.md
@ -2,13 +2,46 @@
|
|||||||
|
|
||||||
Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
||||||
|
|
||||||
|
## [Bridge 3.2.0] Rialto
|
||||||
|
|
||||||
|
### Added
|
||||||
|
* GODT-2552, GODT-2553, GODT-2555, GODT-2556: Add telemetry.
|
||||||
|
* GODT-2575: Add dev info to cookies.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* GODT-2496: Bump gopenPGP to 2.7.1-proton.
|
||||||
|
* GODT-2517: Replace status window with native tray icon context menu.
|
||||||
|
* GODT-2586: Two-columns layout for account details.
|
||||||
|
* GODT-2580: Updated link to support website in GUI.
|
||||||
|
* GODT-2239: Bridgepp worker/overseer unit tests.
|
||||||
|
* GODT-2538: Implement smart picking of default IMAP/SMTP ports.
|
||||||
|
* GODT-2502: Improve logs.
|
||||||
|
* GODT-2551: Store and Recover Last User Agent from Vault.
|
||||||
|
* GODT-2550: Verify IMAP ID is set properly.
|
||||||
|
* GODT-2554: Compute telemetry availability from API UserSettings.
|
||||||
|
* Add missing double quotes in test.
|
||||||
|
* GODT-2239: Unit tests for BridgeUtils.cpp in bridgepp.
|
||||||
|
* Replace go-rfc5322 with gluon's rfc5322 parser.
|
||||||
|
* GODT-2483: Install cert without external tool on macOS.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* GODT-2588: Always perma-delete from Drafts/Trash.
|
||||||
|
* GODT-2582: Dedup recovered messages folder.
|
||||||
|
* GODT-2589: Update BUILDS.md.
|
||||||
|
* GODT-2581: Update outdated link to bridge homepage in CLI 'manual' command.
|
||||||
|
* GODT-2337: Filter reply-to on draft.
|
||||||
|
* GODT-2550: Announce IMAP ID Capability.
|
||||||
|
* GODT-2574: Fix label/unlabel of large amounts of messages.
|
||||||
|
* GODT-2573: Handle invalid header fields in message.
|
||||||
|
* GODT-2573: Crash on null update.
|
||||||
|
* GODT-2407: Replace invalid email addresses with emtpy for new Drafts.
|
||||||
|
|
||||||
|
|
||||||
## [Bridge 3.1.2] Quebec
|
## [Bridge 3.1.2] Quebec
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* GODT-2582 Dedup recovered messages folder.
|
* GODT-2582 Dedup recovered messages folder.
|
||||||
|
|
||||||
|
|
||||||
## [Bridge 3.1.1] Quebec
|
## [Bridge 3.1.1] Quebec
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
4
Makefile
4
Makefile
@ -11,7 +11,7 @@ ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
|||||||
.PHONY: build build-gui build-nogui build-launcher versioner hasher
|
.PHONY: build build-gui build-nogui build-launcher versioner hasher
|
||||||
|
|
||||||
# Keep version hardcoded so app build works also without Git repository.
|
# Keep version hardcoded so app build works also without Git repository.
|
||||||
BRIDGE_APP_VERSION?=3.1.2+git
|
BRIDGE_APP_VERSION?=3.2.0+git
|
||||||
APP_VERSION:=${BRIDGE_APP_VERSION}
|
APP_VERSION:=${BRIDGE_APP_VERSION}
|
||||||
APP_FULL_NAME:=Proton Mail Bridge
|
APP_FULL_NAME:=Proton Mail Bridge
|
||||||
APP_VENDOR:=Proton AG
|
APP_VENDOR:=Proton AG
|
||||||
@ -256,6 +256,8 @@ mocks:
|
|||||||
mockgen --package mocks github.com/ProtonMail/gluon/async PanicHandler > internal/bridge/mocks/async_mocks.go
|
mockgen --package mocks github.com/ProtonMail/gluon/async PanicHandler > internal/bridge/mocks/async_mocks.go
|
||||||
mockgen --package mocks github.com/ProtonMail/gluon/reporter Reporter > internal/bridge/mocks/gluon_mocks.go
|
mockgen --package mocks github.com/ProtonMail/gluon/reporter Reporter > internal/bridge/mocks/gluon_mocks.go
|
||||||
mockgen --package mocks github.com/ProtonMail/proton-bridge/v3/internal/updater Downloader,Installer > internal/updater/mocks/mocks.go
|
mockgen --package mocks github.com/ProtonMail/proton-bridge/v3/internal/updater Downloader,Installer > internal/updater/mocks/mocks.go
|
||||||
|
mockgen --package mocks github.com/ProtonMail/proton-bridge/v3/internal/telemetry HeartbeatManager > internal/telemetry/mocks/mocks.go
|
||||||
|
cp internal/telemetry/mocks/mocks.go internal/bridge/mocks/telemetry_mocks.go
|
||||||
|
|
||||||
lint: gofiles lint-golang lint-license lint-dependencies lint-changelog
|
lint: gofiles lint-golang lint-license lint-dependencies lint-changelog
|
||||||
|
|
||||||
|
|||||||
20
go.mod
20
go.mod
@ -5,10 +5,10 @@ go 1.18
|
|||||||
require (
|
require (
|
||||||
github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557
|
github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557
|
||||||
github.com/Masterminds/semver/v3 v3.2.0
|
github.com/Masterminds/semver/v3 v3.2.0
|
||||||
github.com/ProtonMail/gluon v0.15.1-0.20230426114936-a356ef7f2753
|
github.com/ProtonMail/gluon v0.16.1-0.20230428090920-2797a1764f16
|
||||||
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a
|
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a
|
||||||
github.com/ProtonMail/go-proton-api v0.4.1-0.20230406143739-c7596e170799
|
github.com/ProtonMail/go-proton-api v0.4.1-0.20230426081144-f77778bae1be
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.5.2
|
github.com/ProtonMail/gopenpgp/v2 v2.7.1-proton
|
||||||
github.com/PuerkitoBio/goquery v1.8.1
|
github.com/PuerkitoBio/goquery v1.8.1
|
||||||
github.com/abiosoft/ishell v2.0.0+incompatible
|
github.com/abiosoft/ishell v2.0.0+incompatible
|
||||||
github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37
|
github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37
|
||||||
@ -43,9 +43,9 @@ require (
|
|||||||
github.com/vmihailenco/msgpack/v5 v5.3.5
|
github.com/vmihailenco/msgpack/v5 v5.3.5
|
||||||
go.uber.org/goleak v1.2.1
|
go.uber.org/goleak v1.2.1
|
||||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb
|
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb
|
||||||
golang.org/x/net v0.7.0
|
golang.org/x/net v0.8.0
|
||||||
golang.org/x/sys v0.5.0
|
golang.org/x/sys v0.6.0
|
||||||
golang.org/x/text v0.7.0
|
golang.org/x/text v0.8.0
|
||||||
google.golang.org/grpc v1.53.0
|
google.golang.org/grpc v1.53.0
|
||||||
google.golang.org/protobuf v1.28.1
|
google.golang.org/protobuf v1.28.1
|
||||||
howett.net/plist v1.0.0
|
howett.net/plist v1.0.0
|
||||||
@ -55,8 +55,8 @@ require (
|
|||||||
ariga.io/atlas v0.9.1-0.20230119145809-92243f7c55cb // indirect
|
ariga.io/atlas v0.9.1-0.20230119145809-92243f7c55cb // indirect
|
||||||
entgo.io/ent v0.11.8 // indirect
|
entgo.io/ent v0.11.8 // indirect
|
||||||
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf // indirect
|
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf // indirect
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect
|
github.com/ProtonMail/go-crypto v0.0.0-20230322105811-d73448b7e800 // indirect
|
||||||
github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08 // indirect
|
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect
|
||||||
github.com/ProtonMail/go-srp v0.0.5 // indirect
|
github.com/ProtonMail/go-srp v0.0.5 // indirect
|
||||||
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect
|
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect
|
||||||
github.com/agext/levenshtein v1.2.3 // indirect
|
github.com/agext/levenshtein v1.2.3 // indirect
|
||||||
@ -116,10 +116,10 @@ require (
|
|||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
github.com/zclconf/go-cty v1.12.1 // indirect
|
github.com/zclconf/go-cty v1.12.1 // indirect
|
||||||
golang.org/x/arch v0.2.0 // indirect
|
golang.org/x/arch v0.2.0 // indirect
|
||||||
golang.org/x/crypto v0.6.0 // indirect
|
golang.org/x/crypto v0.7.0 // indirect
|
||||||
golang.org/x/mod v0.8.0 // indirect
|
golang.org/x/mod v0.8.0 // indirect
|
||||||
golang.org/x/sync v0.1.0 // indirect
|
golang.org/x/sync v0.1.0 // indirect
|
||||||
golang.org/x/tools v0.3.1-0.20221202221704-aa9f4b2f3d57 // indirect
|
golang.org/x/tools v0.6.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 // indirect
|
google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
43
go.sum
43
go.sum
@ -28,24 +28,23 @@ github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf h1:yc9daCCYUefEs
|
|||||||
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf/go.mod h1:o0ESU9p83twszAU8LBeJKFAAMX14tISa0yk4Oo5TOqo=
|
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf/go.mod h1:o0ESU9p83twszAU8LBeJKFAAMX14tISa0yk4Oo5TOqo=
|
||||||
github.com/ProtonMail/docker-credential-helpers v1.1.0 h1:+kvUIpwWcbtP3WFv5sSvkFn/XLzSqPOB5AAthuk9xPk=
|
github.com/ProtonMail/docker-credential-helpers v1.1.0 h1:+kvUIpwWcbtP3WFv5sSvkFn/XLzSqPOB5AAthuk9xPk=
|
||||||
github.com/ProtonMail/docker-credential-helpers v1.1.0/go.mod h1:mK0aBveCxhnQ756AmaTfXMZDeULvheYVhF/MWMErN5g=
|
github.com/ProtonMail/docker-credential-helpers v1.1.0/go.mod h1:mK0aBveCxhnQ756AmaTfXMZDeULvheYVhF/MWMErN5g=
|
||||||
github.com/ProtonMail/gluon v0.15.1-0.20230426114936-a356ef7f2753 h1:Ae/Ny7Fm+8RS/L+/9CiT6ntoPFSIJLyrLbD5bI77il4=
|
github.com/ProtonMail/gluon v0.16.1-0.20230428090920-2797a1764f16 h1:X5kb4PwVrgVDQjBkpiobYrDlqKDMuS1o92Ty+rZ1ptE=
|
||||||
github.com/ProtonMail/gluon v0.15.1-0.20230426114936-a356ef7f2753/go.mod h1:yA4hk6CJw0BMo+YL8Y3ckCYs5L20sysu9xseshwY3QI=
|
github.com/ProtonMail/gluon v0.16.1-0.20230428090920-2797a1764f16/go.mod h1:yA4hk6CJw0BMo+YL8Y3ckCYs5L20sysu9xseshwY3QI=
|
||||||
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:D+aZah+k14Gn6kmL7eKxoo/4Dr/lK3ChBcwce2+SQP4=
|
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:D+aZah+k14Gn6kmL7eKxoo/4Dr/lK3ChBcwce2+SQP4=
|
||||||
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4=
|
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a/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-20230124153114-0acdc8ae009b/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
|
github.com/ProtonMail/go-crypto v0.0.0-20230322105811-d73448b7e800 h1:o8/VQLSiuRkkSAfVOpFCG1GnTsWxFIOPLvJ2O7hJcFg=
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA=
|
github.com/ProtonMail/go-crypto v0.0.0-20230322105811-d73448b7e800/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE=
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
|
|
||||||
github.com/ProtonMail/go-message v0.0.0-20210611055058-fabeff2ec753 h1:I8IsYA297x0QLU80G5I6aLYUu3JYNSpo8j5fkXtFDW0=
|
github.com/ProtonMail/go-message v0.0.0-20210611055058-fabeff2ec753 h1:I8IsYA297x0QLU80G5I6aLYUu3JYNSpo8j5fkXtFDW0=
|
||||||
github.com/ProtonMail/go-message v0.0.0-20210611055058-fabeff2ec753/go.mod h1:NBAn21zgCJ/52WLDyed18YvYFm5tEoeDauubFqLokM4=
|
github.com/ProtonMail/go-message v0.0.0-20210611055058-fabeff2ec753/go.mod h1:NBAn21zgCJ/52WLDyed18YvYFm5tEoeDauubFqLokM4=
|
||||||
github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08 h1:dS7r5z4iGS0qCjM7UwWdsEMzQesUQbGcXdSm2/tWboA=
|
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
|
||||||
github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08/go.mod h1:qRZgbeASl2a9OwmsV85aWwRqic0NHPh+9ewGAzb4cgM=
|
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
|
||||||
github.com/ProtonMail/go-proton-api v0.4.1-0.20230406143739-c7596e170799 h1:slk4Drrkij1EVTnFOlIDyJsfjt69tnw8w2g1NMb253U=
|
github.com/ProtonMail/go-proton-api v0.4.1-0.20230426081144-f77778bae1be h1:TNHnEyUQDf97CRGCFWLxg7I5ASSEMO3TN2lbNw2cD6U=
|
||||||
github.com/ProtonMail/go-proton-api v0.4.1-0.20230406143739-c7596e170799/go.mod h1:kis4GD6FHp1ZWnenSBepldt8ai+vYalDPeey9yGwyXk=
|
github.com/ProtonMail/go-proton-api v0.4.1-0.20230426081144-f77778bae1be/go.mod h1:UkrG9gN2o9mzdx/an0XRc6a4s5Haef1A7Eyd2iXlw28=
|
||||||
github.com/ProtonMail/go-srp v0.0.5 h1:xhUioxZgDbCnpo9JehyFhwwsn9JLWkUGfB0oiKXgiGg=
|
github.com/ProtonMail/go-srp v0.0.5 h1:xhUioxZgDbCnpo9JehyFhwwsn9JLWkUGfB0oiKXgiGg=
|
||||||
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/gopenpgp/v2 v2.5.2 h1:97SjlWNAxXl9P22lgwgrZRshQdiEfAht0g3ZoiA1GCw=
|
github.com/ProtonMail/gopenpgp/v2 v2.7.1-proton h1:YS6M20yvjCJPR1r4ADW5TPn6rahs4iAyZaACei86bEc=
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.5.2/go.mod h1:52qDaCnto6r+CoWbuU50T77XQt99lIs46HtHtvgFO3o=
|
github.com/ProtonMail/gopenpgp/v2 v2.7.1-proton/go.mod h1:S1lYsaGHykYpxxh2SnJL6ypcAlANKj5NRSY6HxKryKQ=
|
||||||
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
|
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
|
||||||
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
|
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
|
||||||
github.com/abiosoft/ishell v2.0.0+incompatible h1:zpwIuEHc37EzrsIYah3cpevrIc8Oma7oZPxr03tlmmw=
|
github.com/abiosoft/ishell v2.0.0+incompatible h1:zpwIuEHc37EzrsIYah3cpevrIc8Oma7oZPxr03tlmmw=
|
||||||
@ -433,12 +432,11 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
|
||||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w=
|
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w=
|
||||||
@ -454,7 +452,6 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl
|
|||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
golang.org/x/mobile v0.0.0-20221110043201-43a038452099/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY=
|
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
@ -481,8 +478,10 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||||
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@ -493,7 +492,6 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@ -525,11 +523,13 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
@ -537,8 +537,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||||||
golang.org/x/text v0.3.5-0.20201125200606-c27b9fd57aec/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.5-0.20201125200606-c27b9fd57aec/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||||
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@ -562,8 +563,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
|||||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.3.1-0.20221202221704-aa9f4b2f3d57 h1:/X0t/E4VxbZE7MLS7auvE7YICHeVvbIa9vkOVvYW/24=
|
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||||
golang.org/x/tools v0.3.1-0.20221202221704-aa9f4b2f3d57/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -35,6 +36,7 @@ import (
|
|||||||
"github.com/ProtonMail/proton-bridge/v3/internal/crash"
|
"github.com/ProtonMail/proton-bridge/v3/internal/crash"
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/events"
|
"github.com/ProtonMail/proton-bridge/v3/internal/events"
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/focus"
|
"github.com/ProtonMail/proton-bridge/v3/internal/focus"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v3/internal/frontend/theme"
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/locations"
|
"github.com/ProtonMail/proton-bridge/v3/internal/locations"
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/logging"
|
"github.com/ProtonMail/proton-bridge/v3/internal/logging"
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/sentry"
|
"github.com/ProtonMail/proton-bridge/v3/internal/sentry"
|
||||||
@ -244,6 +246,15 @@ func run(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"lastVersion": v.GetLastVersion().String(),
|
||||||
|
"showAllMail": v.GetShowAllMail(),
|
||||||
|
"updateCh": v.GetUpdateChannel(),
|
||||||
|
"autoUpdate": v.GetAutoUpdate(),
|
||||||
|
"rollout": v.GetUpdateRollout(),
|
||||||
|
"DoH": v.GetProxyAllowed(),
|
||||||
|
}).Info("Vault loaded")
|
||||||
|
|
||||||
// Load the cookies from the vault.
|
// Load the cookies from the vault.
|
||||||
return withCookieJar(v, func(cookieJar http.CookieJar) error {
|
return withCookieJar(v, func(cookieJar http.CookieJar) error {
|
||||||
// Create a new bridge instance.
|
// Create a new bridge instance.
|
||||||
@ -258,6 +269,9 @@ func run(c *cli.Context) error {
|
|||||||
b.PushError(bridge.ErrVaultCorrupt)
|
b.PushError(bridge.ErrVaultCorrupt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start telemetry heartbeat process
|
||||||
|
b.StartHeartbeat(b)
|
||||||
|
|
||||||
// Run the frontend.
|
// Run the frontend.
|
||||||
return runFrontend(c, crashHandler, restarter, locations, b, eventCh, quitCh, c.Int(flagParentPID))
|
return runFrontend(c, crashHandler, restarter, locations, b, eventCh, quitCh, c.Int(flagParentPID))
|
||||||
})
|
})
|
||||||
@ -417,6 +431,10 @@ func withCookieJar(vault *vault.Vault, fn func(http.CookieJar) error) error {
|
|||||||
return fmt.Errorf("could not create cookie jar: %w", err)
|
return fmt.Errorf("could not create cookie jar: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := setDeviceCookies(persister); err != nil {
|
||||||
|
return fmt.Errorf("could not set device cookies: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Persist the cookies to the vault when we close.
|
// Persist the cookies to the vault when we close.
|
||||||
defer func() {
|
defer func() {
|
||||||
logrus.Debug("Persisting cookies")
|
logrus.Debug("Persisting cookies")
|
||||||
@ -428,3 +446,21 @@ func withCookieJar(vault *vault.Vault, fn func(http.CookieJar) error) error {
|
|||||||
|
|
||||||
return fn(persister)
|
return fn(persister)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setDeviceCookies(jar *cookies.Jar) error {
|
||||||
|
url, err := url.Parse(constants.APIHost)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, value := range map[string]string{
|
||||||
|
"hhn": sentry.GetProtectedHostname(),
|
||||||
|
"tz": sentry.GetTimeZone(),
|
||||||
|
"lng": sentry.GetSystemLang(),
|
||||||
|
"clr": string(theme.DefaultTheme()),
|
||||||
|
} {
|
||||||
|
jar.SetCookies(url, []*http.Cookie{{Name: name, Value: value, Secure: true}})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
package bridge
|
package bridge
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@ -36,6 +37,14 @@ func newAPIOptions(
|
|||||||
transport http.RoundTripper,
|
transport http.RoundTripper,
|
||||||
panicHandler async.PanicHandler,
|
panicHandler async.PanicHandler,
|
||||||
) []proton.Option {
|
) []proton.Option {
|
||||||
|
|
||||||
|
if allow := os.Getenv("BRIDGE_ALLOW_PROXY"); allow != "" {
|
||||||
|
transport = &http.Transport{
|
||||||
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
opt := defaultAPIOptions(apiURL, version, cookieJar, transport, panicHandler)
|
opt := defaultAPIOptions(apiURL, version, cookieJar, transport, panicHandler)
|
||||||
|
|
||||||
if host := os.Getenv("BRIDGE_API_HOST"); host != "" {
|
if host := os.Getenv("BRIDGE_API_HOST"); host != "" {
|
||||||
|
|||||||
@ -41,6 +41,7 @@ import (
|
|||||||
"github.com/ProtonMail/proton-bridge/v3/internal/focus"
|
"github.com/ProtonMail/proton-bridge/v3/internal/focus"
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/safe"
|
"github.com/ProtonMail/proton-bridge/v3/internal/safe"
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/sentry"
|
"github.com/ProtonMail/proton-bridge/v3/internal/sentry"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v3/internal/telemetry"
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/user"
|
"github.com/ProtonMail/proton-bridge/v3/internal/user"
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
|
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
|
||||||
"github.com/bradenaw/juniper/xslices"
|
"github.com/bradenaw/juniper/xslices"
|
||||||
@ -78,6 +79,9 @@ type Bridge struct {
|
|||||||
updater Updater
|
updater Updater
|
||||||
installCh chan installJob
|
installCh chan installJob
|
||||||
|
|
||||||
|
// heartbeat is the telemetry heartbeat for metrics.
|
||||||
|
heartbeat telemetry.Heartbeat
|
||||||
|
|
||||||
// curVersion is the current version of the bridge,
|
// curVersion is the current version of the bridge,
|
||||||
// newVersion is the version that was installed by the updater.
|
// newVersion is the version that was installed by the updater.
|
||||||
curVersion *semver.Version
|
curVersion *semver.Version
|
||||||
@ -126,6 +130,9 @@ type Bridge struct {
|
|||||||
// goUpdate triggers a check/install of updates.
|
// goUpdate triggers a check/install of updates.
|
||||||
goUpdate func()
|
goUpdate func()
|
||||||
|
|
||||||
|
// goHeartbeat triggers a check/sending if heartbeat is needed.
|
||||||
|
goHeartbeat func()
|
||||||
|
|
||||||
uidValidityGenerator imap.UIDValidityGenerator
|
uidValidityGenerator imap.UIDValidityGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,6 +244,8 @@ func newBridge(
|
|||||||
return nil, fmt.Errorf("failed to save last version indicator: %w", err)
|
return nil, fmt.Errorf("failed to save last version indicator: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
identifier.SetClientString(vault.GetLastUserAgent())
|
||||||
|
|
||||||
imapServer, err := newIMAPServer(
|
imapServer, err := newIMAPServer(
|
||||||
gluonCacheDir,
|
gluonCacheDir,
|
||||||
gluonDataDir,
|
gluonDataDir,
|
||||||
|
|||||||
@ -49,6 +49,7 @@ import (
|
|||||||
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
|
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
|
||||||
"github.com/ProtonMail/proton-bridge/v3/tests"
|
"github.com/ProtonMail/proton-bridge/v3/tests"
|
||||||
"github.com/bradenaw/juniper/xslices"
|
"github.com/bradenaw/juniper/xslices"
|
||||||
|
imapid "github.com/emersion/go-imap-id"
|
||||||
"github.com/emersion/go-imap/client"
|
"github.com/emersion/go-imap/client"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@ -170,6 +171,92 @@ func TestBridge_UserAgent(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBridge_UserAgent_Persistence(t *testing.T) {
|
||||||
|
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, vaultKey []byte) {
|
||||||
|
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(b *bridge.Bridge, mocks *bridge.Mocks) {
|
||||||
|
currentUserAgent := b.GetCurrentUserAgent()
|
||||||
|
require.Contains(t, currentUserAgent, vault.DefaultUserAgent)
|
||||||
|
|
||||||
|
imapClient, err := client.Dial(fmt.Sprintf("%v:%v", constants.Host, b.GetIMAPPort()))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer func() { _ = imapClient.Logout() }()
|
||||||
|
|
||||||
|
idClient := imapid.NewClient(imapClient)
|
||||||
|
|
||||||
|
// Set IMAP ID before Login to have the value capture in the Login API Call.
|
||||||
|
_, err = idClient.ID(imapid.ID{
|
||||||
|
imapid.FieldName: "MyFancyClient",
|
||||||
|
imapid.FieldVersion: "0.1.2",
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Login the user.
|
||||||
|
_, err = b.LoginFull(context.Background(), username, password, nil, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Assert that the user agent then contains the platform.
|
||||||
|
require.Contains(t, b.GetCurrentUserAgent(), "MyFancyClient/0.1.2")
|
||||||
|
})
|
||||||
|
|
||||||
|
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
|
||||||
|
currentUserAgent := bridge.GetCurrentUserAgent()
|
||||||
|
require.Contains(t, currentUserAgent, "MyFancyClient/0.1.2")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBridge_UserAgentFromIMAPID(t *testing.T) {
|
||||||
|
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, vaultKey []byte) {
|
||||||
|
var (
|
||||||
|
calls []server.Call
|
||||||
|
lock sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
s.AddCallWatcher(func(call server.Call) {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
|
||||||
|
calls = append(calls, call)
|
||||||
|
})
|
||||||
|
|
||||||
|
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(b *bridge.Bridge, mocks *bridge.Mocks) {
|
||||||
|
imapClient, err := client.Dial(fmt.Sprintf("%v:%v", constants.Host, b.GetIMAPPort()))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer func() { _ = imapClient.Logout() }()
|
||||||
|
|
||||||
|
idClient := imapid.NewClient(imapClient)
|
||||||
|
|
||||||
|
// Set IMAP ID before Login to have the value capture in the Login API Call.
|
||||||
|
_, err = idClient.ID(imapid.ID{
|
||||||
|
imapid.FieldName: "MyFancyClient",
|
||||||
|
imapid.FieldVersion: "0.1.2",
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Login the user.
|
||||||
|
userID, err := b.LoginFull(context.Background(), username, password, nil, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
info, err := b.GetUserInfo(userID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, info.State == bridge.Connected)
|
||||||
|
|
||||||
|
require.NoError(t, imapClient.Login(info.Addresses[0], string(info.BridgePass)))
|
||||||
|
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
|
||||||
|
userAgent := calls[len(calls)-1].RequestHeader.Get("User-Agent")
|
||||||
|
|
||||||
|
// Assert that the user agent was sent to the API.
|
||||||
|
require.Contains(t, userAgent, b.GetCurrentUserAgent())
|
||||||
|
require.Contains(t, userAgent, "MyFancyClient/0.1.2")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestBridge_Cookies(t *testing.T) {
|
func TestBridge_Cookies(t *testing.T) {
|
||||||
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, vaultKey []byte) {
|
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, vaultKey []byte) {
|
||||||
var (
|
var (
|
||||||
@ -736,6 +823,9 @@ func withBridgeNoMocks(
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Empty(t, bridge.GetErrors())
|
require.Empty(t, bridge.GetErrors())
|
||||||
|
|
||||||
|
// Start the Heartbeat process.
|
||||||
|
bridge.StartHeartbeat(mocks.Heartbeat)
|
||||||
|
|
||||||
// Wait for bridge to finish loading users.
|
// Wait for bridge to finish loading users.
|
||||||
waitForEvent(t, eventCh, events.AllUsersLoaded{})
|
waitForEvent(t, eventCh, events.AllUsersLoaded{})
|
||||||
// Wait for bridge to start the IMAP server.
|
// Wait for bridge to start the IMAP server.
|
||||||
|
|||||||
128
internal/bridge/heartbeat.go
Normal file
128
internal/bridge/heartbeat.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
// Copyright (c) 2023 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 bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/gluon/reporter"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v3/internal/safe"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v3/internal/telemetry"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v3/pkg/keychain"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const HeartbeatCheckInterval = time.Hour
|
||||||
|
|
||||||
|
func (bridge *Bridge) IsTelemetryAvailable() bool {
|
||||||
|
var flag = true
|
||||||
|
if bridge.GetTelemetryDisabled() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
safe.RLock(func() {
|
||||||
|
for _, user := range bridge.users {
|
||||||
|
flag = flag && user.IsTelemetryEnabled(context.Background())
|
||||||
|
}
|
||||||
|
}, bridge.usersLock)
|
||||||
|
|
||||||
|
return flag
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bridge *Bridge) SendHeartbeat(heartbeat *telemetry.HeartbeatData) bool {
|
||||||
|
data, err := json.Marshal(heartbeat)
|
||||||
|
if err != nil {
|
||||||
|
if err := bridge.reporter.ReportMessageWithContext("Cannot parse heartbeat data.", reporter.Context{
|
||||||
|
"error": err,
|
||||||
|
}); err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to parse heartbeat data.")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var sent = false
|
||||||
|
|
||||||
|
safe.RLock(func() {
|
||||||
|
for _, user := range bridge.users {
|
||||||
|
if err := user.SendTelemetry(context.Background(), data); err == nil {
|
||||||
|
sent = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, bridge.usersLock)
|
||||||
|
|
||||||
|
return sent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bridge *Bridge) GetLastHeartbeatSent() time.Time {
|
||||||
|
return bridge.vault.GetLastHeartbeatSent()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bridge *Bridge) SetLastHeartbeatSent(timestamp time.Time) error {
|
||||||
|
return bridge.vault.SetLastHeartbeatSent(timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bridge *Bridge) StartHeartbeat(manager telemetry.HeartbeatManager) {
|
||||||
|
bridge.heartbeat = telemetry.NewHeartbeat(manager, 1143, 1025, bridge.GetGluonCacheDir(), keychain.DefaultHelper)
|
||||||
|
|
||||||
|
// Check for heartbeat when triggered.
|
||||||
|
bridge.goHeartbeat = bridge.tasks.PeriodicOrTrigger(HeartbeatCheckInterval, 0, func(ctx context.Context) {
|
||||||
|
logrus.Debug("Checking for heartbeat")
|
||||||
|
|
||||||
|
bridge.heartbeat.TrySending()
|
||||||
|
})
|
||||||
|
|
||||||
|
bridge.heartbeat.SetRollout(bridge.GetUpdateRollout())
|
||||||
|
bridge.heartbeat.SetAutoStart(bridge.GetAutostart())
|
||||||
|
bridge.heartbeat.SetAutoUpdate(bridge.GetAutoUpdate())
|
||||||
|
bridge.heartbeat.SetBeta(bridge.GetUpdateChannel())
|
||||||
|
bridge.heartbeat.SetDoh(bridge.GetProxyAllowed())
|
||||||
|
bridge.heartbeat.SetShowAllMail(bridge.GetShowAllMail())
|
||||||
|
bridge.heartbeat.SetIMAPConnectionMode(bridge.GetIMAPSSL())
|
||||||
|
bridge.heartbeat.SetSMTPConnectionMode(bridge.GetSMTPSSL())
|
||||||
|
bridge.heartbeat.SetIMAPPort(bridge.GetIMAPPort())
|
||||||
|
bridge.heartbeat.SetSMTPPort(bridge.GetSMTPPort())
|
||||||
|
bridge.heartbeat.SetCacheLocation(bridge.GetGluonCacheDir())
|
||||||
|
if val, err := bridge.GetKeychainApp(); err != nil {
|
||||||
|
bridge.heartbeat.SetKeyChainPref(val)
|
||||||
|
} else {
|
||||||
|
bridge.heartbeat.SetKeyChainPref(keychain.DefaultHelper)
|
||||||
|
}
|
||||||
|
bridge.heartbeat.SetPrevVersion(bridge.GetLastVersion().String())
|
||||||
|
|
||||||
|
safe.RLock(func() {
|
||||||
|
var splitMode = false
|
||||||
|
for _, user := range bridge.users {
|
||||||
|
if user.GetAddressMode() == vault.SplitMode {
|
||||||
|
splitMode = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var nbAccount = len(bridge.users)
|
||||||
|
bridge.heartbeat.SetNbAccount(nbAccount)
|
||||||
|
bridge.heartbeat.SetSplitMode(splitMode)
|
||||||
|
|
||||||
|
// Do not try to send if there is no user yet.
|
||||||
|
if nbAccount > 0 {
|
||||||
|
defer bridge.goHeartbeat()
|
||||||
|
}
|
||||||
|
}, bridge.usersLock)
|
||||||
|
}
|
||||||
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
package bridge
|
package bridge
|
||||||
|
|
||||||
|
import "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
func (bridge *Bridge) GetCurrentUserAgent() string {
|
func (bridge *Bridge) GetCurrentUserAgent() string {
|
||||||
return bridge.identifier.GetUserAgent()
|
return bridge.identifier.GetUserAgent()
|
||||||
}
|
}
|
||||||
@ -24,3 +26,17 @@ func (bridge *Bridge) GetCurrentUserAgent() string {
|
|||||||
func (bridge *Bridge) SetCurrentPlatform(platform string) {
|
func (bridge *Bridge) SetCurrentPlatform(platform string) {
|
||||||
bridge.identifier.SetPlatform(platform)
|
bridge.identifier.SetPlatform(platform)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bridge *Bridge) setUserAgent(name, version string) {
|
||||||
|
currentUserAgent := bridge.identifier.GetClientString()
|
||||||
|
|
||||||
|
bridge.identifier.SetClient(name, version)
|
||||||
|
|
||||||
|
newUserAgent := bridge.identifier.GetClientString()
|
||||||
|
|
||||||
|
if currentUserAgent != newUserAgent {
|
||||||
|
if err := bridge.vault.SetLastUserAgent(newUserAgent); err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to write new user agent to vault")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -41,18 +41,16 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
defaultClientName = "UnknownClient"
|
|
||||||
defaultClientVersion = "0.0.1"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (bridge *Bridge) serveIMAP() error {
|
func (bridge *Bridge) serveIMAP() error {
|
||||||
port, err := func() (int, error) {
|
port, err := func() (int, error) {
|
||||||
if bridge.imapServer == nil {
|
if bridge.imapServer == nil {
|
||||||
return 0, fmt.Errorf("no IMAP server instance running")
|
return 0, fmt.Errorf("no IMAP server instance running")
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Info("Starting IMAP server")
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"port": bridge.vault.GetIMAPPort(),
|
||||||
|
"ssl": bridge.vault.GetIMAPSSL(),
|
||||||
|
}).Info("Starting IMAP server")
|
||||||
|
|
||||||
imapListener, err := newListener(bridge.vault.GetIMAPPort(), bridge.vault.GetIMAPSSL(), bridge.tlsConfig)
|
imapListener, err := newListener(bridge.vault.GetIMAPPort(), bridge.vault.GetIMAPSSL(), bridge.tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -249,11 +247,6 @@ func (bridge *Bridge) handleIMAPEvent(event imapEvents.Event) {
|
|||||||
}).Info("Received mailbox message count")
|
}).Info("Received mailbox message count")
|
||||||
}
|
}
|
||||||
|
|
||||||
case imapEvents.SessionAdded:
|
|
||||||
if !bridge.identifier.HasClient() {
|
|
||||||
bridge.identifier.SetClient(defaultClientName, defaultClientVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
case imapEvents.IMAPID:
|
case imapEvents.IMAPID:
|
||||||
logrus.WithFields(logrus.Fields{
|
logrus.WithFields(logrus.Fields{
|
||||||
"sessionID": event.SessionID,
|
"sessionID": event.SessionID,
|
||||||
@ -262,7 +255,7 @@ func (bridge *Bridge) handleIMAPEvent(event imapEvents.Event) {
|
|||||||
}).Info("Received IMAP ID")
|
}).Info("Received IMAP ID")
|
||||||
|
|
||||||
if event.IMAPID.Name != "" && event.IMAPID.Version != "" {
|
if event.IMAPID.Name != "" && event.IMAPID.Version != "" {
|
||||||
bridge.identifier.SetClient(event.IMAPID.Name, event.IMAPID.Version)
|
bridge.setUserAgent(event.IMAPID.Name, event.IMAPID.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
case imapEvents.LoginFailed:
|
case imapEvents.LoginFailed:
|
||||||
|
|||||||
@ -24,6 +24,7 @@ type Mocks struct {
|
|||||||
|
|
||||||
CrashHandler *mocks.MockPanicHandler
|
CrashHandler *mocks.MockPanicHandler
|
||||||
Reporter *mocks.MockReporter
|
Reporter *mocks.MockReporter
|
||||||
|
Heartbeat *mocks.MockHeartbeatManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMocks(tb testing.TB, version, minAuto *semver.Version) *Mocks {
|
func NewMocks(tb testing.TB, version, minAuto *semver.Version) *Mocks {
|
||||||
@ -39,14 +40,18 @@ func NewMocks(tb testing.TB, version, minAuto *semver.Version) *Mocks {
|
|||||||
|
|
||||||
CrashHandler: mocks.NewMockPanicHandler(ctl),
|
CrashHandler: mocks.NewMockPanicHandler(ctl),
|
||||||
Reporter: mocks.NewMockReporter(ctl),
|
Reporter: mocks.NewMockReporter(ctl),
|
||||||
|
Heartbeat: mocks.NewMockHeartbeatManager(ctl),
|
||||||
}
|
}
|
||||||
|
|
||||||
// When getting the TLS issue channel, we want to return the test channel.
|
// When getting the TLS issue channel, we want to return the test channel.
|
||||||
mocks.TLSReporter.EXPECT().GetTLSIssueCh().Return(mocks.TLSIssueCh).AnyTimes()
|
mocks.TLSReporter.EXPECT().GetTLSIssueCh().Return(mocks.TLSIssueCh).AnyTimes()
|
||||||
|
|
||||||
// This is called at he end of any go-routine:
|
// This is called at the end of any go-routine:
|
||||||
mocks.CrashHandler.EXPECT().HandlePanic(gomock.Any()).AnyTimes()
|
mocks.CrashHandler.EXPECT().HandlePanic(gomock.Any()).AnyTimes()
|
||||||
|
|
||||||
|
// this is called at start of heartbeat process.
|
||||||
|
mocks.Heartbeat.EXPECT().IsTelemetryAvailable().AnyTimes()
|
||||||
|
|
||||||
return mocks
|
return mocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
92
internal/bridge/mocks/telemetry_mocks.go
Normal file
92
internal/bridge/mocks/telemetry_mocks.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/ProtonMail/proton-bridge/v3/internal/telemetry (interfaces: HeartbeatManager)
|
||||||
|
|
||||||
|
// Package mocks is a generated GoMock package.
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
time "time"
|
||||||
|
|
||||||
|
telemetry "github.com/ProtonMail/proton-bridge/v3/internal/telemetry"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockHeartbeatManager is a mock of HeartbeatManager interface.
|
||||||
|
type MockHeartbeatManager struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockHeartbeatManagerMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockHeartbeatManagerMockRecorder is the mock recorder for MockHeartbeatManager.
|
||||||
|
type MockHeartbeatManagerMockRecorder struct {
|
||||||
|
mock *MockHeartbeatManager
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockHeartbeatManager creates a new mock instance.
|
||||||
|
func NewMockHeartbeatManager(ctrl *gomock.Controller) *MockHeartbeatManager {
|
||||||
|
mock := &MockHeartbeatManager{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockHeartbeatManagerMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockHeartbeatManager) EXPECT() *MockHeartbeatManagerMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLastHeartbeatSent mocks base method.
|
||||||
|
func (m *MockHeartbeatManager) GetLastHeartbeatSent() time.Time {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetLastHeartbeatSent")
|
||||||
|
ret0, _ := ret[0].(time.Time)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLastHeartbeatSent indicates an expected call of GetLastHeartbeatSent.
|
||||||
|
func (mr *MockHeartbeatManagerMockRecorder) GetLastHeartbeatSent() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastHeartbeatSent", reflect.TypeOf((*MockHeartbeatManager)(nil).GetLastHeartbeatSent))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTelemetryAvailable mocks base method.
|
||||||
|
func (m *MockHeartbeatManager) IsTelemetryAvailable() bool {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "IsTelemetryAvailable")
|
||||||
|
ret0, _ := ret[0].(bool)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTelemetryAvailable indicates an expected call of IsTelemetryAvailable.
|
||||||
|
func (mr *MockHeartbeatManagerMockRecorder) IsTelemetryAvailable() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsTelemetryAvailable", reflect.TypeOf((*MockHeartbeatManager)(nil).IsTelemetryAvailable))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendHeartbeat mocks base method.
|
||||||
|
func (m *MockHeartbeatManager) SendHeartbeat(arg0 *telemetry.HeartbeatData) bool {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SendHeartbeat", arg0)
|
||||||
|
ret0, _ := ret[0].(bool)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendHeartbeat indicates an expected call of SendHeartbeat.
|
||||||
|
func (mr *MockHeartbeatManagerMockRecorder) SendHeartbeat(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendHeartbeat", reflect.TypeOf((*MockHeartbeatManager)(nil).SendHeartbeat), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLastHeartbeatSent mocks base method.
|
||||||
|
func (m *MockHeartbeatManager) SetLastHeartbeatSent(arg0 time.Time) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SetLastHeartbeatSent", arg0)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLastHeartbeatSent indicates an expected call of SetLastHeartbeatSent.
|
||||||
|
func (mr *MockHeartbeatManagerMockRecorder) SetLastHeartbeatSent(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLastHeartbeatSent", reflect.TypeOf((*MockHeartbeatManager)(nil).SetLastHeartbeatSent), arg0)
|
||||||
|
}
|
||||||
@ -46,6 +46,8 @@ func (bridge *Bridge) SetKeychainApp(helper string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bridge.heartbeat.SetKeyChainPref(helper)
|
||||||
|
|
||||||
return vault.SetHelper(vaultDir, helper)
|
return vault.SetHelper(vaultDir, helper)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,6 +64,8 @@ func (bridge *Bridge) SetIMAPPort(newPort int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bridge.heartbeat.SetIMAPPort(newPort)
|
||||||
|
|
||||||
return bridge.restartIMAP()
|
return bridge.restartIMAP()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +82,8 @@ func (bridge *Bridge) SetIMAPSSL(newSSL bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bridge.heartbeat.SetIMAPConnectionMode(newSSL)
|
||||||
|
|
||||||
return bridge.restartIMAP()
|
return bridge.restartIMAP()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +100,8 @@ func (bridge *Bridge) SetSMTPPort(newPort int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bridge.heartbeat.SetSMTPPort(newPort)
|
||||||
|
|
||||||
return bridge.restartSMTP()
|
return bridge.restartSMTP()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,6 +118,8 @@ func (bridge *Bridge) SetSMTPSSL(newSSL bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bridge.heartbeat.SetSMTPConnectionMode(newSSL)
|
||||||
|
|
||||||
return bridge.restartSMTP()
|
return bridge.restartSMTP()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,6 +151,8 @@ func (bridge *Bridge) SetGluonDir(ctx context.Context, newGluonDir string) error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bridge.heartbeat.SetCacheLocation(newGluonDir)
|
||||||
|
|
||||||
gluonDataDir, err := bridge.GetGluonDataDir()
|
gluonDataDir, err := bridge.GetGluonDataDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get Gluon Database directory: %w", err)
|
return fmt.Errorf("failed to get Gluon Database directory: %w", err)
|
||||||
@ -207,6 +219,8 @@ func (bridge *Bridge) SetProxyAllowed(allowed bool) error {
|
|||||||
bridge.proxyCtl.DisallowProxy()
|
bridge.proxyCtl.DisallowProxy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bridge.heartbeat.SetDoh(allowed)
|
||||||
|
|
||||||
return bridge.vault.SetProxyAllowed(allowed)
|
return bridge.vault.SetProxyAllowed(allowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,6 +234,8 @@ func (bridge *Bridge) SetShowAllMail(show bool) error {
|
|||||||
user.SetShowAllMail(show)
|
user.SetShowAllMail(show)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bridge.heartbeat.SetShowAllMail(show)
|
||||||
|
|
||||||
return bridge.vault.SetShowAllMail(show)
|
return bridge.vault.SetShowAllMail(show)
|
||||||
}, bridge.usersLock)
|
}, bridge.usersLock)
|
||||||
}
|
}
|
||||||
@ -233,6 +249,8 @@ func (bridge *Bridge) SetAutostart(autostart bool) error {
|
|||||||
if err := bridge.vault.SetAutostart(autostart); err != nil {
|
if err := bridge.vault.SetAutostart(autostart); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bridge.heartbeat.SetAutoStart(autostart)
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@ -253,6 +271,10 @@ func (bridge *Bridge) SetAutostart(autostart bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bridge *Bridge) GetUpdateRollout() float64 {
|
||||||
|
return bridge.vault.GetUpdateRollout()
|
||||||
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) GetAutoUpdate() bool {
|
func (bridge *Bridge) GetAutoUpdate() bool {
|
||||||
return bridge.vault.GetAutoUpdate()
|
return bridge.vault.GetAutoUpdate()
|
||||||
}
|
}
|
||||||
@ -266,11 +288,28 @@ func (bridge *Bridge) SetAutoUpdate(autoUpdate bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bridge.heartbeat.SetAutoUpdate(autoUpdate)
|
||||||
|
|
||||||
bridge.goUpdate()
|
bridge.goUpdate()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bridge *Bridge) GetTelemetryDisabled() bool {
|
||||||
|
return bridge.vault.GetTelemetryDisabled()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bridge *Bridge) SetTelemetryDisabled(isDisabled bool) error {
|
||||||
|
if err := bridge.vault.SetTelemetryDisabled(isDisabled); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// If telemetry is re-enabled locally, try to send the heartbeat.
|
||||||
|
if !isDisabled {
|
||||||
|
defer bridge.goHeartbeat()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) GetUpdateChannel() updater.Channel {
|
func (bridge *Bridge) GetUpdateChannel() updater.Channel {
|
||||||
return bridge.vault.GetUpdateChannel()
|
return bridge.vault.GetUpdateChannel()
|
||||||
}
|
}
|
||||||
@ -284,6 +323,8 @@ func (bridge *Bridge) SetUpdateChannel(channel updater.Channel) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bridge.heartbeat.SetBeta(channel)
|
||||||
|
|
||||||
bridge.goUpdate()
|
bridge.goUpdate()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -33,7 +33,10 @@ import (
|
|||||||
|
|
||||||
func (bridge *Bridge) serveSMTP() error {
|
func (bridge *Bridge) serveSMTP() error {
|
||||||
port, err := func() (int, error) {
|
port, err := func() (int, error) {
|
||||||
logrus.Info("Starting SMTP server")
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"port": bridge.vault.GetSMTPPort(),
|
||||||
|
"ssl": bridge.vault.GetSMTPSSL(),
|
||||||
|
}).Info("Starting SMTP server")
|
||||||
|
|
||||||
smtpListener, err := newListener(bridge.vault.GetSMTPPort(), bridge.vault.GetSMTPSSL(), bridge.tlsConfig)
|
smtpListener, err := newListener(bridge.vault.GetSMTPPort(), bridge.vault.GetSMTPSSL(), bridge.tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import (
|
|||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/safe"
|
"github.com/ProtonMail/proton-bridge/v3/internal/safe"
|
||||||
"github.com/emersion/go-smtp"
|
"github.com/emersion/go-smtp"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type smtpBackend struct {
|
type smtpBackend struct {
|
||||||
@ -85,7 +86,7 @@ func (s *smtpSession) Rcpt(to string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *smtpSession) Data(r io.Reader) error {
|
func (s *smtpSession) Data(r io.Reader) error {
|
||||||
return safe.RLockRet(func() error {
|
err := safe.RLockRet(func() error {
|
||||||
user, ok := s.users[s.userID]
|
user, ok := s.users[s.userID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return ErrNoSuchUser
|
return ErrNoSuchUser
|
||||||
@ -93,4 +94,10 @@ func (s *smtpSession) Data(r io.Reader) error {
|
|||||||
|
|
||||||
return user.SendMail(s.authID, s.from, s.to, r)
|
return user.SendMail(s.authID, s.from, s.to, r)
|
||||||
}, s.usersLock)
|
}, s.usersLock)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithField("pkg", "smtp").WithError(err).Error("Send mail failed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,6 +38,8 @@ type Identifier interface {
|
|||||||
HasClient() bool
|
HasClient() bool
|
||||||
SetClient(name, version string)
|
SetClient(name, version string)
|
||||||
SetPlatform(platform string)
|
SetPlatform(platform string)
|
||||||
|
SetClientString(client string)
|
||||||
|
GetClientString() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProxyController interface {
|
type ProxyController interface {
|
||||||
|
|||||||
@ -295,6 +295,15 @@ func (bridge *Bridge) SetAddressMode(ctx context.Context, userID string, mode va
|
|||||||
AddressMode: mode,
|
AddressMode: mode,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var splitMode = false
|
||||||
|
for _, user := range bridge.users {
|
||||||
|
if user.GetAddressMode() == vault.SplitMode {
|
||||||
|
splitMode = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bridge.heartbeat.SetSplitMode(splitMode)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}, bridge.usersLock)
|
}, bridge.usersLock)
|
||||||
}
|
}
|
||||||
@ -399,7 +408,7 @@ func (bridge *Bridge) loadUsers(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Loading connected user")
|
log.WithField("mode", user.AddressMode()).Info("Loading connected user")
|
||||||
|
|
||||||
bridge.publish(events.UserLoading{
|
bridge.publish(events.UserLoading{
|
||||||
UserID: user.UserID(),
|
UserID: user.UserID(),
|
||||||
@ -550,7 +559,7 @@ func (bridge *Bridge) addUserWithVault(
|
|||||||
// As such, if we find this ID in the context, we should use it to update our user agent.
|
// As such, if we find this ID in the context, we should use it to update our user agent.
|
||||||
client.AddPreRequestHook(func(_ *resty.Client, r *resty.Request) error {
|
client.AddPreRequestHook(func(_ *resty.Client, r *resty.Request) error {
|
||||||
if imapID, ok := imap.GetIMAPIDFromContext(r.Context()); ok {
|
if imapID, ok := imap.GetIMAPIDFromContext(r.Context()); ok {
|
||||||
bridge.identifier.SetClient(imapID.Name, imapID.Version)
|
bridge.setUserAgent(imapID.Name, imapID.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -559,8 +568,12 @@ func (bridge *Bridge) addUserWithVault(
|
|||||||
// Finally, save the user in the bridge.
|
// Finally, save the user in the bridge.
|
||||||
safe.Lock(func() {
|
safe.Lock(func() {
|
||||||
bridge.users[apiUser.ID] = user
|
bridge.users[apiUser.ID] = user
|
||||||
|
bridge.heartbeat.SetNbAccount(len(bridge.users))
|
||||||
}, bridge.usersLock)
|
}, bridge.usersLock)
|
||||||
|
|
||||||
|
// As we need at least one user to send heartbeat, try to send it.
|
||||||
|
defer bridge.goHeartbeat()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,6 +627,8 @@ func (bridge *Bridge) logoutUser(ctx context.Context, user *user.User, withAPI,
|
|||||||
logrus.WithError(err).Error("Failed to logout user")
|
logrus.WithError(err).Error("Failed to logout user")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bridge.heartbeat.SetNbAccount(len(bridge.users))
|
||||||
|
|
||||||
user.Close()
|
user.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -451,6 +451,65 @@ func TestBridge_User_DropConn_NoBadEvent(t *testing.T) {
|
|||||||
}, server.WithListener(dropListener))
|
}, server.WithListener(dropListener))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBridge_User_UpdateDraft(t *testing.T) {
|
||||||
|
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, storeKey []byte) {
|
||||||
|
// Create a bridge user.
|
||||||
|
_, _, err := s.CreateUser("user", password)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Initially sync the user.
|
||||||
|
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
|
||||||
|
userLoginAndSync(ctx, t, bridge, "user", password)
|
||||||
|
})
|
||||||
|
|
||||||
|
withClient(ctx, t, s, "user", password, func(ctx context.Context, c *proton.Client) {
|
||||||
|
user, err := c.GetUser(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
addrs, err := c.GetAddresses(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
salts, err := c.GetSalts(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
keyPass, err := salts.SaltForKey(password, user.Keys.Primary().ID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, addrKRs, err := proton.Unlock(user, addrs, keyPass, async.NoopPanicHandler{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Create a draft (generating a "create draft message" event).
|
||||||
|
draft, err := c.CreateDraft(ctx, addrKRs[addrs[0].ID], proton.CreateDraftReq{
|
||||||
|
Message: proton.DraftTemplate{
|
||||||
|
Subject: "subject",
|
||||||
|
Sender: &mail.Address{Name: "sender", Address: addrs[0].Email},
|
||||||
|
Body: "body",
|
||||||
|
MIMEType: rfc822.TextPlain,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Empty(t, draft.ReplyTos)
|
||||||
|
|
||||||
|
// Process those events
|
||||||
|
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
|
||||||
|
userContinueEventProcess(ctx, t, s, bridge)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Update the draft (generating an "update draft message" event).
|
||||||
|
draft2, err := c.UpdateDraft(ctx, draft.ID, addrKRs[addrs[0].ID], proton.UpdateDraftReq{
|
||||||
|
Message: proton.DraftTemplate{
|
||||||
|
Subject: "subject 2",
|
||||||
|
Sender: &mail.Address{Name: "sender", Address: addrs[0].Email},
|
||||||
|
Body: "body 2",
|
||||||
|
MIMEType: rfc822.TextPlain,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Empty(t, draft2.ReplyTos)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestBridge_User_UpdateDraftAndCreateOtherMessage(t *testing.T) {
|
func TestBridge_User_UpdateDraftAndCreateOtherMessage(t *testing.T) {
|
||||||
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, storeKey []byte) {
|
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, storeKey []byte) {
|
||||||
// Create a bridge user.
|
// Create a bridge user.
|
||||||
@ -784,6 +843,48 @@ func TestBridge_User_HandleParentLabelRename(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TBD: GODT-2527.
|
||||||
|
func _TestBridge503DuringEventDoesNotCauseBadEvent(t *testing.T) { //nolint:unused,deadcode
|
||||||
|
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, storeKey []byte) {
|
||||||
|
// Create a user.
|
||||||
|
userID, addrID, err := s.CreateUser("user", password)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
labelID, err := s.CreateLabel(userID, "folder", "", proton.LabelTypeFolder)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Create 10 messages for the user.
|
||||||
|
withClient(ctx, t, s, "user", password, func(ctx context.Context, c *proton.Client) {
|
||||||
|
createNumMessages(ctx, t, c, addrID, labelID, 10)
|
||||||
|
})
|
||||||
|
|
||||||
|
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
|
||||||
|
userLoginAndSync(ctx, t, bridge, "user", password)
|
||||||
|
|
||||||
|
var messageIDs []string
|
||||||
|
|
||||||
|
// Create 10 more messages for the user, generating events.
|
||||||
|
withClient(ctx, t, s, "user", password, func(ctx context.Context, c *proton.Client) {
|
||||||
|
messageIDs = createNumMessages(ctx, t, c, addrID, labelID, 10)
|
||||||
|
})
|
||||||
|
|
||||||
|
mocks.Reporter.EXPECT().ReportMessageWithContext(gomock.Any(), gomock.Any()).MinTimes(1)
|
||||||
|
|
||||||
|
s.AddStatusHook(func(req *http.Request) (int, bool) {
|
||||||
|
if xslices.Index(xslices.Map(messageIDs[0:5], func(messageID string) string {
|
||||||
|
return "/mail/v4/messages/" + messageID
|
||||||
|
}), req.URL.Path) < 0 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.StatusServiceUnavailable, true
|
||||||
|
})
|
||||||
|
|
||||||
|
userContinueEventProcess(ctx, t, s, bridge)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// userLoginAndSync logs in user and waits until user is fully synced.
|
// userLoginAndSync logs in user and waits until user is fully synced.
|
||||||
func userLoginAndSync(
|
func userLoginAndSync(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|||||||
@ -17,69 +17,141 @@
|
|||||||
|
|
||||||
package certs
|
package certs
|
||||||
|
|
||||||
import (
|
/*
|
||||||
"os"
|
#cgo CFLAGS: -x objective-c
|
||||||
|
#cgo LDFLAGS: -framework Foundation -framework Security
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <Security/Security.h>
|
||||||
|
|
||||||
"golang.org/x/sys/execabs"
|
|
||||||
|
int installTrustedCert(char const *bytes, unsigned long long length) {
|
||||||
|
if (length == 0) {
|
||||||
|
return errSecInvalidData;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSData *der = [NSData dataWithBytes:bytes length:length];
|
||||||
|
|
||||||
|
// Step 1. Import the certificate in the keychain.
|
||||||
|
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (CFDataRef) der);
|
||||||
|
NSDictionary* addQuery = @{
|
||||||
|
(id)kSecValueRef: (__bridge id) cert,
|
||||||
|
(id)kSecClass: (id)kSecClassCertificate,
|
||||||
|
};
|
||||||
|
|
||||||
|
OSStatus status = SecItemAdd((__bridge CFDictionaryRef) addQuery, NULL);
|
||||||
|
if ((errSecSuccess != status) && (errSecDuplicateItem != status)) {
|
||||||
|
CFRelease(cert);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2. Set the trust for the certificate.
|
||||||
|
SecPolicyRef policy = SecPolicyCreateSSL(true, NULL); // we limit our trust to SSL
|
||||||
|
NSDictionary *trustSettings = @{
|
||||||
|
(id)kSecTrustSettingsResult: [NSNumber numberWithInt:kSecTrustSettingsResultTrustRoot],
|
||||||
|
(id)kSecTrustSettingsPolicy: (__bridge id) policy,
|
||||||
|
};
|
||||||
|
status = SecTrustSettingsSetTrustSettings(cert, kSecTrustSettingsDomainAdmin, (__bridge CFTypeRef)(trustSettings));
|
||||||
|
CFRelease(policy);
|
||||||
|
CFRelease(cert);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int removeTrustedCert(char const *bytes, unsigned long long length) {
|
||||||
|
if (0 == length) {
|
||||||
|
return errSecInvalidData;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSData *der = [NSData dataWithBytes: bytes length: length];
|
||||||
|
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (CFDataRef) der);
|
||||||
|
|
||||||
|
// Step 1. Unset the trust for the certificate.
|
||||||
|
SecPolicyRef policy = SecPolicyCreateSSL(true, NULL);
|
||||||
|
NSDictionary * trustSettings = @{
|
||||||
|
(id)kSecTrustSettingsResult: [NSNumber numberWithInt:kSecTrustSettingsResultUnspecified],
|
||||||
|
(id)kSecTrustSettingsPolicy: (__bridge id) policy,
|
||||||
|
};
|
||||||
|
OSStatus status = SecTrustSettingsSetTrustSettings(cert, kSecTrustSettingsDomainAdmin, (__bridge CFTypeRef)(trustSettings));
|
||||||
|
CFRelease(policy);
|
||||||
|
if (errSecSuccess != status) {
|
||||||
|
CFRelease(cert);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2. Remove the certificate from the keychain.
|
||||||
|
NSDictionary *query = @{ (id)kSecClass: (id)kSecClassCertificate,
|
||||||
|
(id)kSecMatchItemList: @[(__bridge id)cert],
|
||||||
|
(id)kSecMatchLimit: (id)kSecMatchLimitOne,
|
||||||
|
};
|
||||||
|
status = SecItemDelete((__bridge CFDictionaryRef) query);
|
||||||
|
|
||||||
|
CFRelease(cert);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// some of the error codes returned by Apple's Security framework.
|
||||||
|
const (
|
||||||
|
errSecSuccess = 0
|
||||||
|
errAuthorizationCanceled = -60006
|
||||||
|
)
|
||||||
|
|
||||||
|
// certPEMToDER converts a certificate in PEM format to DER format, which is the format required by Apple's Security framework.
|
||||||
|
func certPEMToDER(certPEM []byte) ([]byte, error) {
|
||||||
|
|
||||||
|
block, left := pem.Decode(certPEM)
|
||||||
|
if block == nil {
|
||||||
|
return []byte{}, errors.New("invalid PEM certificate")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(left) > 0 {
|
||||||
|
return []byte{}, errors.New("trailing data found at the end of a PEM certificate")
|
||||||
|
}
|
||||||
|
|
||||||
|
return block.Bytes, nil
|
||||||
|
}
|
||||||
|
|
||||||
func installCert(certPEM []byte) error {
|
func installCert(certPEM []byte) error {
|
||||||
name, err := writeToTempFile(certPEM)
|
certDER, err := certPEMToDER(certPEM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return addTrustedCert(name)
|
p := C.CBytes(certDER)
|
||||||
|
defer C.free(unsafe.Pointer(p))
|
||||||
|
|
||||||
|
errCode := C.installTrustedCert((*C.char)(p), (C.ulonglong)(len(certDER)))
|
||||||
|
switch errCode {
|
||||||
|
case errSecSuccess:
|
||||||
|
return nil
|
||||||
|
case errAuthorizationCanceled:
|
||||||
|
return fmt.Errorf("the user cancelled the authorization dialog")
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("could not install certification into keychain (error %v)", errCode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func uninstallCert(certPEM []byte) error {
|
func uninstallCert(certPEM []byte) error {
|
||||||
name, err := writeToTempFile(certPEM)
|
certDER, err := certPEMToDER(certPEM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return removeTrustedCert(name)
|
p := C.CBytes(certDER)
|
||||||
}
|
defer C.free(unsafe.Pointer(p))
|
||||||
|
|
||||||
func addTrustedCert(certPath string) error {
|
if errCode := C.removeTrustedCert((*C.char)(p), (C.ulonglong)(len(certDER))); errCode != 0 {
|
||||||
return execabs.Command( //nolint:gosec
|
return fmt.Errorf("could not install certificate from keychain (error %v)", errCode)
|
||||||
"/usr/bin/security",
|
|
||||||
"execute-with-privileges",
|
|
||||||
"/usr/bin/security",
|
|
||||||
"add-trusted-cert",
|
|
||||||
"-d",
|
|
||||||
"-r", "trustRoot",
|
|
||||||
"-p", "ssl",
|
|
||||||
"-k", "/Library/Keychains/System.keychain",
|
|
||||||
certPath,
|
|
||||||
).Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeTrustedCert(certPath string) error {
|
|
||||||
return execabs.Command( //nolint:gosec
|
|
||||||
"/usr/bin/security",
|
|
||||||
"execute-with-privileges",
|
|
||||||
"/usr/bin/security",
|
|
||||||
"remove-trusted-cert",
|
|
||||||
"-d",
|
|
||||||
certPath,
|
|
||||||
).Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeToTempFile writes the given data to a temporary file and returns the path.
|
|
||||||
func writeToTempFile(data []byte) (string, error) {
|
|
||||||
f, err := os.CreateTemp("", "tls")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := f.Write(data); err != nil {
|
return nil
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.Close(); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return f.Name(), nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
44
internal/certs/cert_store_darwin_test.go
Normal file
44
internal/certs/cert_store_darwin_test.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright (c) 2023 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 darwin
|
||||||
|
|
||||||
|
package certs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This test implies human interactions to enter password and is disabled by default.
|
||||||
|
func _TestTrustedCertsDarwin(t *testing.T) {
|
||||||
|
template, err := NewTLSTemplate()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
certPEM, _, err := GenerateCert(template)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Error(t, installCert([]byte{0})) // Cannot install an invalid cert.
|
||||||
|
require.Error(t, uninstallCert(certPEM)) // Cannot uninstall a cert that is not installed.
|
||||||
|
require.NoError(t, installCert(certPEM)) // Can install a valid cert.
|
||||||
|
require.NoError(t, installCert(certPEM)) // Can install an already installed cert.
|
||||||
|
require.NoError(t, uninstallCert(certPEM)) // Can uninstall an installed cert.
|
||||||
|
require.Error(t, uninstallCert(certPEM)) // Cannot uninstall an already uninstalled cert.
|
||||||
|
require.NoError(t, installCert(certPEM)) // Can reinstall an uninstalled cert.
|
||||||
|
require.NoError(t, uninstallCert(certPEM)) // Can uninstall a reinstalled cert.
|
||||||
|
}
|
||||||
@ -39,6 +39,7 @@ void GRPCQtProxy::connectSignals() {
|
|||||||
connect(this, &GRPCQtProxy::setIsAutostartOnReceived, &settingsTab, &SettingsTab::setIsAutostartOn);
|
connect(this, &GRPCQtProxy::setIsAutostartOnReceived, &settingsTab, &SettingsTab::setIsAutostartOn);
|
||||||
connect(this, &GRPCQtProxy::setIsBetaEnabledReceived, &settingsTab, &SettingsTab::setIsBetaEnabled);
|
connect(this, &GRPCQtProxy::setIsBetaEnabledReceived, &settingsTab, &SettingsTab::setIsBetaEnabled);
|
||||||
connect(this, &GRPCQtProxy::setIsAllMailVisibleReceived, &settingsTab, &SettingsTab::setIsAllMailVisible);
|
connect(this, &GRPCQtProxy::setIsAllMailVisibleReceived, &settingsTab, &SettingsTab::setIsAllMailVisible);
|
||||||
|
connect(this, &GRPCQtProxy::setIsTelemetryDisabledReceived, &settingsTab, &SettingsTab::setIsTelemetryDisabled);
|
||||||
connect(this, &GRPCQtProxy::setColorSchemeNameReceived, &settingsTab, &SettingsTab::setColorSchemeName);
|
connect(this, &GRPCQtProxy::setColorSchemeNameReceived, &settingsTab, &SettingsTab::setColorSchemeName);
|
||||||
connect(this, &GRPCQtProxy::reportBugReceived, &settingsTab, &SettingsTab::setBugReport);
|
connect(this, &GRPCQtProxy::reportBugReceived, &settingsTab, &SettingsTab::setBugReport);
|
||||||
connect(this, &GRPCQtProxy::exportTLSCertificatesReceived, &settingsTab, &SettingsTab::exportTLSCertificates);
|
connect(this, &GRPCQtProxy::exportTLSCertificatesReceived, &settingsTab, &SettingsTab::exportTLSCertificates);
|
||||||
@ -89,6 +90,13 @@ void GRPCQtProxy::setIsAllMailVisible(bool visible) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] isDisabled Is telemetry disabled?
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void GRPCQtProxy::setIsTelemetryDisabled(bool isDisabled) {
|
||||||
|
emit setIsTelemetryDisabledReceived(isDisabled);
|
||||||
|
}
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \param[in] name The color scheme.
|
/// \param[in] name The color scheme.
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
|
|||||||
@ -41,6 +41,7 @@ public: // member functions.
|
|||||||
void setIsAutostartOn(bool on); ///< Forwards a SetIsAutostartOn call via a Qt signal.
|
void setIsAutostartOn(bool on); ///< Forwards a SetIsAutostartOn call via a Qt signal.
|
||||||
void setIsBetaEnabled(bool enabled); ///< Forwards a SetIsBetaEnabled call via a Qt signal.
|
void setIsBetaEnabled(bool enabled); ///< Forwards a SetIsBetaEnabled call via a Qt signal.
|
||||||
void setIsAllMailVisible(bool visible); ///< Forwards a SetIsAllMailVisible call via a Qt signal.
|
void setIsAllMailVisible(bool visible); ///< Forwards a SetIsAllMailVisible call via a Qt signal.
|
||||||
|
void setIsTelemetryDisabled(bool isDisabled); ///< Forwards a SetIsTelemetryDisabled call via a Qt signal.
|
||||||
void setColorSchemeName(QString const &name); ///< Forward a SetColorSchemeName call via a Qt Signal
|
void setColorSchemeName(QString const &name); ///< Forward a SetColorSchemeName call via a Qt Signal
|
||||||
void reportBug(QString const &osType, QString const &osVersion, QString const &emailClient, QString const &address,
|
void reportBug(QString const &osType, QString const &osVersion, QString const &emailClient, QString const &address,
|
||||||
QString const &description, bool includeLogs); ///< Forwards a ReportBug call via a Qt signal.
|
QString const &description, bool includeLogs); ///< Forwards a ReportBug call via a Qt signal.
|
||||||
@ -62,6 +63,7 @@ signals:
|
|||||||
void setIsAutostartOnReceived(bool on); ///< Forwards a SetIsAutostartOn call via a Qt signal.
|
void setIsAutostartOnReceived(bool on); ///< Forwards a SetIsAutostartOn call via a Qt signal.
|
||||||
void setIsBetaEnabledReceived(bool enabled); ///< Forwards a SetIsBetaEnabled call via a Qt signal.
|
void setIsBetaEnabledReceived(bool enabled); ///< Forwards a SetIsBetaEnabled call via a Qt signal.
|
||||||
void setIsAllMailVisibleReceived(bool enabled); ///< Forwards a SetIsBetaEnabled call via a Qt signal.
|
void setIsAllMailVisibleReceived(bool enabled); ///< Forwards a SetIsBetaEnabled call via a Qt signal.
|
||||||
|
void setIsTelemetryDisabledReceived(bool isDisabled); ///< Forwards a SetIsTelemetryDisabled call via a Qt signal.
|
||||||
void setColorSchemeNameReceived(QString const &name); ///< Forward a SetColorScheme call via a Qt Signal
|
void setColorSchemeNameReceived(QString const &name); ///< Forward a SetColorScheme call via a Qt Signal
|
||||||
void reportBugReceived(QString const &osType, QString const &osVersion, QString const &emailClient, QString const &address,
|
void reportBugReceived(QString const &osType, QString const &osVersion, QString const &emailClient, QString const &address,
|
||||||
QString const &description, bool includeLogs); ///< Signal for the ReportBug gRPC call
|
QString const &description, bool includeLogs); ///< Signal for the ReportBug gRPC call
|
||||||
|
|||||||
@ -192,6 +192,28 @@ Status GRPCService::IsAllMailVisible(ServerContext *, Empty const *request, Bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] request The request.
|
||||||
|
/// \return The status for the call.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
grpc::Status GRPCService::SetIsTelemetryDisabled(::grpc::ServerContext *, ::google::protobuf::BoolValue const *request, ::google::protobuf::Empty *) {
|
||||||
|
app().log().debug(__FUNCTION__);
|
||||||
|
qtProxy_.setIsTelemetryDisabledReceived(request->value());
|
||||||
|
return Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[out] response The response.
|
||||||
|
/// \return The status for the call.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
grpc::Status GRPCService::IsTelemetryDisabled(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::BoolValue *response) {
|
||||||
|
app().log().debug(__FUNCTION__);
|
||||||
|
response->set_value(app().mainWindow().settingsTab().isTelemetryDisabled());
|
||||||
|
return Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \return The status for the call.
|
/// \return The status for the call.
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
@ -820,3 +842,4 @@ void GRPCService::finishLogin() {
|
|||||||
|
|
||||||
qtProxy_.sendDelayedEvent(newLoginFinishedEvent(user->id(), alreadyExist));
|
qtProxy_.sendDelayedEvent(newLoginFinishedEvent(user->id(), alreadyExist));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -51,6 +51,8 @@ public: // member functions.
|
|||||||
grpc::Status IsBetaEnabled(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::BoolValue *response) override;
|
grpc::Status IsBetaEnabled(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::BoolValue *response) override;
|
||||||
grpc::Status SetIsAllMailVisible(::grpc::ServerContext *context, ::google::protobuf::BoolValue const *request, ::google::protobuf::Empty *response) override;
|
grpc::Status SetIsAllMailVisible(::grpc::ServerContext *context, ::google::protobuf::BoolValue const *request, ::google::protobuf::Empty *response) override;
|
||||||
grpc::Status IsAllMailVisible(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::BoolValue *response) override;
|
grpc::Status IsAllMailVisible(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::BoolValue *response) override;
|
||||||
|
grpc::Status SetIsTelemetryDisabled(::grpc::ServerContext *, ::google::protobuf::BoolValue const *request, ::google::protobuf::Empty *response) override;
|
||||||
|
grpc::Status IsTelemetryDisabled(::grpc::ServerContext *, ::google::protobuf::Empty const *request, ::google::protobuf::BoolValue *response) override;
|
||||||
grpc::Status TriggerReset(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::Empty *) override;
|
grpc::Status TriggerReset(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::Empty *) override;
|
||||||
grpc::Status Version(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::StringValue *response) override;
|
grpc::Status Version(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::StringValue *response) override;
|
||||||
grpc::Status LogsPath(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::StringValue *response) override;
|
grpc::Status LogsPath(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::StringValue *response) override;
|
||||||
|
|||||||
@ -202,6 +202,22 @@ void SettingsTab::setIsAllMailVisible(bool visible) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \return the value for the 'Disabled Telemetry' check.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
bool SettingsTab::isTelemetryDisabled() const {
|
||||||
|
return ui_.checkIsTelemetryDisabled->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] isDisabled The new value for the 'Disable Telemetry' check box.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void SettingsTab::setIsTelemetryDisabled(bool isDisabled) {
|
||||||
|
ui_.checkIsTelemetryDisabled->setChecked(isDisabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \return The delay to apply before sending automatically generated events.
|
/// \return The delay to apply before sending automatically generated events.
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
|
|||||||
@ -45,6 +45,7 @@ public: // member functions.
|
|||||||
bool isAutostartOn() const; ///< Get the value for the 'Autostart' check.
|
bool isAutostartOn() const; ///< Get the value for the 'Autostart' check.
|
||||||
bool isBetaEnabled() const; ///< Get the value for the 'Beta Enabled' check.
|
bool isBetaEnabled() const; ///< Get the value for the 'Beta Enabled' check.
|
||||||
bool isAllMailVisible() const; ///< Get the value for the 'All Mail Visible' check.
|
bool isAllMailVisible() const; ///< Get the value for the 'All Mail Visible' check.
|
||||||
|
bool isTelemetryDisabled() const; ///< Get the value for the 'Disable Telemetry' check box.
|
||||||
QString colorSchemeName() const; ///< Get the value of the 'Use Dark Theme' checkbox.
|
QString colorSchemeName() const; ///< Get the value of the 'Use Dark Theme' checkbox.
|
||||||
qint32 eventDelayMs() const; ///< Get the delay for sending automatically generated events.
|
qint32 eventDelayMs() const; ///< Get the delay for sending automatically generated events.
|
||||||
QString logsPath() const; ///< Get the content of the 'Logs Path' edit.
|
QString logsPath() const; ///< Get the content of the 'Logs Path' edit.
|
||||||
@ -74,6 +75,7 @@ public slots:
|
|||||||
void setIsAutostartOn(bool on); ///< Set the value for the 'Autostart' check box.
|
void setIsAutostartOn(bool on); ///< Set the value for the 'Autostart' check box.
|
||||||
void setIsBetaEnabled(bool enabled); ///< Set the value for the 'Beta Enabled' check box.
|
void setIsBetaEnabled(bool enabled); ///< Set the value for the 'Beta Enabled' check box.
|
||||||
void setIsAllMailVisible(bool visible); ///< Set the value for the 'All Mail Visible' check box.
|
void setIsAllMailVisible(bool visible); ///< Set the value for the 'All Mail Visible' check box.
|
||||||
|
void setIsTelemetryDisabled(bool isDisabled); ///< Set the value for the 'Disable Telemetry' check box.
|
||||||
void setColorSchemeName(QString const &name); ///< Set the value for the 'Use Dark Theme' check box.
|
void setColorSchemeName(QString const &name); ///< Set the value for the 'Use Dark Theme' check box.
|
||||||
void setBugReport(QString const &osType, QString const &osVersion, QString const &emailClient, QString const &address, QString const &description,
|
void setBugReport(QString const &osType, QString const &osVersion, QString const &emailClient, QString const &address, QString const &description,
|
||||||
bool includeLogs); ///< Set the content of the bug report box.
|
bool includeLogs); ///< Set the content of the bug report box.
|
||||||
|
|||||||
@ -170,6 +170,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QCheckBox" name="checkIsTelemetryDisabled">
|
||||||
|
<property name="text">
|
||||||
|
<string>Disable Telemetry</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@ -114,6 +114,7 @@ add_executable(bridge-gui
|
|||||||
EventStreamWorker.cpp EventStreamWorker.h
|
EventStreamWorker.cpp EventStreamWorker.h
|
||||||
LogUtils.cpp LogUtils.h
|
LogUtils.cpp LogUtils.h
|
||||||
main.cpp
|
main.cpp
|
||||||
|
TrayIcon.cpp TrayIcon.h
|
||||||
Pch.h
|
Pch.h
|
||||||
QMLBackend.cpp QMLBackend.h
|
QMLBackend.cpp QMLBackend.h
|
||||||
UserList.cpp UserList.h
|
UserList.cpp UserList.h
|
||||||
|
|||||||
@ -24,7 +24,6 @@
|
|||||||
#include <bridgepp/Log/LogUtils.h>
|
#include <bridgepp/Log/LogUtils.h>
|
||||||
#include <bridgepp/GRPC/GRPCClient.h>
|
#include <bridgepp/GRPC/GRPCClient.h>
|
||||||
#include <bridgepp/Worker/Overseer.h>
|
#include <bridgepp/Worker/Overseer.h>
|
||||||
#include <bridgepp/BridgeUtils.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define HANDLE_EXCEPTION(x) try { x } \
|
#define HANDLE_EXCEPTION(x) try { x } \
|
||||||
@ -50,6 +49,9 @@ QMLBackend::QMLBackend()
|
|||||||
/// \param[in] serviceConfig
|
/// \param[in] serviceConfig
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
void QMLBackend::init(GRPCConfig const &serviceConfig) {
|
void QMLBackend::init(GRPCConfig const &serviceConfig) {
|
||||||
|
trayIcon_.reset(new TrayIcon());
|
||||||
|
this->setNormalTrayIcon();
|
||||||
|
|
||||||
connect(this, &QMLBackend::fatalError, &app(), &AppController::onFatalError);
|
connect(this, &QMLBackend::fatalError, &app(), &AppController::onFatalError);
|
||||||
|
|
||||||
users_ = new UserList(this);
|
users_ = new UserList(this);
|
||||||
@ -100,6 +102,14 @@ bool QMLBackend::waitForEventStreamReaderToFinish(qint32 timeoutMs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \return The list of users
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
UserList const &QMLBackend::users() const {
|
||||||
|
return *users_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \return The build year as a string (e.g. 2023)
|
/// \return The build year as a string (e.g. 2023)
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
@ -335,6 +345,18 @@ bool QMLBackend::isAllMailVisible() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \return The value for the 'isAllMailVisible' property.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
bool QMLBackend::isTelemetryDisabled() const {
|
||||||
|
HANDLE_EXCEPTION_RETURN_BOOL(
|
||||||
|
bool v;
|
||||||
|
app().grpc().isTelemetryDisabled(v);
|
||||||
|
return v;
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \return The value for the 'colorSchemeName' property.
|
/// \return The value for the 'colorSchemeName' property.
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
@ -569,6 +591,17 @@ void QMLBackend::changeIsAllMailVisible(bool isVisible) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] isDisabled The new state of the 'Is telemetry disabled property'.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::toggleIsTelemetryDisabled(bool isDisabled) {
|
||||||
|
HANDLE_EXCEPTION(
|
||||||
|
app().grpc().setIsTelemetryDisabled(isDisabled);
|
||||||
|
emit isTelemetryDisabledChanged(isDisabled);
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \param[in] scheme the scheme name
|
/// \param[in] scheme the scheme name
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
@ -838,6 +871,49 @@ void QMLBackend::sendBadEventUserFeedback(QString const &userID, bool doResync)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::setNormalTrayIcon() {
|
||||||
|
if (trayIcon_) {
|
||||||
|
trayIcon_->setState(TrayIcon::State::Normal, tr("Connected"), ":/qml/icons/ic-connected.svg");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] stateString A string describing the state.
|
||||||
|
/// \param[in] statusIcon The path of the status icon.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::setErrorTrayIcon(QString const &stateString, QString const &statusIcon) {
|
||||||
|
if (trayIcon_) {
|
||||||
|
trayIcon_->setState(TrayIcon::State::Error, stateString, statusIcon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] stateString A string describing the state.
|
||||||
|
/// \param[in] statusIcon The path of the status icon.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::setWarnTrayIcon(QString const &stateString, QString const &statusIcon) {
|
||||||
|
if (trayIcon_) {
|
||||||
|
trayIcon_->setState(TrayIcon::State::Warn, stateString, statusIcon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] stateString A string describing the state.
|
||||||
|
/// \param[in] statusIcon The path of the status icon.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void QMLBackend::setUpdateTrayIcon(QString const &stateString, QString const &statusIcon) {
|
||||||
|
if (trayIcon_) {
|
||||||
|
trayIcon_->setState(TrayIcon::State::Update, stateString, statusIcon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \param[in] imapPort The IMAP port.
|
/// \param[in] imapPort The IMAP port.
|
||||||
/// \param[in] smtpPort The SMTP port.
|
/// \param[in] smtpPort The SMTP port.
|
||||||
@ -892,7 +968,7 @@ void QMLBackend::onLoginAlreadyLoggedIn(QString const &userID) {
|
|||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \param[in] userID The userID.
|
/// \param[in] userID The userID.
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
void QMLBackend::onUserBadEvent(QString const &userID, QString const& ) {
|
void QMLBackend::onUserBadEvent(QString const &userID, QString const &) {
|
||||||
HANDLE_EXCEPTION(
|
HANDLE_EXCEPTION(
|
||||||
if (badEventDisplayQueue_.contains(userID)) {
|
if (badEventDisplayQueue_.contains(userID)) {
|
||||||
app().log().error("Received 'bad event' for a user that is already in the queue.");
|
app().log().error("Received 'bad event' for a user that is already in the queue.");
|
||||||
@ -921,8 +997,9 @@ void QMLBackend::onIMAPLoginFailed(QString const &username) {
|
|||||||
if ((!user) || (user->state() != UserState::SignedOut)) { // We want to pop-up only if a signed-out user has been detected
|
if ((!user) || (user->state() != UserState::SignedOut)) { // We want to pop-up only if a signed-out user has been detected
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (user->isInIMAPLoginFailureCooldown())
|
if (user->isInIMAPLoginFailureCooldown()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
user->startImapLoginFailureCooldown(60 * 60 * 1000); // 1 hour cooldown during which we will not display this notification to this user again.
|
user->startImapLoginFailureCooldown(60 * 60 * 1000); // 1 hour cooldown during which we will not display this notification to this user again.
|
||||||
emit selectUser(user->id());
|
emit selectUser(user->id());
|
||||||
emit imapLoginWhileSignedOut(username);
|
emit imapLoginWhileSignedOut(username);
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "MacOS/DockIcon.h"
|
#include "MacOS/DockIcon.h"
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
|
#include "TrayIcon.h"
|
||||||
#include "UserList.h"
|
#include "UserList.h"
|
||||||
#include <bridgepp/GRPC/GRPCClient.h>
|
#include <bridgepp/GRPC/GRPCClient.h>
|
||||||
#include <bridgepp/GRPC/GRPCUtils.h>
|
#include <bridgepp/GRPC/GRPCUtils.h>
|
||||||
@ -43,6 +44,7 @@ public: // member functions.
|
|||||||
QMLBackend &operator=(QMLBackend &&) = delete; ///< Disabled move assignment operator.
|
QMLBackend &operator=(QMLBackend &&) = delete; ///< Disabled move assignment operator.
|
||||||
void init(GRPCConfig const &serviceConfig); ///< Initialize the backend.
|
void init(GRPCConfig const &serviceConfig); ///< Initialize the backend.
|
||||||
bool waitForEventStreamReaderToFinish(qint32 timeoutMs); ///< Wait for the event stream reader to finish.
|
bool waitForEventStreamReaderToFinish(qint32 timeoutMs); ///< Wait for the event stream reader to finish.
|
||||||
|
UserList const& users() const; ///< Return the list of users
|
||||||
|
|
||||||
// invokable methods can be called from QML. They generally return a value, which slots cannot do.
|
// invokable methods can be called from QML. They generally return a value, which slots cannot do.
|
||||||
Q_INVOKABLE static QString buildYear(); ///< Return the application build year.
|
Q_INVOKABLE static QString buildYear(); ///< Return the application build year.
|
||||||
@ -67,6 +69,7 @@ public: // Qt/QML properties. Note that the NOTIFY-er signal is required even fo
|
|||||||
Q_PROPERTY(bool isAutostartOn READ isAutostartOn NOTIFY isAutostartOnChanged)
|
Q_PROPERTY(bool isAutostartOn READ isAutostartOn NOTIFY isAutostartOnChanged)
|
||||||
Q_PROPERTY(bool isBetaEnabled READ isBetaEnabled NOTIFY isBetaEnabledChanged)
|
Q_PROPERTY(bool isBetaEnabled READ isBetaEnabled NOTIFY isBetaEnabledChanged)
|
||||||
Q_PROPERTY(bool isAllMailVisible READ isAllMailVisible NOTIFY isAllMailVisibleChanged)
|
Q_PROPERTY(bool isAllMailVisible READ isAllMailVisible NOTIFY isAllMailVisibleChanged)
|
||||||
|
Q_PROPERTY(bool isTelemetryDisabled READ isTelemetryDisabled NOTIFY isTelemetryDisabledChanged)
|
||||||
Q_PROPERTY(QString colorSchemeName READ colorSchemeName NOTIFY colorSchemeNameChanged)
|
Q_PROPERTY(QString colorSchemeName READ colorSchemeName NOTIFY colorSchemeNameChanged)
|
||||||
Q_PROPERTY(QUrl diskCachePath READ diskCachePath NOTIFY diskCachePathChanged)
|
Q_PROPERTY(QUrl diskCachePath READ diskCachePath NOTIFY diskCachePathChanged)
|
||||||
Q_PROPERTY(bool useSSLForIMAP READ useSSLForIMAP WRITE setUseSSLForIMAP NOTIFY useSSLForIMAPChanged)
|
Q_PROPERTY(bool useSSLForIMAP READ useSSLForIMAP WRITE setUseSSLForIMAP NOTIFY useSSLForIMAPChanged)
|
||||||
@ -99,6 +102,7 @@ public: // Qt/QML properties. Note that the NOTIFY-er signal is required even fo
|
|||||||
bool isAutostartOn() const; ///< Getter for the 'isAutostartOn' property.
|
bool isAutostartOn() const; ///< Getter for the 'isAutostartOn' property.
|
||||||
bool isBetaEnabled() const; ///< Getter for the 'isBetaEnabled' property.
|
bool isBetaEnabled() const; ///< Getter for the 'isBetaEnabled' property.
|
||||||
bool isAllMailVisible() const; ///< Getter for the 'isAllMailVisible' property.
|
bool isAllMailVisible() const; ///< Getter for the 'isAllMailVisible' property.
|
||||||
|
bool isTelemetryDisabled() const; ///< Getter for the 'isTelemetryDisabled' property.
|
||||||
QString colorSchemeName() const; ///< Getter for the 'colorSchemeName' property.
|
QString colorSchemeName() const; ///< Getter for the 'colorSchemeName' property.
|
||||||
QUrl diskCachePath() const; ///< Getter for the 'diskCachePath' property.
|
QUrl diskCachePath() const; ///< Getter for the 'diskCachePath' property.
|
||||||
void setUseSSLForIMAP(bool value); ///< Setter for the 'useSSLForIMAP' property.
|
void setUseSSLForIMAP(bool value); ///< Setter for the 'useSSLForIMAP' property.
|
||||||
@ -129,6 +133,7 @@ signals: // Signal used by the Qt property system. Many of them are unused but r
|
|||||||
void isAutomaticUpdateOnChanged(bool value); ///<Signal for the change of the 'isAutomaticUpdateOn' property.
|
void isAutomaticUpdateOnChanged(bool value); ///<Signal for the change of the 'isAutomaticUpdateOn' property.
|
||||||
void isBetaEnabledChanged(bool value); ///<Signal for the change of the 'isBetaEnabled' property.
|
void isBetaEnabledChanged(bool value); ///<Signal for the change of the 'isBetaEnabled' property.
|
||||||
void isAllMailVisibleChanged(bool value); ///<Signal for the change of the 'isAllMailVisible' property.
|
void isAllMailVisibleChanged(bool value); ///<Signal for the change of the 'isAllMailVisible' property.
|
||||||
|
void isTelemetryDisabledChanged(bool isDisabled); ///<Signal for the change of the 'isTelemetryDisabled' property.
|
||||||
void colorSchemeNameChanged(QString const &scheme); ///<Signal for the change of the 'colorSchemeName' property.
|
void colorSchemeNameChanged(QString const &scheme); ///<Signal for the change of the 'colorSchemeName' property.
|
||||||
void isDoHEnabledChanged(bool value); ///<Signal for the change of the 'isDoHEnabled' property.
|
void isDoHEnabledChanged(bool value); ///<Signal for the change of the 'isDoHEnabled' property.
|
||||||
void logsPathChanged(QUrl const &path); ///<Signal for the change of the 'logsPath' property.
|
void logsPathChanged(QUrl const &path); ///<Signal for the change of the 'logsPath' property.
|
||||||
@ -151,6 +156,7 @@ public slots: // slot for signals received from QML -> To be forwarded to Bridge
|
|||||||
void toggleAutostart(bool active); ///< Slot for the autostart toggle.
|
void toggleAutostart(bool active); ///< Slot for the autostart toggle.
|
||||||
void toggleBeta(bool active); ///< Slot for the beta toggle.
|
void toggleBeta(bool active); ///< Slot for the beta toggle.
|
||||||
void changeIsAllMailVisible(bool isVisible); ///< Slot for the changing of 'All Mail' visibility.
|
void changeIsAllMailVisible(bool isVisible); ///< Slot for the changing of 'All Mail' visibility.
|
||||||
|
void toggleIsTelemetryDisabled(bool isDisabled); ///< Slot for toggling telemetry on/off.
|
||||||
void changeColorScheme(QString const &scheme); ///< Slot for the change of the theme.
|
void changeColorScheme(QString const &scheme); ///< Slot for the change of the theme.
|
||||||
void setDiskCachePath(QUrl const &path) const; ///< Slot for the change of the disk cache path.
|
void setDiskCachePath(QUrl const &path) const; ///< Slot for the change of the disk cache path.
|
||||||
void login(QString const &username, QString const &password) const; ///< Slot for the login button (initial login).
|
void login(QString const &username, QString const &password) const; ///< Slot for the login button (initial login).
|
||||||
@ -174,6 +180,10 @@ public slots: // slot for signals received from QML -> To be forwarded to Bridge
|
|||||||
void onVersionChanged(); ///< Slot for the version change signal.
|
void onVersionChanged(); ///< Slot for the version change signal.
|
||||||
void setMailServerSettings(int imapPort, int smtpPort, bool useSSLForIMAP, bool useSSLForSMTP) const; ///< Forwards a connection mode change request from QML to gRPC
|
void setMailServerSettings(int imapPort, int smtpPort, bool useSSLForIMAP, bool useSSLForSMTP) const; ///< Forwards a connection mode change request from QML to gRPC
|
||||||
void sendBadEventUserFeedback(QString const &userID, bool doResync); ///< Slot the providing user feedback for a bad event.
|
void sendBadEventUserFeedback(QString const &userID, bool doResync); ///< Slot the providing user feedback for a bad event.
|
||||||
|
void setNormalTrayIcon(); ///< Set the tray icon to normal.
|
||||||
|
void setErrorTrayIcon(QString const& stateString, QString const &statusIcon); ///< Set the tray icon to 'error' state.
|
||||||
|
void setWarnTrayIcon(QString const& stateString, QString const &statusIcon); ///< Set the tray icon to 'warn' state.
|
||||||
|
void setUpdateTrayIcon(QString const& stateString, QString const &statusIcon); ///< Set the tray icon to 'update' state.
|
||||||
|
|
||||||
public slots: // slot for signals received from gRPC that need transformation instead of simple forwarding
|
public slots: // slot for signals received from gRPC that need transformation instead of simple forwarding
|
||||||
void onMailServerSettingsChanged(int imapPort, int smtpPort, bool useSSLForIMAP, bool useSSLForSMTP); ///< Slot for the ConnectionModeChanged gRPC event.
|
void onMailServerSettingsChanged(int imapPort, int smtpPort, bool useSSLForIMAP, bool useSSLForSMTP); ///< Slot for the ConnectionModeChanged gRPC event.
|
||||||
@ -233,8 +243,10 @@ signals: // Signals received from the Go backend, to be forwarded to QML
|
|||||||
void bugReportSendError(); ///< Signal for the 'bugReportSendError' gRPC stream event.
|
void bugReportSendError(); ///< Signal for the 'bugReportSendError' gRPC stream event.
|
||||||
void showMainWindow(); ///< Signal for the 'showMainWindow' gRPC stream event.
|
void showMainWindow(); ///< Signal for the 'showMainWindow' gRPC stream event.
|
||||||
void hideMainWindow(); ///< Signal for the 'hideMainWindow' gRPC stream event.
|
void hideMainWindow(); ///< Signal for the 'hideMainWindow' gRPC stream event.
|
||||||
|
void showHelp(); ///< Signal for the 'showHelp' event (from the context menu).
|
||||||
|
void showSettings(); ///< Signal for the 'showHelp' event (from the context menu).
|
||||||
|
void selectUser(QString const& userID); ///< Signal emitted in order to selected a user with a given ID in the list.
|
||||||
void genericError(QString const &title, QString const &description); ///< Signal for the 'genericError' gRPC stream event.
|
void genericError(QString const &title, QString const &description); ///< Signal for the 'genericError' gRPC stream event.
|
||||||
void selectUser(QString const); ///< Signal that request the given user account to be displayed.
|
|
||||||
void imapLoginWhileSignedOut(QString const& username); ///< Signal for the notification of IMAP login attempt on a signed out account.
|
void imapLoginWhileSignedOut(QString const& username); ///< Signal for the notification of IMAP login attempt on a signed out account.
|
||||||
|
|
||||||
// This signal is emitted when an exception is intercepted is calls triggered by QML. QML engine would intercept the exception otherwise.
|
// This signal is emitted when an exception is intercepted is calls triggered by QML. QML engine would intercept the exception otherwise.
|
||||||
@ -257,7 +269,7 @@ private: // data members
|
|||||||
bool useSSLForIMAP_ { false }; ///< The cached value for useSSLForIMAP.
|
bool useSSLForIMAP_ { false }; ///< The cached value for useSSLForIMAP.
|
||||||
bool useSSLForSMTP_ { false }; ///< The cached value for useSSLForSMTP.
|
bool useSSLForSMTP_ { false }; ///< The cached value for useSSLForSMTP.
|
||||||
QList<QString> badEventDisplayQueue_; ///< THe queue for displaying 'bad event feedback request dialog'.
|
QList<QString> badEventDisplayQueue_; ///< THe queue for displaying 'bad event feedback request dialog'.
|
||||||
|
std::unique_ptr<TrayIcon> trayIcon_;
|
||||||
friend class AppController;
|
friend class AppController;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -5,11 +5,7 @@
|
|||||||
<file>qml/AccountView.qml</file>
|
<file>qml/AccountView.qml</file>
|
||||||
<file>qml/Banner.qml</file>
|
<file>qml/Banner.qml</file>
|
||||||
<file>qml/Bridge.qml</file>
|
<file>qml/Bridge.qml</file>
|
||||||
<file>qml/Bridge_test.qml</file>
|
|
||||||
<file>qml/bridgeqml.qmlproject</file>
|
<file>qml/bridgeqml.qmlproject</file>
|
||||||
<file>qml/BridgeTest/UserControl.qml</file>
|
|
||||||
<file>qml/BridgeTest/UserList.qml</file>
|
|
||||||
<file>qml/BridgeTest/UserModel.qml</file>
|
|
||||||
<file>qml/BugReportView.qml</file>
|
<file>qml/BugReportView.qml</file>
|
||||||
<file>qml/Configuration.qml</file>
|
<file>qml/Configuration.qml</file>
|
||||||
<file>qml/ConfigurationItem.qml</file>
|
<file>qml/ConfigurationItem.qml</file>
|
||||||
@ -28,6 +24,7 @@
|
|||||||
<file>qml/icons/ic-connected.svg</file>
|
<file>qml/icons/ic-connected.svg</file>
|
||||||
<file>qml/icons/ic-copy.svg</file>
|
<file>qml/icons/ic-copy.svg</file>
|
||||||
<file>qml/icons/ic-cross-close.svg</file>
|
<file>qml/icons/ic-cross-close.svg</file>
|
||||||
|
<file>qml/icons/ic-dot.svg</file>
|
||||||
<file>qml/icons/ic-drive.svg</file>
|
<file>qml/icons/ic-drive.svg</file>
|
||||||
<file>qml/icons/ic-exclamation-circle-filled.svg</file>
|
<file>qml/icons/ic-exclamation-circle-filled.svg</file>
|
||||||
<file>qml/icons/ic-external-link.svg</file>
|
<file>qml/icons/ic-external-link.svg</file>
|
||||||
@ -104,17 +101,6 @@
|
|||||||
<file>qml/ConnectionModeSettings.qml</file>
|
<file>qml/ConnectionModeSettings.qml</file>
|
||||||
<file>qml/SplashScreen.qml</file>
|
<file>qml/SplashScreen.qml</file>
|
||||||
<file>qml/Status.qml</file>
|
<file>qml/Status.qml</file>
|
||||||
<file>qml/StatusWindow.qml</file>
|
|
||||||
<file>qml/tests/Buttons.qml</file>
|
|
||||||
<file>qml/tests/ButtonsColumn.qml</file>
|
|
||||||
<file>qml/tests/CheckBoxes.qml</file>
|
|
||||||
<file>qml/tests/ComboBoxes.qml</file>
|
|
||||||
<file>qml/tests/RadioButtons.qml</file>
|
|
||||||
<file>qml/tests/Switches.qml</file>
|
|
||||||
<file>qml/tests/Test.qml</file>
|
|
||||||
<file>qml/tests/TestComponents.qml</file>
|
|
||||||
<file>qml/tests/TextAreas.qml</file>
|
|
||||||
<file>qml/tests/TextFields.qml</file>
|
|
||||||
<file>qml/WelcomeGuide.qml</file>
|
<file>qml/WelcomeGuide.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
296
internal/frontend/bridge-gui/bridge-gui/TrayIcon.cpp
Normal file
296
internal/frontend/bridge-gui/bridge-gui/TrayIcon.cpp
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
// Copyright (c) 2023 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 "TrayIcon.h"
|
||||||
|
#include "QMLBackend.h"
|
||||||
|
#include <bridgepp/Exception/Exception.h>
|
||||||
|
#include <bridgepp/BridgeUtils.h>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace bridgepp;
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
|
||||||
|
QColor const normalColor(30, 168, 133); /// The normal state color.
|
||||||
|
QColor const errorColor(220, 50, 81); ///< The error state color.
|
||||||
|
QColor const warnColor(255, 153, 0); ///< The warn state color.
|
||||||
|
QColor const updateColor(35, 158, 206); ///< The warn state color.
|
||||||
|
QColor const greyColor(112, 109, 107); ///< The grey color.
|
||||||
|
qint64 const iconRefreshTimerIntervalMs = 1000; ///< The interval for the refresh timer when switching DPI / screen config, in milliseconds.
|
||||||
|
qint64 const iconRefreshDurationSecs = 10; ///< The total number of seconds during wich we periodically refresh the icon after a DPI change.
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \brief Create a single resolution icon from an image. Throw an exception in case of failure.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
QIcon loadIconFromImage(QString const &path) {
|
||||||
|
QPixmap const pixmap(path);
|
||||||
|
if (pixmap.isNull()) {
|
||||||
|
throw Exception(QString("Could create icon from image '%1'.").arg(path));
|
||||||
|
}
|
||||||
|
return QIcon(pixmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \brief Retrieve the color associated with a tray icon state.
|
||||||
|
///
|
||||||
|
/// \param[in] state The state.
|
||||||
|
/// \return The color associated with a given tray icon state.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
QColor stateColor(TrayIcon::State state) {
|
||||||
|
switch (state) {
|
||||||
|
case TrayIcon::State::Normal:
|
||||||
|
return normalColor;
|
||||||
|
case TrayIcon::State::Error:
|
||||||
|
return errorColor;
|
||||||
|
case TrayIcon::State::Warn:
|
||||||
|
return warnColor;
|
||||||
|
case TrayIcon::State::Update:
|
||||||
|
return updateColor;
|
||||||
|
default:
|
||||||
|
app().log().error(QString("Unknown tray icon state %1.").arg(static_cast<qint32>(state)));
|
||||||
|
return normalColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \brief Return the text identifying a state in resource file names.
|
||||||
|
///
|
||||||
|
/// \param[in] state The state.
|
||||||
|
/// \param[in] The text identifying the state in resource file names.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
QString stateText(TrayIcon::State state) {
|
||||||
|
switch (state) {
|
||||||
|
case TrayIcon::State::Normal:
|
||||||
|
return "norm";
|
||||||
|
case TrayIcon::State::Error:
|
||||||
|
return "error";
|
||||||
|
case TrayIcon::State::Warn:
|
||||||
|
return "warn";
|
||||||
|
case TrayIcon::State::Update:
|
||||||
|
return "update";
|
||||||
|
default:
|
||||||
|
app().log().error(QString("Unknown tray icon state %1.").arg(static_cast<qint32>(state)));
|
||||||
|
return "norm";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
TrayIcon::TrayIcon()
|
||||||
|
: QSystemTrayIcon()
|
||||||
|
, menu_(new QMenu) {
|
||||||
|
|
||||||
|
this->generateDotIcons();
|
||||||
|
this->setContextMenu(menu_.get());
|
||||||
|
|
||||||
|
connect(menu_.get(), &QMenu::aboutToShow, this, &TrayIcon::onMenuAboutToShow);
|
||||||
|
connect(this, &TrayIcon::selectUser, &app().backend(), &QMLBackend::selectUser);
|
||||||
|
connect(this, &TrayIcon::activated, this, &TrayIcon::onActivated);
|
||||||
|
|
||||||
|
this->show();
|
||||||
|
this->setState(State::Normal, QString(), QString());
|
||||||
|
|
||||||
|
// TrayIcon does not expose its screen, so we connect relevant screen events to our DPI change handler.
|
||||||
|
for (QScreen *screen: QGuiApplication::screens()) {
|
||||||
|
connect(screen, &QScreen::logicalDotsPerInchChanged, this, &TrayIcon::handleDPIChange);
|
||||||
|
}
|
||||||
|
connect(qApp, &QApplication::screenAdded, [&](QScreen *screen) { connect(screen, &QScreen::logicalDotsPerInchChanged, this, &TrayIcon::handleDPIChange); });
|
||||||
|
connect(qApp, &QApplication::primaryScreenChanged, [&](QScreen *screen) { connect(screen, &QScreen::logicalDotsPerInchChanged, this, &TrayIcon::handleDPIChange); });
|
||||||
|
|
||||||
|
iconRefreshTimer_.setSingleShot(false);
|
||||||
|
iconRefreshTimer_.setInterval(iconRefreshTimerIntervalMs);
|
||||||
|
connect(&iconRefreshTimer_, &QTimer::timeout, this, &TrayIcon::onIconRefreshTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void TrayIcon::onMenuAboutToShow() {
|
||||||
|
this->refreshContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void TrayIcon::onUserClicked() {
|
||||||
|
try {
|
||||||
|
auto action = dynamic_cast<QAction *>(this->sender());
|
||||||
|
if (!action) {
|
||||||
|
throw Exception("Could not retrieve context menu action.");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString const &userID = action->data().toString();
|
||||||
|
if (userID.isNull()) {
|
||||||
|
throw Exception("Could not retrieve context menu's selected user.");
|
||||||
|
}
|
||||||
|
|
||||||
|
emit selectUser(userID);
|
||||||
|
} catch (Exception const &e) {
|
||||||
|
app().log().error(e.qwhat());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] reason The icon activation reason.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void TrayIcon::onActivated(QSystemTrayIcon::ActivationReason reason) {
|
||||||
|
if ((QSystemTrayIcon::Trigger == reason) && !onMacOS()) {
|
||||||
|
app().backend().showMainWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void TrayIcon::handleDPIChange() {
|
||||||
|
this->setIcon();
|
||||||
|
|
||||||
|
// Windows forces us to apply a hack. Tray icon does not redraw by itself, so we use the Qt signal that detects screen and DPI changes.
|
||||||
|
// But the moment we get the signal the DPI change is not yet in effect. so redrawing now will have no effect, and we don't really
|
||||||
|
// know when we can safely redraw. So we will redraw the icon every second for some time.
|
||||||
|
iconRefreshDeadline_ = QDateTime::currentDateTime().addSecs(iconRefreshDurationSecs);
|
||||||
|
if (!iconRefreshTimer_.isActive()) {
|
||||||
|
iconRefreshTimer_.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void TrayIcon::setIcon() {
|
||||||
|
QString const style = onMacOS() ? "mono" : "color";
|
||||||
|
QString const text = stateText(state_);
|
||||||
|
|
||||||
|
QIcon icon = loadIconFromImage(QString(":/qml/icons/systray-%1-%2.png").arg(style, text));
|
||||||
|
icon.setIsMask(true);
|
||||||
|
QSystemTrayIcon::setIcon(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void TrayIcon::onIconRefreshTimer() {
|
||||||
|
this->setIcon();
|
||||||
|
if (QDateTime::currentDateTime() > iconRefreshDeadline_) {
|
||||||
|
iconRefreshTimer_.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void TrayIcon::generateDotIcons() {
|
||||||
|
QPixmap dotSVG(":/qml/icons/ic-dot.svg");
|
||||||
|
struct IconColor {
|
||||||
|
QIcon &icon;
|
||||||
|
QColor color;
|
||||||
|
};
|
||||||
|
for (auto pair: QList<IconColor> {{ greenDot_, normalColor }, { greyDot_, greyColor }, { orangeDot_, warnColor }}) {
|
||||||
|
QPixmap p = dotSVG;
|
||||||
|
QPainter painter(&p);
|
||||||
|
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
||||||
|
painter.fillRect(p.rect(), pair.color);
|
||||||
|
painter.end();
|
||||||
|
pair.icon = QIcon(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] state The state.
|
||||||
|
/// \param[in] stateString A string describing the state.
|
||||||
|
/// \param[in] statusIconPath The status icon path.
|
||||||
|
/// \param[in] statusIconColor The color for the status icon in hex.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void TrayIcon::setState(TrayIcon::State state, QString const &stateString, QString const &statusIconPath) {
|
||||||
|
stateString_ = stateString;
|
||||||
|
state_ = state;
|
||||||
|
this->setIcon();
|
||||||
|
this->generateStatusIcon(statusIconPath, stateColor(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] svgPath The path of the SVG file for the icon.
|
||||||
|
/// \param[in] color The color to apply to the icon.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void TrayIcon::generateStatusIcon(QString const &svgPath, QColor const &color) {
|
||||||
|
// We use the SVG path as pixmap mask and fill it with the appropriate color
|
||||||
|
QString resourcePath = svgPath;
|
||||||
|
resourcePath.replace(QRegularExpression(R"(^\.\/)"), ":/qml/"); // QML resource path are a bit different from the Qt resources path.
|
||||||
|
QPixmap pixmap(resourcePath);
|
||||||
|
QPainter painter(&pixmap);
|
||||||
|
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
||||||
|
painter.fillRect(pixmap.rect(), color);
|
||||||
|
painter.end();
|
||||||
|
statusIcon_ = QIcon(pixmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//**********************************************************************************************************************
|
||||||
|
//
|
||||||
|
//**********************************************************************************************************************
|
||||||
|
void TrayIcon::refreshContextMenu() {
|
||||||
|
if (!menu_) {
|
||||||
|
app().log().error("Native tray icon context menu is null.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
menu_->clear();
|
||||||
|
menu_->addAction(statusIcon_, stateString_, &app().backend(), &QMLBackend::showMainWindow);
|
||||||
|
menu_->addSeparator();
|
||||||
|
UserList const &users = app().backend().users();
|
||||||
|
qint32 const userCount = users.count();
|
||||||
|
for (qint32 i = 0; i < userCount; i++) {
|
||||||
|
User const &user = *users.get(i);
|
||||||
|
UserState const state = user.state();
|
||||||
|
auto action = new QAction(user.primaryEmailOrUsername());
|
||||||
|
action->setIcon((UserState::Connected == state) ? greenDot_ : (UserState::Locked == state ? orangeDot_ : greyDot_));
|
||||||
|
action->setData(user.id());
|
||||||
|
connect(action, &QAction::triggered, this, &TrayIcon::onUserClicked);
|
||||||
|
if (i < 10) {
|
||||||
|
action->setShortcut(QKeySequence(QString("Ctrl+%1").arg((i + 1) % 10)));
|
||||||
|
}
|
||||||
|
menu_->addAction(action);
|
||||||
|
}
|
||||||
|
if (userCount) {
|
||||||
|
menu_->addSeparator();
|
||||||
|
}
|
||||||
|
menu_->addAction(tr("&Open Bridge"), QKeySequence("Ctrl+O"), &app().backend(), &QMLBackend::showMainWindow);
|
||||||
|
menu_->addAction(tr("&Help"), QKeySequence("Ctrl+F1"), &app().backend(), &QMLBackend::showHelp);
|
||||||
|
menu_->addAction(tr("&Settings"), QKeySequence("Ctrl+,"), &app().backend(), &QMLBackend::showSettings);
|
||||||
|
menu_->addSeparator();
|
||||||
|
menu_->addAction(tr("&Quit Bridge"), QKeySequence("Ctrl+Q"), &app().backend(), &QMLBackend::quit);
|
||||||
|
}
|
||||||
77
internal/frontend/bridge-gui/bridge-gui/TrayIcon.h
Normal file
77
internal/frontend/bridge-gui/bridge-gui/TrayIcon.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// Copyright (c) 2023 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_GUI_NATIVE_TRAY_ICON_H
|
||||||
|
#define BRIDGE_GUI_NATIVE_TRAY_ICON_H
|
||||||
|
|
||||||
|
|
||||||
|
//**********************************************************************************************************************
|
||||||
|
/// \brief A native tray icon.
|
||||||
|
//**********************************************************************************************************************
|
||||||
|
class TrayIcon: public QSystemTrayIcon {
|
||||||
|
Q_OBJECT
|
||||||
|
public: // typedef enum
|
||||||
|
enum class State {
|
||||||
|
Normal,
|
||||||
|
Error,
|
||||||
|
Warn,
|
||||||
|
Update,
|
||||||
|
}; ///< Enumeration for the state.
|
||||||
|
|
||||||
|
public: // data members
|
||||||
|
TrayIcon(); ///< Default constructor.
|
||||||
|
~TrayIcon() override = default; ///< Destructor.
|
||||||
|
TrayIcon(TrayIcon const&) = delete; ///< Disabled copy-constructor.
|
||||||
|
TrayIcon(TrayIcon&&) = delete; ///< Disabled assignment copy-constructor.
|
||||||
|
TrayIcon& operator=(TrayIcon const&) = delete; ///< Disabled assignment operator.
|
||||||
|
TrayIcon& operator=(TrayIcon&&) = delete; ///< Disabled move assignment operator.
|
||||||
|
void setState(State state, QString const& stateString, QString const &statusIconPath); ///< Set the state of the icon
|
||||||
|
void showNotificationPopup(QString const& title, QString const &message, QString const& iconPath); ///< Display a pop up notification.
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void selectUser(QString const& userID); ///< Signal for selecting a user with a given userID
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onMenuAboutToShow(); ///< Slot called before the context menu is shown.
|
||||||
|
void onUserClicked(); ///< Slot triggered when clicking on a user in the context menu.
|
||||||
|
static void onActivated(QSystemTrayIcon::ActivationReason reason); ///< Slot for the activation of the system tray icon.
|
||||||
|
void handleDPIChange(); ///< Handles DPI change.
|
||||||
|
void setIcon(); ///< set the tray icon.
|
||||||
|
void onIconRefreshTimer(); ///< Timer for icon refresh.
|
||||||
|
|
||||||
|
private: // member functions.
|
||||||
|
void generateDotIcons(); ///< generate the colored dot icons used for user status.
|
||||||
|
void generateStatusIcon(QString const &svgPath, QColor const& color); ///< Generate the status icon.
|
||||||
|
void refreshContextMenu(); ///< Refresh the context menu.
|
||||||
|
|
||||||
|
private: // data members
|
||||||
|
State state_ { State::Normal }; ///< The state of the tray icon.
|
||||||
|
QString stateString_; ///< The current state string.
|
||||||
|
std::unique_ptr<QMenu> menu_; ///< The context menu for the tray icon. Not owned by the tray icon.
|
||||||
|
QIcon statusIcon_; ///< The path of the status icon displayed in the context menu.
|
||||||
|
QIcon greenDot_; ///< The green dot icon.
|
||||||
|
QIcon greyDot_; ///< The grey dot icon.
|
||||||
|
QIcon orangeDot_; ///< The orange dot icon.
|
||||||
|
|
||||||
|
QTimer iconRefreshTimer_; ///< The timer used to periodically refresh the icon when DPI changes.
|
||||||
|
QDateTime iconRefreshDeadline_; ///< The deadline for refreshing the icon
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //BRIDGE_GUI_NATIVE_TRAY_ICON_H
|
||||||
@ -27,67 +27,55 @@ Item {
|
|||||||
property var notifications
|
property var notifications
|
||||||
property var user
|
property var user
|
||||||
|
|
||||||
signal showSignIn()
|
signal showSignIn
|
||||||
|
|
||||||
signal showSetupGuide(var user, string address)
|
signal showSetupGuide(var user, string address)
|
||||||
|
|
||||||
property int _leftMargin: 64
|
property int _contentWidth: 640
|
||||||
property int _rightMargin: 64
|
|
||||||
property int _topMargin: 32
|
property int _topMargin: 32
|
||||||
property int _detailsTopMargin: 25
|
property int _detailsMargin: 25
|
||||||
property int _bottomMargin: 12
|
|
||||||
property int _spacing: 20
|
property int _spacing: 20
|
||||||
property int _lineWidth: 1
|
property int _lineThickness: 1
|
||||||
|
property bool _connected: root.user ? root.user.state === EUserState.Connected : false
|
||||||
ScrollView {
|
|
||||||
id: scrollView
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
Component.onCompleted: contentItem.boundsBehavior = Flickable.StopAtBounds // Disable the springy effect when scroll reaches top/bottom.
|
color: root.colorScheme.background_weak
|
||||||
|
|
||||||
Item {
|
ScrollView {
|
||||||
// can't use parent here because parent is not ScrollView (Flickable inside contentItem inside ScrollView)
|
id: scrollView
|
||||||
width: scrollView.availableWidth
|
anchors.fill: parent
|
||||||
height: scrollView.availableHeight
|
Component.onCompleted: contentItem.boundsBehavior = Flickable.StopAtBounds
|
||||||
|
|
||||||
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
|
||||||
// do not set implicitWidth because implicit width of ColumnLayout will be equal to maximum implicit width of
|
|
||||||
// internal items. And if one of internal items would be a Text or Label - implicit width of those is always
|
|
||||||
// equal to non-wrapped text (i.e. one line only). That will lead to enabling horizontal scroll when not needed
|
|
||||||
implicitWidth: width
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
id: topLevelColumnLayout
|
||||||
|
anchors.fill: parent
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: topRectangle
|
id: topArea
|
||||||
color: root.colorScheme.background_norm
|
color: root.colorScheme.background_norm
|
||||||
|
clip: true
|
||||||
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
|
||||||
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: childrenRect.height
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: root._spacing
|
id: topLayout
|
||||||
|
width: _contentWidth
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: _spacing
|
||||||
|
|
||||||
anchors.fill: parent
|
RowLayout {
|
||||||
anchors.leftMargin: root._leftMargin
|
// account delegate with action buttons
|
||||||
anchors.rightMargin: root._rightMargin
|
|
||||||
anchors.topMargin: root._topMargin
|
|
||||||
anchors.bottomMargin: root._bottomMargin
|
|
||||||
|
|
||||||
RowLayout { // account delegate with action buttons
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: _topMargin
|
||||||
|
|
||||||
AccountDelegate {
|
AccountDelegate {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
user: root.user
|
user: root.user
|
||||||
type: AccountDelegate.LargeView
|
type: AccountDelegate.LargeView
|
||||||
enabled: root.user ? (root.user.state === EUserState.Connected) : false
|
enabled: _connected
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
@ -95,10 +83,11 @@ Item {
|
|||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Sign out")
|
text: qsTr("Sign out")
|
||||||
secondary: true
|
secondary: true
|
||||||
visible: root.user ? (root.user.state === EUserState.Connected) : false
|
visible: _connected
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!root.user) return
|
if (!root.user)
|
||||||
root.user.logout()
|
return;
|
||||||
|
root.user.logout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,8 +98,9 @@ Item {
|
|||||||
secondary: true
|
secondary: true
|
||||||
visible: root.user ? (root.user.state === EUserState.SignedOut) : false
|
visible: root.user ? (root.user.state === EUserState.SignedOut) : false
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!root.user) return
|
if (!root.user)
|
||||||
root.showSignIn()
|
return;
|
||||||
|
root.showSignIn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,8 +110,9 @@ Item {
|
|||||||
icon.source: "/qml/icons/ic-trash.svg"
|
icon.source: "/qml/icons/ic-trash.svg"
|
||||||
secondary: true
|
secondary: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!root.user) return
|
if (!root.user)
|
||||||
root.notifications.askDeleteAccount(root.user)
|
return;
|
||||||
|
root.notifications.askDeleteAccount(root.user);
|
||||||
}
|
}
|
||||||
visible: root.user ? root.user.state !== EUserState.Locked : false
|
visible: root.user ? root.user.state !== EUserState.Locked : false
|
||||||
}
|
}
|
||||||
@ -129,7 +120,7 @@ Item {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
height: root._lineWidth
|
height: root._lineThickness
|
||||||
color: root.colorScheme.border_weak
|
color: root.colorScheme.border_weak
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,12 +130,12 @@ Item {
|
|||||||
actionText: qsTr("Configure")
|
actionText: qsTr("Configure")
|
||||||
description: qsTr("Using the mailbox details below (re)configure your client.")
|
description: qsTr("Using the mailbox details below (re)configure your client.")
|
||||||
type: SettingsItem.Button
|
type: SettingsItem.Button
|
||||||
enabled: root.user ? root.user.state === EUserState.Connected : false
|
visible: _connected && (!root.user.splitMode) || (root.user.addresses.length === 1)
|
||||||
visible: root.user ? !root.user.splitMode || root.user.addresses.length==1 : false
|
|
||||||
showSeparator: splitMode.visible
|
showSeparator: splitMode.visible
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!root.user) return
|
if (!root.user)
|
||||||
root.showSetupGuide(root.user, user.addresses[0])
|
return;
|
||||||
|
root.showSetupGuide(root.user, user.addresses[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
@ -157,15 +148,14 @@ Item {
|
|||||||
description: qsTr("Setup multiple email addresses individually.")
|
description: qsTr("Setup multiple email addresses individually.")
|
||||||
type: SettingsItem.Toggle
|
type: SettingsItem.Toggle
|
||||||
checked: root.user ? root.user.splitMode : false
|
checked: root.user ? root.user.splitMode : false
|
||||||
visible: root.user ? root.user.addresses.length > 1 : false
|
visible: _connected && root.user.addresses.length > 1
|
||||||
enabled: root.user ? (root.user.state === EUserState.Connected) : false
|
|
||||||
showSeparator: addressSelector.visible
|
showSeparator: addressSelector.visible
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!splitMode.checked){
|
if (!splitMode.checked) {
|
||||||
root.notifications.askEnableSplitMode(user)
|
root.notifications.askEnableSplitMode(user);
|
||||||
} else {
|
} else {
|
||||||
addressSelector.currentIndex = 0
|
addressSelector.currentIndex = 0;
|
||||||
root.user.toggleSplitMode(!splitMode.checked)
|
root.user.toggleSplitMode(!splitMode.checked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,8 +164,8 @@ Item {
|
|||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
enabled: root.user ? (root.user.state === EUserState.Connected) : false
|
Layout.bottomMargin: _spacing
|
||||||
visible: root.user ? root.user.splitMode : false
|
visible: _connected && root.user.splitMode
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: addressSelector
|
id: addressSelector
|
||||||
@ -189,60 +179,68 @@ Item {
|
|||||||
text: qsTr("Configure")
|
text: qsTr("Configure")
|
||||||
secondary: true
|
secondary: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!root.user) return
|
if (!root.user)
|
||||||
root.showSetupGuide(root.user, addressSelector.displayText)
|
return;
|
||||||
|
root.showSetupGuide(root.user, addressSelector.displayText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: 0
|
||||||
|
} // just for some extra space before separator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
id: bottomArea
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: bottomLayout.implicitHeight
|
||||||
color: root.colorScheme.background_weak
|
color: root.colorScheme.background_weak
|
||||||
|
|
||||||
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
|
||||||
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: configuration
|
id: bottomLayout
|
||||||
|
width: _contentWidth
|
||||||
anchors.fill: parent
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.leftMargin: root._leftMargin
|
spacing: _spacing
|
||||||
anchors.rightMargin: root._rightMargin
|
visible: _connected
|
||||||
anchors.topMargin: root._detailsTopMargin
|
|
||||||
anchors.bottomMargin: root._spacing
|
|
||||||
|
|
||||||
spacing: root._spacing
|
|
||||||
visible: root.user ? (root.user.state === EUserState.Connected) : false
|
|
||||||
|
|
||||||
property string currentAddress: addressSelector.displayText
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
Layout.topMargin: _detailsMargin
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Mailbox details")
|
text: qsTr("Mailbox details")
|
||||||
type: Label.Body_semibold
|
type: Label.Body_semibold
|
||||||
}
|
}
|
||||||
|
|
||||||
Configuration {
|
RowLayout {
|
||||||
colorScheme: root.colorScheme
|
id: configuration
|
||||||
title: qsTr("IMAP")
|
spacing: _spacing
|
||||||
hostname: Backend.hostname
|
Layout.fillWidth: true
|
||||||
port: Backend.imapPort.toString()
|
Layout.fillHeight: true
|
||||||
username: configuration.currentAddress
|
|
||||||
password: root.user ? root.user.password : ""
|
|
||||||
security : Backend.useSSLForIMAP ? "SSL" : "STARTTLS"
|
|
||||||
}
|
|
||||||
|
|
||||||
Configuration {
|
property string currentAddress: addressSelector.displayText
|
||||||
colorScheme: root.colorScheme
|
|
||||||
title: qsTr("SMTP")
|
Configuration {
|
||||||
hostname : Backend.hostname
|
Layout.fillWidth: true
|
||||||
port : Backend.smtpPort.toString()
|
colorScheme: root.colorScheme
|
||||||
username : configuration.currentAddress
|
title: qsTr("IMAP")
|
||||||
password : root.user ? root.user.password : ""
|
hostname: Backend.hostname
|
||||||
security : Backend.useSSLForSMTP ? "SSL" : "STARTTLS"
|
port: Backend.imapPort.toString()
|
||||||
|
username: configuration.currentAddress
|
||||||
|
password: root.user ? root.user.password : ""
|
||||||
|
security: Backend.useSSLForIMAP ? "SSL" : "STARTTLS"
|
||||||
|
}
|
||||||
|
|
||||||
|
Configuration {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
title: qsTr("SMTP")
|
||||||
|
hostname: Backend.hostname
|
||||||
|
port: Backend.smtpPort.toString()
|
||||||
|
username: configuration.currentAddress
|
||||||
|
password: root.user ? root.user.password : ""
|
||||||
|
security: Backend.useSSLForSMTP ? "SSL" : "STARTTLS"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,11 +26,8 @@ import Notifications
|
|||||||
QtObject {
|
QtObject {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
function isInInterval(num, lower_limit, upper_limit) {
|
function bound(num, lowerLimit, upperLimit) {
|
||||||
return lower_limit <= num && num <= upper_limit
|
return Math.max(lowerLimit, Math.min(upperLimit, num))
|
||||||
}
|
|
||||||
function bound(num, lower_limit, upper_limit) {
|
|
||||||
return Math.max(lower_limit, Math.min(upper_limit, num))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
property var title: Backend.appname
|
property var title: Backend.appname
|
||||||
@ -38,10 +35,30 @@ QtObject {
|
|||||||
property Notifications _notifications: Notifications {
|
property Notifications _notifications: Notifications {
|
||||||
id: notifications
|
id: notifications
|
||||||
frontendMain: mainWindow
|
frontendMain: mainWindow
|
||||||
frontendStatus: statusWindow
|
|
||||||
frontendTray: trayIcon
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property NotificationFilter _trayNotificationFilter: NotificationFilter {
|
||||||
|
id: trayNotificationFilter
|
||||||
|
source: root._notifications ? root._notifications.all : undefined
|
||||||
|
onTopmostChanged: {
|
||||||
|
if (topmost) {
|
||||||
|
switch (topmost.type) {
|
||||||
|
case Notification.NotificationType.Danger:
|
||||||
|
Backend.setErrorTrayIcon(topmost.brief, topmost.icon)
|
||||||
|
return
|
||||||
|
case Notification.NotificationType.Warning:
|
||||||
|
Backend.setWarnTrayIcon(topmost.brief, topmost.icon)
|
||||||
|
return
|
||||||
|
case Notification.NotificationType.Info:
|
||||||
|
Backend.setUpdateTrayIcon(topmost.brief, topmost.icon)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Backend.setNormalTrayIcon()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
property MainWindow _mainWindow: MainWindow {
|
property MainWindow _mainWindow: MainWindow {
|
||||||
id: mainWindow
|
id: mainWindow
|
||||||
visible: false
|
visible: false
|
||||||
@ -66,190 +83,6 @@ QtObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property StatusWindow _statusWindow: StatusWindow {
|
|
||||||
id: statusWindow
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
title: root.title
|
|
||||||
notifications: root._notifications
|
|
||||||
|
|
||||||
onShowMainWindow: {
|
|
||||||
mainWindow.showAndRise()
|
|
||||||
}
|
|
||||||
|
|
||||||
onShowHelp: {
|
|
||||||
mainWindow.showHelp()
|
|
||||||
mainWindow.showAndRise()
|
|
||||||
}
|
|
||||||
|
|
||||||
onShowSettings: {
|
|
||||||
mainWindow.showSettings()
|
|
||||||
mainWindow.showAndRise()
|
|
||||||
}
|
|
||||||
|
|
||||||
onSelectUser: function(userID) {
|
|
||||||
mainWindow.selectUser(userID)
|
|
||||||
mainWindow.showAndRise()
|
|
||||||
}
|
|
||||||
|
|
||||||
onQuit: {
|
|
||||||
mainWindow.hide()
|
|
||||||
trayIcon.visible = false
|
|
||||||
Backend.quit()
|
|
||||||
}
|
|
||||||
|
|
||||||
property rect screenRect
|
|
||||||
property rect iconRect
|
|
||||||
|
|
||||||
// use binding from function with width and height as arguments so it will be recalculated every time width and height are changed
|
|
||||||
property point position: getPosition(width, height)
|
|
||||||
x: position.x
|
|
||||||
y: position.y
|
|
||||||
|
|
||||||
function getPosition(_width, _height) {
|
|
||||||
if (screenRect.width === 0 || screenRect.height === 0) {
|
|
||||||
return Qt.point(0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
var _x = 0
|
|
||||||
var _y = 0
|
|
||||||
|
|
||||||
// fit above
|
|
||||||
_y = iconRect.top - height
|
|
||||||
if (isInInterval(_y, screenRect.top, screenRect.bottom - height)) {
|
|
||||||
// position preferably in the horizontal center but bound to the screen rect
|
|
||||||
_x = bound(iconRect.left + (iconRect.width - width)/2, screenRect.left, screenRect.right - width)
|
|
||||||
return Qt.point(_x, _y)
|
|
||||||
}
|
|
||||||
|
|
||||||
// fit below
|
|
||||||
_y = iconRect.bottom
|
|
||||||
if (isInInterval(_y, screenRect.top, screenRect.bottom - height)) {
|
|
||||||
// position preferably in the horizontal center but bound to the screen rect
|
|
||||||
_x = bound(iconRect.left + (iconRect.width - width)/2, screenRect.left, screenRect.right - width)
|
|
||||||
return Qt.point(_x, _y)
|
|
||||||
}
|
|
||||||
|
|
||||||
// fit to the left
|
|
||||||
_x = iconRect.left - width
|
|
||||||
if (isInInterval(_x, screenRect.left, screenRect.right - width)) {
|
|
||||||
// position preferably in the vertical center but bound to the screen rect
|
|
||||||
_y = bound(iconRect.top + (iconRect.height - height)/2, screenRect.top, screenRect.bottom - height)
|
|
||||||
return Qt.point(_x, _y)
|
|
||||||
}
|
|
||||||
|
|
||||||
// fit to the right
|
|
||||||
_x = iconRect.right
|
|
||||||
if (isInInterval(_x, screenRect.left, screenRect.right - width)) {
|
|
||||||
// position preferably in the vertical center but bound to the screen rect
|
|
||||||
_y = bound(iconRect.top + (iconRect.height - height)/2, screenRect.top, screenRect.bottom - height)
|
|
||||||
return Qt.point(_x, _y)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback: position status window right above icon and let window manager decide.
|
|
||||||
console.warn("Can't position status window: screenRect =", screenRect, "iconRect =", iconRect)
|
|
||||||
_x = bound(iconRect.left + (iconRect.width - width)/2, screenRect.left, screenRect.right - width)
|
|
||||||
_y = bound(iconRect.top + (iconRect.height - height)/2, screenRect.top, screenRect.bottom - height)
|
|
||||||
return Qt.point(_x, _y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property SystemTrayIcon _trayIcon: SystemTrayIcon {
|
|
||||||
id: trayIcon
|
|
||||||
visible: true
|
|
||||||
icon.source: getTrayIconPath()
|
|
||||||
icon.mask: true // make sure that systems like macOS will use proper color
|
|
||||||
tooltip: `${root.title} v${Backend.version}`
|
|
||||||
onActivated: function(reason) {
|
|
||||||
function calcStatusWindowPosition() {
|
|
||||||
// On some platforms (X11 / Plasma) Qt does not provide icon position and geometry info.
|
|
||||||
// In this case we rely on cursor position
|
|
||||||
var iconRect = Qt.rect(geometry.x, geometry.y, geometry.width, geometry.height)
|
|
||||||
if (geometry.width == 0 && geometry.height == 0) {
|
|
||||||
var mousePos = Backend.getCursorPos()
|
|
||||||
iconRect.x = mousePos.x
|
|
||||||
iconRect.y = mousePos.y
|
|
||||||
iconRect.width = 0
|
|
||||||
iconRect.height = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find screen
|
|
||||||
var screen
|
|
||||||
for (var i in Qt.application.screens) {
|
|
||||||
var _screen = Qt.application.screens[i]
|
|
||||||
if (
|
|
||||||
isInInterval(iconRect.x, _screen.virtualX, _screen.virtualX + _screen.width) &&
|
|
||||||
isInInterval(iconRect.y, _screen.virtualY, _screen.virtualY + _screen.height)
|
|
||||||
) {
|
|
||||||
screen = _screen
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!screen) {
|
|
||||||
// Fallback to primary screen
|
|
||||||
screen = Qt.application.screens[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case we used mouse to detect icon position - we want to make a fake icon rectangle from a point
|
|
||||||
if (iconRect.width == 0 && iconRect.height == 0) {
|
|
||||||
iconRect.x = bound(iconRect.x - 16, screen.virtualX, screen.virtualX + screen.width - 32)
|
|
||||||
iconRect.y = bound(iconRect.y - 16, screen.virtualY, screen.virtualY + screen.height - 32)
|
|
||||||
iconRect.width = 32
|
|
||||||
iconRect.height = 32
|
|
||||||
}
|
|
||||||
|
|
||||||
statusWindow.screenRect = Qt.rect(screen.virtualX, screen.virtualY, screen.width, screen.height)
|
|
||||||
statusWindow.iconRect = iconRect
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleWindow(win) {
|
|
||||||
if (win.visible) {
|
|
||||||
win.close()
|
|
||||||
} else {
|
|
||||||
win.showAndRise()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
switch (reason) {
|
|
||||||
case SystemTrayIcon.Unknown:
|
|
||||||
break;
|
|
||||||
case SystemTrayIcon.Context:
|
|
||||||
case SystemTrayIcon.Trigger:
|
|
||||||
case SystemTrayIcon.DoubleClick:
|
|
||||||
case SystemTrayIcon.MiddleClick:
|
|
||||||
calcStatusWindowPosition()
|
|
||||||
toggleWindow(statusWindow)
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property NotificationFilter _systrayfilter: NotificationFilter {
|
|
||||||
source: root._notifications ? root._notifications.all : undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTrayIconPath() {
|
|
||||||
var color = Backend.goos == "darwin" ? "mono" : "color"
|
|
||||||
|
|
||||||
var level = "norm"
|
|
||||||
if (_systrayfilter.topmost) {
|
|
||||||
switch (_systrayfilter.topmost.type) {
|
|
||||||
case Notification.NotificationType.Danger:
|
|
||||||
level = "error"
|
|
||||||
break;
|
|
||||||
case Notification.NotificationType.Warning:
|
|
||||||
level = "warn"
|
|
||||||
break;
|
|
||||||
case Notification.NotificationType.Info:
|
|
||||||
level = "update"
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return `qrc:/qml/icons/systray-${color}-${level}.png`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (!Backend) {
|
if (!Backend) {
|
||||||
@ -266,7 +99,7 @@ QtObject {
|
|||||||
var c = Backend.users.count
|
var c = Backend.users.count
|
||||||
var u = Backend.users.get(0)
|
var u = Backend.users.get(0)
|
||||||
// DEBUG
|
// DEBUG
|
||||||
if (c != 0) {
|
if (c !== 0) {
|
||||||
console.log("users non zero", c)
|
console.log("users non zero", c)
|
||||||
console.log("first user", u )
|
console.log("first user", u )
|
||||||
}
|
}
|
||||||
@ -290,7 +123,7 @@ QtObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setColorScheme() {
|
function setColorScheme() {
|
||||||
if (Backend.colorSchemeName == "light") ProtonStyle.currentStyle = ProtonStyle.lightStyle
|
if (Backend.colorSchemeName === "light") ProtonStyle.currentStyle = ProtonStyle.lightStyle
|
||||||
if (Backend.colorSchemeName == "dark") ProtonStyle.currentStyle = ProtonStyle.darkStyle
|
if (Backend.colorSchemeName === "dark") ProtonStyle.currentStyle = ProtonStyle.darkStyle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,315 +0,0 @@
|
|||||||
// Copyright (c) 2023 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/>.
|
|
||||||
|
|
||||||
import QtQml
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import QtQuick.Controls
|
|
||||||
|
|
||||||
import Proton
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property var user
|
|
||||||
property var userIndex
|
|
||||||
|
|
||||||
spacing : 5
|
|
||||||
|
|
||||||
Layout.fillHeight: true
|
|
||||||
//Layout.fillWidth: true
|
|
||||||
|
|
||||||
property ColorScheme colorScheme
|
|
||||||
|
|
||||||
TextField {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: user !== undefined ? user.username : ""
|
|
||||||
|
|
||||||
onEditingFinished: {
|
|
||||||
user.username = text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Switch {
|
|
||||||
id: userLoginSwitch
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
|
|
||||||
text: "LoggedIn"
|
|
||||||
enabled: user !== undefined && user.username.length > 0
|
|
||||||
|
|
||||||
checked: user ? root.user.state == EUserState.Connected : false
|
|
||||||
|
|
||||||
onCheckedChanged: {
|
|
||||||
if (!user) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checked) {
|
|
||||||
if (user === Backend.loginUser) {
|
|
||||||
var newUserObject = Backend.userComponent.createObject(Backend, {username: user.username, loggedIn: true, setupGuideSeen: user.setupGuideSeen})
|
|
||||||
Backend.users.append( { object: newUserObject } )
|
|
||||||
|
|
||||||
user.username = ""
|
|
||||||
user.resetLoginRequests()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
user.state = EUserState.Connected
|
|
||||||
user.resetLoginRequests()
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
user.state = EUserState.SignedOut
|
|
||||||
user.resetLoginRequests()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Switch {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
|
|
||||||
text: "Setup guide seen"
|
|
||||||
enabled: user !== undefined && user.username.length > 0
|
|
||||||
|
|
||||||
checked: user ? user.setupGuideSeen : false
|
|
||||||
|
|
||||||
onCheckedChanged: {
|
|
||||||
if (!user) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
user.setupGuideSeen = checked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Label {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: loginLabel
|
|
||||||
text: "Login:"
|
|
||||||
|
|
||||||
Layout.preferredWidth: Math.max(loginLabel.implicitWidth, faLabel.implicitWidth, passLabel.implicitWidth)
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "name/pass error"
|
|
||||||
enabled: user !== undefined //&& user.isLoginRequested && !user.isLogin2FARequested && !user.isLogin2PasswordProvided
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
Backend.loginUsernamePasswordError("")
|
|
||||||
user.resetLoginRequests()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "free user error"
|
|
||||||
enabled: user !== undefined //&& user.isLoginRequested
|
|
||||||
onClicked: {
|
|
||||||
Backend.loginFreeUserError()
|
|
||||||
user.resetLoginRequests()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "connection error"
|
|
||||||
enabled: user !== undefined //&& user.isLoginRequested
|
|
||||||
onClicked: {
|
|
||||||
Backend.loginConnectionError("")
|
|
||||||
user.resetLoginRequests()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Label {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: faLabel
|
|
||||||
text: "2FA:"
|
|
||||||
|
|
||||||
Layout.preferredWidth: Math.max(loginLabel.implicitWidth, faLabel.implicitWidth, passLabel.implicitWidth)
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "request"
|
|
||||||
|
|
||||||
enabled: user !== undefined //&& user.isLoginRequested && !user.isLogin2FARequested && !user.isLogin2PasswordRequested
|
|
||||||
onClicked: {
|
|
||||||
Backend.login2FARequested(user.username)
|
|
||||||
user.isLogin2FARequested = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "error"
|
|
||||||
|
|
||||||
enabled: user !== undefined //&& user.isLogin2FAProvided && !(user.isLogin2PasswordRequested && !user.isLogin2PasswordProvided)
|
|
||||||
onClicked: {
|
|
||||||
Backend.login2FAError("")
|
|
||||||
user.isLogin2FAProvided = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "Abort"
|
|
||||||
|
|
||||||
enabled: user !== undefined //&& user.isLogin2FAProvided && !(user.isLogin2PasswordRequested && !user.isLogin2PasswordProvided)
|
|
||||||
onClicked: {
|
|
||||||
Backend.login2FAErrorAbort("")
|
|
||||||
user.resetLoginRequests()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Label {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: passLabel
|
|
||||||
text: "2 Password:"
|
|
||||||
|
|
||||||
Layout.preferredWidth: Math.max(loginLabel.implicitWidth, faLabel.implicitWidth, passLabel.implicitWidth)
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "request"
|
|
||||||
|
|
||||||
enabled: user !== undefined //&& user.isLoginRequested && !user.isLogin2PasswordRequested && !(user.isLogin2FARequested && !user.isLogin2FAProvided)
|
|
||||||
onClicked: {
|
|
||||||
Backend.login2PasswordRequested("")
|
|
||||||
user.isLogin2PasswordRequested = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "error"
|
|
||||||
|
|
||||||
enabled: user !== undefined //&& user.isLogin2PasswordProvided && !(user.isLogin2FARequested && !user.isLogin2FAProvided)
|
|
||||||
onClicked: {
|
|
||||||
Backend.login2PasswordError("")
|
|
||||||
|
|
||||||
user.isLogin2PasswordProvided = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "Abort"
|
|
||||||
|
|
||||||
enabled: user !== undefined //&& user.isLogin2PasswordProvided && !(user.isLogin2FARequested && !user.isLogin2FAProvided)
|
|
||||||
onClicked: {
|
|
||||||
Backend.login2PasswordErrorAbort("")
|
|
||||||
user.resetLoginRequests()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "Login Finished"
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
Backend.loginFinished(0+loginFinishedIndex.text)
|
|
||||||
user.resetLoginRequests()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: loginFinishedIndex
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
label: "Index:"
|
|
||||||
text: root.userIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "Already logged in"
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
Backend.loginAlreadyLoggedIn(0+loginAlreadyLoggedInIndex.text)
|
|
||||||
user.resetLoginRequests()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: loginAlreadyLoggedInIndex
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
label: "Index:"
|
|
||||||
text: root.userIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
TextField {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
label: "used:"
|
|
||||||
text: user && user.usedBytes ? user.usedBytes : 0
|
|
||||||
onEditingFinished: {
|
|
||||||
user.usedBytes = parseFloat(text)
|
|
||||||
}
|
|
||||||
implicitWidth: 200
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
label: "total:"
|
|
||||||
text: user && user.totalBytes ? user.totalBytes : 0
|
|
||||||
onEditingFinished: {
|
|
||||||
user.totalBytes = parseFloat(text)
|
|
||||||
}
|
|
||||||
implicitWidth: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Label {colorScheme: root.colorScheme; text: "Split mode"}
|
|
||||||
Toggle { colorScheme: root.colorScheme; checked: user ? user.splitMode : false; onClicked: {user.splitMode = !user.splitMode}}
|
|
||||||
Button { colorScheme: root.colorScheme; text: "Toggle Finished"; onClicked: {user.toggleSplitModeFinished()}}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextArea { // TODO: this is causing binding loop on implicitWidth
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: user && user.addresses ? user.addresses.join("\n") : "user@protonmail.com"
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
onEditingFinished: {
|
|
||||||
user.addresses = text.split("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,101 +0,0 @@
|
|||||||
// Copyright (c) 2023 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/>.
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import QtQuick.Controls
|
|
||||||
|
|
||||||
import Proton
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property ColorScheme colorScheme
|
|
||||||
|
|
||||||
property alias currentIndex: usersListView.currentIndex
|
|
||||||
ListView {
|
|
||||||
id: usersListView
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.preferredWidth: 200
|
|
||||||
|
|
||||||
model: Backend.usersTest
|
|
||||||
highlightFollowsCurrentItem: true
|
|
||||||
|
|
||||||
delegate: Item {
|
|
||||||
|
|
||||||
implicitHeight: children[0].implicitHeight + anchors.topMargin + anchors.bottomMargin
|
|
||||||
implicitWidth: children[0].implicitWidth + anchors.leftMargin + anchors.rightMargin
|
|
||||||
|
|
||||||
width: usersListView.width
|
|
||||||
|
|
||||||
anchors.margins: 10
|
|
||||||
|
|
||||||
Label {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: modelData.username
|
|
||||||
anchors.margins: 10
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
usersListView.currentIndex = index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
highlight: Rectangle {
|
|
||||||
color: root.colorScheme.interaction_default_active
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
|
|
||||||
text: "+"
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
var newUserObject = Backend.userComponent.createObject(Backend)
|
|
||||||
newUserObject.username = Backend.loginUser.username.length > 0 ? Backend.loginUser.username : "test@protonmail.com"
|
|
||||||
newUserObject.state = EUserState.Connected
|
|
||||||
newUserObject.setupGuideSeen = true // Backend.loginUser.setupGuideSeen
|
|
||||||
|
|
||||||
Backend.loginUser.username = ""
|
|
||||||
Backend.loginUser.state = EUserState.SignedOut
|
|
||||||
Backend.loginUser.setupGuideSeen = false
|
|
||||||
|
|
||||||
Backend.users.append( { object: newUserObject } )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "-"
|
|
||||||
|
|
||||||
enabled: usersListView.currentIndex != 0
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
// var userObject = Backend.users.get(usersListView.currentIndex - 1)
|
|
||||||
Backend.users.remove(usersListView.currentIndex - 1)
|
|
||||||
// userObject.deleteLater()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,982 +0,0 @@
|
|||||||
// Copyright (c) 2023 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/>.
|
|
||||||
|
|
||||||
import QtQml
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Window
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import QtQuick.Controls
|
|
||||||
|
|
||||||
import QtQml.Models
|
|
||||||
|
|
||||||
import Qt.labs.platform
|
|
||||||
|
|
||||||
import Proton
|
|
||||||
|
|
||||||
import "./BridgeTest"
|
|
||||||
import BridgePreview
|
|
||||||
|
|
||||||
import Notifications
|
|
||||||
|
|
||||||
Window {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
x: 10
|
|
||||||
y: 10
|
|
||||||
width: 800
|
|
||||||
height: 800
|
|
||||||
|
|
||||||
property ColorScheme colorScheme: ProtonStyle.darkStyle
|
|
||||||
|
|
||||||
flags : Qt.Window | Qt.Dialog
|
|
||||||
visible : true
|
|
||||||
title : "Bridge Test GUI"
|
|
||||||
|
|
||||||
// This is needed because on MacOS if first window shown is not transparent -
|
|
||||||
// all other windows of application will not have transparent background (black
|
|
||||||
// instead of transparency). In our case that mean that if BridgeTest will be
|
|
||||||
// shown before StatusWindow - StatusWindow will not have transparent corners.
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
function getCursorPos() {
|
|
||||||
return BridgePreview.getCursorPos()
|
|
||||||
}
|
|
||||||
|
|
||||||
function restart() {
|
|
||||||
root.quit()
|
|
||||||
console.log("Restarting....")
|
|
||||||
root.openBridge()
|
|
||||||
}
|
|
||||||
|
|
||||||
function openBridge() {
|
|
||||||
bridge = bridgeComponent.createObject()
|
|
||||||
var showSetupGuide = false
|
|
||||||
if (showSetupGuide) {
|
|
||||||
var newUserObject = root.userComponent.createObject(root)
|
|
||||||
newUserObject.username = "LerooooyJenkins@protonmail.com"
|
|
||||||
newUserObject.state = EUserState.Connected
|
|
||||||
newUserObject.setupGuideSeen = false
|
|
||||||
root.users.append( { object: newUserObject } )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function quit() {
|
|
||||||
if (bridge !== undefined && bridge !== null) {
|
|
||||||
bridge.destroy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function guiReady() {
|
|
||||||
console.log("Gui Ready")
|
|
||||||
}
|
|
||||||
|
|
||||||
function _log(msg, color) {
|
|
||||||
logTextArea.text += "<p style='color: " + color + ";'>" + msg + "</p>"
|
|
||||||
logTextArea.text += "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
function log(msg) {
|
|
||||||
console.log(msg)
|
|
||||||
_log(msg, root.colorScheme.signal_info)
|
|
||||||
}
|
|
||||||
|
|
||||||
function error(msg) {
|
|
||||||
console.error(msg)
|
|
||||||
_log(msg, root.colorScheme.signal_danger)
|
|
||||||
}
|
|
||||||
|
|
||||||
// No user object should be put in this list until a successful login
|
|
||||||
property var users: UserModel {
|
|
||||||
id: _users
|
|
||||||
|
|
||||||
onRowsInserted: {
|
|
||||||
for (var i = first; i <= last; i++) {
|
|
||||||
_usersTest.insert(i + 1, { object: get(i) } )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onRowsRemoved: {
|
|
||||||
_usersTest.remove(first + 1, first - last + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
onRowsMoved: {
|
|
||||||
_usersTest.move(start + 1, row + 1, end - start + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
onDataChanged: {
|
|
||||||
for (var i = topLeft.row; i <= bottomRight.row; i++) {
|
|
||||||
_usersTest.set(i + 1, { object: get(i) } )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this list is used on test gui: it contains same users list as users above + fake user to represent login request of new user on pos 0
|
|
||||||
property var usersTest: UserModel {
|
|
||||||
id: _usersTest
|
|
||||||
}
|
|
||||||
|
|
||||||
property var userComponent: Component {
|
|
||||||
id: _userComponent
|
|
||||||
|
|
||||||
QtObject {
|
|
||||||
property string username: ""
|
|
||||||
property bool loggedIn: false
|
|
||||||
property bool splitMode: false
|
|
||||||
|
|
||||||
property bool setupGuideSeen: true
|
|
||||||
|
|
||||||
property var usedBytes: 5350*1024*1024
|
|
||||||
property var totalBytes: 20*1024*1024*1024
|
|
||||||
property string avatarText: "jd"
|
|
||||||
|
|
||||||
property string password: "SMj975NnEYYsqu55GGmlpv"
|
|
||||||
property var addresses: [
|
|
||||||
"jaanedoe@protonmail.com",
|
|
||||||
"jane@pm.me",
|
|
||||||
"jdoe@pm.me"
|
|
||||||
]
|
|
||||||
|
|
||||||
signal loginUsernamePasswordError()
|
|
||||||
signal loginFreeUserError()
|
|
||||||
signal loginConnectionError()
|
|
||||||
signal login2FARequested()
|
|
||||||
signal login2FAError()
|
|
||||||
signal login2FAErrorAbort()
|
|
||||||
signal login2PasswordRequested()
|
|
||||||
signal login2PasswordError()
|
|
||||||
signal login2PasswordErrorAbort()
|
|
||||||
|
|
||||||
// Test purpose only:
|
|
||||||
property bool isFakeUser: this === root.loginUser
|
|
||||||
|
|
||||||
function userSignal(msg) {
|
|
||||||
if (isFakeUser) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
root.log("<- User (" + username + "): " + msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleSplitMode(makeActive) {
|
|
||||||
userSignal("toggle split mode "+makeActive)
|
|
||||||
}
|
|
||||||
signal toggleSplitModeFinished()
|
|
||||||
|
|
||||||
function configureAppleMail(address){
|
|
||||||
userSignal("configure apple mail "+address)
|
|
||||||
}
|
|
||||||
|
|
||||||
function logout(){
|
|
||||||
userSignal("logout")
|
|
||||||
loggedIn = false
|
|
||||||
}
|
|
||||||
function remove(){
|
|
||||||
console.log("remove this", users.count)
|
|
||||||
for (var i=0; i<users.count; i++) {
|
|
||||||
if (users.get(i) === this) {
|
|
||||||
users.remove(i,1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
onLoginUsernamePasswordError: {
|
|
||||||
userSignal("loginUsernamePasswordError")
|
|
||||||
}
|
|
||||||
onLoginFreeUserError: {
|
|
||||||
userSignal("loginFreeUserError")
|
|
||||||
}
|
|
||||||
onLoginConnectionError: {
|
|
||||||
userSignal("loginConnectionError")
|
|
||||||
}
|
|
||||||
onLogin2FARequested: {
|
|
||||||
userSignal("login2FARequested")
|
|
||||||
}
|
|
||||||
onLogin2FAError: {
|
|
||||||
userSignal("login2FAError")
|
|
||||||
}
|
|
||||||
onLogin2FAErrorAbort: {
|
|
||||||
userSignal("login2FAErrorAbort")
|
|
||||||
}
|
|
||||||
onLogin2PasswordRequested: {
|
|
||||||
userSignal("login2PasswordRequested")
|
|
||||||
}
|
|
||||||
onLogin2PasswordError: {
|
|
||||||
userSignal("login2PasswordError")
|
|
||||||
}
|
|
||||||
onLogin2PasswordErrorAbort: {
|
|
||||||
userSignal("login2PasswordErrorAbort")
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetLoginRequests() {
|
|
||||||
isLoginRequested = false
|
|
||||||
isLogin2FARequested = false
|
|
||||||
isLogin2FAProvided = false
|
|
||||||
isLogin2PasswordRequested = false
|
|
||||||
isLogin2PasswordProvided = false
|
|
||||||
}
|
|
||||||
|
|
||||||
property bool isLoginRequested: false
|
|
||||||
|
|
||||||
property bool isLogin2FARequested: false
|
|
||||||
property bool isLogin2FAProvided: false
|
|
||||||
|
|
||||||
property bool isLogin2PasswordRequested: false
|
|
||||||
property bool isLogin2PasswordProvided: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this it fake user used only for representing first login request
|
|
||||||
property var loginUser
|
|
||||||
Component.onCompleted: {
|
|
||||||
var newLoginUser = _userComponent.createObject()
|
|
||||||
root.loginUser = newLoginUser
|
|
||||||
root.loginUser.setupGuideSeen = false
|
|
||||||
_usersTest.append({object: newLoginUser})
|
|
||||||
|
|
||||||
newLoginUser.loginUsernamePasswordError.connect(root.loginUsernamePasswordError)
|
|
||||||
newLoginUser.loginFreeUserError.connect(root.loginFreeUserError)
|
|
||||||
newLoginUser.loginConnectionError.connect(root.loginConnectionError)
|
|
||||||
newLoginUser.login2FARequested.connect(root.login2FARequested)
|
|
||||||
newLoginUser.login2FAError.connect(root.login2FAError)
|
|
||||||
newLoginUser.login2FAErrorAbort.connect(root.login2FAErrorAbort)
|
|
||||||
newLoginUser.login2PasswordRequested.connect(root.login2PasswordRequested)
|
|
||||||
newLoginUser.login2PasswordError.connect(root.login2PasswordError)
|
|
||||||
newLoginUser.login2PasswordErrorAbort.connect(root.login2PasswordErrorAbort)
|
|
||||||
|
|
||||||
|
|
||||||
// add one user on start
|
|
||||||
var hasUserOnStart = false
|
|
||||||
if (hasUserOnStart) {
|
|
||||||
var newUserObject = root.userComponent.createObject(root)
|
|
||||||
newUserObject.username = "LerooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooyJenkins@protonmail.com"
|
|
||||||
newUserObject.loggedIn = EUserState.Connected
|
|
||||||
newUserObject.setupGuideSeen = true
|
|
||||||
root.users.append( { object: newUserObject } )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TabBar {
|
|
||||||
id: tabBar
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
TabButton {
|
|
||||||
text: "Global settings"
|
|
||||||
}
|
|
||||||
|
|
||||||
TabButton {
|
|
||||||
text: "User control"
|
|
||||||
}
|
|
||||||
|
|
||||||
TabButton {
|
|
||||||
text: "Notifications"
|
|
||||||
}
|
|
||||||
|
|
||||||
TabButton {
|
|
||||||
text: "Log"
|
|
||||||
}
|
|
||||||
|
|
||||||
TabButton {
|
|
||||||
text: "Settings signals"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
color: root.colorScheme.background_norm
|
|
||||||
|
|
||||||
anchors.top: tabBar.bottom
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
|
|
||||||
implicitHeight: children[0].contentHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
|
||||||
implicitWidth: children[0].contentWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
|
||||||
|
|
||||||
StackLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
currentIndex: tabBar.currentIndex
|
|
||||||
anchors.margins: 10
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: globalTab
|
|
||||||
spacing : 5
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
spacing : 5
|
|
||||||
|
|
||||||
Label {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "Global settings"
|
|
||||||
}
|
|
||||||
|
|
||||||
ButtonGroup {
|
|
||||||
id: styleRadioGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
RadioButton {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: "Light UI"
|
|
||||||
checked: ProtonStyle.currentStyle === ProtonStyle.lightStyle
|
|
||||||
ButtonGroup.group: styleRadioGroup
|
|
||||||
|
|
||||||
onCheckedChanged: {
|
|
||||||
if (checked && ProtonStyle.currentStyle !== ProtonStyle.lightStyle) {
|
|
||||||
root.colorSchemeName = "light"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RadioButton {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: "Dark UI"
|
|
||||||
checked: ProtonStyle.currentStyle === ProtonStyle.darkStyle
|
|
||||||
ButtonGroup.group: styleRadioGroup
|
|
||||||
|
|
||||||
onCheckedChanged: {
|
|
||||||
if (checked && ProtonStyle.currentStyle !== ProtonStyle.darkStyle) {
|
|
||||||
root.colorSchemeName = "dark"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckBox {
|
|
||||||
id: showOnStartupCheckbox
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "Show on startup"
|
|
||||||
checked: root.showOnStartup
|
|
||||||
onCheckedChanged: {
|
|
||||||
root.showOnStartup = checked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckBox {
|
|
||||||
id: showSplashScreen
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "Show splash screen"
|
|
||||||
checked: root.showSplashScreen
|
|
||||||
onCheckedChanged: {
|
|
||||||
root.showSplashScreen = checked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
//Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: "Open Bridge"
|
|
||||||
enabled: bridge === undefined || bridge === null
|
|
||||||
onClicked: root.openBridge()
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
//Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: "Close Bridge"
|
|
||||||
enabled: bridge !== undefined && bridge !== null
|
|
||||||
onClicked: {
|
|
||||||
bridge.destroy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
spacing : 5
|
|
||||||
|
|
||||||
Label {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "Notifications"
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "Notify: danger"
|
|
||||||
enabled: bridge !== undefined && bridge !== null
|
|
||||||
onClicked: {
|
|
||||||
bridge.mainWindow.notifyOnlyPaidUsers()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "Notify: warning"
|
|
||||||
enabled: bridge !== undefined && bridge !== null
|
|
||||||
onClicked: {
|
|
||||||
bridge.mainWindow.notifyUpdateManually()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: "Notify: success"
|
|
||||||
enabled: bridge !== undefined && bridge !== null
|
|
||||||
onClicked: {
|
|
||||||
bridge.mainWindow.notifyUserAdded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: usersTab
|
|
||||||
UserList {
|
|
||||||
id: usersListView
|
|
||||||
Layout.fillHeight: true
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
backend: root
|
|
||||||
}
|
|
||||||
|
|
||||||
UserControl {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
backend: root
|
|
||||||
user: ((root.usersTest.count > usersListView.currentIndex) && usersListView.currentIndex != -1) ? root.usersTest.get(usersListView.currentIndex) : undefined
|
|
||||||
userIndex: usersListView.currentIndex - 1 // -1 because 0 index is fake user
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: notificationsTab
|
|
||||||
spacing: 5
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
spacing: 5
|
|
||||||
|
|
||||||
Switch {
|
|
||||||
text: "Internet connection"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
checked: true
|
|
||||||
onCheckedChanged: {
|
|
||||||
checked ? root.internetOn() : root.internetOff()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Update manual ready"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
onClicked: {
|
|
||||||
root.updateManualReady("3.14.1592")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Update manual done"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
onClicked: {
|
|
||||||
root.updateManualRestartNeeded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Update manual error"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
onClicked: {
|
|
||||||
root.updateManualError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Update force"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
onClicked: {
|
|
||||||
root.updateForce("3.14.1592")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Update force error"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
onClicked: {
|
|
||||||
root.updateForceError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Update silent done"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
onClicked: {
|
|
||||||
root.updateSilentRestartNeeded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Update silent error"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
onClicked: {
|
|
||||||
root.updateSilentError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Update is latest version"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
onClicked: {
|
|
||||||
root.updateIsLatestVersion()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Bug report send OK"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
onClicked: {
|
|
||||||
root.reportBugFinished()
|
|
||||||
root.bugReportSendSuccess()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
spacing: 5
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Bug report send error"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
onClicked: {
|
|
||||||
root.reportBugFinished()
|
|
||||||
root.bugReportSendError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Cache anavailable"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
onClicked: {
|
|
||||||
root.cacheUnavailable()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Cache can't move"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
onClicked: {
|
|
||||||
root.cacheCantMove()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Cache location change success"
|
|
||||||
onClicked: {
|
|
||||||
root.cacheLocationChangeSuccess()
|
|
||||||
}
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Disk full"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
onClicked: {
|
|
||||||
root.diskFull()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "No keychain"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
onClicked: {
|
|
||||||
root.notifyHasNoKeychain()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Rebuild keychain"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
onClicked: {
|
|
||||||
root.notifyRebuildKeychain()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Address changed"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
onClicked: {
|
|
||||||
root.addressChanged("p@v.el")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Address changed + Logout"
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
onClicked: {
|
|
||||||
root.addressChangedLogout("p@v.el")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextArea {
|
|
||||||
id: logTextArea
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Layout.preferredWidth: 400
|
|
||||||
Layout.preferredHeight: 200
|
|
||||||
|
|
||||||
textFormat: TextEdit.RichText
|
|
||||||
//readOnly: true
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollView {
|
|
||||||
id: settingsTab
|
|
||||||
ColumnLayout {
|
|
||||||
RowLayout {
|
|
||||||
Label {colorScheme : root.colorScheme ; text : "GOOS : "}
|
|
||||||
Button {colorScheme : root.colorScheme ; text : "Linux" ; onClicked : root.goos = "linux" ; enabled: root.goos != "linux"}
|
|
||||||
Button {colorScheme : root.colorScheme ; text : "Windows" ; onClicked : root.goos = "windows" ; enabled: root.goos != "windows"}
|
|
||||||
Button {colorScheme : root.colorScheme ; text : "macOS" ; onClicked : root.goos = "darwin" ; enabled: root.goos != "darwin"}
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Label {colorScheme: root.colorScheme; text: "Automatic updates:"}
|
|
||||||
Toggle {colorScheme: root.colorScheme; checked: root.isAutomaticUpdateOn; onClicked: root.isAutomaticUpdateOn = !root.isAutomaticUpdateOn}
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Label {colorScheme: root.colorScheme; text: "Autostart:"}
|
|
||||||
Toggle {colorScheme: root.colorScheme; checked: root.isAutostartOn; onClicked: root.isAutostartOn = !root.isAutostartOn}
|
|
||||||
Button {colorScheme: root.colorScheme; text: "Toggle finished"; onClicked: root.toggleAutostartFinished()}
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Label {colorScheme: root.colorScheme; text: "Beta:"}
|
|
||||||
Toggle {colorScheme: root.colorScheme; checked: root.isBetaEnabled; onClicked: root.isBetaEnabled = !root.isBetaEnabled}
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Label {colorScheme: root.colorScheme; text: "DoH:"}
|
|
||||||
Toggle {colorScheme: root.colorScheme; checked: root.isDoHEnabled; onClicked: root.isDoHEnabled = !root.isDoHEnabled}
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Label {colorScheme: root.colorScheme; text: "All Mail disabled:"}
|
|
||||||
Toggle {colorScheme: root.colorScheme; checked: root.isAllMailVisible; onClicked: root.isAllMailVisible = !root.isAllMailVisible}
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Label {colorScheme: root.colorScheme; text: "Ports:"}
|
|
||||||
TextField {
|
|
||||||
colorScheme:root.colorScheme
|
|
||||||
label: "IMAP"
|
|
||||||
text: root.portIMAP
|
|
||||||
onEditingFinished: root.portIMAP = this.text*1
|
|
||||||
validator: IntValidator {bottom: 1; top: 65536}
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
colorScheme:root.colorScheme
|
|
||||||
label: "SMTP"
|
|
||||||
text: root.portSMTP
|
|
||||||
onEditingFinished: root.portSMTP = this.text*1
|
|
||||||
validator: IntValidator {bottom: 1; top: 65536}
|
|
||||||
}
|
|
||||||
Button {colorScheme: root.colorScheme; text: "Change finished"; onClicked: root.changePortFinished()}
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Label {colorScheme: root.colorScheme; text: "SMTP using SSL:"}
|
|
||||||
Toggle {colorScheme: root.colorScheme; checked: root.useSSLForSMTP; onClicked: root.useSSLForSMTP = !root.useSSLForSMTP}
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Label {colorScheme: root.colorScheme; text: "Local cache:"}
|
|
||||||
Toggle {colorScheme: root.colorScheme; checked: root.isDiskCacheEnabled; onClicked: root.isDiskCacheEnabled = !root.isDiskCacheEnabled}
|
|
||||||
TextField {
|
|
||||||
colorScheme:root.colorScheme
|
|
||||||
label: "Path"
|
|
||||||
text: root.diskCachePath.toString().replace("file://", "")
|
|
||||||
implicitWidth: 160
|
|
||||||
onEditingFinished: {
|
|
||||||
root.diskCachePath = Qt.resolvedUrl("file://"+text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {colorScheme: root.colorScheme; text: "Change finished"; onClicked: root.changeLocalCacheFinished()}
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Label {colorScheme: root.colorScheme; text: "Reset:"}
|
|
||||||
Button {colorScheme: root.colorScheme; text: "Finished"; onClicked: root.resetFinished()}
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Label {colorScheme: root.colorScheme; text: "Check update:"}
|
|
||||||
Button {colorScheme: root.colorScheme; text: "Finished"; onClicked: root.checkUpdatesFinished()}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property Bridge bridge
|
|
||||||
|
|
||||||
property string goos: "darwin"
|
|
||||||
|
|
||||||
property bool showOnStartup: true // this actually needs to be false, but since we use Bridge_test for testing purpose - lets default this to true just for convenience
|
|
||||||
property bool dockIconVisible: false
|
|
||||||
|
|
||||||
// this signals are used only when trying to login with new user (i.e. not in users model)
|
|
||||||
signal loginUsernamePasswordError(string errorMsg)
|
|
||||||
signal loginFreeUserError()
|
|
||||||
signal loginConnectionError(string errorMsg)
|
|
||||||
signal login2FARequested(string username)
|
|
||||||
signal login2FAError(string errorMsg)
|
|
||||||
signal login2FAErrorAbort(string errorMsg)
|
|
||||||
signal login2PasswordRequested()
|
|
||||||
signal login2PasswordError(string errorMsg)
|
|
||||||
signal login2PasswordErrorAbort(string errorMsg)
|
|
||||||
signal loginFinished(int index)
|
|
||||||
signal loginAlreadyLoggedIn(int index)
|
|
||||||
|
|
||||||
signal internetOff()
|
|
||||||
signal internetOn()
|
|
||||||
|
|
||||||
signal updateManualReady(var version)
|
|
||||||
signal updateManualRestartNeeded()
|
|
||||||
signal updateManualError()
|
|
||||||
signal updateForce(var version)
|
|
||||||
signal updateForceError()
|
|
||||||
signal updateSilentRestartNeeded()
|
|
||||||
signal updateSilentError()
|
|
||||||
signal updateIsLatestVersion()
|
|
||||||
function checkUpdates(){
|
|
||||||
console.log("check updates")
|
|
||||||
}
|
|
||||||
signal checkUpdatesFinished()
|
|
||||||
function installUpdate() {
|
|
||||||
console.log("manuall install update triggered")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
property bool isDiskCacheEnabled: true
|
|
||||||
// Qt.resolvedUrl("file:///C:/Users/user/AppData/Roaming/protonmail/bridge-v3/cache/c11/messages")
|
|
||||||
property url diskCachePath: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
|
|
||||||
signal cacheUnavailable()
|
|
||||||
signal cacheCantMove()
|
|
||||||
signal cacheLocationChangeSuccess()
|
|
||||||
signal diskFull()
|
|
||||||
function changeLocalCache(enableDiskCache, diskCachePath) {
|
|
||||||
console.debug("-> disk cache", enableDiskCache, diskCachePath)
|
|
||||||
}
|
|
||||||
signal changeLocalCacheFinished()
|
|
||||||
|
|
||||||
|
|
||||||
// Settings
|
|
||||||
property bool isAutomaticUpdateOn : true
|
|
||||||
function toggleAutomaticUpdate(makeItActive) {
|
|
||||||
console.debug("-> silent updates", makeItActive, root.isAutomaticUpdateOn)
|
|
||||||
var callback = function () {
|
|
||||||
root.isAutomaticUpdateOn = makeItActive;
|
|
||||||
console.debug("-> CHANGED silent updates", makeItActive, root.isAutomaticUpdateOn)
|
|
||||||
}
|
|
||||||
atimer.onTriggered.connect(callback)
|
|
||||||
atimer.restart()
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: atimer
|
|
||||||
interval: 2000
|
|
||||||
running: false
|
|
||||||
repeat: false
|
|
||||||
}
|
|
||||||
|
|
||||||
property bool isAutostartOn : true // Example of settings with loading state
|
|
||||||
function toggleAutostart(makeItActive) {
|
|
||||||
console.debug("-> autostart", makeItActive, root.isAutostartOn)
|
|
||||||
}
|
|
||||||
signal toggleAutostartFinished()
|
|
||||||
|
|
||||||
property bool isBetaEnabled : false
|
|
||||||
function toggleBeta(makeItActive){
|
|
||||||
console.debug("-> beta", makeItActive, root.isBetaEnabled)
|
|
||||||
root.isBetaEnabled = makeItActive
|
|
||||||
}
|
|
||||||
|
|
||||||
property bool isDoHEnabled : true
|
|
||||||
function toggleDoH(makeItActive){
|
|
||||||
console.debug("-> DoH", makeItActive, root.isDoHEnabled)
|
|
||||||
root.isDoHEnabled = makeItActive
|
|
||||||
}
|
|
||||||
|
|
||||||
property bool isAllMailVisible : true
|
|
||||||
function changeIsAllMailVisible(isVisible){
|
|
||||||
console.debug("-> All Mail Visible", isVisible, root.isAllMailVisible)
|
|
||||||
root.isAllMailVisible = isVisible
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
property bool useSSLForSMTP: false
|
|
||||||
function toggleUseSSLForSMTP(makeItActive){
|
|
||||||
console.debug("-> SMTP SSL", makeItActive, root.useSSLForSMTP)
|
|
||||||
}
|
|
||||||
signal toggleUseSSLFinished()
|
|
||||||
|
|
||||||
property string hostname: "127.0.0.1"
|
|
||||||
property int portIMAP: 1143
|
|
||||||
property int portSMTP: 1025
|
|
||||||
function changePorts(imapPort, smtpPort){
|
|
||||||
console.debug("-> ports", imapPort, smtpPort)
|
|
||||||
}
|
|
||||||
function isPortFree(port){
|
|
||||||
if (port == portIMAP) return false
|
|
||||||
if (port == portSMTP) return false
|
|
||||||
if (port == 12345) return false
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
signal changePortFinished()
|
|
||||||
signal imapPortStartupError()
|
|
||||||
signal smtpPortStartupError()
|
|
||||||
|
|
||||||
function triggerReset() {
|
|
||||||
console.debug("-> trigger reset")
|
|
||||||
}
|
|
||||||
signal resetFinished()
|
|
||||||
|
|
||||||
property string version: "2.0.X-BridePreview"
|
|
||||||
property url logsPath: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
|
|
||||||
property url licensePath: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
|
|
||||||
property url releaseNotesLink: Qt.resolvedUrl("https://proton.me/download/bridge/early_releases.html")
|
|
||||||
property url dependencyLicensesLink: Qt.resolvedUrl("https://github.com/ProtonMail/proton-bridge/v3/blob/master/COPYING_NOTES.md#dependencies")
|
|
||||||
property url landingPageLink: Qt.resolvedUrl("https://proton.me/mail/bridge#download")
|
|
||||||
|
|
||||||
property string colorSchemeName: "light"
|
|
||||||
function changeColorScheme(newScheme){
|
|
||||||
root.colorSchemeName = newScheme
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
property string currentEmailClient: "" // "Apple Mail 14.0"
|
|
||||||
function updateCurrentMailClient(){
|
|
||||||
currentEmailClient = "Apple Mail 14.0"
|
|
||||||
}
|
|
||||||
|
|
||||||
function reportBug(description,address,emailClient,includeLogs){
|
|
||||||
console.log("report bug")
|
|
||||||
console.log(" description",description)
|
|
||||||
console.log(" address",address)
|
|
||||||
console.log(" emailClient",emailClient)
|
|
||||||
console.log(" includeLogs",includeLogs)
|
|
||||||
}
|
|
||||||
signal reportBugFinished()
|
|
||||||
signal bugReportSendSuccess()
|
|
||||||
signal bugReportSendError()
|
|
||||||
|
|
||||||
property var availableKeychain: ["gnome-keyring", "pass", "macos-keychain", "windows-credentials"]
|
|
||||||
property string currentKeychain: availableKeychain[0]
|
|
||||||
function changeKeychain(wantedKeychain){
|
|
||||||
console.log("Changing keychain from", root.currentKeychain, "to", wantedKeychain)
|
|
||||||
root.currentKeychain = wantedKeychain
|
|
||||||
root.changeKeychainFinished()
|
|
||||||
}
|
|
||||||
signal changeKeychainFinished()
|
|
||||||
signal notifyHasNoKeychain()
|
|
||||||
signal notifyRebuildKeychain()
|
|
||||||
|
|
||||||
signal noActiveKeyForRecipient(string email)
|
|
||||||
signal showMainWindow()
|
|
||||||
|
|
||||||
signal addressChanged(string address)
|
|
||||||
signal addressChangedLogout(string address)
|
|
||||||
signal userDisconnected(string username)
|
|
||||||
signal apiCertIssue()
|
|
||||||
|
|
||||||
property bool showSplashScreen: false
|
|
||||||
|
|
||||||
|
|
||||||
function login(username, password) {
|
|
||||||
root.log("-> login(" + username + ", " + password + ")")
|
|
||||||
|
|
||||||
loginUser.username = username
|
|
||||||
loginUser.isLoginRequested = true
|
|
||||||
}
|
|
||||||
|
|
||||||
function login2FA(username, code) {
|
|
||||||
root.log("-> login2FA(" + username + ", " + code + ")")
|
|
||||||
|
|
||||||
loginUser.isLogin2FAProvided = true
|
|
||||||
}
|
|
||||||
|
|
||||||
function login2Password(username, password) {
|
|
||||||
root.log("-> login2FA(" + username + ", " + password + ")")
|
|
||||||
|
|
||||||
loginUser.isLogin2PasswordProvided = true
|
|
||||||
}
|
|
||||||
|
|
||||||
function loginAbort(username) {
|
|
||||||
root.log("-> loginAbort(" + username + ")")
|
|
||||||
|
|
||||||
loginUser.resetLoginRequests()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
onLoginUsernamePasswordError: {
|
|
||||||
console.debug("<- loginUsernamePasswordError")
|
|
||||||
}
|
|
||||||
onLoginFreeUserError: {
|
|
||||||
console.debug("<- loginFreeUserError")
|
|
||||||
}
|
|
||||||
onLoginConnectionError: {
|
|
||||||
console.debug("<- loginConnectionError")
|
|
||||||
}
|
|
||||||
onLogin2FARequested: {
|
|
||||||
console.debug("<- login2FARequested", username)
|
|
||||||
}
|
|
||||||
onLogin2FAError: {
|
|
||||||
console.debug("<- login2FAError")
|
|
||||||
}
|
|
||||||
onLogin2FAErrorAbort: {
|
|
||||||
console.debug("<- login2FAErrorAbort")
|
|
||||||
}
|
|
||||||
onLogin2PasswordRequested: {
|
|
||||||
console.debug("<- login2PasswordRequested")
|
|
||||||
}
|
|
||||||
onLogin2PasswordError: {
|
|
||||||
console.debug("<- login2PasswordError")
|
|
||||||
}
|
|
||||||
onLogin2PasswordErrorAbort: {
|
|
||||||
console.debug("<- login2PasswordErrorAbort")
|
|
||||||
}
|
|
||||||
onLoginFinished: {
|
|
||||||
console.debug("<- loginFinished", index)
|
|
||||||
}
|
|
||||||
onLoginAlreadyLoggedIn: {
|
|
||||||
console.debug("<- loginAlreadyLoggedIn", index)
|
|
||||||
}
|
|
||||||
|
|
||||||
onInternetOff: {
|
|
||||||
console.debug("<- internetOff")
|
|
||||||
}
|
|
||||||
onInternetOn: {
|
|
||||||
console.debug("<- internetOn")
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: bridgeComponent
|
|
||||||
|
|
||||||
Bridge {
|
|
||||||
backend: root
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onClosing: {
|
|
||||||
Qt.quit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -61,8 +61,6 @@ Rectangle {
|
|||||||
type: Label.Body_semibold
|
type: Label.Body_semibold
|
||||||
}
|
}
|
||||||
|
|
||||||
Item{}
|
|
||||||
|
|
||||||
ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Hostname") ; value: root.hostname }
|
ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Hostname") ; value: root.hostname }
|
||||||
ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Port") ; value: root.port }
|
ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Port") ; value: root.port }
|
||||||
ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Username") ; value: root.username }
|
ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Username") ; value: root.username }
|
||||||
|
|||||||
@ -169,6 +169,19 @@ SettingsView {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsItem {
|
||||||
|
id: telemetry
|
||||||
|
Layout.fillWidth: true
|
||||||
|
checked: !Backend.isTelemetryDisabled
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
description: qsTr("Help us improve Proton services by sending anonymous usage statistics.")
|
||||||
|
text: qsTr("Collect usage diagnostics")
|
||||||
|
type: SettingsItem.Toggle
|
||||||
|
visible: root._isAdvancedShown
|
||||||
|
|
||||||
|
onClicked: Backend.toggleIsTelemetryDisabled(telemetry.checked)
|
||||||
|
}
|
||||||
|
|
||||||
SettingsItem {
|
SettingsItem {
|
||||||
id: ports
|
id: ports
|
||||||
visible: root._isAdvancedShown
|
visible: root._isAdvancedShown
|
||||||
|
|||||||
@ -41,7 +41,7 @@ SettingsView {
|
|||||||
actionIcon: "/qml/icons/ic-external-link.svg"
|
actionIcon: "/qml/icons/ic-external-link.svg"
|
||||||
description: qsTr("Get help setting up your client with our instructions and FAQs.")
|
description: qsTr("Get help setting up your client with our instructions and FAQs.")
|
||||||
type: SettingsItem.PrimaryButton
|
type: SettingsItem.PrimaryButton
|
||||||
onClicked: {Qt.openUrlExternally("https://proton.me/support/mail")}
|
onClicked: {Qt.openUrlExternally("https://proton.me/support/bridge")}
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,29 +24,20 @@ import QtQuick.Controls
|
|||||||
import Proton
|
import Proton
|
||||||
import Notifications
|
import Notifications
|
||||||
|
|
||||||
import "tests"
|
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: root
|
id: root
|
||||||
|
colorScheme: ProtonStyle.currentStyle
|
||||||
width: 960
|
|
||||||
height: 576
|
|
||||||
|
|
||||||
visible: true
|
visible: true
|
||||||
|
|
||||||
minimumHeight: contentLayout.implicitHeight
|
|
||||||
minimumWidth: contentLayout.implicitWidth
|
|
||||||
|
|
||||||
colorScheme: ProtonStyle.currentStyle
|
property int _defaultWidth: 1080
|
||||||
|
property int _defaultHeight: 780
|
||||||
|
width: _defaultWidth
|
||||||
|
height: _defaultHeight
|
||||||
|
minimumWidth: _defaultWidth
|
||||||
|
|
||||||
property var notifications
|
property var notifications
|
||||||
|
|
||||||
// This is needed because on MacOS if first window shown is not transparent -
|
|
||||||
// all other windows of application will not have transparent background (black
|
|
||||||
// instead of transparency). In our case that mean that if MainWindow will be
|
|
||||||
// shown before StatusWindow - StatusWindow will not have transparent corners.
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
// show Setup Guide on every new user
|
// show Setup Guide on every new user
|
||||||
Connections {
|
Connections {
|
||||||
target: Backend.users
|
target: Backend.users
|
||||||
@ -86,10 +77,6 @@ ApplicationWindow {
|
|||||||
root.showAndRise()
|
root.showAndRise()
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSelectUser(userID) {
|
|
||||||
root.selectUser(userID)
|
|
||||||
}
|
|
||||||
|
|
||||||
function onLoginFinished(index, wasSignedOut) {
|
function onLoginFinished(index, wasSignedOut) {
|
||||||
var user = Backend.users.get(index)
|
var user = Backend.users.get(index)
|
||||||
if (user && !wasSignedOut) {
|
if (user && !wasSignedOut) {
|
||||||
@ -97,6 +84,21 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
console.debug("Login finished", index)
|
console.debug("Login finished", index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onShowHelp() {
|
||||||
|
root.showHelp()
|
||||||
|
root.showAndRise()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onShowSettings() {
|
||||||
|
root.showSettings()
|
||||||
|
root.showAndRise()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSelectUser(userID) {
|
||||||
|
contentWrapper.selectUser(userID)
|
||||||
|
root.showAndRise()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StackLayout {
|
StackLayout {
|
||||||
|
|||||||
@ -24,8 +24,6 @@ QtObject {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property MainWindow frontendMain
|
property MainWindow frontendMain
|
||||||
property StatusWindow frontendStatus
|
|
||||||
property SystemTrayIcon frontendTray
|
|
||||||
|
|
||||||
signal askEnableBeta()
|
signal askEnableBeta()
|
||||||
signal askEnableSplitMode(var user)
|
signal askEnableSplitMode(var user)
|
||||||
@ -140,7 +138,7 @@ QtObject {
|
|||||||
|
|
||||||
property Notification imapPortChangeError: Notification {
|
property Notification imapPortChangeError: Notification {
|
||||||
description: qsTr("The IMAP port could not be changed.")
|
description: qsTr("The IMAP port could not be changed.")
|
||||||
brief: qsTr("IMAP port change error")
|
brief: qsTr("IMAP port error")
|
||||||
icon: "./icons/ic-alert.svg"
|
icon: "./icons/ic-alert.svg"
|
||||||
type: Notification.NotificationType.Danger
|
type: Notification.NotificationType.Danger
|
||||||
group: Notifications.Group.Connection
|
group: Notifications.Group.Connection
|
||||||
@ -156,7 +154,7 @@ QtObject {
|
|||||||
|
|
||||||
property Notification smtpPortChangeError: Notification {
|
property Notification smtpPortChangeError: Notification {
|
||||||
description: qsTr("The SMTP port could not be changed.")
|
description: qsTr("The SMTP port could not be changed.")
|
||||||
brief: qsTr("SMTP port change error")
|
brief: qsTr("SMTP port error")
|
||||||
icon: "./icons/ic-alert.svg"
|
icon: "./icons/ic-alert.svg"
|
||||||
type: Notification.NotificationType.Danger
|
type: Notification.NotificationType.Danger
|
||||||
group: Notifications.Group.Connection
|
group: Notifications.Group.Connection
|
||||||
@ -172,7 +170,7 @@ QtObject {
|
|||||||
|
|
||||||
property Notification imapConnectionModeChangeError: Notification {
|
property Notification imapConnectionModeChangeError: Notification {
|
||||||
description: qsTr("The IMAP connection mode could not be changed.")
|
description: qsTr("The IMAP connection mode could not be changed.")
|
||||||
brief: qsTr("IMAP Connection mode change error")
|
brief: qsTr("IMAP Connection mode error")
|
||||||
icon: "./icons/ic-alert.svg"
|
icon: "./icons/ic-alert.svg"
|
||||||
type: Notification.NotificationType.Danger
|
type: Notification.NotificationType.Danger
|
||||||
group: Notifications.Group.Connection
|
group: Notifications.Group.Connection
|
||||||
@ -196,7 +194,7 @@ QtObject {
|
|||||||
|
|
||||||
property Notification smtpConnectionModeChangeError: Notification {
|
property Notification smtpConnectionModeChangeError: Notification {
|
||||||
description: qsTr("The SMTP connection mode could not be changed.")
|
description: qsTr("The SMTP connection mode could not be changed.")
|
||||||
brief: qsTr("SMTP Connection mode change error")
|
brief: qsTr("SMTP Connection mode error")
|
||||||
icon: "./icons/ic-alert.svg"
|
icon: "./icons/ic-alert.svg"
|
||||||
type: Notification.NotificationType.Danger
|
type: Notification.NotificationType.Danger
|
||||||
group: Notifications.Group.Connection
|
group: Notifications.Group.Connection
|
||||||
@ -227,7 +225,7 @@ QtObject {
|
|||||||
var link = Backend.releaseNotesLink
|
var link = Backend.releaseNotesLink
|
||||||
return `${descr} <a href="${link}">${text}</a>`
|
return `${descr} <a href="${link}">${text}</a>`
|
||||||
}
|
}
|
||||||
brief: qsTr("Update available.")
|
brief: qsTr("Update available")
|
||||||
icon: "./icons/ic-info-circle-filled.svg"
|
icon: "./icons/ic-info-circle-filled.svg"
|
||||||
type: Notification.NotificationType.Info
|
type: Notification.NotificationType.Info
|
||||||
group: Notifications.Group.Update | Notifications.Group.Dialogs
|
group: Notifications.Group.Update | Notifications.Group.Dialogs
|
||||||
@ -514,7 +512,7 @@ QtObject {
|
|||||||
// login
|
// login
|
||||||
property Notification loginConnectionError: Notification {
|
property Notification loginConnectionError: Notification {
|
||||||
description: qsTr("Bridge is not able to contact the server, please check your internet connection.")
|
description: qsTr("Bridge is not able to contact the server, please check your internet connection.")
|
||||||
brief: description
|
brief: qsTr("Connection error")
|
||||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||||
type: Notification.NotificationType.Danger
|
type: Notification.NotificationType.Danger
|
||||||
group: Notifications.Group.Configuration
|
group: Notifications.Group.Configuration
|
||||||
@ -538,7 +536,7 @@ QtObject {
|
|||||||
|
|
||||||
property Notification onlyPaidUsers: Notification {
|
property Notification onlyPaidUsers: Notification {
|
||||||
description: qsTr("Bridge is exclusive to our paid plans. Upgrade your account to use Bridge.")
|
description: qsTr("Bridge is exclusive to our paid plans. Upgrade your account to use Bridge.")
|
||||||
brief: description
|
brief: qsTr("Upgrade your account")
|
||||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||||
type: Notification.NotificationType.Danger
|
type: Notification.NotificationType.Danger
|
||||||
group: Notifications.Group.Configuration
|
group: Notifications.Group.Configuration
|
||||||
@ -562,7 +560,7 @@ QtObject {
|
|||||||
|
|
||||||
property Notification alreadyLoggedIn: Notification {
|
property Notification alreadyLoggedIn: Notification {
|
||||||
description: qsTr("This account is already signed in.")
|
description: qsTr("This account is already signed in.")
|
||||||
brief: description
|
brief: qsTr("Already signed in")
|
||||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||||
type: Notification.NotificationType.Info
|
type: Notification.NotificationType.Info
|
||||||
group: Notifications.Group.Configuration
|
group: Notifications.Group.Configuration
|
||||||
@ -587,7 +585,7 @@ QtObject {
|
|||||||
// Bug reports
|
// Bug reports
|
||||||
property Notification bugReportSendSuccess: Notification {
|
property Notification bugReportSendSuccess: Notification {
|
||||||
description: qsTr("Thank you for the report. We'll get back to you as soon as we can.")
|
description: qsTr("Thank you for the report. We'll get back to you as soon as we can.")
|
||||||
brief: description
|
brief: qsTr("Report sent")
|
||||||
icon: "./icons/ic-info-circle-filled.svg"
|
icon: "./icons/ic-info-circle-filled.svg"
|
||||||
type: Notification.NotificationType.Success
|
type: Notification.NotificationType.Success
|
||||||
group: Notifications.Group.Configuration
|
group: Notifications.Group.Configuration
|
||||||
@ -611,7 +609,7 @@ QtObject {
|
|||||||
|
|
||||||
property Notification bugReportSendError: Notification {
|
property Notification bugReportSendError: Notification {
|
||||||
description: qsTr("Report could not be sent. Try again or email us directly.")
|
description: qsTr("Report could not be sent. Try again or email us directly.")
|
||||||
brief: description
|
brief: qsTr("Error sending report")
|
||||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||||
type: Notification.NotificationType.Danger
|
type: Notification.NotificationType.Danger
|
||||||
group: Notifications.Group.Configuration
|
group: Notifications.Group.Configuration
|
||||||
@ -634,8 +632,8 @@ QtObject {
|
|||||||
// Cache
|
// Cache
|
||||||
property Notification cacheUnavailable: Notification {
|
property Notification cacheUnavailable: Notification {
|
||||||
title: qsTr("Cache location is unavailable")
|
title: qsTr("Cache location is unavailable")
|
||||||
description: qsTr("Check the directory or change it in your settings.")
|
description: qsTr("The current cache location is unavailable. Check the directory or change it in your settings.")
|
||||||
brief: qsTr("The current cache location is unavailable. Check the directory or change it in your settings.")
|
brief: title
|
||||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||||
type: Notification.NotificationType.Warning
|
type: Notification.NotificationType.Warning
|
||||||
group: Notifications.Group.Configuration | Notifications.Group.Dialogs
|
group: Notifications.Group.Configuration | Notifications.Group.Dialogs
|
||||||
@ -725,7 +723,7 @@ QtObject {
|
|||||||
// Other
|
// Other
|
||||||
property Notification accountChanged: Notification {
|
property Notification accountChanged: Notification {
|
||||||
description: qsTr("The address list for .... account has changed. You need to reconfigure your email client.")
|
description: qsTr("The address list for .... account has changed. You need to reconfigure your email client.")
|
||||||
brief: qsTr("The address list for your account has changed. Reconfigure your email client.")
|
brief: qsTr("Address list changed")
|
||||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||||
type: Notification.NotificationType.Danger
|
type: Notification.NotificationType.Danger
|
||||||
group: Notifications.Group.Configuration
|
group: Notifications.Group.Configuration
|
||||||
@ -742,7 +740,7 @@ QtObject {
|
|||||||
property Notification diskFull: Notification {
|
property Notification diskFull: Notification {
|
||||||
title: qsTr("Your disk is almost full")
|
title: qsTr("Your disk is almost full")
|
||||||
description: qsTr("Quit Bridge and free disk space or disable the local cache (not recommended).")
|
description: qsTr("Quit Bridge and free disk space or disable the local cache (not recommended).")
|
||||||
brief: qsTr("Your disk is almost full. Free disk space or disable the local cache.")
|
brief: title
|
||||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||||
type: Notification.NotificationType.Warning
|
type: Notification.NotificationType.Warning
|
||||||
group: Notifications.Group.Configuration | Notifications.Group.Dialogs
|
group: Notifications.Group.Configuration | Notifications.Group.Dialogs
|
||||||
@ -948,8 +946,8 @@ QtObject {
|
|||||||
|
|
||||||
property Notification noKeychain: Notification {
|
property Notification noKeychain: Notification {
|
||||||
title: qsTr("No keychain available")
|
title: qsTr("No keychain available")
|
||||||
description: qsTr("Bridge is not able to detect a supported password manager (pass or secret-service). Please install and setup supported password manager and restart the application.")
|
|
||||||
brief: title
|
brief: title
|
||||||
|
description: qsTr("Bridge is not able to detect a supported password manager (pass or secret-service). Please install and setup supported password manager and restart the application.")
|
||||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||||
type: Notification.NotificationType.Danger
|
type: Notification.NotificationType.Danger
|
||||||
group: Notifications.Group.Dialogs | Notifications.Group.Configuration
|
group: Notifications.Group.Dialogs | Notifications.Group.Configuration
|
||||||
@ -982,13 +980,13 @@ QtObject {
|
|||||||
|
|
||||||
property Notification rebuildKeychain: Notification {
|
property Notification rebuildKeychain: Notification {
|
||||||
title: qsTr("Your macOS keychain might be corrupted")
|
title: qsTr("Your macOS keychain might be corrupted")
|
||||||
description: qsTr("Bridge is not able to access your macOS keychain. Please consult the instructions on our support page.")
|
|
||||||
brief: title
|
brief: title
|
||||||
|
description: qsTr("Bridge is not able to access your macOS keychain. Please consult the instructions on our support page.")
|
||||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||||
type: Notification.NotificationType.Danger
|
type: Notification.NotificationType.Danger
|
||||||
group: Notifications.Group.Dialogs | Notifications.Group.Configuration
|
group: Notifications.Group.Dialogs | Notifications.Group.Configuration
|
||||||
|
|
||||||
property var supportLink: "https://proton.me/support/mail"
|
property var supportLink: "https://proton.me/support/bridge"
|
||||||
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@ -1014,8 +1012,8 @@ QtObject {
|
|||||||
|
|
||||||
property Notification addressChanged: Notification {
|
property Notification addressChanged: Notification {
|
||||||
title: qsTr("Address list changes")
|
title: qsTr("Address list changes")
|
||||||
|
brief: title
|
||||||
description: qsTr("The address list for your account has changed. You might need to reconfigure your email client.")
|
description: qsTr("The address list for your account has changed. You might need to reconfigure your email client.")
|
||||||
brief: description
|
|
||||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||||
type: Notification.NotificationType.Warning
|
type: Notification.NotificationType.Warning
|
||||||
group: Notifications.Group.Configuration
|
group: Notifications.Group.Configuration
|
||||||
@ -1047,11 +1045,11 @@ QtObject {
|
|||||||
|
|
||||||
property Notification apiCertIssue: Notification {
|
property Notification apiCertIssue: Notification {
|
||||||
title: qsTr("Unable to establish a \nsecure connection to \nProton servers")
|
title: qsTr("Unable to establish a \nsecure connection to \nProton servers")
|
||||||
|
brief: qsTr("Cannot establish secure connection")
|
||||||
description: qsTr("Bridge cannot verify the authenticity of Proton servers on your current network due to a TLS certificate error. " +
|
description: qsTr("Bridge cannot verify the authenticity of Proton servers on your current network due to a TLS certificate error. " +
|
||||||
"Start Bridge again after ensuring your connection is secure and/or connecting to a VPN. Learn more about TLS pinning " +
|
"Start Bridge again after ensuring your connection is secure and/or connecting to a VPN. Learn more about TLS pinning " +
|
||||||
"<a href=\"https://proton.me/blog/tls-ssl-certificate#Extra-security-precautions-taken-by-ProtonMail\">here</a>.")
|
"<a href=\"https://proton.me/blog/tls-ssl-certificate#Extra-security-precautions-taken-by-ProtonMail\">here</a>.")
|
||||||
|
|
||||||
brief: title
|
|
||||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||||
type: Notification.NotificationType.Danger
|
type: Notification.NotificationType.Danger
|
||||||
group: Notifications.Group.Dialogs | Notifications.Group.Connection
|
group: Notifications.Group.Dialogs | Notifications.Group.Connection
|
||||||
@ -1078,6 +1076,7 @@ QtObject {
|
|||||||
|
|
||||||
property Notification noActiveKeyForRecipient: Notification {
|
property Notification noActiveKeyForRecipient: Notification {
|
||||||
title: qsTr("Unable to send \nencrypted message")
|
title: qsTr("Unable to send \nencrypted message")
|
||||||
|
brief: title
|
||||||
description: "#PlaceholderText#"
|
description: "#PlaceholderText#"
|
||||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||||
type: Notification.NotificationType.Danger
|
type: Notification.NotificationType.Danger
|
||||||
@ -1174,8 +1173,9 @@ QtObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property Notification genericError: Notification {
|
property Notification genericError: Notification {
|
||||||
title: "#PlaceholderText#"
|
title: ""
|
||||||
description: "#PlaceholderText#"
|
brief: title
|
||||||
|
description: ""
|
||||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||||
type: Notification.NotificationType.Danger
|
type: Notification.NotificationType.Danger
|
||||||
group: Notifications.Group.Dialogs
|
group: Notifications.Group.Dialogs
|
||||||
@ -1201,7 +1201,7 @@ QtObject {
|
|||||||
|
|
||||||
property Notification genericQuestion: Notification {
|
property Notification genericQuestion: Notification {
|
||||||
title: ""
|
title: ""
|
||||||
brief: ""
|
brief: title
|
||||||
description: ""
|
description: ""
|
||||||
type: Notification.NotificationType.Warning
|
type: Notification.NotificationType.Warning
|
||||||
group: Notifications.Group.Dialogs
|
group: Notifications.Group.Dialogs
|
||||||
|
|||||||
@ -1,352 +0,0 @@
|
|||||||
// Copyright (c) 2023 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/>.
|
|
||||||
|
|
||||||
import QtQml
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Window
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import QtQuick.Controls
|
|
||||||
|
|
||||||
import Proton
|
|
||||||
import Notifications
|
|
||||||
|
|
||||||
Window {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
height: contentLayout.implicitHeight
|
|
||||||
width: contentLayout.implicitWidth
|
|
||||||
|
|
||||||
flags: (Qt.platform.os === "linux" ? Qt.Tool : 0) | Qt.FramelessWindowHint | Qt.NoDropShadowWindowHint | Qt.WindowStaysOnTopHint | Qt.WA_TranslucentBackground
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
property ColorScheme colorScheme: ProtonStyle.currentStyle
|
|
||||||
|
|
||||||
property var notifications
|
|
||||||
|
|
||||||
signal showMainWindow()
|
|
||||||
signal showHelp()
|
|
||||||
signal showSettings()
|
|
||||||
signal selectUser(string userID)
|
|
||||||
signal quit()
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: mouseArea
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
}
|
|
||||||
|
|
||||||
function enableHoverOnOpenBridgeButton() {
|
|
||||||
openBridgeButton.hoverEnabled = true
|
|
||||||
mouseArea.positionChanged.disconnect(enableHoverOnOpenBridgeButton)
|
|
||||||
}
|
|
||||||
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (visible) { // GODT-1479 To avoid a visual glitch where the 'Open bridge button' would appear hovered when the status windows opens,
|
|
||||||
// we've disabled hover on it when it was last closed. Re-enabling hover here will not work on all platforms. so we temporarily connect
|
|
||||||
// mouse move event over the window's mouseArea to a function that will re-enable hover on the open bridge button.
|
|
||||||
openBridgeButton.focus = false
|
|
||||||
mouseArea.positionChanged.connect(enableHoverOnOpenBridgeButton)
|
|
||||||
} else {
|
|
||||||
menu.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: contentLayout
|
|
||||||
|
|
||||||
Layout.minimumHeight: 201
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.minimumWidth: 448
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
Item {
|
|
||||||
implicitHeight: 12
|
|
||||||
Layout.fillWidth: true
|
|
||||||
clip: true
|
|
||||||
Rectangle {
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
height: parent.height * 2
|
|
||||||
radius: ProtonStyle.dialog_radius
|
|
||||||
|
|
||||||
color: {
|
|
||||||
if (!statusItem.activeNotification) {
|
|
||||||
return root.colorScheme.signal_success
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (statusItem.activeNotification.type) {
|
|
||||||
case Notification.NotificationType.Danger:
|
|
||||||
return root.colorScheme.signal_danger
|
|
||||||
case Notification.NotificationType.Warning:
|
|
||||||
return root.colorScheme.signal_warning
|
|
||||||
case Notification.NotificationType.Success:
|
|
||||||
return root.colorScheme.signal_success
|
|
||||||
case Notification.NotificationType.Info:
|
|
||||||
return root.colorScheme.signal_info
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
|
||||||
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
|
||||||
|
|
||||||
color: colorScheme.background_norm
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
anchors.topMargin: 8
|
|
||||||
anchors.bottomMargin: 8
|
|
||||||
anchors.leftMargin: 24
|
|
||||||
anchors.rightMargin: 24
|
|
||||||
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
Status {
|
|
||||||
id: statusItem
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Layout.topMargin: 12
|
|
||||||
Layout.bottomMargin: 12
|
|
||||||
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
notifications: root.notifications
|
|
||||||
|
|
||||||
notificationWhitelist: Notifications.Group.Connection | Notifications.Group.Update | Notifications.Group.Configuration
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
secondary: true
|
|
||||||
|
|
||||||
Layout.topMargin: 12
|
|
||||||
Layout.bottomMargin: 12
|
|
||||||
|
|
||||||
visible: statusItem.activeNotification && statusItem.activeNotification.action.length > 0
|
|
||||||
action: statusItem.activeNotification && statusItem.activeNotification.action.length > 0 ? statusItem.activeNotification.action[0] : null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
height: 1
|
|
||||||
color: root.colorScheme.background_norm
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.leftMargin: 24
|
|
||||||
anchors.rightMargin: 24
|
|
||||||
color: root.colorScheme.border_norm
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
Layout.maximumHeight: accountListView.count ?
|
|
||||||
accountListView.contentHeight / accountListView.count * 3 + accountListView.anchors.topMargin + accountListView.anchors.bottomMargin :
|
|
||||||
Number.POSITIVE_INFINITY
|
|
||||||
|
|
||||||
color: root.colorScheme.background_norm
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
implicitHeight: children[0].contentHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
|
||||||
implicitWidth: children[0].contentWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
|
||||||
|
|
||||||
ListView {
|
|
||||||
id: accountListView
|
|
||||||
|
|
||||||
model: Backend.users
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
anchors.topMargin: 8
|
|
||||||
anchors.bottomMargin: 8
|
|
||||||
anchors.leftMargin: 24
|
|
||||||
anchors.rightMargin: 24
|
|
||||||
|
|
||||||
interactive: contentHeight > parent.height
|
|
||||||
snapMode: ListView.SnapToItem
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
|
||||||
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
delegate: Item {
|
|
||||||
id: viewItem
|
|
||||||
width: ListView.view.width
|
|
||||||
|
|
||||||
implicitHeight: children[0].implicitHeight
|
|
||||||
implicitWidth: children[0].implicitWidth
|
|
||||||
|
|
||||||
property var user: Backend.users.get(index)
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
spacing: 0
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
AccountDelegate {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Layout.topMargin: 12
|
|
||||||
Layout.bottomMargin: 12
|
|
||||||
|
|
||||||
user: viewItem.user
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
Layout.topMargin: 12
|
|
||||||
Layout.bottomMargin: 12
|
|
||||||
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
visible: viewItem.user ? (viewItem.user.state === EUserState.SignedOut) : false
|
|
||||||
text: qsTr("Sign in")
|
|
||||||
onClicked: {
|
|
||||||
root.selectUser(viewItem.user.id) // selectUser will show login screen if user is in SignedOut state.
|
|
||||||
root.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
implicitHeight: children[1].implicitHeight + children[1].anchors.topMargin + children[1].anchors.bottomMargin
|
|
||||||
implicitWidth: children[1].implicitWidth + children[1].anchors.leftMargin + children[1].anchors.rightMargin
|
|
||||||
|
|
||||||
// background:
|
|
||||||
clip: true
|
|
||||||
Rectangle {
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
height: parent.height * 2
|
|
||||||
radius: ProtonStyle.dialog_radius
|
|
||||||
|
|
||||||
color: root.colorScheme.background_weak
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: 8
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: openBridgeButton
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
secondary: true
|
|
||||||
text: qsTr("Open Bridge")
|
|
||||||
|
|
||||||
borderless: true
|
|
||||||
labelType: Label.LabelType.Caption_semibold
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
// GODT-1479: we disable hover for the button to avoid a visual glitch where the button is
|
|
||||||
// wrongly hovered when re-opening the status window after clicking
|
|
||||||
hoverEnabled = false;
|
|
||||||
root.showMainWindow()
|
|
||||||
root.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
secondary: true
|
|
||||||
icon.source: "/qml/icons/ic-three-dots-vertical.svg"
|
|
||||||
borderless: true
|
|
||||||
checkable: true
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
menu.open()
|
|
||||||
}
|
|
||||||
|
|
||||||
Menu {
|
|
||||||
id: menu
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
modal: true
|
|
||||||
|
|
||||||
y: 0 - height
|
|
||||||
|
|
||||||
MenuItem {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: qsTr("Help")
|
|
||||||
onClicked: {
|
|
||||||
root.showHelp()
|
|
||||||
root.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MenuItem {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: qsTr("Settings")
|
|
||||||
onClicked: {
|
|
||||||
root.showSettings()
|
|
||||||
root.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MenuItem {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: qsTr("Quit Bridge")
|
|
||||||
onClicked: {
|
|
||||||
root.close()
|
|
||||||
root.quit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onClosed: {
|
|
||||||
parent.checked = false
|
|
||||||
}
|
|
||||||
onOpened: {
|
|
||||||
parent.checked = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onActiveChanged: {
|
|
||||||
if (!active) root.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
function showAndRise() {
|
|
||||||
root.show()
|
|
||||||
root.raise()
|
|
||||||
if (!root.active) {
|
|
||||||
root.requestActivate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.533333,0,0,0.533333,238.933,238.933)">
|
||||||
|
<circle cx="512" cy="512" r="480" style="fill:white;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 593 B |
@ -160,3 +160,41 @@ target_link_libraries(bridgepp
|
|||||||
)
|
)
|
||||||
|
|
||||||
target_precompile_headers(bridgepp PRIVATE Pch.h)
|
target_precompile_headers(bridgepp PRIVATE Pch.h)
|
||||||
|
|
||||||
|
#*****************************************************************************************************************************************************
|
||||||
|
# GoogleTest
|
||||||
|
#*****************************************************************************************************************************************************
|
||||||
|
|
||||||
|
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
|
||||||
|
cmake_policy(SET CMP0135 NEW) # avoid warning DOWNLOAD_EXTRACT_TIMESTAMP
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
FetchContent_Declare(
|
||||||
|
googletest
|
||||||
|
URL https://github.com/google/googletest/archive/b796f7d44681514f58a683a3a71ff17c94edb0c1.zip
|
||||||
|
)
|
||||||
|
|
||||||
|
# For Windows: Prevent overriding the parent project's compiler/linker settings
|
||||||
|
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(googletest)
|
||||||
|
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
#*****************************************************************************************************************************************************
|
||||||
|
# Tests
|
||||||
|
#*****************************************************************************************************************************************************
|
||||||
|
add_executable(bridgepp-test
|
||||||
|
Test/TestBridgeUtils.cpp
|
||||||
|
Test/TestException.cpp
|
||||||
|
Test/TestWorker.cpp Test/TestWorker.h)
|
||||||
|
add_dependencies(bridgepp-test bridgepp)
|
||||||
|
target_precompile_headers(bridgepp-test PRIVATE Pch.h)
|
||||||
|
target_link_libraries(bridgepp-test
|
||||||
|
GTest::gtest_main
|
||||||
|
bridgepp
|
||||||
|
)
|
||||||
|
|
||||||
|
include(GoogleTest)
|
||||||
|
gtest_discover_tests(bridgepp-test)
|
||||||
|
|||||||
111
internal/frontend/bridge-gui/bridgepp/Test/TestBridgeUtils.cpp
Normal file
111
internal/frontend/bridge-gui/bridgepp/Test/TestBridgeUtils.cpp
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// Copyright (c) 2023 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 <gtest/gtest.h>
|
||||||
|
#include <bridgepp/BridgeUtils.h>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace bridgepp;
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
TEST(BridgeUtils, OS) {
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
EXPECT_EQ(os(), OS::MacOS);
|
||||||
|
EXPECT_FALSE(onLinux());
|
||||||
|
EXPECT_TRUE(onMacOS());
|
||||||
|
EXPECT_FALSE(onWindows());
|
||||||
|
EXPECT_EQ(goos(), "darwin");
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
EXPECT_EQ(os(), OS::Windows);
|
||||||
|
EXPECT_FALSE(onLinux());
|
||||||
|
EXPECT_FALSE(onMacOS());
|
||||||
|
EXPECT_TRUE(onWindows());
|
||||||
|
EXPECT_EQ(goos(), "windows");
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
EXPECT_EQ(os(), OS::Linux);
|
||||||
|
EXPECT_TRUE(onLinux());
|
||||||
|
EXPECT_FALSE(onMacOS());
|
||||||
|
EXPECT_FALSE(onWindows());
|
||||||
|
EXPECT_EQ(goos(), "linux");
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EXPECT_TRUE(false); // should be unreachable.
|
||||||
|
}
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
TEST(BridgeUtils, UserFolders) {
|
||||||
|
typedef QString (*dirFunction)();
|
||||||
|
QList<dirFunction> functions = { userConfigDir, userCacheDir, userDataDir, sentryCacheDir };
|
||||||
|
QString path;
|
||||||
|
for (dirFunction f: functions) {
|
||||||
|
EXPECT_NO_THROW(path = f());
|
||||||
|
EXPECT_FALSE(path.isEmpty());
|
||||||
|
EXPECT_TRUE(QDir(path).exists());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
TEST(BridgeUtils, Random) {
|
||||||
|
qint32 repeatCount = 1000;
|
||||||
|
qint32 const maxValue = 5;
|
||||||
|
for (qint32 i = 0; i < repeatCount; ++i) {
|
||||||
|
qint64 n = 0;
|
||||||
|
EXPECT_NO_THROW(n = randN(maxValue));
|
||||||
|
EXPECT_TRUE((n >= 0) && (n < maxValue));
|
||||||
|
QString name;
|
||||||
|
EXPECT_NO_THROW(name = randomFirstName());
|
||||||
|
EXPECT_FALSE(name.isEmpty());
|
||||||
|
EXPECT_NO_THROW(name = randomLastName());
|
||||||
|
EXPECT_FALSE(name.isEmpty());
|
||||||
|
EXPECT_NO_THROW(randomUser());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
TEST(BridgeUtils, ElideLongString) {
|
||||||
|
std::function const test = [](QString const &input, qint32 maxLength, QString const &expected) -> bool {
|
||||||
|
QString output;
|
||||||
|
EXPECT_NO_THROW(output = elideLongString(input, maxLength));
|
||||||
|
return output == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPECT_TRUE(test( "", 0, ""));
|
||||||
|
EXPECT_TRUE(test("1234", 4, "1234"));
|
||||||
|
EXPECT_TRUE(test("123", 2, "..."));
|
||||||
|
EXPECT_TRUE(test("1234567890", 8, "12...90"));
|
||||||
|
EXPECT_TRUE(test("1234567890", 10, "1234567890"));
|
||||||
|
EXPECT_TRUE(test("1234567890", 100, "1234567890"));
|
||||||
|
}
|
||||||
95
internal/frontend/bridge-gui/bridgepp/Test/TestException.cpp
Normal file
95
internal/frontend/bridge-gui/bridgepp/Test/TestException.cpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
// Copyright (c) 2023 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 <bridgepp/Exception/Exception.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace bridgepp;
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
QString const testQWhat = "What";
|
||||||
|
QString const testDetails = "Some details";
|
||||||
|
QString const testFunction = "function";
|
||||||
|
QByteArray const testAttachment = QString("Some data").toLocal8Bit();
|
||||||
|
Exception const testException(testQWhat, testDetails, testFunction, testAttachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
TEST(Exceptions, ExceptionConstructor) {
|
||||||
|
// Default exception
|
||||||
|
Exception const emptyException;
|
||||||
|
EXPECT_TRUE(emptyException.qwhat().isEmpty());
|
||||||
|
EXPECT_EQ(strlen(emptyException.what()), 0);
|
||||||
|
EXPECT_EQ(emptyException.attachment().size(), 0);
|
||||||
|
EXPECT_TRUE(emptyException.details().isEmpty());
|
||||||
|
EXPECT_TRUE(emptyException.detailedWhat().isEmpty());
|
||||||
|
|
||||||
|
// Fully detailed exception
|
||||||
|
EXPECT_EQ(testException.qwhat(), testQWhat);
|
||||||
|
EXPECT_EQ(QString::fromLocal8Bit(testException.what()), testQWhat);
|
||||||
|
EXPECT_EQ(testException.details(), testDetails);
|
||||||
|
EXPECT_EQ(testException.attachment(), testAttachment);
|
||||||
|
QString const detailed = testException.detailedWhat();
|
||||||
|
EXPECT_TRUE(detailed.contains(testQWhat));
|
||||||
|
EXPECT_TRUE(detailed.contains(testFunction));
|
||||||
|
EXPECT_TRUE(detailed.contains(testDetails));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
TEST(Exceptions, ExceptionCopyMoveConstructors) {
|
||||||
|
Exception const e(testQWhat, testDetails, testFunction, testAttachment);
|
||||||
|
|
||||||
|
// Check copy-constructor
|
||||||
|
Exception eCopied(e);
|
||||||
|
EXPECT_EQ(eCopied.qwhat(), testQWhat);
|
||||||
|
EXPECT_EQ(eCopied.details(), testDetails);
|
||||||
|
EXPECT_EQ(eCopied.function(), testFunction);
|
||||||
|
EXPECT_EQ(eCopied.attachment(), testAttachment);
|
||||||
|
|
||||||
|
// Check move-constructor
|
||||||
|
Exception eMoved(std::move(eCopied));
|
||||||
|
EXPECT_EQ(eMoved.qwhat(), testQWhat);
|
||||||
|
EXPECT_EQ(eMoved.details(), testDetails);
|
||||||
|
EXPECT_EQ(eMoved.function(), testFunction);
|
||||||
|
EXPECT_EQ(eMoved.attachment(), testAttachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
TEST(Exceptions, ExceptionThrow) {
|
||||||
|
std::function t = []() { throw testException; };
|
||||||
|
EXPECT_THROW(t(), Exception);
|
||||||
|
EXPECT_THROW(t(), std::exception);
|
||||||
|
bool caught = false;
|
||||||
|
try {
|
||||||
|
t();
|
||||||
|
} catch (Exception const &e) {
|
||||||
|
caught = true;
|
||||||
|
EXPECT_EQ(e.detailedWhat(), testException.detailedWhat());
|
||||||
|
}
|
||||||
|
EXPECT_TRUE(caught);
|
||||||
|
}
|
||||||
215
internal/frontend/bridge-gui/bridgepp/Test/TestWorker.cpp
Normal file
215
internal/frontend/bridge-gui/bridgepp/Test/TestWorker.cpp
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
// Copyright (c) 2023 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/>.
|
||||||
|
|
||||||
|
// clazy:excludeall=lambda-in-connect
|
||||||
|
|
||||||
|
#include "TestWorker.h"
|
||||||
|
#include <bridgepp/Worker/Overseer.h>
|
||||||
|
#include <bridgepp/Exception/Exception.h>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace bridgepp;
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
|
||||||
|
qint32 dummyArgc = 1; ///< A dummy int value because QCoreApplication constructor requires a reference to it.
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
Workers::Workers()
|
||||||
|
: testing::Test()
|
||||||
|
, app_(dummyArgc, nullptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void Workers::SetUp() {
|
||||||
|
Test::SetUp();
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(worker_ = new TestWorker);
|
||||||
|
|
||||||
|
QObject::connect(worker_, &TestWorker::started, [&]() { results_.started = true; });
|
||||||
|
QObject::connect(worker_, &TestWorker::finished, [&]() { results_.finished = true; });
|
||||||
|
QObject::connect(worker_, &TestWorker::finished, &loop_, &QEventLoop::quit);
|
||||||
|
QObject::connect(worker_, &TestWorker::error, [&] { results_.error = true; });
|
||||||
|
QObject::connect(worker_, &TestWorker::error, &loop_, &QEventLoop::quit);
|
||||||
|
QObject::connect(worker_, &TestWorker::error, [&] { results_.error = true; });
|
||||||
|
QObject::connect(worker_, &TestWorker::error, &loop_, &QEventLoop::quit);
|
||||||
|
QObject::connect(worker_, &TestWorker::cancelled, [&] { results_.cancelled = true; });
|
||||||
|
QObject::connect(worker_, &TestWorker::cancelled, &loop_, &QEventLoop::quit);
|
||||||
|
|
||||||
|
overseer_ = std::make_unique<Overseer>(worker_, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void Workers::TearDown() {
|
||||||
|
EXPECT_NO_FATAL_FAILURE(overseer_.reset());
|
||||||
|
Test::TearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] lifetimeMs The lifetime of the worker in milliseconds.
|
||||||
|
/// \param[in] willSucceed Will the worker succeed (emit finished) or fail (emit error).
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
TestWorker::TestWorker()
|
||||||
|
: Worker(nullptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] lifetimeMs The lifetime of the worker in milliseconds.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void TestWorker::setLifetime(qint64 lifetimeMs) {
|
||||||
|
lifetimeMs_ = lifetimeMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] willSucceed Will the worker succeed?
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void TestWorker::setWillSucceed(bool willSucceed) {
|
||||||
|
willSucceed_ = willSucceed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void TestWorker::run() {
|
||||||
|
emit started();
|
||||||
|
|
||||||
|
QElapsedTimer timer;
|
||||||
|
timer.start();
|
||||||
|
while (true) {
|
||||||
|
if (cancelled_.loadRelaxed()) {
|
||||||
|
emit cancelled();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (timer.elapsed() >= lifetimeMs_) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (willSucceed_) {
|
||||||
|
emit finished();
|
||||||
|
} else {
|
||||||
|
emit error(QString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
void TestWorker::cancel() {
|
||||||
|
cancelled_.storeRelaxed(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
TEST_F(Workers, SuccessfulWorker) {
|
||||||
|
worker_->setLifetime(10);
|
||||||
|
worker_->setWillSucceed(true);
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(overseer_->startWorker(false));
|
||||||
|
EXPECT_NO_THROW(loop_.exec());
|
||||||
|
|
||||||
|
EXPECT_TRUE(results_.started);
|
||||||
|
EXPECT_TRUE(results_.finished);
|
||||||
|
EXPECT_FALSE(results_.error);
|
||||||
|
EXPECT_FALSE(results_.cancelled);
|
||||||
|
|
||||||
|
EXPECT_TRUE(overseer_->worker() != nullptr); // overseer started without autorelease.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
TEST_F(Workers, ErrorWorker) {
|
||||||
|
worker_->setLifetime(10);
|
||||||
|
worker_->setWillSucceed(false);
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(overseer_->startWorker(true));
|
||||||
|
EXPECT_NO_THROW(loop_.exec());
|
||||||
|
|
||||||
|
EXPECT_TRUE(results_.started);
|
||||||
|
EXPECT_FALSE(results_.finished);
|
||||||
|
EXPECT_TRUE(results_.error);
|
||||||
|
EXPECT_FALSE(results_.cancelled);
|
||||||
|
|
||||||
|
EXPECT_TRUE(overseer_->worker() == nullptr); // overseer started with autorelease.
|
||||||
|
}
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
TEST_F(Workers, CancelledWorker) {
|
||||||
|
worker_->setLifetime(10000);
|
||||||
|
worker_->setWillSucceed(true);
|
||||||
|
EXPECT_NO_THROW(overseer_->startWorker(false));
|
||||||
|
EXPECT_NO_THROW(QTimer::singleShot(10, [&]() { worker_->cancel(); }));
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(loop_.exec());
|
||||||
|
|
||||||
|
EXPECT_TRUE(results_.started);
|
||||||
|
EXPECT_FALSE(results_.finished);
|
||||||
|
EXPECT_FALSE(results_.error);
|
||||||
|
EXPECT_TRUE(results_.cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
//
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
TEST_F(Workers, Wait) {
|
||||||
|
worker_->setLifetime(10000);
|
||||||
|
worker_->setWillSucceed(true);
|
||||||
|
overseer_->startWorker(true);
|
||||||
|
|
||||||
|
bool isFinished = false;
|
||||||
|
EXPECT_NO_THROW(isFinished = overseer_->isFinished());
|
||||||
|
EXPECT_FALSE(isFinished);
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(isFinished = overseer_->wait(10));
|
||||||
|
EXPECT_FALSE(isFinished);
|
||||||
|
|
||||||
|
worker_->cancel();
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(isFinished = overseer_->wait(10000));
|
||||||
|
EXPECT_TRUE(isFinished);
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(isFinished = overseer_->isFinished());
|
||||||
|
EXPECT_TRUE(isFinished);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
88
internal/frontend/bridge-gui/bridgepp/Test/TestWorker.h
Normal file
88
internal/frontend/bridge-gui/bridgepp/Test/TestWorker.h
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// Copyright (c) 2023 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_GUI_TEST_WORKER_H
|
||||||
|
#define BRIDGE_GUI_TEST_WORKER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <bridgepp/Worker/Overseer.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \brief Test worker class.
|
||||||
|
///
|
||||||
|
/// This worker simply waits:
|
||||||
|
/// - For a specified amount of time and will succeed (emit finished()) or fail (emit error()) based on its parameters.
|
||||||
|
/// - to be cancelled (and will emit cancelled in that case).
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
class TestWorker : public bridgepp::Worker {
|
||||||
|
Q_OBJECT
|
||||||
|
public: // member functions.
|
||||||
|
TestWorker(); ///< Default constructor.
|
||||||
|
TestWorker(TestWorker const &) = delete; ///< Disabled copy-constructor.
|
||||||
|
TestWorker(TestWorker &&) = delete; ///< Disabled assignment copy-constructor.
|
||||||
|
~TestWorker() override = default; ///< Destructor.
|
||||||
|
TestWorker &operator=(TestWorker const &) = delete; ///< Disabled assignment operator.
|
||||||
|
TestWorker &operator=(TestWorker &&) = delete; ///< Disabled move assignment operator.
|
||||||
|
void setLifetime(qint64 lifetimeMs); ///< Set the lifetime of the worker.
|
||||||
|
void setWillSucceed(bool willSucceed); ///< Set if the worker will succeed.
|
||||||
|
void run() override; ///< Run the worker.
|
||||||
|
void cancel(); ///< Cancel the worker.
|
||||||
|
|
||||||
|
private: // data members
|
||||||
|
qint64 lifetimeMs_ { 10 }; ///< The lifetime of the worker in milliseconds.
|
||||||
|
bool willSucceed_ { true }; ///< Will the worker succeed?
|
||||||
|
QAtomicInteger<char> cancelled_; ///< Has the worker been cancelled.
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \brief Fixture class for worker tests.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
class Workers : public testing::Test {
|
||||||
|
public: // member functions.
|
||||||
|
Workers(); ///< Default constructor.
|
||||||
|
Workers(Workers const &) = delete; ///< Disabled copy-constructor.
|
||||||
|
Workers(Workers &&) = delete; ///< Disabled assignment copy-constructor.
|
||||||
|
~Workers() = default; ///< Destructor.
|
||||||
|
Workers &operator=(Workers const &) = delete; ///< Disabled assignment operator.
|
||||||
|
Workers &operator=(Workers &&) = delete; ///< Disabled move assignment operator.
|
||||||
|
|
||||||
|
protected: // member functions.
|
||||||
|
void SetUp() override; ///< Setup the fixture.
|
||||||
|
void TearDown() override; ///< Tear down the fixture.
|
||||||
|
|
||||||
|
protected: // data type
|
||||||
|
struct Results {
|
||||||
|
bool started { false };
|
||||||
|
bool finished { false };
|
||||||
|
bool error { false };
|
||||||
|
bool cancelled { false };
|
||||||
|
}; ///< Test results data type
|
||||||
|
|
||||||
|
protected: // data members
|
||||||
|
QCoreApplication app_; ///< The Qt application required for event loop.
|
||||||
|
bridgepp::UPOverseer overseer_; ///< The overseer for the worker.
|
||||||
|
TestWorker *worker_ { nullptr }; ///< The worker.
|
||||||
|
QEventLoop loop_; ///< The event loop.
|
||||||
|
Results results_; ///< The test results.
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //BRIDGE_GUI_TEST_WORKER_H
|
||||||
@ -20,7 +20,7 @@
|
|||||||
#define BRIDGE_PP_TESTER_BRIDGE_UTILS_H
|
#define BRIDGE_PP_TESTER_BRIDGE_UTILS_H
|
||||||
|
|
||||||
|
|
||||||
#include <bridgepp/User/User.h>
|
#include "User/User.h"
|
||||||
|
|
||||||
|
|
||||||
namespace bridgepp {
|
namespace bridgepp {
|
||||||
|
|||||||
@ -87,6 +87,14 @@ QString Exception::details() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \return The function that threw the exception.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
QString Exception::function() const noexcept {
|
||||||
|
return function_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \return The attachment for the exception.
|
/// \return The attachment for the exception.
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
@ -109,4 +117,5 @@ QString Exception::detailedWhat() const {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace bridgepp
|
} // namespace bridgepp
|
||||||
|
|||||||
@ -42,6 +42,7 @@ public: // member functions
|
|||||||
QString qwhat() const noexcept; ///< Return the description of the exception as a QString
|
QString 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
|
const char *what() const noexcept override; ///< Return the description of the exception as C style string
|
||||||
QString details() const noexcept; ///< Return the details for the exception
|
QString details() const noexcept; ///< Return the details for the exception
|
||||||
|
QString function() const noexcept; ///< Return the function that threw the exception.
|
||||||
QByteArray attachment() const noexcept; ///< Return the attachment for the exception.
|
QByteArray attachment() const noexcept; ///< Return the attachment for the exception.
|
||||||
QString detailedWhat() const; ///< Return the detailed description of the message (i.e. including the function name and the details).
|
QString detailedWhat() const; ///< Return the detailed description of the message (i.e. including the function name and the details).
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,6 @@
|
|||||||
#include "GRPCClient.h"
|
#include "GRPCClient.h"
|
||||||
#include "GRPCUtils.h"
|
#include "GRPCUtils.h"
|
||||||
#include "GRPCErrors.h"
|
#include "GRPCErrors.h"
|
||||||
#include "../BridgeUtils.h"
|
|
||||||
#include "../Exception/Exception.h"
|
#include "../Exception/Exception.h"
|
||||||
#include "../ProcessMonitor.h"
|
#include "../ProcessMonitor.h"
|
||||||
#include "../Log/LogUtils.h"
|
#include "../Log/LogUtils.h"
|
||||||
@ -295,6 +294,24 @@ grpc::Status GRPCClient::isAllMailVisible(bool &outIsVisible) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[out] outIsDisabled The value for the property
|
||||||
|
/// \return The status for the gRPC call.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
grpc::Status GRPCClient::isTelemetryDisabled(bool &outIsDisabled) {
|
||||||
|
return this->logGRPCCallStatus(this->getBool(&Bridge::Stub::IsTelemetryDisabled, outIsDisabled), __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[out] isDisabled The new value for the property
|
||||||
|
/// \return The status for the gRPC call.
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
grpc::Status GRPCClient::setIsTelemetryDisabled(bool isDisabled) {
|
||||||
|
return this->logGRPCCallStatus(this->setBool(&Bridge::Stub::SetIsTelemetryDisabled, isDisabled), __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \param[in] isVisible The new value for the property.
|
/// \param[in] isVisible The new value for the property.
|
||||||
/// \return The status for the gRPC call.
|
/// \return The status for the gRPC call.
|
||||||
|
|||||||
@ -71,6 +71,8 @@ public: // member functions.
|
|||||||
grpc::Status setIsBetaEnabled(bool enabled); ///< Performs the 'setIsBetaEnabled' gRPC call.
|
grpc::Status setIsBetaEnabled(bool enabled); ///< Performs the 'setIsBetaEnabled' gRPC call.
|
||||||
grpc::Status isAllMailVisible(bool &outIsVisible); ///< Performs the "isAllMailVisible" gRPC call.
|
grpc::Status isAllMailVisible(bool &outIsVisible); ///< Performs the "isAllMailVisible" gRPC call.
|
||||||
grpc::Status setIsAllMailVisible(bool isVisible); ///< Performs the 'setIsAllMailVisible' gRPC call.
|
grpc::Status setIsAllMailVisible(bool isVisible); ///< Performs the 'setIsAllMailVisible' gRPC call.
|
||||||
|
grpc::Status isTelemetryDisabled(bool &outIsDisabled); ///< Performs the 'setIsTelemetryDisabled' gRPC call.
|
||||||
|
grpc::Status setIsTelemetryDisabled(bool isDisabled); ///< Performs the 'isTelemetryDisabled' gRPC call.
|
||||||
grpc::Status colorSchemeName(QString &outName); ///< Performs the "colorSchemeName' 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 setColorSchemeName(QString const &name); ///< Performs the "setColorSchemeName' gRPC call.
|
||||||
grpc::Status currentEmailClient(QString &outName); ///< Performs the 'currentEmailClient' gRPC call.
|
grpc::Status currentEmailClient(QString &outName); ///< Performs the 'currentEmailClient' gRPC call.
|
||||||
|
|||||||
@ -34,6 +34,8 @@ static const char* Bridge_method_names[] = {
|
|||||||
"/grpc.Bridge/IsBetaEnabled",
|
"/grpc.Bridge/IsBetaEnabled",
|
||||||
"/grpc.Bridge/SetIsAllMailVisible",
|
"/grpc.Bridge/SetIsAllMailVisible",
|
||||||
"/grpc.Bridge/IsAllMailVisible",
|
"/grpc.Bridge/IsAllMailVisible",
|
||||||
|
"/grpc.Bridge/SetIsTelemetryDisabled",
|
||||||
|
"/grpc.Bridge/IsTelemetryDisabled",
|
||||||
"/grpc.Bridge/GoOs",
|
"/grpc.Bridge/GoOs",
|
||||||
"/grpc.Bridge/TriggerReset",
|
"/grpc.Bridge/TriggerReset",
|
||||||
"/grpc.Bridge/Version",
|
"/grpc.Bridge/Version",
|
||||||
@ -98,49 +100,51 @@ Bridge::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, co
|
|||||||
, rpcmethod_IsBetaEnabled_(Bridge_method_names[9], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_IsBetaEnabled_(Bridge_method_names[9], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_SetIsAllMailVisible_(Bridge_method_names[10], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_SetIsAllMailVisible_(Bridge_method_names[10], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_IsAllMailVisible_(Bridge_method_names[11], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_IsAllMailVisible_(Bridge_method_names[11], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_GoOs_(Bridge_method_names[12], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_SetIsTelemetryDisabled_(Bridge_method_names[12], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_TriggerReset_(Bridge_method_names[13], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_IsTelemetryDisabled_(Bridge_method_names[13], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_Version_(Bridge_method_names[14], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_GoOs_(Bridge_method_names[14], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_LogsPath_(Bridge_method_names[15], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_TriggerReset_(Bridge_method_names[15], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_LicensePath_(Bridge_method_names[16], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_Version_(Bridge_method_names[16], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_ReleaseNotesPageLink_(Bridge_method_names[17], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_LogsPath_(Bridge_method_names[17], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_DependencyLicensesLink_(Bridge_method_names[18], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_LicensePath_(Bridge_method_names[18], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_LandingPageLink_(Bridge_method_names[19], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_ReleaseNotesPageLink_(Bridge_method_names[19], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_SetColorSchemeName_(Bridge_method_names[20], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_DependencyLicensesLink_(Bridge_method_names[20], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_ColorSchemeName_(Bridge_method_names[21], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_LandingPageLink_(Bridge_method_names[21], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_CurrentEmailClient_(Bridge_method_names[22], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_SetColorSchemeName_(Bridge_method_names[22], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_ReportBug_(Bridge_method_names[23], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_ColorSchemeName_(Bridge_method_names[23], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_ExportTLSCertificates_(Bridge_method_names[24], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_CurrentEmailClient_(Bridge_method_names[24], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_ForceLauncher_(Bridge_method_names[25], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_ReportBug_(Bridge_method_names[25], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_SetMainExecutable_(Bridge_method_names[26], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_ExportTLSCertificates_(Bridge_method_names[26], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_Login_(Bridge_method_names[27], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_ForceLauncher_(Bridge_method_names[27], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_Login2FA_(Bridge_method_names[28], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_SetMainExecutable_(Bridge_method_names[28], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_Login2Passwords_(Bridge_method_names[29], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_Login_(Bridge_method_names[29], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_LoginAbort_(Bridge_method_names[30], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_Login2FA_(Bridge_method_names[30], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_CheckUpdate_(Bridge_method_names[31], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_Login2Passwords_(Bridge_method_names[31], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_InstallUpdate_(Bridge_method_names[32], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_LoginAbort_(Bridge_method_names[32], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_SetIsAutomaticUpdateOn_(Bridge_method_names[33], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_CheckUpdate_(Bridge_method_names[33], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_IsAutomaticUpdateOn_(Bridge_method_names[34], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_InstallUpdate_(Bridge_method_names[34], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_DiskCachePath_(Bridge_method_names[35], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_SetIsAutomaticUpdateOn_(Bridge_method_names[35], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_SetDiskCachePath_(Bridge_method_names[36], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_IsAutomaticUpdateOn_(Bridge_method_names[36], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_SetIsDoHEnabled_(Bridge_method_names[37], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_DiskCachePath_(Bridge_method_names[37], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_IsDoHEnabled_(Bridge_method_names[38], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_SetDiskCachePath_(Bridge_method_names[38], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_MailServerSettings_(Bridge_method_names[39], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_SetIsDoHEnabled_(Bridge_method_names[39], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_SetMailServerSettings_(Bridge_method_names[40], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_IsDoHEnabled_(Bridge_method_names[40], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_Hostname_(Bridge_method_names[41], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_MailServerSettings_(Bridge_method_names[41], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_IsPortFree_(Bridge_method_names[42], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_SetMailServerSettings_(Bridge_method_names[42], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_AvailableKeychains_(Bridge_method_names[43], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_Hostname_(Bridge_method_names[43], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_SetCurrentKeychain_(Bridge_method_names[44], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_IsPortFree_(Bridge_method_names[44], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_CurrentKeychain_(Bridge_method_names[45], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_AvailableKeychains_(Bridge_method_names[45], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_GetUserList_(Bridge_method_names[46], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_SetCurrentKeychain_(Bridge_method_names[46], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_GetUser_(Bridge_method_names[47], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_CurrentKeychain_(Bridge_method_names[47], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_SetUserSplitMode_(Bridge_method_names[48], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_GetUserList_(Bridge_method_names[48], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_SendBadEventUserFeedback_(Bridge_method_names[49], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_GetUser_(Bridge_method_names[49], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_LogoutUser_(Bridge_method_names[50], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_SetUserSplitMode_(Bridge_method_names[50], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_RemoveUser_(Bridge_method_names[51], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_SendBadEventUserFeedback_(Bridge_method_names[51], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_ConfigureUserAppleMail_(Bridge_method_names[52], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_LogoutUser_(Bridge_method_names[52], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_RunEventStream_(Bridge_method_names[53], options.suffix_for_stats(),::grpc::internal::RpcMethod::SERVER_STREAMING, channel)
|
, rpcmethod_RemoveUser_(Bridge_method_names[53], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
, rpcmethod_StopEventStream_(Bridge_method_names[54], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
, rpcmethod_ConfigureUserAppleMail_(Bridge_method_names[54], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
|
, rpcmethod_RunEventStream_(Bridge_method_names[55], options.suffix_for_stats(),::grpc::internal::RpcMethod::SERVER_STREAMING, channel)
|
||||||
|
, rpcmethod_StopEventStream_(Bridge_method_names[56], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
::grpc::Status Bridge::Stub::CheckTokens(::grpc::ClientContext* context, const ::google::protobuf::StringValue& request, ::google::protobuf::StringValue* response) {
|
::grpc::Status Bridge::Stub::CheckTokens(::grpc::ClientContext* context, const ::google::protobuf::StringValue& request, ::google::protobuf::StringValue* response) {
|
||||||
@ -419,6 +423,52 @@ void Bridge::Stub::async::IsAllMailVisible(::grpc::ClientContext* context, const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::grpc::Status Bridge::Stub::SetIsTelemetryDisabled(::grpc::ClientContext* context, const ::google::protobuf::BoolValue& request, ::google::protobuf::Empty* response) {
|
||||||
|
return ::grpc::internal::BlockingUnaryCall< ::google::protobuf::BoolValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), rpcmethod_SetIsTelemetryDisabled_, context, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bridge::Stub::async::SetIsTelemetryDisabled(::grpc::ClientContext* context, const ::google::protobuf::BoolValue* request, ::google::protobuf::Empty* response, std::function<void(::grpc::Status)> f) {
|
||||||
|
::grpc::internal::CallbackUnaryCall< ::google::protobuf::BoolValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_SetIsTelemetryDisabled_, context, request, response, std::move(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bridge::Stub::async::SetIsTelemetryDisabled(::grpc::ClientContext* context, const ::google::protobuf::BoolValue* request, ::google::protobuf::Empty* response, ::grpc::ClientUnaryReactor* reactor) {
|
||||||
|
::grpc::internal::ClientCallbackUnaryFactory::Create< ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_SetIsTelemetryDisabled_, context, request, response, reactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
::grpc::ClientAsyncResponseReader< ::google::protobuf::Empty>* Bridge::Stub::PrepareAsyncSetIsTelemetryDisabledRaw(::grpc::ClientContext* context, const ::google::protobuf::BoolValue& request, ::grpc::CompletionQueue* cq) {
|
||||||
|
return ::grpc::internal::ClientAsyncResponseReaderHelper::Create< ::google::protobuf::Empty, ::google::protobuf::BoolValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), cq, rpcmethod_SetIsTelemetryDisabled_, context, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
::grpc::ClientAsyncResponseReader< ::google::protobuf::Empty>* Bridge::Stub::AsyncSetIsTelemetryDisabledRaw(::grpc::ClientContext* context, const ::google::protobuf::BoolValue& request, ::grpc::CompletionQueue* cq) {
|
||||||
|
auto* result =
|
||||||
|
this->PrepareAsyncSetIsTelemetryDisabledRaw(context, request, cq);
|
||||||
|
result->StartCall();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
::grpc::Status Bridge::Stub::IsTelemetryDisabled(::grpc::ClientContext* context, const ::google::protobuf::Empty& request, ::google::protobuf::BoolValue* response) {
|
||||||
|
return ::grpc::internal::BlockingUnaryCall< ::google::protobuf::Empty, ::google::protobuf::BoolValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), rpcmethod_IsTelemetryDisabled_, context, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bridge::Stub::async::IsTelemetryDisabled(::grpc::ClientContext* context, const ::google::protobuf::Empty* request, ::google::protobuf::BoolValue* response, std::function<void(::grpc::Status)> f) {
|
||||||
|
::grpc::internal::CallbackUnaryCall< ::google::protobuf::Empty, ::google::protobuf::BoolValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_IsTelemetryDisabled_, context, request, response, std::move(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bridge::Stub::async::IsTelemetryDisabled(::grpc::ClientContext* context, const ::google::protobuf::Empty* request, ::google::protobuf::BoolValue* response, ::grpc::ClientUnaryReactor* reactor) {
|
||||||
|
::grpc::internal::ClientCallbackUnaryFactory::Create< ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_IsTelemetryDisabled_, context, request, response, reactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
::grpc::ClientAsyncResponseReader< ::google::protobuf::BoolValue>* Bridge::Stub::PrepareAsyncIsTelemetryDisabledRaw(::grpc::ClientContext* context, const ::google::protobuf::Empty& request, ::grpc::CompletionQueue* cq) {
|
||||||
|
return ::grpc::internal::ClientAsyncResponseReaderHelper::Create< ::google::protobuf::BoolValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), cq, rpcmethod_IsTelemetryDisabled_, context, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
::grpc::ClientAsyncResponseReader< ::google::protobuf::BoolValue>* Bridge::Stub::AsyncIsTelemetryDisabledRaw(::grpc::ClientContext* context, const ::google::protobuf::Empty& request, ::grpc::CompletionQueue* cq) {
|
||||||
|
auto* result =
|
||||||
|
this->PrepareAsyncIsTelemetryDisabledRaw(context, request, cq);
|
||||||
|
result->StartCall();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
::grpc::Status Bridge::Stub::GoOs(::grpc::ClientContext* context, const ::google::protobuf::Empty& request, ::google::protobuf::StringValue* response) {
|
::grpc::Status Bridge::Stub::GoOs(::grpc::ClientContext* context, const ::google::protobuf::Empty& request, ::google::protobuf::StringValue* response) {
|
||||||
return ::grpc::internal::BlockingUnaryCall< ::google::protobuf::Empty, ::google::protobuf::StringValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), rpcmethod_GoOs_, context, request, response);
|
return ::grpc::internal::BlockingUnaryCall< ::google::protobuf::Empty, ::google::protobuf::StringValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), rpcmethod_GoOs_, context, request, response);
|
||||||
}
|
}
|
||||||
@ -1525,22 +1575,22 @@ Bridge::Service::Service() {
|
|||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[12],
|
Bridge_method_names[12],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::StringValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::BoolValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
::grpc::ServerContext* ctx,
|
::grpc::ServerContext* ctx,
|
||||||
const ::google::protobuf::Empty* req,
|
const ::google::protobuf::BoolValue* req,
|
||||||
::google::protobuf::StringValue* resp) {
|
::google::protobuf::Empty* resp) {
|
||||||
return service->GoOs(ctx, req, resp);
|
return service->SetIsTelemetryDisabled(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[13],
|
Bridge_method_names[13],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::BoolValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
::grpc::ServerContext* ctx,
|
::grpc::ServerContext* ctx,
|
||||||
const ::google::protobuf::Empty* req,
|
const ::google::protobuf::Empty* req,
|
||||||
::google::protobuf::Empty* resp) {
|
::google::protobuf::BoolValue* resp) {
|
||||||
return service->TriggerReset(ctx, req, resp);
|
return service->IsTelemetryDisabled(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[14],
|
Bridge_method_names[14],
|
||||||
@ -1550,17 +1600,17 @@ Bridge::Service::Service() {
|
|||||||
::grpc::ServerContext* ctx,
|
::grpc::ServerContext* ctx,
|
||||||
const ::google::protobuf::Empty* req,
|
const ::google::protobuf::Empty* req,
|
||||||
::google::protobuf::StringValue* resp) {
|
::google::protobuf::StringValue* resp) {
|
||||||
return service->Version(ctx, req, resp);
|
return service->GoOs(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[15],
|
Bridge_method_names[15],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::StringValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
::grpc::ServerContext* ctx,
|
::grpc::ServerContext* ctx,
|
||||||
const ::google::protobuf::Empty* req,
|
const ::google::protobuf::Empty* req,
|
||||||
::google::protobuf::StringValue* resp) {
|
::google::protobuf::Empty* resp) {
|
||||||
return service->LogsPath(ctx, req, resp);
|
return service->TriggerReset(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[16],
|
Bridge_method_names[16],
|
||||||
@ -1570,7 +1620,7 @@ Bridge::Service::Service() {
|
|||||||
::grpc::ServerContext* ctx,
|
::grpc::ServerContext* ctx,
|
||||||
const ::google::protobuf::Empty* req,
|
const ::google::protobuf::Empty* req,
|
||||||
::google::protobuf::StringValue* resp) {
|
::google::protobuf::StringValue* resp) {
|
||||||
return service->LicensePath(ctx, req, resp);
|
return service->Version(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[17],
|
Bridge_method_names[17],
|
||||||
@ -1580,7 +1630,7 @@ Bridge::Service::Service() {
|
|||||||
::grpc::ServerContext* ctx,
|
::grpc::ServerContext* ctx,
|
||||||
const ::google::protobuf::Empty* req,
|
const ::google::protobuf::Empty* req,
|
||||||
::google::protobuf::StringValue* resp) {
|
::google::protobuf::StringValue* resp) {
|
||||||
return service->ReleaseNotesPageLink(ctx, req, resp);
|
return service->LogsPath(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[18],
|
Bridge_method_names[18],
|
||||||
@ -1590,7 +1640,7 @@ Bridge::Service::Service() {
|
|||||||
::grpc::ServerContext* ctx,
|
::grpc::ServerContext* ctx,
|
||||||
const ::google::protobuf::Empty* req,
|
const ::google::protobuf::Empty* req,
|
||||||
::google::protobuf::StringValue* resp) {
|
::google::protobuf::StringValue* resp) {
|
||||||
return service->DependencyLicensesLink(ctx, req, resp);
|
return service->LicensePath(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[19],
|
Bridge_method_names[19],
|
||||||
@ -1600,17 +1650,17 @@ Bridge::Service::Service() {
|
|||||||
::grpc::ServerContext* ctx,
|
::grpc::ServerContext* ctx,
|
||||||
const ::google::protobuf::Empty* req,
|
const ::google::protobuf::Empty* req,
|
||||||
::google::protobuf::StringValue* resp) {
|
::google::protobuf::StringValue* resp) {
|
||||||
return service->LandingPageLink(ctx, req, resp);
|
return service->ReleaseNotesPageLink(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[20],
|
Bridge_method_names[20],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::StringValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
::grpc::ServerContext* ctx,
|
::grpc::ServerContext* ctx,
|
||||||
const ::google::protobuf::StringValue* req,
|
const ::google::protobuf::Empty* req,
|
||||||
::google::protobuf::Empty* resp) {
|
::google::protobuf::StringValue* resp) {
|
||||||
return service->SetColorSchemeName(ctx, req, resp);
|
return service->DependencyLicensesLink(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[21],
|
Bridge_method_names[21],
|
||||||
@ -1620,11 +1670,31 @@ Bridge::Service::Service() {
|
|||||||
::grpc::ServerContext* ctx,
|
::grpc::ServerContext* ctx,
|
||||||
const ::google::protobuf::Empty* req,
|
const ::google::protobuf::Empty* req,
|
||||||
::google::protobuf::StringValue* resp) {
|
::google::protobuf::StringValue* resp) {
|
||||||
return service->ColorSchemeName(ctx, req, resp);
|
return service->LandingPageLink(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[22],
|
Bridge_method_names[22],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
|
[](Bridge::Service* service,
|
||||||
|
::grpc::ServerContext* ctx,
|
||||||
|
const ::google::protobuf::StringValue* req,
|
||||||
|
::google::protobuf::Empty* resp) {
|
||||||
|
return service->SetColorSchemeName(ctx, req, resp);
|
||||||
|
}, this)));
|
||||||
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
|
Bridge_method_names[23],
|
||||||
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::StringValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
|
[](Bridge::Service* service,
|
||||||
|
::grpc::ServerContext* ctx,
|
||||||
|
const ::google::protobuf::Empty* req,
|
||||||
|
::google::protobuf::StringValue* resp) {
|
||||||
|
return service->ColorSchemeName(ctx, req, resp);
|
||||||
|
}, this)));
|
||||||
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
|
Bridge_method_names[24],
|
||||||
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::StringValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::StringValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
::grpc::ServerContext* ctx,
|
::grpc::ServerContext* ctx,
|
||||||
@ -1633,7 +1703,7 @@ Bridge::Service::Service() {
|
|||||||
return service->CurrentEmailClient(ctx, req, resp);
|
return service->CurrentEmailClient(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[23],
|
Bridge_method_names[25],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::ReportBugRequest, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::ReportBugRequest, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1643,7 +1713,7 @@ Bridge::Service::Service() {
|
|||||||
return service->ReportBug(ctx, req, resp);
|
return service->ReportBug(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[24],
|
Bridge_method_names[26],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1653,7 +1723,7 @@ Bridge::Service::Service() {
|
|||||||
return service->ExportTLSCertificates(ctx, req, resp);
|
return service->ExportTLSCertificates(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[25],
|
Bridge_method_names[27],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1663,7 +1733,7 @@ Bridge::Service::Service() {
|
|||||||
return service->ForceLauncher(ctx, req, resp);
|
return service->ForceLauncher(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[26],
|
Bridge_method_names[28],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1673,7 +1743,7 @@ Bridge::Service::Service() {
|
|||||||
return service->SetMainExecutable(ctx, req, resp);
|
return service->SetMainExecutable(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[27],
|
Bridge_method_names[29],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::LoginRequest, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::LoginRequest, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1683,7 +1753,7 @@ Bridge::Service::Service() {
|
|||||||
return service->Login(ctx, req, resp);
|
return service->Login(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[28],
|
Bridge_method_names[30],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::LoginRequest, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::LoginRequest, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1693,7 +1763,7 @@ Bridge::Service::Service() {
|
|||||||
return service->Login2FA(ctx, req, resp);
|
return service->Login2FA(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[29],
|
Bridge_method_names[31],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::LoginRequest, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::LoginRequest, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1703,7 +1773,7 @@ Bridge::Service::Service() {
|
|||||||
return service->Login2Passwords(ctx, req, resp);
|
return service->Login2Passwords(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[30],
|
Bridge_method_names[32],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::LoginAbortRequest, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::LoginAbortRequest, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1713,7 +1783,7 @@ Bridge::Service::Service() {
|
|||||||
return service->LoginAbort(ctx, req, resp);
|
return service->LoginAbort(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[31],
|
Bridge_method_names[33],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1723,7 +1793,7 @@ Bridge::Service::Service() {
|
|||||||
return service->CheckUpdate(ctx, req, resp);
|
return service->CheckUpdate(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[32],
|
Bridge_method_names[34],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1733,7 +1803,7 @@ Bridge::Service::Service() {
|
|||||||
return service->InstallUpdate(ctx, req, resp);
|
return service->InstallUpdate(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[33],
|
Bridge_method_names[35],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::BoolValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::BoolValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1743,7 +1813,7 @@ Bridge::Service::Service() {
|
|||||||
return service->SetIsAutomaticUpdateOn(ctx, req, resp);
|
return service->SetIsAutomaticUpdateOn(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[34],
|
Bridge_method_names[36],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::BoolValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::BoolValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1753,7 +1823,7 @@ Bridge::Service::Service() {
|
|||||||
return service->IsAutomaticUpdateOn(ctx, req, resp);
|
return service->IsAutomaticUpdateOn(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[35],
|
Bridge_method_names[37],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::StringValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::StringValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1763,7 +1833,7 @@ Bridge::Service::Service() {
|
|||||||
return service->DiskCachePath(ctx, req, resp);
|
return service->DiskCachePath(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[36],
|
Bridge_method_names[38],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1773,7 +1843,7 @@ Bridge::Service::Service() {
|
|||||||
return service->SetDiskCachePath(ctx, req, resp);
|
return service->SetDiskCachePath(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[37],
|
Bridge_method_names[39],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::BoolValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::BoolValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1783,7 +1853,7 @@ Bridge::Service::Service() {
|
|||||||
return service->SetIsDoHEnabled(ctx, req, resp);
|
return service->SetIsDoHEnabled(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[38],
|
Bridge_method_names[40],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::BoolValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::BoolValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1793,7 +1863,7 @@ Bridge::Service::Service() {
|
|||||||
return service->IsDoHEnabled(ctx, req, resp);
|
return service->IsDoHEnabled(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[39],
|
Bridge_method_names[41],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::grpc::ImapSmtpSettings, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::grpc::ImapSmtpSettings, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1803,7 +1873,7 @@ Bridge::Service::Service() {
|
|||||||
return service->MailServerSettings(ctx, req, resp);
|
return service->MailServerSettings(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[40],
|
Bridge_method_names[42],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::ImapSmtpSettings, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::ImapSmtpSettings, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1813,7 +1883,7 @@ Bridge::Service::Service() {
|
|||||||
return service->SetMailServerSettings(ctx, req, resp);
|
return service->SetMailServerSettings(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[41],
|
Bridge_method_names[43],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::StringValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::StringValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1823,7 +1893,7 @@ Bridge::Service::Service() {
|
|||||||
return service->Hostname(ctx, req, resp);
|
return service->Hostname(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[42],
|
Bridge_method_names[44],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Int32Value, ::google::protobuf::BoolValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Int32Value, ::google::protobuf::BoolValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1833,7 +1903,7 @@ Bridge::Service::Service() {
|
|||||||
return service->IsPortFree(ctx, req, resp);
|
return service->IsPortFree(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[43],
|
Bridge_method_names[45],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::grpc::AvailableKeychainsResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::grpc::AvailableKeychainsResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1843,7 +1913,7 @@ Bridge::Service::Service() {
|
|||||||
return service->AvailableKeychains(ctx, req, resp);
|
return service->AvailableKeychains(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[44],
|
Bridge_method_names[46],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1853,7 +1923,7 @@ Bridge::Service::Service() {
|
|||||||
return service->SetCurrentKeychain(ctx, req, resp);
|
return service->SetCurrentKeychain(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[45],
|
Bridge_method_names[47],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::StringValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::StringValue, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1863,7 +1933,7 @@ Bridge::Service::Service() {
|
|||||||
return service->CurrentKeychain(ctx, req, resp);
|
return service->CurrentKeychain(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[46],
|
Bridge_method_names[48],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::grpc::UserListResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::grpc::UserListResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1873,7 +1943,7 @@ Bridge::Service::Service() {
|
|||||||
return service->GetUserList(ctx, req, resp);
|
return service->GetUserList(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[47],
|
Bridge_method_names[49],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::grpc::User, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::grpc::User, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1883,7 +1953,7 @@ Bridge::Service::Service() {
|
|||||||
return service->GetUser(ctx, req, resp);
|
return service->GetUser(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[48],
|
Bridge_method_names[50],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::UserSplitModeRequest, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::UserSplitModeRequest, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1893,7 +1963,7 @@ Bridge::Service::Service() {
|
|||||||
return service->SetUserSplitMode(ctx, req, resp);
|
return service->SetUserSplitMode(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[49],
|
Bridge_method_names[51],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::UserBadEventFeedbackRequest, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::UserBadEventFeedbackRequest, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1903,7 +1973,7 @@ Bridge::Service::Service() {
|
|||||||
return service->SendBadEventUserFeedback(ctx, req, resp);
|
return service->SendBadEventUserFeedback(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[50],
|
Bridge_method_names[52],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1913,7 +1983,7 @@ Bridge::Service::Service() {
|
|||||||
return service->LogoutUser(ctx, req, resp);
|
return service->LogoutUser(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[51],
|
Bridge_method_names[53],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::StringValue, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1923,7 +1993,7 @@ Bridge::Service::Service() {
|
|||||||
return service->RemoveUser(ctx, req, resp);
|
return service->RemoveUser(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[52],
|
Bridge_method_names[54],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::ConfigureAppleMailRequest, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::grpc::ConfigureAppleMailRequest, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1933,7 +2003,7 @@ Bridge::Service::Service() {
|
|||||||
return service->ConfigureUserAppleMail(ctx, req, resp);
|
return service->ConfigureUserAppleMail(ctx, req, resp);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[53],
|
Bridge_method_names[55],
|
||||||
::grpc::internal::RpcMethod::SERVER_STREAMING,
|
::grpc::internal::RpcMethod::SERVER_STREAMING,
|
||||||
new ::grpc::internal::ServerStreamingHandler< Bridge::Service, ::grpc::EventStreamRequest, ::grpc::StreamEvent>(
|
new ::grpc::internal::ServerStreamingHandler< Bridge::Service, ::grpc::EventStreamRequest, ::grpc::StreamEvent>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -1943,7 +2013,7 @@ Bridge::Service::Service() {
|
|||||||
return service->RunEventStream(ctx, req, writer);
|
return service->RunEventStream(ctx, req, writer);
|
||||||
}, this)));
|
}, this)));
|
||||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||||
Bridge_method_names[54],
|
Bridge_method_names[56],
|
||||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||||
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
new ::grpc::internal::RpcMethodHandler< Bridge::Service, ::google::protobuf::Empty, ::google::protobuf::Empty, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||||
[](Bridge::Service* service,
|
[](Bridge::Service* service,
|
||||||
@ -2041,6 +2111,20 @@ Bridge::Service::~Service() {
|
|||||||
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
|
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::grpc::Status Bridge::Service::SetIsTelemetryDisabled(::grpc::ServerContext* context, const ::google::protobuf::BoolValue* request, ::google::protobuf::Empty* response) {
|
||||||
|
(void) context;
|
||||||
|
(void) request;
|
||||||
|
(void) response;
|
||||||
|
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
::grpc::Status Bridge::Service::IsTelemetryDisabled(::grpc::ServerContext* context, const ::google::protobuf::Empty* request, ::google::protobuf::BoolValue* response) {
|
||||||
|
(void) context;
|
||||||
|
(void) request;
|
||||||
|
(void) response;
|
||||||
|
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
|
||||||
|
}
|
||||||
|
|
||||||
::grpc::Status Bridge::Service::GoOs(::grpc::ServerContext* context, const ::google::protobuf::Empty* request, ::google::protobuf::StringValue* response) {
|
::grpc::Status Bridge::Service::GoOs(::grpc::ServerContext* context, const ::google::protobuf::Empty* request, ::google::protobuf::StringValue* response) {
|
||||||
(void) context;
|
(void) context;
|
||||||
(void) request;
|
(void) request;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1673,7 +1673,7 @@ const char descriptor_table_protodef_bridge_2eproto[] PROTOBUF_SECTION_VARIABLE(
|
|||||||
"!SMTP_CONNECTION_MODE_CHANGE_ERROR\020\005*S\n\t"
|
"!SMTP_CONNECTION_MODE_CHANGE_ERROR\020\005*S\n\t"
|
||||||
"ErrorCode\022\021\n\rUNKNOWN_ERROR\020\000\022\031\n\025TLS_CERT"
|
"ErrorCode\022\021\n\rUNKNOWN_ERROR\020\000\022\031\n\025TLS_CERT"
|
||||||
"_EXPORT_ERROR\020\001\022\030\n\024TLS_KEY_EXPORT_ERROR\020"
|
"_EXPORT_ERROR\020\001\022\030\n\024TLS_KEY_EXPORT_ERROR\020"
|
||||||
"\0022\360\035\n\006Bridge\022I\n\013CheckTokens\022\034.google.pro"
|
"\0022\211\037\n\006Bridge\022I\n\013CheckTokens\022\034.google.pro"
|
||||||
"tobuf.StringValue\032\034.google.protobuf.Stri"
|
"tobuf.StringValue\032\034.google.protobuf.Stri"
|
||||||
"ngValue\022\?\n\013AddLogEntry\022\030.grpc.AddLogEntr"
|
"ngValue\022\?\n\013AddLogEntry\022\030.grpc.AddLogEntr"
|
||||||
"yRequest\032\026.google.protobuf.Empty\022:\n\010GuiR"
|
"yRequest\032\026.google.protobuf.Empty\022:\n\010GuiR"
|
||||||
@ -1693,84 +1693,88 @@ const char descriptor_table_protodef_bridge_2eproto[] PROTOBUF_SECTION_VARIABLE(
|
|||||||
"\n\023SetIsAllMailVisible\022\032.google.protobuf."
|
"\n\023SetIsAllMailVisible\022\032.google.protobuf."
|
||||||
"BoolValue\032\026.google.protobuf.Empty\022F\n\020IsA"
|
"BoolValue\032\026.google.protobuf.Empty\022F\n\020IsA"
|
||||||
"llMailVisible\022\026.google.protobuf.Empty\032\032."
|
"llMailVisible\022\026.google.protobuf.Empty\032\032."
|
||||||
"google.protobuf.BoolValue\022<\n\004GoOs\022\026.goog"
|
"google.protobuf.BoolValue\022L\n\026SetIsTeleme"
|
||||||
"le.protobuf.Empty\032\034.google.protobuf.Stri"
|
"tryDisabled\022\032.google.protobuf.BoolValue\032"
|
||||||
"ngValue\022>\n\014TriggerReset\022\026.google.protobu"
|
"\026.google.protobuf.Empty\022I\n\023IsTelemetryDi"
|
||||||
"f.Empty\032\026.google.protobuf.Empty\022\?\n\007Versi"
|
"sabled\022\026.google.protobuf.Empty\032\032.google."
|
||||||
"on\022\026.google.protobuf.Empty\032\034.google.prot"
|
"protobuf.BoolValue\022<\n\004GoOs\022\026.google.prot"
|
||||||
"obuf.StringValue\022@\n\010LogsPath\022\026.google.pr"
|
|
||||||
"otobuf.Empty\032\034.google.protobuf.StringVal"
|
|
||||||
"ue\022C\n\013LicensePath\022\026.google.protobuf.Empt"
|
|
||||||
"y\032\034.google.protobuf.StringValue\022L\n\024Relea"
|
|
||||||
"seNotesPageLink\022\026.google.protobuf.Empty\032"
|
|
||||||
"\034.google.protobuf.StringValue\022N\n\026Depende"
|
|
||||||
"ncyLicensesLink\022\026.google.protobuf.Empty\032"
|
|
||||||
"\034.google.protobuf.StringValue\022G\n\017Landing"
|
|
||||||
"PageLink\022\026.google.protobuf.Empty\032\034.googl"
|
|
||||||
"e.protobuf.StringValue\022J\n\022SetColorScheme"
|
|
||||||
"Name\022\034.google.protobuf.StringValue\032\026.goo"
|
|
||||||
"gle.protobuf.Empty\022G\n\017ColorSchemeName\022\026."
|
|
||||||
"google.protobuf.Empty\032\034.google.protobuf."
|
|
||||||
"StringValue\022J\n\022CurrentEmailClient\022\026.goog"
|
|
||||||
"le.protobuf.Empty\032\034.google.protobuf.Stri"
|
|
||||||
"ngValue\022;\n\tReportBug\022\026.grpc.ReportBugReq"
|
|
||||||
"uest\032\026.google.protobuf.Empty\022M\n\025ExportTL"
|
|
||||||
"SCertificates\022\034.google.protobuf.StringVa"
|
|
||||||
"lue\032\026.google.protobuf.Empty\022E\n\rForceLaun"
|
|
||||||
"cher\022\034.google.protobuf.StringValue\032\026.goo"
|
|
||||||
"gle.protobuf.Empty\022I\n\021SetMainExecutable\022"
|
|
||||||
"\034.google.protobuf.StringValue\032\026.google.p"
|
|
||||||
"rotobuf.Empty\0223\n\005Login\022\022.grpc.LoginReque"
|
|
||||||
"st\032\026.google.protobuf.Empty\0226\n\010Login2FA\022\022"
|
|
||||||
".grpc.LoginRequest\032\026.google.protobuf.Emp"
|
|
||||||
"ty\022=\n\017Login2Passwords\022\022.grpc.LoginReques"
|
|
||||||
"t\032\026.google.protobuf.Empty\022=\n\nLoginAbort\022"
|
|
||||||
"\027.grpc.LoginAbortRequest\032\026.google.protob"
|
|
||||||
"uf.Empty\022=\n\013CheckUpdate\022\026.google.protobu"
|
|
||||||
"f.Empty\032\026.google.protobuf.Empty\022\?\n\rInsta"
|
|
||||||
"llUpdate\022\026.google.protobuf.Empty\032\026.googl"
|
|
||||||
"e.protobuf.Empty\022L\n\026SetIsAutomaticUpdate"
|
|
||||||
"On\022\032.google.protobuf.BoolValue\032\026.google."
|
|
||||||
"protobuf.Empty\022I\n\023IsAutomaticUpdateOn\022\026."
|
|
||||||
"google.protobuf.Empty\032\032.google.protobuf."
|
|
||||||
"BoolValue\022E\n\rDiskCachePath\022\026.google.prot"
|
|
||||||
"obuf.Empty\032\034.google.protobuf.StringValue"
|
"obuf.Empty\032\034.google.protobuf.StringValue"
|
||||||
"\022H\n\020SetDiskCachePath\022\034.google.protobuf.S"
|
"\022>\n\014TriggerReset\022\026.google.protobuf.Empty"
|
||||||
"tringValue\032\026.google.protobuf.Empty\022E\n\017Se"
|
"\032\026.google.protobuf.Empty\022\?\n\007Version\022\026.go"
|
||||||
"tIsDoHEnabled\022\032.google.protobuf.BoolValu"
|
"ogle.protobuf.Empty\032\034.google.protobuf.St"
|
||||||
"e\032\026.google.protobuf.Empty\022B\n\014IsDoHEnable"
|
"ringValue\022@\n\010LogsPath\022\026.google.protobuf."
|
||||||
"d\022\026.google.protobuf.Empty\032\032.google.proto"
|
"Empty\032\034.google.protobuf.StringValue\022C\n\013L"
|
||||||
"buf.BoolValue\022D\n\022MailServerSettings\022\026.go"
|
"icensePath\022\026.google.protobuf.Empty\032\034.goo"
|
||||||
"ogle.protobuf.Empty\032\026.grpc.ImapSmtpSetti"
|
"gle.protobuf.StringValue\022L\n\024ReleaseNotes"
|
||||||
"ngs\022G\n\025SetMailServerSettings\022\026.grpc.Imap"
|
"PageLink\022\026.google.protobuf.Empty\032\034.googl"
|
||||||
"SmtpSettings\032\026.google.protobuf.Empty\022@\n\010"
|
"e.protobuf.StringValue\022N\n\026DependencyLice"
|
||||||
"Hostname\022\026.google.protobuf.Empty\032\034.googl"
|
"nsesLink\022\026.google.protobuf.Empty\032\034.googl"
|
||||||
"e.protobuf.StringValue\022E\n\nIsPortFree\022\033.g"
|
"e.protobuf.StringValue\022G\n\017LandingPageLin"
|
||||||
"oogle.protobuf.Int32Value\032\032.google.proto"
|
"k\022\026.google.protobuf.Empty\032\034.google.proto"
|
||||||
"buf.BoolValue\022N\n\022AvailableKeychains\022\026.go"
|
"buf.StringValue\022J\n\022SetColorSchemeName\022\034."
|
||||||
"ogle.protobuf.Empty\032 .grpc.AvailableKeyc"
|
"google.protobuf.StringValue\032\026.google.pro"
|
||||||
"hainsResponse\022J\n\022SetCurrentKeychain\022\034.go"
|
"tobuf.Empty\022G\n\017ColorSchemeName\022\026.google."
|
||||||
"ogle.protobuf.StringValue\032\026.google.proto"
|
"protobuf.Empty\032\034.google.protobuf.StringV"
|
||||||
"buf.Empty\022G\n\017CurrentKeychain\022\026.google.pr"
|
"alue\022J\n\022CurrentEmailClient\022\026.google.prot"
|
||||||
"otobuf.Empty\032\034.google.protobuf.StringVal"
|
"obuf.Empty\032\034.google.protobuf.StringValue"
|
||||||
"ue\022=\n\013GetUserList\022\026.google.protobuf.Empt"
|
"\022;\n\tReportBug\022\026.grpc.ReportBugRequest\032\026."
|
||||||
"y\032\026.grpc.UserListResponse\0223\n\007GetUser\022\034.g"
|
"google.protobuf.Empty\022M\n\025ExportTLSCertif"
|
||||||
"oogle.protobuf.StringValue\032\n.grpc.User\022F"
|
"icates\022\034.google.protobuf.StringValue\032\026.g"
|
||||||
"\n\020SetUserSplitMode\022\032.grpc.UserSplitModeR"
|
"oogle.protobuf.Empty\022E\n\rForceLauncher\022\034."
|
||||||
"equest\032\026.google.protobuf.Empty\022U\n\030SendBa"
|
"google.protobuf.StringValue\032\026.google.pro"
|
||||||
"dEventUserFeedback\022!.grpc.UserBadEventFe"
|
"tobuf.Empty\022I\n\021SetMainExecutable\022\034.googl"
|
||||||
"edbackRequest\032\026.google.protobuf.Empty\022B\n"
|
"e.protobuf.StringValue\032\026.google.protobuf"
|
||||||
"\nLogoutUser\022\034.google.protobuf.StringValu"
|
".Empty\0223\n\005Login\022\022.grpc.LoginRequest\032\026.go"
|
||||||
"e\032\026.google.protobuf.Empty\022B\n\nRemoveUser\022"
|
"ogle.protobuf.Empty\0226\n\010Login2FA\022\022.grpc.L"
|
||||||
"\034.google.protobuf.StringValue\032\026.google.p"
|
"oginRequest\032\026.google.protobuf.Empty\022=\n\017L"
|
||||||
"rotobuf.Empty\022Q\n\026ConfigureUserAppleMail\022"
|
"ogin2Passwords\022\022.grpc.LoginRequest\032\026.goo"
|
||||||
"\037.grpc.ConfigureAppleMailRequest\032\026.googl"
|
"gle.protobuf.Empty\022=\n\nLoginAbort\022\027.grpc."
|
||||||
"e.protobuf.Empty\022\?\n\016RunEventStream\022\030.grp"
|
"LoginAbortRequest\032\026.google.protobuf.Empt"
|
||||||
"c.EventStreamRequest\032\021.grpc.StreamEvent0"
|
"y\022=\n\013CheckUpdate\022\026.google.protobuf.Empty"
|
||||||
"\001\022A\n\017StopEventStream\022\026.google.protobuf.E"
|
"\032\026.google.protobuf.Empty\022\?\n\rInstallUpdat"
|
||||||
"mpty\032\026.google.protobuf.EmptyB6Z4github.c"
|
"e\022\026.google.protobuf.Empty\032\026.google.proto"
|
||||||
"om/ProtonMail/proton-bridge/v3/internal/"
|
"buf.Empty\022L\n\026SetIsAutomaticUpdateOn\022\032.go"
|
||||||
"grpcb\006proto3"
|
"ogle.protobuf.BoolValue\032\026.google.protobu"
|
||||||
|
"f.Empty\022I\n\023IsAutomaticUpdateOn\022\026.google."
|
||||||
|
"protobuf.Empty\032\032.google.protobuf.BoolVal"
|
||||||
|
"ue\022E\n\rDiskCachePath\022\026.google.protobuf.Em"
|
||||||
|
"pty\032\034.google.protobuf.StringValue\022H\n\020Set"
|
||||||
|
"DiskCachePath\022\034.google.protobuf.StringVa"
|
||||||
|
"lue\032\026.google.protobuf.Empty\022E\n\017SetIsDoHE"
|
||||||
|
"nabled\022\032.google.protobuf.BoolValue\032\026.goo"
|
||||||
|
"gle.protobuf.Empty\022B\n\014IsDoHEnabled\022\026.goo"
|
||||||
|
"gle.protobuf.Empty\032\032.google.protobuf.Boo"
|
||||||
|
"lValue\022D\n\022MailServerSettings\022\026.google.pr"
|
||||||
|
"otobuf.Empty\032\026.grpc.ImapSmtpSettings\022G\n\025"
|
||||||
|
"SetMailServerSettings\022\026.grpc.ImapSmtpSet"
|
||||||
|
"tings\032\026.google.protobuf.Empty\022@\n\010Hostnam"
|
||||||
|
"e\022\026.google.protobuf.Empty\032\034.google.proto"
|
||||||
|
"buf.StringValue\022E\n\nIsPortFree\022\033.google.p"
|
||||||
|
"rotobuf.Int32Value\032\032.google.protobuf.Boo"
|
||||||
|
"lValue\022N\n\022AvailableKeychains\022\026.google.pr"
|
||||||
|
"otobuf.Empty\032 .grpc.AvailableKeychainsRe"
|
||||||
|
"sponse\022J\n\022SetCurrentKeychain\022\034.google.pr"
|
||||||
|
"otobuf.StringValue\032\026.google.protobuf.Emp"
|
||||||
|
"ty\022G\n\017CurrentKeychain\022\026.google.protobuf."
|
||||||
|
"Empty\032\034.google.protobuf.StringValue\022=\n\013G"
|
||||||
|
"etUserList\022\026.google.protobuf.Empty\032\026.grp"
|
||||||
|
"c.UserListResponse\0223\n\007GetUser\022\034.google.p"
|
||||||
|
"rotobuf.StringValue\032\n.grpc.User\022F\n\020SetUs"
|
||||||
|
"erSplitMode\022\032.grpc.UserSplitModeRequest\032"
|
||||||
|
"\026.google.protobuf.Empty\022U\n\030SendBadEventU"
|
||||||
|
"serFeedback\022!.grpc.UserBadEventFeedbackR"
|
||||||
|
"equest\032\026.google.protobuf.Empty\022B\n\nLogout"
|
||||||
|
"User\022\034.google.protobuf.StringValue\032\026.goo"
|
||||||
|
"gle.protobuf.Empty\022B\n\nRemoveUser\022\034.googl"
|
||||||
|
"e.protobuf.StringValue\032\026.google.protobuf"
|
||||||
|
".Empty\022Q\n\026ConfigureUserAppleMail\022\037.grpc."
|
||||||
|
"ConfigureAppleMailRequest\032\026.google.proto"
|
||||||
|
"buf.Empty\022\?\n\016RunEventStream\022\030.grpc.Event"
|
||||||
|
"StreamRequest\032\021.grpc.StreamEvent0\001\022A\n\017St"
|
||||||
|
"opEventStream\022\026.google.protobuf.Empty\032\026."
|
||||||
|
"google.protobuf.EmptyB6Z4github.com/Prot"
|
||||||
|
"onMail/proton-bridge/v3/internal/grpcb\006p"
|
||||||
|
"roto3"
|
||||||
;
|
;
|
||||||
static const ::_pbi::DescriptorTable* const descriptor_table_bridge_2eproto_deps[2] = {
|
static const ::_pbi::DescriptorTable* const descriptor_table_bridge_2eproto_deps[2] = {
|
||||||
&::descriptor_table_google_2fprotobuf_2fempty_2eproto,
|
&::descriptor_table_google_2fprotobuf_2fempty_2eproto,
|
||||||
@ -1778,7 +1782,7 @@ static const ::_pbi::DescriptorTable* const descriptor_table_bridge_2eproto_deps
|
|||||||
};
|
};
|
||||||
static ::_pbi::once_flag descriptor_table_bridge_2eproto_once;
|
static ::_pbi::once_flag descriptor_table_bridge_2eproto_once;
|
||||||
const ::_pbi::DescriptorTable descriptor_table_bridge_2eproto = {
|
const ::_pbi::DescriptorTable descriptor_table_bridge_2eproto = {
|
||||||
false, false, 10532, descriptor_table_protodef_bridge_2eproto,
|
false, false, 10685, descriptor_table_protodef_bridge_2eproto,
|
||||||
"bridge.proto",
|
"bridge.proto",
|
||||||
&descriptor_table_bridge_2eproto_once, descriptor_table_bridge_2eproto_deps, 2, 64,
|
&descriptor_table_bridge_2eproto_once, descriptor_table_bridge_2eproto_deps, 2, 64,
|
||||||
schemas, file_default_instances, TableStruct_bridge_2eproto::offsets,
|
schemas, file_default_instances, TableStruct_bridge_2eproto::offsets,
|
||||||
|
|||||||
@ -17,8 +17,8 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "LogUtils.h"
|
#include "LogUtils.h"
|
||||||
#include <bridgepp/BridgeUtils.h>
|
#include "../BridgeUtils.h"
|
||||||
#include <bridgepp/Exception/Exception.h>
|
#include "../Exception/Exception.h"
|
||||||
|
|
||||||
|
|
||||||
namespace bridgepp {
|
namespace bridgepp {
|
||||||
@ -43,7 +43,12 @@ QString latestBridgeLogPath() {
|
|||||||
if (logsDir.isEmpty()) {
|
if (logsDir.isEmpty()) {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QFileInfoList files = logsDir.entryInfoList({ "v*.log" }, QDir::Files); // could do sorting, but only by last modification time. we want to sort by creation time.
|
QFileInfoList files = logsDir.entryInfoList({ "v*.log" }, QDir::Files); // could do sorting, but only by last modification time. we want to sort by creation time.
|
||||||
|
if (files.isEmpty()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
std::sort(files.begin(), files.end(), [](QFileInfo const &lhs, QFileInfo const &rhs) -> bool {
|
std::sort(files.begin(), files.end(), [](QFileInfo const &lhs, QFileInfo const &rhs) -> bool {
|
||||||
return lhs.birthTime() < rhs.birthTime();
|
return lhs.birthTime() < rhs.birthTime();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -289,6 +289,23 @@ func New(
|
|||||||
})
|
})
|
||||||
fe.AddCmd(badEventCmd)
|
fe.AddCmd(badEventCmd)
|
||||||
|
|
||||||
|
// Telemetry commands
|
||||||
|
telemetryCmd := &ishell.Cmd{
|
||||||
|
Name: "telemetry",
|
||||||
|
Help: "choose whether usage diagnostics are collected or not",
|
||||||
|
}
|
||||||
|
telemetryCmd.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "enable",
|
||||||
|
Help: "Usage diagnostics collection will be enabled",
|
||||||
|
Func: fe.enableTelemetry,
|
||||||
|
})
|
||||||
|
telemetryCmd.AddCmd(&ishell.Cmd{
|
||||||
|
Name: "disable",
|
||||||
|
Help: "Usage diagnostics collection will be disabled",
|
||||||
|
Func: fe.disableTelemetry,
|
||||||
|
})
|
||||||
|
fe.AddCmd(telemetryCmd)
|
||||||
|
|
||||||
go fe.watchEvents(eventCh)
|
go fe.watchEvents(eventCh)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
@ -40,7 +40,7 @@ func (f *frontendCLI) printLogDir(c *ishell.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *frontendCLI) printManual(c *ishell.Context) {
|
func (f *frontendCLI) printManual(c *ishell.Context) {
|
||||||
f.Println("More instructions about the Bridge can be found at\n\n https://protonmail.com/bridge")
|
f.Println("More instructions about the Bridge can be found at\n\n https://proton.me/mail/bridge")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *frontendCLI) printCredits(c *ishell.Context) {
|
func (f *frontendCLI) printCredits(c *ishell.Context) {
|
||||||
@ -195,6 +195,38 @@ func (f *frontendCLI) showAllMail(c *ishell.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *frontendCLI) enableTelemetry(_ *ishell.Context) {
|
||||||
|
if !f.bridge.GetTelemetryDisabled() {
|
||||||
|
f.Println("Usage diagnostics collection is enabled.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Println("Usage diagnostics collection is disabled right now.")
|
||||||
|
|
||||||
|
if f.yesNoQuestion("Do you want to enable usage diagnostics collection") {
|
||||||
|
if err := f.bridge.SetTelemetryDisabled(false); err != nil {
|
||||||
|
f.printAndLogError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *frontendCLI) disableTelemetry(_ *ishell.Context) {
|
||||||
|
if f.bridge.GetTelemetryDisabled() {
|
||||||
|
f.Println("Usage diagnostics collection is disabled.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Println("Usage diagnostics collection is enabled right now.")
|
||||||
|
|
||||||
|
if f.yesNoQuestion("Do you want to disable usage diagnostics collection") {
|
||||||
|
if err := f.bridge.SetTelemetryDisabled(true); err != nil {
|
||||||
|
f.printAndLogError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (f *frontendCLI) setGluonLocation(c *ishell.Context) {
|
func (f *frontendCLI) setGluonLocation(c *ishell.Context) {
|
||||||
if gluonDir := f.bridge.GetGluonCacheDir(); gluonDir != "" {
|
if gluonDir := f.bridge.GetGluonCacheDir(); gluonDir != "" {
|
||||||
f.Println("The current message cache location is:", gluonDir)
|
f.Println("The current message cache location is:", gluonDir)
|
||||||
|
|||||||
@ -17,8 +17,8 @@
|
|||||||
|
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.28.0
|
||||||
// protoc v3.21.12
|
// protoc v3.21.3
|
||||||
// source: bridge.proto
|
// source: bridge.proto
|
||||||
|
|
||||||
package grpc
|
package grpc
|
||||||
@ -4803,8 +4803,8 @@ var file_bridge_proto_rawDesc = []byte{
|
|||||||
0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x54, 0x4c, 0x53,
|
0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x54, 0x4c, 0x53,
|
||||||
0x5f, 0x43, 0x45, 0x52, 0x54, 0x5f, 0x45, 0x58, 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x45, 0x52, 0x52,
|
0x5f, 0x43, 0x45, 0x52, 0x54, 0x5f, 0x45, 0x58, 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x45, 0x52, 0x52,
|
||||||
0x4f, 0x52, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x4c, 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x5f,
|
0x4f, 0x52, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x4c, 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x5f,
|
||||||
0x45, 0x58, 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x32, 0xf0,
|
0x45, 0x58, 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x32, 0x89,
|
||||||
0x1d, 0x0a, 0x06, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65, 0x12, 0x49, 0x0a, 0x0b, 0x43, 0x68, 0x65,
|
0x1f, 0x0a, 0x06, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65, 0x12, 0x49, 0x0a, 0x0b, 0x43, 0x68, 0x65,
|
||||||
0x63, 0x6b, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
0x63, 0x6b, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||||
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e,
|
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e,
|
||||||
0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||||
@ -4855,199 +4855,208 @@ var file_bridge_proto_rawDesc = []byte{
|
|||||||
0x6c, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
0x6c, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
|
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
|
||||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f,
|
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f,
|
||||||
0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3c, 0x0a, 0x04, 0x47, 0x6f, 0x4f, 0x73, 0x12, 0x16,
|
0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x4c, 0x0a, 0x16, 0x53, 0x65, 0x74, 0x49, 0x73, 0x54,
|
||||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64,
|
||||||
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
0x12, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56,
|
0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67,
|
||||||
0x61, 0x6c, 0x75, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x52,
|
|
||||||
0x65, 0x73, 0x65, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
|
|
||||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67,
|
|
||||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
|
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
|
||||||
0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12,
|
0x6d, 0x70, 0x74, 0x79, 0x12, 0x49, 0x0a, 0x13, 0x49, 0x73, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65,
|
||||||
|
0x74, 0x72, 0x79, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x16, 0x2e, 0x67, 0x6f,
|
||||||
|
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
|
||||||
|
0x70, 0x74, 0x79, 0x1a, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12,
|
||||||
|
0x3c, 0x0a, 0x04, 0x47, 0x6f, 0x4f, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||||
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a,
|
||||||
|
0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||||
|
0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3e, 0x0a,
|
||||||
|
0x0c, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x52, 0x65, 0x73, 0x65, 0x74, 0x12, 0x16, 0x2e,
|
||||||
|
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||||
|
0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||||
|
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a,
|
||||||
|
0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||||
|
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||||
|
0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
|
0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x40,
|
||||||
|
0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x73, 0x50, 0x61, 0x74, 0x68, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
|
||||||
|
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
|
||||||
|
0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65,
|
||||||
|
0x12, 0x43, 0x0a, 0x0b, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12,
|
||||||
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||||
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67,
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67,
|
||||||
0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x40, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x73, 0x50, 0x61, 0x74,
|
0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x4c, 0x0a, 0x14, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65,
|
||||||
0x68, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
0x4e, 0x6f, 0x74, 0x65, 0x73, 0x50, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x16, 0x2e,
|
||||||
0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
|
||||||
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69,
|
|
||||||
0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x43, 0x0a, 0x0b, 0x4c, 0x69, 0x63, 0x65, 0x6e,
|
|
||||||
0x73, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c,
|
|
||||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
|
||||||
0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x4c, 0x0a, 0x14,
|
|
||||||
0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x74, 0x65, 0x73, 0x50, 0x61, 0x67, 0x65,
|
|
||||||
0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
|
|
||||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x67,
|
|
||||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53,
|
|
||||||
0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x4e, 0x0a, 0x16, 0x44, 0x65,
|
|
||||||
0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x73,
|
|
||||||
0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
|
|
||||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x67,
|
|
||||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53,
|
|
||||||
0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x47, 0x0a, 0x0f, 0x4c, 0x61,
|
|
||||||
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x16, 0x2e,
|
|
||||||
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||||
0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61,
|
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61,
|
||||||
0x6c, 0x75, 0x65, 0x12, 0x4a, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53,
|
0x6c, 0x75, 0x65, 0x12, 0x4e, 0x0a, 0x16, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63,
|
||||||
0x63, 0x68, 0x65, 0x6d, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
0x79, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x16, 0x2e,
|
||||||
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69,
|
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||||
0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12,
|
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61,
|
||||||
0x47, 0x0a, 0x0f, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x4e, 0x61,
|
0x6c, 0x75, 0x65, 0x12, 0x47, 0x0a, 0x0f, 0x4c, 0x61, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61,
|
||||||
0x6d, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
0x67, 0x65, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||||
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f,
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c,
|
||||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72,
|
|
||||||
0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x4a, 0x0a, 0x12, 0x43, 0x75, 0x72, 0x72,
|
|
||||||
0x65, 0x6e, 0x74, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x16,
|
|
||||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||||
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x4a, 0x0a, 0x12,
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56,
|
0x53, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x4e, 0x61,
|
||||||
0x61, 0x6c, 0x75, 0x65, 0x12, 0x3b, 0x0a, 0x09, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x75,
|
0x6d, 0x65, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
0x67, 0x12, 0x16, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x42,
|
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65,
|
||||||
0x75, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
|
0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x47, 0x0a, 0x0f, 0x43, 0x6f, 0x6c, 0x6f,
|
||||||
0x79, 0x12, 0x4d, 0x0a, 0x15, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x4c, 0x53, 0x43, 0x65,
|
0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f,
|
||||||
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f,
|
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
|
||||||
|
0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75,
|
||||||
|
0x65, 0x12, 0x4a, 0x0a, 0x12, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x6d, 0x61, 0x69,
|
||||||
|
0x6c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||||
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a,
|
||||||
|
0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||||
|
0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3b, 0x0a,
|
||||||
|
0x09, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x75, 0x67, 0x12, 0x16, 0x2e, 0x67, 0x72, 0x70,
|
||||||
|
0x63, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x75, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
|
0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4d, 0x0a, 0x15, 0x45, 0x78,
|
||||||
|
0x70, 0x6f, 0x72, 0x74, 0x54, 0x4c, 0x53, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
|
||||||
|
0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75,
|
||||||
|
0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
|
0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x45, 0x0a, 0x0d, 0x46, 0x6f, 0x72,
|
||||||
|
0x63, 0x65, 0x4c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f,
|
||||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72,
|
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72,
|
||||||
0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||||
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||||
0x12, 0x45, 0x0a, 0x0d, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x4c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x65,
|
0x12, 0x49, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x4d, 0x61, 0x69, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x75,
|
||||||
0x72, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||||
0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a,
|
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61,
|
||||||
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x49, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x4d, 0x61,
|
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x4c,
|
||||||
0x69, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1c, 0x2e, 0x67,
|
0x6f, 0x67, 0x69, 0x6e, 0x12, 0x12, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x67, 0x69,
|
||||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53,
|
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||||
0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
|
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
|
0x12, 0x36, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x32, 0x46, 0x41, 0x12, 0x12, 0x2e, 0x67,
|
||||||
0x74, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x12, 0x2e, 0x67, 0x72,
|
0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||||
|
0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
|
0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3d, 0x0a, 0x0f, 0x4c, 0x6f, 0x67, 0x69,
|
||||||
|
0x6e, 0x32, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x12, 0x2e, 0x67, 0x72,
|
||||||
0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||||
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||||
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x36, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x69, 0x6e,
|
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3d, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x69, 0x6e,
|
||||||
0x32, 0x46, 0x41, 0x12, 0x12, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e,
|
0x41, 0x62, 0x6f, 0x72, 0x74, 0x12, 0x17, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x67,
|
||||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
0x69, 0x6e, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16,
|
||||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12,
|
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||||
0x3d, 0x0a, 0x0f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x32, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
|
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3d, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55,
|
||||||
0x64, 0x73, 0x12, 0x12, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52,
|
0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e,
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3d,
|
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||||
0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x12, 0x17, 0x2e, 0x67,
|
0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x0d, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c,
|
||||||
0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x52, 0x65,
|
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16,
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3d, 0x0a,
|
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||||
0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x16, 0x2e, 0x67,
|
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4c, 0x0a, 0x16, 0x53, 0x65, 0x74, 0x49, 0x73, 0x41,
|
||||||
|
0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4f, 0x6e,
|
||||||
|
0x12, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
|
0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67,
|
||||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
|
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
|
||||||
0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
|
0x6d, 0x70, 0x74, 0x79, 0x12, 0x49, 0x0a, 0x13, 0x49, 0x73, 0x41, 0x75, 0x74, 0x6f, 0x6d, 0x61,
|
||||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x0d,
|
0x74, 0x69, 0x63, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4f, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f,
|
||||||
0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x16, 0x2e,
|
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
|
||||||
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
0x70, 0x74, 0x79, 0x1a, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12,
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4c, 0x0a,
|
0x45, 0x0a, 0x0d, 0x44, 0x69, 0x73, 0x6b, 0x43, 0x61, 0x63, 0x68, 0x65, 0x50, 0x61, 0x74, 0x68,
|
||||||
0x16, 0x53, 0x65, 0x74, 0x49, 0x73, 0x41, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x55,
|
|
||||||
0x70, 0x64, 0x61, 0x74, 0x65, 0x4f, 0x6e, 0x12, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
|
||||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61,
|
|
||||||
0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
|
||||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x49, 0x0a, 0x13, 0x49,
|
|
||||||
0x73, 0x41, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
|
|
||||||
0x4f, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
|
||||||
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
|
|
||||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f,
|
|
||||||
0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x45, 0x0a, 0x0d, 0x44, 0x69, 0x73, 0x6b, 0x43, 0x61,
|
|
||||||
0x63, 0x68, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
|
||||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a,
|
|
||||||
0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
|
||||||
0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x48, 0x0a,
|
|
||||||
0x10, 0x53, 0x65, 0x74, 0x44, 0x69, 0x73, 0x6b, 0x43, 0x61, 0x63, 0x68, 0x65, 0x50, 0x61, 0x74,
|
|
||||||
0x68, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
|
||||||
0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a,
|
|
||||||
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
|
||||||
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x45, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x49, 0x73,
|
|
||||||
0x44, 0x6f, 0x48, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
|
|
||||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f,
|
|
||||||
0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x42,
|
|
||||||
0x0a, 0x0c, 0x49, 0x73, 0x44, 0x6f, 0x48, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x16,
|
|
||||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
|
||||||
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c,
|
|
||||||
0x75, 0x65, 0x12, 0x44, 0x0a, 0x12, 0x4d, 0x61, 0x69, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
|
|
||||||
0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
|
||||||
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
|
||||||
0x1a, 0x16, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6d, 0x61, 0x70, 0x53, 0x6d, 0x74, 0x70,
|
|
||||||
0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x47, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x4d,
|
|
||||||
0x61, 0x69, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
|
|
||||||
0x73, 0x12, 0x16, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6d, 0x61, 0x70, 0x53, 0x6d, 0x74,
|
|
||||||
0x70, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
|
||||||
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
|
|
||||||
0x79, 0x12, 0x40, 0x0a, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x2e,
|
|
||||||
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
|
||||||
0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61,
|
|
||||||
0x6c, 0x75, 0x65, 0x12, 0x45, 0x0a, 0x0a, 0x49, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x46, 0x72, 0x65,
|
|
||||||
0x65, 0x12, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
|
||||||
0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x1a,
|
|
||||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
|
||||||
0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x4e, 0x0a, 0x12, 0x41, 0x76,
|
|
||||||
0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x4b, 0x65, 0x79, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73,
|
|
||||||
0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x20, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e,
|
0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||||
0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x4b, 0x65, 0x79, 0x63, 0x68, 0x61, 0x69,
|
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e,
|
||||||
0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x12, 0x53, 0x65,
|
0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x48, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x44, 0x69, 0x73,
|
||||||
0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x63, 0x68, 0x61, 0x69, 0x6e,
|
0x6b, 0x43, 0x61, 0x63, 0x68, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f,
|
||||||
0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72,
|
||||||
0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16,
|
0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||||
|
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||||
|
0x12, 0x45, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x49, 0x73, 0x44, 0x6f, 0x48, 0x45, 0x6e, 0x61, 0x62,
|
||||||
|
0x6c, 0x65, 0x64, 0x12, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a,
|
||||||
|
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||||
|
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x42, 0x0a, 0x0c, 0x49, 0x73, 0x44, 0x6f, 0x48,
|
||||||
|
0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||||
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a,
|
||||||
|
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||||
|
0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x44, 0x0a, 0x12, 0x4d,
|
||||||
|
0x61, 0x69, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
|
||||||
|
0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
|
0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x72, 0x70, 0x63,
|
||||||
|
0x2e, 0x49, 0x6d, 0x61, 0x70, 0x53, 0x6d, 0x74, 0x70, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
|
||||||
|
0x73, 0x12, 0x47, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x4d, 0x61, 0x69, 0x6c, 0x53, 0x65, 0x72, 0x76,
|
||||||
|
0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x72, 0x70,
|
||||||
|
0x63, 0x2e, 0x49, 0x6d, 0x61, 0x70, 0x53, 0x6d, 0x74, 0x70, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
|
||||||
|
0x67, 0x73, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x40, 0x0a, 0x08, 0x48, 0x6f,
|
||||||
|
0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||||
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c,
|
||||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||||
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x47, 0x0a, 0x0f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e,
|
0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x45, 0x0a, 0x0a,
|
||||||
0x74, 0x4b, 0x65, 0x79, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
0x49, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x46, 0x72, 0x65, 0x65, 0x12, 0x1b, 0x2e, 0x67, 0x6f, 0x6f,
|
||||||
|
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74,
|
||||||
|
0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||||
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61,
|
||||||
|
0x6c, 0x75, 0x65, 0x12, 0x4e, 0x0a, 0x12, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65,
|
||||||
|
0x4b, 0x65, 0x79, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
||||||
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
|
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
|
||||||
0x79, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
0x79, 0x1a, 0x20, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62,
|
||||||
0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12,
|
0x6c, 0x65, 0x4b, 0x65, 0x79, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||||
0x3d, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16,
|
0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e,
|
||||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
0x74, 0x4b, 0x65, 0x79, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
||||||
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x73,
|
|
||||||
0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33,
|
|
||||||
0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
|
||||||
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69,
|
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69,
|
||||||
0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x0a, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x55,
|
0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||||
0x73, 0x65, 0x72, 0x12, 0x46, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x70,
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12,
|
||||||
0x6c, 0x69, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1a, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x55,
|
0x47, 0x0a, 0x0f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x63, 0x68, 0x61,
|
||||||
0x73, 0x65, 0x72, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75,
|
0x69, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f,
|
||||||
|
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72,
|
||||||
|
0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3d, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x55,
|
||||||
|
0x73, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||||
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a,
|
||||||
|
0x16, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52,
|
||||||
|
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73,
|
||||||
|
0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65,
|
||||||
|
0x1a, 0x0a, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x12, 0x46, 0x0a, 0x10,
|
||||||
|
0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x4d, 0x6f, 0x64, 0x65,
|
||||||
|
0x12, 0x1a, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53, 0x70, 0x6c, 0x69,
|
||||||
|
0x74, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67,
|
||||||
|
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
|
||||||
|
0x6d, 0x70, 0x74, 0x79, 0x12, 0x55, 0x0a, 0x18, 0x53, 0x65, 0x6e, 0x64, 0x42, 0x61, 0x64, 0x45,
|
||||||
|
0x76, 0x65, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x46, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b,
|
||||||
|
0x12, 0x21, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x42, 0x61, 0x64, 0x45,
|
||||||
|
0x76, 0x65, 0x6e, 0x74, 0x46, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75,
|
||||||
0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x55, 0x0a, 0x18, 0x53,
|
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x42, 0x0a, 0x0a, 0x4c,
|
||||||
0x65, 0x6e, 0x64, 0x42, 0x61, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x72, 0x46,
|
0x6f, 0x67, 0x6f, 0x75, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
||||||
0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x21, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x55,
|
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69,
|
||||||
0x73, 0x65, 0x72, 0x42, 0x61, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x46, 0x65, 0x65, 0x64, 0x62,
|
0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||||
0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12,
|
||||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
|
0x42, 0x0a, 0x0a, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1c, 0x2e,
|
||||||
0x74, 0x79, 0x12, 0x42, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x55, 0x73, 0x65, 0x72,
|
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||||
0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f,
|
||||||
0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16,
|
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
|
||||||
|
0x70, 0x74, 0x79, 0x12, 0x51, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65,
|
||||||
|
0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x4d, 0x61, 0x69, 0x6c, 0x12, 0x1f, 0x2e,
|
||||||
|
0x67, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x41, 0x70,
|
||||||
|
0x70, 0x6c, 0x65, 0x4d, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16,
|
||||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||||
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x42, 0x0a, 0x0a, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65,
|
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x0e, 0x52, 0x75, 0x6e, 0x45, 0x76, 0x65,
|
||||||
0x55, 0x73, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
|
0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x18, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e,
|
||||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c,
|
0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
0x73, 0x74, 0x1a, 0x11, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d,
|
||||||
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x51, 0x0a, 0x16, 0x43, 0x6f,
|
0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x41, 0x0a, 0x0f, 0x53, 0x74, 0x6f, 0x70, 0x45,
|
||||||
0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, 0x65,
|
0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
|
||||||
0x4d, 0x61, 0x69, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x66,
|
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
|
||||||
0x69, 0x67, 0x75, 0x72, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x4d, 0x61, 0x69, 0x6c, 0x52, 0x65,
|
0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69,
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a,
|
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x4d,
|
||||||
0x0e, 0x52, 0x75, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12,
|
0x61, 0x69, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2d, 0x62, 0x72, 0x69, 0x64, 0x67,
|
||||||
0x18, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65,
|
0x65, 0x2f, 0x76, 0x33, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x72,
|
||||||
0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x67, 0x72, 0x70, 0x63,
|
0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x41,
|
|
||||||
0x0a, 0x0f, 0x53, 0x74, 0x6f, 0x70, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61,
|
|
||||||
0x6d, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
|
||||||
0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
|
||||||
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
|
|
||||||
0x79, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
|
|
||||||
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x4d, 0x61, 0x69, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
|
||||||
0x6e, 0x2d, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x2f, 0x76, 0x33, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
|
||||||
0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
|
||||||
0x33,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -5214,106 +5223,110 @@ var file_bridge_proto_depIdxs = []int32{
|
|||||||
72, // 69: grpc.Bridge.IsBetaEnabled:input_type -> google.protobuf.Empty
|
72, // 69: grpc.Bridge.IsBetaEnabled:input_type -> google.protobuf.Empty
|
||||||
73, // 70: grpc.Bridge.SetIsAllMailVisible:input_type -> google.protobuf.BoolValue
|
73, // 70: grpc.Bridge.SetIsAllMailVisible:input_type -> google.protobuf.BoolValue
|
||||||
72, // 71: grpc.Bridge.IsAllMailVisible:input_type -> google.protobuf.Empty
|
72, // 71: grpc.Bridge.IsAllMailVisible:input_type -> google.protobuf.Empty
|
||||||
72, // 72: grpc.Bridge.GoOs:input_type -> google.protobuf.Empty
|
73, // 72: grpc.Bridge.SetIsTelemetryDisabled:input_type -> google.protobuf.BoolValue
|
||||||
72, // 73: grpc.Bridge.TriggerReset:input_type -> google.protobuf.Empty
|
72, // 73: grpc.Bridge.IsTelemetryDisabled:input_type -> google.protobuf.Empty
|
||||||
72, // 74: grpc.Bridge.Version:input_type -> google.protobuf.Empty
|
72, // 74: grpc.Bridge.GoOs:input_type -> google.protobuf.Empty
|
||||||
72, // 75: grpc.Bridge.LogsPath:input_type -> google.protobuf.Empty
|
72, // 75: grpc.Bridge.TriggerReset:input_type -> google.protobuf.Empty
|
||||||
72, // 76: grpc.Bridge.LicensePath:input_type -> google.protobuf.Empty
|
72, // 76: grpc.Bridge.Version:input_type -> google.protobuf.Empty
|
||||||
72, // 77: grpc.Bridge.ReleaseNotesPageLink:input_type -> google.protobuf.Empty
|
72, // 77: grpc.Bridge.LogsPath:input_type -> google.protobuf.Empty
|
||||||
72, // 78: grpc.Bridge.DependencyLicensesLink:input_type -> google.protobuf.Empty
|
72, // 78: grpc.Bridge.LicensePath:input_type -> google.protobuf.Empty
|
||||||
72, // 79: grpc.Bridge.LandingPageLink:input_type -> google.protobuf.Empty
|
72, // 79: grpc.Bridge.ReleaseNotesPageLink:input_type -> google.protobuf.Empty
|
||||||
71, // 80: grpc.Bridge.SetColorSchemeName:input_type -> google.protobuf.StringValue
|
72, // 80: grpc.Bridge.DependencyLicensesLink:input_type -> google.protobuf.Empty
|
||||||
72, // 81: grpc.Bridge.ColorSchemeName:input_type -> google.protobuf.Empty
|
72, // 81: grpc.Bridge.LandingPageLink:input_type -> google.protobuf.Empty
|
||||||
72, // 82: grpc.Bridge.CurrentEmailClient:input_type -> google.protobuf.Empty
|
71, // 82: grpc.Bridge.SetColorSchemeName:input_type -> google.protobuf.StringValue
|
||||||
9, // 83: grpc.Bridge.ReportBug:input_type -> grpc.ReportBugRequest
|
72, // 83: grpc.Bridge.ColorSchemeName:input_type -> google.protobuf.Empty
|
||||||
71, // 84: grpc.Bridge.ExportTLSCertificates:input_type -> google.protobuf.StringValue
|
72, // 84: grpc.Bridge.CurrentEmailClient:input_type -> google.protobuf.Empty
|
||||||
71, // 85: grpc.Bridge.ForceLauncher:input_type -> google.protobuf.StringValue
|
9, // 85: grpc.Bridge.ReportBug:input_type -> grpc.ReportBugRequest
|
||||||
71, // 86: grpc.Bridge.SetMainExecutable:input_type -> google.protobuf.StringValue
|
71, // 86: grpc.Bridge.ExportTLSCertificates:input_type -> google.protobuf.StringValue
|
||||||
10, // 87: grpc.Bridge.Login:input_type -> grpc.LoginRequest
|
71, // 87: grpc.Bridge.ForceLauncher:input_type -> google.protobuf.StringValue
|
||||||
10, // 88: grpc.Bridge.Login2FA:input_type -> grpc.LoginRequest
|
71, // 88: grpc.Bridge.SetMainExecutable:input_type -> google.protobuf.StringValue
|
||||||
10, // 89: grpc.Bridge.Login2Passwords:input_type -> grpc.LoginRequest
|
10, // 89: grpc.Bridge.Login:input_type -> grpc.LoginRequest
|
||||||
11, // 90: grpc.Bridge.LoginAbort:input_type -> grpc.LoginAbortRequest
|
10, // 90: grpc.Bridge.Login2FA:input_type -> grpc.LoginRequest
|
||||||
72, // 91: grpc.Bridge.CheckUpdate:input_type -> google.protobuf.Empty
|
10, // 91: grpc.Bridge.Login2Passwords:input_type -> grpc.LoginRequest
|
||||||
72, // 92: grpc.Bridge.InstallUpdate:input_type -> google.protobuf.Empty
|
11, // 92: grpc.Bridge.LoginAbort:input_type -> grpc.LoginAbortRequest
|
||||||
73, // 93: grpc.Bridge.SetIsAutomaticUpdateOn:input_type -> google.protobuf.BoolValue
|
72, // 93: grpc.Bridge.CheckUpdate:input_type -> google.protobuf.Empty
|
||||||
72, // 94: grpc.Bridge.IsAutomaticUpdateOn:input_type -> google.protobuf.Empty
|
72, // 94: grpc.Bridge.InstallUpdate:input_type -> google.protobuf.Empty
|
||||||
72, // 95: grpc.Bridge.DiskCachePath:input_type -> google.protobuf.Empty
|
73, // 95: grpc.Bridge.SetIsAutomaticUpdateOn:input_type -> google.protobuf.BoolValue
|
||||||
71, // 96: grpc.Bridge.SetDiskCachePath:input_type -> google.protobuf.StringValue
|
72, // 96: grpc.Bridge.IsAutomaticUpdateOn:input_type -> google.protobuf.Empty
|
||||||
73, // 97: grpc.Bridge.SetIsDoHEnabled:input_type -> google.protobuf.BoolValue
|
72, // 97: grpc.Bridge.DiskCachePath:input_type -> google.protobuf.Empty
|
||||||
72, // 98: grpc.Bridge.IsDoHEnabled:input_type -> google.protobuf.Empty
|
71, // 98: grpc.Bridge.SetDiskCachePath:input_type -> google.protobuf.StringValue
|
||||||
72, // 99: grpc.Bridge.MailServerSettings:input_type -> google.protobuf.Empty
|
73, // 99: grpc.Bridge.SetIsDoHEnabled:input_type -> google.protobuf.BoolValue
|
||||||
12, // 100: grpc.Bridge.SetMailServerSettings:input_type -> grpc.ImapSmtpSettings
|
72, // 100: grpc.Bridge.IsDoHEnabled:input_type -> google.protobuf.Empty
|
||||||
72, // 101: grpc.Bridge.Hostname:input_type -> google.protobuf.Empty
|
72, // 101: grpc.Bridge.MailServerSettings:input_type -> google.protobuf.Empty
|
||||||
74, // 102: grpc.Bridge.IsPortFree:input_type -> google.protobuf.Int32Value
|
12, // 102: grpc.Bridge.SetMailServerSettings:input_type -> grpc.ImapSmtpSettings
|
||||||
72, // 103: grpc.Bridge.AvailableKeychains:input_type -> google.protobuf.Empty
|
72, // 103: grpc.Bridge.Hostname:input_type -> google.protobuf.Empty
|
||||||
71, // 104: grpc.Bridge.SetCurrentKeychain:input_type -> google.protobuf.StringValue
|
74, // 104: grpc.Bridge.IsPortFree:input_type -> google.protobuf.Int32Value
|
||||||
72, // 105: grpc.Bridge.CurrentKeychain:input_type -> google.protobuf.Empty
|
72, // 105: grpc.Bridge.AvailableKeychains:input_type -> google.protobuf.Empty
|
||||||
72, // 106: grpc.Bridge.GetUserList:input_type -> google.protobuf.Empty
|
71, // 106: grpc.Bridge.SetCurrentKeychain:input_type -> google.protobuf.StringValue
|
||||||
71, // 107: grpc.Bridge.GetUser:input_type -> google.protobuf.StringValue
|
72, // 107: grpc.Bridge.CurrentKeychain:input_type -> google.protobuf.Empty
|
||||||
15, // 108: grpc.Bridge.SetUserSplitMode:input_type -> grpc.UserSplitModeRequest
|
72, // 108: grpc.Bridge.GetUserList:input_type -> google.protobuf.Empty
|
||||||
16, // 109: grpc.Bridge.SendBadEventUserFeedback:input_type -> grpc.UserBadEventFeedbackRequest
|
71, // 109: grpc.Bridge.GetUser:input_type -> google.protobuf.StringValue
|
||||||
71, // 110: grpc.Bridge.LogoutUser:input_type -> google.protobuf.StringValue
|
15, // 110: grpc.Bridge.SetUserSplitMode:input_type -> grpc.UserSplitModeRequest
|
||||||
71, // 111: grpc.Bridge.RemoveUser:input_type -> google.protobuf.StringValue
|
16, // 111: grpc.Bridge.SendBadEventUserFeedback:input_type -> grpc.UserBadEventFeedbackRequest
|
||||||
18, // 112: grpc.Bridge.ConfigureUserAppleMail:input_type -> grpc.ConfigureAppleMailRequest
|
71, // 112: grpc.Bridge.LogoutUser:input_type -> google.protobuf.StringValue
|
||||||
19, // 113: grpc.Bridge.RunEventStream:input_type -> grpc.EventStreamRequest
|
71, // 113: grpc.Bridge.RemoveUser:input_type -> google.protobuf.StringValue
|
||||||
72, // 114: grpc.Bridge.StopEventStream:input_type -> google.protobuf.Empty
|
18, // 114: grpc.Bridge.ConfigureUserAppleMail:input_type -> grpc.ConfigureAppleMailRequest
|
||||||
71, // 115: grpc.Bridge.CheckTokens:output_type -> google.protobuf.StringValue
|
19, // 115: grpc.Bridge.RunEventStream:input_type -> grpc.EventStreamRequest
|
||||||
72, // 116: grpc.Bridge.AddLogEntry:output_type -> google.protobuf.Empty
|
72, // 116: grpc.Bridge.StopEventStream:input_type -> google.protobuf.Empty
|
||||||
8, // 117: grpc.Bridge.GuiReady:output_type -> grpc.GuiReadyResponse
|
71, // 117: grpc.Bridge.CheckTokens:output_type -> google.protobuf.StringValue
|
||||||
72, // 118: grpc.Bridge.Quit:output_type -> google.protobuf.Empty
|
72, // 118: grpc.Bridge.AddLogEntry:output_type -> google.protobuf.Empty
|
||||||
72, // 119: grpc.Bridge.Restart:output_type -> google.protobuf.Empty
|
8, // 119: grpc.Bridge.GuiReady:output_type -> grpc.GuiReadyResponse
|
||||||
73, // 120: grpc.Bridge.ShowOnStartup:output_type -> google.protobuf.BoolValue
|
72, // 120: grpc.Bridge.Quit:output_type -> google.protobuf.Empty
|
||||||
72, // 121: grpc.Bridge.SetIsAutostartOn:output_type -> google.protobuf.Empty
|
72, // 121: grpc.Bridge.Restart:output_type -> google.protobuf.Empty
|
||||||
73, // 122: grpc.Bridge.IsAutostartOn:output_type -> google.protobuf.BoolValue
|
73, // 122: grpc.Bridge.ShowOnStartup:output_type -> google.protobuf.BoolValue
|
||||||
72, // 123: grpc.Bridge.SetIsBetaEnabled:output_type -> google.protobuf.Empty
|
72, // 123: grpc.Bridge.SetIsAutostartOn:output_type -> google.protobuf.Empty
|
||||||
73, // 124: grpc.Bridge.IsBetaEnabled:output_type -> google.protobuf.BoolValue
|
73, // 124: grpc.Bridge.IsAutostartOn:output_type -> google.protobuf.BoolValue
|
||||||
72, // 125: grpc.Bridge.SetIsAllMailVisible:output_type -> google.protobuf.Empty
|
72, // 125: grpc.Bridge.SetIsBetaEnabled:output_type -> google.protobuf.Empty
|
||||||
73, // 126: grpc.Bridge.IsAllMailVisible:output_type -> google.protobuf.BoolValue
|
73, // 126: grpc.Bridge.IsBetaEnabled:output_type -> google.protobuf.BoolValue
|
||||||
71, // 127: grpc.Bridge.GoOs:output_type -> google.protobuf.StringValue
|
72, // 127: grpc.Bridge.SetIsAllMailVisible:output_type -> google.protobuf.Empty
|
||||||
72, // 128: grpc.Bridge.TriggerReset:output_type -> google.protobuf.Empty
|
73, // 128: grpc.Bridge.IsAllMailVisible:output_type -> google.protobuf.BoolValue
|
||||||
71, // 129: grpc.Bridge.Version:output_type -> google.protobuf.StringValue
|
72, // 129: grpc.Bridge.SetIsTelemetryDisabled:output_type -> google.protobuf.Empty
|
||||||
71, // 130: grpc.Bridge.LogsPath:output_type -> google.protobuf.StringValue
|
73, // 130: grpc.Bridge.IsTelemetryDisabled:output_type -> google.protobuf.BoolValue
|
||||||
71, // 131: grpc.Bridge.LicensePath:output_type -> google.protobuf.StringValue
|
71, // 131: grpc.Bridge.GoOs:output_type -> google.protobuf.StringValue
|
||||||
71, // 132: grpc.Bridge.ReleaseNotesPageLink:output_type -> google.protobuf.StringValue
|
72, // 132: grpc.Bridge.TriggerReset:output_type -> google.protobuf.Empty
|
||||||
71, // 133: grpc.Bridge.DependencyLicensesLink:output_type -> google.protobuf.StringValue
|
71, // 133: grpc.Bridge.Version:output_type -> google.protobuf.StringValue
|
||||||
71, // 134: grpc.Bridge.LandingPageLink:output_type -> google.protobuf.StringValue
|
71, // 134: grpc.Bridge.LogsPath:output_type -> google.protobuf.StringValue
|
||||||
72, // 135: grpc.Bridge.SetColorSchemeName:output_type -> google.protobuf.Empty
|
71, // 135: grpc.Bridge.LicensePath:output_type -> google.protobuf.StringValue
|
||||||
71, // 136: grpc.Bridge.ColorSchemeName:output_type -> google.protobuf.StringValue
|
71, // 136: grpc.Bridge.ReleaseNotesPageLink:output_type -> google.protobuf.StringValue
|
||||||
71, // 137: grpc.Bridge.CurrentEmailClient:output_type -> google.protobuf.StringValue
|
71, // 137: grpc.Bridge.DependencyLicensesLink:output_type -> google.protobuf.StringValue
|
||||||
72, // 138: grpc.Bridge.ReportBug:output_type -> google.protobuf.Empty
|
71, // 138: grpc.Bridge.LandingPageLink:output_type -> google.protobuf.StringValue
|
||||||
72, // 139: grpc.Bridge.ExportTLSCertificates:output_type -> google.protobuf.Empty
|
72, // 139: grpc.Bridge.SetColorSchemeName:output_type -> google.protobuf.Empty
|
||||||
72, // 140: grpc.Bridge.ForceLauncher:output_type -> google.protobuf.Empty
|
71, // 140: grpc.Bridge.ColorSchemeName:output_type -> google.protobuf.StringValue
|
||||||
72, // 141: grpc.Bridge.SetMainExecutable:output_type -> google.protobuf.Empty
|
71, // 141: grpc.Bridge.CurrentEmailClient:output_type -> google.protobuf.StringValue
|
||||||
72, // 142: grpc.Bridge.Login:output_type -> google.protobuf.Empty
|
72, // 142: grpc.Bridge.ReportBug:output_type -> google.protobuf.Empty
|
||||||
72, // 143: grpc.Bridge.Login2FA:output_type -> google.protobuf.Empty
|
72, // 143: grpc.Bridge.ExportTLSCertificates:output_type -> google.protobuf.Empty
|
||||||
72, // 144: grpc.Bridge.Login2Passwords:output_type -> google.protobuf.Empty
|
72, // 144: grpc.Bridge.ForceLauncher:output_type -> google.protobuf.Empty
|
||||||
72, // 145: grpc.Bridge.LoginAbort:output_type -> google.protobuf.Empty
|
72, // 145: grpc.Bridge.SetMainExecutable:output_type -> google.protobuf.Empty
|
||||||
72, // 146: grpc.Bridge.CheckUpdate:output_type -> google.protobuf.Empty
|
72, // 146: grpc.Bridge.Login:output_type -> google.protobuf.Empty
|
||||||
72, // 147: grpc.Bridge.InstallUpdate:output_type -> google.protobuf.Empty
|
72, // 147: grpc.Bridge.Login2FA:output_type -> google.protobuf.Empty
|
||||||
72, // 148: grpc.Bridge.SetIsAutomaticUpdateOn:output_type -> google.protobuf.Empty
|
72, // 148: grpc.Bridge.Login2Passwords:output_type -> google.protobuf.Empty
|
||||||
73, // 149: grpc.Bridge.IsAutomaticUpdateOn:output_type -> google.protobuf.BoolValue
|
72, // 149: grpc.Bridge.LoginAbort:output_type -> google.protobuf.Empty
|
||||||
71, // 150: grpc.Bridge.DiskCachePath:output_type -> google.protobuf.StringValue
|
72, // 150: grpc.Bridge.CheckUpdate:output_type -> google.protobuf.Empty
|
||||||
72, // 151: grpc.Bridge.SetDiskCachePath:output_type -> google.protobuf.Empty
|
72, // 151: grpc.Bridge.InstallUpdate:output_type -> google.protobuf.Empty
|
||||||
72, // 152: grpc.Bridge.SetIsDoHEnabled:output_type -> google.protobuf.Empty
|
72, // 152: grpc.Bridge.SetIsAutomaticUpdateOn:output_type -> google.protobuf.Empty
|
||||||
73, // 153: grpc.Bridge.IsDoHEnabled:output_type -> google.protobuf.BoolValue
|
73, // 153: grpc.Bridge.IsAutomaticUpdateOn:output_type -> google.protobuf.BoolValue
|
||||||
12, // 154: grpc.Bridge.MailServerSettings:output_type -> grpc.ImapSmtpSettings
|
71, // 154: grpc.Bridge.DiskCachePath:output_type -> google.protobuf.StringValue
|
||||||
72, // 155: grpc.Bridge.SetMailServerSettings:output_type -> google.protobuf.Empty
|
72, // 155: grpc.Bridge.SetDiskCachePath:output_type -> google.protobuf.Empty
|
||||||
71, // 156: grpc.Bridge.Hostname:output_type -> google.protobuf.StringValue
|
72, // 156: grpc.Bridge.SetIsDoHEnabled:output_type -> google.protobuf.Empty
|
||||||
73, // 157: grpc.Bridge.IsPortFree:output_type -> google.protobuf.BoolValue
|
73, // 157: grpc.Bridge.IsDoHEnabled:output_type -> google.protobuf.BoolValue
|
||||||
13, // 158: grpc.Bridge.AvailableKeychains:output_type -> grpc.AvailableKeychainsResponse
|
12, // 158: grpc.Bridge.MailServerSettings:output_type -> grpc.ImapSmtpSettings
|
||||||
72, // 159: grpc.Bridge.SetCurrentKeychain:output_type -> google.protobuf.Empty
|
72, // 159: grpc.Bridge.SetMailServerSettings:output_type -> google.protobuf.Empty
|
||||||
71, // 160: grpc.Bridge.CurrentKeychain:output_type -> google.protobuf.StringValue
|
71, // 160: grpc.Bridge.Hostname:output_type -> google.protobuf.StringValue
|
||||||
17, // 161: grpc.Bridge.GetUserList:output_type -> grpc.UserListResponse
|
73, // 161: grpc.Bridge.IsPortFree:output_type -> google.protobuf.BoolValue
|
||||||
14, // 162: grpc.Bridge.GetUser:output_type -> grpc.User
|
13, // 162: grpc.Bridge.AvailableKeychains:output_type -> grpc.AvailableKeychainsResponse
|
||||||
72, // 163: grpc.Bridge.SetUserSplitMode:output_type -> google.protobuf.Empty
|
72, // 163: grpc.Bridge.SetCurrentKeychain:output_type -> google.protobuf.Empty
|
||||||
72, // 164: grpc.Bridge.SendBadEventUserFeedback:output_type -> google.protobuf.Empty
|
71, // 164: grpc.Bridge.CurrentKeychain:output_type -> google.protobuf.StringValue
|
||||||
72, // 165: grpc.Bridge.LogoutUser:output_type -> google.protobuf.Empty
|
17, // 165: grpc.Bridge.GetUserList:output_type -> grpc.UserListResponse
|
||||||
72, // 166: grpc.Bridge.RemoveUser:output_type -> google.protobuf.Empty
|
14, // 166: grpc.Bridge.GetUser:output_type -> grpc.User
|
||||||
72, // 167: grpc.Bridge.ConfigureUserAppleMail:output_type -> google.protobuf.Empty
|
72, // 167: grpc.Bridge.SetUserSplitMode:output_type -> google.protobuf.Empty
|
||||||
20, // 168: grpc.Bridge.RunEventStream:output_type -> grpc.StreamEvent
|
72, // 168: grpc.Bridge.SendBadEventUserFeedback:output_type -> google.protobuf.Empty
|
||||||
72, // 169: grpc.Bridge.StopEventStream:output_type -> google.protobuf.Empty
|
72, // 169: grpc.Bridge.LogoutUser:output_type -> google.protobuf.Empty
|
||||||
115, // [115:170] is the sub-list for method output_type
|
72, // 170: grpc.Bridge.RemoveUser:output_type -> google.protobuf.Empty
|
||||||
60, // [60:115] is the sub-list for method input_type
|
72, // 171: grpc.Bridge.ConfigureUserAppleMail:output_type -> google.protobuf.Empty
|
||||||
|
20, // 172: grpc.Bridge.RunEventStream:output_type -> grpc.StreamEvent
|
||||||
|
72, // 173: grpc.Bridge.StopEventStream:output_type -> google.protobuf.Empty
|
||||||
|
117, // [117:174] is the sub-list for method output_type
|
||||||
|
60, // [60:117] is the sub-list for method input_type
|
||||||
60, // [60:60] is the sub-list for extension type_name
|
60, // [60:60] is the sub-list for extension type_name
|
||||||
60, // [60:60] is the sub-list for extension extendee
|
60, // [60:60] is the sub-list for extension extendee
|
||||||
0, // [0:60] is the sub-list for field type_name
|
0, // [0:60] is the sub-list for field type_name
|
||||||
|
|||||||
@ -42,6 +42,8 @@ service Bridge {
|
|||||||
rpc IsBetaEnabled(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
rpc IsBetaEnabled(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
||||||
rpc SetIsAllMailVisible(google.protobuf.BoolValue) returns (google.protobuf.Empty);
|
rpc SetIsAllMailVisible(google.protobuf.BoolValue) returns (google.protobuf.Empty);
|
||||||
rpc IsAllMailVisible(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
rpc IsAllMailVisible(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
||||||
|
rpc SetIsTelemetryDisabled(google.protobuf.BoolValue) returns (google.protobuf.Empty);
|
||||||
|
rpc IsTelemetryDisabled(google.protobuf.Empty) returns (google.protobuf.BoolValue);
|
||||||
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);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -214,6 +214,23 @@ func (s *Service) IsAllMailVisible(ctx context.Context, _ *emptypb.Empty) (*wrap
|
|||||||
return wrapperspb.Bool(s.bridge.GetShowAllMail()), nil
|
return wrapperspb.Bool(s.bridge.GetShowAllMail()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) SetIsTelemetryDisabled(_ context.Context, isDisabled *wrapperspb.BoolValue) (*emptypb.Empty, error) {
|
||||||
|
s.log.WithField("isEnabled", isDisabled.Value).Debug("SetIsTelemetryDisabled")
|
||||||
|
|
||||||
|
if err := s.bridge.SetTelemetryDisabled(isDisabled.Value); err != nil {
|
||||||
|
s.log.WithError(err).Error("Failed to set telemetry status")
|
||||||
|
return nil, status.Errorf(codes.Internal, "failed to set telemetry status: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) IsTelemetryDisabled(_ context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) {
|
||||||
|
s.log.Debug("IsTelemetryDisabled")
|
||||||
|
|
||||||
|
return wrapperspb.Bool(s.bridge.GetTelemetryDisabled()), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) GoOs(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
func (s *Service) GoOs(ctx context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
|
||||||
s.log.Debug("GoOs") // TO-DO We can probably get rid of this and use QSysInfo::product name
|
s.log.Debug("GoOs") // TO-DO We can probably get rid of this and use QSysInfo::product name
|
||||||
|
|
||||||
|
|||||||
@ -15,14 +15,18 @@
|
|||||||
// 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/>.
|
||||||
|
|
||||||
import QtQml.Models
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
ListModel {
|
package sentry
|
||||||
// overriding get method to ignore any role and return directly object itself
|
|
||||||
function get(row) {
|
import "os"
|
||||||
if (row < 0 || row >= count) {
|
|
||||||
return undefined
|
func GetSystemLang() string {
|
||||||
}
|
lang := os.Getenv("LC_ALL")
|
||||||
return data(index(row, 0), Qt.DisplayRole)
|
if lang == "" {
|
||||||
}
|
lang = os.Getenv("LANG")
|
||||||
|
}
|
||||||
|
|
||||||
|
return lang
|
||||||
}
|
}
|
||||||
67
internal/sentry/lang_windows.go
Normal file
67
internal/sentry/lang_windows.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// Copyright (c) 2023 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 windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package sentry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultLocaleUser = "GetUserDefaultLocaleName" // https://learn.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getuserdefaultlocalename
|
||||||
|
defaultLocaleSystem = "GetSystemDefaultLocaleName" // https://learn.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getsystemdefaultlocalename
|
||||||
|
localeNameMaxLength = 85 // https://learn.microsoft.com/en-us/windows/win32/intl/locale-name-constants
|
||||||
|
)
|
||||||
|
|
||||||
|
func getLocale(dll *syscall.DLL, procName string) (string, error) {
|
||||||
|
proc, err := dll.FindProc(procName)
|
||||||
|
if err != nil {
|
||||||
|
return "errProc", err
|
||||||
|
}
|
||||||
|
|
||||||
|
b := make([]uint16, localeNameMaxLength)
|
||||||
|
|
||||||
|
r, _, err := proc.Call(uintptr(unsafe.Pointer(&b[0])), uintptr(localeNameMaxLength))
|
||||||
|
if r == 0 || err != nil {
|
||||||
|
return "errCall", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return syscall.UTF16ToString(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSystemLang() string {
|
||||||
|
dll, err := syscall.LoadDLL("kernel32")
|
||||||
|
if err != nil {
|
||||||
|
return "errDll"
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = dll.Release()
|
||||||
|
}()
|
||||||
|
|
||||||
|
if lang, err := getLocale(dll, defaultLocaleUser); err == nil {
|
||||||
|
return lang
|
||||||
|
}
|
||||||
|
|
||||||
|
lang, _ := getLocale(dll, defaultLocaleSystem)
|
||||||
|
|
||||||
|
return lang
|
||||||
|
}
|
||||||
@ -18,7 +18,6 @@
|
|||||||
package sentry
|
package sentry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
@ -29,6 +28,7 @@ import (
|
|||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
"github.com/ProtonMail/gluon/reporter"
|
"github.com/ProtonMail/gluon/reporter"
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/constants"
|
"github.com/ProtonMail/proton-bridge/v3/internal/constants"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v3/pkg/algo"
|
||||||
"github.com/ProtonMail/proton-bridge/v3/pkg/restarter"
|
"github.com/ProtonMail/proton-bridge/v3/pkg/restarter"
|
||||||
"github.com/getsentry/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -50,7 +50,7 @@ func init() { //nolint:gochecknoinits
|
|||||||
Release: constants.AppVersion(appVersion),
|
Release: constants.AppVersion(appVersion),
|
||||||
BeforeSend: EnhanceSentryEvent,
|
BeforeSend: EnhanceSentryEvent,
|
||||||
Transport: sentrySyncTransport,
|
Transport: sentrySyncTransport,
|
||||||
ServerName: getProtectedHostname(),
|
ServerName: GetProtectedHostname(),
|
||||||
Environment: constants.BuildEnv,
|
Environment: constants.BuildEnv,
|
||||||
MaxBreadcrumbs: 50,
|
MaxBreadcrumbs: 50,
|
||||||
}
|
}
|
||||||
@ -61,7 +61,7 @@ func init() { //nolint:gochecknoinits
|
|||||||
|
|
||||||
sentry.ConfigureScope(func(scope *sentry.Scope) {
|
sentry.ConfigureScope(func(scope *sentry.Scope) {
|
||||||
scope.SetFingerprint([]string{"{{ default }}"})
|
scope.SetFingerprint([]string{"{{ default }}"})
|
||||||
scope.SetUser(sentry.User{ID: getProtectedHostname()})
|
scope.SetUser(sentry.User{ID: GetProtectedHostname()})
|
||||||
})
|
})
|
||||||
|
|
||||||
sentry.Logger = log.New(
|
sentry.Logger = log.New(
|
||||||
@ -81,12 +81,17 @@ type Identifier interface {
|
|||||||
GetUserAgent() string
|
GetUserAgent() string
|
||||||
}
|
}
|
||||||
|
|
||||||
func getProtectedHostname() string {
|
func GetProtectedHostname() string {
|
||||||
hostname, err := os.Hostname()
|
hostname, err := os.Hostname()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(hostname)))
|
return algo.HashBase64SHA256(hostname)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTimeZone() string {
|
||||||
|
zone, offset := time.Now().Zone()
|
||||||
|
return fmt.Sprintf("%s%+d", zone, offset/3600)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewReporter creates new sentry reporter with appName and appVersion to report.
|
// NewReporter creates new sentry reporter with appName and appVersion to report.
|
||||||
|
|||||||
172
internal/telemetry/heartbeat.go
Normal file
172
internal/telemetry/heartbeat.go
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
// Copyright (c) 2023 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 telemetry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewHeartbeat(manager HeartbeatManager, imapPort, smtpPort int, cacheDir, keychain string) Heartbeat {
|
||||||
|
heartbeat := Heartbeat{
|
||||||
|
log: logrus.WithField("pkg", "telemetry"),
|
||||||
|
manager: manager,
|
||||||
|
metrics: HeartbeatData{
|
||||||
|
MeasurementGroup: "bridge.any.usage",
|
||||||
|
Event: "bridge_heartbeat",
|
||||||
|
},
|
||||||
|
defaultIMAPPort: imapPort,
|
||||||
|
defaultSMTPPort: smtpPort,
|
||||||
|
defaultCache: cacheDir,
|
||||||
|
defaultKeychain: keychain,
|
||||||
|
}
|
||||||
|
return heartbeat
|
||||||
|
}
|
||||||
|
|
||||||
|
func (heartbeat *Heartbeat) SetRollout(val float64) {
|
||||||
|
heartbeat.metrics.Dimensions.Rollout = strconv.Itoa(int(val * 100))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (heartbeat *Heartbeat) SetNbAccount(val int) {
|
||||||
|
heartbeat.metrics.Values.NbAccount = val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (heartbeat *Heartbeat) SetAutoUpdate(val bool) {
|
||||||
|
if val {
|
||||||
|
heartbeat.metrics.Dimensions.AutoUpdate = dimensionON
|
||||||
|
} else {
|
||||||
|
heartbeat.metrics.Dimensions.AutoUpdate = dimensionOFF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (heartbeat *Heartbeat) SetAutoStart(val bool) {
|
||||||
|
if val {
|
||||||
|
heartbeat.metrics.Dimensions.AutoStart = dimensionON
|
||||||
|
} else {
|
||||||
|
heartbeat.metrics.Dimensions.AutoStart = dimensionOFF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (heartbeat *Heartbeat) SetBeta(val updater.Channel) {
|
||||||
|
if val == updater.EarlyChannel {
|
||||||
|
heartbeat.metrics.Dimensions.Beta = dimensionON
|
||||||
|
} else {
|
||||||
|
heartbeat.metrics.Dimensions.Beta = dimensionOFF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (heartbeat *Heartbeat) SetDoh(val bool) {
|
||||||
|
if val {
|
||||||
|
heartbeat.metrics.Dimensions.Doh = dimensionON
|
||||||
|
} else {
|
||||||
|
heartbeat.metrics.Dimensions.Doh = dimensionOFF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (heartbeat *Heartbeat) SetSplitMode(val bool) {
|
||||||
|
if val {
|
||||||
|
heartbeat.metrics.Dimensions.SplitMode = dimensionON
|
||||||
|
} else {
|
||||||
|
heartbeat.metrics.Dimensions.SplitMode = dimensionOFF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (heartbeat *Heartbeat) SetShowAllMail(val bool) {
|
||||||
|
if val {
|
||||||
|
heartbeat.metrics.Dimensions.ShowAllMail = dimensionON
|
||||||
|
} else {
|
||||||
|
heartbeat.metrics.Dimensions.ShowAllMail = dimensionOFF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (heartbeat *Heartbeat) SetIMAPConnectionMode(val bool) {
|
||||||
|
if val {
|
||||||
|
heartbeat.metrics.Dimensions.IMAPConnectionMode = dimensionSSL
|
||||||
|
} else {
|
||||||
|
heartbeat.metrics.Dimensions.IMAPConnectionMode = dimensionStartTLS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (heartbeat *Heartbeat) SetSMTPConnectionMode(val bool) {
|
||||||
|
if val {
|
||||||
|
heartbeat.metrics.Dimensions.SMTPConnectionMode = dimensionSSL
|
||||||
|
} else {
|
||||||
|
heartbeat.metrics.Dimensions.SMTPConnectionMode = dimensionStartTLS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (heartbeat *Heartbeat) SetIMAPPort(val int) {
|
||||||
|
if val == heartbeat.defaultIMAPPort {
|
||||||
|
heartbeat.metrics.Dimensions.IMAPPort = dimensionDefault
|
||||||
|
} else {
|
||||||
|
heartbeat.metrics.Dimensions.IMAPPort = dimensionCustom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (heartbeat *Heartbeat) SetSMTPPort(val int) {
|
||||||
|
if val == heartbeat.defaultSMTPPort {
|
||||||
|
heartbeat.metrics.Dimensions.SMTPPort = dimensionDefault
|
||||||
|
} else {
|
||||||
|
heartbeat.metrics.Dimensions.SMTPPort = dimensionCustom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (heartbeat *Heartbeat) SetCacheLocation(val string) {
|
||||||
|
if val == heartbeat.defaultCache {
|
||||||
|
heartbeat.metrics.Dimensions.CacheLocation = dimensionDefault
|
||||||
|
} else {
|
||||||
|
heartbeat.metrics.Dimensions.CacheLocation = dimensionCustom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (heartbeat *Heartbeat) SetKeyChainPref(val string) {
|
||||||
|
if val == heartbeat.defaultKeychain {
|
||||||
|
heartbeat.metrics.Dimensions.KeychainPref = dimensionDefault
|
||||||
|
} else {
|
||||||
|
heartbeat.metrics.Dimensions.KeychainPref = dimensionCustom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (heartbeat *Heartbeat) SetPrevVersion(val string) {
|
||||||
|
heartbeat.metrics.Dimensions.PrevVersion = val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (heartbeat *Heartbeat) TrySending() {
|
||||||
|
if heartbeat.manager.IsTelemetryAvailable() {
|
||||||
|
lastSent := heartbeat.manager.GetLastHeartbeatSent()
|
||||||
|
now := time.Now()
|
||||||
|
if now.Year() > lastSent.Year() || (now.Year() == lastSent.Year() && now.YearDay() > lastSent.YearDay()) {
|
||||||
|
if !heartbeat.manager.SendHeartbeat(&heartbeat.metrics) {
|
||||||
|
heartbeat.log.WithFields(logrus.Fields{
|
||||||
|
"metrics": heartbeat.metrics,
|
||||||
|
}).Error("Failed to send heartbeat")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
heartbeat.log.WithFields(logrus.Fields{
|
||||||
|
"metrics": heartbeat.metrics,
|
||||||
|
}).Info("Heartbeat sent")
|
||||||
|
|
||||||
|
if err := heartbeat.manager.SetLastHeartbeatSent(now); err != nil {
|
||||||
|
heartbeat.log.WithError(err).Warn("Cannot save last heartbeat sent to the vault.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
97
internal/telemetry/heartbeat_test.go
Normal file
97
internal/telemetry/heartbeat_test.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
// Copyright (c) 2023 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 telemetry_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/proton-bridge/v3/internal/telemetry"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v3/internal/telemetry/mocks"
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHeartbeat_default_heartbeat(t *testing.T) {
|
||||||
|
withHeartbeat(t, 1143, 1025, "/tmp", "defaultKeychain", func(hb *telemetry.Heartbeat, mock *mocks.MockHeartbeatManager) {
|
||||||
|
data := telemetry.HeartbeatData{
|
||||||
|
MeasurementGroup: "bridge.any.usage",
|
||||||
|
Event: "bridge_heartbeat",
|
||||||
|
Values: telemetry.HeartbeatValues{
|
||||||
|
NbAccount: 1,
|
||||||
|
},
|
||||||
|
Dimensions: telemetry.HeartbeatDimensions{
|
||||||
|
AutoUpdate: "on",
|
||||||
|
AutoStart: "on",
|
||||||
|
Beta: "off",
|
||||||
|
Doh: "off",
|
||||||
|
SplitMode: "off",
|
||||||
|
ShowAllMail: "off",
|
||||||
|
IMAPConnectionMode: "ssl",
|
||||||
|
SMTPConnectionMode: "ssl",
|
||||||
|
IMAPPort: "default",
|
||||||
|
SMTPPort: "default",
|
||||||
|
CacheLocation: "default",
|
||||||
|
KeychainPref: "default",
|
||||||
|
PrevVersion: "1.2.3",
|
||||||
|
Rollout: "10",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mock.EXPECT().IsTelemetryAvailable().Return(true)
|
||||||
|
mock.EXPECT().GetLastHeartbeatSent().Return(time.Date(2022, 6, 4, 0, 0, 0, 0, time.UTC))
|
||||||
|
mock.EXPECT().SendHeartbeat(&data).Return(true)
|
||||||
|
mock.EXPECT().SetLastHeartbeatSent(gomock.Any()).Return(nil)
|
||||||
|
|
||||||
|
hb.TrySending()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHeartbeat_already_sent_heartbeat(t *testing.T) {
|
||||||
|
withHeartbeat(t, 1143, 1025, "/tmp", "defaultKeychain", func(hb *telemetry.Heartbeat, mock *mocks.MockHeartbeatManager) {
|
||||||
|
mock.EXPECT().IsTelemetryAvailable().Return(true)
|
||||||
|
mock.EXPECT().GetLastHeartbeatSent().Return(time.Now().Truncate(24 * time.Hour))
|
||||||
|
|
||||||
|
hb.TrySending()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func withHeartbeat(t *testing.T, imap, smtp int, cache, keychain string, tests func(hb *telemetry.Heartbeat, mock *mocks.MockHeartbeatManager)) {
|
||||||
|
ctl := gomock.NewController(t)
|
||||||
|
defer ctl.Finish()
|
||||||
|
|
||||||
|
manager := mocks.NewMockHeartbeatManager(ctl)
|
||||||
|
heartbeat := telemetry.NewHeartbeat(manager, imap, smtp, cache, keychain)
|
||||||
|
|
||||||
|
heartbeat.SetRollout(0.1)
|
||||||
|
heartbeat.SetNbAccount(1)
|
||||||
|
heartbeat.SetSplitMode(false)
|
||||||
|
heartbeat.SetAutoStart(true)
|
||||||
|
heartbeat.SetAutoUpdate(true)
|
||||||
|
heartbeat.SetBeta("stable")
|
||||||
|
heartbeat.SetDoh(false)
|
||||||
|
heartbeat.SetShowAllMail(false)
|
||||||
|
heartbeat.SetIMAPConnectionMode(true)
|
||||||
|
heartbeat.SetSMTPConnectionMode(true)
|
||||||
|
heartbeat.SetIMAPPort(1143)
|
||||||
|
heartbeat.SetSMTPPort(1025)
|
||||||
|
heartbeat.SetCacheLocation("/tmp")
|
||||||
|
heartbeat.SetKeyChainPref("defaultKeychain")
|
||||||
|
heartbeat.SetPrevVersion("1.2.3")
|
||||||
|
|
||||||
|
tests(&heartbeat, manager)
|
||||||
|
}
|
||||||
92
internal/telemetry/mocks/mocks.go
Normal file
92
internal/telemetry/mocks/mocks.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/ProtonMail/proton-bridge/v3/internal/telemetry (interfaces: HeartbeatManager)
|
||||||
|
|
||||||
|
// Package mocks is a generated GoMock package.
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
time "time"
|
||||||
|
|
||||||
|
telemetry "github.com/ProtonMail/proton-bridge/v3/internal/telemetry"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockHeartbeatManager is a mock of HeartbeatManager interface.
|
||||||
|
type MockHeartbeatManager struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockHeartbeatManagerMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockHeartbeatManagerMockRecorder is the mock recorder for MockHeartbeatManager.
|
||||||
|
type MockHeartbeatManagerMockRecorder struct {
|
||||||
|
mock *MockHeartbeatManager
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockHeartbeatManager creates a new mock instance.
|
||||||
|
func NewMockHeartbeatManager(ctrl *gomock.Controller) *MockHeartbeatManager {
|
||||||
|
mock := &MockHeartbeatManager{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockHeartbeatManagerMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockHeartbeatManager) EXPECT() *MockHeartbeatManagerMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLastHeartbeatSent mocks base method.
|
||||||
|
func (m *MockHeartbeatManager) GetLastHeartbeatSent() time.Time {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetLastHeartbeatSent")
|
||||||
|
ret0, _ := ret[0].(time.Time)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLastHeartbeatSent indicates an expected call of GetLastHeartbeatSent.
|
||||||
|
func (mr *MockHeartbeatManagerMockRecorder) GetLastHeartbeatSent() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastHeartbeatSent", reflect.TypeOf((*MockHeartbeatManager)(nil).GetLastHeartbeatSent))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTelemetryAvailable mocks base method.
|
||||||
|
func (m *MockHeartbeatManager) IsTelemetryAvailable() bool {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "IsTelemetryAvailable")
|
||||||
|
ret0, _ := ret[0].(bool)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTelemetryAvailable indicates an expected call of IsTelemetryAvailable.
|
||||||
|
func (mr *MockHeartbeatManagerMockRecorder) IsTelemetryAvailable() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsTelemetryAvailable", reflect.TypeOf((*MockHeartbeatManager)(nil).IsTelemetryAvailable))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendHeartbeat mocks base method.
|
||||||
|
func (m *MockHeartbeatManager) SendHeartbeat(arg0 *telemetry.HeartbeatData) bool {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SendHeartbeat", arg0)
|
||||||
|
ret0, _ := ret[0].(bool)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendHeartbeat indicates an expected call of SendHeartbeat.
|
||||||
|
func (mr *MockHeartbeatManagerMockRecorder) SendHeartbeat(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendHeartbeat", reflect.TypeOf((*MockHeartbeatManager)(nil).SendHeartbeat), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLastHeartbeatSent mocks base method.
|
||||||
|
func (m *MockHeartbeatManager) SetLastHeartbeatSent(arg0 time.Time) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SetLastHeartbeatSent", arg0)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLastHeartbeatSent indicates an expected call of SetLastHeartbeatSent.
|
||||||
|
func (mr *MockHeartbeatManagerMockRecorder) SetLastHeartbeatSent(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLastHeartbeatSent", reflect.TypeOf((*MockHeartbeatManager)(nil).SetLastHeartbeatSent), arg0)
|
||||||
|
}
|
||||||
79
internal/telemetry/types_heartbeat.go
Normal file
79
internal/telemetry/types_heartbeat.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Copyright (c) 2023 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 telemetry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dimensionON = "on"
|
||||||
|
dimensionOFF = "off"
|
||||||
|
dimensionDefault = "default"
|
||||||
|
dimensionCustom = "custom"
|
||||||
|
dimensionSSL = "ssl"
|
||||||
|
dimensionStartTLS = "starttls"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HeartbeatManager interface {
|
||||||
|
IsTelemetryAvailable() bool
|
||||||
|
SendHeartbeat(heartbeat *HeartbeatData) bool
|
||||||
|
GetLastHeartbeatSent() time.Time
|
||||||
|
SetLastHeartbeatSent(time.Time) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type HeartbeatValues struct {
|
||||||
|
NbAccount int `json:"nb_account"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HeartbeatDimensions struct {
|
||||||
|
AutoUpdate string `json:"auto_update"`
|
||||||
|
AutoStart string `json:"auto_start"`
|
||||||
|
Beta string `json:"beta"`
|
||||||
|
Doh string `json:"doh"`
|
||||||
|
SplitMode string `json:"split_mode"`
|
||||||
|
ShowAllMail string `json:"show_all_mail"`
|
||||||
|
IMAPConnectionMode string `json:"imap_connection_mode"`
|
||||||
|
SMTPConnectionMode string `json:"smtp_connection_mode"`
|
||||||
|
IMAPPort string `json:"imap_port"`
|
||||||
|
SMTPPort string `json:"smtp_port"`
|
||||||
|
CacheLocation string `json:"cache_location"`
|
||||||
|
KeychainPref string `json:"keychain_pref"`
|
||||||
|
PrevVersion string `json:"prev_version"`
|
||||||
|
Rollout string `json:"rollout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HeartbeatData struct {
|
||||||
|
MeasurementGroup string
|
||||||
|
Event string
|
||||||
|
Values HeartbeatValues
|
||||||
|
Dimensions HeartbeatDimensions
|
||||||
|
}
|
||||||
|
|
||||||
|
type Heartbeat struct {
|
||||||
|
log *logrus.Entry
|
||||||
|
manager HeartbeatManager
|
||||||
|
metrics HeartbeatData
|
||||||
|
|
||||||
|
defaultIMAPPort int
|
||||||
|
defaultSMTPPort int
|
||||||
|
defaultCache string
|
||||||
|
defaultKeychain string
|
||||||
|
}
|
||||||
@ -650,15 +650,28 @@ func (user *User) handleUpdateMessageEvent(ctx context.Context, message proton.M
|
|||||||
"subject": logging.Sensitive(message.Subject),
|
"subject": logging.Sensitive(message.Subject),
|
||||||
}).Info("Handling message updated event")
|
}).Info("Handling message updated event")
|
||||||
|
|
||||||
|
flags := imap.NewFlagSet()
|
||||||
|
|
||||||
|
if message.Seen() {
|
||||||
|
flags.AddToSelf(imap.FlagSeen)
|
||||||
|
}
|
||||||
|
|
||||||
|
if message.Starred() {
|
||||||
|
flags.AddToSelf(imap.FlagFlagged)
|
||||||
|
}
|
||||||
|
|
||||||
|
if message.IsDraft() {
|
||||||
|
flags.AddToSelf(imap.FlagDraft)
|
||||||
|
}
|
||||||
|
|
||||||
|
if message.IsRepliedAll == true || message.IsReplied == true { //nolint: gosimple
|
||||||
|
flags.AddToSelf(imap.FlagAnswered)
|
||||||
|
}
|
||||||
|
|
||||||
update := imap.NewMessageMailboxesUpdated(
|
update := imap.NewMessageMailboxesUpdated(
|
||||||
imap.MessageID(message.ID),
|
imap.MessageID(message.ID),
|
||||||
mapTo[string, imap.MailboxID](wantLabels(user.apiLabels, message.LabelIDs)),
|
mapTo[string, imap.MailboxID](wantLabels(user.apiLabels, message.LabelIDs)),
|
||||||
imap.MessageCustomFlags{
|
flags,
|
||||||
Seen: message.Seen(),
|
|
||||||
Flagged: message.Starred(),
|
|
||||||
Draft: message.IsDraft(),
|
|
||||||
Answered: message.IsRepliedAll == true || message.IsReplied == true, //nolint: gosimple
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
user.updateCh[message.AddressID].Enqueue(update)
|
user.updateCh[message.AddressID].Enqueue(update)
|
||||||
|
|||||||
@ -20,6 +20,7 @@ package user
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@ -350,7 +351,13 @@ func (conn *imapConnector) CreateMessage(
|
|||||||
wantFlags = wantFlags.Add(proton.MessageFlagReplied)
|
wantFlags = wantFlags.Add(proton.MessageFlagReplied)
|
||||||
}
|
}
|
||||||
|
|
||||||
return conn.importMessage(ctx, literal, wantLabelIDs, wantFlags, unread)
|
msg, literal, err := conn.importMessage(ctx, literal, wantLabelIDs, wantFlags, unread)
|
||||||
|
if err != nil && errors.Is(err, proton.ErrImportSizeExceeded) {
|
||||||
|
// Remap error so that Gluon does not put this message in the recovery mailbox.
|
||||||
|
err = fmt.Errorf("%v: %w", err, connector.ErrMessageSizeExceedsLimits)
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg, literal, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *imapConnector) GetMessageLiteral(ctx context.Context, id imap.MessageID) ([]byte, error) {
|
func (conn *imapConnector) GetMessageLiteral(ctx context.Context, id imap.MessageID) ([]byte, error) {
|
||||||
@ -400,32 +407,8 @@ func (conn *imapConnector) RemoveMessagesFromMailbox(ctx context.Context, messag
|
|||||||
}
|
}
|
||||||
|
|
||||||
if mailboxID == proton.TrashLabel || mailboxID == proton.DraftsLabel {
|
if mailboxID == proton.TrashLabel || mailboxID == proton.DraftsLabel {
|
||||||
var metadata []proton.MessageMetadata
|
if err := conn.client.DeleteMessage(ctx, xslices.Map(messageIDs, func(m imap.MessageID) string {
|
||||||
|
return string(m)
|
||||||
// There's currently no limit on how many IDs we can filter on,
|
|
||||||
// but to be nice to API, let's chunk it by 150.
|
|
||||||
for _, messageIDs := range xslices.Chunk(messageIDs, 150) {
|
|
||||||
m, err := conn.client.GetMessageMetadata(ctx, proton.MessageFilter{
|
|
||||||
ID: mapTo[imap.MessageID, string](messageIDs),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a message is not preset in any other label other than AllMail, AllDrafts and AllSent, it can be
|
|
||||||
// permanently deleted.
|
|
||||||
m = xslices.Filter(m, func(m proton.MessageMetadata) bool {
|
|
||||||
labelsThatMatter := xslices.Filter(m.LabelIDs, func(id string) bool {
|
|
||||||
return id != proton.AllDraftsLabel && id != proton.AllMailLabel && id != proton.AllSentLabel
|
|
||||||
})
|
|
||||||
return len(labelsThatMatter) == 0
|
|
||||||
})
|
|
||||||
|
|
||||||
metadata = append(metadata, m...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := conn.client.DeleteMessage(ctx, xslices.Map(metadata, func(m proton.MessageMetadata) string {
|
|
||||||
return m.ID
|
|
||||||
})...); err != nil {
|
})...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -626,7 +609,7 @@ func (conn *imapConnector) createDraft(ctx context.Context, literal []byte, addr
|
|||||||
return proton.Message{}, fmt.Errorf("failed to create parser: %w", err)
|
return proton.Message{}, fmt.Errorf("failed to create parser: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
message, err := message.ParseWithParser(parser)
|
message, err := message.ParseWithParser(parser, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return proton.Message{}, fmt.Errorf("failed to parse message: %w", err)
|
return proton.Message{}, fmt.Errorf("failed to parse message: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,10 +18,7 @@
|
|||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
@ -219,73 +216,7 @@ func (h *sendRecorder) getWaitCh(hash string) (<-chan struct{}, bool) {
|
|||||||
// - the Content-Disposition header of each (leaf) part,
|
// - the Content-Disposition header of each (leaf) part,
|
||||||
// - the (decoded) body of each part.
|
// - the (decoded) body of each part.
|
||||||
func getMessageHash(b []byte) (string, error) {
|
func getMessageHash(b []byte) (string, error) {
|
||||||
section := rfc822.Parse(b)
|
return rfc822.GetMessageHash(b)
|
||||||
|
|
||||||
header, err := section.ParseHeader()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
h := sha256.New()
|
|
||||||
|
|
||||||
if _, err := h.Write([]byte(header.Get("Subject"))); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := h.Write([]byte(header.Get("From"))); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := h.Write([]byte(header.Get("To"))); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := h.Write([]byte(header.Get("Cc"))); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := h.Write([]byte(header.Get("Reply-To"))); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := h.Write([]byte(header.Get("In-Reply-To"))); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := section.Walk(func(section *rfc822.Section) error {
|
|
||||||
children, err := section.Children()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else if len(children) > 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
header, err := section.ParseHeader()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := h.Write([]byte(header.Get("Content-Type"))); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := h.Write([]byte(header.Get("Content-Disposition"))); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
body := section.Body()
|
|
||||||
body = bytes.ReplaceAll(body, []byte{'\r'}, nil)
|
|
||||||
body = bytes.TrimSpace(body)
|
|
||||||
if _, err := h.Write(body); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchToList(a, b []string) bool {
|
func matchToList(a, b []string) bool {
|
||||||
|
|||||||
@ -140,7 +140,7 @@ func (user *User) sendMail(authID string, from string, to []string, r io.Reader)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse the message we want to send (after we have attached the public key).
|
// Parse the message we want to send (after we have attached the public key).
|
||||||
message, err := message.ParseWithParser(parser)
|
message, err := message.ParseWithParser(parser, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to parse message: %w", err)
|
return fmt.Errorf("failed to parse message: %w", err)
|
||||||
}
|
}
|
||||||
@ -300,7 +300,9 @@ func getParentID(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If no parent was found, try to find it in the last external reference.
|
// If no parent was found, try to find it in the last external reference.
|
||||||
// There can be multiple messages with the same external ID; in this case, we don't pick any parent.
|
// There can be multiple messages with the same external ID; in this case, we first look if
|
||||||
|
// there is a single one sent by this account (with the `MessageFlagSent` flag set), if yes,
|
||||||
|
// then pick that, otherwise don't pick any parent.
|
||||||
if parentID == "" && len(external) > 0 {
|
if parentID == "" && len(external) > 0 {
|
||||||
var addrID string
|
var addrID string
|
||||||
|
|
||||||
@ -316,8 +318,21 @@ func getParentID(
|
|||||||
return "", fmt.Errorf("failed to get message metadata: %w", err)
|
return "", fmt.Errorf("failed to get message metadata: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(metadata) == 1 {
|
switch len(metadata) {
|
||||||
|
case 1:
|
||||||
|
// found exactly one parent
|
||||||
parentID = metadata[0].ID
|
parentID = metadata[0].ID
|
||||||
|
case 0:
|
||||||
|
// found no parents
|
||||||
|
default:
|
||||||
|
// found multiple parents, search through metadata to try to find a singular parent that
|
||||||
|
// was sent by this account.
|
||||||
|
for _, metadata := range metadata {
|
||||||
|
if metadata.Flags.Has(proton.MessageFlagSent) {
|
||||||
|
parentID = metadata.ID
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -262,7 +262,7 @@ func (user *User) syncMessages(
|
|||||||
syncStartTime := time.Now()
|
syncStartTime := time.Now()
|
||||||
defer func() { logrus.WithField("duration", time.Since(syncStartTime)).Info("Message sync completed") }()
|
defer func() { logrus.WithField("duration", time.Since(syncStartTime)).Info("Message sync completed") }()
|
||||||
|
|
||||||
logrus.WithFields(logrus.Fields{
|
user.log.WithFields(logrus.Fields{
|
||||||
"messages": len(messageIDs),
|
"messages": len(messageIDs),
|
||||||
"numCPU": runtime.NumCPU(),
|
"numCPU": runtime.NumCPU(),
|
||||||
}).Info("Starting message sync")
|
}).Info("Starting message sync")
|
||||||
|
|||||||
@ -119,6 +119,12 @@ func New(
|
|||||||
return nil, fmt.Errorf("failed to get labels: %w", err)
|
return nil, fmt.Errorf("failed to get labels: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"userID": apiUser.ID,
|
||||||
|
"numAddr": len(apiAddrs),
|
||||||
|
"numLabels": len(apiLabels),
|
||||||
|
}).Info("Creating user object")
|
||||||
|
|
||||||
// Create the user object.
|
// Create the user object.
|
||||||
user := &User{
|
user := &User{
|
||||||
log: logrus.WithField("userID", apiUser.ID),
|
log: logrus.WithField("userID", apiUser.ID),
|
||||||
@ -591,6 +597,36 @@ func (user *User) Close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsTelemetryEnabled check if the telemetry is enabled or disabled for this user.
|
||||||
|
func (user *User) IsTelemetryEnabled(ctx context.Context) bool {
|
||||||
|
settings, err := user.client.GetUserSettings(ctx)
|
||||||
|
if err != nil {
|
||||||
|
user.log.WithError(err).Error("Failed to retrieve API user Settings")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return settings.Telemetry == proton.SettingEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendTelemetry send telemetry request.
|
||||||
|
func (user *User) SendTelemetry(ctx context.Context, data []byte) error {
|
||||||
|
var req proton.SendStatsReq
|
||||||
|
if err := json.Unmarshal(data, &req); err != nil {
|
||||||
|
user.log.WithError(err).Error("Failed to build telemetry request.")
|
||||||
|
if err := user.reporter.ReportMessageWithContext("Failed to build telemetry request.", reporter.Context{
|
||||||
|
"error": err,
|
||||||
|
}); err != nil {
|
||||||
|
logrus.WithError(err).Error("Failed to report telemetry request build error")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err := user.client.SendDataEvent(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
user.log.WithError(err).Error("Failed to send telemetry.")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// initUpdateCh initializes the user's update channels in the given address mode.
|
// initUpdateCh initializes the user's update channels in the given address mode.
|
||||||
// It is assumed that user.apiAddrs and user.updateCh are already locked.
|
// It is assumed that user.apiAddrs and user.updateCh are already locked.
|
||||||
func (user *User) initUpdateCh(mode vault.AddressMode) {
|
func (user *User) initUpdateCh(mode vault.AddressMode) {
|
||||||
|
|||||||
@ -80,6 +80,23 @@ func TestUser_AddressMode(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUser_Telemetry(t *testing.T) {
|
||||||
|
withAPI(t, context.Background(), func(ctx context.Context, s *server.Server, m *proton.Manager) {
|
||||||
|
withAccount(t, s, "username", "password", []string{}, func(string, []string) {
|
||||||
|
withUser(t, ctx, s, m, "username", "password", func(user *User) {
|
||||||
|
// By default, user should have Telemetry enabled.
|
||||||
|
telemetry := user.IsTelemetryEnabled(ctx)
|
||||||
|
require.Equal(t, true, telemetry)
|
||||||
|
|
||||||
|
user.client.Close()
|
||||||
|
// If telemetry cannot be retrieved it is disabled.
|
||||||
|
telemetry = user.IsTelemetryEnabled(ctx)
|
||||||
|
require.Equal(t, false, telemetry)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func withAPI(_ testing.TB, ctx context.Context, fn func(context.Context, *server.Server, *proton.Manager)) { //nolint:revive
|
func withAPI(_ testing.TB, ctx context.Context, fn func(context.Context, *server.Server, *proton.Manager)) { //nolint:revive
|
||||||
server := server.New()
|
server := server.New()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|||||||
@ -44,6 +44,20 @@ func (ua *UserAgent) SetClient(name, version string) {
|
|||||||
ua.client = fmt.Sprintf("%v/%v", name, regexp.MustCompile(`(.*) \((.*)\)`).ReplaceAllString(version, "$1-$2"))
|
ua.client = fmt.Sprintf("%v/%v", name, regexp.MustCompile(`(.*) \((.*)\)`).ReplaceAllString(version, "$1-$2"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ua *UserAgent) SetClientString(client string) {
|
||||||
|
ua.lock.Lock()
|
||||||
|
defer ua.lock.Unlock()
|
||||||
|
|
||||||
|
ua.client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ua *UserAgent) GetClientString() string {
|
||||||
|
ua.lock.RLock()
|
||||||
|
defer ua.lock.RUnlock()
|
||||||
|
|
||||||
|
return ua.client
|
||||||
|
}
|
||||||
|
|
||||||
func (ua *UserAgent) HasClient() bool {
|
func (ua *UserAgent) HasClient() bool {
|
||||||
ua.lock.RLock()
|
ua.lock.RLock()
|
||||||
defer ua.lock.RUnlock()
|
defer ua.lock.RUnlock()
|
||||||
|
|||||||
@ -20,6 +20,7 @@ package vault
|
|||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
|
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
|
||||||
@ -184,6 +185,18 @@ func (vault *Vault) SetAutoUpdate(autoUpdate bool) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTelemetryDisabled checks whether telemetry is disabled.
|
||||||
|
func (vault *Vault) GetTelemetryDisabled() bool {
|
||||||
|
return vault.get().Settings.TelemetryDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTelemetryDisabled sets whether telemetry is disabled.
|
||||||
|
func (vault *Vault) SetTelemetryDisabled(telemetryDisabled bool) error {
|
||||||
|
return vault.mod(func(data *Data) {
|
||||||
|
data.Settings.TelemetryDisabled = telemetryDisabled
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// GetLastVersion returns the last version of the bridge that was run.
|
// GetLastVersion returns the last version of the bridge that was run.
|
||||||
func (vault *Vault) GetLastVersion() *semver.Version {
|
func (vault *Vault) GetLastVersion() *semver.Version {
|
||||||
return semver.MustParse(vault.get().Settings.LastVersion)
|
return semver.MustParse(vault.get().Settings.LastVersion)
|
||||||
@ -225,3 +238,34 @@ func (vault *Vault) SetMaxSyncMemory(maxMemory uint64) error {
|
|||||||
data.Settings.MaxSyncMemory = maxMemory
|
data.Settings.MaxSyncMemory = maxMemory
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLastUserAgent returns the last user agent recorded by bridge.
|
||||||
|
func (vault *Vault) GetLastUserAgent() string {
|
||||||
|
v := vault.get().Settings.LastUserAgent
|
||||||
|
|
||||||
|
// Handle case where there may be no value.
|
||||||
|
if len(v) == 0 {
|
||||||
|
v = DefaultUserAgent
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLastUserAgent store the last user agent recorded by bridge.
|
||||||
|
func (vault *Vault) SetLastUserAgent(userAgent string) error {
|
||||||
|
return vault.mod(func(data *Data) {
|
||||||
|
data.Settings.LastUserAgent = userAgent
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLastHeartbeatSent returns the last time heartbeat was sent.
|
||||||
|
func (vault *Vault) GetLastHeartbeatSent() time.Time {
|
||||||
|
return vault.get().Settings.LastHeartbeatSent
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLastHeartbeatSent store the last time heartbeat was sent.
|
||||||
|
func (vault *Vault) SetLastHeartbeatSent(timestamp time.Time) error {
|
||||||
|
return vault.mod(func(data *Data) {
|
||||||
|
data.Settings.LastHeartbeatSent = timestamp
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@ -153,6 +153,20 @@ func TestVault_Settings_ShowAllMail(t *testing.T) {
|
|||||||
require.Equal(t, false, s.GetShowAllMail())
|
require.Equal(t, false, s.GetShowAllMail())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVault_Settings_TelemetryDisabled(t *testing.T) {
|
||||||
|
// create a new test vault.
|
||||||
|
s := newVault(t)
|
||||||
|
|
||||||
|
// Check the default show all mail setting.
|
||||||
|
require.Equal(t, false, s.GetTelemetryDisabled())
|
||||||
|
|
||||||
|
// Modify the show all mail setting.
|
||||||
|
require.NoError(t, s.SetTelemetryDisabled(true))
|
||||||
|
|
||||||
|
// Check the new show all mail setting.
|
||||||
|
require.Equal(t, true, s.GetTelemetryDisabled())
|
||||||
|
}
|
||||||
|
|
||||||
func TestVault_Settings_Autostart(t *testing.T) {
|
func TestVault_Settings_Autostart(t *testing.T) {
|
||||||
// create a new test vault.
|
// create a new test vault.
|
||||||
s := newVault(t)
|
s := newVault(t)
|
||||||
@ -216,3 +230,11 @@ func TestVault_Settings_MaxSyncMemory(t *testing.T) {
|
|||||||
// Check the default first start value.
|
// Check the default first start value.
|
||||||
require.Equal(t, vault.DefaultMaxSyncMemory, s.GetMaxSyncMemory())
|
require.Equal(t, vault.DefaultMaxSyncMemory, s.GetMaxSyncMemory())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVault_Settings_LastUserAgent(t *testing.T) {
|
||||||
|
// create a new test vault.
|
||||||
|
s := newVault(t)
|
||||||
|
|
||||||
|
// Check the default first start value.
|
||||||
|
require.Equal(t, vault.DefaultUserAgent, s.GetLastUserAgent())
|
||||||
|
}
|
||||||
|
|||||||
@ -20,8 +20,10 @@ package vault
|
|||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
|
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v3/pkg/ports"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Settings struct {
|
type Settings struct {
|
||||||
@ -35,23 +37,29 @@ type Settings struct {
|
|||||||
UpdateChannel updater.Channel
|
UpdateChannel updater.Channel
|
||||||
UpdateRollout float64
|
UpdateRollout float64
|
||||||
|
|
||||||
ColorScheme string
|
ColorScheme string
|
||||||
ProxyAllowed bool
|
ProxyAllowed bool
|
||||||
ShowAllMail bool
|
ShowAllMail bool
|
||||||
Autostart bool
|
Autostart bool
|
||||||
AutoUpdate bool
|
AutoUpdate bool
|
||||||
|
TelemetryDisabled bool
|
||||||
|
|
||||||
LastVersion string
|
LastVersion string
|
||||||
FirstStart bool
|
FirstStart bool
|
||||||
|
|
||||||
MaxSyncMemory uint64
|
MaxSyncMemory uint64
|
||||||
|
|
||||||
|
LastUserAgent string
|
||||||
|
|
||||||
|
LastHeartbeatSent time.Time
|
||||||
|
|
||||||
// **WARNING**: These entry can't be removed until they vault has proper migration support.
|
// **WARNING**: These entry can't be removed until they vault has proper migration support.
|
||||||
SyncWorkers int
|
SyncWorkers int
|
||||||
SyncAttPool int
|
SyncAttPool int
|
||||||
}
|
}
|
||||||
|
|
||||||
const DefaultMaxSyncMemory = 2 * 1024 * uint64(1024*1024)
|
const DefaultMaxSyncMemory = 2 * 1024 * uint64(1024*1024)
|
||||||
|
const DefaultUserAgent = "UnknownClient/0.0.1"
|
||||||
|
|
||||||
func GetDefaultSyncWorkerCount() int {
|
func GetDefaultSyncWorkerCount() int {
|
||||||
const minSyncWorkers = 16
|
const minSyncWorkers = 16
|
||||||
@ -67,23 +75,26 @@ func GetDefaultSyncWorkerCount() int {
|
|||||||
|
|
||||||
func newDefaultSettings(gluonDir string) Settings {
|
func newDefaultSettings(gluonDir string) Settings {
|
||||||
syncWorkers := GetDefaultSyncWorkerCount()
|
syncWorkers := GetDefaultSyncWorkerCount()
|
||||||
|
imapPort := ports.FindFreePortFrom(1143)
|
||||||
|
smtpPort := ports.FindFreePortFrom(1025, imapPort)
|
||||||
|
|
||||||
return Settings{
|
return Settings{
|
||||||
GluonDir: gluonDir,
|
GluonDir: gluonDir,
|
||||||
|
|
||||||
IMAPPort: 1143,
|
IMAPPort: imapPort,
|
||||||
SMTPPort: 1025,
|
SMTPPort: smtpPort,
|
||||||
IMAPSSL: false,
|
IMAPSSL: false,
|
||||||
SMTPSSL: false,
|
SMTPSSL: false,
|
||||||
|
|
||||||
UpdateChannel: updater.DefaultUpdateChannel,
|
UpdateChannel: updater.DefaultUpdateChannel,
|
||||||
UpdateRollout: rand.Float64(), //nolint:gosec
|
UpdateRollout: rand.Float64(), //nolint:gosec
|
||||||
|
|
||||||
ColorScheme: "",
|
ColorScheme: "",
|
||||||
ProxyAllowed: false,
|
ProxyAllowed: false,
|
||||||
ShowAllMail: true,
|
ShowAllMail: true,
|
||||||
Autostart: true,
|
Autostart: true,
|
||||||
AutoUpdate: true,
|
AutoUpdate: true,
|
||||||
|
TelemetryDisabled: false,
|
||||||
|
|
||||||
LastVersion: "0.0.0",
|
LastVersion: "0.0.0",
|
||||||
FirstStart: true,
|
FirstStart: true,
|
||||||
@ -91,5 +102,8 @@ func newDefaultSettings(gluonDir string) Settings {
|
|||||||
MaxSyncMemory: DefaultMaxSyncMemory,
|
MaxSyncMemory: DefaultMaxSyncMemory,
|
||||||
SyncWorkers: syncWorkers,
|
SyncWorkers: syncWorkers,
|
||||||
SyncAttPool: syncWorkers,
|
SyncAttPool: syncWorkers,
|
||||||
|
|
||||||
|
LastUserAgent: DefaultUserAgent,
|
||||||
|
LastHeartbeatSent: time.Time{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,7 +38,7 @@ func init() { //nolint:gochecknoinits
|
|||||||
Helpers[MacOSKeychain] = newMacOSHelper
|
Helpers[MacOSKeychain] = newMacOSHelper
|
||||||
|
|
||||||
// Use MacOSKeychain by default.
|
// Use MacOSKeychain by default.
|
||||||
defaultHelper = MacOSKeychain
|
DefaultHelper = MacOSKeychain
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseError(original error) error {
|
func parseError(original error) error {
|
||||||
|
|||||||
@ -48,14 +48,14 @@ func init() { //nolint:gochecknoinits
|
|||||||
Helpers[Pass] = newPassHelper
|
Helpers[Pass] = newPassHelper
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultHelper = SecretServiceDBus
|
DefaultHelper = SecretServiceDBus
|
||||||
|
|
||||||
// If Pass is available, use it by default.
|
// If Pass is available, use it by default.
|
||||||
// Otherwise, if SecretService is available, use it by default.
|
// Otherwise, if SecretService is available, use it by default.
|
||||||
if _, ok := Helpers[Pass]; ok {
|
if _, ok := Helpers[Pass]; ok {
|
||||||
defaultHelper = Pass
|
DefaultHelper = Pass
|
||||||
} else if _, ok := Helpers[SecretService]; ok {
|
} else if _, ok := Helpers[SecretService]; ok {
|
||||||
defaultHelper = SecretService
|
DefaultHelper = SecretService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,7 @@ func init() { //nolint:gochecknoinits
|
|||||||
Helpers[WindowsCredentials] = newWinCredHelper
|
Helpers[WindowsCredentials] = newWinCredHelper
|
||||||
|
|
||||||
// Use WindowsCredentials by default.
|
// Use WindowsCredentials by default.
|
||||||
defaultHelper = WindowsCredentials
|
DefaultHelper = WindowsCredentials
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWinCredHelper(string) (credentials.Helper, error) {
|
func newWinCredHelper(string) (credentials.Helper, error) {
|
||||||
|
|||||||
@ -42,8 +42,8 @@ var (
|
|||||||
// Helpers holds all discovered keychain helpers. It is populated in init().
|
// Helpers holds all discovered keychain helpers. It is populated in init().
|
||||||
Helpers map[string]helperConstructor //nolint:gochecknoglobals
|
Helpers map[string]helperConstructor //nolint:gochecknoglobals
|
||||||
|
|
||||||
// defaultHelper is the default helper to use if the user hasn't yet set a preference.
|
// DefaultHelper is the default helper to use if the user hasn't yet set a preference.
|
||||||
defaultHelper string //nolint:gochecknoglobals
|
DefaultHelper string //nolint:gochecknoglobals
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewKeychain creates a new native keychain.
|
// NewKeychain creates a new native keychain.
|
||||||
@ -55,7 +55,7 @@ func NewKeychain(preferred, keychainName string) (*Keychain, error) {
|
|||||||
|
|
||||||
// If the preferred keychain is unsupported, fallback to the default one.
|
// If the preferred keychain is unsupported, fallback to the default one.
|
||||||
if _, ok := Helpers[preferred]; !ok {
|
if _, ok := Helpers[preferred]; !ok {
|
||||||
preferred = defaultHelper
|
preferred = DefaultHelper
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the user's preferred keychain helper.
|
// Load the user's preferred keychain helper.
|
||||||
|
|||||||
@ -441,7 +441,7 @@ func getMessageHeader(msg proton.Message, opts JobOptions) message.Header {
|
|||||||
hdr.Set("From", msg.Sender.String())
|
hdr.Set("From", msg.Sender.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(msg.ReplyTos) > 0 {
|
if len(msg.ReplyTos) > 0 && !msg.IsDraft() {
|
||||||
if !(len(msg.ReplyTos) == 1 && addressEmpty(msg.ReplyTos[0])) {
|
if !(len(msg.ReplyTos) == 1 && addressEmpty(msg.ReplyTos[0])) {
|
||||||
hdr.Set("Reply-To", toAddressList(msg.ReplyTos))
|
hdr.Set("Reply-To", toAddressList(msg.ReplyTos))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -431,7 +431,7 @@ func TestBuildSignedPlainEncryptedMessageWithPubKey(t *testing.T) {
|
|||||||
section(t, res).
|
section(t, res).
|
||||||
expectDate(is(`Wed, 01 Jan 2020 00:00:00 +0000`)).
|
expectDate(is(`Wed, 01 Jan 2020 00:00:00 +0000`)).
|
||||||
expectContentType(is(`multipart/signed`)).
|
expectContentType(is(`multipart/signed`)).
|
||||||
expectContentTypeParam(`micalg`, is(`pgp-sha256`)).
|
expectContentTypeParam(`micalg`, is(`SHA-256`)).
|
||||||
expectContentTypeParam(`protocol`, is(`application/pgp-signature`))
|
expectContentTypeParam(`protocol`, is(`application/pgp-signature`))
|
||||||
|
|
||||||
section(t, res, 1).
|
section(t, res, 1).
|
||||||
@ -439,7 +439,7 @@ func TestBuildSignedPlainEncryptedMessageWithPubKey(t *testing.T) {
|
|||||||
expectContentTypeParam(`protected-headers`, is(`v1`)).
|
expectContentTypeParam(`protected-headers`, is(`v1`)).
|
||||||
expectHeader(`Subject`, is(`simple plaintext body`)).
|
expectHeader(`Subject`, is(`simple plaintext body`)).
|
||||||
expectHeader(`From`, is(`"pm.bridge.qa" <pm.bridge.qa@gmail.com>`)).
|
expectHeader(`From`, is(`"pm.bridge.qa" <pm.bridge.qa@gmail.com>`)).
|
||||||
expectHeader(`To`, is(`schizofrenic@pm.me`)).
|
expectHeader(`To`, is("\"InfernalBridgeTester@proton.me\" <InfernalbridgeTester@proton.me>")).
|
||||||
expectSection(verifiesAgainst(section(t, res, 1, 1, 2).pubKey(), section(t, res, 2).signature()))
|
expectSection(verifiesAgainst(section(t, res, 1, 1, 2).pubKey(), section(t, res, 2).signature()))
|
||||||
|
|
||||||
section(t, res, 1, 1).
|
section(t, res, 1, 1).
|
||||||
@ -477,7 +477,7 @@ func TestBuildSignedHTMLEncryptedMessageWithPubKey(t *testing.T) {
|
|||||||
section(t, res).
|
section(t, res).
|
||||||
expectDate(is(`Wed, 01 Jan 2020 00:00:00 +0000`)).
|
expectDate(is(`Wed, 01 Jan 2020 00:00:00 +0000`)).
|
||||||
expectContentType(is(`multipart/signed`)).
|
expectContentType(is(`multipart/signed`)).
|
||||||
expectContentTypeParam(`micalg`, is(`pgp-sha256`)).
|
expectContentTypeParam(`micalg`, is(`SHA-256`)).
|
||||||
expectContentTypeParam(`protocol`, is(`application/pgp-signature`))
|
expectContentTypeParam(`protocol`, is(`application/pgp-signature`))
|
||||||
|
|
||||||
section(t, res, 1).
|
section(t, res, 1).
|
||||||
@ -485,7 +485,7 @@ func TestBuildSignedHTMLEncryptedMessageWithPubKey(t *testing.T) {
|
|||||||
expectContentTypeParam(`protected-headers`, is(`v1`)).
|
expectContentTypeParam(`protected-headers`, is(`v1`)).
|
||||||
expectHeader(`Subject`, is(`simple html body`)).
|
expectHeader(`Subject`, is(`simple html body`)).
|
||||||
expectHeader(`From`, is(`"pm.bridge.qa" <pm.bridge.qa@gmail.com>`)).
|
expectHeader(`From`, is(`"pm.bridge.qa" <pm.bridge.qa@gmail.com>`)).
|
||||||
expectHeader(`To`, is(`schizofrenic@pm.me`)).
|
expectHeader(`To`, is("\"InfernalBridgeTester@proton.me\" <InfernalbridgeTester@proton.me>")).
|
||||||
expectSection(verifiesAgainst(section(t, res, 1, 1, 2).pubKey(), section(t, res, 2).signature()))
|
expectSection(verifiesAgainst(section(t, res, 1, 1, 2).pubKey(), section(t, res, 2).signature()))
|
||||||
|
|
||||||
section(t, res, 1, 1).
|
section(t, res, 1, 1).
|
||||||
@ -524,7 +524,7 @@ func TestBuildSignedMultipartAlternativeEncryptedMessageWithPubKey(t *testing.T)
|
|||||||
section(t, res).
|
section(t, res).
|
||||||
expectDate(is(`Wed, 01 Jan 2020 00:00:00 +0000`)).
|
expectDate(is(`Wed, 01 Jan 2020 00:00:00 +0000`)).
|
||||||
expectContentType(is(`multipart/signed`)).
|
expectContentType(is(`multipart/signed`)).
|
||||||
expectContentTypeParam(`micalg`, is(`pgp-sha256`)).
|
expectContentTypeParam(`micalg`, is(`SHA-256`)).
|
||||||
expectContentTypeParam(`protocol`, is(`application/pgp-signature`))
|
expectContentTypeParam(`protocol`, is(`application/pgp-signature`))
|
||||||
|
|
||||||
section(t, res, 1).
|
section(t, res, 1).
|
||||||
@ -532,8 +532,8 @@ func TestBuildSignedMultipartAlternativeEncryptedMessageWithPubKey(t *testing.T)
|
|||||||
expectContentTypeParam(`protected-headers`, is(`v1`)).
|
expectContentTypeParam(`protected-headers`, is(`v1`)).
|
||||||
expectHeader(`Subject`, is(`Alternative`)).
|
expectHeader(`Subject`, is(`Alternative`)).
|
||||||
expectHeader(`From`, is(`"pm.bridge.qa" <pm.bridge.qa@gmail.com>`)).
|
expectHeader(`From`, is(`"pm.bridge.qa" <pm.bridge.qa@gmail.com>`)).
|
||||||
expectHeader(`To`, is(`schizofrenic@pm.me`)).
|
expectHeader(`To`, is("\"InfernalBridgeTester@proton.me\" <InfernalbridgeTester@proton.me>")).
|
||||||
expectSection(verifiesAgainst(section(t, res, 1, 1, 3).pubKey(), section(t, res, 2).signature()))
|
expectSection(verifiesAgainst(section(t, res, 1, 1, 2).pubKey(), section(t, res, 2).signature()))
|
||||||
|
|
||||||
section(t, res, 1, 1).
|
section(t, res, 1, 1).
|
||||||
expectContentType(is(`multipart/mixed`))
|
expectContentType(is(`multipart/mixed`))
|
||||||
@ -549,17 +549,11 @@ func TestBuildSignedMultipartAlternativeEncryptedMessageWithPubKey(t *testing.T)
|
|||||||
|
|
||||||
section(t, res, 1, 1, 1, 2).
|
section(t, res, 1, 1, 1, 2).
|
||||||
expectContentType(is(`text/html`)).
|
expectContentType(is(`text/html`)).
|
||||||
expectBody(contains(`This <font color="#ee24cc">Rich</font> formated text`)).
|
expectBody(contains(`This Rich formated text`)).
|
||||||
expectBody(contains(`What kind of shoes do ninjas wear`)).
|
expectBody(contains(`What kind of shoes do ninjas wear`)).
|
||||||
expectBody(contains(`How does a penguin build its house`))
|
expectBody(contains(`How does a penguin build its house`))
|
||||||
|
|
||||||
section(t, res, 1, 1, 2).
|
section(t, res, 1, 1, 2).
|
||||||
expectContentType(is(`application/pdf`)).
|
|
||||||
expectTransferEncoding(is(`base64`)).
|
|
||||||
expectContentTypeParam(`name`, is(`minimal.pdf`)).
|
|
||||||
expectContentDispositionParam(`filename`, is(`minimal.pdf`))
|
|
||||||
|
|
||||||
section(t, res, 1, 1, 3).
|
|
||||||
expectContentType(is(`application/pgp-keys`)).
|
expectContentType(is(`application/pgp-keys`)).
|
||||||
expectContentTypeParam(`name`, is(`OpenPGP_0x161C0875822359F7.asc`)).
|
expectContentTypeParam(`name`, is(`OpenPGP_0x161C0875822359F7.asc`)).
|
||||||
expectContentDisposition(is(`attachment`)).
|
expectContentDisposition(is(`attachment`)).
|
||||||
@ -587,16 +581,16 @@ func TestBuildSignedEmbeddedMessageRFC822EncryptedMessageWithPubKey(t *testing.T
|
|||||||
section(t, res).
|
section(t, res).
|
||||||
expectDate(is(`Wed, 01 Jan 2020 00:00:00 +0000`)).
|
expectDate(is(`Wed, 01 Jan 2020 00:00:00 +0000`)).
|
||||||
expectContentType(is(`multipart/signed`)).
|
expectContentType(is(`multipart/signed`)).
|
||||||
expectContentTypeParam(`micalg`, is(`pgp-sha256`)).
|
expectContentTypeParam(`micalg`, is(`SHA-256`)).
|
||||||
expectContentTypeParam(`protocol`, is(`application/pgp-signature`))
|
expectContentTypeParam(`protocol`, is(`application/pgp-signature`))
|
||||||
|
|
||||||
section(t, res, 1).
|
section(t, res, 1).
|
||||||
expectContentType(is(`multipart/mixed`)).
|
expectContentType(is(`multipart/mixed`)).
|
||||||
expectContentTypeParam(`protected-headers`, is(`v1`)).
|
expectContentTypeParam(`protected-headers`, is(`v1`)).
|
||||||
expectHeader(`Subject`, is(`Fwd: HTML with attachment external PGP`)).
|
expectHeader(`Subject`, is(`Fwd: simple html body`)).
|
||||||
expectHeader(`From`, is(`"pm.bridge.qa" <pm.bridge.qa@gmail.com>`)).
|
expectHeader(`From`, is(`"pm.bridge.qa" <pm.bridge.qa@gmail.com>`)).
|
||||||
expectHeader(`To`, is(`schizofrenic@pm.me`)).
|
expectHeader(`To`, is("\"InfernalBridgeTester@proton.me\" <InfernalbridgeTester@proton.me>")).
|
||||||
expectSection(verifiesAgainst(section(t, res, 1, 1, 2).pubKey(), section(t, res, 2).signature()))
|
expectSection(verifiesAgainst(section(t, res, 1, 1, 3).pubKey(), section(t, res, 2).signature()))
|
||||||
|
|
||||||
section(t, res, 1, 1).
|
section(t, res, 1, 1).
|
||||||
expectContentType(is(`multipart/mixed`))
|
expectContentType(is(`multipart/mixed`))
|
||||||
@ -605,17 +599,17 @@ func TestBuildSignedEmbeddedMessageRFC822EncryptedMessageWithPubKey(t *testing.T
|
|||||||
expectContentType(is(`text/plain`))
|
expectContentType(is(`text/plain`))
|
||||||
|
|
||||||
section(t, res, 1, 1, 2).
|
section(t, res, 1, 1, 2).
|
||||||
|
expectContentType(is(`message/rfc822`)).
|
||||||
|
expectContentTypeParam(`name`, is(`simple html body.eml`)).
|
||||||
|
expectContentDisposition(is(`attachment`)).
|
||||||
|
expectContentDispositionParam(`filename`, is(`simple html body.eml`))
|
||||||
|
|
||||||
|
section(t, res, 1, 1, 3).
|
||||||
expectContentType(is(`application/pgp-keys`)).
|
expectContentType(is(`application/pgp-keys`)).
|
||||||
expectContentTypeParam(`name`, is(`OpenPGP_0x161C0875822359F7.asc`)).
|
expectContentTypeParam(`name`, is(`OpenPGP_0x161C0875822359F7.asc`)).
|
||||||
expectContentDisposition(is(`attachment`)).
|
expectContentDisposition(is(`attachment`)).
|
||||||
expectContentDispositionParam(`filename`, is(`OpenPGP_0x161C0875822359F7.asc`))
|
expectContentDispositionParam(`filename`, is(`OpenPGP_0x161C0875822359F7.asc`))
|
||||||
|
|
||||||
section(t, res, 1, 1, 3).
|
|
||||||
expectContentType(is(`message/rfc822`)).
|
|
||||||
expectContentTypeParam(`name`, is(`HTML with attachment external PGP.eml`)).
|
|
||||||
expectContentDisposition(is(`attachment`)).
|
|
||||||
expectContentDispositionParam(`filename`, is(`HTML with attachment external PGP.eml`))
|
|
||||||
|
|
||||||
section(t, res, 2).
|
section(t, res, 2).
|
||||||
expectContentType(is(`application/pgp-signature`)).
|
expectContentType(is(`application/pgp-signature`)).
|
||||||
expectContentTypeParam(`name`, is(`OpenPGP_signature.asc`)).
|
expectContentTypeParam(`name`, is(`OpenPGP_signature.asc`)).
|
||||||
|
|||||||
@ -73,6 +73,15 @@ type Attachment struct {
|
|||||||
|
|
||||||
// Parse parses an RFC822 message.
|
// Parse parses an RFC822 message.
|
||||||
func Parse(r io.Reader) (m Message, err error) {
|
func Parse(r io.Reader) (m Message, err error) {
|
||||||
|
return parseIOReaderImpl(r, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseAndAllowInvalidAddressLists parses an RFC822 message and allows email address lists to be invalid.
|
||||||
|
func ParseAndAllowInvalidAddressLists(r io.Reader) (m Message, err error) {
|
||||||
|
return parseIOReaderImpl(r, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseIOReaderImpl(r io.Reader, allowInvalidAddressLists bool) (m Message, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
err = fmt.Errorf("panic while parsing message: %v", r)
|
err = fmt.Errorf("panic while parsing message: %v", r)
|
||||||
@ -84,21 +93,21 @@ func Parse(r io.Reader) (m Message, err error) {
|
|||||||
return Message{}, errors.Wrap(err, "failed to create new parser")
|
return Message{}, errors.Wrap(err, "failed to create new parser")
|
||||||
}
|
}
|
||||||
|
|
||||||
return parse(p)
|
return parse(p, allowInvalidAddressLists)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseWithParser parses an RFC822 message using an existing parser.
|
// ParseWithParser parses an RFC822 message using an existing parser.
|
||||||
func ParseWithParser(p *parser.Parser) (m Message, err error) {
|
func ParseWithParser(p *parser.Parser, allowInvalidAddressLists bool) (m Message, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
err = fmt.Errorf("panic while parsing message: %v", r)
|
err = fmt.Errorf("panic while parsing message: %v", r)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return parse(p)
|
return parse(p, allowInvalidAddressLists)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse(p *parser.Parser) (Message, error) {
|
func parse(p *parser.Parser, allowInvalidAddressLists bool) (Message, error) {
|
||||||
if err := convertEncodedTransferEncoding(p); err != nil {
|
if err := convertEncodedTransferEncoding(p); err != nil {
|
||||||
return Message{}, errors.Wrap(err, "failed to convert encoded transfer encoding")
|
return Message{}, errors.Wrap(err, "failed to convert encoded transfer encoding")
|
||||||
}
|
}
|
||||||
@ -107,7 +116,7 @@ func parse(p *parser.Parser) (Message, error) {
|
|||||||
return Message{}, errors.Wrap(err, "failed to convert foreign encodings")
|
return Message{}, errors.Wrap(err, "failed to convert foreign encodings")
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := parseMessageHeader(p.Root().Header)
|
m, err := parseMessageHeader(p.Root().Header, allowInvalidAddressLists)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Message{}, errors.Wrap(err, "failed to parse message header")
|
return Message{}, errors.Wrap(err, "failed to parse message header")
|
||||||
}
|
}
|
||||||
@ -433,7 +442,7 @@ func getPlainBody(part *parser.Part) []byte {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseMessageHeader(h message.Header) (Message, error) {
|
func parseMessageHeader(h message.Header, allowInvalidAddressLists bool) (Message, error) {
|
||||||
var m Message
|
var m Message
|
||||||
|
|
||||||
for fields := h.Fields(); fields.Next(); {
|
for fields := h.Fields(); fields.Next(); {
|
||||||
@ -451,7 +460,11 @@ func parseMessageHeader(h message.Header) (Message, error) {
|
|||||||
case "from":
|
case "from":
|
||||||
sender, err := rfc5322.ParseAddressList(fields.Value())
|
sender, err := rfc5322.ParseAddressList(fields.Value())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Message{}, errors.Wrap(err, "failed to parse from")
|
if !allowInvalidAddressLists {
|
||||||
|
return Message{}, errors.Wrap(err, "failed to parse from")
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.WithError(err).Warn("failed to parse from")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(sender) > 0 {
|
if len(sender) > 0 {
|
||||||
@ -461,7 +474,11 @@ func parseMessageHeader(h message.Header) (Message, error) {
|
|||||||
case "to":
|
case "to":
|
||||||
toList, err := rfc5322.ParseAddressList(fields.Value())
|
toList, err := rfc5322.ParseAddressList(fields.Value())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Message{}, errors.Wrap(err, "failed to parse to")
|
if !allowInvalidAddressLists {
|
||||||
|
return Message{}, errors.Wrap(err, "failed to parse to")
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.WithError(err).Warn("failed to parse to")
|
||||||
}
|
}
|
||||||
|
|
||||||
m.ToList = toList
|
m.ToList = toList
|
||||||
@ -469,7 +486,11 @@ func parseMessageHeader(h message.Header) (Message, error) {
|
|||||||
case "reply-to":
|
case "reply-to":
|
||||||
replyTos, err := rfc5322.ParseAddressList(fields.Value())
|
replyTos, err := rfc5322.ParseAddressList(fields.Value())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Message{}, errors.Wrap(err, "failed to parse reply-to")
|
if !allowInvalidAddressLists {
|
||||||
|
return Message{}, errors.Wrap(err, "failed to parse reply-to")
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.WithError(err).Warn("failed to parse reply-to")
|
||||||
}
|
}
|
||||||
|
|
||||||
m.ReplyTos = replyTos
|
m.ReplyTos = replyTos
|
||||||
@ -477,7 +498,11 @@ func parseMessageHeader(h message.Header) (Message, error) {
|
|||||||
case "cc":
|
case "cc":
|
||||||
ccList, err := rfc5322.ParseAddressList(fields.Value())
|
ccList, err := rfc5322.ParseAddressList(fields.Value())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Message{}, errors.Wrap(err, "failed to parse cc")
|
if !allowInvalidAddressLists {
|
||||||
|
return Message{}, errors.Wrap(err, "failed to parse cc")
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.WithError(err).Warn("failed to parse cc")
|
||||||
}
|
}
|
||||||
|
|
||||||
m.CCList = ccList
|
m.CCList = ccList
|
||||||
@ -485,7 +510,11 @@ func parseMessageHeader(h message.Header) (Message, error) {
|
|||||||
case "bcc":
|
case "bcc":
|
||||||
bccList, err := rfc5322.ParseAddressList(fields.Value())
|
bccList, err := rfc5322.ParseAddressList(fields.Value())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Message{}, errors.Wrap(err, "failed to parse bcc")
|
if !allowInvalidAddressLists {
|
||||||
|
return Message{}, errors.Wrap(err, "failed to parse bcc")
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.WithError(err).Warn("failed to parse bcc")
|
||||||
}
|
}
|
||||||
|
|
||||||
m.BCCList = bccList
|
m.BCCList = bccList
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ProtonMail/go-proton-api"
|
"github.com/ProtonMail/go-proton-api"
|
||||||
@ -444,7 +445,7 @@ func TestParseWithAttachedPublicKey(t *testing.T) {
|
|||||||
p, err := parser.New(f)
|
p, err := parser.New(f)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
m, err := ParseWithParser(p)
|
m, err := ParseWithParser(p, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
p.AttachPublicKey("publickey", "publickeyname")
|
p.AttachPublicKey("publickey", "publickeyname")
|
||||||
@ -636,6 +637,32 @@ func TestParseIcsAttachment(t *testing.T) {
|
|||||||
assert.Equal(t, string(m.Attachments[0].Data), "This is an ics calendar invite")
|
assert.Equal(t, string(m.Attachments[0].Data), "This is an ics calendar invite")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseAllowInvalidAddress(t *testing.T) {
|
||||||
|
const literal = `To: foo
|
||||||
|
From: bar
|
||||||
|
BCC: fff
|
||||||
|
CC: FFF
|
||||||
|
Reply-To: AAA
|
||||||
|
Subject: Test
|
||||||
|
`
|
||||||
|
|
||||||
|
// This will fail as the addresses are not valid.
|
||||||
|
{
|
||||||
|
_, err := Parse(strings.NewReader(literal))
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will work as invalid addresses will be ignored.
|
||||||
|
m, err := ParseAndAllowInvalidAddressLists(strings.NewReader(literal))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Empty(t, m.ToList)
|
||||||
|
assert.Empty(t, m.Sender)
|
||||||
|
assert.Empty(t, m.CCList)
|
||||||
|
assert.Empty(t, m.BCCList)
|
||||||
|
assert.Empty(t, m.ReplyTos)
|
||||||
|
}
|
||||||
|
|
||||||
func TestParsePanic(t *testing.T) {
|
func TestParsePanic(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
|||||||
@ -1,212 +1,252 @@
|
|||||||
Content-Type: multipart/signed; micalg=pgp-sha256;
|
Content-Type: multipart/signed;
|
||||||
protocol="application/pgp-signature";
|
boundary=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855;
|
||||||
boundary="Rrmlds5vN3IeeCVjbnepHmuVgyROSBjsS"
|
micalg=SHA-256; protocol="application/pgp-signature"
|
||||||
|
|
||||||
This is an OpenPGP/MIME signed message (RFC 4880 and 3156)
|
--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||||
--Rrmlds5vN3IeeCVjbnepHmuVgyROSBjsS
|
Content-Type: multipart/mixed; boundary="------------6sNRIgaKOJHTghuLxPUzlxn1";
|
||||||
Content-Type: multipart/mixed; boundary="avFkF0LAPYPXcFHcnsgGmACbGIPeVDdYc";
|
protected-headers="v1"
|
||||||
protected-headers="v1"
|
From: "pm.bridge.qa" <pm.bridge.qa@gmail.com>
|
||||||
Subject: Fwd: HTML with attachment external PGP
|
To: "InfernalBridgeTester@proton.me" <InfernalbridgeTester@proton.me>
|
||||||
From: "pm.bridge.qa" <pm.bridge.qa@gmail.com>
|
Message-ID: <3f60022f-1ff2-9792-926b-a556cc39195d@gmail.com>
|
||||||
To: schizofrenic@pm.me
|
Subject: Fwd: simple html body
|
||||||
Message-ID: <7c04869b-c470-116f-b8e5-8b4fd5e1195d@gmail.com>
|
References: <1655e652-1abc-575f-6e03-5bbf119afbfa@gmail.com>
|
||||||
References: <LCxIUvb0rqBufdwR4JNxMg4ZDMI8x7pRG0vEHGHYwA@cp7-web-042.plabs.ch>
|
In-Reply-To: <1655e652-1abc-575f-6e03-5bbf119afbfa@gmail.com>
|
||||||
In-Reply-To: <LCxIUvb0rqBufdwR4JNxMg4ZDMI8x7pRG0vEHGHYwA@cp7-web-042.plabs.ch>
|
|
||||||
|
--------------6sNRIgaKOJHTghuLxPUzlxn1
|
||||||
--avFkF0LAPYPXcFHcnsgGmACbGIPeVDdYc
|
Content-Type: multipart/mixed; boundary="------------Z0a0IncIqoVlOIpOfcALEK5P"
|
||||||
Content-Type: multipart/mixed;
|
|
||||||
boundary="------------2F19EE9A8A1A6F779F5D14AF"
|
--------------Z0a0IncIqoVlOIpOfcALEK5P
|
||||||
Content-Language: en-US
|
Content-Type: text/plain; charset=UTF-8; format=flowed
|
||||||
|
Content-Transfer-Encoding: base64
|
||||||
This is a multi-part message in MIME format.
|
|
||||||
--------------2F19EE9A8A1A6F779F5D14AF
|
DQo=
|
||||||
Content-Type: text/plain; charset=utf-8; format=flowed
|
--------------Z0a0IncIqoVlOIpOfcALEK5P
|
||||||
Content-Transfer-Encoding: quoted-printable
|
Content-Type: message/rfc822; name="simple html body.eml"
|
||||||
|
Content-Disposition: attachment; filename="simple html body.eml"
|
||||||
|
Content-Transfer-Encoding: 7bit
|
||||||
|
|
||||||
--------------2F19EE9A8A1A6F779F5D14AF
|
Message-ID: <1655e652-1abc-575f-6e03-5bbf119afbfa@gmail.com>
|
||||||
Content-Type: application/pgp-keys;
|
Date: Tue, 25 Apr 2023 17:12:15 +0200
|
||||||
name="OpenPGP_0x161C0875822359F7.asc"
|
MIME-Version: 1.0
|
||||||
Content-Transfer-Encoding: quoted-printable
|
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101
|
||||||
Content-Disposition: attachment;
|
Thunderbird/102.10.0
|
||||||
filename="OpenPGP_0x161C0875822359F7.asc"
|
Content-Language: en-US
|
||||||
|
To: "InfernalBridgeTester@proton.me" <InfernalbridgeTester@proton.me>
|
||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
From: "pm.bridge.qa" <pm.bridge.qa@gmail.com>
|
||||||
|
Subject: simple html body
|
||||||
xsBNBFxlUPwBCACx954Ey4SD88f8DSKFw9BaZNXrNwYxNYSgqaqOGHQ0WllF3mstEhTfuxxCZ=
|
Autocrypt: addr=pm.bridge.qa@gmail.com; keydata=
|
||||||
pDh
|
xsBNBFxlUPwBCACx954Ey4SD88f8DSKFw9BaZNXrNwYxNYSgqaqOGHQ0WllF3mstEhTfuxxC
|
||||||
I5IhWCXUNxanzsFkn88mRDwFRVl2sf2aAG4/P/p1381oh2kd0UElMRQaQGzoCadQMaQOL9WYT=
|
ZpDhI5IhWCXUNxanzsFkn88mRDwFRVl2sf2aAG4/P/p1381oh2kd0UElMRQaQGzoCadQMaQO
|
||||||
f4S
|
L9WYTf4SPWSCzjrPyKgjq5FbqjbF/ndu376na9L+tnsEXyL6RrI6aZhjWG73xlqxS65dzTIY
|
||||||
PWSCzjrPyKgjq5FbqjbF/ndu376na9L+tnsEXyL6RrI6aZhjWG73xlqxS65dzTIYzsyM/P97x=
|
zsyM/P97xSndNvlvWtGvLlpFkzxfAEGpVzfOYVYFKoc8rGmUDwrDWYfk5JczRDDogJnY+BNM
|
||||||
Snd
|
Zf9pjSqk6rTyBOfNH5fpU8r7A5Q7l+HVakvMUQ9DzDWJtg2ru1Y8hexnJOF68avO4+a1ABEB
|
||||||
NvlvWtGvLlpFkzxfAEGpVzfOYVYFKoc8rGmUDwrDWYfk5JczRDDogJnY+BNMZf9pjSqk6rTyB=
|
AAHNKEJyaWRnZSBLeXUtRWh5aiA8cG0uYnJpZGdlLnFhQGdtYWlsLmNvbT7CwI4EEwEIADgC
|
||||||
OfN
|
GwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQRc5gl5cC8oW/Mo+bEWHAh1giNZ9wUCZEfU
|
||||||
H5fpU8r7A5Q7l+HVakvMUQ9DzDWJtg2ru1Y8hexnJOF68avO4+a1ABEBAAHNKEJyaWRnZSBLe=
|
zQAKCRAWHAh1giNZ9872B/4mM6cXErSSYK6/apGUVebg9QiP1RhFlLE/kg7BW3FaSP/yTUZN
|
||||||
XUt
|
ZdX7WPnEVyaa5Dk4xRZiB47Yc5myspwc+JEJ3YDAHq+4n/D74TF1kUCzP+QVsWcn40UqX9gH
|
||||||
RWh5aiA8cG0uYnJpZGdlLnFhQGdtYWlsLmNvbT7CwJQEEwEIAD4CGwMFCwkIBwIGFQoJCAsCB=
|
bO01O/DYtoxMOljEgkfQjEZcRoHuUhCUzldFf8aV+uZKiOXhrPYCwsilnh0RAmDV7fLoOfKX
|
||||||
BYC
|
MLiKXE8wM/5Bax+dk2AmEM4bOTIo58GGDDqseIg03ocrW7vPegmxiLUwmsHIIDwTq6qZ0CVx
|
||||||
AwECHgECF4AWIQRc5gl5cC8oW/Mo+bEWHAh1giNZ9wUCYC32ygUJB4sMzgAKCRAWHAh1giNZ9=
|
bt2uv4cCyNz/0pmRzG7p8192Evdu8JOuLSj3pI1X00Ub326yay3BBUnsL4PJIGoly8hnLb5N
|
||||||
/K8
|
3cyNzsBNBFxlUPwBCADh2HsX23yVnJt9fxFz3D07kCBNvu4HQfps6h1rgNxGhE32VmpESHeb
|
||||||
B/4qs84Ii/zKH+q+C8vwO4jUJkOM73qD0pgB7zBs651zWbpgopyol1YUKNpFaHlx/Qch7RDI7=
|
vIB5xjL6xKbIqqRa3x/7KDVBNJvca0gUsqEt5kzYF88Fyf2NBcejpIbcP7BS/g+C6KOowYj+
|
||||||
Vcz
|
Et26T6GdwFXExUcl80JvoX8yHQOfvJpdiBRbjyB8UqfCaknm3c7dNuXmhflz/w3aBj32q9Zy
|
||||||
1+60/KZJSJR19/N2EDVbCUdh8ueioUp9X/218YWV2TRJNxTnljd4FAn7smZnXuP1TsLjQ6sKO=
|
GqA1NpHCpLyVAlvSNQ/pat/rGUCPZ9duw4KhUUqEmatQPVFPkutTouEZQbMK+i+chOH3AsKC
|
||||||
V0U
|
uNDfvCDwirnsSqIJmAgl1lC4de+bsWYCMqN9ei99hOCRUyhZ3g3sr8RBowVAdcvjZxeIDKAL
|
||||||
u6JoiG6LZFXqDgxYpA++58Rkl6xaY6R71VkmVQlbEKtubX9AjHydq97Y+Jvn11XzWZaKhv4L7=
|
ABEBAAHCwHYEGAEIACACGwwWIQRc5gl5cC8oW/Mo+bEWHAh1giNZ9wUCZEfUugAKCRAWHAh1
|
||||||
6Pa
|
giNZ9ycfB/97iOFFeYSFB5CqOxsyo9TjHve0BOzfGyLh632+sUQEw3qqwVreAqrjVa/wnLFW
|
||||||
4tMKXvvrKh1oywMmh6mZJo+5ZA/ABTkr45cwlTPYqGTS9+uvOHt+PH/oYwwJB4ls2cIAUldSj=
|
67Pbf4GBn6ZnaIMIq4rv6d4wnDZ4b18yQkITErWvU+3thpeZS/NUeWlLZlXFauDhCdPpAq5T
|
||||||
TVQ
|
nw7+5lq0pe2BEcNBF4IRoBRs7UQVhr/QpN+xawbnQUM1oIOMljTWuQZtPo75OsltQn59+OTn
|
||||||
IsseYz3LlbcCfKJiiCFxeHOQXA5J6zNLKOT58TsczsBNBFxlUPwBCADh2HsX23yVnJt9fxFz3=
|
7ZYQD4Q3Sn0vPAzFbPBa4fostzbx8ktTGWfhBctQrpwS06VqoSowr8RFY9S+ssjCK+Hlq2t8
|
||||||
D07
|
ZC0CBL1aU1AAPqcAOfrw9rin0rN1dH07g1uDeZ9SnyLuee5QN7r3VY8b9tiOJV9m
|
||||||
kCBNvu4HQfps6h1rgNxGhE32VmpESHebvIB5xjL6xKbIqqRa3x/7KDVBNJvca0gUsqEt5kzYF=
|
Content-Type: multipart/signed; micalg=pgp-sha256;
|
||||||
88F
|
protocol="application/pgp-signature";
|
||||||
yf2NBcejpIbcP7BS/g+C6KOowYj+Et26T6GdwFXExUcl80JvoX8yHQOfvJpdiBRbjyB8UqfCa=
|
boundary="------------Kh8bXWpcGJrKDmQHlv9QWtgJ"
|
||||||
knm
|
|
||||||
3c7dNuXmhflz/w3aBj32q9ZyGqA1NpHCpLyVAlvSNQ/pat/rGUCPZ9duw4KhUUqEmatQPVFPk=
|
This is an OpenPGP/MIME signed message (RFC 4880 and 3156)
|
||||||
utT
|
--------------Kh8bXWpcGJrKDmQHlv9QWtgJ
|
||||||
ouEZQbMK+i+chOH3AsKCuNDfvCDwirnsSqIJmAgl1lC4de+bsWYCMqN9ei99hOCRUyhZ3g3sr=
|
Content-Type: multipart/mixed; boundary="------------iFRY0nd000IWAcfqrg6ZdUuF";
|
||||||
8RB
|
protected-headers="v1"
|
||||||
owVAdcvjZxeIDKALABEBAAHCwHwEGAEIACYCGwwWIQRc5gl5cC8oW/Mo+bEWHAh1giNZ9wUCY=
|
From: "pm.bridge.qa" <pm.bridge.qa@gmail.com>
|
||||||
C32
|
To: "InfernalBridgeTester@proton.me" <InfernalbridgeTester@proton.me>
|
||||||
lAUJB4sMmAAKCRAWHAh1giNZ9+Y2B/9rTKZaKviae+ummXNumXcrKvbkAAvfuLpKUn53FlQLm=
|
Message-ID: <1655e652-1abc-575f-6e03-5bbf119afbfa@gmail.com>
|
||||||
L6H
|
Subject: simple html body
|
||||||
jB++lJnPWvVSzdZxdv8FiPP3d632XHKUrkQRQM/9byRDXDommi7Qttx7YCkhd4JLVYqJqpnAQ=
|
|
||||||
xI5
|
--------------iFRY0nd000IWAcfqrg6ZdUuF
|
||||||
RMkXiZNWyr1lz8JOM1XvDk1M7sJwPMWews8VOIE03E1nt7AsQGnvHtadgEnQaufrYNX3hFA8S=
|
Content-Type: multipart/mixed; boundary="------------0tRoF00iqRv70TBczVNfagKk"
|
||||||
osO
|
|
||||||
HSnedcys6yrzCSIGCqCD9VHbnMtS4DOv0XJGh2hwc8omzH0KZA517dyKBorJRwadcVauGXDKx=
|
--------------0tRoF00iqRv70TBczVNfagKk
|
||||||
Etv
|
Content-Type: text/html; charset=UTF-8
|
||||||
Im4rl94PR/3An1Mj6HeeVVpLqDQ5Jb9J90BahWeQ53FzRa4EQzYCw0nLnxcsT1ZEEP5u
|
Content-Transfer-Encoding: quoted-printable
|
||||||
=3Dv/1p
|
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
<html>
|
||||||
|
<head>
|
||||||
--------------2F19EE9A8A1A6F779F5D14AF
|
|
||||||
Content-Type: message/rfc822;
|
<meta http-equiv=3D"content-type" content=3D"text/html; charset=3DUTF=
|
||||||
name="HTML with attachment external PGP.eml"
|
-8">
|
||||||
Content-Transfer-Encoding: 7bit
|
</head>
|
||||||
Content-Disposition: attachment;
|
<body>
|
||||||
filename="HTML with attachment external PGP.eml"
|
<p> </p>
|
||||||
|
<p> And this is HTML<br>
|
||||||
Delivered-To: pm.bridge.qa@gmail.com
|
</p>
|
||||||
Received: by 2002:a17:906:a051:0:0:0:0 with SMTP id bg17csp66709ejb;
|
<ul>
|
||||||
Wed, 24 Mar 2021 22:03:32 -0700 (PDT)
|
<li><b>Do I enjoy making courthouse puns?</b>
|
||||||
X-Google-Smtp-Source: ABdhPJxllBuHnnJzKWy77R291tZbVFVk0iahkLm1TQsluEYTvyAXdOWB/zp1y10e60UlGGZYH3YF
|
Guilty.=3D3DE2=3D3D80=3D3D9=3D
|
||||||
X-Received: by 2002:a05:6000:118c:: with SMTP id g12mr6758087wrx.353.1616648612550;
|
4 <i>@=3D3D baddadjokes</i></li>
|
||||||
Wed, 24 Mar 2021 22:03:32 -0700 (PDT)
|
<li><b>Can February March?</b> No, but April May. =3D3DE2=3D3D80=3D=
|
||||||
ARC-Seal: i=1; a=rsa-sha256; t=1616648612; cv=none;
|
3D94<i=3D>@Bear=3D3D
|
||||||
d=google.com; s=arc-20160816;
|
dedMOGuy</i=3D></li>
|
||||||
b=Jf4vmKEoeJQ3rIDMbI2twiDkfn50ejNnqIbs2nkaFruITcw6XhvhbcfV9HLC80Yt8E
|
</ul>
|
||||||
tfN7TV9qoBneSWzfSJ+Sqw31hBKKtKpMhuqZT9GPzBN5gdMJKj5ISAQ8Lgm9zvR3Zbjn
|
<p> <br>
|
||||||
N0nOzCu/oT1amMMm+48hpKj8VL2tydjvNG+g/a5lk1Aw7JdqIKV6t1XhsyyYaa1O+NFC
|
</p>
|
||||||
rQThdalcQj2NjoZWba1mjZSzI7B7hJdZg5d+jado2TPMQXe2kz2wGmr3+/JcKvPJjrSA
|
<p>
|
||||||
S+jzhpjcd7ZnctkzTfpsdlBJAGKoDBnSvQc3eMJ/AgRHFc+5ks5nRDt/1DowSjQ7i7rp
|
<meta http-equiv=3D"3D"" content-type"=3D""
|
||||||
4a+g==
|
content=3D"3D"text/html;" charset=3D"3DUTF=3D" -8"=3D"">
|
||||||
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816;
|
</p>
|
||||||
h=mime-version:message-id:subject:reply-to:from:to:dkim-signature
|
</body>
|
||||||
:date;
|
</html>
|
||||||
bh=vmJ0JT+IfeO4idMYP7zPvldBkdONjKTXWTp7ly/B9qk=;
|
--------------0tRoF00iqRv70TBczVNfagKk
|
||||||
b=f8VY+ajsE/XNYrqD666FM0WCtNEQtUyU/Zh3pFCI9sFrMnAui4Qp9Gs1fe/8HLxt2v
|
Content-Type: application/pgp-keys; name="OpenPGP_0x161C0875822359F7.asc"
|
||||||
/C4l4eHELvPBv4vX0KtUvOlRZYPZbLZCNdtTcFtiuZEKUHWx370p7yyMWcmSMdlUbq4J
|
Content-Disposition: attachment; filename="OpenPGP_0x161C0875822359F7.asc"
|
||||||
NrKMPGfaYiZe5Rt3MyD5RKm4RJpqvep34VCHMYtoFQP/0Po4/1JMDw0Fy6SXUJ54rBRw
|
Content-Description: OpenPGP public key
|
||||||
bmzqNNBkonda3YghhK3WNrxTxzZ8I7KW9YdpENNS9ewJLeVtFQKdiLZwz5EpMZxOxG0I
|
Content-Transfer-Encoding: quoted-printable
|
||||||
LW0jRtDlmZnqRe7bvTAo51IuLf9okHRI8PRiK0UHl+4Vr5Igq4mub7Ee8pC/Nz3Yj29G
|
|
||||||
KODw==
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
ARC-Authentication-Results: i=1; mx.google.com;
|
|
||||||
dkim=pass header.i=@protonmail.com header.s=protonmail header.b=EX07e46H;
|
xsBNBFxlUPwBCACx954Ey4SD88f8DSKFw9BaZNXrNwYxNYSgqaqOGHQ0WllF3mst
|
||||||
spf=pass (google.com: domain of bridge-test-user@protonmail.com designates 185.70.40.22 as permitted sender) smtp.mailfrom=bridge-test-user@protonmail.com;
|
EhTfuxxCZpDhI5IhWCXUNxanzsFkn88mRDwFRVl2sf2aAG4/P/p1381oh2kd0UEl
|
||||||
dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=protonmail.com
|
MRQaQGzoCadQMaQOL9WYTf4SPWSCzjrPyKgjq5FbqjbF/ndu376na9L+tnsEXyL6
|
||||||
Return-Path: <bridge-test-user@protonmail.com>
|
RrI6aZhjWG73xlqxS65dzTIYzsyM/P97xSndNvlvWtGvLlpFkzxfAEGpVzfOYVYF
|
||||||
Received: from mail2.protonmail.ch (mail2.protonmail.ch. [185.70.40.22])
|
Koc8rGmUDwrDWYfk5JczRDDogJnY+BNMZf9pjSqk6rTyBOfNH5fpU8r7A5Q7l+HV
|
||||||
by mx.google.com with ESMTPS id g6si2999785wrr.110.2021.03.24.22.03.32
|
akvMUQ9DzDWJtg2ru1Y8hexnJOF68avO4+a1ABEBAAHNKEJyaWRnZSBLeXUtRWh5
|
||||||
for <pm.bridge.qa@gmail.com>
|
aiA8cG0uYnJpZGdlLnFhQGdtYWlsLmNvbT7CwI4EEwEIADgCGwMFCwkIBwIGFQoJ
|
||||||
(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
|
CAsCBBYCAwECHgECF4AWIQRc5gl5cC8oW/Mo+bEWHAh1giNZ9wUCZEfUzQAKCRAW
|
||||||
Wed, 24 Mar 2021 22:03:32 -0700 (PDT)
|
HAh1giNZ9872B/4mM6cXErSSYK6/apGUVebg9QiP1RhFlLE/kg7BW3FaSP/yTUZN
|
||||||
Received-SPF: pass (google.com: domain of bridge-test-user@protonmail.com designates 185.70.40.22 as permitted sender) client-ip=185.70.40.22;
|
ZdX7WPnEVyaa5Dk4xRZiB47Yc5myspwc+JEJ3YDAHq+4n/D74TF1kUCzP+QVsWcn
|
||||||
Authentication-Results: mx.google.com;
|
40UqX9gHbO01O/DYtoxMOljEgkfQjEZcRoHuUhCUzldFf8aV+uZKiOXhrPYCwsil
|
||||||
dkim=pass header.i=@protonmail.com header.s=protonmail header.b=EX07e46H;
|
nh0RAmDV7fLoOfKXMLiKXE8wM/5Bax+dk2AmEM4bOTIo58GGDDqseIg03ocrW7vP
|
||||||
spf=pass (google.com: domain of bridge-test-user@protonmail.com designates 185.70.40.22 as permitted sender) smtp.mailfrom=bridge-test-user@protonmail.com;
|
egmxiLUwmsHIIDwTq6qZ0CVxbt2uv4cCyNz/0pmRzG7p8192Evdu8JOuLSj3pI1X
|
||||||
dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=protonmail.com
|
00Ub326yay3BBUnsL4PJIGoly8hnLb5N3cyNwsCUBBMBCAA+FiEEXOYJeXAvKFvz
|
||||||
Date: Thu, 25 Mar 2021 05:03:27 +0000
|
KPmxFhwIdYIjWfcFAlxlUPwCGwMFCQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgEC
|
||||||
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com;
|
F4AACgkQFhwIdYIjWfeQzgf+NWseR+UvrJ7I1CFd6M2+RmyIC1QTQ+uyJIhIBQfC
|
||||||
s=protonmail; t=1616648611;
|
vBhueU9GUPWR6a6GWsb2DGCMkBM2aHoTCcf46fq87VspYb3+b0JsKfRFxBa2cuNH
|
||||||
bh=vmJ0JT+IfeO4idMYP7zPvldBkdONjKTXWTp7ly/B9qk=;
|
Ey6PK3xHBDnYFtSZaRB7QMQMfYVin5oyHq8mOd4mImOKfpGhuuhq1hrT8sOhVxRY
|
||||||
h=Date:To:From:Reply-To:Subject:From;
|
Nl/2Hanya7tEJlVyAAEwtN4QVCqiRjjD7kBQ+mgxdDFo62X+sl4Zz2BFlZks+c1+
|
||||||
b=EX07e46H5/HmotAWZ69I4qa5jCVRao/p3KEM3eQn/AQ8s+cLMaR5b2ozdHrPCsTw5
|
LRWwaQZvGgf2tm2NqZhC04CKc2Gg5j7wNJBPVh/FVluxY27D2hV6v9/cTvXDGo8J
|
||||||
i5b1DLUHZHBf+6Ven47WJfKNwLUfkAGD2P0aI/dAk/h/h0Bg4Ni85pv+uPpRHLNQKv
|
pnZ28CnQqiiaKEU83BGwcUlcr4G5YApHyIPujaxffE2R887ATQRcZVD8AQgA4dh7
|
||||||
T3VnDP9MSwl6IUJu5zoM2EC70MLoiHS07lxhM2pw=
|
F9t8lZybfX8Rc9w9O5AgTb7uB0H6bOoda4DcRoRN9lZqREh3m7yAecYy+sSmyKqk
|
||||||
To: External Bridge <pm.bridge.qa@gmail.com>
|
Wt8f+yg1QTSb3GtIFLKhLeZM2BfPBcn9jQXHo6SG3D+wUv4PguijqMGI/hLduk+h
|
||||||
From: Bridge Test <bridge-test-user@protonmail.com>
|
ncBVxMVHJfNCb6F/Mh0Dn7yaXYgUW48gfFKnwmpJ5t3O3Tbl5oX5c/8N2gY99qvW
|
||||||
Reply-To: Bridge Test <bridge-test-user@protonmail.com>
|
chqgNTaRwqS8lQJb0jUP6Wrf6xlAj2fXbsOCoVFKhJmrUD1RT5LrU6LhGUGzCvov
|
||||||
Subject: HTML with attachment external PGP
|
nITh9wLCgrjQ37wg8Iq57EqiCZgIJdZQuHXvm7FmAjKjfXovfYTgkVMoWd4N7K/E
|
||||||
Message-ID: <LCxIUvb0rqBufdwR4JNxMg4ZDMI8x7pRG0vEHGHYwA@cp7-web-042.plabs.ch>
|
QaMFQHXL42cXiAygCwARAQABwsB2BBgBCAAgAhsMFiEEXOYJeXAvKFvzKPmxFhwI
|
||||||
MIME-Version: 1.0
|
dYIjWfcFAmRH1LoACgkQFhwIdYIjWfcnHwf/e4jhRXmEhQeQqjsbMqPU4x73tATs
|
||||||
Content-Type: multipart/mixed;
|
3xsi4et9vrFEBMN6qsFa3gKq41Wv8JyxVuuz23+BgZ+mZ2iDCKuK7+neMJw2eG9f
|
||||||
boundary="b1_LCxIUvb0rqBufdwR4JNxMg4ZDMI8x7pRG0vEHGHYwA"
|
MkJCExK1r1Pt7YaXmUvzVHlpS2ZVxWrg4QnT6QKuU58O/uZatKXtgRHDQReCEaAU
|
||||||
X-Spam-Status: No, score=-1.2 required=10.0 tests=ALL_TRUSTED,DKIM_SIGNED,
|
bO1EFYa/0KTfsWsG50FDNaCDjJY01rkGbT6O+TrJbUJ+ffjk5+2WEA+EN0p9LzwM
|
||||||
DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,HTML_MESSAGE
|
xWzwWuH6LLc28fJLUxln4QXLUK6cEtOlaqEqMK/ERWPUvrLIwivh5atrfGQtAgS9
|
||||||
shortcircuit=no autolearn=disabled version=3.4.4
|
WlNQAD6nADn68Pa4p9KzdXR9O4Nbg3mfUp8i7nnuUDe691WPG/bYjiVfZsLAfAQY
|
||||||
X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on
|
AQgAJhYhBFzmCXlwLyhb8yj5sRYcCHWCI1n3BQJcZVD8AhsMBQkDwmcAAAoJEBYc
|
||||||
mailout.protonmail.ch
|
CHWCI1n3rhcH/iCB0ZV861H0RKJ2F7bXEyCLR2ncBFUCnFo3muSrN9NXTojz2vwv
|
||||||
|
zexRBpZzaRJoksBkvH+ofuZ1iK7ycZO23dnukvPwGQsz3QiITjVeB6ZR0250MG1q
|
||||||
This is a multi-part message in MIME format.
|
A5yZRlZCsCbGJb4/2e8Ey8BbblHn49Zta4l2Ex5NpNNQ8FYoqXhXu5Bd2F/wX/Bp
|
||||||
|
5gkZegfE3H9Dw4QjP82Mt0RZSBg9RMGCk6nNfEuze1Up+IOdtqzf3/Z8J5XxLzN2
|
||||||
--b1_LCxIUvb0rqBufdwR4JNxMg4ZDMI8x7pRG0vEHGHYwA
|
s8WPmDwJDwvxJRtto8U+ulv4ElcwlA+wYiKAq7cRCKGM/si5ClkUNgb319grUrBU
|
||||||
Content-Type: multipart/alternative;
|
6h8SuYtgnD84965xRiVAgtH4wCPN544N8CE=3D
|
||||||
boundary="b2_LCxIUvb0rqBufdwR4JNxMg4ZDMI8x7pRG0vEHGHYwA"
|
=3DNmqc
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
--b2_LCxIUvb0rqBufdwR4JNxMg4ZDMI8x7pRG0vEHGHYwA
|
|
||||||
Content-Type: text/plain; charset=utf-8
|
--------------0tRoF00iqRv70TBczVNfagKk--
|
||||||
Content-Transfer-Encoding: base64
|
|
||||||
|
--------------iFRY0nd000IWAcfqrg6ZdUuF--
|
||||||
VGhpcyBpcyBib2R5IG9mIEhUTUwgbWFpbCB3aXRoIGF0dGFjaG1lbnQ=
|
|
||||||
|
--------------Kh8bXWpcGJrKDmQHlv9QWtgJ
|
||||||
--b2_LCxIUvb0rqBufdwR4JNxMg4ZDMI8x7pRG0vEHGHYwA
|
Content-Type: application/pgp-signature; name="OpenPGP_signature.asc"
|
||||||
Content-Type: text/html; charset=utf-8
|
Content-Description: OpenPGP digital signature
|
||||||
Content-Transfer-Encoding: base64
|
Content-Disposition: attachment; filename="OpenPGP_signature"
|
||||||
|
|
||||||
PGh0bWw+PGhlYWQ+PC9oZWFkPjxib2R5PlRoaXMgaXMgYm9keSBvZiA8Yj5IVE1MIG1haWw8L2I+
|
-----BEGIN PGP SIGNATURE-----
|
||||||
IHdpdGggYXR0YWNobWVudA0KPC9ib2R5PjwvaHRtbD4=
|
|
||||||
|
wsB5BAABCAAjFiEEXOYJeXAvKFvzKPmxFhwIdYIjWfcFAmRH7c8FAwAAAAAACgkQFhwIdYIjWffG
|
||||||
|
uQgAlK68JzT/9NLOg4SZPUbVaEdlbru6ebX1Lwaj93FZEKfmiEZ4heuYfq4yVw1JI/CGhHa1wxUJ
|
||||||
--b2_LCxIUvb0rqBufdwR4JNxMg4ZDMI8x7pRG0vEHGHYwA--
|
M6+Tg+0lqIslG1rTposgIziugMBhEWXYWXBwDdvu+9T2eqo0se3h+oXueVlRihowfuPcP9jvt6p3
|
||||||
|
DR+pu+hJ/D42DarJrn8v0CMm3PyLNHodyKLmEtZrzP4ok9Wal850xZ3Tmu5dL3v300wPGJaxrn8o
|
||||||
--b1_LCxIUvb0rqBufdwR4JNxMg4ZDMI8x7pRG0vEHGHYwA
|
4OeHRUodE43UijUrFKNoEfhqzY8HsMKIdF8Bi6+XiA0sHwcaUqJsaczBqgmbJbPqG8N4CokiUnUu
|
||||||
Content-Type: image/png; name=outline-light-instagram-48.png
|
4QDmClWa0rG4I5rrx4PrTbzAt7cGIb8N3ACNI7vI/Q==
|
||||||
Content-Transfer-Encoding: base64
|
=Llod
|
||||||
Content-Disposition: attachment; filename=outline-light-instagram-48.png
|
-----END PGP SIGNATURE-----
|
||||||
|
|
||||||
iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAALVBMVEUAAAD/////////////////
|
--------------Kh8bXWpcGJrKDmQHlv9QWtgJ--
|
||||||
//////////////////////////////////////+hSKubAAAADnRSTlMAgO8QQM+/IJ9gj1AwcIQd
|
--------------Z0a0IncIqoVlOIpOfcALEK5P
|
||||||
OXUAAAGdSURBVDjLXJC9SgNBFIVPXDURTYhgIQghINgowyLYCAYtRFAIgtYhpAjYhC0srCRW6YIg
|
Content-Type: application/pgp-keys; name="OpenPGP_0x161C0875822359F7.asc"
|
||||||
WNpoHVSsg/gEii+Qnfxq4DyDc3cyMfrBwl2+O+fOHTi8p7LS5RUf/9gpMKL7iT9sK47Q95ggpkzv
|
Content-Disposition: attachment; filename="OpenPGP_0x161C0875822359F7.asc"
|
||||||
1cvRcsGYNMYsmP+zKN27NR2vcDyTNVdfkOuuniNPMWafvIbljt+YoMEvW8y7lt+ARwhvrgPjhA0I
|
Content-Description: OpenPGP public key
|
||||||
BTng7S1GLPlypBvtIBPidY4YBDJFdtnkscQ5JGaGqxC9i7jSDwcwnB8qHWBaQjw1ABI8wYgtVoG6
|
Content-Transfer-Encoding: quoted-printable
|
||||||
9pFkH8iZIiJeulFt4JLvJq8I5N2GMWYbHWDWzM3JZTMdeSWla0kW86FcuI0mfStiNKQ/AhEeh8h0
|
|
||||||
YUTffFwrMTT5oSwdojIQ0UKcocgAKRH1HiqhFQmmJa5qRaYHNbRiSsOgslY0NdixItUTUWlZkedP
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
HXVyAgAIA1F0wP5btQZPIyTwvAqa/Fl4oacuP+e4XHAjSYpkQkxSiMX+T7FPoZJToSStzED70HCy
|
|
||||||
KE3NGCg4jJrC6Ti7AFwZLhnW0gMbzFZc0RmmeAAAAABJRU5ErkJggg==
|
xsBNBFxlUPwBCACx954Ey4SD88f8DSKFw9BaZNXrNwYxNYSgqaqOGHQ0WllF3mst
|
||||||
|
EhTfuxxCZpDhI5IhWCXUNxanzsFkn88mRDwFRVl2sf2aAG4/P/p1381oh2kd0UEl
|
||||||
--b1_LCxIUvb0rqBufdwR4JNxMg4ZDMI8x7pRG0vEHGHYwA--
|
MRQaQGzoCadQMaQOL9WYTf4SPWSCzjrPyKgjq5FbqjbF/ndu376na9L+tnsEXyL6
|
||||||
|
RrI6aZhjWG73xlqxS65dzTIYzsyM/P97xSndNvlvWtGvLlpFkzxfAEGpVzfOYVYF
|
||||||
|
Koc8rGmUDwrDWYfk5JczRDDogJnY+BNMZf9pjSqk6rTyBOfNH5fpU8r7A5Q7l+HV
|
||||||
--------------2F19EE9A8A1A6F779F5D14AF--
|
akvMUQ9DzDWJtg2ru1Y8hexnJOF68avO4+a1ABEBAAHNKEJyaWRnZSBLeXUtRWh5
|
||||||
|
aiA8cG0uYnJpZGdlLnFhQGdtYWlsLmNvbT7CwI4EEwEIADgCGwMFCwkIBwIGFQoJ
|
||||||
--avFkF0LAPYPXcFHcnsgGmACbGIPeVDdYc--
|
CAsCBBYCAwECHgECF4AWIQRc5gl5cC8oW/Mo+bEWHAh1giNZ9wUCZEfUzQAKCRAW
|
||||||
|
HAh1giNZ9872B/4mM6cXErSSYK6/apGUVebg9QiP1RhFlLE/kg7BW3FaSP/yTUZN
|
||||||
--Rrmlds5vN3IeeCVjbnepHmuVgyROSBjsS
|
ZdX7WPnEVyaa5Dk4xRZiB47Yc5myspwc+JEJ3YDAHq+4n/D74TF1kUCzP+QVsWcn
|
||||||
Content-Type: application/pgp-signature; name="OpenPGP_signature.asc"
|
40UqX9gHbO01O/DYtoxMOljEgkfQjEZcRoHuUhCUzldFf8aV+uZKiOXhrPYCwsil
|
||||||
Content-Description: OpenPGP digital signature
|
nh0RAmDV7fLoOfKXMLiKXE8wM/5Bax+dk2AmEM4bOTIo58GGDDqseIg03ocrW7vP
|
||||||
Content-Disposition: attachment; filename="OpenPGP_signature"
|
egmxiLUwmsHIIDwTq6qZ0CVxbt2uv4cCyNz/0pmRzG7p8192Evdu8JOuLSj3pI1X
|
||||||
|
00Ub326yay3BBUnsL4PJIGoly8hnLb5N3cyNwsCUBBMBCAA+FiEEXOYJeXAvKFvz
|
||||||
-----BEGIN PGP SIGNATURE-----
|
KPmxFhwIdYIjWfcFAlxlUPwCGwMFCQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgEC
|
||||||
|
F4AACgkQFhwIdYIjWfeQzgf+NWseR+UvrJ7I1CFd6M2+RmyIC1QTQ+uyJIhIBQfC
|
||||||
wsB5BAABCAAjFiEEXOYJeXAvKFvzKPmxFhwIdYIjWfcFAmBciIAFAwAAAAAACgkQFhwIdYIjWfcN
|
vBhueU9GUPWR6a6GWsb2DGCMkBM2aHoTCcf46fq87VspYb3+b0JsKfRFxBa2cuNH
|
||||||
ZQf+NzAoEJRTSW5JFNgSGkwLsH89wAbw3wEt4PYuZaa+35xBuU8Sojm1oLOyuPkIasQf98Iu5P1o
|
Ey6PK3xHBDnYFtSZaRB7QMQMfYVin5oyHq8mOd4mImOKfpGhuuhq1hrT8sOhVxRY
|
||||||
8cokViEa6wm+ZZpcFMi6T2/3+UNlSm81Epm7GrFyjAFTWrdTPLb4k4x47sz77RoTp/UEwm/7fVI5
|
Nl/2Hanya7tEJlVyAAEwtN4QVCqiRjjD7kBQ+mgxdDFo62X+sl4Zz2BFlZks+c1+
|
||||||
gMYhQyIYaocXHmDk61UshWE9q/Po6qjHBnnWS8YBnhUS9lK8uimpfRO9UQ9bIUjIYDGDPAtBoYnb
|
LRWwaQZvGgf2tm2NqZhC04CKc2Gg5j7wNJBPVh/FVluxY27D2hV6v9/cTvXDGo8J
|
||||||
X9V4SjBvbbdNrgoVaDxPw6HYCb3RhzRXunr5Icdnjfbc2H40/FayVi/p7GzFh+8zv/TzRxMkHo72
|
pnZ28CnQqiiaKEU83BGwcUlcr4G5YApHyIPujaxffE2R887ATQRcZVD8AQgA4dh7
|
||||||
DBsONaC7r8bxQ9BwJvpmWufqL7ZXHfVXQ6z+M43e1Q==
|
F9t8lZybfX8Rc9w9O5AgTb7uB0H6bOoda4DcRoRN9lZqREh3m7yAecYy+sSmyKqk
|
||||||
=Stx+
|
Wt8f+yg1QTSb3GtIFLKhLeZM2BfPBcn9jQXHo6SG3D+wUv4PguijqMGI/hLduk+h
|
||||||
-----END PGP SIGNATURE-----
|
ncBVxMVHJfNCb6F/Mh0Dn7yaXYgUW48gfFKnwmpJ5t3O3Tbl5oX5c/8N2gY99qvW
|
||||||
|
chqgNTaRwqS8lQJb0jUP6Wrf6xlAj2fXbsOCoVFKhJmrUD1RT5LrU6LhGUGzCvov
|
||||||
--Rrmlds5vN3IeeCVjbnepHmuVgyROSBjsS--
|
nITh9wLCgrjQ37wg8Iq57EqiCZgIJdZQuHXvm7FmAjKjfXovfYTgkVMoWd4N7K/E
|
||||||
|
QaMFQHXL42cXiAygCwARAQABwsB2BBgBCAAgAhsMFiEEXOYJeXAvKFvzKPmxFhwI
|
||||||
|
dYIjWfcFAmRH1LoACgkQFhwIdYIjWfcnHwf/e4jhRXmEhQeQqjsbMqPU4x73tATs
|
||||||
|
3xsi4et9vrFEBMN6qsFa3gKq41Wv8JyxVuuz23+BgZ+mZ2iDCKuK7+neMJw2eG9f
|
||||||
|
MkJCExK1r1Pt7YaXmUvzVHlpS2ZVxWrg4QnT6QKuU58O/uZatKXtgRHDQReCEaAU
|
||||||
|
bO1EFYa/0KTfsWsG50FDNaCDjJY01rkGbT6O+TrJbUJ+ffjk5+2WEA+EN0p9LzwM
|
||||||
|
xWzwWuH6LLc28fJLUxln4QXLUK6cEtOlaqEqMK/ERWPUvrLIwivh5atrfGQtAgS9
|
||||||
|
WlNQAD6nADn68Pa4p9KzdXR9O4Nbg3mfUp8i7nnuUDe691WPG/bYjiVfZsLAfAQY
|
||||||
|
AQgAJhYhBFzmCXlwLyhb8yj5sRYcCHWCI1n3BQJcZVD8AhsMBQkDwmcAAAoJEBYc
|
||||||
|
CHWCI1n3rhcH/iCB0ZV861H0RKJ2F7bXEyCLR2ncBFUCnFo3muSrN9NXTojz2vwv
|
||||||
|
zexRBpZzaRJoksBkvH+ofuZ1iK7ycZO23dnukvPwGQsz3QiITjVeB6ZR0250MG1q
|
||||||
|
A5yZRlZCsCbGJb4/2e8Ey8BbblHn49Zta4l2Ex5NpNNQ8FYoqXhXu5Bd2F/wX/Bp
|
||||||
|
5gkZegfE3H9Dw4QjP82Mt0RZSBg9RMGCk6nNfEuze1Up+IOdtqzf3/Z8J5XxLzN2
|
||||||
|
s8WPmDwJDwvxJRtto8U+ulv4ElcwlA+wYiKAq7cRCKGM/si5ClkUNgb319grUrBU
|
||||||
|
6h8SuYtgnD84965xRiVAgtH4wCPN544N8CE=3D
|
||||||
|
=3DNmqc
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
--------------Z0a0IncIqoVlOIpOfcALEK5P--
|
||||||
|
|
||||||
|
--------------6sNRIgaKOJHTghuLxPUzlxn1--
|
||||||
|
|
||||||
|
--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||||
|
Content-Description: OpenPGP digital signature
|
||||||
|
Content-Disposition: attachment; filename=OpenPGP_signature
|
||||||
|
Content-Type: application/pgp-signature; name=OpenPGP_signature.asc
|
||||||
|
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
Version: GopenPGP 2.7.1
|
||||||
|
Comment: https://gopenpgp.org
|
||||||
|
|
||||||
|
wsB5BAABCAAjFiEEXOYJeXAvKFvzKPmxFhwIdYIjWfcFAmRIzW4FAwAAAAAACgkQ
|
||||||
|
FhwIdYIjWfe84QgAj1mf/g/jvobkik9RRvyUCN3InyHBqcwMOgyVDmd4U5uh/DVZ
|
||||||
|
sZ8kFU+/koEfr/8+Y7ataenEG5pY2f4krZNzc85VzjwniK1xcjxDW86UFa/ud/2Q
|
||||||
|
OD7eqJUQO1UpuQ/xe4gbfy3BKvTp5u09UjcBDnZ/lHASSREpfGBhk37Y2lkb5iIP
|
||||||
|
4KR7DOi1S1MFrdDPgKp59B9mk2ZMSC1Njzido9Zm/2tdYldAI66uMQiMgB2iv7yn
|
||||||
|
FMGxKzewt2OxZWKVmNaAzzvrQw7AYfIr70NbwnK75Gmh1CkwLadNpzSRMCqW5BP6
|
||||||
|
TsypxeGfXuQi0upWT2E3YpIvtkOrHRGXY9jXNw==
|
||||||
|
=5Yce
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
|
--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855--
|
||||||
|
|||||||
@ -1,116 +1,119 @@
|
|||||||
Content-Type: multipart/signed; micalg=pgp-sha256;
|
Content-Type: multipart/signed;
|
||||||
protocol="application/pgp-signature";
|
boundary=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855;
|
||||||
boundary="pavrbLYh8Q4RWBboYnVxY3mNBBzan1Zz4"
|
micalg=SHA-256; protocol="application/pgp-signature"
|
||||||
|
|
||||||
This is an OpenPGP/MIME signed message (RFC 4880 and 3156)
|
--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||||
--pavrbLYh8Q4RWBboYnVxY3mNBBzan1Zz4
|
Content-Type: multipart/mixed; boundary="------------iFRY0nd000IWAcfqrg6ZdUuF";
|
||||||
Content-Type: multipart/mixed; boundary="avFoFILZo8SdHM1Pc1OUviN4UKQh16HyR";
|
protected-headers="v1"
|
||||||
protected-headers="v1"
|
From: "pm.bridge.qa" <pm.bridge.qa@gmail.com>
|
||||||
Subject: simple html body
|
To: "InfernalBridgeTester@proton.me" <InfernalbridgeTester@proton.me>
|
||||||
From: "pm.bridge.qa" <pm.bridge.qa@gmail.com>
|
Message-ID: <1655e652-1abc-575f-6e03-5bbf119afbfa@gmail.com>
|
||||||
To: schizofrenic@pm.me
|
Subject: simple html body
|
||||||
Message-ID: <d9c99685-4e1c-8f95-8b68-c6b0fcfd62ef@gmail.com>
|
|
||||||
|
--------------iFRY0nd000IWAcfqrg6ZdUuF
|
||||||
--avFoFILZo8SdHM1Pc1OUviN4UKQh16HyR
|
Content-Type: multipart/mixed; boundary="------------0tRoF00iqRv70TBczVNfagKk"
|
||||||
Content-Type: multipart/mixed;
|
|
||||||
boundary="------------9EAE2E1A715ACB9849E5C4E3"
|
--------------0tRoF00iqRv70TBczVNfagKk
|
||||||
Content-Language: en-US
|
Content-Type: text/html; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: quoted-printable
|
||||||
This is a multi-part message in MIME format.
|
|
||||||
--------------9EAE2E1A715ACB9849E5C4E3
|
<html>
|
||||||
Content-Type: text/html; charset=utf-8
|
<head>
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
|
<meta http-equiv=3D"content-type" content=3D"text/html; charset=3DUTF=
|
||||||
<html>
|
-8">
|
||||||
<head>
|
</head>
|
||||||
|
<body>
|
||||||
<meta http-equiv=3D"content-type" content=3D"text/html; charset=3DUTF=
|
<p> </p>
|
||||||
-8">
|
<p> And this is HTML<br>
|
||||||
</head>
|
</p>
|
||||||
<body>
|
<ul>
|
||||||
And this is HTML<br>
|
<li><b>Do I enjoy making courthouse puns?</b>
|
||||||
<ul>
|
Guilty.=3D3DE2=3D3D80=3D3D9=3D
|
||||||
<li><b>Do I enjoy making courthouse puns?</b> Guilty.=E2=80=94 <i>@=
|
4 <i>@=3D3D baddadjokes</i></li>
|
||||||
baddadjokes</i></li>
|
<li><b>Can February March?</b> No, but April May. =3D3DE2=3D3D80=3D=
|
||||||
<li><b>Can February March?</b> No, but April May. =E2=80=94<i>@Bear=
|
3D94<i=3D>@Bear=3D3D
|
||||||
dedMOGuy</i></li>
|
dedMOGuy</i=3D></li>
|
||||||
</ul>
|
</ul>
|
||||||
</body>
|
<p> <br>
|
||||||
</html>
|
</p>
|
||||||
|
<p>
|
||||||
--------------9EAE2E1A715ACB9849E5C4E3
|
<meta http-equiv=3D"3D"" content-type"=3D""
|
||||||
Content-Type: application/pgp-keys;
|
content=3D"3D"text/html;" charset=3D"3DUTF=3D" -8"=3D"">
|
||||||
name="OpenPGP_0x161C0875822359F7.asc"
|
</p>
|
||||||
Content-Transfer-Encoding: quoted-printable
|
</body>
|
||||||
Content-Disposition: attachment;
|
</html>
|
||||||
filename="OpenPGP_0x161C0875822359F7.asc"
|
--------------0tRoF00iqRv70TBczVNfagKk
|
||||||
|
Content-Type: application/pgp-keys; name="OpenPGP_0x161C0875822359F7.asc"
|
||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
Content-Disposition: attachment; filename="OpenPGP_0x161C0875822359F7.asc"
|
||||||
|
Content-Description: OpenPGP public key
|
||||||
xsBNBFxlUPwBCACx954Ey4SD88f8DSKFw9BaZNXrNwYxNYSgqaqOGHQ0WllF3mstEhTfuxxCZ=
|
Content-Transfer-Encoding: quoted-printable
|
||||||
pDh
|
|
||||||
I5IhWCXUNxanzsFkn88mRDwFRVl2sf2aAG4/P/p1381oh2kd0UElMRQaQGzoCadQMaQOL9WYT=
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
f4S
|
|
||||||
PWSCzjrPyKgjq5FbqjbF/ndu376na9L+tnsEXyL6RrI6aZhjWG73xlqxS65dzTIYzsyM/P97x=
|
xsBNBFxlUPwBCACx954Ey4SD88f8DSKFw9BaZNXrNwYxNYSgqaqOGHQ0WllF3mst
|
||||||
Snd
|
EhTfuxxCZpDhI5IhWCXUNxanzsFkn88mRDwFRVl2sf2aAG4/P/p1381oh2kd0UEl
|
||||||
NvlvWtGvLlpFkzxfAEGpVzfOYVYFKoc8rGmUDwrDWYfk5JczRDDogJnY+BNMZf9pjSqk6rTyB=
|
MRQaQGzoCadQMaQOL9WYTf4SPWSCzjrPyKgjq5FbqjbF/ndu376na9L+tnsEXyL6
|
||||||
OfN
|
RrI6aZhjWG73xlqxS65dzTIYzsyM/P97xSndNvlvWtGvLlpFkzxfAEGpVzfOYVYF
|
||||||
H5fpU8r7A5Q7l+HVakvMUQ9DzDWJtg2ru1Y8hexnJOF68avO4+a1ABEBAAHNKEJyaWRnZSBLe=
|
Koc8rGmUDwrDWYfk5JczRDDogJnY+BNMZf9pjSqk6rTyBOfNH5fpU8r7A5Q7l+HV
|
||||||
XUt
|
akvMUQ9DzDWJtg2ru1Y8hexnJOF68avO4+a1ABEBAAHNKEJyaWRnZSBLeXUtRWh5
|
||||||
RWh5aiA8cG0uYnJpZGdlLnFhQGdtYWlsLmNvbT7CwJQEEwEIAD4CGwMFCwkIBwIGFQoJCAsCB=
|
aiA8cG0uYnJpZGdlLnFhQGdtYWlsLmNvbT7CwI4EEwEIADgCGwMFCwkIBwIGFQoJ
|
||||||
BYC
|
CAsCBBYCAwECHgECF4AWIQRc5gl5cC8oW/Mo+bEWHAh1giNZ9wUCZEfUzQAKCRAW
|
||||||
AwECHgECF4AWIQRc5gl5cC8oW/Mo+bEWHAh1giNZ9wUCYC32ygUJB4sMzgAKCRAWHAh1giNZ9=
|
HAh1giNZ9872B/4mM6cXErSSYK6/apGUVebg9QiP1RhFlLE/kg7BW3FaSP/yTUZN
|
||||||
/K8
|
ZdX7WPnEVyaa5Dk4xRZiB47Yc5myspwc+JEJ3YDAHq+4n/D74TF1kUCzP+QVsWcn
|
||||||
B/4qs84Ii/zKH+q+C8vwO4jUJkOM73qD0pgB7zBs651zWbpgopyol1YUKNpFaHlx/Qch7RDI7=
|
40UqX9gHbO01O/DYtoxMOljEgkfQjEZcRoHuUhCUzldFf8aV+uZKiOXhrPYCwsil
|
||||||
Vcz
|
nh0RAmDV7fLoOfKXMLiKXE8wM/5Bax+dk2AmEM4bOTIo58GGDDqseIg03ocrW7vP
|
||||||
1+60/KZJSJR19/N2EDVbCUdh8ueioUp9X/218YWV2TRJNxTnljd4FAn7smZnXuP1TsLjQ6sKO=
|
egmxiLUwmsHIIDwTq6qZ0CVxbt2uv4cCyNz/0pmRzG7p8192Evdu8JOuLSj3pI1X
|
||||||
V0U
|
00Ub326yay3BBUnsL4PJIGoly8hnLb5N3cyNwsCUBBMBCAA+FiEEXOYJeXAvKFvz
|
||||||
u6JoiG6LZFXqDgxYpA++58Rkl6xaY6R71VkmVQlbEKtubX9AjHydq97Y+Jvn11XzWZaKhv4L7=
|
KPmxFhwIdYIjWfcFAlxlUPwCGwMFCQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgEC
|
||||||
6Pa
|
F4AACgkQFhwIdYIjWfeQzgf+NWseR+UvrJ7I1CFd6M2+RmyIC1QTQ+uyJIhIBQfC
|
||||||
4tMKXvvrKh1oywMmh6mZJo+5ZA/ABTkr45cwlTPYqGTS9+uvOHt+PH/oYwwJB4ls2cIAUldSj=
|
vBhueU9GUPWR6a6GWsb2DGCMkBM2aHoTCcf46fq87VspYb3+b0JsKfRFxBa2cuNH
|
||||||
TVQ
|
Ey6PK3xHBDnYFtSZaRB7QMQMfYVin5oyHq8mOd4mImOKfpGhuuhq1hrT8sOhVxRY
|
||||||
IsseYz3LlbcCfKJiiCFxeHOQXA5J6zNLKOT58TsczsBNBFxlUPwBCADh2HsX23yVnJt9fxFz3=
|
Nl/2Hanya7tEJlVyAAEwtN4QVCqiRjjD7kBQ+mgxdDFo62X+sl4Zz2BFlZks+c1+
|
||||||
D07
|
LRWwaQZvGgf2tm2NqZhC04CKc2Gg5j7wNJBPVh/FVluxY27D2hV6v9/cTvXDGo8J
|
||||||
kCBNvu4HQfps6h1rgNxGhE32VmpESHebvIB5xjL6xKbIqqRa3x/7KDVBNJvca0gUsqEt5kzYF=
|
pnZ28CnQqiiaKEU83BGwcUlcr4G5YApHyIPujaxffE2R887ATQRcZVD8AQgA4dh7
|
||||||
88F
|
F9t8lZybfX8Rc9w9O5AgTb7uB0H6bOoda4DcRoRN9lZqREh3m7yAecYy+sSmyKqk
|
||||||
yf2NBcejpIbcP7BS/g+C6KOowYj+Et26T6GdwFXExUcl80JvoX8yHQOfvJpdiBRbjyB8UqfCa=
|
Wt8f+yg1QTSb3GtIFLKhLeZM2BfPBcn9jQXHo6SG3D+wUv4PguijqMGI/hLduk+h
|
||||||
knm
|
ncBVxMVHJfNCb6F/Mh0Dn7yaXYgUW48gfFKnwmpJ5t3O3Tbl5oX5c/8N2gY99qvW
|
||||||
3c7dNuXmhflz/w3aBj32q9ZyGqA1NpHCpLyVAlvSNQ/pat/rGUCPZ9duw4KhUUqEmatQPVFPk=
|
chqgNTaRwqS8lQJb0jUP6Wrf6xlAj2fXbsOCoVFKhJmrUD1RT5LrU6LhGUGzCvov
|
||||||
utT
|
nITh9wLCgrjQ37wg8Iq57EqiCZgIJdZQuHXvm7FmAjKjfXovfYTgkVMoWd4N7K/E
|
||||||
ouEZQbMK+i+chOH3AsKCuNDfvCDwirnsSqIJmAgl1lC4de+bsWYCMqN9ei99hOCRUyhZ3g3sr=
|
QaMFQHXL42cXiAygCwARAQABwsB2BBgBCAAgAhsMFiEEXOYJeXAvKFvzKPmxFhwI
|
||||||
8RB
|
dYIjWfcFAmRH1LoACgkQFhwIdYIjWfcnHwf/e4jhRXmEhQeQqjsbMqPU4x73tATs
|
||||||
owVAdcvjZxeIDKALABEBAAHCwHwEGAEIACYCGwwWIQRc5gl5cC8oW/Mo+bEWHAh1giNZ9wUCY=
|
3xsi4et9vrFEBMN6qsFa3gKq41Wv8JyxVuuz23+BgZ+mZ2iDCKuK7+neMJw2eG9f
|
||||||
C32
|
MkJCExK1r1Pt7YaXmUvzVHlpS2ZVxWrg4QnT6QKuU58O/uZatKXtgRHDQReCEaAU
|
||||||
lAUJB4sMmAAKCRAWHAh1giNZ9+Y2B/9rTKZaKviae+ummXNumXcrKvbkAAvfuLpKUn53FlQLm=
|
bO1EFYa/0KTfsWsG50FDNaCDjJY01rkGbT6O+TrJbUJ+ffjk5+2WEA+EN0p9LzwM
|
||||||
L6H
|
xWzwWuH6LLc28fJLUxln4QXLUK6cEtOlaqEqMK/ERWPUvrLIwivh5atrfGQtAgS9
|
||||||
jB++lJnPWvVSzdZxdv8FiPP3d632XHKUrkQRQM/9byRDXDommi7Qttx7YCkhd4JLVYqJqpnAQ=
|
WlNQAD6nADn68Pa4p9KzdXR9O4Nbg3mfUp8i7nnuUDe691WPG/bYjiVfZsLAfAQY
|
||||||
xI5
|
AQgAJhYhBFzmCXlwLyhb8yj5sRYcCHWCI1n3BQJcZVD8AhsMBQkDwmcAAAoJEBYc
|
||||||
RMkXiZNWyr1lz8JOM1XvDk1M7sJwPMWews8VOIE03E1nt7AsQGnvHtadgEnQaufrYNX3hFA8S=
|
CHWCI1n3rhcH/iCB0ZV861H0RKJ2F7bXEyCLR2ncBFUCnFo3muSrN9NXTojz2vwv
|
||||||
osO
|
zexRBpZzaRJoksBkvH+ofuZ1iK7ycZO23dnukvPwGQsz3QiITjVeB6ZR0250MG1q
|
||||||
HSnedcys6yrzCSIGCqCD9VHbnMtS4DOv0XJGh2hwc8omzH0KZA517dyKBorJRwadcVauGXDKx=
|
A5yZRlZCsCbGJb4/2e8Ey8BbblHn49Zta4l2Ex5NpNNQ8FYoqXhXu5Bd2F/wX/Bp
|
||||||
Etv
|
5gkZegfE3H9Dw4QjP82Mt0RZSBg9RMGCk6nNfEuze1Up+IOdtqzf3/Z8J5XxLzN2
|
||||||
Im4rl94PR/3An1Mj6HeeVVpLqDQ5Jb9J90BahWeQ53FzRa4EQzYCw0nLnxcsT1ZEEP5u
|
s8WPmDwJDwvxJRtto8U+ulv4ElcwlA+wYiKAq7cRCKGM/si5ClkUNgb319grUrBU
|
||||||
=3Dv/1p
|
6h8SuYtgnD84965xRiVAgtH4wCPN544N8CE=3D
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
=3DNmqc
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
--------------9EAE2E1A715ACB9849E5C4E3--
|
|
||||||
|
--------------0tRoF00iqRv70TBczVNfagKk--
|
||||||
--avFoFILZo8SdHM1Pc1OUviN4UKQh16HyR--
|
|
||||||
|
--------------iFRY0nd000IWAcfqrg6ZdUuF--
|
||||||
--pavrbLYh8Q4RWBboYnVxY3mNBBzan1Zz4
|
|
||||||
Content-Type: application/pgp-signature; name="OpenPGP_signature.asc"
|
--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||||
Content-Description: OpenPGP digital signature
|
Content-Description: OpenPGP digital signature
|
||||||
Content-Disposition: attachment; filename="OpenPGP_signature"
|
Content-Disposition: attachment; filename=OpenPGP_signature
|
||||||
|
Content-Type: application/pgp-signature; name=OpenPGP_signature.asc
|
||||||
-----BEGIN PGP SIGNATURE-----
|
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
wsB5BAABCAAjFiEEXOYJeXAvKFvzKPmxFhwIdYIjWfcFAmBa9hAFAwAAAAAACgkQFhwIdYIjWffL
|
Version: GopenPGP 2.7.1
|
||||||
1AgApF18AVOPEm9y5R+d0NQmxqhSwAtvaqCwqQpG3mArIYK3Y0zrDkPQZZl/3emW8LWht7ZyYCAb
|
Comment: https://gopenpgp.org
|
||||||
NZo7HoYxjLy3yxAOPUl/Pc0nJpEqk/wAZT58yOnzv8DU5Q9o+444FfTMJpcrcH/M5cXYyqRtVhas
|
|
||||||
k5wu5u2DEgSO3Kj/5l7lThb+CUgRC6wSiOuUkqGEWLiAguCdd88XDkLMbwrDnOu3PbhcA8o1msns
|
wsB5BAABCAAjFiEEXOYJeXAvKFvzKPmxFhwIdYIjWfcFAmRH7c8FAwAAAAAACgkQ
|
||||||
PfkBdq3mFjp4M8M4ha+D2MxmV6tBv1E7snWf/spBVb9fHIa7zI4ZS6shpzGHCnJarO0Jco0Qh3IZ
|
FhwIdYIjWffGuQgAlK68JzT/9NLOg4SZPUbVaEdlbru6ebX1Lwaj93FZEKfmiEZ4
|
||||||
ZVfwhtJeFsmdqSm6DLvCmQWAYk2fDOZDMVKqe9IbUA==
|
heuYfq4yVw1JI/CGhHa1wxUJM6+Tg+0lqIslG1rTposgIziugMBhEWXYWXBwDdvu
|
||||||
=pkS0
|
+9T2eqo0se3h+oXueVlRihowfuPcP9jvt6p3DR+pu+hJ/D42DarJrn8v0CMm3PyL
|
||||||
-----END PGP SIGNATURE-----
|
NHodyKLmEtZrzP4ok9Wal850xZ3Tmu5dL3v300wPGJaxrn8o4OeHRUodE43UijUr
|
||||||
|
FKNoEfhqzY8HsMKIdF8Bi6+XiA0sHwcaUqJsaczBqgmbJbPqG8N4CokiUnUu4QDm
|
||||||
--pavrbLYh8Q4RWBboYnVxY3mNBBzan1Zz4--
|
ClWa0rG4I5rrx4PrTbzAt7cGIb8N3ACNI7vI/Q==
|
||||||
|
=Llod
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
|
--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855--
|
||||||
|
|||||||
@ -1,161 +1,143 @@
|
|||||||
Content-Type: multipart/signed; micalg=pgp-sha256;
|
Content-Type: multipart/signed;
|
||||||
protocol="application/pgp-signature";
|
boundary=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855;
|
||||||
boundary="MHEDFShwcX18dyE3X7RXujo5fjpgdjHNM"
|
micalg=SHA-256; protocol="application/pgp-signature"
|
||||||
|
|
||||||
This is an OpenPGP/MIME signed message (RFC 4880 and 3156)
|
--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||||
--MHEDFShwcX18dyE3X7RXujo5fjpgdjHNM
|
Content-Type: multipart/mixed; boundary="------------8730PPHC3FpcshavBTup7Fxz";
|
||||||
Content-Type: multipart/mixed; boundary="FBBl2LNv76z8UkvHhSkT9vLwVwxqV8378";
|
protected-headers="v1"
|
||||||
protected-headers="v1"
|
From: "pm.bridge.qa" <pm.bridge.qa@gmail.com>
|
||||||
Subject: Alternative
|
To: "InfernalBridgeTester@proton.me" <InfernalbridgeTester@proton.me>
|
||||||
From: "pm.bridge.qa" <pm.bridge.qa@gmail.com>
|
Message-ID: <5dce90f2-f1e5-eecb-672e-f39def728f44@gmail.com>
|
||||||
To: schizofrenic@pm.me
|
Subject: Alternative
|
||||||
Message-ID: <753d0314-0286-2c88-2abb-f8080ac7a4cb@gmail.com>
|
|
||||||
|
--------------8730PPHC3FpcshavBTup7Fxz
|
||||||
--FBBl2LNv76z8UkvHhSkT9vLwVwxqV8378
|
Content-Type: multipart/mixed; boundary="------------CCuN7f3eLVIM0a1fSlNjk0pq"
|
||||||
Content-Type: multipart/mixed;
|
|
||||||
boundary="------------F97C8ED4878E94675762AE43"
|
--------------CCuN7f3eLVIM0a1fSlNjk0pq
|
||||||
Content-Language: en-US
|
Content-Type: multipart/alternative;
|
||||||
|
boundary="------------5kz0EAFr3j63ikmxfQPRt0bq"
|
||||||
This is a multi-part message in MIME format.
|
|
||||||
--------------F97C8ED4878E94675762AE43
|
--------------5kz0EAFr3j63ikmxfQPRt0bq
|
||||||
Content-Type: multipart/alternative;
|
Content-Type: text/plain; charset=UTF-8; format=flowed
|
||||||
boundary="------------041318B15DD3FA540FED32C6"
|
Content-Transfer-Encoding: base64
|
||||||
|
|
||||||
|
VGhpcyBSaWNoIGZvcm1hdGVkIHRleHQNCg0KICAqIFdoYXQga2luZCBvZiBzaG9lcyBkbyBu
|
||||||
--------------041318B15DD3FA540FED32C6
|
aW5qYXMgd2Vhcj8gKlNuZWFrZXJzISoNCiAgKiBIb3cgZG9lcyBhIHBlbmd1aW4gYnVpbGQg
|
||||||
Content-Type: text/plain; charset=utf-8; format=flowed
|
aXRzIGhvdXNlPyAqSWdsb29zIGl0IHRvZ2V0aGVyKg0KDQpUaGlzIFJpY2ggZm9ybWF0ZWQg
|
||||||
Content-Transfer-Encoding: quoted-printable
|
dGV4dA0KDQogICogL1doYXQga2luZCBvZiBzaG9lcyBkbyBuaW5qYXMgd2Vhcj8gLypTbmVh
|
||||||
|
a2VycyEqDQogICogL0hvdyBkb2VzIGEgcGVuZ3VpbiBidWlsZCBpdHMgaG91c2U/LyoqXy8q
|
||||||
This Rich formated text
|
SWdsb29zIGl0IHRvZ2V0aGVyLiovXw0KDQoNCg0K
|
||||||
|
--------------5kz0EAFr3j63ikmxfQPRt0bq
|
||||||
* /What kind of shoes do ninjas wear? /*Sneakers!*
|
Content-Type: text/html; charset=UTF-8
|
||||||
* /How does a penguin build its house?/**_/*Igloos it together.*/_
|
Content-Transfer-Encoding: quoted-printable
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
|
||||||
--------------041318B15DD3FA540FED32C6
|
<meta http-equiv=3D"content-type" content=3D"text/html; charset=3DUTF=
|
||||||
Content-Type: text/html; charset=utf-8
|
-8">
|
||||||
Content-Transfer-Encoding: quoted-printable
|
</head>
|
||||||
|
<body>
|
||||||
<html>
|
<p>This Rich formated text<br>
|
||||||
<head>
|
</p>
|
||||||
|
<ul>
|
||||||
<meta http-equiv=3D"content-type" content=3D"text/html; charset=3DUTF=
|
<li>What kind of shoes do ninjas wear? <b>Sneakers!</b></li>
|
||||||
-8">
|
<li>How does a penguin build its house? <b>Igloos it together</b></=
|
||||||
</head>
|
li>
|
||||||
<body>
|
</ul>
|
||||||
<p>This <font color=3D"#ee24cc">Rich</font> formated text</p>
|
<p> </p>
|
||||||
<ul>
|
<p>This <font color=3D"3D"#ee24cc"">Rich</font> formated
|
||||||
<li><i>What kind of shoes do ninjas wear? </i><b>Sneakers!</b></li>=
|
text</p>
|
||||||
|
<ul>
|
||||||
<li><i>How does a penguin build its house?</i><b> </b><u><i><b>Iglo=
|
<li><i>What kind of shoes do ninjas wear? </i><b>Sneakers!</b></li>=
|
||||||
os
|
|
||||||
it together.</b></i></u></li>
|
<li><i>How does a penguin build its house?</i><b> </b><u><i><b>Iglo=
|
||||||
</ul>
|
os
|
||||||
<p><br>
|
it together.</b></i></u></li>
|
||||||
</p>
|
</ul>
|
||||||
<p><br>
|
<p><br>
|
||||||
</p>
|
</p>
|
||||||
</body>
|
<p><br>
|
||||||
</html>
|
</p>
|
||||||
|
<p>
|
||||||
--------------041318B15DD3FA540FED32C6--
|
<meta http-equiv=3D"3D"content-type""
|
||||||
|
content=3D"3D"text/html;" charset=3D"3DUTF-8"">
|
||||||
--------------F97C8ED4878E94675762AE43
|
</p>
|
||||||
Content-Type: application/pdf;
|
</body>
|
||||||
name="minimal.pdf"
|
</html>
|
||||||
Content-Transfer-Encoding: base64
|
|
||||||
Content-Disposition: attachment;
|
--------------5kz0EAFr3j63ikmxfQPRt0bq--
|
||||||
filename="minimal.pdf"
|
--------------CCuN7f3eLVIM0a1fSlNjk0pq
|
||||||
|
Content-Type: application/pgp-keys; name="OpenPGP_0x161C0875822359F7.asc"
|
||||||
JVBERi0xLjEKJcKlwrHDqwoKMSAwIG9iagogIDw8IC9UeXBlIC9DYXRhbG9nCiAgICAgL1Bh
|
Content-Disposition: attachment; filename="OpenPGP_0x161C0875822359F7.asc"
|
||||||
Z2VzIDIgMCBSCiAgPj4KZW5kb2JqCgoyIDAgb2JqCiAgPDwgL1R5cGUgL1BhZ2VzCiAgICAg
|
Content-Description: OpenPGP public key
|
||||||
L0tpZHMgWzMgMCBSXQogICAgIC9Db3VudCAxCiAgICAgL01lZGlhQm94IFswIDAgMzAwIDE0
|
Content-Transfer-Encoding: quoted-printable
|
||||||
NF0KICA+PgplbmRvYmoKCjMgMCBvYmoKICA8PCAgL1R5cGUgL1BhZ2UKICAgICAgL1BhcmVu
|
|
||||||
dCAyIDAgUgogICAgICAvUmVzb3VyY2VzCiAgICAgICA8PCAvRm9udAogICAgICAgICAgIDw8
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
IC9GMQogICAgICAgICAgICAgICA8PCAvVHlwZSAvRm9udAogICAgICAgICAgICAgICAgICAv
|
|
||||||
U3VidHlwZSAvVHlwZTEKICAgICAgICAgICAgICAgICAgL0Jhc2VGb250IC9UaW1lcy1Sb21h
|
xsBNBFxlUPwBCACx954Ey4SD88f8DSKFw9BaZNXrNwYxNYSgqaqOGHQ0WllF3mst
|
||||||
bgogICAgICAgICAgICAgICA+PgogICAgICAgICAgID4+CiAgICAgICA+PgogICAgICAvQ29u
|
EhTfuxxCZpDhI5IhWCXUNxanzsFkn88mRDwFRVl2sf2aAG4/P/p1381oh2kd0UEl
|
||||||
dGVudHMgNCAwIFIKICA+PgplbmRvYmoKCjQgMCBvYmoKICA8PCAvTGVuZ3RoIDU1ID4+CnN0
|
MRQaQGzoCadQMaQOL9WYTf4SPWSCzjrPyKgjq5FbqjbF/ndu376na9L+tnsEXyL6
|
||||||
cmVhbQogIEJUCiAgICAvRjEgMTggVGYKICAgIDAgMCBUZAogICAgKEhlbGxvIFdvcmxkKSBU
|
RrI6aZhjWG73xlqxS65dzTIYzsyM/P97xSndNvlvWtGvLlpFkzxfAEGpVzfOYVYF
|
||||||
agogIEVUCmVuZHN0cmVhbQplbmRvYmoKCnhyZWYKMCA1CjAwMDAwMDAwMDAgNjU1MzUgZiAK
|
Koc8rGmUDwrDWYfk5JczRDDogJnY+BNMZf9pjSqk6rTyBOfNH5fpU8r7A5Q7l+HV
|
||||||
MDAwMDAwMDAxOCAwMDAwMCBuIAowMDAwMDAwMDc3IDAwMDAwIG4gCjAwMDAwMDAxNzggMDAw
|
akvMUQ9DzDWJtg2ru1Y8hexnJOF68avO4+a1ABEBAAHNKEJyaWRnZSBLeXUtRWh5
|
||||||
MDAgbiAKMDAwMDAwMDQ1NyAwMDAwMCBuIAp0cmFpbGVyCiAgPDwgIC9Sb290IDEgMCBSCiAg
|
aiA8cG0uYnJpZGdlLnFhQGdtYWlsLmNvbT7CwI4EEwEIADgCGwMFCwkIBwIGFQoJ
|
||||||
ICAgIC9TaXplIDUKICA+PgpzdGFydHhyZWYKNTY1CiUlRU9GCg==
|
CAsCBBYCAwECHgECF4AWIQRc5gl5cC8oW/Mo+bEWHAh1giNZ9wUCZEfUzQAKCRAW
|
||||||
--------------F97C8ED4878E94675762AE43
|
HAh1giNZ9872B/4mM6cXErSSYK6/apGUVebg9QiP1RhFlLE/kg7BW3FaSP/yTUZN
|
||||||
Content-Type: application/pgp-keys;
|
ZdX7WPnEVyaa5Dk4xRZiB47Yc5myspwc+JEJ3YDAHq+4n/D74TF1kUCzP+QVsWcn
|
||||||
name="OpenPGP_0x161C0875822359F7.asc"
|
40UqX9gHbO01O/DYtoxMOljEgkfQjEZcRoHuUhCUzldFf8aV+uZKiOXhrPYCwsil
|
||||||
Content-Transfer-Encoding: quoted-printable
|
nh0RAmDV7fLoOfKXMLiKXE8wM/5Bax+dk2AmEM4bOTIo58GGDDqseIg03ocrW7vP
|
||||||
Content-Disposition: attachment;
|
egmxiLUwmsHIIDwTq6qZ0CVxbt2uv4cCyNz/0pmRzG7p8192Evdu8JOuLSj3pI1X
|
||||||
filename="OpenPGP_0x161C0875822359F7.asc"
|
00Ub326yay3BBUnsL4PJIGoly8hnLb5N3cyNwsCUBBMBCAA+FiEEXOYJeXAvKFvz
|
||||||
|
KPmxFhwIdYIjWfcFAlxlUPwCGwMFCQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgEC
|
||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
F4AACgkQFhwIdYIjWfeQzgf+NWseR+UvrJ7I1CFd6M2+RmyIC1QTQ+uyJIhIBQfC
|
||||||
|
vBhueU9GUPWR6a6GWsb2DGCMkBM2aHoTCcf46fq87VspYb3+b0JsKfRFxBa2cuNH
|
||||||
xsBNBFxlUPwBCACx954Ey4SD88f8DSKFw9BaZNXrNwYxNYSgqaqOGHQ0WllF3mstEhTfuxxCZ=
|
Ey6PK3xHBDnYFtSZaRB7QMQMfYVin5oyHq8mOd4mImOKfpGhuuhq1hrT8sOhVxRY
|
||||||
pDh
|
Nl/2Hanya7tEJlVyAAEwtN4QVCqiRjjD7kBQ+mgxdDFo62X+sl4Zz2BFlZks+c1+
|
||||||
I5IhWCXUNxanzsFkn88mRDwFRVl2sf2aAG4/P/p1381oh2kd0UElMRQaQGzoCadQMaQOL9WYT=
|
LRWwaQZvGgf2tm2NqZhC04CKc2Gg5j7wNJBPVh/FVluxY27D2hV6v9/cTvXDGo8J
|
||||||
f4S
|
pnZ28CnQqiiaKEU83BGwcUlcr4G5YApHyIPujaxffE2R887ATQRcZVD8AQgA4dh7
|
||||||
PWSCzjrPyKgjq5FbqjbF/ndu376na9L+tnsEXyL6RrI6aZhjWG73xlqxS65dzTIYzsyM/P97x=
|
F9t8lZybfX8Rc9w9O5AgTb7uB0H6bOoda4DcRoRN9lZqREh3m7yAecYy+sSmyKqk
|
||||||
Snd
|
Wt8f+yg1QTSb3GtIFLKhLeZM2BfPBcn9jQXHo6SG3D+wUv4PguijqMGI/hLduk+h
|
||||||
NvlvWtGvLlpFkzxfAEGpVzfOYVYFKoc8rGmUDwrDWYfk5JczRDDogJnY+BNMZf9pjSqk6rTyB=
|
ncBVxMVHJfNCb6F/Mh0Dn7yaXYgUW48gfFKnwmpJ5t3O3Tbl5oX5c/8N2gY99qvW
|
||||||
OfN
|
chqgNTaRwqS8lQJb0jUP6Wrf6xlAj2fXbsOCoVFKhJmrUD1RT5LrU6LhGUGzCvov
|
||||||
H5fpU8r7A5Q7l+HVakvMUQ9DzDWJtg2ru1Y8hexnJOF68avO4+a1ABEBAAHNKEJyaWRnZSBLe=
|
nITh9wLCgrjQ37wg8Iq57EqiCZgIJdZQuHXvm7FmAjKjfXovfYTgkVMoWd4N7K/E
|
||||||
XUt
|
QaMFQHXL42cXiAygCwARAQABwsB2BBgBCAAgAhsMFiEEXOYJeXAvKFvzKPmxFhwI
|
||||||
RWh5aiA8cG0uYnJpZGdlLnFhQGdtYWlsLmNvbT7CwJQEEwEIAD4CGwMFCwkIBwIGFQoJCAsCB=
|
dYIjWfcFAmRH1LoACgkQFhwIdYIjWfcnHwf/e4jhRXmEhQeQqjsbMqPU4x73tATs
|
||||||
BYC
|
3xsi4et9vrFEBMN6qsFa3gKq41Wv8JyxVuuz23+BgZ+mZ2iDCKuK7+neMJw2eG9f
|
||||||
AwECHgECF4AWIQRc5gl5cC8oW/Mo+bEWHAh1giNZ9wUCYC32ygUJB4sMzgAKCRAWHAh1giNZ9=
|
MkJCExK1r1Pt7YaXmUvzVHlpS2ZVxWrg4QnT6QKuU58O/uZatKXtgRHDQReCEaAU
|
||||||
/K8
|
bO1EFYa/0KTfsWsG50FDNaCDjJY01rkGbT6O+TrJbUJ+ffjk5+2WEA+EN0p9LzwM
|
||||||
B/4qs84Ii/zKH+q+C8vwO4jUJkOM73qD0pgB7zBs651zWbpgopyol1YUKNpFaHlx/Qch7RDI7=
|
xWzwWuH6LLc28fJLUxln4QXLUK6cEtOlaqEqMK/ERWPUvrLIwivh5atrfGQtAgS9
|
||||||
Vcz
|
WlNQAD6nADn68Pa4p9KzdXR9O4Nbg3mfUp8i7nnuUDe691WPG/bYjiVfZsLAfAQY
|
||||||
1+60/KZJSJR19/N2EDVbCUdh8ueioUp9X/218YWV2TRJNxTnljd4FAn7smZnXuP1TsLjQ6sKO=
|
AQgAJhYhBFzmCXlwLyhb8yj5sRYcCHWCI1n3BQJcZVD8AhsMBQkDwmcAAAoJEBYc
|
||||||
V0U
|
CHWCI1n3rhcH/iCB0ZV861H0RKJ2F7bXEyCLR2ncBFUCnFo3muSrN9NXTojz2vwv
|
||||||
u6JoiG6LZFXqDgxYpA++58Rkl6xaY6R71VkmVQlbEKtubX9AjHydq97Y+Jvn11XzWZaKhv4L7=
|
zexRBpZzaRJoksBkvH+ofuZ1iK7ycZO23dnukvPwGQsz3QiITjVeB6ZR0250MG1q
|
||||||
6Pa
|
A5yZRlZCsCbGJb4/2e8Ey8BbblHn49Zta4l2Ex5NpNNQ8FYoqXhXu5Bd2F/wX/Bp
|
||||||
4tMKXvvrKh1oywMmh6mZJo+5ZA/ABTkr45cwlTPYqGTS9+uvOHt+PH/oYwwJB4ls2cIAUldSj=
|
5gkZegfE3H9Dw4QjP82Mt0RZSBg9RMGCk6nNfEuze1Up+IOdtqzf3/Z8J5XxLzN2
|
||||||
TVQ
|
s8WPmDwJDwvxJRtto8U+ulv4ElcwlA+wYiKAq7cRCKGM/si5ClkUNgb319grUrBU
|
||||||
IsseYz3LlbcCfKJiiCFxeHOQXA5J6zNLKOT58TsczsBNBFxlUPwBCADh2HsX23yVnJt9fxFz3=
|
6h8SuYtgnD84965xRiVAgtH4wCPN544N8CE=3D
|
||||||
D07
|
=3DNmqc
|
||||||
kCBNvu4HQfps6h1rgNxGhE32VmpESHebvIB5xjL6xKbIqqRa3x/7KDVBNJvca0gUsqEt5kzYF=
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
88F
|
|
||||||
yf2NBcejpIbcP7BS/g+C6KOowYj+Et26T6GdwFXExUcl80JvoX8yHQOfvJpdiBRbjyB8UqfCa=
|
--------------CCuN7f3eLVIM0a1fSlNjk0pq--
|
||||||
knm
|
|
||||||
3c7dNuXmhflz/w3aBj32q9ZyGqA1NpHCpLyVAlvSNQ/pat/rGUCPZ9duw4KhUUqEmatQPVFPk=
|
--------------8730PPHC3FpcshavBTup7Fxz--
|
||||||
utT
|
|
||||||
ouEZQbMK+i+chOH3AsKCuNDfvCDwirnsSqIJmAgl1lC4de+bsWYCMqN9ei99hOCRUyhZ3g3sr=
|
--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||||
8RB
|
Content-Description: OpenPGP digital signature
|
||||||
owVAdcvjZxeIDKALABEBAAHCwHwEGAEIACYCGwwWIQRc5gl5cC8oW/Mo+bEWHAh1giNZ9wUCY=
|
Content-Disposition: attachment; filename=OpenPGP_signature
|
||||||
C32
|
Content-Type: application/pgp-signature; name=OpenPGP_signature.asc
|
||||||
lAUJB4sMmAAKCRAWHAh1giNZ9+Y2B/9rTKZaKviae+ummXNumXcrKvbkAAvfuLpKUn53FlQLm=
|
|
||||||
L6H
|
-----BEGIN PGP SIGNATURE-----
|
||||||
jB++lJnPWvVSzdZxdv8FiPP3d632XHKUrkQRQM/9byRDXDommi7Qttx7YCkhd4JLVYqJqpnAQ=
|
Version: GopenPGP 2.7.1
|
||||||
xI5
|
Comment: https://gopenpgp.org
|
||||||
RMkXiZNWyr1lz8JOM1XvDk1M7sJwPMWews8VOIE03E1nt7AsQGnvHtadgEnQaufrYNX3hFA8S=
|
|
||||||
osO
|
wsB5BAABCAAjFiEEXOYJeXAvKFvzKPmxFhwIdYIjWfcFAmRH94EFAwAAAAAACgkQ
|
||||||
HSnedcys6yrzCSIGCqCD9VHbnMtS4DOv0XJGh2hwc8omzH0KZA517dyKBorJRwadcVauGXDKx=
|
FhwIdYIjWfcFfgf/XHYGr9+DcntW0TLe6dO82xhLuO6HFmQnnChnfNHKA5U6pOeU
|
||||||
Etv
|
5wJoEFL2O4WEU1hbuf5K0wpgPi8ZF+r966bCUTt+tYT/p7sKV0OXYJjtPYxiL+ju
|
||||||
Im4rl94PR/3An1Mj6HeeVVpLqDQ5Jb9J90BahWeQ53FzRa4EQzYCw0nLnxcsT1ZEEP5u
|
RaNZgK3rIDyi2DNjbRXSV1Y7H2S3pMLAwPio7ovNpe3OgfkAgFDdiG+NnXl8CzqN
|
||||||
=3Dv/1p
|
suhMwqJbLJRWlEaX7UdbcLimpNtPIU7wtr4YV0NIsqt3EV7AeHnVI9cHJMLvF5tB
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
VrnkxXbdO3xEylUB+MNmX5NDIrCg5Iwwq4NMk6qaMK80J9XSxERln56SJVN/+tIu
|
||||||
|
TZFgwBWM/n48prGXrEo8TPk5UkrVYhLXl/ndTg==
|
||||||
--------------F97C8ED4878E94675762AE43--
|
=FIoh
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
--FBBl2LNv76z8UkvHhSkT9vLwVwxqV8378--
|
--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855--
|
||||||
|
|
||||||
--MHEDFShwcX18dyE3X7RXujo5fjpgdjHNM
|
|
||||||
Content-Type: application/pgp-signature; name="OpenPGP_signature.asc"
|
|
||||||
Content-Description: OpenPGP digital signature
|
|
||||||
Content-Disposition: attachment; filename="OpenPGP_signature"
|
|
||||||
|
|
||||||
-----BEGIN PGP SIGNATURE-----
|
|
||||||
|
|
||||||
wsB5BAABCAAjFiEEXOYJeXAvKFvzKPmxFhwIdYIjWfcFAmBciUoFAwAAAAAACgkQFhwIdYIjWfez
|
|
||||||
rgf+NZCibnCUTovpWRVRiiPQtBPGeHUPEwz2xq2zz4AaqrHC2v4mYUIPe6am7INk8fkBLsa8Dj/A
|
|
||||||
UN/28Qh7tNb7JsXtHDT4PIoXszukQ8VIRbe09mSkkP6jR4WzNR166d6n3rSxzHpviOyQldjjpOMr
|
|
||||||
Zl7LxmgGr4ojsgCf6pvurWwCCOGJqbSusrD6JVv6DsmPmmQeBmnlTK/0oG9pnlNkugpNB1WS2K5d
|
|
||||||
RY6+kWkSrxbq95HrgILpHip8Y/+ITWvQocm14PBIAAdW8Hr7iFQLETFJ/KDA+VP19Bt8n4Kitdi8
|
|
||||||
DPqMsV0oOhATqBjnD63AePJ0VWg8R1z6GEK5A+WOpg==
|
|
||||||
=Bc6p
|
|
||||||
-----END PGP SIGNATURE-----
|
|
||||||
|
|
||||||
--MHEDFShwcX18dyE3X7RXujo5fjpgdjHNM--
|
|
||||||
|
|||||||
@ -1,103 +1,95 @@
|
|||||||
Content-Type: multipart/signed; micalg=pgp-sha256;
|
Content-Type: multipart/signed;
|
||||||
protocol="application/pgp-signature";
|
boundary=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855;
|
||||||
boundary="x4FrOFG2PnNJvlbzxe80NPwxzh2yUHABp"
|
micalg=SHA-256; protocol="application/pgp-signature"
|
||||||
|
|
||||||
This is an OpenPGP/MIME signed message (RFC 4880 and 3156)
|
--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||||
--x4FrOFG2PnNJvlbzxe80NPwxzh2yUHABp
|
Content-Type: multipart/mixed; boundary="------------bhHlRtbq4tEzp2NgVmAJ4AbV";
|
||||||
Content-Type: multipart/mixed; boundary="bBln6dwDJTLkin5LPHkHBQudqRLwIzTUH";
|
protected-headers="v1"
|
||||||
protected-headers="v1"
|
From: "pm.bridge.qa" <pm.bridge.qa@gmail.com>
|
||||||
Subject: simple plaintext body
|
To: "InfernalBridgeTester@proton.me" <InfernalbridgeTester@proton.me>
|
||||||
From: "pm.bridge.qa" <pm.bridge.qa@gmail.com>
|
Message-ID: <9bb09a2a-1442-9439-f53f-490df7e46331@gmail.com>
|
||||||
To: schizofrenic@pm.me
|
Subject: simple plaintext body
|
||||||
Message-ID: <adb5ac5d-b8f6-c9a3-5cc0-0fb2e9677512@gmail.com>
|
|
||||||
|
--------------bhHlRtbq4tEzp2NgVmAJ4AbV
|
||||||
--bBln6dwDJTLkin5LPHkHBQudqRLwIzTUH
|
Content-Type: multipart/mixed; boundary="------------FHzf8jBNv06d9SowGc7KOjXr"
|
||||||
Content-Type: multipart/mixed;
|
|
||||||
boundary="------------1B34C666A4C2FB03E0324F1A"
|
--------------FHzf8jBNv06d9SowGc7KOjXr
|
||||||
Content-Language: en-US
|
Content-Type: text/plain; charset=UTF-8; format=flowed
|
||||||
|
Content-Transfer-Encoding: base64
|
||||||
This is a multi-part message in MIME format.
|
|
||||||
--------------1B34C666A4C2FB03E0324F1A
|
V2h5IGRvbid0IGNyYWJzIGdpdmUgdG8gY2hhcml0eT8gQmVjYXVzZSB0aGV5J3JlIHNoZWxs
|
||||||
Content-Type: text/plain; charset=utf-8; format=flowed
|
ZmlzaC4NCg0K
|
||||||
Content-Transfer-Encoding: quoted-printable
|
--------------FHzf8jBNv06d9SowGc7KOjXr
|
||||||
|
Content-Type: application/pgp-keys; name="OpenPGP_0x161C0875822359F7.asc"
|
||||||
Why don't crabs give to charity? Because they're shellfish.
|
Content-Disposition: attachment; filename="OpenPGP_0x161C0875822359F7.asc"
|
||||||
|
Content-Description: OpenPGP public key
|
||||||
|
Content-Transfer-Encoding: quoted-printable
|
||||||
|
|
||||||
--------------1B34C666A4C2FB03E0324F1A
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
Content-Type: application/pgp-keys;
|
|
||||||
name="OpenPGP_0x161C0875822359F7.asc"
|
xsBNBFxlUPwBCACx954Ey4SD88f8DSKFw9BaZNXrNwYxNYSgqaqOGHQ0WllF3mst
|
||||||
Content-Transfer-Encoding: quoted-printable
|
EhTfuxxCZpDhI5IhWCXUNxanzsFkn88mRDwFRVl2sf2aAG4/P/p1381oh2kd0UEl
|
||||||
Content-Disposition: attachment;
|
MRQaQGzoCadQMaQOL9WYTf4SPWSCzjrPyKgjq5FbqjbF/ndu376na9L+tnsEXyL6
|
||||||
filename="OpenPGP_0x161C0875822359F7.asc"
|
RrI6aZhjWG73xlqxS65dzTIYzsyM/P97xSndNvlvWtGvLlpFkzxfAEGpVzfOYVYF
|
||||||
|
Koc8rGmUDwrDWYfk5JczRDDogJnY+BNMZf9pjSqk6rTyBOfNH5fpU8r7A5Q7l+HV
|
||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
akvMUQ9DzDWJtg2ru1Y8hexnJOF68avO4+a1ABEBAAHNKEJyaWRnZSBLeXUtRWh5
|
||||||
|
aiA8cG0uYnJpZGdlLnFhQGdtYWlsLmNvbT7CwI4EEwEIADgCGwMFCwkIBwIGFQoJ
|
||||||
xsBNBFxlUPwBCACx954Ey4SD88f8DSKFw9BaZNXrNwYxNYSgqaqOGHQ0WllF3mstEhTfuxxCZ=
|
CAsCBBYCAwECHgECF4AWIQRc5gl5cC8oW/Mo+bEWHAh1giNZ9wUCZEfUzQAKCRAW
|
||||||
pDh
|
HAh1giNZ9872B/4mM6cXErSSYK6/apGUVebg9QiP1RhFlLE/kg7BW3FaSP/yTUZN
|
||||||
I5IhWCXUNxanzsFkn88mRDwFRVl2sf2aAG4/P/p1381oh2kd0UElMRQaQGzoCadQMaQOL9WYT=
|
ZdX7WPnEVyaa5Dk4xRZiB47Yc5myspwc+JEJ3YDAHq+4n/D74TF1kUCzP+QVsWcn
|
||||||
f4S
|
40UqX9gHbO01O/DYtoxMOljEgkfQjEZcRoHuUhCUzldFf8aV+uZKiOXhrPYCwsil
|
||||||
PWSCzjrPyKgjq5FbqjbF/ndu376na9L+tnsEXyL6RrI6aZhjWG73xlqxS65dzTIYzsyM/P97x=
|
nh0RAmDV7fLoOfKXMLiKXE8wM/5Bax+dk2AmEM4bOTIo58GGDDqseIg03ocrW7vP
|
||||||
Snd
|
egmxiLUwmsHIIDwTq6qZ0CVxbt2uv4cCyNz/0pmRzG7p8192Evdu8JOuLSj3pI1X
|
||||||
NvlvWtGvLlpFkzxfAEGpVzfOYVYFKoc8rGmUDwrDWYfk5JczRDDogJnY+BNMZf9pjSqk6rTyB=
|
00Ub326yay3BBUnsL4PJIGoly8hnLb5N3cyNwsCUBBMBCAA+FiEEXOYJeXAvKFvz
|
||||||
OfN
|
KPmxFhwIdYIjWfcFAlxlUPwCGwMFCQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgEC
|
||||||
H5fpU8r7A5Q7l+HVakvMUQ9DzDWJtg2ru1Y8hexnJOF68avO4+a1ABEBAAHNKEJyaWRnZSBLe=
|
F4AACgkQFhwIdYIjWfeQzgf+NWseR+UvrJ7I1CFd6M2+RmyIC1QTQ+uyJIhIBQfC
|
||||||
XUt
|
vBhueU9GUPWR6a6GWsb2DGCMkBM2aHoTCcf46fq87VspYb3+b0JsKfRFxBa2cuNH
|
||||||
RWh5aiA8cG0uYnJpZGdlLnFhQGdtYWlsLmNvbT7CwJQEEwEIAD4CGwMFCwkIBwIGFQoJCAsCB=
|
Ey6PK3xHBDnYFtSZaRB7QMQMfYVin5oyHq8mOd4mImOKfpGhuuhq1hrT8sOhVxRY
|
||||||
BYC
|
Nl/2Hanya7tEJlVyAAEwtN4QVCqiRjjD7kBQ+mgxdDFo62X+sl4Zz2BFlZks+c1+
|
||||||
AwECHgECF4AWIQRc5gl5cC8oW/Mo+bEWHAh1giNZ9wUCYC32ygUJB4sMzgAKCRAWHAh1giNZ9=
|
LRWwaQZvGgf2tm2NqZhC04CKc2Gg5j7wNJBPVh/FVluxY27D2hV6v9/cTvXDGo8J
|
||||||
/K8
|
pnZ28CnQqiiaKEU83BGwcUlcr4G5YApHyIPujaxffE2R887ATQRcZVD8AQgA4dh7
|
||||||
B/4qs84Ii/zKH+q+C8vwO4jUJkOM73qD0pgB7zBs651zWbpgopyol1YUKNpFaHlx/Qch7RDI7=
|
F9t8lZybfX8Rc9w9O5AgTb7uB0H6bOoda4DcRoRN9lZqREh3m7yAecYy+sSmyKqk
|
||||||
Vcz
|
Wt8f+yg1QTSb3GtIFLKhLeZM2BfPBcn9jQXHo6SG3D+wUv4PguijqMGI/hLduk+h
|
||||||
1+60/KZJSJR19/N2EDVbCUdh8ueioUp9X/218YWV2TRJNxTnljd4FAn7smZnXuP1TsLjQ6sKO=
|
ncBVxMVHJfNCb6F/Mh0Dn7yaXYgUW48gfFKnwmpJ5t3O3Tbl5oX5c/8N2gY99qvW
|
||||||
V0U
|
chqgNTaRwqS8lQJb0jUP6Wrf6xlAj2fXbsOCoVFKhJmrUD1RT5LrU6LhGUGzCvov
|
||||||
u6JoiG6LZFXqDgxYpA++58Rkl6xaY6R71VkmVQlbEKtubX9AjHydq97Y+Jvn11XzWZaKhv4L7=
|
nITh9wLCgrjQ37wg8Iq57EqiCZgIJdZQuHXvm7FmAjKjfXovfYTgkVMoWd4N7K/E
|
||||||
6Pa
|
QaMFQHXL42cXiAygCwARAQABwsB2BBgBCAAgAhsMFiEEXOYJeXAvKFvzKPmxFhwI
|
||||||
4tMKXvvrKh1oywMmh6mZJo+5ZA/ABTkr45cwlTPYqGTS9+uvOHt+PH/oYwwJB4ls2cIAUldSj=
|
dYIjWfcFAmRH1LoACgkQFhwIdYIjWfcnHwf/e4jhRXmEhQeQqjsbMqPU4x73tATs
|
||||||
TVQ
|
3xsi4et9vrFEBMN6qsFa3gKq41Wv8JyxVuuz23+BgZ+mZ2iDCKuK7+neMJw2eG9f
|
||||||
IsseYz3LlbcCfKJiiCFxeHOQXA5J6zNLKOT58TsczsBNBFxlUPwBCADh2HsX23yVnJt9fxFz3=
|
MkJCExK1r1Pt7YaXmUvzVHlpS2ZVxWrg4QnT6QKuU58O/uZatKXtgRHDQReCEaAU
|
||||||
D07
|
bO1EFYa/0KTfsWsG50FDNaCDjJY01rkGbT6O+TrJbUJ+ffjk5+2WEA+EN0p9LzwM
|
||||||
kCBNvu4HQfps6h1rgNxGhE32VmpESHebvIB5xjL6xKbIqqRa3x/7KDVBNJvca0gUsqEt5kzYF=
|
xWzwWuH6LLc28fJLUxln4QXLUK6cEtOlaqEqMK/ERWPUvrLIwivh5atrfGQtAgS9
|
||||||
88F
|
WlNQAD6nADn68Pa4p9KzdXR9O4Nbg3mfUp8i7nnuUDe691WPG/bYjiVfZsLAfAQY
|
||||||
yf2NBcejpIbcP7BS/g+C6KOowYj+Et26T6GdwFXExUcl80JvoX8yHQOfvJpdiBRbjyB8UqfCa=
|
AQgAJhYhBFzmCXlwLyhb8yj5sRYcCHWCI1n3BQJcZVD8AhsMBQkDwmcAAAoJEBYc
|
||||||
knm
|
CHWCI1n3rhcH/iCB0ZV861H0RKJ2F7bXEyCLR2ncBFUCnFo3muSrN9NXTojz2vwv
|
||||||
3c7dNuXmhflz/w3aBj32q9ZyGqA1NpHCpLyVAlvSNQ/pat/rGUCPZ9duw4KhUUqEmatQPVFPk=
|
zexRBpZzaRJoksBkvH+ofuZ1iK7ycZO23dnukvPwGQsz3QiITjVeB6ZR0250MG1q
|
||||||
utT
|
A5yZRlZCsCbGJb4/2e8Ey8BbblHn49Zta4l2Ex5NpNNQ8FYoqXhXu5Bd2F/wX/Bp
|
||||||
ouEZQbMK+i+chOH3AsKCuNDfvCDwirnsSqIJmAgl1lC4de+bsWYCMqN9ei99hOCRUyhZ3g3sr=
|
5gkZegfE3H9Dw4QjP82Mt0RZSBg9RMGCk6nNfEuze1Up+IOdtqzf3/Z8J5XxLzN2
|
||||||
8RB
|
s8WPmDwJDwvxJRtto8U+ulv4ElcwlA+wYiKAq7cRCKGM/si5ClkUNgb319grUrBU
|
||||||
owVAdcvjZxeIDKALABEBAAHCwHwEGAEIACYCGwwWIQRc5gl5cC8oW/Mo+bEWHAh1giNZ9wUCY=
|
6h8SuYtgnD84965xRiVAgtH4wCPN544N8CE=3D
|
||||||
C32
|
=3DNmqc
|
||||||
lAUJB4sMmAAKCRAWHAh1giNZ9+Y2B/9rTKZaKviae+ummXNumXcrKvbkAAvfuLpKUn53FlQLm=
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
L6H
|
|
||||||
jB++lJnPWvVSzdZxdv8FiPP3d632XHKUrkQRQM/9byRDXDommi7Qttx7YCkhd4JLVYqJqpnAQ=
|
--------------FHzf8jBNv06d9SowGc7KOjXr--
|
||||||
xI5
|
|
||||||
RMkXiZNWyr1lz8JOM1XvDk1M7sJwPMWews8VOIE03E1nt7AsQGnvHtadgEnQaufrYNX3hFA8S=
|
--------------bhHlRtbq4tEzp2NgVmAJ4AbV--
|
||||||
osO
|
|
||||||
HSnedcys6yrzCSIGCqCD9VHbnMtS4DOv0XJGh2hwc8omzH0KZA517dyKBorJRwadcVauGXDKx=
|
--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||||
Etv
|
Content-Description: OpenPGP digital signature
|
||||||
Im4rl94PR/3An1Mj6HeeVVpLqDQ5Jb9J90BahWeQ53FzRa4EQzYCw0nLnxcsT1ZEEP5u
|
Content-Disposition: attachment; filename=OpenPGP_signature
|
||||||
=3Dv/1p
|
Content-Type: application/pgp-signature; name=OpenPGP_signature.asc
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
--------------1B34C666A4C2FB03E0324F1A--
|
Comment: https://gopenpgp.org
|
||||||
|
Version: GopenPGP 2.7.1
|
||||||
--bBln6dwDJTLkin5LPHkHBQudqRLwIzTUH--
|
|
||||||
|
wsB5BAABCAAjFiEEXOYJeXAvKFvzKPmxFhwIdYIjWfcFAmRH6MwFAwAAAAAACgkQ
|
||||||
--x4FrOFG2PnNJvlbzxe80NPwxzh2yUHABp
|
FhwIdYIjWfdX5Qf/W2hFY5PiCrRTvcXGASc2RBLXmCh/mn0tTNUsGtyq/MhcNKfR
|
||||||
Content-Type: application/pgp-signature; name="OpenPGP_signature.asc"
|
9bSFCb3xz9q26MAJDHtO/Vm0lUjre42rLMkEIDIdJT960HIClELzmgglwFbVgdqy
|
||||||
Content-Description: OpenPGP digital signature
|
T0Psma8ySQpZ2LxZ1oleCXeXaxm4DOwQP+COfb5+FmLTA2z1djLA3HjFPNKglcUr
|
||||||
Content-Disposition: attachment; filename="OpenPGP_signature"
|
atzCTvlt2yqwrx6aeqTxcFezPkl1o+kdqjMCMP0LFFuImuor0vaCFUz76hgNC3kk
|
||||||
|
CflcYGPJIVH7D06UXkLKC5vnJZ+Pidn4K5sMkF3nXBlourmSU2cZFTNUUdHNAxi8
|
||||||
-----BEGIN PGP SIGNATURE-----
|
s9XTavfQxm4fyLPyGcgNpdM6PhZW9r+lZkr66w==
|
||||||
|
=se49
|
||||||
wsB5BAABCAAjFiEEXOYJeXAvKFvzKPmxFhwIdYIjWfcFAmBa9YIFAwAAAAAACgkQFhwIdYIjWfem
|
-----END PGP SIGNATURE-----
|
||||||
vQgAjUMAaxL7D6fRtFBqLjdQGr7PkDBigeQD9ax17CJFld7Zfo2dAYUzYJRi0HP0Kn1YCSBppF0w
|
--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855--
|
||||||
5/P8458H2sqfPC32ptbDCZ/seL0Rpt/gRx6yufbz7wQC0iUZxqxBq2Ox9PGZYSCrTO837lAVYxUo
|
|
||||||
aMnDL/K9ohAGIyTZVv31z+r3LLWQsFpfpB5hJFqsjQXA9IGKSQIkWbaeE+0wveJSwqxdTwYvsHs2
|
|
||||||
xjBw+s8tRHO/whP4pvzL185fGsHAb8x9a9oyoDVcszhw5xBpiWW37mI58qkQ6g+4wTarreuXGTp3
|
|
||||||
RKgPupoYOMJja90yh3TWovcmuZz6QOgne5Rbn3s+Vg==
|
|
||||||
=hUb8
|
|
||||||
-----END PGP SIGNATURE-----
|
|
||||||
|
|
||||||
--x4FrOFG2PnNJvlbzxe80NPwxzh2yUHABp--
|
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/constants"
|
"github.com/ProtonMail/proton-bridge/v3/internal/constants"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -43,19 +44,19 @@ func IsPortFree(port int) bool {
|
|||||||
|
|
||||||
func isOccupied(port string) bool {
|
func isOccupied(port string) bool {
|
||||||
// Try to create server at port.
|
// Try to create server at port.
|
||||||
dummyserver, err := net.Listen("tcp", port)
|
dummyServer, err := net.Listen("tcp", port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
_ = dummyserver.Close()
|
_ = dummyServer.Close()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindFreePortFrom finds first empty port, starting with `startPort`.
|
// FindFreePortFrom finds first empty port, starting with `startPort`, and excluding ports listed in exclude.
|
||||||
func FindFreePortFrom(startPort int) int {
|
func FindFreePortFrom(startPort int, exclude ...int) int {
|
||||||
loopedOnce := false
|
loopedOnce := false
|
||||||
freePort := startPort
|
freePort := startPort
|
||||||
for !IsPortFree(freePort) {
|
for slices.Contains(exclude, freePort) || !IsPortFree(freePort) {
|
||||||
freePort++
|
freePort++
|
||||||
if freePort >= maxPortNumber {
|
if freePort >= maxPortNumber {
|
||||||
freePort = 1
|
freePort = 1
|
||||||
|
|||||||
@ -32,12 +32,12 @@ func TestFreePort(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOccupiedPort(t *testing.T) {
|
func TestOccupiedPort(t *testing.T) {
|
||||||
dummyserver, err := net.Listen("tcp", ":"+strconv.Itoa(testPort))
|
dummyServer, err := net.Listen("tcp", ":"+strconv.Itoa(testPort))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.True(t, !IsPortFree(testPort), "port should be occupied")
|
require.True(t, !IsPortFree(testPort), "port should be occupied")
|
||||||
|
|
||||||
_ = dummyserver.Close()
|
_ = dummyServer.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFindFreePortFromDirectly(t *testing.T) {
|
func TestFindFreePortFromDirectly(t *testing.T) {
|
||||||
@ -46,11 +46,21 @@ func TestFindFreePortFromDirectly(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFindFreePortFromNextOne(t *testing.T) {
|
func TestFindFreePortFromNextOne(t *testing.T) {
|
||||||
dummyserver, err := net.Listen("tcp", ":"+strconv.Itoa(testPort))
|
dummyServer, err := net.Listen("tcp", ":"+strconv.Itoa(testPort))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
foundPort := FindFreePortFrom(testPort)
|
foundPort := FindFreePortFrom(testPort)
|
||||||
require.Equal(t, testPort+1, foundPort)
|
require.Equal(t, testPort+1, foundPort)
|
||||||
|
|
||||||
_ = dummyserver.Close()
|
_ = dummyServer.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindFreePortExcluding(t *testing.T) {
|
||||||
|
dummyServer, err := net.Listen("tcp", ":"+strconv.Itoa(testPort))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
foundPort := FindFreePortFrom(testPort, testPort+1, testPort+2)
|
||||||
|
require.Equal(t, testPort+3, foundPort)
|
||||||
|
|
||||||
|
_ = dummyServer.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -107,7 +107,10 @@ func TestFeatures(testingT *testing.T) {
|
|||||||
ctx.Step(`^the header in the "([^"]*)" request to "([^"]*)" has "([^"]*)" set to "([^"]*)"$`, s.theHeaderInTheRequestToHasSetTo)
|
ctx.Step(`^the header in the "([^"]*)" request to "([^"]*)" has "([^"]*)" set to "([^"]*)"$`, s.theHeaderInTheRequestToHasSetTo)
|
||||||
ctx.Step(`^the body in the "([^"]*)" request to "([^"]*)" is:$`, s.theBodyInTheRequestToIs)
|
ctx.Step(`^the body in the "([^"]*)" request to "([^"]*)" is:$`, s.theBodyInTheRequestToIs)
|
||||||
ctx.Step(`^the API requires bridge version at least "([^"]*)"$`, s.theAPIRequiresBridgeVersion)
|
ctx.Step(`^the API requires bridge version at least "([^"]*)"$`, s.theAPIRequiresBridgeVersion)
|
||||||
|
ctx.Step(`^the network port (\d+) is busy$`, s.networkPortIsBusy)
|
||||||
|
ctx.Step(`^the network port range (\d+)-(\d+) is busy$`, s.networkPortRangeIsBusy)
|
||||||
|
ctx.Step(`^bridge IMAP port is (\d+)`, s.bridgeIMAPPortIs)
|
||||||
|
ctx.Step(`^bridge SMTP port is (\d+)`, s.bridgeSMTPPortIs)
|
||||||
// ==== SETUP ====
|
// ==== SETUP ====
|
||||||
ctx.Step(`^there exists an account with username "([^"]*)" and password "([^"]*)"$`, s.thereExistsAnAccountWithUsernameAndPassword)
|
ctx.Step(`^there exists an account with username "([^"]*)" and password "([^"]*)"$`, s.thereExistsAnAccountWithUsernameAndPassword)
|
||||||
ctx.Step(`^there exists a disabled account with username "([^"]*)" and password "([^"]*)"$`, s.thereExistsAnAccountWithUsernameAndPasswordWithDisablePrimary)
|
ctx.Step(`^there exists a disabled account with username "([^"]*)" and password "([^"]*)"$`, s.thereExistsAnAccountWithUsernameAndPasswordWithDisablePrimary)
|
||||||
@ -121,7 +124,7 @@ func TestFeatures(testingT *testing.T) {
|
|||||||
ctx.Step(`^the address "([^"]*)" of account "([^"]*)" has the following messages in "([^"]*)":$`, s.theAddressOfAccountHasTheFollowingMessagesInMailbox)
|
ctx.Step(`^the address "([^"]*)" of account "([^"]*)" has the following messages in "([^"]*)":$`, s.theAddressOfAccountHasTheFollowingMessagesInMailbox)
|
||||||
ctx.Step(`^the address "([^"]*)" of account "([^"]*)" has (\d+) messages in "([^"]*)"$`, s.theAddressOfAccountHasMessagesInMailbox)
|
ctx.Step(`^the address "([^"]*)" of account "([^"]*)" has (\d+) messages in "([^"]*)"$`, s.theAddressOfAccountHasMessagesInMailbox)
|
||||||
ctx.Step(`^the following fields were changed in draft (\d+) for address "([^"]*)" of account "([^"]*)":$`, s.theFollowingFieldsWereChangedInDraftForAddressOfAccount)
|
ctx.Step(`^the following fields were changed in draft (\d+) for address "([^"]*)" of account "([^"]*)":$`, s.theFollowingFieldsWereChangedInDraftForAddressOfAccount)
|
||||||
ctx.Step(`^draft (\d+) for address "([^"]*)" of account "([^"]*) was moved to trash$`, s.drafAtIndexWasMovedToTrashForAddressOfAccount)
|
ctx.Step(`^draft (\d+) for address "([^"]*)" of account "([^"]*)" was moved to trash$`, s.drafAtIndexWasMovedToTrashForAddressOfAccount)
|
||||||
|
|
||||||
// === REPORTER ===
|
// === REPORTER ===
|
||||||
ctx.Step(`^test skips reporter checks$`, s.skipReporterChecks)
|
ctx.Step(`^test skips reporter checks$`, s.skipReporterChecks)
|
||||||
@ -132,15 +135,22 @@ func TestFeatures(testingT *testing.T) {
|
|||||||
ctx.Step(`^bridge stops$`, s.bridgeStops)
|
ctx.Step(`^bridge stops$`, s.bridgeStops)
|
||||||
ctx.Step(`^bridge is version "([^"]*)" and the latest available version is "([^"]*)" reachable from "([^"]*)"$`, s.bridgeVersionIsAndTheLatestAvailableVersionIsReachableFrom)
|
ctx.Step(`^bridge is version "([^"]*)" and the latest available version is "([^"]*)" reachable from "([^"]*)"$`, s.bridgeVersionIsAndTheLatestAvailableVersionIsReachableFrom)
|
||||||
ctx.Step(`^the user has disabled automatic updates$`, s.theUserHasDisabledAutomaticUpdates)
|
ctx.Step(`^the user has disabled automatic updates$`, s.theUserHasDisabledAutomaticUpdates)
|
||||||
|
ctx.Step(`^the user has disabled automatic start`, s.theUserHasDisabledAutomaticStart)
|
||||||
|
ctx.Step(`^the user has enabled alternative routing`, s.theUserHasEnabledAlternativeRouting)
|
||||||
|
ctx.Step(`^the user set IMAP mode to SSL`, s.theUserSetIMAPModeToSSL)
|
||||||
|
ctx.Step(`^the user set SMTP mode to SSL`, s.theUserSetSMTPModeToSSL)
|
||||||
ctx.Step(`^the user changes the IMAP port to (\d+)$`, s.theUserChangesTheIMAPPortTo)
|
ctx.Step(`^the user changes the IMAP port to (\d+)$`, s.theUserChangesTheIMAPPortTo)
|
||||||
ctx.Step(`^the user changes the SMTP port to (\d+)$`, s.theUserChangesTheSMTPPortTo)
|
ctx.Step(`^the user changes the SMTP port to (\d+)$`, s.theUserChangesTheSMTPPortTo)
|
||||||
ctx.Step(`^the user sets the address mode of user "([^"]*)" to "([^"]*)"$`, s.theUserSetsTheAddressModeOfUserTo)
|
ctx.Step(`^the user sets the address mode of user "([^"]*)" to "([^"]*)"$`, s.theUserSetsTheAddressModeOfUserTo)
|
||||||
|
ctx.Step(`^the user changes the default keychain application`, s.theUserChangesTheDefaultKeychainApplication)
|
||||||
ctx.Step(`^the user changes the gluon path$`, s.theUserChangesTheGluonPath)
|
ctx.Step(`^the user changes the gluon path$`, s.theUserChangesTheGluonPath)
|
||||||
ctx.Step(`^the user deletes the gluon files$`, s.theUserDeletesTheGluonFiles)
|
ctx.Step(`^the user deletes the gluon files$`, s.theUserDeletesTheGluonFiles)
|
||||||
ctx.Step(`^the user deletes the gluon cache$`, s.theUserDeletesTheGluonCache)
|
ctx.Step(`^the user deletes the gluon cache$`, s.theUserDeletesTheGluonCache)
|
||||||
ctx.Step(`^the user reports a bug$`, s.theUserReportsABug)
|
ctx.Step(`^the user reports a bug$`, s.theUserReportsABug)
|
||||||
ctx.Step(`^the user hides All Mail$`, s.theUserHidesAllMail)
|
ctx.Step(`^the user hides All Mail$`, s.theUserHidesAllMail)
|
||||||
ctx.Step(`^the user shows All Mail$`, s.theUserShowsAllMail)
|
ctx.Step(`^the user shows All Mail$`, s.theUserShowsAllMail)
|
||||||
|
ctx.Step(`^the user disables telemetry in bridge settings$`, s.theUserDisablesTelemetryInBridgeSettings)
|
||||||
|
ctx.Step(`^the user enables telemetry in bridge settings$`, s.theUserEnablesTelemetryInBridgeSettings)
|
||||||
ctx.Step(`^bridge sends a connection up event$`, s.bridgeSendsAConnectionUpEvent)
|
ctx.Step(`^bridge sends a connection up event$`, s.bridgeSendsAConnectionUpEvent)
|
||||||
ctx.Step(`^bridge sends a connection down event$`, s.bridgeSendsAConnectionDownEvent)
|
ctx.Step(`^bridge sends a connection down event$`, s.bridgeSendsAConnectionDownEvent)
|
||||||
ctx.Step(`^bridge sends a deauth event for user "([^"]*)"$`, s.bridgeSendsADeauthEventForUser)
|
ctx.Step(`^bridge sends a deauth event for user "([^"]*)"$`, s.bridgeSendsADeauthEventForUser)
|
||||||
@ -153,6 +163,8 @@ func TestFeatures(testingT *testing.T) {
|
|||||||
ctx.Step(`^bridge sends an update not available event$`, s.bridgeSendsAnUpdateNotAvailableEvent)
|
ctx.Step(`^bridge sends an update not available event$`, s.bridgeSendsAnUpdateNotAvailableEvent)
|
||||||
ctx.Step(`^bridge sends a forced update event$`, s.bridgeSendsAForcedUpdateEvent)
|
ctx.Step(`^bridge sends a forced update event$`, s.bridgeSendsAForcedUpdateEvent)
|
||||||
ctx.Step(`^bridge reports a message with "([^"]*)"$`, s.bridgeReportsMessage)
|
ctx.Step(`^bridge reports a message with "([^"]*)"$`, s.bridgeReportsMessage)
|
||||||
|
ctx.Step(`^bridge telemetry feature is enabled$`, s.bridgeTelemetryFeatureEnabled)
|
||||||
|
ctx.Step(`^bridge telemetry feature is disabled$`, s.bridgeTelemetryFeatureDisabled)
|
||||||
|
|
||||||
// ==== FRONTEND ====
|
// ==== FRONTEND ====
|
||||||
ctx.Step(`^frontend sees that bridge is version "([^"]*)"$`, s.frontendSeesThatBridgeIsVersion)
|
ctx.Step(`^frontend sees that bridge is version "([^"]*)"$`, s.frontendSeesThatBridgeIsVersion)
|
||||||
@ -166,6 +178,7 @@ func TestFeatures(testingT *testing.T) {
|
|||||||
ctx.Step(`^user "([^"]*)" is listed but not connected$`, s.userIsListedButNotConnected)
|
ctx.Step(`^user "([^"]*)" is listed but not connected$`, s.userIsListedButNotConnected)
|
||||||
ctx.Step(`^user "([^"]*)" is not listed$`, s.userIsNotListed)
|
ctx.Step(`^user "([^"]*)" is not listed$`, s.userIsNotListed)
|
||||||
ctx.Step(`^user "([^"]*)" finishes syncing$`, s.userFinishesSyncing)
|
ctx.Step(`^user "([^"]*)" finishes syncing$`, s.userFinishesSyncing)
|
||||||
|
ctx.Step(`^user "([^"]*)" has telemetry set to (\d+)$`, s.userHasTelemetrySetTo)
|
||||||
|
|
||||||
// ==== IMAP ====
|
// ==== IMAP ====
|
||||||
ctx.Step(`^user "([^"]*)" connects IMAP client "([^"]*)"$`, s.userConnectsIMAPClient)
|
ctx.Step(`^user "([^"]*)" connects IMAP client "([^"]*)"$`, s.userConnectsIMAPClient)
|
||||||
@ -206,6 +219,7 @@ func TestFeatures(testingT *testing.T) {
|
|||||||
ctx.Step(`^IMAP client "([^"]*)" appends "([^"]*)" to "([^"]*)"$`, s.imapClientAppendsToMailbox)
|
ctx.Step(`^IMAP client "([^"]*)" appends "([^"]*)" to "([^"]*)"$`, s.imapClientAppendsToMailbox)
|
||||||
ctx.Step(`^IMAP clients "([^"]*)" and "([^"]*)" move message with subject "([^"]*)" of "([^"]*)" to "([^"]*)" by ([^"]*) ([^"]*) ([^"]*)`, s.imapClientsMoveMessageWithSubjectUserFromToByOrderedOperations)
|
ctx.Step(`^IMAP clients "([^"]*)" and "([^"]*)" move message with subject "([^"]*)" of "([^"]*)" to "([^"]*)" by ([^"]*) ([^"]*) ([^"]*)`, s.imapClientsMoveMessageWithSubjectUserFromToByOrderedOperations)
|
||||||
ctx.Step(`^IMAP client "([^"]*)" sees header "([^"]*)" in message with subject "([^"]*)" in "([^"]*)"$`, s.imapClientSeesHeaderInMessageWithSubject)
|
ctx.Step(`^IMAP client "([^"]*)" sees header "([^"]*)" in message with subject "([^"]*)" in "([^"]*)"$`, s.imapClientSeesHeaderInMessageWithSubject)
|
||||||
|
ctx.Step(`^IMAP client "([^"]*)" does not see header "([^"]*)" in message with subject "([^"]*)" in "([^"]*)"$`, s.imapClientDoesNotSeeHeaderInMessageWithSubject)
|
||||||
|
|
||||||
// ==== SMTP ====
|
// ==== SMTP ====
|
||||||
ctx.Step(`^user "([^"]*)" connects SMTP client "([^"]*)"$`, s.userConnectsSMTPClient)
|
ctx.Step(`^user "([^"]*)" connects SMTP client "([^"]*)"$`, s.userConnectsSMTPClient)
|
||||||
@ -222,6 +236,12 @@ func TestFeatures(testingT *testing.T) {
|
|||||||
ctx.Step(`^SMTP client "([^"]*)" sends RSET$`, s.smtpClientSendsReset)
|
ctx.Step(`^SMTP client "([^"]*)" sends RSET$`, s.smtpClientSendsReset)
|
||||||
ctx.Step(`^SMTP client "([^"]*)" sends the following message from "([^"]*)" to "([^"]*)":$`, s.smtpClientSendsTheFollowingMessageFromTo)
|
ctx.Step(`^SMTP client "([^"]*)" sends the following message from "([^"]*)" to "([^"]*)":$`, s.smtpClientSendsTheFollowingMessageFromTo)
|
||||||
ctx.Step(`^SMTP client "([^"]*)" logs out$`, s.smtpClientLogsOut)
|
ctx.Step(`^SMTP client "([^"]*)" logs out$`, s.smtpClientLogsOut)
|
||||||
|
|
||||||
|
// ==== TELEMETRY ====
|
||||||
|
ctx.Step(`^bridge eventually sends the following heartbeat:$`, s.bridgeEventuallySendsTheFollowingHeartbeat)
|
||||||
|
ctx.Step(`^bridge needs to send heartbeat`, s.bridgeNeedsToSendHeartbeat)
|
||||||
|
ctx.Step(`^bridge do not need to send heartbeat`, s.bridgeDoNotNeedToSendHeartbeat)
|
||||||
|
ctx.Step(`^heartbeat is not whitelisted`, s.heartbeatIsNotwhitelisted)
|
||||||
},
|
},
|
||||||
Options: &godog.Options{
|
Options: &godog.Options{
|
||||||
Format: "pretty",
|
Format: "pretty",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user