Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 01043e033e | |||
| 94b44b383a | |||
| 4b95ef4d82 | |||
| 951c7c27fb | |||
| e7423a9519 | |||
| b7ef6e1486 | |||
| 0d03f84711 | |||
| 949666724d | |||
| bbe19bf960 | |||
| bfe25e3a46 | |||
| 236c958703 | |||
| e6b312b437 | |||
| 45d2e9ea63 | |||
| 86e8a566c7 |
@ -18,10 +18,6 @@
|
|||||||
---
|
---
|
||||||
image: gitlab.protontech.ch:4567/go/bridge-internal:test-go1.20
|
image: gitlab.protontech.ch:4567/go/bridge-internal:test-go1.20
|
||||||
|
|
||||||
default:
|
|
||||||
tags:
|
|
||||||
- shared-small
|
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
GOPRIVATE: gitlab.protontech.ch
|
GOPRIVATE: gitlab.protontech.ch
|
||||||
GOMAXPROCS: $(( ${CI_TAG_CPU} / 2 ))
|
GOMAXPROCS: $(( ${CI_TAG_CPU} / 2 ))
|
||||||
@ -75,7 +71,7 @@ stages:
|
|||||||
- export GO111MODULE=on
|
- export GO111MODULE=on
|
||||||
- export PATH="${GOPATH}/bin:${PATH}"
|
- export PATH="${GOPATH}/bin:${PATH}"
|
||||||
- export MSYSTEM=
|
- export MSYSTEM=
|
||||||
- export QT6DIR=/c/grrrQt/6.4.3/msvc2019_64
|
- export QT6DIR=/c/grrrQt/6.3.2/msvc2019_64
|
||||||
- export PATH=$PATH:${QT6DIR}/bin
|
- export PATH=$PATH:${QT6DIR}/bin
|
||||||
- export PATH="/c/Program Files/Microsoft Visual Studio/2022/Community/Common7/IDE/CommonExtensions/Microsoft/CMake/CMake/bin:$PATH"
|
- export PATH="/c/Program Files/Microsoft Visual Studio/2022/Community/Common7/IDE/CommonExtensions/Microsoft/CMake/CMake/bin:$PATH"
|
||||||
- $(git config --global -l | grep -o 'url.*gitlab.protontech.ch.*insteadof' | xargs -L 1 git config --global --unset &> /dev/null) || echo "nothing to remove"
|
- $(git config --global -l | grep -o 'url.*gitlab.protontech.ch.*insteadof' | xargs -L 1 git config --global --unset &> /dev/null) || echo "nothing to remove"
|
||||||
@ -97,7 +93,7 @@ stages:
|
|||||||
- export PATH="${GOROOT}/bin:$PATH"
|
- export PATH="${GOROOT}/bin:$PATH"
|
||||||
- export GOPATH=~/go1.20
|
- export GOPATH=~/go1.20
|
||||||
- export PATH="${GOPATH}/bin:$PATH"
|
- export PATH="${GOPATH}/bin:$PATH"
|
||||||
- export QT6DIR=/opt/Qt/6.4.3/macos
|
- export QT6DIR=/opt/Qt/6.3.2/macos
|
||||||
- export PATH="${QT6DIR}/bin:$PATH"
|
- export PATH="${QT6DIR}/bin:$PATH"
|
||||||
- uname -a
|
- uname -a
|
||||||
cache: {}
|
cache: {}
|
||||||
@ -105,7 +101,7 @@ stages:
|
|||||||
- macos-m1-bridge
|
- macos-m1-bridge
|
||||||
|
|
||||||
.env-linux-build:
|
.env-linux-build:
|
||||||
image: gitlab.protontech.ch:4567/go/bridge-internal:build-go1.20-qt6.4.3
|
image: gitlab.protontech.ch:4567/go/bridge-internal:build-go1.20-qt6.3.2
|
||||||
variables:
|
variables:
|
||||||
VCPKG_DEFAULT_BINARY_CACHE: ${CI_PROJECT_DIR}/.cache
|
VCPKG_DEFAULT_BINARY_CACHE: ${CI_PROJECT_DIR}/.cache
|
||||||
cache:
|
cache:
|
||||||
@ -122,7 +118,7 @@ stages:
|
|||||||
- $(git config --global -l | grep -o 'url.*gitlab.protontech.ch.*insteadof' | xargs -L 1 git config --global --unset &> /dev/null) || echo "nothing to remove"
|
- $(git config --global -l | grep -o 'url.*gitlab.protontech.ch.*insteadof' | xargs -L 1 git config --global --unset &> /dev/null) || echo "nothing to remove"
|
||||||
- git config --global url.https://gitlab-ci-token:${CI_JOB_TOKEN}@${CI_SERVER_HOST}.insteadOf https://${CI_SERVER_HOST}
|
- git config --global url.https://gitlab-ci-token:${CI_JOB_TOKEN}@${CI_SERVER_HOST}.insteadOf https://${CI_SERVER_HOST}
|
||||||
tags:
|
tags:
|
||||||
- shared-large
|
- large
|
||||||
|
|
||||||
# Stage: TEST
|
# Stage: TEST
|
||||||
|
|
||||||
@ -133,7 +129,7 @@ lint:
|
|||||||
script:
|
script:
|
||||||
- make lint
|
- make lint
|
||||||
tags:
|
tags:
|
||||||
- shared-medium
|
- medium
|
||||||
|
|
||||||
bug-report-preview:
|
bug-report-preview:
|
||||||
stage: test
|
stage: test
|
||||||
@ -142,7 +138,7 @@ bug-report-preview:
|
|||||||
script:
|
script:
|
||||||
- make lint-bug-report-preview
|
- make lint-bug-report-preview
|
||||||
tags:
|
tags:
|
||||||
- shared-medium
|
- medium
|
||||||
|
|
||||||
.script-test:
|
.script-test:
|
||||||
stage: test
|
stage: test
|
||||||
@ -158,7 +154,7 @@ test-linux:
|
|||||||
extends:
|
extends:
|
||||||
- .script-test
|
- .script-test
|
||||||
tags:
|
tags:
|
||||||
- shared-large
|
- large
|
||||||
|
|
||||||
fuzz-linux:
|
fuzz-linux:
|
||||||
stage: test
|
stage: test
|
||||||
@ -167,7 +163,7 @@ fuzz-linux:
|
|||||||
script:
|
script:
|
||||||
- make fuzz
|
- make fuzz
|
||||||
tags:
|
tags:
|
||||||
- shared-large
|
- large
|
||||||
|
|
||||||
test-linux-race:
|
test-linux-race:
|
||||||
extends:
|
extends:
|
||||||
@ -222,7 +218,7 @@ test-coverage:
|
|||||||
- test-integration
|
- test-integration
|
||||||
- test-integration-nightly
|
- test-integration-nightly
|
||||||
tags:
|
tags:
|
||||||
- shared-small
|
- small
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- coverage*
|
- coverage*
|
||||||
@ -286,18 +282,4 @@ build-windows-qa:
|
|||||||
variables:
|
variables:
|
||||||
BUILD_TAGS: "build_qa"
|
BUILD_TAGS: "build_qa"
|
||||||
|
|
||||||
trigger-qa-installer:
|
|
||||||
stage: build
|
|
||||||
needs: ["lint"]
|
|
||||||
extends:
|
|
||||||
- .rules-branch-and-MR-manual
|
|
||||||
variables:
|
|
||||||
APP: bridge
|
|
||||||
WORKFLOW: build-all
|
|
||||||
SRC_TAG: $CI_COMMIT_BRANCH
|
|
||||||
SRC_HASH: $CI_COMMIT_SHA
|
|
||||||
trigger:
|
|
||||||
project: "jcuth/bridge-release"
|
|
||||||
branch: master
|
|
||||||
|
|
||||||
# TODO: PUT BACK ALL THE JOBS! JUST DID THIS FOR NOW TO GET CI WORKING AGAIN...
|
# TODO: PUT BACK ALL THE JOBS! JUST DID THIS FOR NOW TO GET CI WORKING AGAIN...
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
* Windres (Windows)
|
* Windres (Windows)
|
||||||
* libglvnd and libsecret development files (Linux)
|
* libglvnd and libsecret development files (Linux)
|
||||||
* pkg-config (Linux)
|
* pkg-config (Linux)
|
||||||
* cmake, ninja-build and Qt 6.4.3 are required to build the graphical user interface. On 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.
|
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
|
||||||
@ -19,7 +19,7 @@ Otherwise, the sending of crash reports will be disabled.
|
|||||||
|
|
||||||
## Build
|
## Build
|
||||||
In order to build Bridge app with Qt interface we are using
|
In order to build Bridge app with Qt interface we are using
|
||||||
[Qt 6.4.3](https://doc.qt.io/qt-6/gettingstarted.html).
|
[Qt 6.3](https://doc.qt.io/qt-6/gettingstarted.html).
|
||||||
|
|
||||||
Please note that qmake path must be in your `PATH` to ensure Qt to be found.
|
Please note that qmake path must be in your `PATH` to ensure Qt to be found.
|
||||||
Also, before you start build **on Windows**, please unset the `MSYSTEM` variable
|
Also, before you start build **on Windows**, please unset the `MSYSTEM` variable
|
||||||
|
|||||||
@ -40,7 +40,6 @@ Proton Mail Bridge includes the following 3rd party software:
|
|||||||
* [go-message](https://github.com/emersion/go-message) available under [license](https://github.com/emersion/go-message/blob/master/LICENSE)
|
* [go-message](https://github.com/emersion/go-message) available under [license](https://github.com/emersion/go-message/blob/master/LICENSE)
|
||||||
* [go-sasl](https://github.com/emersion/go-sasl) available under [license](https://github.com/emersion/go-sasl/blob/master/LICENSE)
|
* [go-sasl](https://github.com/emersion/go-sasl) available under [license](https://github.com/emersion/go-sasl/blob/master/LICENSE)
|
||||||
* [go-smtp](https://github.com/emersion/go-smtp) available under [license](https://github.com/emersion/go-smtp/blob/master/LICENSE)
|
* [go-smtp](https://github.com/emersion/go-smtp) available under [license](https://github.com/emersion/go-smtp/blob/master/LICENSE)
|
||||||
* [go-vcard](https://github.com/emersion/go-vcard) available under [license](https://github.com/emersion/go-vcard/blob/master/LICENSE)
|
|
||||||
* [color](https://github.com/fatih/color) available under [license](https://github.com/fatih/color/blob/master/LICENSE)
|
* [color](https://github.com/fatih/color) available under [license](https://github.com/fatih/color/blob/master/LICENSE)
|
||||||
* [sentry-go](https://github.com/getsentry/sentry-go) available under [license](https://github.com/getsentry/sentry-go/blob/master/LICENSE)
|
* [sentry-go](https://github.com/getsentry/sentry-go) available under [license](https://github.com/getsentry/sentry-go/blob/master/LICENSE)
|
||||||
* [resty](https://github.com/go-resty/resty/v2) available under [license](https://github.com/go-resty/resty/v2/blob/master/LICENSE)
|
* [resty](https://github.com/go-resty/resty/v2) available under [license](https://github.com/go-resty/resty/v2/blob/master/LICENSE)
|
||||||
@ -84,6 +83,7 @@ Proton Mail Bridge includes the following 3rd party software:
|
|||||||
* [go-spew](https://github.com/davecgh/go-spew) available under [license](https://github.com/davecgh/go-spew/blob/master/LICENSE)
|
* [go-spew](https://github.com/davecgh/go-spew) available under [license](https://github.com/davecgh/go-spew/blob/master/LICENSE)
|
||||||
* [go-windows](https://github.com/elastic/go-windows) available under [license](https://github.com/elastic/go-windows/blob/master/LICENSE)
|
* [go-windows](https://github.com/elastic/go-windows) available under [license](https://github.com/elastic/go-windows/blob/master/LICENSE)
|
||||||
* [go-textwrapper](https://github.com/emersion/go-textwrapper) available under [license](https://github.com/emersion/go-textwrapper/blob/master/LICENSE)
|
* [go-textwrapper](https://github.com/emersion/go-textwrapper) available under [license](https://github.com/emersion/go-textwrapper/blob/master/LICENSE)
|
||||||
|
* [go-vcard](https://github.com/emersion/go-vcard) available under [license](https://github.com/emersion/go-vcard/blob/master/LICENSE)
|
||||||
* [fgprof](https://github.com/felixge/fgprof) available under [license](https://github.com/felixge/fgprof/blob/master/LICENSE)
|
* [fgprof](https://github.com/felixge/fgprof) available under [license](https://github.com/felixge/fgprof/blob/master/LICENSE)
|
||||||
* [go-shlex](https://github.com/flynn-archive/go-shlex) available under [license](https://github.com/flynn-archive/go-shlex/blob/master/LICENSE)
|
* [go-shlex](https://github.com/flynn-archive/go-shlex) available under [license](https://github.com/flynn-archive/go-shlex/blob/master/LICENSE)
|
||||||
* [mimetype](https://github.com/gabriel-vasile/mimetype) available under [license](https://github.com/gabriel-vasile/mimetype/blob/master/LICENSE)
|
* [mimetype](https://github.com/gabriel-vasile/mimetype) available under [license](https://github.com/gabriel-vasile/mimetype/blob/master/LICENSE)
|
||||||
@ -123,7 +123,6 @@ Proton Mail Bridge includes the following 3rd party software:
|
|||||||
* [codec](https://github.com/ugorji/go/codec) available under [license](https://github.com/ugorji/go/codec/blob/master/LICENSE)
|
* [codec](https://github.com/ugorji/go/codec) available under [license](https://github.com/ugorji/go/codec/blob/master/LICENSE)
|
||||||
* [tagparser](https://github.com/vmihailenco/tagparser/v2) available under [license](https://github.com/vmihailenco/tagparser/v2/blob/master/LICENSE)
|
* [tagparser](https://github.com/vmihailenco/tagparser/v2) available under [license](https://github.com/vmihailenco/tagparser/v2/blob/master/LICENSE)
|
||||||
* [smetrics](https://github.com/xrash/smetrics) available under [license](https://github.com/xrash/smetrics/blob/master/LICENSE)
|
* [smetrics](https://github.com/xrash/smetrics) available under [license](https://github.com/xrash/smetrics/blob/master/LICENSE)
|
||||||
* [go-ordered-json](https://gitlab.com/c0b/go-ordered-json)
|
|
||||||
* [arch](https://golang.org/x/arch) available under [license](https://cs.opensource.google/go/x/arch/+/master:LICENSE)
|
* [arch](https://golang.org/x/arch) available under [license](https://cs.opensource.google/go/x/arch/+/master:LICENSE)
|
||||||
* [crypto](https://golang.org/x/crypto) available under [license](https://cs.opensource.google/go/x/crypto/+/master:LICENSE)
|
* [crypto](https://golang.org/x/crypto) available under [license](https://cs.opensource.google/go/x/crypto/+/master:LICENSE)
|
||||||
* [mod](https://golang.org/x/mod) available under [license](https://cs.opensource.google/go/x/mod/+/master:LICENSE)
|
* [mod](https://golang.org/x/mod) available under [license](https://cs.opensource.google/go/x/mod/+/master:LICENSE)
|
||||||
@ -133,6 +132,5 @@ Proton Mail Bridge includes the following 3rd party software:
|
|||||||
* [yaml](https://gopkg.in/yaml.v3) available under [license](https://github.com/go-yaml/yaml/blob/v3.0.1/LICENSE)
|
* [yaml](https://gopkg.in/yaml.v3) available under [license](https://github.com/go-yaml/yaml/blob/v3.0.1/LICENSE)
|
||||||
* [docker-credential-helpers](https://github.com/ProtonMail/docker-credential-helpers) available under [license](https://github.com/ProtonMail/docker-credential-helpers/blob/master/LICENSE)
|
* [docker-credential-helpers](https://github.com/ProtonMail/docker-credential-helpers) available under [license](https://github.com/ProtonMail/docker-credential-helpers/blob/master/LICENSE)
|
||||||
* [go-message](https://github.com/ProtonMail/go-message) available under [license](https://github.com/ProtonMail/go-message/blob/master/LICENSE)
|
* [go-message](https://github.com/ProtonMail/go-message) available under [license](https://github.com/ProtonMail/go-message/blob/master/LICENSE)
|
||||||
* [resty](https://github.com/LBeernaertProton/resty/v2) available under [license](https://github.com/LBeernaertProton/resty/v2/blob/master/LICENSE)
|
|
||||||
* [go-keychain](https://github.com/cuthix/go-keychain) available under [license](https://github.com/cuthix/go-keychain/blob/master/LICENSE)
|
* [go-keychain](https://github.com/cuthix/go-keychain) available under [license](https://github.com/cuthix/go-keychain/blob/master/LICENSE)
|
||||||
<!-- END AUTOGEN -->
|
<!-- END AUTOGEN -->
|
||||||
|
|||||||
87
Changelog.md
@ -3,93 +3,6 @@
|
|||||||
Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
||||||
|
|
||||||
|
|
||||||
## Wakato Bridge 3.7.0
|
|
||||||
|
|
||||||
### Added
|
|
||||||
* Test(GODT-1224): Add testing around package creation.
|
|
||||||
* Add debug_assemble binary.
|
|
||||||
* Test(GODT-2723): Add importing a message with remote content.
|
|
||||||
* Test(GODT-2737): Sending HTML messages to internal.
|
|
||||||
* Test(GODT-3036): Keep inline attachment order on GPA Fake Server.
|
|
||||||
* GODT-3015: Add simple algorithm to deal with multiple attachment for bug report.
|
|
||||||
* Test: make message structure check more verbose.
|
|
||||||
* Test: Add test around account settings.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
* GODT-3097: Warn about PGPInline encryption scheme which will be deprecated.
|
|
||||||
* Test: Support multiple users when waiting for sync event.
|
|
||||||
* Test: Update fake server with defautl draft content-type and test it.
|
|
||||||
* Test: be less aggressive while checking for message structure.
|
|
||||||
* GODT-2996: Set password fields to hidden when resetting the login form.
|
|
||||||
* GODT-2990: Change runner tags.
|
|
||||||
* GODT-2835: Bump GPA adding support for AsyncAttachments for BugReport +...
|
|
||||||
* GODT-2940: Allow 3 attempts for mailbox password.
|
|
||||||
* GODT-3095: Update GOpenPGP.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
* GODT-3106: Broken import route.
|
|
||||||
* GODT-3041: Fix Invalid Or Missing message signature during send.
|
|
||||||
* GODT-3087: Exclude attachment content-disposition part when determining...
|
|
||||||
* GODT-2887: Inline images with Apple Mail.
|
|
||||||
* GODT-3100: Fix issue where a fatal error that bubble up to cli.Run() is not written in the log file.
|
|
||||||
* GODT-3094: Clean up old update files on bridge startup.
|
|
||||||
* GODT-3012: Fix multipart request retries.
|
|
||||||
* GODT-2935: Do not allow parentID into drafts.
|
|
||||||
* GODT-2935: Correct error message when draft fails to create.
|
|
||||||
* GODT-2970: Correctly handle rename of Inbox.
|
|
||||||
* GODT-2969: Prevent duration corruption for config status event.
|
|
||||||
* Fixed type in QA installer CI job name.
|
|
||||||
* GODT-3019: Fix title of main window when no account is connected.
|
|
||||||
* GODT-3013: IMAP service getting "stuck".
|
|
||||||
* GODT-2966: Allow permissive parsing of MediaType parameters for import.
|
|
||||||
* GODT-2966: Add more test regarding quoted/unquoted filename in attachment.
|
|
||||||
* GODT-2490: Fix sync progress not being reset when toggling split mode.
|
|
||||||
* GODT-2515: Customized notification of unavailable keychain on macOS.
|
|
||||||
|
|
||||||
|
|
||||||
## Vasco da Gama Bridge 3.6.1
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
* GODT-3033: Unable to receive new mail.
|
|
||||||
|
|
||||||
|
|
||||||
## Umshiang Bridge 3.5.4
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
* GODT-3033: Unable to receive new mail.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Vasco da Gama Bridge 3.6.0
|
|
||||||
|
|
||||||
### Added
|
|
||||||
* GODT-2762: Setup wizard.
|
|
||||||
* GODT-2772: Setup wizard content.
|
|
||||||
* GODT-2769: Setup Wizard architecture.
|
|
||||||
* GODT-2767: Setup Wizard foundations.
|
|
||||||
* GODT-2725: Implement receive message step with expected structure exposed.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
* GODT-2960: Added content in empty view when there is no account.
|
|
||||||
* GODT-2771: Cert related tools for macOS.
|
|
||||||
* GODT-2770: Proof of concept for web view as a tool window and overlay (not used).
|
|
||||||
* GODT-2916: Split Decryption from Message Building.
|
|
||||||
* GODT-2597: Implement contact specific settings in integration tests.
|
|
||||||
* GODT-2664: Trigger QA installer.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
* GODT-2992: Fix link in 'no account view' in main window after 2FA or TOTP are cancelled.
|
|
||||||
* GODT-2989: Allow to send bug report when no account connected.
|
|
||||||
* GODT-2988: Fix setup wizard KB links.
|
|
||||||
* GODT-2968: Use proper base64 encoded string even for bad password test.
|
|
||||||
* GODT-2965: Fix multipart/mixed testdata + structure parsing steps related to this.
|
|
||||||
* GODT-2932: Fix syncing not being reported in GUI.
|
|
||||||
* GODT-2967: Tray menu entries close the setup wizard when needed.
|
|
||||||
* GODT-2212: Preserver Header order in message building.
|
|
||||||
* Fixed missing GoOs gRPC call in bridge-gui-tester.
|
|
||||||
* GODT-2929: Message dedup with different text transfer encoding.
|
|
||||||
|
|
||||||
|
|
||||||
## Umshiang Bridge 3.5.3
|
## Umshiang Bridge 3.5.3
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
2
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.7.0+git
|
BRIDGE_APP_VERSION?=3.5.3+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
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import (
|
|||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/app"
|
"github.com/ProtonMail/proton-bridge/v3/internal/app"
|
||||||
"github.com/bradenaw/juniper/xslices"
|
"github.com/bradenaw/juniper/xslices"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -43,5 +44,7 @@ import (
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
_ = app.New().Run(xslices.Filter(os.Args, func(arg string) bool { return !strings.Contains(arg, "-psn_") }))
|
if err := app.New().Run(xslices.Filter(os.Args, func(arg string) bool { return !strings.Contains(arg, "-psn_") })); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
go.mod
@ -5,10 +5,10 @@ go 1.20
|
|||||||
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.17.1-0.20231025125916-5c7941465df8
|
github.com/ProtonMail/gluon v0.17.1-0.20231009084701-3af0474b0b3c
|
||||||
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.20231106093533-5f248dfc820d
|
github.com/ProtonMail/go-proton-api v0.4.1-0.20231011062329-f3b976b7dbca
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.7.4-proton
|
github.com/ProtonMail/gopenpgp/v2 v2.7.3-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
|
||||||
@ -22,7 +22,6 @@ require (
|
|||||||
github.com/emersion/go-message v0.16.0
|
github.com/emersion/go-message v0.16.0
|
||||||
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead
|
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead
|
||||||
github.com/emersion/go-smtp v0.15.1-0.20221021114529-49b17434419d
|
github.com/emersion/go-smtp v0.15.1-0.20221021114529-49b17434419d
|
||||||
github.com/emersion/go-vcard v0.0.0-20230331202150-f3d26859ccd3
|
|
||||||
github.com/fatih/color v1.13.0
|
github.com/fatih/color v1.13.0
|
||||||
github.com/getsentry/sentry-go v0.15.0
|
github.com/getsentry/sentry-go v0.15.0
|
||||||
github.com/go-resty/resty/v2 v2.7.0
|
github.com/go-resty/resty/v2 v2.7.0
|
||||||
@ -43,10 +42,10 @@ 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-20230522175609-2e198f4a06a1
|
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
|
||||||
golang.org/x/net v0.17.0
|
golang.org/x/net v0.10.0
|
||||||
golang.org/x/sys v0.13.0
|
golang.org/x/sys v0.8.0
|
||||||
golang.org/x/text v0.13.0
|
golang.org/x/text v0.9.0
|
||||||
google.golang.org/grpc v1.56.3
|
google.golang.org/grpc v1.53.0
|
||||||
google.golang.org/protobuf v1.30.0
|
google.golang.org/protobuf v1.30.0
|
||||||
howett.net/plist v1.0.0
|
howett.net/plist v1.0.0
|
||||||
)
|
)
|
||||||
@ -69,6 +68,7 @@ require (
|
|||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/elastic/go-windows v1.0.1 // indirect
|
github.com/elastic/go-windows v1.0.1 // indirect
|
||||||
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 // indirect
|
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 // indirect
|
||||||
|
github.com/emersion/go-vcard v0.0.0-20230331202150-f3d26859ccd3 // indirect
|
||||||
github.com/felixge/fgprof v0.9.3 // indirect
|
github.com/felixge/fgprof v0.9.3 // indirect
|
||||||
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
|
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
@ -79,7 +79,7 @@ require (
|
|||||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/gofrs/uuid v4.3.0+incompatible // indirect
|
github.com/gofrs/uuid v4.3.0+incompatible // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect
|
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||||
@ -108,19 +108,17 @@ require (
|
|||||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a // indirect
|
|
||||||
golang.org/x/arch v0.3.0 // indirect
|
golang.org/x/arch v0.3.0 // indirect
|
||||||
golang.org/x/crypto v0.14.0 // indirect
|
golang.org/x/crypto v0.9.0 // indirect
|
||||||
golang.org/x/mod v0.8.0 // indirect
|
golang.org/x/mod v0.8.0 // indirect
|
||||||
golang.org/x/sync v0.2.0 // indirect
|
golang.org/x/sync v0.2.0 // indirect
|
||||||
golang.org/x/tools v0.6.0 // indirect
|
golang.org/x/tools v0.6.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // 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
|
||||||
)
|
)
|
||||||
|
|
||||||
replace (
|
replace (
|
||||||
github.com/docker/docker-credential-helpers => github.com/ProtonMail/docker-credential-helpers v1.1.0
|
github.com/docker/docker-credential-helpers => github.com/ProtonMail/docker-credential-helpers v1.1.0
|
||||||
github.com/emersion/go-message => github.com/ProtonMail/go-message v0.13.1-0.20230526094639-b62c999c85b7
|
github.com/emersion/go-message => github.com/ProtonMail/go-message v0.13.1-0.20230526094639-b62c999c85b7
|
||||||
github.com/go-resty/resty/v2 => github.com/LBeernaertProton/resty/v2 v2.0.0-20231030122409-92db8bee3605
|
|
||||||
github.com/keybase/go-keychain => github.com/cuthix/go-keychain v0.0.0-20230517073537-fc1740a83768
|
github.com/keybase/go-keychain => github.com/cuthix/go-keychain v0.0.0-20230517073537-fc1740a83768
|
||||||
)
|
)
|
||||||
|
|||||||
48
go.sum
@ -15,8 +15,6 @@ github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557 h1:l6surSnJ3RP4qA
|
|||||||
github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557/go.mod h1:sTrmvD/TxuypdOERsDOS7SndZg0rzzcCi1b6wQMXUYM=
|
github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557/go.mod h1:sTrmvD/TxuypdOERsDOS7SndZg0rzzcCi1b6wQMXUYM=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/LBeernaertProton/resty/v2 v2.0.0-20231030122409-92db8bee3605 h1:54Fh3JS6s2Tjy6ZIRLtt1amZOqfYDcjErdye45z8fkQ=
|
|
||||||
github.com/LBeernaertProton/resty/v2 v2.0.0-20231030122409-92db8bee3605/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
|
|
||||||
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
|
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
|
||||||
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
@ -25,8 +23,8 @@ 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.17.1-0.20231025125916-5c7941465df8 h1:sG0o5pEoS2z2jNR9zK7Juq5Tr3X+GfHmQ8L99RPowaE=
|
github.com/ProtonMail/gluon v0.17.1-0.20231009084701-3af0474b0b3c h1:gUDu4pOswgbou0QczfreNiXQFrmvVlpSh8Q+vft/JvI=
|
||||||
github.com/ProtonMail/gluon v0.17.1-0.20231025125916-5c7941465df8/go.mod h1:Og5/Dz1MiGpCJn51XujZwxiLG7WzvvjE5PRpZBQmAHo=
|
github.com/ProtonMail/gluon v0.17.1-0.20231009084701-3af0474b0b3c/go.mod h1:Og5/Dz1MiGpCJn51XujZwxiLG7WzvvjE5PRpZBQmAHo=
|
||||||
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-20230321155629-9a39f2531310/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE=
|
github.com/ProtonMail/go-crypto v0.0.0-20230321155629-9a39f2531310/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE=
|
||||||
@ -36,12 +34,12 @@ github.com/ProtonMail/go-message v0.13.1-0.20230526094639-b62c999c85b7 h1:+j+Kd/
|
|||||||
github.com/ProtonMail/go-message v0.13.1-0.20230526094639-b62c999c85b7/go.mod h1:NBAn21zgCJ/52WLDyed18YvYFm5tEoeDauubFqLokM4=
|
github.com/ProtonMail/go-message v0.13.1-0.20230526094639-b62c999c85b7/go.mod h1:NBAn21zgCJ/52WLDyed18YvYFm5tEoeDauubFqLokM4=
|
||||||
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
|
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
|
||||||
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
|
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.20231106093533-5f248dfc820d h1:LI2kvxBisX19f7lyMh0H6NcAHHg/Y7/x/xZWtxVrXOc=
|
github.com/ProtonMail/go-proton-api v0.4.1-0.20231011062329-f3b976b7dbca h1:nO/xuvyEgWWLo2cBAqfxCHh7Ri0ofV3PXnTOfk0QcyI=
|
||||||
github.com/ProtonMail/go-proton-api v0.4.1-0.20231106093533-5f248dfc820d/go.mod h1:WEXJqj5DSc2YI77SgXdpMY0nk33Qy92Vu2r4tOEazA8=
|
github.com/ProtonMail/go-proton-api v0.4.1-0.20231011062329-f3b976b7dbca/go.mod h1:IGVXKy6NLHt4WeWiOnAFmSsXRpd6elkjDZMtr5vBLJ8=
|
||||||
github.com/ProtonMail/go-srp v0.0.7 h1:Sos3Qk+th4tQR64vsxGIxYpN3rdnG9Wf9K4ZloC1JrI=
|
github.com/ProtonMail/go-srp v0.0.7 h1:Sos3Qk+th4tQR64vsxGIxYpN3rdnG9Wf9K4ZloC1JrI=
|
||||||
github.com/ProtonMail/go-srp v0.0.7/go.mod h1:giCp+7qRnMIcCvI6V6U3S1lDDXDQYx2ewJ6F/9wdlJk=
|
github.com/ProtonMail/go-srp v0.0.7/go.mod h1:giCp+7qRnMIcCvI6V6U3S1lDDXDQYx2ewJ6F/9wdlJk=
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.7.4-proton h1:8tqHYM6IGsdEc6Vxf1TWiwpHNj8yIEQNACPhxsDagrk=
|
github.com/ProtonMail/gopenpgp/v2 v2.7.3-proton h1:wuAxBUU9qF2wyDVJprn/2xPDx000eol5gwlKbOUYY88=
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.7.4-proton/go.mod h1:omVkSsfPAhmptzPF/piMXb16wKIWUvVhZbVW7sJKh0A=
|
github.com/ProtonMail/gopenpgp/v2 v2.7.3-proton/go.mod h1:omVkSsfPAhmptzPF/piMXb16wKIWUvVhZbVW7sJKh0A=
|
||||||
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=
|
||||||
@ -157,6 +155,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
|||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
||||||
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
|
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
|
||||||
|
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
@ -178,8 +178,8 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
|||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
@ -399,8 +399,6 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRT
|
|||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a h1:DxppxFKRqJ8WD6oJ3+ZXKDY0iMONQDl5UTg2aTyHh8k=
|
|
||||||
gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a/go.mod h1:NREvu3a57BaK0R1+ztrEzHWiZAihohNLQ6trPxlIqZI=
|
|
||||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
@ -421,8 +419,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
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.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||||
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=
|
||||||
@ -465,15 +463,15 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
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-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.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
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/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||||
|
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
|
||||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
|
||||||
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=
|
||||||
@ -521,17 +519,14 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
|
||||||
golang.org/x/sys v0.13.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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
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/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
|
||||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
|
||||||
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=
|
||||||
@ -542,13 +537,10 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|
||||||
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/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
|
||||||
golang.org/x/time v0.3.0/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=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@ -594,13 +586,13 @@ google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98
|
|||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
|
google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 h1:muK+gVBJBfFb4SejshDBlN2/UgxCCOKH9Y34ljqEGOc=
|
||||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
|
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
|
||||||
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
|
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||||
|
|||||||
@ -204,7 +204,7 @@ func run(c *cli.Context) error {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// Restart the app if requested.
|
// Restart the app if requested.
|
||||||
err = withRestarter(exe, func(restarter *restarter.Restarter) error {
|
return withRestarter(exe, func(restarter *restarter.Restarter) error {
|
||||||
// Handle crashes with various actions.
|
// Handle crashes with various actions.
|
||||||
return withCrashHandler(restarter, reporter, func(crashHandler *crash.Handler, quitCh <-chan struct{}) error {
|
return withCrashHandler(restarter, reporter, func(crashHandler *crash.Handler, quitCh <-chan struct{}) error {
|
||||||
migrationErr := migrateOldVersions()
|
migrationErr := migrateOldVersions()
|
||||||
@ -276,9 +276,6 @@ func run(c *cli.Context) error {
|
|||||||
b.PushError(bridge.ErrVaultCorrupt)
|
b.PushError(bridge.ErrVaultCorrupt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove old updates files
|
|
||||||
b.RemoveOldUpdates()
|
|
||||||
|
|
||||||
// Start telemetry heartbeat process
|
// Start telemetry heartbeat process
|
||||||
b.StartHeartbeat(b)
|
b.StartHeartbeat(b)
|
||||||
|
|
||||||
@ -293,13 +290,6 @@ func run(c *cli.Context) error {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// if an error occurs, it must be logged now because we're about to close the log file.
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there's another instance already running, try to raise it and exit.
|
// If there's another instance already running, try to raise it and exit.
|
||||||
|
|||||||
@ -155,7 +155,7 @@ func newUpdater(locations *locations.Locations) (*updater.Updater, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return updater.NewUpdater(
|
return updater.NewUpdater(
|
||||||
versioner.New(updatesDir),
|
updater.NewInstaller(versioner.New(updatesDir)),
|
||||||
verifier,
|
verifier,
|
||||||
constants.UpdateName,
|
constants.UpdateName,
|
||||||
runtime.GOOS,
|
runtime.GOOS,
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/ProtonMail/gluon/async"
|
"github.com/ProtonMail/gluon/async"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v3/internal/certs"
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/constants"
|
"github.com/ProtonMail/proton-bridge/v3/internal/constants"
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/locations"
|
"github.com/ProtonMail/proton-bridge/v3/internal/locations"
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
|
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
|
||||||
@ -44,6 +45,23 @@ func WithVault(locations *locations.Locations, panicHandler async.PanicHandler,
|
|||||||
"corrupt": corrupt,
|
"corrupt": corrupt,
|
||||||
}).Debug("Vault created")
|
}).Debug("Vault created")
|
||||||
|
|
||||||
|
// Install the certificates if needed.
|
||||||
|
if installed := encVault.GetCertsInstalled(); !installed {
|
||||||
|
logrus.Debug("Installing certificates")
|
||||||
|
|
||||||
|
certPEM, _ := encVault.GetBridgeTLSCert()
|
||||||
|
|
||||||
|
if err := certs.NewInstaller().InstallCert(certPEM); err != nil {
|
||||||
|
return fmt.Errorf("failed to install certs: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := encVault.SetCertsInstalled(true); err != nil {
|
||||||
|
return fmt.Errorf("failed to set certs installed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debug("Certificates successfully installed")
|
||||||
|
}
|
||||||
|
|
||||||
// GODT-1950: Add teardown actions (e.g. to close the vault).
|
// GODT-1950: Add teardown actions (e.g. to close the vault).
|
||||||
|
|
||||||
return fn(encVault, insecure, corrupt)
|
return fn(encVault, insecure, corrupt)
|
||||||
|
|||||||
@ -487,15 +487,27 @@ func (bridge *Bridge) remWatcher(watcher *watcher.Watcher[events.Event]) {
|
|||||||
watcher.Close()
|
watcher.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) onStatusUp(_ context.Context) {
|
func (bridge *Bridge) onStatusUp(ctx context.Context) {
|
||||||
logrus.Info("Handling API status up")
|
logrus.Info("Handling API status up")
|
||||||
|
|
||||||
|
safe.RLock(func() {
|
||||||
|
for _, user := range bridge.users {
|
||||||
|
user.OnStatusUp(ctx)
|
||||||
|
}
|
||||||
|
}, bridge.usersLock)
|
||||||
|
|
||||||
bridge.goLoad()
|
bridge.goLoad()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) onStatusDown(ctx context.Context) {
|
func (bridge *Bridge) onStatusDown(ctx context.Context) {
|
||||||
logrus.Info("Handling API status down")
|
logrus.Info("Handling API status down")
|
||||||
|
|
||||||
|
safe.RLock(func() {
|
||||||
|
for _, user := range bridge.users {
|
||||||
|
user.OnStatusDown(ctx)
|
||||||
|
}
|
||||||
|
}, bridge.usersLock)
|
||||||
|
|
||||||
for backoff := time.Second; ; backoff = min(backoff*2, 30*time.Second) {
|
for backoff := time.Second; ; backoff = min(backoff*2, 30*time.Second) {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
|||||||
@ -19,7 +19,6 @@ package bridge
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/ProtonMail/go-proton-api"
|
"github.com/ProtonMail/go-proton-api"
|
||||||
@ -34,133 +33,63 @@ const (
|
|||||||
DefaultMaxSessionCountForBugReport = 10
|
DefaultMaxSessionCountForBugReport = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
type ReportBugReq struct {
|
func (bridge *Bridge) ReportBug(ctx context.Context, osType, osVersion, title, description, username, email, client string, attachLogs bool) error {
|
||||||
OSType string
|
var account string
|
||||||
OSVersion string
|
|
||||||
Title string
|
|
||||||
Description string
|
|
||||||
Username string
|
|
||||||
Email string
|
|
||||||
EmailClient string
|
|
||||||
IncludeLogs bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bridge *Bridge) ReportBug(ctx context.Context, report *ReportBugReq) error {
|
if info, err := bridge.QueryUserInfo(username); err == nil {
|
||||||
if info, err := bridge.QueryUserInfo(report.Username); err == nil {
|
account = info.Username
|
||||||
report.Username = info.Username
|
|
||||||
} else if userIDs := bridge.GetUserIDs(); len(userIDs) > 0 {
|
} else if userIDs := bridge.GetUserIDs(); len(userIDs) > 0 {
|
||||||
if err := bridge.vault.GetUser(userIDs[0], func(user *vault.User) {
|
if err := bridge.vault.GetUser(userIDs[0], func(user *vault.User) {
|
||||||
report.Username = user.Username()
|
account = user.Username()
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var attachments []proton.ReportBugAttachment
|
var attachment []proton.ReportBugAttachment
|
||||||
if report.IncludeLogs {
|
|
||||||
logs, err := bridge.CollectLogs()
|
if attachLogs {
|
||||||
|
logsPath, err := bridge.locator.ProvideLogsPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
attachments = append(attachments, logs)
|
|
||||||
|
buffer, err := logging.ZipLogsForBugReport(logsPath, DefaultMaxSessionCountForBugReport, DefaultMaxBugReportZipSize)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(buffer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
attachment = append(attachment, proton.ReportBugAttachment{
|
||||||
|
Name: "logs.zip",
|
||||||
|
Filename: "logs.zip",
|
||||||
|
MIMEType: "application/zip",
|
||||||
|
Body: body,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var firstAtt proton.ReportBugAttachment
|
safe.Lock(func() {
|
||||||
if len(attachments) > 0 && report.IncludeLogs {
|
|
||||||
firstAtt = attachments[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
attachmentType := proton.AttachmentTypeSync
|
|
||||||
if len(attachments) > 1 {
|
|
||||||
attachmentType = proton.AttachmentTypeAsync
|
|
||||||
}
|
|
||||||
|
|
||||||
token, err := bridge.createTicket(ctx, report, attachmentType, firstAtt)
|
|
||||||
if err != nil || token == "" {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
safe.RLock(func() {
|
|
||||||
for _, user := range bridge.users {
|
for _, user := range bridge.users {
|
||||||
user.ReportBugSent()
|
user.ReportBugSent()
|
||||||
}
|
}
|
||||||
}, bridge.usersLock)
|
}, bridge.usersLock)
|
||||||
|
|
||||||
// if we have a token we can append more attachment to the bugReport
|
return bridge.api.ReportBug(ctx, proton.ReportBugReq{
|
||||||
for i, att := range attachments {
|
OS: osType,
|
||||||
if i == 0 && report.IncludeLogs {
|
OSVersion: osVersion,
|
||||||
continue
|
|
||||||
}
|
|
||||||
err := bridge.appendComment(ctx, token, att)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bridge *Bridge) CollectLogs() (proton.ReportBugAttachment, error) {
|
Title: "[Bridge] Bug - " + title,
|
||||||
logsPath, err := bridge.locator.ProvideLogsPath()
|
Description: description,
|
||||||
if err != nil {
|
|
||||||
return proton.ReportBugAttachment{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer, err := logging.ZipLogsForBugReport(logsPath, DefaultMaxSessionCountForBugReport, DefaultMaxBugReportZipSize)
|
Client: client,
|
||||||
if err != nil {
|
|
||||||
return proton.ReportBugAttachment{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := io.ReadAll(buffer)
|
|
||||||
if err != nil {
|
|
||||||
return proton.ReportBugAttachment{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return proton.ReportBugAttachment{
|
|
||||||
Name: "logs.zip",
|
|
||||||
Filename: "logs.zip",
|
|
||||||
MIMEType: "application/zip",
|
|
||||||
Body: body,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bridge *Bridge) createTicket(ctx context.Context, report *ReportBugReq,
|
|
||||||
asyncAttach proton.AttachmentType, att proton.ReportBugAttachment) (string, error) {
|
|
||||||
var attachments []proton.ReportBugAttachment
|
|
||||||
attachments = append(attachments, att)
|
|
||||||
res, err := bridge.api.ReportBug(ctx, proton.ReportBugReq{
|
|
||||||
OS: report.OSType,
|
|
||||||
OSVersion: report.OSVersion,
|
|
||||||
|
|
||||||
Title: "[Bridge] Bug - " + report.Title,
|
|
||||||
Description: report.Description,
|
|
||||||
|
|
||||||
Client: report.EmailClient,
|
|
||||||
ClientType: proton.ClientTypeEmail,
|
ClientType: proton.ClientTypeEmail,
|
||||||
ClientVersion: constants.AppVersion(bridge.curVersion.Original()),
|
ClientVersion: constants.AppVersion(bridge.curVersion.Original()),
|
||||||
|
|
||||||
Username: report.Username,
|
Username: account,
|
||||||
Email: report.Email,
|
Email: email,
|
||||||
|
}, attachment...)
|
||||||
AsyncAttachments: asyncAttach,
|
|
||||||
}, attachments...)
|
|
||||||
|
|
||||||
if err != nil || asyncAttach != proton.AttachmentTypeAsync {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if asyncAttach == proton.AttachmentTypeAsync && res.Token == nil {
|
|
||||||
return "", errors.New("no token returns for AsyncAttachments")
|
|
||||||
}
|
|
||||||
|
|
||||||
return *res.Token, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bridge *Bridge) appendComment(ctx context.Context, token string, att proton.ReportBugAttachment) error {
|
|
||||||
var attachments []proton.ReportBugAttachment
|
|
||||||
attachments = append(attachments, att)
|
|
||||||
return bridge.api.ReportBugAttachement(ctx, proton.ReportBugAttachmentReq{
|
|
||||||
Product: proton.ClientTypeEmail,
|
|
||||||
Body: "Comment adding attachment: " + att.Filename,
|
|
||||||
Token: token,
|
|
||||||
}, attachments...)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (bridge *Bridge) ReportBugClicked() {
|
func (bridge *Bridge) ReportBugClicked() {
|
||||||
safe.RLock(func() {
|
safe.Lock(func() {
|
||||||
for _, user := range bridge.users {
|
for _, user := range bridge.users {
|
||||||
user.ReportBugClicked()
|
user.ReportBugClicked()
|
||||||
}
|
}
|
||||||
@ -30,7 +30,7 @@ func (bridge *Bridge) ReportBugClicked() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) AutoconfigUsed(client string) {
|
func (bridge *Bridge) AutoconfigUsed(client string) {
|
||||||
safe.RLock(func() {
|
safe.Lock(func() {
|
||||||
for _, user := range bridge.users {
|
for _, user := range bridge.users {
|
||||||
user.AutoconfigUsed(client)
|
user.AutoconfigUsed(client)
|
||||||
}
|
}
|
||||||
@ -38,7 +38,7 @@ func (bridge *Bridge) AutoconfigUsed(client string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) KBArticleOpened(article string) {
|
func (bridge *Bridge) KBArticleOpened(article string) {
|
||||||
safe.RLock(func() {
|
safe.Lock(func() {
|
||||||
for _, user := range bridge.users {
|
for _, user := range bridge.users {
|
||||||
user.KBArticleOpened(article)
|
user.KBArticleOpened(article)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -154,7 +154,3 @@ func (testUpdater *TestUpdater) GetVersionInfo(_ context.Context, _ updater.Down
|
|||||||
func (testUpdater *TestUpdater) InstallUpdate(_ context.Context, _ updater.Downloader, _ updater.VersionInfo) error {
|
func (testUpdater *TestUpdater) InstallUpdate(_ context.Context, _ updater.Downloader, _ updater.VersionInfo) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (testUpdater *TestUpdater) RemoveOldUpdates() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@ -336,9 +336,6 @@ func TestBridge_SendInvite(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBridge_SendAddTextBodyPartIfNotExists(t *testing.T) {
|
func TestBridge_SendAddTextBodyPartIfNotExists(t *testing.T) {
|
||||||
// NOTE: Prior to GODT-2887, these tests had inline images, however after the implementation to support
|
|
||||||
// inline images new parts are injected to reference inline images without content-id set. The images
|
|
||||||
// in this test have been changed to regular attachments to keep the original checks in place.
|
|
||||||
const messageMultipartWithoutText = `Content-Type: multipart/mixed;
|
const messageMultipartWithoutText = `Content-Type: multipart/mixed;
|
||||||
boundary="Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84"
|
boundary="Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84"
|
||||||
Subject: A new message
|
Subject: A new message
|
||||||
@ -346,7 +343,7 @@ Date: Mon, 13 Mar 2023 16:06:16 +0100
|
|||||||
|
|
||||||
|
|
||||||
--Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84
|
--Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84
|
||||||
Content-Disposition: attachment;
|
Content-Disposition: inline;
|
||||||
filename=Cat_August_2010-4.jpeg
|
filename=Cat_August_2010-4.jpeg
|
||||||
Content-Type: image/jpeg;
|
Content-Type: image/jpeg;
|
||||||
name="Cat_August_2010-4.jpeg"
|
name="Cat_August_2010-4.jpeg"
|
||||||
@ -363,7 +360,7 @@ Subject: A new message Part2
|
|||||||
Date: Mon, 13 Mar 2023 16:06:16 +0100
|
Date: Mon, 13 Mar 2023 16:06:16 +0100
|
||||||
|
|
||||||
--Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84
|
--Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84
|
||||||
Content-Disposition: attachment;
|
Content-Disposition: inline;
|
||||||
filename=Cat_August_2010-4.jpeg
|
filename=Cat_August_2010-4.jpeg
|
||||||
Content-Type: image/jpeg;
|
Content-Type: image/jpeg;
|
||||||
name="Cat_August_2010-4.jpeg"
|
name="Cat_August_2010-4.jpeg"
|
||||||
@ -523,181 +520,3 @@ SGVsbG8gd29ybGQK
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBridge_SendInlineImage(t *testing.T) {
|
|
||||||
const messageInlineImageOnly = `Content-Type: multipart/mixed;
|
|
||||||
boundary="Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84"
|
|
||||||
Subject: A new message
|
|
||||||
Date: Mon, 13 Mar 2023 16:06:16 +0100
|
|
||||||
|
|
||||||
|
|
||||||
--Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84
|
|
||||||
Content-Disposition: inline;
|
|
||||||
filename=Cat_August_2010-4.jpeg
|
|
||||||
Content-Type: image/jpeg;
|
|
||||||
name="Cat_August_2010-4.jpeg"
|
|
||||||
Content-Transfer-Encoding: base64
|
|
||||||
|
|
||||||
SGVsbG8gd29ybGQ=
|
|
||||||
|
|
||||||
--Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84--
|
|
||||||
`
|
|
||||||
|
|
||||||
const messageInlineImageWithHTML = `Content-Type: multipart/mixed;
|
|
||||||
boundary="Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84"
|
|
||||||
Subject: A new message Part2
|
|
||||||
Date: Mon, 13 Mar 2023 16:06:16 +0100
|
|
||||||
|
|
||||||
--Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84
|
|
||||||
Content-Type: text/html;charset=utf8
|
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
|
|
||||||
Hello world
|
|
||||||
|
|
||||||
--Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84
|
|
||||||
Content-Disposition: inline;
|
|
||||||
filename=Cat_August_2010-4.jpeg
|
|
||||||
Content-Type: image/jpeg;
|
|
||||||
name="Cat_August_2010-4.jpeg"
|
|
||||||
Content-Transfer-Encoding: base64
|
|
||||||
|
|
||||||
SGVsbG8gd29ybGQ=
|
|
||||||
|
|
||||||
--Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84--
|
|
||||||
`
|
|
||||||
|
|
||||||
const messageInlineImageWithText = `Content-Type: multipart/mixed;
|
|
||||||
boundary="Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84"
|
|
||||||
Subject: A new message Part3
|
|
||||||
Date: Mon, 13 Mar 2023 16:06:16 +0100
|
|
||||||
|
|
||||||
--Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84
|
|
||||||
Content-Type: text/plain;charset=utf8
|
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
|
|
||||||
Hello world
|
|
||||||
|
|
||||||
--Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84
|
|
||||||
Content-Disposition: inline;
|
|
||||||
filename=Cat_August_2010-4.jpeg
|
|
||||||
Content-Type: image/jpeg;
|
|
||||||
name="Cat_August_2010-4.jpeg"
|
|
||||||
Content-Transfer-Encoding: base64
|
|
||||||
|
|
||||||
SGVsbG8gd29ybGQ=
|
|
||||||
|
|
||||||
--Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84--
|
|
||||||
`
|
|
||||||
|
|
||||||
const messageInlineImageFollowedByText = `Content-Type: multipart/mixed;
|
|
||||||
boundary="Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84"
|
|
||||||
Subject: A new message Part4
|
|
||||||
Date: Mon, 13 Mar 2023 16:06:16 +0100
|
|
||||||
|
|
||||||
--Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84
|
|
||||||
Content-Disposition: inline;
|
|
||||||
filename=Cat_August_2010-4.jpeg
|
|
||||||
Content-Type: image/jpeg;
|
|
||||||
name="Cat_August_2010-4.jpeg"
|
|
||||||
Content-Transfer-Encoding: base64
|
|
||||||
|
|
||||||
SGVsbG8gd29ybGQ=
|
|
||||||
|
|
||||||
--Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84
|
|
||||||
Content-Type: text/plain;charset=utf8
|
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
|
|
||||||
Hello world
|
|
||||||
|
|
||||||
--Apple-Mail=_E7AC06C7-4EB2-4453-8CBB-80F4412A7C84--
|
|
||||||
`
|
|
||||||
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, storeKey []byte) {
|
|
||||||
_, _, err := s.CreateUser("recipient", password)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, _ *bridge.Mocks) {
|
|
||||||
smtpWaiter := waitForSMTPServerReady(bridge)
|
|
||||||
defer smtpWaiter.Done()
|
|
||||||
|
|
||||||
senderUserID, err := bridge.LoginFull(ctx, username, password, nil, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
recipientUserID, err := bridge.LoginFull(ctx, "recipient", password, nil, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
senderInfo, err := bridge.GetUserInfo(senderUserID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
recipientInfo, err := bridge.GetUserInfo(recipientUserID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
messages := []string{
|
|
||||||
messageInlineImageOnly,
|
|
||||||
messageInlineImageWithHTML,
|
|
||||||
messageInlineImageWithText,
|
|
||||||
messageInlineImageFollowedByText,
|
|
||||||
}
|
|
||||||
|
|
||||||
smtpWaiter.Wait()
|
|
||||||
|
|
||||||
for _, m := range messages {
|
|
||||||
// Dial the server.
|
|
||||||
client, err := smtp.Dial(net.JoinHostPort(constants.Host, fmt.Sprint(bridge.GetSMTPPort())))
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer client.Close() //nolint:errcheck
|
|
||||||
|
|
||||||
// Upgrade to TLS.
|
|
||||||
require.NoError(t, client.StartTLS(&tls.Config{InsecureSkipVerify: true}))
|
|
||||||
|
|
||||||
// Authorize with SASL LOGIN.
|
|
||||||
require.NoError(t, client.Auth(sasl.NewLoginClient(
|
|
||||||
senderInfo.Addresses[0],
|
|
||||||
string(senderInfo.BridgePass)),
|
|
||||||
))
|
|
||||||
|
|
||||||
// Send the message.
|
|
||||||
require.NoError(t, client.SendMail(
|
|
||||||
senderInfo.Addresses[0],
|
|
||||||
[]string{recipientInfo.Addresses[0]},
|
|
||||||
strings.NewReader(m),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect the sender IMAP client.
|
|
||||||
senderIMAPClient, err := eventuallyDial(net.JoinHostPort(constants.Host, fmt.Sprint(bridge.GetIMAPPort())))
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NoError(t, senderIMAPClient.Login(senderInfo.Addresses[0], string(senderInfo.BridgePass)))
|
|
||||||
defer senderIMAPClient.Logout() //nolint:errcheck
|
|
||||||
|
|
||||||
// Connect the recipient IMAP client.
|
|
||||||
recipientIMAPClient, err := eventuallyDial(net.JoinHostPort(constants.Host, fmt.Sprint(bridge.GetIMAPPort())))
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NoError(t, recipientIMAPClient.Login(recipientInfo.Addresses[0], string(recipientInfo.BridgePass)))
|
|
||||||
defer recipientIMAPClient.Logout() //nolint:errcheck
|
|
||||||
|
|
||||||
require.Eventually(t, func() bool {
|
|
||||||
messages, err := clientFetch(senderIMAPClient, `Sent`, imap.FetchBodyStructure)
|
|
||||||
require.NoError(t, err)
|
|
||||||
if len(messages) != 4 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// messages may not be in order
|
|
||||||
for _, message := range messages {
|
|
||||||
require.Equal(t, 1, len(message.BodyStructure.Parts))
|
|
||||||
require.Equal(t, "multipart", message.BodyStructure.MIMEType)
|
|
||||||
require.Equal(t, "mixed", message.BodyStructure.MIMESubType)
|
|
||||||
require.Equal(t, "multipart", message.BodyStructure.Parts[0].MIMEType)
|
|
||||||
require.Equal(t, "related", message.BodyStructure.Parts[0].MIMESubType)
|
|
||||||
require.Len(t, message.BodyStructure.Parts[0].Parts, 2)
|
|
||||||
require.Equal(t, "text", message.BodyStructure.Parts[0].Parts[0].MIMEType)
|
|
||||||
require.Equal(t, "html", message.BodyStructure.Parts[0].Parts[0].MIMESubType)
|
|
||||||
require.Equal(t, "image", message.BodyStructure.Parts[0].Parts[1].MIMEType)
|
|
||||||
require.Equal(t, "jpeg", message.BodyStructure.Parts[0].Parts[1].MIMESubType)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}, 10*time.Second, 100*time.Millisecond)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@ -53,5 +53,4 @@ type Autostarter interface {
|
|||||||
type Updater interface {
|
type Updater interface {
|
||||||
GetVersionInfo(context.Context, updater.Downloader, updater.Channel) (updater.VersionInfo, error)
|
GetVersionInfo(context.Context, updater.Downloader, updater.Channel) (updater.VersionInfo, error)
|
||||||
InstallUpdate(context.Context, updater.Downloader, updater.VersionInfo) error
|
InstallUpdate(context.Context, updater.Downloader, updater.VersionInfo) error
|
||||||
RemoveOldUpdates() error
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -139,9 +139,3 @@ func (bridge *Bridge) installUpdate(ctx context.Context, job installJob) {
|
|||||||
}
|
}
|
||||||
}, bridge.newVersionLock)
|
}, bridge.newVersionLock)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) RemoveOldUpdates() {
|
|
||||||
if err := bridge.updater.RemoveOldUpdates(); err != nil {
|
|
||||||
logrus.WithError(err).Error("Remove old updates fails")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -46,8 +46,6 @@ const (
|
|||||||
Connected
|
Connected
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrFailedToUnlock = errors.New("failed to unlock user keys")
|
|
||||||
|
|
||||||
type UserInfo struct {
|
type UserInfo struct {
|
||||||
// UserID is the user's API ID.
|
// UserID is the user's API ID.
|
||||||
UserID string
|
UserID string
|
||||||
@ -68,10 +66,10 @@ type UserInfo struct {
|
|||||||
BridgePass []byte
|
BridgePass []byte
|
||||||
|
|
||||||
// UsedSpace is the amount of space used by the user.
|
// UsedSpace is the amount of space used by the user.
|
||||||
UsedSpace uint64
|
UsedSpace int
|
||||||
|
|
||||||
// MaxSpace is the total amount of space available to the user.
|
// MaxSpace is the total amount of space available to the user.
|
||||||
MaxSpace uint64
|
MaxSpace int
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserIDs returns the IDs of all known users (authorized or not).
|
// GetUserIDs returns the IDs of all known users (authorized or not).
|
||||||
@ -159,15 +157,11 @@ func (bridge *Bridge) LoginUser(
|
|||||||
func() (string, error) {
|
func() (string, error) {
|
||||||
return bridge.loginUser(ctx, client, auth.UID, auth.RefreshToken, keyPass)
|
return bridge.loginUser(ctx, client, auth.UID, auth.RefreshToken, keyPass)
|
||||||
},
|
},
|
||||||
|
func() error {
|
||||||
|
return client.AuthDelete(ctx)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Failure to unlock will allow retries, so we do not delete auth.
|
|
||||||
if !errors.Is(err, ErrFailedToUnlock) {
|
|
||||||
if deleteErr := client.AuthDelete(ctx); deleteErr != nil {
|
|
||||||
logrus.WithError(deleteErr).Error("Failed to delete auth")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("failed to login user: %w", err)
|
return "", fmt.Errorf("failed to login user: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,16 +217,7 @@ func (bridge *Bridge) LoginFull(
|
|||||||
keyPass = password
|
keyPass = password
|
||||||
}
|
}
|
||||||
|
|
||||||
userID, err := bridge.LoginUser(ctx, client, auth, keyPass)
|
return bridge.LoginUser(ctx, client, auth, keyPass)
|
||||||
if err != nil {
|
|
||||||
if deleteErr := client.AuthDelete(ctx); deleteErr != nil {
|
|
||||||
logrus.WithError(err).Error("Failed to delete auth")
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return userID, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogoutUser logs out the given user.
|
// LogoutUser logs out the given user.
|
||||||
@ -329,7 +314,7 @@ func (bridge *Bridge) SetAddressMode(ctx context.Context, userID string, mode va
|
|||||||
func (bridge *Bridge) SendBadEventUserFeedback(_ context.Context, userID string, doResync bool) error {
|
func (bridge *Bridge) SendBadEventUserFeedback(_ context.Context, userID string, doResync bool) error {
|
||||||
logrus.WithField("userID", userID).WithField("doResync", doResync).Info("Passing bad event feedback to user")
|
logrus.WithField("userID", userID).WithField("doResync", doResync).Info("Passing bad event feedback to user")
|
||||||
|
|
||||||
return safe.RLockRet(func() error {
|
return safe.LockRet(func() error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
user, ok := bridge.users[userID]
|
user, ok := bridge.users[userID]
|
||||||
@ -389,9 +374,9 @@ func (bridge *Bridge) loginUser(ctx context.Context, client *proton.Client, auth
|
|||||||
}
|
}
|
||||||
|
|
||||||
if userKR, err := apiUser.Keys.Unlock(saltedKeyPass, nil); err != nil {
|
if userKR, err := apiUser.Keys.Unlock(saltedKeyPass, nil); err != nil {
|
||||||
return "", fmt.Errorf("%w: %w", ErrFailedToUnlock, err)
|
return "", fmt.Errorf("failed to unlock user keys: %w", err)
|
||||||
} else if userKR.CountDecryptionEntities() == 0 {
|
} else if userKR.CountDecryptionEntities() == 0 {
|
||||||
return "", ErrFailedToUnlock
|
return "", fmt.Errorf("failed to unlock user keys")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := bridge.addUser(ctx, client, apiUser, authUID, authRef, saltedKeyPass, true); err != nil {
|
if err := bridge.addUser(ctx, client, apiUser, authUID, authRef, saltedKeyPass, true); err != nil {
|
||||||
|
|||||||
@ -49,7 +49,7 @@ func (bridge *Bridge) handleUserDeauth(ctx context.Context, user *user.User) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) handleUserBadEvent(ctx context.Context, user *user.User, event events.UserBadEvent) {
|
func (bridge *Bridge) handleUserBadEvent(ctx context.Context, user *user.User, event events.UserBadEvent) {
|
||||||
safe.RLock(func() {
|
safe.Lock(func() {
|
||||||
if rerr := bridge.reporter.ReportMessageWithContext("Failed to handle event", reporter.Context{
|
if rerr := bridge.reporter.ReportMessageWithContext("Failed to handle event", reporter.Context{
|
||||||
"user_id": user.ID(),
|
"user_id": user.ID(),
|
||||||
"old_event_id": event.OldEventID,
|
"old_event_id": event.OldEventID,
|
||||||
|
|||||||
@ -23,200 +23,71 @@ package certs
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <Security/Security.h>
|
#import <Security/Security.h>
|
||||||
|
|
||||||
// Memory management rules:
|
|
||||||
// Foundation object (Objective-C prefixed with `NS`) get ARC (Automatic Reference Counting), and do not need to be released manually.
|
|
||||||
// Core Foundation objects (C), prefixed with need to be released manually using CFRelease() unless:
|
|
||||||
// - They're obtained using a CF method containing the word Get (a.k.a. the Get Rule).
|
|
||||||
// - They're obtained using toll-free bridging from a Foundation Object (using the __bridge keyword).
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
int installTrustedCert(char const *bytes, unsigned long long length) {
|
||||||
/// \brief Create a certificate object from DER-encoded data.
|
if (length == 0) {
|
||||||
///
|
return errSecInvalidData;
|
||||||
/// \return The certifcation. The caller is responsible for releasing the object using CFRelease.
|
}
|
||||||
/// \return NULL if data is not a valid DER-encoded certificate.
|
|
||||||
//****************************************************************************************************************************************************
|
NSData *der = [NSData dataWithBytes:bytes length:length];
|
||||||
SecCertificateRef certFromData(char const* data, uint64_t length) {
|
|
||||||
NSData *der = [NSData dataWithBytes:data length:length];
|
// Step 1. Import the certificate in the keychain.
|
||||||
return SecCertificateCreateWithData(NULL, (__bridge CFDataRef)der);
|
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, kSecTrustSettingsDomainUser, (__bridge CFTypeRef)(trustSettings));
|
||||||
|
CFRelease(policy);
|
||||||
|
CFRelease(cert);
|
||||||
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
int removeTrustedCert(char const *bytes, unsigned long long length) {
|
||||||
/// \brief Check if a certificate is in the user's keychain.
|
if (0 == length) {
|
||||||
///
|
return errSecInvalidData;
|
||||||
/// \param[in] cert The certificate.
|
}
|
||||||
/// \return true iff the certificate is in the user's keychain.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
bool _isCertificateInKeychain(SecCertificateRef const cert) {
|
|
||||||
NSDictionary *attrs = @{
|
|
||||||
(id)kSecMatchItemList: @[(__bridge id)cert],
|
|
||||||
(id)kSecClass: (id)kSecClassCertificate,
|
|
||||||
(id)kSecReturnData: @YES
|
|
||||||
};
|
|
||||||
return errSecSuccess == SecItemCopyMatching((__bridge CFDictionaryRef)attrs, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
NSData *der = [NSData dataWithBytes: bytes length: length];
|
||||||
/// \brief Check if a certificate is in the user's keychain.
|
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (CFDataRef) der);
|
||||||
///
|
|
||||||
/// \param[in] certData The certificate data in DER encoded format.
|
|
||||||
/// \param[in] certSize The size of the certData in bytes.
|
|
||||||
/// \return true iff the certificate is in the user's keychain.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
bool isCertificateInKeychain(char const* certData, uint64_t certSize) {
|
|
||||||
return _isCertificateInKeychain(certFromData(certData, certSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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, kSecTrustSettingsDomainUser, (__bridge CFTypeRef)(trustSettings));
|
||||||
|
CFRelease(policy);
|
||||||
|
if (errSecSuccess != status) {
|
||||||
|
CFRelease(cert);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
// Step 2. Remove the certificate from the keychain.
|
||||||
/// \brief Add a certificate to the user's keychain.
|
NSDictionary *query = @{ (id)kSecClass: (id)kSecClassCertificate,
|
||||||
///
|
(id)kSecMatchItemList: @[(__bridge id)cert],
|
||||||
/// \param[in] cert The certificate.
|
(id)kSecMatchLimit: (id)kSecMatchLimitOne,
|
||||||
/// \return The status for the operation.
|
};
|
||||||
//****************************************************************************************************************************************************
|
status = SecItemDelete((__bridge CFDictionaryRef) query);
|
||||||
OSStatus _addCertificateToKeychain(SecCertificateRef const cert) {
|
|
||||||
NSDictionary* addQuery = @{
|
|
||||||
(id)kSecValueRef: (__bridge id) cert,
|
|
||||||
(id)kSecClass: (id)kSecClassCertificate,
|
|
||||||
};
|
|
||||||
return SecItemAdd((__bridge CFDictionaryRef) addQuery, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
CFRelease(cert);
|
||||||
/// \brief Add a certificate to the user's keychain.
|
return status;
|
||||||
///
|
|
||||||
/// \param[in] certData The certificate data in DER encoded format.
|
|
||||||
/// \param[in] certSize The size of the certData in bytes.
|
|
||||||
/// \return The status for the operation.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
OSStatus addCertificateToKeychain(char const* certData, uint64_t certSize) {
|
|
||||||
return _addCertificateToKeychain(certFromData(certData, certSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \brief Add a certificate to the user's keychain.
|
|
||||||
///
|
|
||||||
/// \param[in] cert The certificate.
|
|
||||||
/// \return The status for the operation.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
OSStatus _removeCertificateFromKeychain(SecCertificateRef const cert) {
|
|
||||||
NSDictionary *query = @{ (id)kSecClass: (id)kSecClassCertificate,
|
|
||||||
(id)kSecMatchItemList: @[(__bridge id)cert],
|
|
||||||
(id)kSecMatchLimit: (id)kSecMatchLimitOne,
|
|
||||||
};
|
|
||||||
return SecItemDelete((__bridge CFDictionaryRef) query);
|
|
||||||
}
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \brief Add a certificate to the user's keychain.
|
|
||||||
///
|
|
||||||
/// \param[in] certData The certificate data in DER encoded format.
|
|
||||||
/// \param[in] certSize The size of the certData in bytes.
|
|
||||||
/// \return The status for the operation.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
OSStatus removeCertificateFromKeychain(char const* certData, uint64_t certSize) {
|
|
||||||
return _removeCertificateFromKeychain(certFromData(certData, certSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \brief Check if a certificate is trusted in the user's keychain.
|
|
||||||
///
|
|
||||||
/// \param[in] cert The certificate.
|
|
||||||
/// \return true iff the certificate is trusted in the user's keychain.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
bool _isCertificateTrusted(SecCertificateRef const cert) {
|
|
||||||
CFArrayRef trustSettings = NULL;
|
|
||||||
OSStatus status = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainUser, &trustSettings);
|
|
||||||
if (status != errSecSuccess) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
CFIndex count = CFArrayGetCount(trustSettings);
|
|
||||||
bool result = false;
|
|
||||||
for (CFIndex index = 0; index < count; ++index) {
|
|
||||||
CFDictionaryRef dict = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, index);
|
|
||||||
if (!dict) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(dict, kSecTrustSettingsResult);
|
|
||||||
int value;
|
|
||||||
if (num && CFNumberGetValue(num, kCFNumberSInt32Type, &value) && (value == kSecTrustSettingsResultTrustRoot)) {
|
|
||||||
result = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CFRelease(trustSettings);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \brief Check if a certificate is trusted in the user's keychain.
|
|
||||||
///
|
|
||||||
/// \param[in] certData The certificate data in DER encoded format.
|
|
||||||
/// \param[in] certSize The size of the certData in bytes.
|
|
||||||
/// \return true iff the certificate is trusted in the user's keychain.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
bool isCertificateTrusted(char const* certData, uint64_t certSize) {
|
|
||||||
return _isCertificateTrusted(certFromData(certData, certSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \brief Set the trust level for a certificate in the user's keychain. This call will trigger a security prompt.
|
|
||||||
///
|
|
||||||
/// \param[in] cert The certificate.
|
|
||||||
/// \param[in] trustLevel The trust level.
|
|
||||||
/// \return The status for the operation.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
OSStatus _setCertificateTrustLevel(SecCertificateRef const cert, int trustLevel) {
|
|
||||||
SecPolicyRef policy = SecPolicyCreateSSL(true, NULL); // we limit our trust to SSL
|
|
||||||
NSDictionary *trustSettings = @{
|
|
||||||
(id)kSecTrustSettingsResult: [NSNumber numberWithInt:trustLevel],
|
|
||||||
(id)kSecTrustSettingsPolicy: (__bridge id) policy,
|
|
||||||
};
|
|
||||||
OSStatus status = SecTrustSettingsSetTrustSettings(cert, kSecTrustSettingsDomainUser, (__bridge CFTypeRef)(trustSettings));
|
|
||||||
CFRelease(policy);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \brief Set a certificate as trusted in the user's keychain. This call will trigger a security prompt.
|
|
||||||
///
|
|
||||||
/// \param[in] cert The certificate.
|
|
||||||
/// \return The status for the operation.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
OSStatus _setCertificateTrusted(SecCertificateRef cert) {
|
|
||||||
return _setCertificateTrustLevel(cert, kSecTrustSettingsResultTrustRoot);
|
|
||||||
}
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \brief Set a certificate as trusted in the user's keychain. This call will trigger a security prompt.
|
|
||||||
///
|
|
||||||
/// \param[in] certData The certificate data in DER encoded format.
|
|
||||||
/// \param[in] certSize The size of the certData in bytes.
|
|
||||||
/// \return The status for the operation.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
OSStatus setCertificateTrusted(char const* certData, uint64_t certSize) {
|
|
||||||
return _setCertificateTrusted(certFromData(certData, certSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \brief Remove the trust level of a certificate in the user's keychain.
|
|
||||||
///
|
|
||||||
/// \param[in] cert The certificate.
|
|
||||||
/// \return The status for the operation.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
OSStatus _removeCertificateTrust(SecCertificateRef cert) {
|
|
||||||
return _setCertificateTrustLevel(cert, kSecTrustSettingsResultUnspecified);
|
|
||||||
}
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \brief Remove the trust level of a certificate in the user's keychain.
|
|
||||||
///
|
|
||||||
/// \param[in] certData The certificate data in DER encoded format.
|
|
||||||
/// \param[in] certSize The size of the certData in bytes.
|
|
||||||
/// \return The status for the operation.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
OSStatus removeCertificateTrust(char const* certData, uint64_t certSize) {
|
|
||||||
return _removeCertificateTrust(certFromData(certData, certSize));
|
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
@ -248,116 +119,6 @@ func certPEMToDER(certPEM []byte) ([]byte, error) {
|
|||||||
return block.Bytes, nil
|
return block.Bytes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// wrapCGoCertCallReturningBool wrap call to a CGo function returning a bool.
|
|
||||||
// if the certificate is invalid the call will return false.
|
|
||||||
func wrapCGoCertCallReturningBool(certPEM []byte, fn func(*C.char, C.ulonglong) bool) bool {
|
|
||||||
certDER, err := certPEMToDER(certPEM)
|
|
||||||
if err != nil {
|
|
||||||
return false // error are ignored
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer := C.CBytes(certDER)
|
|
||||||
defer C.free(unsafe.Pointer(buffer)) //nolint:unconvert
|
|
||||||
|
|
||||||
return fn((*C.char)(buffer), C.ulonglong(len(certDER)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// wrapCGoCertCallReturningBool wrap call to a CGo function returning an error
|
|
||||||
func wrapCGoCertCallReturningError(certPEM []byte, fn func(*C.char, C.ulonglong) error) error {
|
|
||||||
certDER, err := certPEMToDER(certPEM)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer := C.CBytes(certDER)
|
|
||||||
defer C.free(unsafe.Pointer(buffer)) //nolint:unconvert
|
|
||||||
|
|
||||||
return fn((*C.char)(buffer), C.ulonglong(len(certDER)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// isCertInKeychain returns true if the given certificate is stored in the user's keychain.
|
|
||||||
func isCertInKeychain(certPEM []byte) bool {
|
|
||||||
return wrapCGoCertCallReturningBool(certPEM, isCertInKeychainCGo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isCertInKeychainCGo(buffer *C.char, size C.ulonglong) bool {
|
|
||||||
return bool(C.isCertificateInKeychain(buffer, size))
|
|
||||||
}
|
|
||||||
|
|
||||||
// addCertToKeychain adds a certificate to the user's keychain.
|
|
||||||
// Trying to add a certificate that is already in the keychain will result in an error.
|
|
||||||
func addCertToKeychain(certPEM []byte) error {
|
|
||||||
return wrapCGoCertCallReturningError(certPEM, addCertToKeychainCGo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func addCertToKeychainCGo(buffer *C.char, size C.ulonglong) error {
|
|
||||||
if errCode := C.addCertificateToKeychain(buffer, size); errCode != errSecSuccess {
|
|
||||||
return fmt.Errorf("could not add certificate to keychain (error %v)", errCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// removeCertFromKeychain removes a certificate from the user's keychain.
|
|
||||||
// Trying to remove a certificate that is not in the keychain will result in an error.
|
|
||||||
func removeCertFromKeychain(certPEM []byte) error {
|
|
||||||
return wrapCGoCertCallReturningError(certPEM, removeCertFromKeychainCGo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeCertFromKeychainCGo(buffer *C.char, size C.ulonglong) error {
|
|
||||||
if errCode := C.removeCertificateFromKeychain(buffer, size); errCode != errSecSuccess {
|
|
||||||
return fmt.Errorf("could not remove certificate from keychain (error %v)", errCode)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// isCertTrusted check if a certificate is trusted in the user's keychain.
|
|
||||||
func isCertTrusted(certPEM []byte) bool {
|
|
||||||
return wrapCGoCertCallReturningBool(certPEM, isCertTrustedCGo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isCertTrustedCGo(buffer *C.char, size C.ulonglong) bool {
|
|
||||||
return bool(C.isCertificateTrusted(buffer, size))
|
|
||||||
}
|
|
||||||
|
|
||||||
// setCertTrusted sets a certificate as trusted in the user's keychain.
|
|
||||||
// This function will trigger a security prompt from the system.
|
|
||||||
func setCertTrusted(certPEM []byte) error {
|
|
||||||
return wrapCGoCertCallReturningError(certPEM, setCertTrustedCGo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setCertTrustedCGo(buffer *C.char, size C.ulonglong) error {
|
|
||||||
errCode := C.setCertificateTrusted(buffer, size)
|
|
||||||
switch errCode {
|
|
||||||
case errSecSuccess:
|
|
||||||
return nil
|
|
||||||
case errAuthorizationCanceled:
|
|
||||||
return ErrUserCanceledCertificateInstall
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("could not set certificate trust in keychain (error %v)", errCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// removeCertTrust remove the trust level of the certificated from the user's keychain.
|
|
||||||
// This function will trigger a security prompt from the system.
|
|
||||||
func removeCertTrust(certPEM []byte) error {
|
|
||||||
return wrapCGoCertCallReturningError(certPEM, removeCertTrustCGo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeCertTrustCGo(buffer *C.char, size C.ulonglong) error {
|
|
||||||
errCode := C.removeCertificateTrust(buffer, size)
|
|
||||||
switch errCode {
|
|
||||||
case errSecSuccess:
|
|
||||||
return nil
|
|
||||||
case errAuthorizationCanceled:
|
|
||||||
return ErrUserCanceledCertificateInstall
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("could not set certificate trust in keychain (error %v)", errCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// installCert installs a certificate in the keychain. The certificate is added to the keychain and it is set as trusted.
|
|
||||||
// This function will trigger a security prompt from the system, unless the certificate is already trusted in the user keychain.
|
|
||||||
func installCert(certPEM []byte) error {
|
func installCert(certPEM []byte) error {
|
||||||
certDER, err := certPEMToDER(certPEM)
|
certDER, err := certPEMToDER(certPEM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -366,24 +127,18 @@ func installCert(certPEM []byte) error {
|
|||||||
|
|
||||||
p := C.CBytes(certDER)
|
p := C.CBytes(certDER)
|
||||||
defer C.free(unsafe.Pointer(p)) //nolint:unconvert
|
defer C.free(unsafe.Pointer(p)) //nolint:unconvert
|
||||||
buffer := (*C.char)(p)
|
|
||||||
size := C.ulonglong(len(certDER))
|
|
||||||
|
|
||||||
if !isCertInKeychainCGo(buffer, size) {
|
errCode := C.installTrustedCert((*C.char)(p), (C.ulonglong)(len(certDER)))
|
||||||
if err := addCertToKeychainCGo(buffer, size); err != nil {
|
switch errCode {
|
||||||
return err
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isCertTrustedCGo(buffer, size) {
|
|
||||||
return setCertTrustedCGo(buffer, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// uninstallCert uninstalls a certificate in the keychain. The certificate trust is removed and the certificated is deleted from the keychain.
|
|
||||||
// This function will trigger a security prompt from the system, unless the certificate is not trusted in the user keychain.
|
|
||||||
func uninstallCert(certPEM []byte) error {
|
func uninstallCert(certPEM []byte) error {
|
||||||
certDER, err := certPEMToDER(certPEM)
|
certDER, err := certPEMToDER(certPEM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -392,32 +147,10 @@ func uninstallCert(certPEM []byte) error {
|
|||||||
|
|
||||||
p := C.CBytes(certDER)
|
p := C.CBytes(certDER)
|
||||||
defer C.free(unsafe.Pointer(p)) //nolint:unconvert
|
defer C.free(unsafe.Pointer(p)) //nolint:unconvert
|
||||||
buffer := (*C.char)(p)
|
|
||||||
size := C.ulonglong(len(certDER))
|
|
||||||
|
|
||||||
if isCertTrustedCGo(buffer, size) {
|
if errCode := C.removeTrustedCert((*C.char)(p), (C.ulonglong)(len(certDER))); errCode != 0 {
|
||||||
if err := removeCertTrustCGo(buffer, size); err != nil {
|
return fmt.Errorf("could not install certificate from keychain (error %v)", errCode)
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isCertInKeychainCGo(buffer, size) {
|
|
||||||
return removeCertFromKeychainCGo(buffer, size)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isCertInstalled(certPEM []byte) bool {
|
|
||||||
certDER, err := certPEMToDER(certPEM)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
p := C.CBytes(certDER)
|
|
||||||
defer C.free(unsafe.Pointer(p)) //nolint:unconvert
|
|
||||||
buffer := (*C.char)(p)
|
|
||||||
size := C.ulonglong(len(certDER))
|
|
||||||
|
|
||||||
return isCertInKeychainCGo(buffer, size) && isCertTrustedCGo(buffer, size)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -25,73 +25,20 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCertInKeychain(t *testing.T) {
|
// This test implies human interactions to enter password and is disabled by default.
|
||||||
// no trust settings change is performed, so this test will not trigger an OS security prompt.
|
func _TestTrustedCertsDarwin(t *testing.T) { //nolint:unused
|
||||||
certPEM := generatePEMCertificate(t)
|
|
||||||
require.False(t, isCertInKeychain(certPEM))
|
|
||||||
require.NoError(t, addCertToKeychain(certPEM))
|
|
||||||
require.True(t, isCertInKeychain(certPEM))
|
|
||||||
require.Error(t, addCertToKeychain(certPEM))
|
|
||||||
require.True(t, isCertInKeychain(certPEM))
|
|
||||||
require.NoError(t, removeCertFromKeychain(certPEM))
|
|
||||||
require.False(t, isCertInKeychain(certPEM))
|
|
||||||
require.Error(t, removeCertFromKeychain(certPEM))
|
|
||||||
require.False(t, isCertInKeychain(certPEM))
|
|
||||||
}
|
|
||||||
|
|
||||||
// This test require human interaction (macOS security prompts), and is disabled by default.
|
|
||||||
func _TestCertificateTrust(t *testing.T) { //nolint:unused
|
|
||||||
certPEM := generatePEMCertificate(t)
|
|
||||||
require.False(t, isCertTrusted(certPEM))
|
|
||||||
require.NoError(t, addCertToKeychain(certPEM))
|
|
||||||
require.NoError(t, setCertTrusted(certPEM))
|
|
||||||
require.True(t, isCertTrusted(certPEM))
|
|
||||||
require.NoError(t, removeCertTrust(certPEM))
|
|
||||||
require.False(t, isCertTrusted(certPEM))
|
|
||||||
require.NoError(t, removeCertFromKeychain(certPEM))
|
|
||||||
}
|
|
||||||
|
|
||||||
// This test require human interaction (macOS security prompts), and is disabled by default.
|
|
||||||
func _TestInstallAndRemove(t *testing.T) { //nolint:unused
|
|
||||||
certPEM := generatePEMCertificate(t)
|
|
||||||
|
|
||||||
// fresh install
|
|
||||||
require.False(t, isCertInstalled(certPEM))
|
|
||||||
require.NoError(t, installCert(certPEM))
|
|
||||||
require.True(t, isCertInKeychain(certPEM))
|
|
||||||
require.True(t, isCertTrusted(certPEM))
|
|
||||||
require.True(t, isCertInstalled(certPEM))
|
|
||||||
require.NoError(t, uninstallCert(certPEM))
|
|
||||||
require.False(t, isCertInKeychain(certPEM))
|
|
||||||
require.False(t, isCertTrusted(certPEM))
|
|
||||||
require.False(t, isCertInstalled(certPEM))
|
|
||||||
|
|
||||||
// Install where certificate is already in Keychain, but not trusted.
|
|
||||||
require.NoError(t, addCertToKeychain(certPEM))
|
|
||||||
require.False(t, isCertInstalled(certPEM))
|
|
||||||
require.NoError(t, installCert(certPEM))
|
|
||||||
require.True(t, isCertInstalled(certPEM))
|
|
||||||
|
|
||||||
// Install where certificate is already installed
|
|
||||||
require.NoError(t, installCert(certPEM))
|
|
||||||
|
|
||||||
// Remove when certificate is not trusted.
|
|
||||||
require.NoError(t, removeCertTrust(certPEM))
|
|
||||||
require.NoError(t, uninstallCert(certPEM))
|
|
||||||
require.False(t, isCertInstalled(certPEM))
|
|
||||||
|
|
||||||
// Remove when certificate has already been removed.
|
|
||||||
require.NoError(t, uninstallCert(certPEM))
|
|
||||||
require.False(t, isCertTrusted(certPEM))
|
|
||||||
require.False(t, isCertInKeychain(certPEM))
|
|
||||||
}
|
|
||||||
|
|
||||||
func generatePEMCertificate(t *testing.T) []byte {
|
|
||||||
template, err := NewTLSTemplate()
|
template, err := NewTLSTemplate()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
certPEM, _, err := GenerateCert(template)
|
certPEM, _, err := GenerateCert(template)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
return certPEM
|
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.
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,3 @@ func installCert([]byte) error {
|
|||||||
func uninstallCert([]byte) error {
|
func uninstallCert([]byte) error {
|
||||||
return nil // Linux doesn't have a root cert store.
|
return nil // Linux doesn't have a root cert store.
|
||||||
}
|
}
|
||||||
|
|
||||||
func isCertInstalled([]byte) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|||||||
@ -24,7 +24,3 @@ func installCert([]byte) error {
|
|||||||
func uninstallCert([]byte) error {
|
func uninstallCert([]byte) error {
|
||||||
return nil // NOTE(GODT-986): Uninstall certs from root cert store?
|
return nil // NOTE(GODT-986): Uninstall certs from root cert store?
|
||||||
}
|
}
|
||||||
|
|
||||||
func isCertInstalled([]byte) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|||||||
@ -17,50 +17,16 @@
|
|||||||
|
|
||||||
package certs
|
package certs
|
||||||
|
|
||||||
import (
|
type Installer struct{}
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrUserCanceledCertificateInstall = errors.New("the user cancelled the authorization dialog")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Installer struct {
|
|
||||||
log *logrus.Entry
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewInstaller() *Installer {
|
func NewInstaller() *Installer {
|
||||||
return &Installer{
|
return &Installer{}
|
||||||
log: logrus.WithField("pkg", "certs"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (installer *Installer) InstallCert(certPEM []byte) error {
|
func (installer *Installer) InstallCert(certPEM []byte) error {
|
||||||
installer.log.Info("Installing the Bridge TLS certificate in the OS keychain")
|
return installCert(certPEM)
|
||||||
|
|
||||||
if err := installCert(certPEM); err != nil {
|
|
||||||
installer.log.WithError(err).Error("The Bridge TLS certificate could not be installed in the OS keychain")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
installer.log.Info("The Bridge TLS certificate was successfully installed in the OS keychain")
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (installer *Installer) UninstallCert(certPEM []byte) error {
|
func (installer *Installer) UninstallCert(certPEM []byte) error {
|
||||||
installer.log.Info("Uninstalling the Bridge TLS certificate from the OS keychain")
|
return uninstallCert(certPEM)
|
||||||
|
|
||||||
if err := uninstallCert(certPEM); err != nil {
|
|
||||||
installer.log.WithError(err).Error("The Bridge TLS certificate could not be uninstalled from the OS keychain")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
installer.log.Info("The Bridge TLS certificate was successfully uninstalled from the OS keychain")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (installer *Installer) IsCertInstalled(certPEM []byte) bool {
|
|
||||||
return isCertInstalled(certPEM)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -95,13 +95,6 @@ func (status *ConfigurationStatus) IsPending() bool {
|
|||||||
return !status.Data.DataV1.PendingSince.IsZero()
|
return !status.Data.DataV1.PendingSince.IsZero()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (status *ConfigurationStatus) isPendingSinceMin() int {
|
|
||||||
if min := int(time.Since(status.Data.DataV1.PendingSince).Minutes()); min > 0 {
|
|
||||||
return min
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (status *ConfigurationStatus) IsFromFailure() bool {
|
func (status *ConfigurationStatus) IsFromFailure() bool {
|
||||||
status.DataLock.RLock()
|
status.DataLock.RLock()
|
||||||
defer status.DataLock.RUnlock()
|
defer status.DataLock.RUnlock()
|
||||||
|
|||||||
@ -19,6 +19,7 @@ package configstatus
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigAbortValues struct {
|
type ConfigAbortValues struct {
|
||||||
@ -40,20 +41,17 @@ type ConfigAbortData struct {
|
|||||||
|
|
||||||
type ConfigAbortBuilder struct{}
|
type ConfigAbortBuilder struct{}
|
||||||
|
|
||||||
func (*ConfigAbortBuilder) New(config *ConfigurationStatus) ConfigAbortData {
|
func (*ConfigAbortBuilder) New(data *ConfigurationStatusData) ConfigAbortData {
|
||||||
config.DataLock.RLock()
|
|
||||||
defer config.DataLock.RUnlock()
|
|
||||||
|
|
||||||
return ConfigAbortData{
|
return ConfigAbortData{
|
||||||
MeasurementGroup: "bridge.any.configuration",
|
MeasurementGroup: "bridge.any.configuration",
|
||||||
Event: "bridge_config_abort",
|
Event: "bridge_config_abort",
|
||||||
Values: ConfigSuccessValues{
|
Values: ConfigSuccessValues{
|
||||||
Duration: config.isPendingSinceMin(),
|
Duration: int(time.Since(data.DataV1.PendingSince).Minutes()),
|
||||||
},
|
},
|
||||||
Dimensions: ConfigSuccessDimensions{
|
Dimensions: ConfigSuccessDimensions{
|
||||||
ReportClick: strconv.FormatBool(config.Data.DataV1.ReportClick),
|
ReportClick: strconv.FormatBool(data.DataV1.ReportClick),
|
||||||
ReportSent: strconv.FormatBool(config.Data.DataV1.ReportSent),
|
ReportSent: strconv.FormatBool(data.DataV1.ReportSent),
|
||||||
ClickedLink: config.Data.clickedLinkToString(),
|
ClickedLink: data.clickedLinkToString(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,7 @@ func TestConfigurationAbort_default(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var builder = configstatus.ConfigAbortBuilder{}
|
var builder = configstatus.ConfigAbortBuilder{}
|
||||||
req := builder.New(config)
|
req := builder.New(config.Data)
|
||||||
|
|
||||||
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
|
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
|
||||||
require.Equal(t, "bridge_config_abort", req.Event)
|
require.Equal(t, "bridge_config_abort", req.Event)
|
||||||
@ -64,7 +64,7 @@ func TestConfigurationAbort_fed(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var builder = configstatus.ConfigAbortBuilder{}
|
var builder = configstatus.ConfigAbortBuilder{}
|
||||||
req := builder.New(config)
|
req := builder.New(config.Data)
|
||||||
|
|
||||||
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
|
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
|
||||||
require.Equal(t, "bridge_config_abort", req.Event)
|
require.Equal(t, "bridge_config_abort", req.Event)
|
||||||
|
|||||||
@ -33,16 +33,13 @@ type ConfigProgressData struct {
|
|||||||
|
|
||||||
type ConfigProgressBuilder struct{}
|
type ConfigProgressBuilder struct{}
|
||||||
|
|
||||||
func (*ConfigProgressBuilder) New(config *ConfigurationStatus) ConfigProgressData {
|
func (*ConfigProgressBuilder) New(data *ConfigurationStatusData) ConfigProgressData {
|
||||||
config.DataLock.RLock()
|
|
||||||
defer config.DataLock.RUnlock()
|
|
||||||
|
|
||||||
return ConfigProgressData{
|
return ConfigProgressData{
|
||||||
MeasurementGroup: "bridge.any.configuration",
|
MeasurementGroup: "bridge.any.configuration",
|
||||||
Event: "bridge_config_progress",
|
Event: "bridge_config_progress",
|
||||||
Values: ConfigProgressValues{
|
Values: ConfigProgressValues{
|
||||||
NbDay: numberOfDay(time.Now(), config.Data.DataV1.PendingSince),
|
NbDay: numberOfDay(time.Now(), data.DataV1.PendingSince),
|
||||||
NbDaySinceLast: numberOfDay(time.Now(), config.Data.DataV1.LastProgress),
|
NbDaySinceLast: numberOfDay(time.Now(), data.DataV1.LastProgress),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,7 @@ func TestConfigurationProgress_default(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var builder = configstatus.ConfigProgressBuilder{}
|
var builder = configstatus.ConfigProgressBuilder{}
|
||||||
req := builder.New(config)
|
req := builder.New(config.Data)
|
||||||
|
|
||||||
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
|
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
|
||||||
require.Equal(t, "bridge_config_progress", req.Event)
|
require.Equal(t, "bridge_config_progress", req.Event)
|
||||||
@ -62,7 +62,7 @@ func TestConfigurationProgress_fed(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var builder = configstatus.ConfigProgressBuilder{}
|
var builder = configstatus.ConfigProgressBuilder{}
|
||||||
req := builder.New(config)
|
req := builder.New(config.Data)
|
||||||
|
|
||||||
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
|
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
|
||||||
require.Equal(t, "bridge_config_progress", req.Event)
|
require.Equal(t, "bridge_config_progress", req.Event)
|
||||||
|
|||||||
@ -19,6 +19,7 @@ package configstatus
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigRecoveryValues struct {
|
type ConfigRecoveryValues struct {
|
||||||
@ -42,22 +43,19 @@ type ConfigRecoveryData struct {
|
|||||||
|
|
||||||
type ConfigRecoveryBuilder struct{}
|
type ConfigRecoveryBuilder struct{}
|
||||||
|
|
||||||
func (*ConfigRecoveryBuilder) New(config *ConfigurationStatus) ConfigRecoveryData {
|
func (*ConfigRecoveryBuilder) New(data *ConfigurationStatusData) ConfigRecoveryData {
|
||||||
config.DataLock.RLock()
|
|
||||||
defer config.DataLock.RUnlock()
|
|
||||||
|
|
||||||
return ConfigRecoveryData{
|
return ConfigRecoveryData{
|
||||||
MeasurementGroup: "bridge.any.configuration",
|
MeasurementGroup: "bridge.any.configuration",
|
||||||
Event: "bridge_config_recovery",
|
Event: "bridge_config_recovery",
|
||||||
Values: ConfigRecoveryValues{
|
Values: ConfigRecoveryValues{
|
||||||
Duration: config.isPendingSinceMin(),
|
Duration: int(time.Since(data.DataV1.PendingSince).Minutes()),
|
||||||
},
|
},
|
||||||
Dimensions: ConfigRecoveryDimensions{
|
Dimensions: ConfigRecoveryDimensions{
|
||||||
Autoconf: config.Data.DataV1.Autoconf,
|
Autoconf: data.DataV1.Autoconf,
|
||||||
ReportClick: strconv.FormatBool(config.Data.DataV1.ReportClick),
|
ReportClick: strconv.FormatBool(data.DataV1.ReportClick),
|
||||||
ReportSent: strconv.FormatBool(config.Data.DataV1.ReportSent),
|
ReportSent: strconv.FormatBool(data.DataV1.ReportSent),
|
||||||
ClickedLink: config.Data.clickedLinkToString(),
|
ClickedLink: data.clickedLinkToString(),
|
||||||
FailureDetails: config.Data.DataV1.FailureDetails,
|
FailureDetails: data.DataV1.FailureDetails,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,7 @@ func TestConfigurationRecovery_default(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var builder = configstatus.ConfigRecoveryBuilder{}
|
var builder = configstatus.ConfigRecoveryBuilder{}
|
||||||
req := builder.New(config)
|
req := builder.New(config.Data)
|
||||||
|
|
||||||
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
|
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
|
||||||
require.Equal(t, "bridge_config_recovery", req.Event)
|
require.Equal(t, "bridge_config_recovery", req.Event)
|
||||||
@ -66,7 +66,7 @@ func TestConfigurationRecovery_fed(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var builder = configstatus.ConfigRecoveryBuilder{}
|
var builder = configstatus.ConfigRecoveryBuilder{}
|
||||||
req := builder.New(config)
|
req := builder.New(config.Data)
|
||||||
|
|
||||||
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
|
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
|
||||||
require.Equal(t, "bridge_config_recovery", req.Event)
|
require.Equal(t, "bridge_config_recovery", req.Event)
|
||||||
|
|||||||
@ -19,6 +19,7 @@ package configstatus
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigSuccessValues struct {
|
type ConfigSuccessValues struct {
|
||||||
@ -41,21 +42,18 @@ type ConfigSuccessData struct {
|
|||||||
|
|
||||||
type ConfigSuccessBuilder struct{}
|
type ConfigSuccessBuilder struct{}
|
||||||
|
|
||||||
func (*ConfigSuccessBuilder) New(config *ConfigurationStatus) ConfigSuccessData {
|
func (*ConfigSuccessBuilder) New(data *ConfigurationStatusData) ConfigSuccessData {
|
||||||
config.DataLock.RLock()
|
|
||||||
defer config.DataLock.RUnlock()
|
|
||||||
|
|
||||||
return ConfigSuccessData{
|
return ConfigSuccessData{
|
||||||
MeasurementGroup: "bridge.any.configuration",
|
MeasurementGroup: "bridge.any.configuration",
|
||||||
Event: "bridge_config_success",
|
Event: "bridge_config_success",
|
||||||
Values: ConfigSuccessValues{
|
Values: ConfigSuccessValues{
|
||||||
Duration: config.isPendingSinceMin(),
|
Duration: int(time.Since(data.DataV1.PendingSince).Minutes()),
|
||||||
},
|
},
|
||||||
Dimensions: ConfigSuccessDimensions{
|
Dimensions: ConfigSuccessDimensions{
|
||||||
Autoconf: config.Data.DataV1.Autoconf,
|
Autoconf: data.DataV1.Autoconf,
|
||||||
ReportClick: strconv.FormatBool(config.Data.DataV1.ReportClick),
|
ReportClick: strconv.FormatBool(data.DataV1.ReportClick),
|
||||||
ReportSent: strconv.FormatBool(config.Data.DataV1.ReportSent),
|
ReportSent: strconv.FormatBool(data.DataV1.ReportSent),
|
||||||
ClickedLink: config.Data.clickedLinkToString(),
|
ClickedLink: data.clickedLinkToString(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,7 @@ func TestConfigurationSuccess_default(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var builder = configstatus.ConfigSuccessBuilder{}
|
var builder = configstatus.ConfigSuccessBuilder{}
|
||||||
req := builder.New(config)
|
req := builder.New(config.Data)
|
||||||
|
|
||||||
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
|
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
|
||||||
require.Equal(t, "bridge_config_success", req.Event)
|
require.Equal(t, "bridge_config_success", req.Event)
|
||||||
@ -65,7 +65,7 @@ func TestConfigurationSuccess_fed(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var builder = configstatus.ConfigSuccessBuilder{}
|
var builder = configstatus.ConfigSuccessBuilder{}
|
||||||
req := builder.New(config)
|
req := builder.New(config.Data)
|
||||||
|
|
||||||
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
|
require.Equal(t, "bridge.any.configuration", req.MeasurementGroup)
|
||||||
require.Equal(t, "bridge_config_success", req.Event)
|
require.Equal(t, "bridge_config_success", req.Event)
|
||||||
|
|||||||
@ -175,7 +175,7 @@ type UsedSpaceChanged struct {
|
|||||||
|
|
||||||
UserID string
|
UserID string
|
||||||
|
|
||||||
UsedSpace uint64
|
UsedSpace int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (event UsedSpaceChanged) String() string {
|
func (event UsedSpaceChanged) String() string {
|
||||||
|
|||||||
@ -42,7 +42,6 @@ void GRPCQtProxy::connectSignals() {
|
|||||||
connect(this, &GRPCQtProxy::setIsTelemetryDisabledReceived, &settingsTab, &SettingsTab::setIsTelemetryDisabled);
|
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::installTLSCertificateReceived, &settingsTab, &SettingsTab::installTLSCertificate);
|
|
||||||
connect(this, &GRPCQtProxy::exportTLSCertificatesReceived, &settingsTab, &SettingsTab::exportTLSCertificates);
|
connect(this, &GRPCQtProxy::exportTLSCertificatesReceived, &settingsTab, &SettingsTab::exportTLSCertificates);
|
||||||
connect(this, &GRPCQtProxy::setIsStreamingReceived, &settingsTab, &SettingsTab::setIsStreaming);
|
connect(this, &GRPCQtProxy::setIsStreamingReceived, &settingsTab, &SettingsTab::setIsStreaming);
|
||||||
connect(this, &GRPCQtProxy::setClientPlatformReceived, &settingsTab, &SettingsTab::setClientPlatform);
|
connect(this, &GRPCQtProxy::setClientPlatformReceived, &settingsTab, &SettingsTab::setClientPlatform);
|
||||||
@ -120,13 +119,6 @@ void GRPCQtProxy::reportBug(QString const &osType, QString const &osVersion, QSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
//
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
void GRPCQtProxy::installTLSCertificate() {
|
|
||||||
emit installTLSCertificateReceived();
|
|
||||||
}
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \param[in] folderPath The folder path.
|
/// \param[in] folderPath The folder path.
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
|
|||||||
@ -45,7 +45,6 @@ public: // member functions.
|
|||||||
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.
|
||||||
void installTLSCertificate(); ///< Forwards a InstallTLScertificate call via a Qt signal.
|
|
||||||
void exportTLSCertificates(QString const &folderPath); //< Forward an 'ExportTLSCertificates' call via a Qt signal.
|
void exportTLSCertificates(QString const &folderPath); //< Forward an 'ExportTLSCertificates' call via a Qt signal.
|
||||||
void setIsStreaming(bool isStreaming); ///< Forward a isStreaming internal messages via a Qt signal.
|
void setIsStreaming(bool isStreaming); ///< Forward a isStreaming internal messages via a Qt signal.
|
||||||
void setClientPlatform(QString const &clientPlatform); ///< Forward a setClientPlatform call via a Qt signal.
|
void setClientPlatform(QString const &clientPlatform); ///< Forward a setClientPlatform call via a Qt signal.
|
||||||
@ -68,7 +67,6 @@ signals:
|
|||||||
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
|
||||||
void installTLSCertificateReceived(); ///< Signal for the InstallTLSCertificate gRPC call.
|
|
||||||
void exportTLSCertificatesReceived(QString const &folderPath); ///< Signal for the ExportTLSCertificates gRPC call.
|
void exportTLSCertificatesReceived(QString const &folderPath); ///< Signal for the ExportTLSCertificates gRPC call.
|
||||||
void setIsStreamingReceived(bool isStreaming); ///< Signal for the IsStreaming internal message.
|
void setIsStreamingReceived(bool isStreaming); ///< Signal for the IsStreaming internal message.
|
||||||
void setClientPlatformReceived(QString const &clientPlatform); ///< Signal for the SetClientPlatform gRPC call.
|
void setClientPlatformReceived(QString const &clientPlatform); ///< Signal for the SetClientPlatform gRPC call.
|
||||||
|
|||||||
@ -214,16 +214,6 @@ grpc::Status GRPCService::IsTelemetryDisabled(::grpc::ServerContext *, ::google:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \param[out] response The response.
|
|
||||||
/// \return The status for the call.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
Status GRPCService::GoOs(ServerContext *, Empty const*, StringValue *response) {
|
|
||||||
response->set_value(app().mainWindow().settingsTab().os().toStdString());
|
|
||||||
return Status::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \return The status for the call.
|
/// \return The status for the call.
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
@ -371,6 +361,22 @@ Status GRPCService::ReportBug(ServerContext *, ReportBugRequest const *request,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
/// \param[in] request The request
|
||||||
|
//****************************************************************************************************************************************************
|
||||||
|
Status GRPCService::ExportTLSCertificates(ServerContext *, StringValue const *request, Empty *response) {
|
||||||
|
SettingsTab &tab = app().mainWindow().settingsTab();
|
||||||
|
if (!tab.nextTLSCertExportWillSucceed()) {
|
||||||
|
qtProxy_.sendDelayedEvent(newGenericErrorEvent(grpc::TLS_CERT_EXPORT_ERROR));
|
||||||
|
}
|
||||||
|
if (!tab.nextTLSKeyExportWillSucceed()) {
|
||||||
|
qtProxy_.sendDelayedEvent(newGenericErrorEvent(grpc::TLS_KEY_EXPORT_ERROR));
|
||||||
|
}
|
||||||
|
qtProxy_.exportTLSCertificates(QString::fromStdString(request->value()));
|
||||||
|
return Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \param[in] request The request.
|
/// \param[in] request The request.
|
||||||
/// \return The status for the call.
|
/// \return The status for the call.
|
||||||
@ -400,7 +406,7 @@ Status GRPCService::Login(ServerContext *, LoginRequest const *request, Empty *)
|
|||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
if (usersTab.nextUserTwoPasswordsRequired()) {
|
if (usersTab.nextUserTwoPasswordsRequired()) {
|
||||||
qtProxy_.sendDelayedEvent(newLoginTwoPasswordsRequestedEvent(loginUsername_));
|
qtProxy_.sendDelayedEvent(newLoginTwoPasswordsRequestedEvent());
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,7 +431,7 @@ Status GRPCService::Login2FA(ServerContext *, LoginRequest const *request, Empty
|
|||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
if (usersTab.nextUserTwoPasswordsRequired()) {
|
if (usersTab.nextUserTwoPasswordsRequired()) {
|
||||||
qtProxy_.sendDelayedEvent(newLoginTwoPasswordsRequestedEvent(loginUsername_));
|
qtProxy_.sendDelayedEvent(newLoginTwoPasswordsRequestedEvent());
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -750,87 +756,10 @@ Status GRPCService::ConfigureUserAppleMail(ServerContext *, ConfigureAppleMailRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \param[in] request The request
|
|
||||||
/// \return The status for the call.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
Status GRPCService::ExportTLSCertificates(ServerContext *, StringValue const *request, Empty *response) {
|
|
||||||
app().log().debug(__FUNCTION__);
|
|
||||||
SettingsTab &tab = app().mainWindow().settingsTab();
|
|
||||||
if (!tab.nextTLSCertExportWillSucceed()) {
|
|
||||||
qtProxy_.sendDelayedEvent(newGenericErrorEvent(grpc::TLS_CERT_EXPORT_ERROR));
|
|
||||||
}
|
|
||||||
if (!tab.nextTLSKeyExportWillSucceed()) {
|
|
||||||
qtProxy_.sendDelayedEvent(newGenericErrorEvent(grpc::TLS_KEY_EXPORT_ERROR));
|
|
||||||
}
|
|
||||||
qtProxy_.exportTLSCertificates(QString::fromStdString(request->value()));
|
|
||||||
return Status::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \param[in] response The reponse.
|
|
||||||
/// \return The status for the call.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
Status GRPCService::IsTLSCertificateInstalled(ServerContext *, const Empty *request, BoolValue *response) {
|
|
||||||
app().log().debug(__FUNCTION__);
|
|
||||||
response->set_value(app().mainWindow().settingsTab().isTLSCertificateInstalled());
|
|
||||||
return Status::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
//
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
Status GRPCService::InstallTLSCertificate(ServerContext *, Empty const *, Empty *) {
|
|
||||||
app().log().debug(__FUNCTION__);
|
|
||||||
SPStreamEvent event;
|
|
||||||
qtProxy_.installTLSCertificate();
|
|
||||||
switch (app().mainWindow().settingsTab().nextTLSCertIntallResult()) {
|
|
||||||
case SettingsTab::TLSCertInstallResult::Success:
|
|
||||||
event = newCertificateInstallSuccessEvent();
|
|
||||||
break;
|
|
||||||
case SettingsTab::TLSCertInstallResult::Canceled:
|
|
||||||
event = newCertificateInstallCanceledEvent();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
event = newCertificateInstallFailedEvent();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
qtProxy_.sendDelayedEvent(event);
|
|
||||||
return Status::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \param[in] request The request.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
Status GRPCService::KBArticleClicked(::grpc::ServerContext *, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *) {
|
|
||||||
app().log().debug(QString("%1 - URL = %2").arg(__FUNCTION__, QString::fromStdString(request->value())));
|
|
||||||
return Status::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
//
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
Status GRPCService::ReportBugClicked(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::Empty *) {
|
|
||||||
app().log().debug(__FUNCTION__);
|
|
||||||
return Status::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \param[in] request The request.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
Status GRPCService::AutoconfigClicked(::grpc::ServerContext *, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *response) {
|
|
||||||
app().log().debug(QString("%1 - Client = %2").arg(__FUNCTION__, QString::fromStdString(request->value())));
|
|
||||||
return Status::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \param[in] request The request
|
/// \param[in] request The request
|
||||||
/// \param[in] writer The writer
|
/// \param[in] writer The writer
|
||||||
|
/// \return The status for the call.
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
Status GRPCService::RunEventStream(ServerContext *ctx, EventStreamRequest const *request, ServerWriter<StreamEvent> *writer) {
|
Status GRPCService::RunEventStream(ServerContext *ctx, EventStreamRequest const *request, ServerWriter<StreamEvent> *writer) {
|
||||||
app().log().debug(__FUNCTION__);
|
app().log().debug(__FUNCTION__);
|
||||||
@ -921,3 +850,4 @@ void GRPCService::finishLogin() {
|
|||||||
|
|
||||||
qtProxy_.sendDelayedEvent(newLoginFinishedEvent(user->id(), alreadyExist));
|
qtProxy_.sendDelayedEvent(newLoginFinishedEvent(user->id(), alreadyExist));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -53,7 +53,6 @@ public: // member functions.
|
|||||||
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 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 IsTelemetryDisabled(::grpc::ServerContext *, ::google::protobuf::Empty const *request, ::google::protobuf::BoolValue *response) override;
|
||||||
grpc::Status GoOs(::grpc::ServerContext *, ::google::protobuf::Empty const*, ::google::protobuf::StringValue *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;
|
||||||
@ -65,6 +64,7 @@ public: // member functions.
|
|||||||
grpc::Status ColorSchemeName(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::StringValue *response) override;
|
grpc::Status ColorSchemeName(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::StringValue *response) override;
|
||||||
grpc::Status CurrentEmailClient(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::StringValue *response) override;
|
grpc::Status CurrentEmailClient(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::StringValue *response) override;
|
||||||
grpc::Status ReportBug(::grpc::ServerContext *, ::grpc::ReportBugRequest const *request, ::google::protobuf::Empty *) override;
|
grpc::Status ReportBug(::grpc::ServerContext *, ::grpc::ReportBugRequest const *request, ::google::protobuf::Empty *) override;
|
||||||
|
grpc::Status ExportTLSCertificates(::grpc::ServerContext *context, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *response) override;
|
||||||
grpc::Status ForceLauncher(::grpc::ServerContext *, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *) override;
|
grpc::Status ForceLauncher(::grpc::ServerContext *, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *) override;
|
||||||
grpc::Status SetMainExecutable(::grpc::ServerContext *, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *) override;
|
grpc::Status SetMainExecutable(::grpc::ServerContext *, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *) override;
|
||||||
grpc::Status Login(::grpc::ServerContext *, ::grpc::LoginRequest const *request, ::google::protobuf::Empty *) override;
|
grpc::Status Login(::grpc::ServerContext *, ::grpc::LoginRequest const *request, ::google::protobuf::Empty *) override;
|
||||||
@ -93,12 +93,6 @@ public: // member functions.
|
|||||||
grpc::Status LogoutUser(::grpc::ServerContext *, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *) override;
|
grpc::Status LogoutUser(::grpc::ServerContext *, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *) override;
|
||||||
grpc::Status RemoveUser(::grpc::ServerContext *, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *) override;
|
grpc::Status RemoveUser(::grpc::ServerContext *, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *) override;
|
||||||
grpc::Status ConfigureUserAppleMail(::grpc::ServerContext *, ::grpc::ConfigureAppleMailRequest const *request, ::google::protobuf::Empty *) override;
|
grpc::Status ConfigureUserAppleMail(::grpc::ServerContext *, ::grpc::ConfigureAppleMailRequest const *request, ::google::protobuf::Empty *) override;
|
||||||
grpc::Status IsTLSCertificateInstalled(::grpc::ServerContext *, ::google::protobuf::Empty const*, ::google::protobuf::BoolValue *response) override;
|
|
||||||
grpc::Status InstallTLSCertificate(::grpc::ServerContext *, ::google::protobuf::Empty const*, ::google::protobuf::Empty *) override;
|
|
||||||
grpc::Status ExportTLSCertificates(::grpc::ServerContext *, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *) override;
|
|
||||||
grpc::Status ReportBugClicked(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::Empty *) override;
|
|
||||||
grpc::Status AutoconfigClicked(::grpc::ServerContext *context, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *) override;
|
|
||||||
grpc::Status KBArticleClicked(::grpc::ServerContext *, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *) override;
|
|
||||||
grpc::Status RunEventStream(::grpc::ServerContext *ctx, ::grpc::EventStreamRequest const *request, ::grpc::ServerWriter<::grpc::StreamEvent> *writer) override;
|
grpc::Status RunEventStream(::grpc::ServerContext *ctx, ::grpc::EventStreamRequest const *request, ::grpc::ServerWriter<::grpc::StreamEvent> *writer) override;
|
||||||
grpc::Status StopEventStream(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::Empty *) override;
|
grpc::Status StopEventStream(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::Empty *) override;
|
||||||
bool sendEvent(bridgepp::SPStreamEvent const &event); ///< Queue an event for sending through the event stream.
|
bool sendEvent(bridgepp::SPStreamEvent const &event); ///< Queue an event for sending through the event stream.
|
||||||
|
|||||||
@ -285,20 +285,11 @@ void SettingsTab::setBugReport(QString const &osType, QString const &osVersion,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
//
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
void SettingsTab::installTLSCertificate() {
|
|
||||||
ui_.labelLastTLSCertInstall->setText(QString("Last install: %1").arg(QDateTime::currentDateTime().toString(Qt::ISODateWithMs)));
|
|
||||||
ui_.checkTLSCertIsInstalled->setChecked(this->nextTLSCertIntallResult() == TLSCertInstallResult::Success);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \param[in] folderPath The folder path.
|
/// \param[in] folderPath The folder path.
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
void SettingsTab::exportTLSCertificates(QString const &folderPath) {
|
void SettingsTab::exportTLSCertificates(QString const &folderPath) {
|
||||||
ui_.labeLastTLSCertExport->setText(QString("%1 Export to %2")
|
ui_.labeLastTLSCertsExport->setText(QString("%1 Export to %2")
|
||||||
.arg(QDateTime::currentDateTime().toString(Qt::ISODateWithMs))
|
.arg(QDateTime::currentDateTime().toString(Qt::ISODateWithMs))
|
||||||
.arg(folderPath));
|
.arg(folderPath));
|
||||||
}
|
}
|
||||||
@ -312,22 +303,6 @@ bool SettingsTab::nextBugReportWillSucceed() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \return the state of the 'TLS Certificate is installed' check box.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
bool SettingsTab::isTLSCertificateInstalled() const {
|
|
||||||
return ui_.checkTLSCertIsInstalled->isChecked();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \return The value for the 'Next TLS cert install result'.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
SettingsTab::TLSCertInstallResult SettingsTab::nextTLSCertIntallResult() const {
|
|
||||||
return TLSCertInstallResult(ui_.comboNextTLSCertInstallResult->currentIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \return true if the 'Next TLS key export will succeed' check box is checked
|
/// \return true if the 'Next TLS key export will succeed' check box is checked
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
@ -530,11 +505,4 @@ void SettingsTab::resetUI() {
|
|||||||
ui_.comboCacheError->setCurrentIndex(0);
|
ui_.comboCacheError->setCurrentIndex(0);
|
||||||
|
|
||||||
ui_.checkAutomaticUpdate->setChecked(true);
|
ui_.checkAutomaticUpdate->setChecked(true);
|
||||||
|
|
||||||
ui_.checkTLSCertIsInstalled->setChecked(false);
|
|
||||||
ui_.comboNextTLSCertInstallResult->setCurrentIndex(0);
|
|
||||||
ui_.checkTLSCertExportWillSucceed->setChecked(true);
|
|
||||||
ui_.checkTLSKeyExportWillSucceed->setChecked(true);
|
|
||||||
ui_.labeLastTLSCertExport->setText("Last export: never");
|
|
||||||
ui_.labelLastTLSCertInstall->setText("Last install: never");
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,13 +28,6 @@
|
|||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
class SettingsTab : public QWidget {
|
class SettingsTab : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public: // data types.
|
|
||||||
enum class TLSCertInstallResult {
|
|
||||||
Success = 0,
|
|
||||||
Canceled = 1,
|
|
||||||
Failure = 2
|
|
||||||
}; ///< Enumberation for the result of a TLS certificate installation.
|
|
||||||
|
|
||||||
public: // member functions.
|
public: // member functions.
|
||||||
explicit SettingsTab(QWidget *parent = nullptr); ///< Default constructor.
|
explicit SettingsTab(QWidget *parent = nullptr); ///< Default constructor.
|
||||||
SettingsTab(SettingsTab const &) = delete; ///< Disabled copy-constructor.
|
SettingsTab(SettingsTab const &) = delete; ///< Disabled copy-constructor.
|
||||||
@ -61,8 +54,6 @@ public: // member functions.
|
|||||||
QString dependencyLicenseLink() const; ///< Get the content of the 'Dependency License Link' edit.
|
QString dependencyLicenseLink() const; ///< Get the content of the 'Dependency License Link' edit.
|
||||||
QString landingPageLink() const; ///< Get the content of the 'Landing Page Link' edit.
|
QString landingPageLink() const; ///< Get the content of the 'Landing Page Link' edit.
|
||||||
bool nextBugReportWillSucceed() const; ///< Get the status of the 'Next Bug Report Will Fail' check box.
|
bool nextBugReportWillSucceed() const; ///< Get the status of the 'Next Bug Report Will Fail' check box.
|
||||||
bool isTLSCertificateInstalled() const; ///< Get the status of the 'TLS Certificate is installed' check box.
|
|
||||||
TLSCertInstallResult nextTLSCertIntallResult() const; ///< Get the value of the 'Next TLS Certificate install result' combo box.
|
|
||||||
bool nextTLSCertExportWillSucceed() const; ///< Get the status of the 'Next TLS Cert export will succeed' check box.
|
bool nextTLSCertExportWillSucceed() const; ///< Get the status of the 'Next TLS Cert export will succeed' check box.
|
||||||
bool nextTLSKeyExportWillSucceed() const; ///< Get the status of the 'Next TLS Key export will succeed' check box.
|
bool nextTLSKeyExportWillSucceed() const; ///< Get the status of the 'Next TLS Key export will succeed' check box.
|
||||||
QString hostname() const; ///< Get the value of the 'Hostname' edit.
|
QString hostname() const; ///< Get the value of the 'Hostname' edit.
|
||||||
@ -88,7 +79,6 @@ public slots:
|
|||||||
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.
|
||||||
void installTLSCertificate(); ///< Install the TLS certificate.
|
|
||||||
void exportTLSCertificates(QString const &folderPath); ///< Export the TLS certificates.
|
void exportTLSCertificates(QString const &folderPath); ///< Export the TLS certificates.
|
||||||
void setMailServerSettings(qint32 imapPort, qint32 smtpPort, bool useSSLForIMAP, bool useSSLForSMTP); ///< Change the mail server settings.
|
void setMailServerSettings(qint32 imapPort, qint32 smtpPort, bool useSSLForIMAP, bool useSSLForSMTP); ///< Change the mail server settings.
|
||||||
void setIsDoHEnabled(bool enabled); ///< Set the value for the 'DoH Enabled' check box.
|
void setIsDoHEnabled(bool enabled); ///< Set the value for the 'DoH Enabled' check box.
|
||||||
|
|||||||
@ -370,7 +370,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupCert">
|
<widget class="QGroupBox" name="groupCache_2">
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>0</width>
|
<width>0</width>
|
||||||
@ -380,81 +380,34 @@
|
|||||||
<property name="title">
|
<property name="title">
|
||||||
<string>TLS Certficates</string>
|
<string>TLS Certficates</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,1">
|
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||||
<item row="0" column="0">
|
<item>
|
||||||
<widget class="QCheckBox" name="checkTLSCertIsInstalled">
|
<widget class="QLabel" name="labeLastTLSCertsExport">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Certificate is installed</string>
|
<string>Last Export: Never</string>
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item>
|
||||||
<widget class="QCheckBox" name="checkTLSCertExportWillSucceed">
|
<widget class="QCheckBox" name="checkTLSCertExportWillSucceed">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Certificate export will succeed</string>
|
<string>TLS certificate export will succeed</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="checked">
|
<property name="checked">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item>
|
||||||
<widget class="QCheckBox" name="checkTLSKeyExportWillSucceed">
|
<widget class="QCheckBox" name="checkTLSKeyExportWillSucceed">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Key export will succeed</string>
|
<string>TLS private key export will succeed</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="checked">
|
<property name="checked">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QLabel" name="labeLastTLSCertExport">
|
|
||||||
<property name="text">
|
|
||||||
<string>Last Export: never</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="labelLastTLSCertInstall">
|
|
||||||
<property name="text">
|
|
||||||
<string>Last install: never</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_14" stretch="0,1">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="labelNextInstall">
|
|
||||||
<property name="text">
|
|
||||||
<string>Next install will</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QComboBox" name="comboNextTLSCertInstallResult">
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Succeed</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Be Canceled</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Fail</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@ -40,8 +40,7 @@ using namespace bridgepp;
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
||||||
QString const bugReportFile = ":qml/Resources/bug_report_flow.json";
|
QString const bugReportFile = ":qml/Resources/bug_report_flow.json";
|
||||||
QString const bridgeKBUrl = "https://proton.me/support/bridge"; ///< The URL for the root of the bridge knowledge base.
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -279,30 +278,6 @@ void QMLBackend::clearAnswers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \return true iff the Bridge TLS certificate is installed.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
bool QMLBackend::isTLSCertificateInstalled() {
|
|
||||||
HANDLE_EXCEPTION_RETURN_BOOL(
|
|
||||||
bool v = false;
|
|
||||||
app().grpc().isTLSCertificateInstalled(v);
|
|
||||||
return v;
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \param[in] url The URL of the knowledge base article. If empty/invalid, the home page for the Bridge knowledge base is opened.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
void QMLBackend::openKBArticle(QString const &url) {
|
|
||||||
HANDLE_EXCEPTION(
|
|
||||||
QString const u = url.isEmpty() ? bridgeKBUrl : url;
|
|
||||||
QDesktopServices::openUrl(u);
|
|
||||||
emit notifyKBArticleClicked(u);
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \return The value for the 'showOnStartup' property.
|
/// \return The value for the 'showOnStartup' property.
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
@ -966,15 +941,6 @@ void QMLBackend::reportBug(QString const &category, QString const &description,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
//
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
void QMLBackend::installTLSCertificate() {
|
|
||||||
HANDLE_EXCEPTION(
|
|
||||||
app().grpc().installTLSCertificate();
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
//
|
//
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
@ -1301,9 +1267,6 @@ void QMLBackend::connectGrpcEvents() {
|
|||||||
connect(client, &GRPCClient::reportBugSuccess, this, &QMLBackend::bugReportSendSuccess);
|
connect(client, &GRPCClient::reportBugSuccess, this, &QMLBackend::bugReportSendSuccess);
|
||||||
connect(client, &GRPCClient::reportBugFallback, this, &QMLBackend::bugReportSendFallback);
|
connect(client, &GRPCClient::reportBugFallback, this, &QMLBackend::bugReportSendFallback);
|
||||||
connect(client, &GRPCClient::reportBugError, this, &QMLBackend::bugReportSendError);
|
connect(client, &GRPCClient::reportBugError, this, &QMLBackend::bugReportSendError);
|
||||||
connect(client, &GRPCClient::certificateInstallSuccess, this, &QMLBackend::certificateInstallSuccess);
|
|
||||||
connect(client, &GRPCClient::certificateInstallCanceled, this, &QMLBackend::certificateInstallCanceled);
|
|
||||||
connect(client, &GRPCClient::certificateInstallFailed, this, &QMLBackend::certificateInstallFailed);
|
|
||||||
connect(client, &GRPCClient::showMainWindow, [&]() { this->showMainWindow("gRPC showMainWindow event"); });
|
connect(client, &GRPCClient::showMainWindow, [&]() { this->showMainWindow("gRPC showMainWindow event"); });
|
||||||
|
|
||||||
// cache events
|
// cache events
|
||||||
|
|||||||
@ -64,8 +64,6 @@ public: // member functions.
|
|||||||
Q_INVOKABLE QString getQuestionAnswer(quint8 questionId) const; ///< Get the answer for a given question.
|
Q_INVOKABLE QString getQuestionAnswer(quint8 questionId) const; ///< Get the answer for a given question.
|
||||||
Q_INVOKABLE QString collectAnswers(quint8 categoryId) const; ///< Collect answer for a given set of questions.
|
Q_INVOKABLE QString collectAnswers(quint8 categoryId) const; ///< Collect answer for a given set of questions.
|
||||||
Q_INVOKABLE void clearAnswers(); ///< Clear all collected answers.
|
Q_INVOKABLE void clearAnswers(); ///< Clear all collected answers.
|
||||||
Q_INVOKABLE bool isTLSCertificateInstalled(); ///< Check if the bridge certificate is installed in the OS keychain.
|
|
||||||
Q_INVOKABLE void openKBArticle(QString const & url = QString()); ///< Open a knowledge base article.
|
|
||||||
|
|
||||||
public: // Qt/QML properties. Note that the NOTIFY-er signal is required even for read-only properties (QML warning otherwise)
|
public: // Qt/QML properties. Note that the NOTIFY-er signal is required even for read-only properties (QML warning otherwise)
|
||||||
Q_PROPERTY(bool showOnStartup READ showOnStartup NOTIFY showOnStartupChanged)
|
Q_PROPERTY(bool showOnStartup READ showOnStartup NOTIFY showOnStartupChanged)
|
||||||
@ -197,7 +195,6 @@ public slots: // slot for signals received from QML -> To be forwarded to Bridge
|
|||||||
void installUpdate() const; ///< Slot for the update install.
|
void installUpdate() const; ///< Slot for the update install.
|
||||||
void triggerReset() const; ///< Slot for the triggering of reset.
|
void triggerReset() const; ///< Slot for the triggering of reset.
|
||||||
void reportBug(QString const &category, QString const &description, QString const &address, QString const &emailClient, bool includeLogs) const; ///< Slot for the bug report.
|
void reportBug(QString const &category, QString const &description, QString const &address, QString const &emailClient, bool includeLogs) const; ///< Slot for the bug report.
|
||||||
void installTLSCertificate(); ///< Installs the Bridge TLS certificate in the Keychain.
|
|
||||||
void exportTLSCertificates() const; ///< Slot for the export of the TLS certificates.
|
void exportTLSCertificates() const; ///< Slot for the export of the TLS certificates.
|
||||||
void onResetFinished(); ///< Slot for the reset finish signal.
|
void onResetFinished(); ///< Slot for the reset finish signal.
|
||||||
void onVersionChanged(); ///< Slot for the version change signal.
|
void onVersionChanged(); ///< Slot for the version change signal.
|
||||||
@ -234,7 +231,7 @@ signals: // Signals received from the Go backend, to be forwarded to QML
|
|||||||
void login2FARequested(QString const &username); ///< Signal for the 'login2FARequested' gRPC stream event.
|
void login2FARequested(QString const &username); ///< Signal for the 'login2FARequested' gRPC stream event.
|
||||||
void login2FAError(QString const &errorMsg); ///< Signal for the 'login2FAError' gRPC stream event.
|
void login2FAError(QString const &errorMsg); ///< Signal for the 'login2FAError' gRPC stream event.
|
||||||
void login2FAErrorAbort(QString const &errorMsg); ///< Signal for the 'login2FAErrorAbort' gRPC stream event.
|
void login2FAErrorAbort(QString const &errorMsg); ///< Signal for the 'login2FAErrorAbort' gRPC stream event.
|
||||||
void login2PasswordRequested(QString const &username); ///< Signal for the 'login2PasswordRequested' gRPC stream event.
|
void login2PasswordRequested(); ///< Signal for the 'login2PasswordRequested' gRPC stream event.
|
||||||
void login2PasswordError(QString const &errorMsg); ///< Signal for the 'login2PasswordError' gRPC stream event.
|
void login2PasswordError(QString const &errorMsg); ///< Signal for the 'login2PasswordError' gRPC stream event.
|
||||||
void login2PasswordErrorAbort(QString const &errorMsg); ///< Signal for the 'login2PasswordErrorAbort' gRPC stream event.
|
void login2PasswordErrorAbort(QString const &errorMsg); ///< Signal for the 'login2PasswordErrorAbort' gRPC stream event.
|
||||||
void loginFinished(int index, bool wasSignedOut); ///< Signal for the 'loginFinished' gRPC stream event.
|
void loginFinished(int index, bool wasSignedOut); ///< Signal for the 'loginFinished' gRPC stream event.
|
||||||
@ -271,13 +268,10 @@ signals: // Signals received from the Go backend, to be forwarded to QML
|
|||||||
void bugReportSendSuccess(); ///< Signal for the 'bugReportSendSuccess' gRPC stream event.
|
void bugReportSendSuccess(); ///< Signal for the 'bugReportSendSuccess' gRPC stream event.
|
||||||
void bugReportSendFallback(); ///< Signal for the 'bugReportSendFallback' gRPC stream event.
|
void bugReportSendFallback(); ///< Signal for the 'bugReportSendFallback' gRPC stream event.
|
||||||
void bugReportSendError(); ///< Signal for the 'bugReportSendError' gRPC stream event.
|
void bugReportSendError(); ///< Signal for the 'bugReportSendError' gRPC stream event.
|
||||||
void certificateInstallSuccess(); ///< Signal for the 'certificateInstallSuccess' gRPC stream event.
|
|
||||||
void certificateInstallCanceled(); ///< Signal for the 'certificateInstallCanceled' gRPC stream event.
|
|
||||||
void certificateInstallFailed(); /// Signal for the 'certificateInstallFailed' 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 showHelp(); ///< Signal for the 'showHelp' event (from the context menu).
|
||||||
void showSettings(); ///< Signal for the 'showSettings' event (from the context menu).
|
void showSettings(); ///< Signal for the 'showHelp' event (from the context menu).
|
||||||
void selectUser(QString const& userID, bool forceShowWindow); ///< Signal emitted in order to selected a user with a given ID in the list.
|
void selectUser(QString const& userID, bool forceShowWindow); ///< 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 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.
|
||||||
|
|||||||
@ -5,6 +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/bridgeqml.qmlproject</file>
|
||||||
<file>qml/BugCategoryView.qml</file>
|
<file>qml/BugCategoryView.qml</file>
|
||||||
<file>qml/BugQuestionView.qml</file>
|
<file>qml/BugQuestionView.qml</file>
|
||||||
<file>qml/BugReportFlow.qml</file>
|
<file>qml/BugReportFlow.qml</file>
|
||||||
@ -19,11 +20,9 @@
|
|||||||
<file>qml/icons/ic-alert.svg</file>
|
<file>qml/icons/ic-alert.svg</file>
|
||||||
<file>qml/icons/ic-apple-mail.svg</file>
|
<file>qml/icons/ic-apple-mail.svg</file>
|
||||||
<file>qml/icons/ic-arrow-left.svg</file>
|
<file>qml/icons/ic-arrow-left.svg</file>
|
||||||
<file>qml/icons/ic-bridge.svg</file>
|
|
||||||
<file>qml/icons/ic-card-identity.svg</file>
|
<file>qml/icons/ic-card-identity.svg</file>
|
||||||
<file>qml/icons/ic-check.svg</file>
|
<file>qml/icons/ic-check.svg</file>
|
||||||
<file>qml/icons/ic-chevron-down.svg</file>
|
<file>qml/icons/ic-chevron-down.svg</file>
|
||||||
<file>qml/icons/ic-chevron-left.svg</file>
|
|
||||||
<file>qml/icons/ic-chevron-right.svg</file>
|
<file>qml/icons/ic-chevron-right.svg</file>
|
||||||
<file>qml/icons/ic-chevron-up.svg</file>
|
<file>qml/icons/ic-chevron-up.svg</file>
|
||||||
<file>qml/icons/ic-cog-wheel.svg</file>
|
<file>qml/icons/ic-cog-wheel.svg</file>
|
||||||
@ -50,18 +49,13 @@
|
|||||||
<file>qml/icons/ic-success.svg</file>
|
<file>qml/icons/ic-success.svg</file>
|
||||||
<file>qml/icons/ic-three-dots-vertical.svg</file>
|
<file>qml/icons/ic-three-dots-vertical.svg</file>
|
||||||
<file>qml/icons/ic-trash.svg</file>
|
<file>qml/icons/ic-trash.svg</file>
|
||||||
<file>qml/icons/ic-warning-orange.svg</file>
|
|
||||||
<file>qml/icons/img-client-config-selector.svg</file>
|
|
||||||
<file>qml/icons/img-client-config-success.svg</file>
|
|
||||||
<file>qml/icons/img-macos-cert-screenshot.png</file>
|
|
||||||
<file>qml/icons/img-macos-profile-screenshot.png</file>
|
|
||||||
<file>qml/icons/img-mail-clients.svg</file>
|
|
||||||
<file>qml/icons/img-mail-logo-wordmark-dark.svg</file>
|
|
||||||
<file>qml/icons/img-mail-logo-wordmark.svg</file>
|
|
||||||
<file>qml/icons/img-proton-logos.png</file>
|
<file>qml/icons/img-proton-logos.png</file>
|
||||||
<file>qml/icons/img-proton-logos.svg</file>
|
<file>qml/icons/img-proton-logos.svg</file>
|
||||||
<file>qml/icons/img-splash.png</file>
|
<file>qml/icons/img-splash.png</file>
|
||||||
<file>qml/icons/img-splash.svg</file>
|
<file>qml/icons/img-splash.svg</file>
|
||||||
|
<file>qml/icons/img-welcome-dark.png</file>
|
||||||
|
<file>qml/icons/img-welcome-dark.svg</file>
|
||||||
|
<file>qml/icons/img-welcome.png</file>
|
||||||
<file>qml/icons/img-welcome.svg</file>
|
<file>qml/icons/img-welcome.svg</file>
|
||||||
<file>qml/icons/Loader_16.svg</file>
|
<file>qml/icons/Loader_16.svg</file>
|
||||||
<file>qml/icons/Loader_48.svg</file>
|
<file>qml/icons/Loader_48.svg</file>
|
||||||
@ -81,7 +75,6 @@
|
|||||||
<file>qml/KeychainSettings.qml</file>
|
<file>qml/KeychainSettings.qml</file>
|
||||||
<file>qml/LocalCacheSettings.qml</file>
|
<file>qml/LocalCacheSettings.qml</file>
|
||||||
<file>qml/MainWindow.qml</file>
|
<file>qml/MainWindow.qml</file>
|
||||||
<file>qml/NoAccountView.qml</file>
|
|
||||||
<file>qml/NotificationDialog.qml</file>
|
<file>qml/NotificationDialog.qml</file>
|
||||||
<file>qml/NotificationPopups.qml</file>
|
<file>qml/NotificationPopups.qml</file>
|
||||||
<file>qml/Notifications/Notification.qml</file>
|
<file>qml/Notifications/Notification.qml</file>
|
||||||
@ -97,7 +90,6 @@
|
|||||||
<file>qml/Proton/ComboBox.qml</file>
|
<file>qml/Proton/ComboBox.qml</file>
|
||||||
<file>qml/Proton/Dialog.qml</file>
|
<file>qml/Proton/Dialog.qml</file>
|
||||||
<file>qml/Proton/Label.qml</file>
|
<file>qml/Proton/Label.qml</file>
|
||||||
<file>qml/Proton/LinkLabel.qml</file>
|
|
||||||
<file>qml/Proton/Menu.qml</file>
|
<file>qml/Proton/Menu.qml</file>
|
||||||
<file>qml/Proton/MenuItem.qml</file>
|
<file>qml/Proton/MenuItem.qml</file>
|
||||||
<file>qml/Proton/Popup.qml</file>
|
<file>qml/Proton/Popup.qml</file>
|
||||||
@ -109,26 +101,14 @@
|
|||||||
<file>qml/Proton/TextField.qml</file>
|
<file>qml/Proton/TextField.qml</file>
|
||||||
<file>qml/Proton/Toggle.qml</file>
|
<file>qml/Proton/Toggle.qml</file>
|
||||||
<file>qml/QuestionItem.qml</file>
|
<file>qml/QuestionItem.qml</file>
|
||||||
<file>qml/Resources/bug_report_flow.json</file>
|
<file>qml/Resources/bug_report_flow.json</file>
|
||||||
<file>qml/Resources/Help/Template.html</file>
|
|
||||||
<file>qml/Resources/Help/WhyBridge.html</file>
|
|
||||||
<file>qml/Resources/Help/WhyCertificate.html</file>
|
|
||||||
<file>qml/Resources/Help/WhyProfileWarning.html</file>
|
|
||||||
<file>qml/SettingsItem.qml</file>
|
<file>qml/SettingsItem.qml</file>
|
||||||
<file>qml/SettingsView.qml</file>
|
<file>qml/SettingsView.qml</file>
|
||||||
<file>qml/SetupWizard/ClientListItem.qml</file>
|
<file>qml/SetupGuide.qml</file>
|
||||||
<file>qml/SetupWizard/LeftPane.qml</file>
|
<file>qml/SignIn.qml</file>
|
||||||
<file>qml/SetupWizard/ClientConfigAppleMail.qml</file>
|
|
||||||
<file>qml/SetupWizard/ClientConfigEnd.qml</file>
|
|
||||||
<file>qml/SetupWizard/ClientConfigParameters.qml</file>
|
|
||||||
<file>qml/SetupWizard/ClientConfigSelector.qml</file>
|
|
||||||
<file>qml/SetupWizard/HelpButton.qml</file>
|
|
||||||
<file>qml/SetupWizard/SetupWizard.qml</file>
|
|
||||||
<file>qml/SetupWizard/Login.qml</file>
|
|
||||||
<file>qml/SetupWizard/Onboarding.qml</file>
|
|
||||||
<file>qml/SetupWizard/StepDescriptionBox.qml</file>
|
|
||||||
<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/WelcomeGuide.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@ -262,7 +262,7 @@ void UserList::onUsedBytesChanged(QString const &userID, qint64 usedBytes) {
|
|||||||
void UserList::onSyncStarted(QString const &userID) {
|
void UserList::onSyncStarted(QString const &userID) {
|
||||||
int const index = this->rowOfUserID(userID);
|
int const index = this->rowOfUserID(userID);
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
app().log().error(QString("Received syncStarted event for unknown userID %1").arg(userID));
|
app().log().error(QString("Received onSyncStarted event for unknown userID %1").arg(userID));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
users_[index]->setIsSyncing(true);
|
users_[index]->setIsSyncing(true);
|
||||||
@ -275,7 +275,7 @@ void UserList::onSyncStarted(QString const &userID) {
|
|||||||
void UserList::onSyncFinished(QString const &userID) {
|
void UserList::onSyncFinished(QString const &userID) {
|
||||||
int const index = this->rowOfUserID(userID);
|
int const index = this->rowOfUserID(userID);
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
app().log().error(QString("Received syncFinished event for unknown userID %1").arg(userID));
|
app().log().error(QString("Received onSyncFinished event for unknown userID %1").arg(userID));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
users_[index]->setIsSyncing(false);
|
users_[index]->setIsSyncing(false);
|
||||||
@ -293,7 +293,7 @@ void UserList::onSyncProgress(QString const &userID, double progress, float elap
|
|||||||
Q_UNUSED(remainingMs)
|
Q_UNUSED(remainingMs)
|
||||||
int const index = this->rowOfUserID(userID);
|
int const index = this->rowOfUserID(userID);
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
app().log().error(QString("Received syncProgress event for unknown userID %1").arg(userID));
|
app().log().error(QString("Received onSyncFinished event for unknown userID %1").arg(userID));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
users_[index]->setSyncProgress(progress);
|
users_[index]->setSyncProgress(progress);
|
||||||
|
|||||||
@ -22,7 +22,7 @@ Item {
|
|||||||
LargeView
|
LargeView
|
||||||
}
|
}
|
||||||
|
|
||||||
property var _spacing: 12
|
property var _spacing: 12 * ProtonStyle.px
|
||||||
property ColorScheme colorScheme
|
property ColorScheme colorScheme
|
||||||
property color progressColor: {
|
property color progressColor: {
|
||||||
if (!root.enabled)
|
if (!root.enabled)
|
||||||
@ -154,7 +154,7 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Item {
|
Item {
|
||||||
implicitHeight: root.type === AccountDelegate.LargeView ? 6 : 0
|
implicitHeight: root.type === AccountDelegate.LargeView ? 6 * ProtonStyle.px : 0
|
||||||
}
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
@ -222,15 +222,15 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Item {
|
Item {
|
||||||
implicitHeight: root.type === AccountDelegate.LargeView ? 3 : 0
|
implicitHeight: root.type === AccountDelegate.LargeView ? 3 * ProtonStyle.px : 0
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: progress_bar
|
id: progress_bar
|
||||||
color: root.colorScheme.border_weak
|
color: root.colorScheme.border_weak
|
||||||
height: 4
|
height: 4 * ProtonStyle.px
|
||||||
radius: ProtonStyle.progress_bar_radius
|
radius: ProtonStyle.progress_bar_radius
|
||||||
visible: root.user ? root.type === AccountDelegate.LargeView : false
|
visible: root.user ? root.type === AccountDelegate.LargeView : false
|
||||||
width: 140
|
width: 140 * ProtonStyle.px
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: progress_bar_filled
|
id: progress_bar_filled
|
||||||
|
|||||||
@ -23,14 +23,13 @@ Item {
|
|||||||
property int _detailsMargin: 25
|
property int _detailsMargin: 25
|
||||||
property int _lineThickness: 1
|
property int _lineThickness: 1
|
||||||
property int _spacing: 20
|
property int _spacing: 20
|
||||||
property int _buttonSpacing: 8
|
|
||||||
property int _topMargin: 32
|
property int _topMargin: 32
|
||||||
property ColorScheme colorScheme
|
property ColorScheme colorScheme
|
||||||
property var notifications
|
property var notifications
|
||||||
property var user
|
property var user
|
||||||
|
|
||||||
signal showClientConfigurator(var user, string address, bool justLoggedIn)
|
signal showSetupGuide(var user, string address)
|
||||||
signal showLogin(var username)
|
signal showSignIn
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@ -64,7 +63,7 @@ Item {
|
|||||||
// account delegate with action buttons
|
// account delegate with action buttons
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: _topMargin
|
Layout.topMargin: _topMargin
|
||||||
spacing: _buttonSpacing
|
|
||||||
AccountDelegate {
|
AccountDelegate {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
@ -93,9 +92,9 @@ Item {
|
|||||||
visible: root.user ? (root.user.state === EUserState.SignedOut) : false
|
visible: root.user ? (root.user.state === EUserState.SignedOut) : false
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (user) {
|
if (!root.user)
|
||||||
root.showLogin(user.primaryEmailOrUsername());
|
return;
|
||||||
}
|
root.showSignIn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
@ -119,18 +118,18 @@ Item {
|
|||||||
}
|
}
|
||||||
SettingsItem {
|
SettingsItem {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
actionText: qsTr("Configure email client")
|
actionText: qsTr("Configure")
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
description: qsTr("Using the mailbox details below (re)configure your client.")
|
description: qsTr("Using the mailbox details below (re)configure your client.")
|
||||||
showSeparator: splitMode.visible
|
showSeparator: splitMode.visible
|
||||||
text: qsTr("Email clients")
|
text: qsTr("Email clients")
|
||||||
type: SettingsItem.PrimaryButton
|
type: SettingsItem.Button
|
||||||
visible: _connected && ((!root.user.splitMode) || (root.user.addresses.length === 1))
|
visible: _connected && (!root.user.splitMode) || (root.user.addresses.length === 1)
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!root.user)
|
if (!root.user)
|
||||||
return;
|
return;
|
||||||
root.showClientConfigurator(root.user, user.addresses[0], false);
|
root.showSetupGuide(root.user, user.addresses[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SettingsItem {
|
SettingsItem {
|
||||||
@ -166,13 +165,13 @@ Item {
|
|||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
secondary: false
|
secondary: true
|
||||||
text: qsTr("Configure email client")
|
text: qsTr("Configure")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!root.user)
|
if (!root.user)
|
||||||
return;
|
return;
|
||||||
root.showClientConfigurator(root.user, addressSelector.displayText, false);
|
root.showSetupGuide(root.user, addressSelector.displayText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,6 +40,7 @@ QtObject {
|
|||||||
function onHideMainWindow() {
|
function onHideMainWindow() {
|
||||||
mainWindow.hide();
|
mainWindow.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
target: Backend
|
target: Backend
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,6 @@ Rectangle {
|
|||||||
|
|
||||||
property int _margin: 24
|
property int _margin: 24
|
||||||
property ColorScheme colorScheme
|
property ColorScheme colorScheme
|
||||||
property bool highlightPassword
|
|
||||||
property string hostname
|
property string hostname
|
||||||
property string password
|
property string password
|
||||||
property string port
|
property string port
|
||||||
@ -69,8 +68,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
ConfigurationItem {
|
ConfigurationItem {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
label: highlightPassword ? qsTr("Use this password") : qsTr("Password")
|
label: qsTr("Password")
|
||||||
labelColor: highlightPassword ? colorScheme.signal_warning_active : colorScheme.text_norm
|
|
||||||
value: root.password
|
value: root.password
|
||||||
}
|
}
|
||||||
ConfigurationItem {
|
ConfigurationItem {
|
||||||
|
|||||||
@ -21,7 +21,6 @@ Item {
|
|||||||
|
|
||||||
property var colorScheme
|
property var colorScheme
|
||||||
property string label
|
property string label
|
||||||
property string labelColor: root.colorScheme.text_norm
|
|
||||||
property string value
|
property string value
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
@ -36,10 +35,9 @@ Item {
|
|||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Label {
|
Label {
|
||||||
color: labelColor
|
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: root.label
|
text: root.label
|
||||||
type: Label.Body_semibold
|
type: Label.Body
|
||||||
}
|
}
|
||||||
TextEdit {
|
TextEdit {
|
||||||
id: valueText
|
id: valueText
|
||||||
|
|||||||
@ -24,8 +24,7 @@ Item {
|
|||||||
|
|
||||||
signal closeWindow
|
signal closeWindow
|
||||||
signal quitBridge
|
signal quitBridge
|
||||||
signal showClientConfigurator(var user, string address, bool justLoggedIn)
|
signal showSetupGuide(var user, string address)
|
||||||
signal showLogin(var username)
|
|
||||||
|
|
||||||
function selectUser(userID) {
|
function selectUser(userID) {
|
||||||
const users = Backend.users;
|
const users = Backend.users;
|
||||||
@ -36,14 +35,11 @@ Item {
|
|||||||
}
|
}
|
||||||
accounts.currentIndex = i;
|
accounts.currentIndex = i;
|
||||||
if (user.state === EUserState.SignedOut)
|
if (user.state === EUserState.SignedOut)
|
||||||
showLogin(user.primaryEmailOrUsername());
|
showSignIn(user.primaryEmailOrUsername());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.error("User with ID ", userID, " was not found in the account list");
|
console.error("User with ID ", userID, " was not found in the account list");
|
||||||
}
|
}
|
||||||
function showBugReport() {
|
|
||||||
rightContent.showBugReport();
|
|
||||||
}
|
|
||||||
function showHelp() {
|
function showHelp() {
|
||||||
rightContent.showHelpView();
|
rightContent.showHelpView();
|
||||||
}
|
}
|
||||||
@ -53,9 +49,9 @@ Item {
|
|||||||
function showSettings() {
|
function showSettings() {
|
||||||
rightContent.showGeneralSettings();
|
rightContent.showGeneralSettings();
|
||||||
}
|
}
|
||||||
|
function showSignIn(username) {
|
||||||
function hasAccount() {
|
signIn.username = username;
|
||||||
return Backend.users.count > 0
|
rightContent.showSignIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
@ -194,41 +190,6 @@ Item {
|
|||||||
Layout.minimumHeight: 1
|
Layout.minimumHeight: 1
|
||||||
color: leftBar.colorScheme.border_weak
|
color: leftBar.colorScheme.border_weak
|
||||||
}
|
}
|
||||||
Item {
|
|
||||||
id: noAccountBox
|
|
||||||
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 24
|
|
||||||
visible: !hasAccount()
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
Label {
|
|
||||||
colorScheme: leftBar.colorScheme
|
|
||||||
color: colorScheme.text_weak
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
text: qsTr("No accounts")
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
colorScheme: leftBar.colorScheme
|
|
||||||
text: qsTr("Add an account")
|
|
||||||
secondary: true
|
|
||||||
onClicked: root.showLogin("")
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: accounts
|
id: accounts
|
||||||
|
|
||||||
@ -245,7 +206,7 @@ Item {
|
|||||||
clip: true
|
clip: true
|
||||||
model: Backend.users
|
model: Backend.users
|
||||||
spacing: 12
|
spacing: 12
|
||||||
visible: hasAccount()
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
||||||
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
||||||
@ -272,7 +233,8 @@ Item {
|
|||||||
if (user.state !== EUserState.SignedOut) {
|
if (user.state !== EUserState.SignedOut) {
|
||||||
rightContent.showAccount();
|
rightContent.showAccount();
|
||||||
} else {
|
} else {
|
||||||
showLogin(user.primaryEmailOrUsername());
|
signIn.username = user.primaryEmailOrUsername();
|
||||||
|
rightContent.showSignIn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -320,7 +282,8 @@ Item {
|
|||||||
width: 36
|
width: 36
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.showLogin("");
|
signIn.username = "";
|
||||||
|
rightContent.showSignIn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -360,41 +323,64 @@ Item {
|
|||||||
function showPortSettings() {
|
function showPortSettings() {
|
||||||
rightContent.currentIndex = 4;
|
rightContent.currentIndex = 4;
|
||||||
}
|
}
|
||||||
|
function showSignIn() {
|
||||||
|
rightContent.currentIndex = 1;
|
||||||
|
signIn.focus = true;
|
||||||
|
}
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
StackLayout {
|
AccountView {
|
||||||
// 0
|
// 0
|
||||||
currentIndex: hasAccount() ? 1 : 0
|
colorScheme: root.colorScheme
|
||||||
NoAccountView {
|
notifications: root.notifications
|
||||||
colorScheme: root.colorScheme
|
user: {
|
||||||
onStartSetup: {
|
if (accounts.currentIndex < 0)
|
||||||
root.showLogin("")
|
return undefined;
|
||||||
}
|
if (Backend.users.count === 0)
|
||||||
|
return undefined;
|
||||||
|
return Backend.users.get(accounts.currentIndex);
|
||||||
}
|
}
|
||||||
AccountView {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
notifications: root.notifications
|
|
||||||
user: {
|
|
||||||
if (accounts.currentIndex < 0)
|
|
||||||
return undefined;
|
|
||||||
if (Backend.users.count === 0)
|
|
||||||
return undefined;
|
|
||||||
return Backend.users.get(accounts.currentIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
onShowClientConfigurator: function (user, address, justLoggedIn) {
|
onShowSetupGuide: function (user, address) {
|
||||||
root.showClientConfigurator(user, address, justLoggedIn);
|
root.showSetupGuide(user, address);
|
||||||
}
|
}
|
||||||
onShowLogin: function (username) {
|
onShowSignIn: {
|
||||||
root.showLogin(username);
|
const user = this.user;
|
||||||
}
|
signIn.username = user ? user.primaryEmailOrUsername() : "";
|
||||||
|
rightContent.showSignIn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Rectangle {
|
GridLayout {
|
||||||
Layout.fillHeight: true
|
// 1 Sign In
|
||||||
Layout.fillWidth: true
|
columns: 2
|
||||||
color: "#ff9900"
|
|
||||||
|
Button {
|
||||||
|
id: backButton
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
Layout.leftMargin: 18
|
||||||
|
Layout.topMargin: 10
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
horizontalPadding: 8
|
||||||
|
icon.source: "/qml/icons/ic-arrow-left.svg"
|
||||||
|
secondary: true
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
signIn.abort();
|
||||||
|
rightContent.showAccount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SignIn {
|
||||||
|
id: signIn
|
||||||
|
Layout.bottomMargin: 68
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 80 - backButton.width - 18
|
||||||
|
Layout.preferredWidth: 320
|
||||||
|
Layout.rightMargin: 80
|
||||||
|
Layout.topMargin: 68
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
}
|
||||||
}
|
}
|
||||||
GeneralSettings {
|
GeneralSettings {
|
||||||
// 2
|
// 2
|
||||||
|
|||||||
@ -36,7 +36,8 @@ SettingsView {
|
|||||||
type: SettingsItem.PrimaryButton
|
type: SettingsItem.PrimaryButton
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Backend.openKBArticle();
|
Backend.notifyKBArticleClicked("https://proton.me/support/bridge");
|
||||||
|
Qt.openUrlExternally("https://proton.me/support/bridge");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SettingsItem {
|
SettingsItem {
|
||||||
@ -103,7 +104,7 @@ SettingsView {
|
|||||||
type: Label.Caption
|
type: Label.Caption
|
||||||
|
|
||||||
onLinkActivated: function (link) {
|
onLinkActivated: function (link) {
|
||||||
Qt.openUrlExternally(link)
|
Qt.openUrlExternally(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,29 +17,14 @@ import QtQuick.Layouts
|
|||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Proton
|
import Proton
|
||||||
import Notifications
|
import Notifications
|
||||||
import "SetupWizard"
|
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
property int _defaultHeight: 780
|
||||||
|
property int _defaultWidth: 1080
|
||||||
property var notifications
|
property var notifications
|
||||||
|
|
||||||
function layoutForUserCount(userCount) {
|
|
||||||
if (userCount === 0) {
|
|
||||||
contentLayout.currentIndex = 1;
|
|
||||||
setupWizard.showOnboarding();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const u = Backend.users.get(0);
|
|
||||||
if (!u) {
|
|
||||||
console.trace();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ((userCount === 1) && (u.state === EUserState.SignedOut)) {
|
|
||||||
contentLayout.currentIndex = 1;
|
|
||||||
setupWizard.showLogin(u.primaryEmailOrUsername());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function selectUser(userID) {
|
function selectUser(userID) {
|
||||||
contentWrapper.selectUser(userID);
|
contentWrapper.selectUser(userID);
|
||||||
}
|
}
|
||||||
@ -50,42 +35,42 @@ ApplicationWindow {
|
|||||||
root.requestActivate();
|
root.requestActivate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function showClientConfigurator(user, address, justLoggedIn) {
|
|
||||||
contentLayout.currentIndex = 1;
|
|
||||||
setupWizard.showClientConfig(user, address, justLoggedIn);
|
|
||||||
}
|
|
||||||
function showHelp() {
|
function showHelp() {
|
||||||
contentWrapper.showHelp();
|
contentWrapper.showHelp();
|
||||||
}
|
}
|
||||||
function showLocalCacheSettings() {
|
function showLocalCacheSettings() {
|
||||||
contentWrapper.showLocalCacheSettings();
|
contentWrapper.showLocalCacheSettings();
|
||||||
}
|
}
|
||||||
function showLogin(username = "") {
|
|
||||||
contentLayout.currentIndex = 1;
|
|
||||||
setupWizard.showLogin(username);
|
|
||||||
}
|
|
||||||
function showSettings() {
|
function showSettings() {
|
||||||
contentWrapper.showSettings();
|
contentWrapper.showSettings();
|
||||||
}
|
}
|
||||||
|
function showSetup(user, address) {
|
||||||
|
setupGuide.user = user;
|
||||||
|
setupGuide.address = address;
|
||||||
|
setupGuide.reset();
|
||||||
|
contentLayout._showSetup = !!setupGuide.user;
|
||||||
|
}
|
||||||
|
function showSignIn(username) {
|
||||||
|
if (contentLayout.currentIndex === 1)
|
||||||
|
return;
|
||||||
|
contentWrapper.showSignIn(username);
|
||||||
|
}
|
||||||
|
|
||||||
colorScheme: ProtonStyle.currentStyle
|
colorScheme: ProtonStyle.currentStyle
|
||||||
height: ProtonStyle.window_default_height
|
height: _defaultHeight
|
||||||
minimumHeight:ProtonStyle.window_minimum_height
|
minimumWidth: _defaultWidth
|
||||||
minimumWidth: ProtonStyle.window_minimum_width
|
|
||||||
visible: true
|
visible: true
|
||||||
width: ProtonStyle.window_default_width
|
width: _defaultWidth
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
layoutForUserCount(Backend.users.count);
|
|
||||||
}
|
|
||||||
|
|
||||||
// show Setup Guide on every new user
|
// show Setup Guide on every new user
|
||||||
Connections {
|
Connections {
|
||||||
function onRowsAboutToBeRemoved(parent, first, last) {
|
function onRowsAboutToBeRemoved(parent, first, last) {
|
||||||
for (let i = first; i <= last; i++) {
|
for (let i = first; i <= last; i++) {
|
||||||
const user = Backend.users.get(i);
|
const user = Backend.users.get(i);
|
||||||
if (setupWizard.user === user) {
|
if (setupGuide.user === user) {
|
||||||
setupWizard.closeWizard();
|
setupGuide.user = null;
|
||||||
|
contentLayout._showSetup = false;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,53 +83,65 @@ ApplicationWindow {
|
|||||||
if (user.setupGuideSeen) {
|
if (user.setupGuideSeen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
root.showClientConfigurator(user, user.addresses[0], false);
|
root.showSetup(user, user.addresses[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
target: Backend.users
|
target: Backend.users
|
||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
|
function onLoginFinished(index, wasSignedOut) {
|
||||||
|
const user = Backend.users.get(index);
|
||||||
|
if (user && !wasSignedOut) {
|
||||||
|
root.showSetup(user, user.addresses[0]);
|
||||||
|
}
|
||||||
|
console.debug("Login finished", index);
|
||||||
|
}
|
||||||
function onSelectUser(userID, forceShowWindow) {
|
function onSelectUser(userID, forceShowWindow) {
|
||||||
contentWrapper.selectUser(userID);
|
contentWrapper.selectUser(userID);
|
||||||
if (setupWizard.visible) {
|
|
||||||
setupWizard.closeWizard()
|
|
||||||
}
|
|
||||||
if (forceShowWindow) {
|
if (forceShowWindow) {
|
||||||
root.showAndRise();
|
root.showAndRise();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function onShowHelp() {
|
function onShowHelp() {
|
||||||
root.showHelp();
|
root.showHelp();
|
||||||
if (setupWizard.visible) {
|
|
||||||
setupWizard.closeWizard()
|
|
||||||
}
|
|
||||||
|
|
||||||
root.showAndRise();
|
root.showAndRise();
|
||||||
}
|
}
|
||||||
function onShowMainWindow() {
|
function onShowMainWindow() {
|
||||||
root.showAndRise();
|
root.showAndRise();
|
||||||
}
|
}
|
||||||
function onShowSettings() {
|
function onShowSettings() {
|
||||||
if (setupWizard.visible) {
|
|
||||||
setupWizard.closeWizard()
|
|
||||||
}
|
|
||||||
root.showSettings();
|
root.showSettings();
|
||||||
root.showAndRise();
|
root.showAndRise();
|
||||||
}
|
}
|
||||||
|
|
||||||
target: Backend
|
target: Backend
|
||||||
}
|
}
|
||||||
Connections {
|
|
||||||
function onCountChanged(count) {
|
|
||||||
layoutForUserCount(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
target: Backend.users
|
|
||||||
}
|
|
||||||
StackLayout {
|
StackLayout {
|
||||||
id: contentLayout
|
id: contentLayout
|
||||||
|
|
||||||
|
property bool _showSetup: false
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
currentIndex: 0
|
currentIndex: {
|
||||||
|
// show welcome when there are no users
|
||||||
|
if (Backend.users.count === 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
const u = Backend.users.get(0);
|
||||||
|
if (!u) {
|
||||||
|
console.trace();
|
||||||
|
console.log("empty user");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if ((Backend.users.count === 1) && (u.state === EUserState.SignedOut)) {
|
||||||
|
showSignIn(u.primaryEmailOrUsername());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (contentLayout._showSetup) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
ContentWrapper {
|
ContentWrapper {
|
||||||
// 0
|
// 0
|
||||||
@ -163,24 +160,30 @@ ApplicationWindow {
|
|||||||
root.close();
|
root.close();
|
||||||
Backend.quit();
|
Backend.quit();
|
||||||
}
|
}
|
||||||
onShowClientConfigurator: function (user, address, justLoggedIn) {
|
onShowSetupGuide: function (user, address) {
|
||||||
root.showClientConfigurator(user, address, justLoggedIn);
|
root.showSetup(user, address);
|
||||||
}
|
|
||||||
onShowLogin: function (username) {
|
|
||||||
root.showLogin(username);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SetupWizard {
|
WelcomeGuide {
|
||||||
id: setupWizard
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true // 1
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
}
|
||||||
|
SetupGuide {
|
||||||
|
// 2
|
||||||
|
id: setupGuide
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
|
|
||||||
onBugReportRequested: {
|
onDismissed: {
|
||||||
contentWrapper.showBugReport();
|
root.showSetup(null, "");
|
||||||
}
|
}
|
||||||
onWizardEnded: {
|
onFinished: {
|
||||||
contentLayout.currentIndex = 0;
|
// TODO: Do not close window. Trigger Backend to check that
|
||||||
|
// there is a successfully connected client. Then Backend
|
||||||
|
// should send another signal to close the setup guide.
|
||||||
|
root.showSetup(null, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,58 +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
|
|
||||||
import "SetupWizard"
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property ColorScheme colorScheme
|
|
||||||
|
|
||||||
color: root.colorScheme.background_norm
|
|
||||||
|
|
||||||
signal startSetup()
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
// we use the setup wizard left pane (onboarding version)
|
|
||||||
LeftPane {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.preferredWidth: ProtonStyle.wizard_pane_width
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
wizard: setupWizard
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
showNoAccount();
|
|
||||||
}
|
|
||||||
onStartSetup: {
|
|
||||||
root.startSetup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Image {
|
|
||||||
id: mailLogoWithWordmark
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.bottomMargin: ProtonStyle.wizard_window_margin
|
|
||||||
height: sourceSize.height
|
|
||||||
source: root.colorScheme.mail_logo_with_wordmark
|
|
||||||
sourceSize.height: 36
|
|
||||||
sourceSize.width: 134
|
|
||||||
width: sourceSize.width
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -453,7 +453,7 @@ QtObject {
|
|||||||
brief: title
|
brief: title
|
||||||
description: qsTr("Changing between split and combined address mode will require you to delete your account(s) from your email client and begin the setup process from scratch.")
|
description: qsTr("Changing between split and combined address mode will require you to delete your account(s) from your email client and begin the setup process from scratch.")
|
||||||
group: Notifications.Group.Configuration | Notifications.Group.Dialogs
|
group: Notifications.Group.Configuration | Notifications.Group.Dialogs
|
||||||
icon: "./icons/ic-question-circle.svg"
|
icon: "/qml/icons/ic-question-circle.svg"
|
||||||
title: qsTr("Enable split mode?")
|
title: qsTr("Enable split mode?")
|
||||||
type: Notification.NotificationType.Warning
|
type: Notification.NotificationType.Warning
|
||||||
|
|
||||||
@ -728,12 +728,10 @@ QtObject {
|
|||||||
}
|
}
|
||||||
property Notification noKeychain: Notification {
|
property Notification noKeychain: Notification {
|
||||||
brief: title
|
brief: title
|
||||||
description: Backend.goos === "darwin" ?
|
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.")
|
||||||
qsTr("Bridge is not able to access your keychain. Please make sure your keychain is not locked and restart the application.") :
|
|
||||||
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.")
|
|
||||||
group: Notifications.Group.Dialogs | Notifications.Group.Configuration
|
group: Notifications.Group.Dialogs | Notifications.Group.Configuration
|
||||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||||
title: Backend.goos === "darwin" ? qsTr("Cannot access keychain") : qsTr("No keychain available")
|
title: qsTr("No keychain available")
|
||||||
type: Notification.NotificationType.Danger
|
type: Notification.NotificationType.Danger
|
||||||
|
|
||||||
action: [
|
action: [
|
||||||
@ -790,6 +788,8 @@ QtObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
property Notification rebuildKeychain: Notification {
|
property Notification rebuildKeychain: Notification {
|
||||||
|
property var supportLink: "https://proton.me/support/bridge"
|
||||||
|
|
||||||
brief: title
|
brief: title
|
||||||
description: qsTr("Bridge is not able to access your macOS keychain. Please consult the instructions on our support page.")
|
description: qsTr("Bridge is not able to access your macOS keychain. Please consult the instructions on our support page.")
|
||||||
group: Notifications.Group.Dialogs | Notifications.Group.Configuration
|
group: Notifications.Group.Dialogs | Notifications.Group.Configuration
|
||||||
@ -802,7 +802,8 @@ QtObject {
|
|||||||
text: qsTr("Open the support page")
|
text: qsTr("Open the support page")
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
Backend.openKBArticle();
|
Backend.notifyKBArticleClicked(root.rebuildKeychain.supportLink);
|
||||||
|
Qt.openUrlExternally(root.rebuildKeychain.supportLink);
|
||||||
Backend.quit();
|
Backend.quit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,13 +23,11 @@ T.Button {
|
|||||||
property bool borderless: false
|
property bool borderless: false
|
||||||
property ColorScheme colorScheme
|
property ColorScheme colorScheme
|
||||||
readonly property bool hasTextAndIcon: (control.text !== "") && (iconImage.source.toString().length > 0)
|
readonly property bool hasTextAndIcon: (control.text !== "") && (iconImage.source.toString().length > 0)
|
||||||
property bool iconOnTheLeft: false
|
|
||||||
readonly property bool isIcon: control.text === ""
|
readonly property bool isIcon: control.text === ""
|
||||||
property int labelType: Proton.Label.LabelType.Body
|
property int labelType: Proton.Label.LabelType.Body
|
||||||
property bool loading: false
|
property bool loading: false
|
||||||
readonly property bool primary: !secondary
|
readonly property bool primary: !secondary
|
||||||
property alias secondary: control.flat
|
property alias secondary: control.flat
|
||||||
property bool secondaryIsOpaque: false
|
|
||||||
property alias textHorizontalAlignment: label.horizontalAlignment
|
property alias textHorizontalAlignment: label.horizontalAlignment
|
||||||
property alias textVerticalAlignment: label.verticalAlignment
|
property alias textVerticalAlignment: label.verticalAlignment
|
||||||
|
|
||||||
@ -79,7 +77,7 @@ T.Button {
|
|||||||
if (control.loading) {
|
if (control.loading) {
|
||||||
return control.colorScheme.interaction_default_hover;
|
return control.colorScheme.interaction_default_hover;
|
||||||
}
|
}
|
||||||
return secondaryIsOpaque ? control.colorScheme.background_norm : control.colorScheme.interaction_default;
|
return control.colorScheme.interaction_default;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (primary) {
|
if (primary) {
|
||||||
@ -105,7 +103,7 @@ T.Button {
|
|||||||
if (control.loading) {
|
if (control.loading) {
|
||||||
return control.colorScheme.interaction_default_hover;
|
return control.colorScheme.interaction_default_hover;
|
||||||
}
|
}
|
||||||
return secondaryIsOpaque ? control.colorScheme.background_norm : control.colorScheme.interaction_default;
|
return control.colorScheme.interaction_default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,7 +115,6 @@ T.Button {
|
|||||||
}
|
}
|
||||||
contentItem: RowLayout {
|
contentItem: RowLayout {
|
||||||
id: _contentItem
|
id: _contentItem
|
||||||
layoutDirection: iconOnTheLeft ? Qt.RightToLeft : Qt.LeftToRight
|
|
||||||
spacing: control.hasTextAndIcon ? control.spacing : 0
|
spacing: control.hasTextAndIcon ? control.spacing : 0
|
||||||
|
|
||||||
Proton.Label {
|
Proton.Label {
|
||||||
@ -131,13 +128,12 @@ T.Button {
|
|||||||
return control.colorScheme.text_norm;
|
return control.colorScheme.text_norm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
colorScheme: control.colorScheme
|
colorScheme: root.colorScheme
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
horizontalAlignment: Qt.AlignHCenter
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
opacity: control.enabled || control.loading ? 1.0 : 0.5
|
opacity: control.enabled || control.loading ? 1.0 : 0.5
|
||||||
text: control.text
|
text: control.text
|
||||||
type: labelType
|
type: labelType
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
visible: !control.isIcon
|
visible: !control.isIcon
|
||||||
}
|
}
|
||||||
ColorImage {
|
ColorImage {
|
||||||
|
|||||||
@ -48,7 +48,6 @@ QtObject {
|
|||||||
property color interaction_weak_active
|
property color interaction_weak_active
|
||||||
property color interaction_weak_hover
|
property color interaction_weak_hover
|
||||||
property string logo_img
|
property string logo_img
|
||||||
property string mail_logo_with_wordmark
|
|
||||||
|
|
||||||
// Primary
|
// Primary
|
||||||
property color primary_norm
|
property color primary_norm
|
||||||
@ -83,4 +82,7 @@ QtObject {
|
|||||||
// Text
|
// Text
|
||||||
property color text_norm
|
property color text_norm
|
||||||
property color text_weak
|
property color text_weak
|
||||||
|
|
||||||
|
// Images
|
||||||
|
property string welcome_img
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,88 +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.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property var callback: null
|
|
||||||
property ColorScheme colorScheme
|
|
||||||
property bool external: false
|
|
||||||
property string link: "#"
|
|
||||||
property string text: ""
|
|
||||||
|
|
||||||
function clear() {
|
|
||||||
root.callback = null;
|
|
||||||
root.text = "";
|
|
||||||
root.link = "";
|
|
||||||
root.external = false;
|
|
||||||
}
|
|
||||||
function link(url, text) {
|
|
||||||
return label.link(url, text);
|
|
||||||
}
|
|
||||||
function setCallback(callback, linkText, external) {
|
|
||||||
root.callback = callback;
|
|
||||||
root.text = linkText;
|
|
||||||
root.link = "#"; // Cannot be empty, otherwise the text is not an hyperlink.
|
|
||||||
root.external = external;
|
|
||||||
}
|
|
||||||
function setLink(linkURL, linkText, external) {
|
|
||||||
root.callback = null;
|
|
||||||
root.text = linkText;
|
|
||||||
root.link = linkURL;
|
|
||||||
root.external = external;
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: label
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: label.link(root.link, root.text)
|
|
||||||
type: Label.LabelType.Body
|
|
||||||
|
|
||||||
onLinkActivated: function (link) {
|
|
||||||
if ((link !== "#") && (link.length > 0)) {
|
|
||||||
Qt.openUrlExternally(link);
|
|
||||||
}
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ColorImage {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
color: label.linkColor
|
|
||||||
height: sourceSize.height
|
|
||||||
source: "/qml/icons/ic-external-link.svg"
|
|
||||||
sourceSize.height: 16
|
|
||||||
sourceSize.width: 16
|
|
||||||
visible: external
|
|
||||||
width: sourceSize.width
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
label.onLinkActivated(root.link);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HoverHandler {
|
|
||||||
acceptedDevices: PointerDevice.Mouse
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
enabled: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -20,21 +20,21 @@ import "."
|
|||||||
QtObject {
|
QtObject {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property int account_hover_radius: 12
|
property real account_hover_radius: 12 * root.px // px
|
||||||
property int account_row_radius: 12
|
property real account_row_radius: 12 * root.px // px
|
||||||
property int avatar_radius: 8
|
property real avatar_radius: 8 * root.px // px
|
||||||
property int banner_radius: 12
|
property real banner_radius: 12 * root.px // px
|
||||||
property int big_avatar_radius: 12
|
property real big_avatar_radius: 12 * root.px // px
|
||||||
property int body_font_size: 14
|
property int body_font_size: 14
|
||||||
property real body_letter_spacing: 0.2
|
property real body_letter_spacing: 0.2 * root.px
|
||||||
property int body_line_height: 20
|
property int body_line_height: 20
|
||||||
property int button_radius: 8
|
property real button_radius: 8 * root.px // px
|
||||||
property int caption_font_size: 12
|
property int caption_font_size: 12
|
||||||
property real caption_letter_spacing: 0.4
|
property real caption_letter_spacing: 0.4 * root.px
|
||||||
property int caption_line_height: 16
|
property int caption_line_height: 16
|
||||||
property int card_radius: 12
|
property real card_radius: 12 * root.px // px
|
||||||
property int checkbox_radius: 4
|
property real checkbox_radius: 4 * root.px // px
|
||||||
property int context_item_radius: 8
|
property real context_item_radius: 8 * root.px // px
|
||||||
property ColorScheme currentStyle: lightStyle
|
property ColorScheme currentStyle: lightStyle
|
||||||
property ColorScheme darkProminentStyle: ColorScheme {
|
property ColorScheme darkProminentStyle: ColorScheme {
|
||||||
id: _darkProminentStyle
|
id: _darkProminentStyle
|
||||||
@ -72,7 +72,6 @@ QtObject {
|
|||||||
interaction_weak_active: "#6D697D"
|
interaction_weak_active: "#6D697D"
|
||||||
interaction_weak_hover: "#5B576B"
|
interaction_weak_hover: "#5B576B"
|
||||||
logo_img: "/qml/icons/product_logos_dark.svg"
|
logo_img: "/qml/icons/product_logos_dark.svg"
|
||||||
mail_logo_with_wordmark: "/qml/icons/img-mail-logo-wordmark-dark.svg"
|
|
||||||
|
|
||||||
// Primary
|
// Primary
|
||||||
primary_norm: "#8A6EFF"
|
primary_norm: "#8A6EFF"
|
||||||
@ -106,6 +105,9 @@ QtObject {
|
|||||||
// Text
|
// Text
|
||||||
text_norm: "#FFFFFF"
|
text_norm: "#FFFFFF"
|
||||||
text_weak: "#A7A4B5"
|
text_weak: "#A7A4B5"
|
||||||
|
|
||||||
|
// Images
|
||||||
|
welcome_img: "/qml/icons/img-welcome-dark.png"
|
||||||
}
|
}
|
||||||
property ColorScheme darkStyle: ColorScheme {
|
property ColorScheme darkStyle: ColorScheme {
|
||||||
id: _darkStyle
|
id: _darkStyle
|
||||||
@ -143,7 +145,6 @@ QtObject {
|
|||||||
interaction_weak_active: "#6D697D"
|
interaction_weak_active: "#6D697D"
|
||||||
interaction_weak_hover: "#5B576B"
|
interaction_weak_hover: "#5B576B"
|
||||||
logo_img: "/qml/icons/product_logos_dark.svg"
|
logo_img: "/qml/icons/product_logos_dark.svg"
|
||||||
mail_logo_with_wordmark: "/qml/icons/img-mail-logo-wordmark-dark.svg"
|
|
||||||
|
|
||||||
// Primary
|
// Primary
|
||||||
primary_norm: "#8A6EFF"
|
primary_norm: "#8A6EFF"
|
||||||
@ -177,8 +178,11 @@ QtObject {
|
|||||||
// Text
|
// Text
|
||||||
text_norm: "#FFFFFF"
|
text_norm: "#FFFFFF"
|
||||||
text_weak: "#A7A4B5"
|
text_weak: "#A7A4B5"
|
||||||
|
|
||||||
|
// Images
|
||||||
|
welcome_img: "/qml/icons/img-welcome-dark.png"
|
||||||
}
|
}
|
||||||
property int dialog_radius: 12
|
property real dialog_radius: 12 * root.px // px
|
||||||
property int fontWeight_100: Font.Thin
|
property int fontWeight_100: Font.Thin
|
||||||
property int fontWeight_200: Font.Light
|
property int fontWeight_200: Font.Light
|
||||||
property int fontWeight_300: Font.ExtraLight
|
property int fontWeight_300: Font.ExtraLight
|
||||||
@ -202,7 +206,7 @@ QtObject {
|
|||||||
}
|
}
|
||||||
property int heading_font_size: 28
|
property int heading_font_size: 28
|
||||||
property int heading_line_height: 36
|
property int heading_line_height: 36
|
||||||
property int input_radius: 8
|
property real input_radius: 8 * root.px // px
|
||||||
property int lead_font_size: 18
|
property int lead_font_size: 18
|
||||||
property int lead_line_height: 26
|
property int lead_line_height: 26
|
||||||
property ColorScheme lightProminentStyle: ColorScheme {
|
property ColorScheme lightProminentStyle: ColorScheme {
|
||||||
@ -241,7 +245,6 @@ QtObject {
|
|||||||
interaction_weak_active: "#8A6EFF"
|
interaction_weak_active: "#8A6EFF"
|
||||||
interaction_weak_hover: "#6D4AFF"
|
interaction_weak_hover: "#6D4AFF"
|
||||||
logo_img: "/qml/icons/product_logos_dark.svg"
|
logo_img: "/qml/icons/product_logos_dark.svg"
|
||||||
mail_logo_with_wordmark: "/qml/icons/img-mail-logo-wordmark-dark.svg"
|
|
||||||
|
|
||||||
// Primary
|
// Primary
|
||||||
primary_norm: "#8A6EFF"
|
primary_norm: "#8A6EFF"
|
||||||
@ -275,6 +278,9 @@ QtObject {
|
|||||||
// Text
|
// Text
|
||||||
text_norm: "#FFFFFF"
|
text_norm: "#FFFFFF"
|
||||||
text_weak: "#9282D4"
|
text_weak: "#9282D4"
|
||||||
|
|
||||||
|
// Images
|
||||||
|
welcome_img: "/qml/icons/img-welcome-dark.png"
|
||||||
}
|
}
|
||||||
// TODO: Once we will use Qt >=5.15 this should be refactored with inline components as follows:
|
// TODO: Once we will use Qt >=5.15 this should be refactored with inline components as follows:
|
||||||
// https://doc.qt.io/qt-5/qtqml-documents-definetypes.html#inline-components
|
// https://doc.qt.io/qt-5/qtqml-documents-definetypes.html#inline-components
|
||||||
@ -319,7 +325,6 @@ QtObject {
|
|||||||
interaction_weak_active: "#A8A6A3"
|
interaction_weak_active: "#A8A6A3"
|
||||||
interaction_weak_hover: "#C2BFBC"
|
interaction_weak_hover: "#C2BFBC"
|
||||||
logo_img: "/qml/icons/product_logos.svg"
|
logo_img: "/qml/icons/product_logos.svg"
|
||||||
mail_logo_with_wordmark: "/qml/icons/img-mail-logo-wordmark.svg"
|
|
||||||
|
|
||||||
// Primary
|
// Primary
|
||||||
primary_norm: "#6D4AFF"
|
primary_norm: "#6D4AFF"
|
||||||
@ -353,35 +358,13 @@ QtObject {
|
|||||||
// Text
|
// Text
|
||||||
text_norm: "#0C0C14"
|
text_norm: "#0C0C14"
|
||||||
text_weak: "#706D6B"
|
text_weak: "#706D6B"
|
||||||
|
|
||||||
|
// Images
|
||||||
|
welcome_img: "/qml/icons/img-welcome.png"
|
||||||
}
|
}
|
||||||
property int progress_bar_radius: 3
|
property real progress_bar_radius: 3 * root.px // px
|
||||||
|
property real px: 1.00 // px
|
||||||
property int title_font_size: 20
|
property int title_font_size: 20
|
||||||
property int title_line_height: 24
|
property int title_line_height: 24
|
||||||
property int tooltip_radius: 8
|
property real tooltip_radius: 8 * root.px // px
|
||||||
|
|
||||||
// WebView overlay styling
|
|
||||||
property int web_view_button_width: 320
|
|
||||||
property int web_view_corner_radius: 10
|
|
||||||
property int web_view_overlay_button_vertical_margin: 10
|
|
||||||
property int web_view_overlay_horizontal_padding: 10
|
|
||||||
property int web_view_overlay_horizontal_margin: 250
|
|
||||||
property int web_view_overlay_vertical_margin: 50
|
|
||||||
property real web_view_overlay_opacity: 0.6
|
|
||||||
property int web_view_overlay_vertical_padding: web_view_corner_radius
|
|
||||||
property int web_view_overley_border_width: 1
|
|
||||||
|
|
||||||
property int window_default_height: 780
|
|
||||||
property int window_default_width: 1080
|
|
||||||
property int window_minimum_height: 650
|
|
||||||
property int window_minimum_width: window_default_width
|
|
||||||
|
|
||||||
// setup wizard constant
|
|
||||||
property int wizard_pane_bottomMargin: 92
|
|
||||||
property int wizard_pane_width: 364
|
|
||||||
property int wizard_window_margin: 40
|
|
||||||
property int wizard_spacing_extra_large: 32
|
|
||||||
property int wizard_spacing_extra_small: 4
|
|
||||||
property int wizard_spacing_large: 24
|
|
||||||
property int wizard_spacing_medium: 16
|
|
||||||
property int wizard_spacing_small: 8
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -238,12 +238,12 @@ FocusScope {
|
|||||||
bottomPadding: 8
|
bottomPadding: 8
|
||||||
color: {
|
color: {
|
||||||
if (!control.enabled) {
|
if (!control.enabled) {
|
||||||
return root.colorScheme.text_disabled;
|
return root.colorScheme.text_disabled
|
||||||
}
|
}
|
||||||
if (control.readOnly) {
|
if (control.readOnly) {
|
||||||
return root.colorScheme.text_hint;
|
return root.colorScheme.text_hint
|
||||||
}
|
}
|
||||||
return root.colorScheme.text_norm;
|
return root.colorScheme.text_norm
|
||||||
}
|
}
|
||||||
|
|
||||||
// enforcing default focus here within component
|
// enforcing default focus here within component
|
||||||
|
|||||||
@ -114,9 +114,6 @@ FocusScope {
|
|||||||
function getText(start, end) {
|
function getText(start, end) {
|
||||||
control.getText(start, end);
|
control.getText(start, end);
|
||||||
}
|
}
|
||||||
function hidePassword() {
|
|
||||||
eyeButton.checked = false;
|
|
||||||
}
|
|
||||||
function insert(position, text) {
|
function insert(position, text) {
|
||||||
control.insert(position, text);
|
control.insert(position, text);
|
||||||
}
|
}
|
||||||
@ -150,9 +147,6 @@ FocusScope {
|
|||||||
function selectWord() {
|
function selectWord() {
|
||||||
control.selectWord();
|
control.selectWord();
|
||||||
}
|
}
|
||||||
function showPassword() {
|
|
||||||
eyeButton.checked = true;
|
|
||||||
}
|
|
||||||
function undo() {
|
function undo() {
|
||||||
control.undo();
|
control.undo();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,6 @@ CheckBox 4.0 CheckBox.qml
|
|||||||
ComboBox 4.0 ComboBox.qml
|
ComboBox 4.0 ComboBox.qml
|
||||||
Dialog 4.0 Dialog.qml
|
Dialog 4.0 Dialog.qml
|
||||||
Label 4.0 Label.qml
|
Label 4.0 Label.qml
|
||||||
LinkLabel 4.0 LinkLabel.qml
|
|
||||||
Menu 4.0 Menu.qml
|
Menu 4.0 Menu.qml
|
||||||
MenuItem 4.0 MenuItem.qml
|
MenuItem 4.0 MenuItem.qml
|
||||||
Popup 4.0 Popup.qml
|
Popup 4.0 Popup.qml
|
||||||
@ -37,4 +36,3 @@ Switch 4.0 Switch.qml
|
|||||||
TextArea 4.0 TextArea.qml
|
TextArea 4.0 TextArea.qml
|
||||||
TextField 4.0 TextField.qml
|
TextField 4.0 TextField.qml
|
||||||
Toggle 4.0 Toggle.qml
|
Toggle 4.0 Toggle.qml
|
||||||
WebFrame 4.0 WebFrame.qml
|
|
||||||
|
|||||||
@ -1,16 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title></title>
|
|
||||||
<style>
|
|
||||||
body {font-family: sans-serif}
|
|
||||||
h1 { font-size: 1.5em; text-align: center; margin-bottom: 2em;}
|
|
||||||
p {text-align: justify; margin-bottom: 2em; }
|
|
||||||
p.standfirst { font-weight: bold;}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
%1
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
<h1>Why do I need bridge?</h1>
|
|
||||||
<p class="standfirst">
|
|
||||||
Proton does not have access to the content of your messages, so it cannot share your unencrypted messages with your email client from the
|
|
||||||
Proton servers.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Email clients such as Microsoft Outlook, Mozilla Thunderbird and Apple Mail use standard protocols named IMAP and SMTP to receive and send emails.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Even though the IMAP and SMTP protocols can use secure channels (using SSL/TLS), they do not offer support for encrypted messages.
|
|
||||||
Because Proton does not have access to the content of your messages, it is not possible to configure your email client to connect directly to
|
|
||||||
Proton servers.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The key to solving this problem is Bridge. Once installed on your computer and connected to your Proton account, Bridge can access your
|
|
||||||
encrypted messages stored on the Proton servers. Bridge integrates an IMAP and a SMTP server that run on your computer and are accessible only
|
|
||||||
to applications executing on your machine. Your email client connects to these local servers and Bridge is responsible for seamlessly encrypting
|
|
||||||
and decrypting the messages that you send and receive.
|
|
||||||
</p>
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
<h1>Why do I need to install a certificate when configuring Apple Mail with Bridge?</h1>
|
|
||||||
<p class="standfirst">
|
|
||||||
Apple Mail requires a secure channel for communications with email servers, and the server needs to be acknowledged as trusted.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
In order to communicate with Bridge, Apple Mail requires secure connections using SSL/TLS. This cryptographic protocol includes an identity
|
|
||||||
verification system using certificates. For publicly available servers, certificates are normally issued and digitally signed by a certificate
|
|
||||||
authority, such as Let's Encrypt. This is not possible for Bridge, as the IMAP and SMTP servers are running on your own computer, and are not
|
|
||||||
accessible from any network (local or internet).
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The solution is to use a self-signed certificate. When setting up an email account where the server provides a self-signed certificate, most
|
|
||||||
email clients will issue a warning asking you whether you trust the server or not, because the certificate was not issued by a certificate
|
|
||||||
authority.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Apple Mail requires an extra step. It will simply refuse to connect if the certificate is not set as trusted. Bridge solves this by storing this
|
|
||||||
certificate in the macOS keychain. This operation requires that you provide your macOS account password.
|
|
||||||
</p>
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
<h1>Why is there a warning sign when installing the Bridge profile on macOS?</h1>
|
|
||||||
<p class="standfirst">
|
|
||||||
This warning indicates that the certificate used to secure the communication channel between Bridge and your email client is not signed by a
|
|
||||||
trusted third party.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
In order to communicate with Bridge, Apple Mail requires secure connections using SSL/TLS. This cryptographic protocol includes an identity
|
|
||||||
verification system using certificates. For publicly available servers, certificates are normally issued and digitally signed by a certificate
|
|
||||||
authority, such as Let's Encrypt. This is not possible for Bridge, as the IMAP and SMTP servers are running on your own computer, and are not
|
|
||||||
accessible from any network (local or internet).
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The solution is to use a self-signed certificate. When setting up an email account where the server provides a self-signed certificate, most
|
|
||||||
email clients will issue a warning asking you whether you trust the server or not, because the certificate was not issued by a certificate
|
|
||||||
authority. The client has no way of verifying that the server is who it pretends to be.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
You can safely ignore this warning. The check concerns only the communication between your email client and Bridge, which occurs within your
|
|
||||||
computer. On the other end, the communication between Bridge and the Proton servers uses the HTTPS protocol, and the identity of the remote
|
|
||||||
server is verified by Bridge.
|
|
||||||
</p>
|
|
||||||
@ -90,7 +90,7 @@ Item {
|
|||||||
icon.source: root.actionIcon
|
icon.source: root.actionIcon
|
||||||
loading: root.loading
|
loading: root.loading
|
||||||
secondary: root.type !== SettingsItem.PrimaryButton
|
secondary: root.type !== SettingsItem.PrimaryButton
|
||||||
text: root.actionText
|
text: root.actionText + (root.actionIcon !== "" ? " " : "")
|
||||||
visible: root.type === SettingsItem.Button || root.type === SettingsItem.PrimaryButton
|
visible: root.type === SettingsItem.Button || root.type === SettingsItem.PrimaryButton
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|||||||
293
internal/frontend/bridge-gui/bridge-gui/qml/SetupGuide.qml
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
// 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 QtQuick.Controls.impl
|
||||||
|
import Proton
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string address
|
||||||
|
property ColorScheme colorScheme
|
||||||
|
property var user
|
||||||
|
|
||||||
|
signal dismissed
|
||||||
|
signal finished
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
guidePages.currentIndex = 0;
|
||||||
|
clientList.currentIndex = -1;
|
||||||
|
actionList.currentIndex = -1;
|
||||||
|
}
|
||||||
|
function setupAction(actionID, clientID) {
|
||||||
|
if (user) {
|
||||||
|
user.setupGuideSeen = true;
|
||||||
|
}
|
||||||
|
switch (actionID) {
|
||||||
|
case -1:
|
||||||
|
root.dismissed();
|
||||||
|
break; // dismiss
|
||||||
|
case 0 // automatic
|
||||||
|
:
|
||||||
|
if (user) {
|
||||||
|
switch (clientID) {
|
||||||
|
case 0:
|
||||||
|
root.user.configureAppleMail(root.address);
|
||||||
|
Backend.notifyAutoconfigClicked("AppleMail");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.finished();
|
||||||
|
break;
|
||||||
|
case 1 // manual
|
||||||
|
:
|
||||||
|
let clientObj = clients.get(clientID);
|
||||||
|
if (clientObj !== undefined && clientObj.link !== "") {
|
||||||
|
Qt.openUrlExternally(clientObj.link);
|
||||||
|
Backend.notifyKBArticleClicked(clientObj.link);
|
||||||
|
} else {
|
||||||
|
console.log("unexpected client index", actionID, clientID);
|
||||||
|
}
|
||||||
|
root.finished();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log("unexpected client setup action", actionID, clientID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitHeight: children[0].implicitHeight
|
||||||
|
implicitWidth: children[0].implicitWidth
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: clients
|
||||||
|
|
||||||
|
property bool haveAutoSetup: true
|
||||||
|
property string iconSource: "/qml/icons/ic-apple-mail.svg"
|
||||||
|
property string link: "https://proton.me/support/protonmail-bridge-clients-apple-mail"
|
||||||
|
property string name: "Apple Mail"
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (Backend.goos === "darwin") {
|
||||||
|
append({
|
||||||
|
"name": "Apple Mail",
|
||||||
|
"iconSource": "/qml/icons/ic-apple-mail.svg",
|
||||||
|
"haveAutoSetup": true,
|
||||||
|
"link": "https://proton.me/support/protonmail-bridge-clients-apple-mail"
|
||||||
|
});
|
||||||
|
append({
|
||||||
|
"name": "Microsoft Outlook",
|
||||||
|
"iconSource": "/qml/icons/ic-microsoft-outlook.svg",
|
||||||
|
"haveAutoSetup": false,
|
||||||
|
"link": "https://proton.me/support/protonmail-bridge-clients-macos-outlook-2019"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (Backend.goos === "windows") {
|
||||||
|
append({
|
||||||
|
"name": "Microsoft Outlook",
|
||||||
|
"iconSource": "/qml/icons/ic-microsoft-outlook.svg",
|
||||||
|
"haveAutoSetup": false,
|
||||||
|
"link": "https://proton.me/support/protonmail-bridge-clients-windows-outlook-2019"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
append({
|
||||||
|
"name": "Mozilla Thunderbird",
|
||||||
|
"iconSource": "/qml/icons/ic-mozilla-thunderbird.svg",
|
||||||
|
"haveAutoSetup": false,
|
||||||
|
"link": "https://proton.me/support/protonmail-bridge-clients-windows-thunderbird"
|
||||||
|
});
|
||||||
|
append({
|
||||||
|
"name": "Other",
|
||||||
|
"iconSource": "/qml/icons/ic-other-mail-clients.svg",
|
||||||
|
"haveAutoSetup": false,
|
||||||
|
"link": "https://proton.me/support/protonmail-bridge-configure-client"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: root
|
||||||
|
color: root.colorScheme.background_norm
|
||||||
|
}
|
||||||
|
StackLayout {
|
||||||
|
id: guidePages
|
||||||
|
anchors.bottomMargin: 70
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: 80
|
||||||
|
anchors.rightMargin: 80
|
||||||
|
anchors.topMargin: 30
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
// 0: Client selection
|
||||||
|
id: clientView
|
||||||
|
|
||||||
|
property int columnWidth: 268
|
||||||
|
|
||||||
|
Layout.fillHeight: true
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
Label {
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
text: qsTr("Setting up email client")
|
||||||
|
type: Label.LabelType.Heading
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
color: root.colorScheme.text_weak
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
text: address
|
||||||
|
type: Label.LabelType.Lead
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
Layout.topMargin: 32 - clientView.spacing
|
||||||
|
spacing: 24
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: clientColumn
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: labelA
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
text: qsTr("Choose an email client")
|
||||||
|
type: Label.LabelType.Body_semibold
|
||||||
|
}
|
||||||
|
ListView {
|
||||||
|
id: clientList
|
||||||
|
Layout.fillHeight: true
|
||||||
|
model: clients
|
||||||
|
width: clientView.columnWidth
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
implicitHeight: clientRow.height
|
||||||
|
implicitWidth: clientRow.width
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: clientRow
|
||||||
|
width: clientList.width
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.bottomMargin: 12
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.topMargin: 12
|
||||||
|
|
||||||
|
ColorImage {
|
||||||
|
height: 36
|
||||||
|
source: model.iconSource
|
||||||
|
sourceSize.height: 36
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
Layout.leftMargin: 12
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
text: model.name
|
||||||
|
type: Label.LabelType.Body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 1
|
||||||
|
color: root.colorScheme.border_weak
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
clientList.currentIndex = index;
|
||||||
|
if (!model.haveAutoSetup) {
|
||||||
|
root.setupAction(1, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
highlight: Rectangle {
|
||||||
|
color: root.colorScheme.interaction_default_active
|
||||||
|
radius: ProtonStyle.context_item_radius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
id: actionColumn
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
visible: clientList.currentIndex >= 0 && clients.get(clientList.currentIndex).haveAutoSetup
|
||||||
|
|
||||||
|
Label {
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
text: qsTr("Choose configuration mode")
|
||||||
|
type: Label.LabelType.Body_semibold
|
||||||
|
}
|
||||||
|
ListView {
|
||||||
|
id: actionList
|
||||||
|
Layout.fillHeight: true
|
||||||
|
model: [qsTr("Configure automatically"), qsTr("Configure manually")]
|
||||||
|
width: clientView.columnWidth
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
implicitHeight: children[0].height
|
||||||
|
implicitWidth: children[0].width
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
width: actionList.width
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.bottomMargin: 20
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.topMargin: 20
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
text: modelData
|
||||||
|
type: Label.LabelType.Body
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 1
|
||||||
|
color: root.colorScheme.border_weak
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
actionList.currentIndex = index;
|
||||||
|
root.setupAction(index, clientList.currentIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
highlight: Rectangle {
|
||||||
|
color: root.colorScheme.interaction_default_active
|
||||||
|
radius: ProtonStyle.context_item_radius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
flat: true
|
||||||
|
text: qsTr("Set up later")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.setupAction(-1, -1);
|
||||||
|
if (user) {
|
||||||
|
user.setupGuideSeen = true;
|
||||||
|
}
|
||||||
|
root.dismissed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,274 +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
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
enum Screen {
|
|
||||||
CertificateInstall,
|
|
||||||
ProfileInstall
|
|
||||||
}
|
|
||||||
|
|
||||||
property var wizard
|
|
||||||
|
|
||||||
signal appleMailAutoconfigCertificateInstallPageShown
|
|
||||||
signal appleMailAutoconfigProfileInstallPageShow
|
|
||||||
|
|
||||||
function showAutoconfig() {
|
|
||||||
if (Backend.isTLSCertificateInstalled()) {
|
|
||||||
showProfileInstall();
|
|
||||||
} else {
|
|
||||||
showCertificateInstall();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function showCertificateInstall() {
|
|
||||||
certificateInstall.reset();
|
|
||||||
stack.currentIndex = ClientConfigAppleMail.Screen.CertificateInstall;
|
|
||||||
appleMailAutoconfigCertificateInstallPageShown();
|
|
||||||
}
|
|
||||||
function showProfileInstall() {
|
|
||||||
profileInstall.reset();
|
|
||||||
stack.currentIndex = ClientConfigAppleMail.Screen.ProfileInstall;
|
|
||||||
appleMailAutoconfigProfileInstallPageShow();
|
|
||||||
}
|
|
||||||
|
|
||||||
StackLayout {
|
|
||||||
id: stack
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
// stack index 0
|
|
||||||
Item {
|
|
||||||
id: certificateInstall
|
|
||||||
|
|
||||||
property string errorString: ""
|
|
||||||
property bool showBugReportLink: false
|
|
||||||
property bool waitingForCert: false
|
|
||||||
|
|
||||||
function clearError() {
|
|
||||||
errorString = "";
|
|
||||||
showBugReportLink = false;
|
|
||||||
}
|
|
||||||
function reset() {
|
|
||||||
waitingForCert = false;
|
|
||||||
clearError();
|
|
||||||
}
|
|
||||||
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: ProtonStyle.wizard_spacing_large
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
function onCertificateInstallCanceled() {
|
|
||||||
certificateInstall.waitingForCert = false;
|
|
||||||
certificateInstall.errorString = qsTr("Apple Mail cannot be configured if you do not install the certificate. Please retry.");
|
|
||||||
certificateInstall.showBugReportLink = false;
|
|
||||||
}
|
|
||||||
function onCertificateInstallFailed() {
|
|
||||||
certificateInstall.waitingForCert = false;
|
|
||||||
certificateInstall.errorString = qsTr("An error occurred while installing the certificate.");
|
|
||||||
certificateInstall.showBugReportLink = true;
|
|
||||||
}
|
|
||||||
function onCertificateInstallSuccess() {
|
|
||||||
certificateInstall.reset();
|
|
||||||
root.showAutoconfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
target: Backend
|
|
||||||
}
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: ProtonStyle.wizard_spacing_medium
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: qsTr("Install the bridge certificate")
|
|
||||||
type: Label.LabelType.Title
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: colorScheme.text_weak
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: qsTr("After clicking on the button below, a system pop-up will ask you for your credentials, please enter your macOS user credentials (not your Proton account’s) and validate.")
|
|
||||||
type: Label.LabelType.Body
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Image {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
height: 182
|
|
||||||
opacity: certificateInstall.waitingForCert ? 0.3 : 1.0
|
|
||||||
source: "/qml/icons/img-macos-cert-screenshot.png"
|
|
||||||
width: 140
|
|
||||||
}
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: ProtonStyle.wizard_spacing_medium
|
|
||||||
|
|
||||||
Button {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
enabled: !certificateInstall.waitingForCert
|
|
||||||
loading: certificateInstall.waitingForCert
|
|
||||||
text: qsTr("Install the certificate")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
certificateInstall.clearError();
|
|
||||||
certificateInstall.waitingForCert = true;
|
|
||||||
Backend.installTLSCertificate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
enabled: !certificateInstall.waitingForCert
|
|
||||||
secondary: true
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
wizard.closeWizard();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: ProtonStyle.wizard_spacing_small
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: ProtonStyle.wizard_spacing_extra_small
|
|
||||||
|
|
||||||
ColorImage {
|
|
||||||
color: wizard.colorScheme.signal_danger
|
|
||||||
height: errorLabel.lineHeight
|
|
||||||
source: "/qml/icons/ic-exclamation-circle-filled.svg"
|
|
||||||
sourceSize.height: errorLabel.lineHeight
|
|
||||||
visible: certificateInstall.errorString.length > 0
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
id: errorLabel
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: wizard.colorScheme.signal_danger
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: certificateInstall.errorString
|
|
||||||
type: Label.LabelType.Body_semibold
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LinkLabel {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
callback: wizard.showBugReport
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
link: "#"
|
|
||||||
text: qsTr("Report the problem")
|
|
||||||
visible: certificateInstall.showBugReportLink
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// stack index 1
|
|
||||||
Item {
|
|
||||||
id: profileInstall
|
|
||||||
|
|
||||||
property bool profilePaneLaunched: false
|
|
||||||
|
|
||||||
function reset() {
|
|
||||||
profilePaneLaunched = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: ProtonStyle.wizard_spacing_large
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: ProtonStyle.wizard_spacing_medium
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: qsTr("Install the profile")
|
|
||||||
type: Label.LabelType.Title
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: colorScheme.text_weak
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: qsTr("A system pop-up will appear. Double click on the entry with your email, and click ’Install’ in the dialog that appears.")
|
|
||||||
type: Label.LabelType.Body
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Image {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
height: 102
|
|
||||||
source: "/qml/icons/img-macos-profile-screenshot.png"
|
|
||||||
width: 364
|
|
||||||
}
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: ProtonStyle.wizard_spacing_medium
|
|
||||||
|
|
||||||
Button {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
text: profileInstall.profilePaneLaunched ? qsTr("I have installed the profile") : qsTr("Install the profile")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if (profileInstall.profilePaneLaunched) {
|
|
||||||
wizard.showClientConfigEnd();
|
|
||||||
} else {
|
|
||||||
wizard.user.configureAppleMail(wizard.address);
|
|
||||||
profileInstall.profilePaneLaunched = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
secondary: true
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
wizard.closeWizard();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,99 +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
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property ColorScheme colorScheme: wizard.colorScheme
|
|
||||||
property var wizard
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
color: colorScheme.background_norm
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: centeredContainer
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: 84
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: 32
|
|
||||||
clip: true
|
|
||||||
width: ProtonStyle.wizard_pane_width
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: ProtonStyle.wizard_spacing_medium
|
|
||||||
|
|
||||||
Image {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.preferredHeight: sourceSize.height
|
|
||||||
Layout.preferredWidth: sourceSize.width
|
|
||||||
source: "/qml/icons/img-client-config-success.svg"
|
|
||||||
sourceSize.height: 104
|
|
||||||
sourceSize.width: 190
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: qsTr("Congratulations! You're all setup")
|
|
||||||
type: Label.LabelType.Title
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: colorScheme.text_weak
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: wizard.address
|
|
||||||
type: Label.LabelType.Body
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: qsTr("Your client has been configured. While complete synchronization might take some time, you can already send encrypted emails.")
|
|
||||||
type: Label.LabelType.Body
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: qsTr("Done")
|
|
||||||
|
|
||||||
onClicked: wizard.closeWizard()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Image {
|
|
||||||
id: mailLogoWithWordmark
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: 32
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
height: 36
|
|
||||||
source: root.colorScheme.mail_logo_with_wordmark
|
|
||||||
sourceSize.height: height
|
|
||||||
sourceSize.width: width
|
|
||||||
width: 134
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,163 +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 ".."
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property ColorScheme colorScheme: wizard.colorScheme
|
|
||||||
readonly property bool genericClient: SetupWizard.Client.Generic === wizard.client
|
|
||||||
property var wizard
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
color: colorScheme.background_weak
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: centeredContainer
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.top: parent.top
|
|
||||||
width: 640
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: ProtonStyle.wizard_spacing_medium
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: qsTr("Configure %1").arg(wizard.clientName())
|
|
||||||
type: Label.LabelType.Title
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
Rectangle {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
border.color: colorScheme.border_norm
|
|
||||||
border.width: 1
|
|
||||||
color: "transparent"
|
|
||||||
height: childrenRect.height + 2 * ProtonStyle.wizard_spacing_medium
|
|
||||||
radius: 12
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.margins: ProtonStyle.wizard_spacing_medium
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
spacing: ProtonStyle.wizard_spacing_small
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
text: (SetupWizard.Client.MicrosoftOutlook === wizard.client) ? qsTr("Are you unsure about your Outlook version or do you need assistance in configuring Outlook?") : qsTr("Do you need assistance in configuring %1?".arg(wizard.clientName()))
|
|
||||||
type: Label.LabelType.Body
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
icon.source: "/qml/icons/ic-external-link.svg"
|
|
||||||
text: qsTr("Open guide")
|
|
||||||
|
|
||||||
onClicked: function () {
|
|
||||||
Backend.openKBArticle(wizard.setupGuideLink());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Rectangle {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
border.color: colorScheme.signal_warning
|
|
||||||
border.width: 1
|
|
||||||
color: "transparent"
|
|
||||||
height: childrenRect.height + 2 * ProtonStyle.wizard_spacing_medium
|
|
||||||
radius: ProtonStyle.banner_radius
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.margins: ProtonStyle.wizard_spacing_medium
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
spacing: ProtonStyle.wizard_spacing_medium
|
|
||||||
|
|
||||||
ColorImage {
|
|
||||||
id: image
|
|
||||||
height: 36
|
|
||||||
source: "/qml/icons/ic-warning-orange.svg"
|
|
||||||
sourceSize.height: height
|
|
||||||
sourceSize.width: width
|
|
||||||
width: height
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
text: qsTr("Copy paste the provided configuration parameters. Use the password below (not your Proton password), when adding your Proton account to %1.".arg(wizard.clientName()))
|
|
||||||
type: Label.LabelType.Body
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
id: configuration
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: ProtonStyle.wizard_spacing_extra_large
|
|
||||||
|
|
||||||
Configuration {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
highlightPassword: true
|
|
||||||
hostname: Backend.hostname
|
|
||||||
password: wizard.user ? wizard.user.password : ""
|
|
||||||
port: Backend.imapPort.toString()
|
|
||||||
security: Backend.useSSLForIMAP ? "SSL" : "STARTTLS"
|
|
||||||
title: "IMAP"
|
|
||||||
username: wizard.address
|
|
||||||
}
|
|
||||||
Configuration {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
highlightPassword: true
|
|
||||||
hostname: Backend.hostname
|
|
||||||
password: wizard.user ? wizard.user.password : ""
|
|
||||||
port: Backend.smtpPort.toString()
|
|
||||||
security: Backend.useSSLForSMTP ? "SSL" : "STARTTLS"
|
|
||||||
title: "SMTP"
|
|
||||||
username: wizard.address
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.preferredWidth: 304
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
secondary: true
|
|
||||||
secondaryIsOpaque: true
|
|
||||||
text: qsTr("Continue")
|
|
||||||
|
|
||||||
onClicked: wizard.showClientConfigEnd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -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 QtQml
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import QtQuick.Controls
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
readonly property bool onMacOS: (Backend.goos === "darwin")
|
|
||||||
readonly property bool onWindows: (Backend.goos === "windows")
|
|
||||||
property var wizard
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: ProtonStyle.wizard_spacing_medium
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: ProtonStyle.wizard_spacing_medium
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Qt.AlignHCenter
|
|
||||||
text: qsTr("Select your email client")
|
|
||||||
type: Label.LabelType.Title
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
ClientListItem {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
iconSource: "/qml/icons/ic-apple-mail.svg"
|
|
||||||
text: "Apple Mail"
|
|
||||||
visible: root.onMacOS
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
wizard.client = SetupWizard.Client.AppleMail;
|
|
||||||
wizard.showAppleMailAutoConfig();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ClientListItem {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
iconSource: "/qml/icons/ic-microsoft-outlook.svg"
|
|
||||||
text: "Microsoft Outlook"
|
|
||||||
visible: root.onMacOS || root.onWindows
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
wizard.client = SetupWizard.Client.MicrosoftOutlook;
|
|
||||||
wizard.showClientParams();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ClientListItem {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
iconSource: "/qml/icons/ic-mozilla-thunderbird.svg"
|
|
||||||
text: "Mozilla Thunderbird"
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
wizard.client = SetupWizard.Client.MozillaThunderbird;
|
|
||||||
wizard.showClientParams();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ClientListItem {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
iconSource: "/qml/icons/ic-other-mail-clients.svg"
|
|
||||||
text: qsTr("Other")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
wizard.client = SetupWizard.Client.Generic;
|
|
||||||
wizard.showClientParams();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 20
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
secondary: true
|
|
||||||
secondaryIsOpaque: true
|
|
||||||
text: qsTr("Setup later")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
root.wizard.closeWizard();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,70 +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
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property ColorScheme colorScheme
|
|
||||||
property string iconSource
|
|
||||||
property string text
|
|
||||||
|
|
||||||
signal clicked
|
|
||||||
|
|
||||||
border.color: colorScheme.border_norm
|
|
||||||
border.width: 1
|
|
||||||
color: {
|
|
||||||
if (mouseArea.pressed) {
|
|
||||||
return colorScheme.interaction_default_active;
|
|
||||||
}
|
|
||||||
if (mouseArea.containsMouse) {
|
|
||||||
return colorScheme.interaction_default_hover;
|
|
||||||
}
|
|
||||||
return colorScheme.background_norm;
|
|
||||||
}
|
|
||||||
height: 68
|
|
||||||
radius: ProtonStyle.banner_radius
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: ProtonStyle.wizard_spacing_medium
|
|
||||||
|
|
||||||
ColorImage {
|
|
||||||
height: sourceSize.height
|
|
||||||
source: iconSource
|
|
||||||
sourceSize.height: 36
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.leftMargin: 12
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
text: root.text
|
|
||||||
type: Label.LabelType.Body
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MouseArea {
|
|
||||||
id: mouseArea
|
|
||||||
acceptedButtons: Qt.LeftButton
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
root.clicked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,65 +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
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property var wizard
|
|
||||||
readonly property int _iconPadding: 8 // The SVG image we use has internal padding that we need to compensate for alignment.
|
|
||||||
readonly property int _iconSize: 24
|
|
||||||
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: ProtonStyle.wizard_window_margin - _iconPadding
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: ProtonStyle.wizard_window_margin - _iconPadding
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalPadding: 0
|
|
||||||
icon.color: wizard.colorScheme.text_weak
|
|
||||||
icon.height: _iconSize
|
|
||||||
icon.source: "/qml/icons/ic-question-circle.svg"
|
|
||||||
icon.width: _iconSize
|
|
||||||
verticalPadding: 0
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
menu.popup(-menu.width + root.width, -menu.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
Menu {
|
|
||||||
id: menu
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
modal: true
|
|
||||||
|
|
||||||
MenuItem {
|
|
||||||
id: getHelpItem
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: qsTr("Get help")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
Backend.openKBArticle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MenuItem {
|
|
||||||
id: reportAProblemItem
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: qsTr("Report a problem")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
wizard.showBugReport();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,145 +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
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
readonly property string addAccountTitle: qsTr("Add a Proton Mail account")
|
|
||||||
readonly property string welcomeDescription: qsTr("Bridge is the gateway between your Proton account and your email client. It runs in the background and encrypts and decrypts your messages seamlessly. ");
|
|
||||||
readonly property string welcomeTitle: qsTr("Welcome to\nProton Mail Bridge")
|
|
||||||
readonly property string welcomeImage: "/qml/icons/img-welcome.svg"
|
|
||||||
readonly property int welcomeImageHeight: 148;
|
|
||||||
readonly property int welcomeImageWidth: 265;
|
|
||||||
|
|
||||||
property int iconHeight
|
|
||||||
property string iconSource
|
|
||||||
property int iconWidth
|
|
||||||
property var wizard
|
|
||||||
property ColorScheme colorScheme
|
|
||||||
property var _colorScheme: wizard ? wizard.colorScheme : colorScheme
|
|
||||||
|
|
||||||
signal startSetup()
|
|
||||||
|
|
||||||
function showAppleMailAutoconfigCertificateInstall() {
|
|
||||||
showAppleMailAutoconfigCommon();
|
|
||||||
descriptionLabel.text = qsTr("Apple Mail configuration is mostly automated, but in order to work, Bridge needs to install a certificate in your keychain.");
|
|
||||||
linkLabel1.setCallback(function() { Backend.openKBArticle("https://proton.me/support/apple-mail-certificate"); }, qsTr("Why is this certificate needed?"), true);
|
|
||||||
linkLabel2.clear();
|
|
||||||
}
|
|
||||||
function showAppleMailAutoconfigCommon() {
|
|
||||||
titleLabel.text = "";
|
|
||||||
linkLabel1.clear();
|
|
||||||
linkLabel2.clear();
|
|
||||||
iconSource = wizard.clientIconSource();
|
|
||||||
iconHeight = 80;
|
|
||||||
iconWidth = 80;
|
|
||||||
}
|
|
||||||
function showAppleMailAutoconfigProfileInstall() {
|
|
||||||
showAppleMailAutoconfigCommon();
|
|
||||||
descriptionLabel.text = qsTr("The final step before you can start using Apple Mail is to install the Bridge server profile in the system preferences.\n\nAdding a server profile is necessary to ensure that your Mac can receive and send Proton Mails.");
|
|
||||||
linkLabel1.setCallback(function() { Backend.openKBArticle("https://proton.me/support/macos-certificate-warning"); }, qsTr("Why is there a yellow warning sign?"), true);
|
|
||||||
linkLabel2.setCallback(wizard.showClientParams, qsTr("Configure Apple Mail manually"), false);
|
|
||||||
}
|
|
||||||
function showClientSelector(newAccount = true) {
|
|
||||||
titleLabel.text = "";
|
|
||||||
descriptionLabel.text = newAccount ? qsTr("Bridge is now connected to Proton, and has already started downloading your messages. Let’s now connect your email client to Bridge.") : qsTr("Let’s connect your email client to Bridge.");
|
|
||||||
linkLabel1.clear();
|
|
||||||
linkLabel2.clear();
|
|
||||||
iconSource = "/qml/icons/img-client-config-selector.svg";
|
|
||||||
iconHeight = 104;
|
|
||||||
iconWidth = 266;
|
|
||||||
}
|
|
||||||
function showLogin() {
|
|
||||||
showOnboarding();
|
|
||||||
}
|
|
||||||
function showLogin2FA() {
|
|
||||||
showOnboarding();
|
|
||||||
}
|
|
||||||
function showLoginMailboxPassword() {
|
|
||||||
showOnboarding();
|
|
||||||
}
|
|
||||||
|
|
||||||
function showNoAccount() {
|
|
||||||
titleLabel.text = welcomeTitle;
|
|
||||||
descriptionLabel.text = welcomeDescription;
|
|
||||||
linkLabel1.setCallback(startSetup, "Start setup", false);
|
|
||||||
linkLabel2.clear();
|
|
||||||
root.iconSource = welcomeImage;
|
|
||||||
root.iconHeight = welcomeImageHeight;
|
|
||||||
root.iconWidth = welcomeImageWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
function showOnboarding() {
|
|
||||||
titleLabel.text = (Backend.users.count === 0) ? welcomeTitle : addAccountTitle;
|
|
||||||
descriptionLabel.text = welcomeDescription
|
|
||||||
linkLabel1.setCallback(function() { Backend.openKBArticle("https://proton.me/support/why-you-need-bridge"); }, qsTr("Why do I need Bridge?"), true);
|
|
||||||
linkLabel2.clear();
|
|
||||||
root.iconSource = welcomeImage;
|
|
||||||
root.iconHeight = welcomeImageHeight;
|
|
||||||
root.iconWidth = welcomeImageWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: ProtonStyle.wizard_spacing_medium
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: icon
|
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
|
|
||||||
Layout.preferredHeight: root.iconHeight
|
|
||||||
Layout.preferredWidth: root.iconWidth
|
|
||||||
source: root.iconSource
|
|
||||||
sourceSize.height: root.iconHeight
|
|
||||||
sourceSize.width: root.iconWidth
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
id: titleLabel
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: _colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: ""
|
|
||||||
type: Label.LabelType.Heading
|
|
||||||
visible: text.length !== 0
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
id: descriptionLabel
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: _colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: ""
|
|
||||||
type: Label.LabelType.Body
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
LinkLabel {
|
|
||||||
id: linkLabel1
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
colorScheme: _colorScheme
|
|
||||||
visible: (text !== "")
|
|
||||||
}
|
|
||||||
LinkLabel {
|
|
||||||
id: linkLabel2
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
colorScheme: _colorScheme
|
|
||||||
visible: (text !== "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,479 +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
|
|
||||||
|
|
||||||
FocusScope {
|
|
||||||
id: root
|
|
||||||
enum RootStack {
|
|
||||||
Login,
|
|
||||||
TOTP,
|
|
||||||
MailboxPassword
|
|
||||||
}
|
|
||||||
|
|
||||||
property alias currentIndex: stackLayout.currentIndex
|
|
||||||
property alias username: usernameTextField.text
|
|
||||||
property var wizard
|
|
||||||
|
|
||||||
signal loginAbort(string username, bool wasSignedOut)
|
|
||||||
|
|
||||||
function abort() {
|
|
||||||
root.reset();
|
|
||||||
loginAbort(usernameTextField.text, false);
|
|
||||||
Backend.loginAbort(usernameTextField.text);
|
|
||||||
}
|
|
||||||
function reset(clearUsername = false) {
|
|
||||||
stackLayout.currentIndex = Login.RootStack.Login;
|
|
||||||
loginLayout.reset(clearUsername);
|
|
||||||
totpLayout.reset();
|
|
||||||
mailboxPasswordLayout.reset();
|
|
||||||
if (username.length === 0) {
|
|
||||||
usernameTextField.forceActiveFocus();
|
|
||||||
} else {
|
|
||||||
passwordTextField.forceActiveFocus();
|
|
||||||
}
|
|
||||||
passwordTextField.hidePassword();
|
|
||||||
secondPasswordTextField.hidePassword();
|
|
||||||
}
|
|
||||||
|
|
||||||
StackLayout {
|
|
||||||
id: stackLayout
|
|
||||||
function loginFailed() {
|
|
||||||
signInButton.loading = false;
|
|
||||||
usernameTextField.enabled = true;
|
|
||||||
usernameTextField.error = true;
|
|
||||||
passwordTextField.enabled = true;
|
|
||||||
passwordTextField.error = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
function onLogin2FAError(_) {
|
|
||||||
console.assert(stackLayout.currentIndex === Login.RootStack.TOTP, "Unexpected login2FAError");
|
|
||||||
twoFAButton.loading = false;
|
|
||||||
twoFactorPasswordTextField.enabled = true;
|
|
||||||
twoFactorPasswordTextField.error = true;
|
|
||||||
twoFactorPasswordTextField.errorString = qsTr("Your code is incorrect");
|
|
||||||
twoFactorPasswordTextField.focus = true;
|
|
||||||
}
|
|
||||||
function onLogin2FAErrorAbort(_) {
|
|
||||||
console.assert(stackLayout.currentIndex === Login.RootStack.TOTP, "Unexpected login2FAErrorAbort");
|
|
||||||
root.reset();
|
|
||||||
errorLabel.text = qsTr("Incorrect login credentials. Please try again.");
|
|
||||||
}
|
|
||||||
function onLogin2FARequested(username) {
|
|
||||||
console.assert(stackLayout.currentIndex === Login.RootStack.Login, "Unexpected login2FARequested");
|
|
||||||
twoFactorUsernameLabel.text = username;
|
|
||||||
stackLayout.currentIndex = Login.RootStack.TOTP;
|
|
||||||
twoFactorPasswordTextField.focus = true;
|
|
||||||
}
|
|
||||||
function onLogin2PasswordError(_) {
|
|
||||||
console.assert(stackLayout.currentIndex === Login.RootStack.MailboxPassword, "Unexpected login2PasswordError");
|
|
||||||
secondPasswordButton.loading = false;
|
|
||||||
secondPasswordTextField.enabled = true;
|
|
||||||
secondPasswordTextField.error = true;
|
|
||||||
secondPasswordTextField.errorString = qsTr("Your mailbox password is incorrect");
|
|
||||||
secondPasswordTextField.focus = true;
|
|
||||||
}
|
|
||||||
function onLogin2PasswordErrorAbort(_) {
|
|
||||||
console.assert(stackLayout.currentIndex === Login.RootStack.MailboxPassword, "Unexpected login2PasswordErrorAbort");
|
|
||||||
root.reset();
|
|
||||||
errorLabel.text = qsTr("Incorrect login credentials. Please try again.");
|
|
||||||
}
|
|
||||||
function onLogin2PasswordRequested(username) {
|
|
||||||
console.assert(stackLayout.currentIndex === Login.RootStack.Login || stackLayout.currentIndex === Login.RootStack.TOTP, "Unexpected login2PasswordRequested");
|
|
||||||
stackLayout.currentIndex = Login.RootStack.MailboxPassword;
|
|
||||||
mailboxPasswordUsernameLabel.text = username;
|
|
||||||
secondPasswordTextField.focus = true;
|
|
||||||
}
|
|
||||||
function onLoginAlreadyLoggedIn(_) {
|
|
||||||
stackLayout.currentIndex = Login.RootStack.Login;
|
|
||||||
root.reset();
|
|
||||||
}
|
|
||||||
function onLoginConnectionError(_) {
|
|
||||||
if (stackLayout.currentIndex === Login.RootStack.Login) {
|
|
||||||
stackLayout.loginFailed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onLoginFinished(_) {
|
|
||||||
stackLayout.currentIndex = Login.RootStack.Login;
|
|
||||||
root.reset();
|
|
||||||
}
|
|
||||||
function onLoginFreeUserError() {
|
|
||||||
console.assert(stackLayout.currentIndex === Login.RootStack.Login, "Unexpected loginFreeUserError");
|
|
||||||
stackLayout.loginFailed();
|
|
||||||
}
|
|
||||||
function onLoginUsernamePasswordError(errorMsg) {
|
|
||||||
console.assert(stackLayout.currentIndex === Login.RootStack.Login, "Unexpected loginUsernamePasswordError");
|
|
||||||
stackLayout.loginFailed();
|
|
||||||
if (errorMsg !== "")
|
|
||||||
errorLabel.text = errorMsg;
|
|
||||||
else
|
|
||||||
errorLabel.text = qsTr("Incorrect login credentials");
|
|
||||||
}
|
|
||||||
|
|
||||||
target: Backend
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
ColumnLayout {
|
|
||||||
id: loginLayout
|
|
||||||
function clearErrors() {
|
|
||||||
usernameTextField.error = false;
|
|
||||||
usernameTextField.errorString = "";
|
|
||||||
passwordTextField.error = false;
|
|
||||||
passwordTextField.errorString = "";
|
|
||||||
errorLabel.text = "";
|
|
||||||
}
|
|
||||||
function reset(clearUsername = false) {
|
|
||||||
signInButton.loading = false;
|
|
||||||
errorLabel.text = "";
|
|
||||||
usernameTextField.enabled = true;
|
|
||||||
usernameTextField.focus = true;
|
|
||||||
if (clearUsername) {
|
|
||||||
usernameTextField.text = "";
|
|
||||||
}
|
|
||||||
passwordTextField.enabled = true;
|
|
||||||
passwordTextField.text = "";
|
|
||||||
clearErrors();
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: ProtonStyle.wizard_spacing_medium
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: ProtonStyle.wizard_spacing_small
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: qsTr("Sign in")
|
|
||||||
type: Label.LabelType.Title
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: wizard.colorScheme.text_weak
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: qsTr("Enter your Proton Account details.")
|
|
||||||
type: Label.LabelType.Body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
ColorImage {
|
|
||||||
color: wizard.colorScheme.signal_danger
|
|
||||||
height: errorLabel.lineHeight
|
|
||||||
source: "/qml/icons/ic-exclamation-circle-filled.svg"
|
|
||||||
sourceSize.height: errorLabel.lineHeight
|
|
||||||
visible: errorLabel.text.length > 0
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
id: errorLabel
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.leftMargin: 4
|
|
||||||
color: wizard.colorScheme.signal_danger
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
type: Label.LabelType.Caption_semibold
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: usernameTextField
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
focus: true
|
|
||||||
label: qsTr("Email or username")
|
|
||||||
validateOnEditingFinished: false
|
|
||||||
validator: function (str) {
|
|
||||||
if (str.length === 0) {
|
|
||||||
return qsTr("Enter email or username");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onAccepted: passwordTextField.forceActiveFocus()
|
|
||||||
onTextChanged: {
|
|
||||||
loginLayout.clearErrors();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: passwordTextField
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
label: qsTr("Password")
|
|
||||||
validateOnEditingFinished: false
|
|
||||||
validator: function (str) {
|
|
||||||
if (str.length === 0) {
|
|
||||||
return qsTr("Enter password");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onAccepted: signInButton.checkAndSignIn()
|
|
||||||
onTextChanged: {
|
|
||||||
loginLayout.clearErrors();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
id: signInButton
|
|
||||||
function checkAndSignIn() {
|
|
||||||
usernameTextField.validate();
|
|
||||||
passwordTextField.validate();
|
|
||||||
if (usernameTextField.error || passwordTextField.error) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
usernameTextField.enabled = false;
|
|
||||||
passwordTextField.enabled = false;
|
|
||||||
loading = true;
|
|
||||||
Backend.login(usernameTextField.text, Qt.btoa(passwordTextField.text));
|
|
||||||
}
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
enabled: !loading
|
|
||||||
text: loading ? qsTr("Signing in") : qsTr("Sign in")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
checkAndSignIn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
enabled: !signInButton.loading
|
|
||||||
secondary: true
|
|
||||||
secondaryIsOpaque: true
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
root.abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LinkLabel {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
external: true
|
|
||||||
link: "https://proton.me/mail/pricing"
|
|
||||||
text: qsTr("Create or upgrade your account")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
ColumnLayout {
|
|
||||||
id: totpLayout
|
|
||||||
function reset() {
|
|
||||||
twoFAButton.loading = false;
|
|
||||||
twoFactorPasswordTextField.enabled = true;
|
|
||||||
twoFactorPasswordTextField.error = false;
|
|
||||||
twoFactorPasswordTextField.errorString = "";
|
|
||||||
twoFactorPasswordTextField.text = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: ProtonStyle.wizard_spacing_medium
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: ProtonStyle.wizard_spacing_small
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: qsTr("Two-factor authentication")
|
|
||||||
type: Label.LabelType.Title
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
id: twoFactorUsernameLabel
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: wizard.colorScheme.text_weak
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: ""
|
|
||||||
type: Label.LabelType.Body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
id: descriptionLabel
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: qsTr("You have enabled two-factor authentication. Please enter the 6-digit code provided by your authenticator application.")
|
|
||||||
type: Label.LabelType.Body
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: twoFactorPasswordTextField
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
label: qsTr("Two-factor code")
|
|
||||||
validateOnEditingFinished: false
|
|
||||||
validator: function (str) {
|
|
||||||
if (str.length === 0) {
|
|
||||||
return qsTr("Enter the 6-digit code");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onAccepted: {
|
|
||||||
twoFAButton.onClicked();
|
|
||||||
}
|
|
||||||
onTextChanged: {
|
|
||||||
if (text.length >= 6) {
|
|
||||||
twoFAButton.onClicked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
id: twoFAButton
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
enabled: !loading
|
|
||||||
text: loading ? qsTr("Authenticating") : qsTr("Authenticate")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
twoFactorPasswordTextField.validate();
|
|
||||||
if (twoFactorPasswordTextField.error) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
twoFactorPasswordTextField.enabled = false;
|
|
||||||
loading = true;
|
|
||||||
Backend.login2FA(usernameTextField.text, Qt.btoa(twoFactorPasswordTextField.text));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
enabled: !twoFAButton.loading
|
|
||||||
secondary: true
|
|
||||||
secondaryIsOpaque: true
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
root.abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
ColumnLayout {
|
|
||||||
id: mailboxPasswordLayout
|
|
||||||
function reset() {
|
|
||||||
secondPasswordButton.loading = false;
|
|
||||||
secondPasswordTextField.enabled = true;
|
|
||||||
secondPasswordTextField.error = false;
|
|
||||||
secondPasswordTextField.errorString = "";
|
|
||||||
secondPasswordTextField.text = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: ProtonStyle.wizard_spacing_medium
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: ProtonStyle.wizard_spacing_small
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: qsTr("Unlock your mailbox")
|
|
||||||
type: Label.LabelType.Title
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
id: mailboxPasswordUsernameLabel
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: wizard.colorScheme.text_weak
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: ""
|
|
||||||
type: Label.LabelType.Body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: qsTr("You have secured your account with a separate mailbox password.")
|
|
||||||
type: Label.LabelType.Body
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: secondPasswordTextField
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
label: qsTr("Mailbox password")
|
|
||||||
validateOnEditingFinished: false
|
|
||||||
validator: function (str) {
|
|
||||||
if (str.length === 0) {
|
|
||||||
return qsTr("Enter password");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onAccepted: {
|
|
||||||
secondPasswordButton.onClicked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
id: secondPasswordButton
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
enabled: !loading
|
|
||||||
text: loading ? qsTr("Unlocking") : qsTr("Unlock")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
secondPasswordTextField.validate();
|
|
||||||
if (secondPasswordTextField.error) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
secondPasswordTextField.enabled = false;
|
|
||||||
loading = true;
|
|
||||||
Backend.login2Password(usernameTextField.text, Qt.btoa(secondPasswordTextField.text));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
enabled: !secondPasswordButton.loading
|
|
||||||
secondary: true
|
|
||||||
secondaryIsOpaque: true
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
root.abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,52 +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
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property var wizard
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: ProtonStyle.wizard_spacing_large
|
|
||||||
|
|
||||||
StepDescriptionBox {
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
description: qsTr("Connect Bridge to your Proton account")
|
|
||||||
icon: "/qml/icons/ic-bridge.svg"
|
|
||||||
iconSize: 48
|
|
||||||
title: qsTr("Step 1")
|
|
||||||
}
|
|
||||||
StepDescriptionBox {
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
description: qsTr("Connect your email client to Bridge")
|
|
||||||
icon: "/qml/icons/img-mail-clients.svg"
|
|
||||||
iconSize: 48
|
|
||||||
title: qsTr("Step 2")
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: wizard.colorScheme
|
|
||||||
text: qsTr("Start setup")
|
|
||||||
|
|
||||||
onClicked: wizard.showLogin()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,300 +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
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
enum Client {
|
|
||||||
AppleMail,
|
|
||||||
MicrosoftOutlook,
|
|
||||||
MozillaThunderbird,
|
|
||||||
Generic
|
|
||||||
}
|
|
||||||
enum ContentStack {
|
|
||||||
Onboarding,
|
|
||||||
Login,
|
|
||||||
ClientConfigSelector,
|
|
||||||
ClientConfigAppleMail
|
|
||||||
}
|
|
||||||
enum RootStack {
|
|
||||||
TwoPanesView,
|
|
||||||
ClientConfigParameters,
|
|
||||||
ClientConfigEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
property string address
|
|
||||||
property var backAction: null
|
|
||||||
property int client
|
|
||||||
property ColorScheme colorScheme
|
|
||||||
property var user
|
|
||||||
|
|
||||||
signal bugReportRequested
|
|
||||||
signal wizardEnded
|
|
||||||
|
|
||||||
function _showClientConfig() {
|
|
||||||
showClientConfig(root.user, root.address, false);
|
|
||||||
}
|
|
||||||
function clientIconSource() {
|
|
||||||
switch (client) {
|
|
||||||
case SetupWizard.Client.AppleMail:
|
|
||||||
return "/qml/icons/ic-apple-mail.svg";
|
|
||||||
case SetupWizard.Client.MicrosoftOutlook:
|
|
||||||
return "/qml/icons/ic-microsoft-outlook.svg";
|
|
||||||
case SetupWizard.Client.MozillaThunderbird:
|
|
||||||
return "/qml/icons/ic-mozilla-thunderbird.svg";
|
|
||||||
case SetupWizard.Client.Generic:
|
|
||||||
return "/qml/icons/ic-other-mail-clients.svg";
|
|
||||||
default:
|
|
||||||
console.error("Unknown mail client " + client);
|
|
||||||
return "/qml/icons/ic-other-mail-clients.svg";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function clientName() {
|
|
||||||
switch (client) {
|
|
||||||
case SetupWizard.Client.AppleMail:
|
|
||||||
return "Apple Mail";
|
|
||||||
case SetupWizard.Client.MicrosoftOutlook:
|
|
||||||
return "Outlook";
|
|
||||||
case SetupWizard.Client.MozillaThunderbird:
|
|
||||||
return "Thunderbird";
|
|
||||||
case SetupWizard.Client.Generic:
|
|
||||||
return qsTr("your email client");
|
|
||||||
default:
|
|
||||||
console.error("Unknown mail client " + client);
|
|
||||||
return qsTr("your email client");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function closeWizard() {
|
|
||||||
wizardEnded();
|
|
||||||
}
|
|
||||||
function setupGuideLink() {
|
|
||||||
switch (client) {
|
|
||||||
case SetupWizard.Client.AppleMail:
|
|
||||||
return "https://proton.me/support/protonmail-bridge-clients-apple-mail";
|
|
||||||
case SetupWizard.Client.MicrosoftOutlook:
|
|
||||||
return (Backend.goos === "darwin") ? "https://proton.me/support/protonmail-bridge-clients-macos-outlook-2019" : "https://proton.me/support/protonmail-bridge-clients-windows-outlook-2019";
|
|
||||||
case SetupWizard.Client.MozillaThunderbird:
|
|
||||||
return "https://proton.me/support/protonmail-bridge-clients-windows-thunderbird";
|
|
||||||
default:
|
|
||||||
return "https://proton.me/support/protonmail-bridge-configure-client";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function showAppleMailAutoConfig() {
|
|
||||||
backAction = _showClientConfig;
|
|
||||||
rootStackLayout.currentIndex = SetupWizard.RootStack.TwoPanesView;
|
|
||||||
rightContent.currentIndex = SetupWizard.ContentStack.ClientConfigAppleMail;
|
|
||||||
clientConfigAppleMail.showAutoconfig(); // This will trigger signals that will display the appropriate left content.
|
|
||||||
}
|
|
||||||
function showBugReport() {
|
|
||||||
closeWizard();
|
|
||||||
bugReportRequested();
|
|
||||||
}
|
|
||||||
function showClientConfig(user, address, justLoggedIn) {
|
|
||||||
backAction = null;
|
|
||||||
root.user = user;
|
|
||||||
root.address = address;
|
|
||||||
rootStackLayout.currentIndex = SetupWizard.RootStack.TwoPanesView;
|
|
||||||
leftContent.showClientSelector(justLoggedIn);
|
|
||||||
rightContent.currentIndex = SetupWizard.ContentStack.ClientConfigSelector;
|
|
||||||
}
|
|
||||||
function showClientConfigEnd() {
|
|
||||||
backAction = null;
|
|
||||||
rootStackLayout.currentIndex = SetupWizard.RootStack.ClientConfigEnd;
|
|
||||||
}
|
|
||||||
function showClientParams() {
|
|
||||||
backAction = _showClientConfig;
|
|
||||||
rootStackLayout.currentIndex = SetupWizard.RootStack.ClientConfigParameters;
|
|
||||||
}
|
|
||||||
function showLogin(username = "") {
|
|
||||||
backAction = null;
|
|
||||||
rootStackLayout.currentIndex = SetupWizard.RootStack.TwoPanesView;
|
|
||||||
root.address = "";
|
|
||||||
leftContent.showLogin();
|
|
||||||
rightContent.currentIndex = SetupWizard.ContentStack.Login;
|
|
||||||
login.username = username;
|
|
||||||
login.reset(false);
|
|
||||||
}
|
|
||||||
function showOnboarding() {
|
|
||||||
backAction = null;
|
|
||||||
rootStackLayout.currentIndex = SetupWizard.RootStack.TwoPanesView;
|
|
||||||
root.address = "";
|
|
||||||
root.user = null;
|
|
||||||
leftContent.showOnboarding();
|
|
||||||
rightContent.currentIndex = SetupWizard.ContentStack.Onboarding;
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
function onLoginFinished(userIndex, wasSignedOut) {
|
|
||||||
if (wasSignedOut) {
|
|
||||||
closeWizard();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let user = Backend.users.get(userIndex);
|
|
||||||
let address = user ? user.addresses[0] : "";
|
|
||||||
showClientConfig(user, address, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
target: Backend
|
|
||||||
}
|
|
||||||
StackLayout {
|
|
||||||
id: rootStackLayout
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
// rootStackLayout index 0
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: leftHalf
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: root.colorScheme.background_norm
|
|
||||||
|
|
||||||
LeftPane {
|
|
||||||
id: leftContent
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: ProtonStyle.wizard_pane_bottomMargin
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: ProtonStyle.wizard_window_margin
|
|
||||||
clip: true
|
|
||||||
width: ProtonStyle.wizard_pane_width
|
|
||||||
wizard: root
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
function onAppleMailAutoconfigCertificateInstallPageShown() {
|
|
||||||
leftContent.showAppleMailAutoconfigCertificateInstall();
|
|
||||||
}
|
|
||||||
function onAppleMailAutoconfigProfileInstallPageShow() {
|
|
||||||
leftContent.showAppleMailAutoconfigProfileInstall();
|
|
||||||
}
|
|
||||||
|
|
||||||
target: clientConfigAppleMail
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
function onLogin2FARequested() {
|
|
||||||
leftContent.showLogin2FA();
|
|
||||||
}
|
|
||||||
function onLogin2PasswordRequested() {
|
|
||||||
leftContent.showLoginMailboxPassword();
|
|
||||||
}
|
|
||||||
|
|
||||||
target: Backend
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Image {
|
|
||||||
id: mailLogoWithWordmark
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: ProtonStyle.wizard_window_margin
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
height: sourceSize.height
|
|
||||||
source: root.colorScheme.mail_logo_with_wordmark
|
|
||||||
sourceSize.height: 36
|
|
||||||
sourceSize.width: 134
|
|
||||||
width: sourceSize.width
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Rectangle {
|
|
||||||
id: rightHalf
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: root.colorScheme.background_weak
|
|
||||||
|
|
||||||
StackLayout {
|
|
||||||
id: rightContent
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: ProtonStyle.wizard_pane_bottomMargin
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: ProtonStyle.wizard_window_margin
|
|
||||||
clip: true
|
|
||||||
currentIndex: 0
|
|
||||||
width: ProtonStyle.wizard_pane_width
|
|
||||||
|
|
||||||
// rightContent stack index 0
|
|
||||||
Onboarding {
|
|
||||||
wizard: root
|
|
||||||
}
|
|
||||||
|
|
||||||
// rightContent tack index 1
|
|
||||||
Login {
|
|
||||||
id: login
|
|
||||||
wizard: root
|
|
||||||
|
|
||||||
onLoginAbort: {
|
|
||||||
root.closeWizard();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// rightContent stack index 2
|
|
||||||
ClientConfigSelector {
|
|
||||||
id: clientConfigSelector
|
|
||||||
wizard: root
|
|
||||||
}
|
|
||||||
// rightContent stack index 3
|
|
||||||
ClientConfigAppleMail {
|
|
||||||
id: clientConfigAppleMail
|
|
||||||
wizard: root
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// rootStackLayout index 1
|
|
||||||
ClientConfigParameters {
|
|
||||||
id: clientConfigParameters
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
wizard: root
|
|
||||||
}
|
|
||||||
|
|
||||||
// rootStackLayout index 2
|
|
||||||
ClientConfigEnd {
|
|
||||||
id: clientConfigEnd
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
wizard: root
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HelpButton {
|
|
||||||
wizard: root
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
id: backButton
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: ProtonStyle.wizard_window_margin
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: ProtonStyle.wizard_window_margin
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
icon.source: "/qml/icons/ic-chevron-left.svg"
|
|
||||||
iconOnTheLeft: true
|
|
||||||
secondary: true
|
|
||||||
secondaryIsOpaque: true
|
|
||||||
spacing: ProtonStyle.wizard_spacing_small
|
|
||||||
text: qsTr("Back")
|
|
||||||
visible: backAction != null
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if (backAction) {
|
|
||||||
backAction();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,59 +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
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property ColorScheme colorScheme
|
|
||||||
property string description
|
|
||||||
property string icon
|
|
||||||
property int iconSize: 64
|
|
||||||
property string title
|
|
||||||
|
|
||||||
spacing: ProtonStyle.wizard_spacing_large
|
|
||||||
|
|
||||||
Image {
|
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
|
||||||
Layout.preferredHeight: iconSize
|
|
||||||
Layout.preferredWidth: iconSize
|
|
||||||
mipmap: true
|
|
||||||
source: root.icon
|
|
||||||
}
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: ProtonStyle.wizard_spacing_small
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
|
||||||
Layout.fillHeight: false
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: root.title
|
|
||||||
type: Label.LabelType.Body_bold
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: root.description
|
|
||||||
type: Label.LabelType.Body
|
|
||||||
verticalAlignment: Text.AlignTop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
413
internal/frontend/bridge-gui/bridge-gui/qml/SignIn.qml
Normal file
@ -0,0 +1,413 @@
|
|||||||
|
// 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 QtQuick.Controls.impl
|
||||||
|
import Proton
|
||||||
|
|
||||||
|
FocusScope {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property ColorScheme colorScheme
|
||||||
|
property alias currentIndex: stackLayout.currentIndex
|
||||||
|
property alias username: usernameTextField.text
|
||||||
|
|
||||||
|
function abort() {
|
||||||
|
root.reset();
|
||||||
|
Backend.loginAbort(usernameTextField.text);
|
||||||
|
}
|
||||||
|
function reset() {
|
||||||
|
stackLayout.currentIndex = 0;
|
||||||
|
loginNormalLayout.reset();
|
||||||
|
login2FALayout.reset();
|
||||||
|
login2PasswordLayout.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitHeight: children[0].implicitHeight
|
||||||
|
implicitWidth: children[0].implicitWidth
|
||||||
|
state: "Page 1"
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "Page 1"
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
currentIndex: 0
|
||||||
|
target: stackLayout
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "Page 2"
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
currentIndex: 1
|
||||||
|
target: stackLayout
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "Page 3"
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
currentIndex: 2
|
||||||
|
target: stackLayout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
StackLayout {
|
||||||
|
id: stackLayout
|
||||||
|
function loginFailed() {
|
||||||
|
signInButton.loading = false;
|
||||||
|
usernameTextField.enabled = true;
|
||||||
|
usernameTextField.error = true;
|
||||||
|
passwordTextField.enabled = true;
|
||||||
|
passwordTextField.error = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onLogin2FAError(_) {
|
||||||
|
console.assert(stackLayout.currentIndex === 1, "Unexpected login2FAError");
|
||||||
|
twoFAButton.loading = false;
|
||||||
|
twoFactorPasswordTextField.enabled = true;
|
||||||
|
twoFactorPasswordTextField.error = true;
|
||||||
|
twoFactorPasswordTextField.errorString = qsTr("Your code is incorrect");
|
||||||
|
twoFactorPasswordTextField.focus = true;
|
||||||
|
}
|
||||||
|
function onLogin2FAErrorAbort(_) {
|
||||||
|
console.assert(stackLayout.currentIndex === 1, "Unexpected login2FAErrorAbort");
|
||||||
|
root.reset();
|
||||||
|
errorLabel.text = qsTr("Incorrect login credentials. Please try again.");
|
||||||
|
}
|
||||||
|
function onLogin2FARequested(username) {
|
||||||
|
console.assert(stackLayout.currentIndex === 0, "Unexpected login2FARequested");
|
||||||
|
twoFactorUsernameLabel.text = username;
|
||||||
|
stackLayout.currentIndex = 1;
|
||||||
|
twoFactorPasswordTextField.focus = true;
|
||||||
|
}
|
||||||
|
function onLogin2PasswordError(_) {
|
||||||
|
console.assert(stackLayout.currentIndex === 2, "Unexpected login2PasswordError");
|
||||||
|
secondPasswordButton.loading = false;
|
||||||
|
secondPasswordTextField.enabled = true;
|
||||||
|
secondPasswordTextField.error = true;
|
||||||
|
secondPasswordTextField.errorString = qsTr("Your mailbox password is incorrect");
|
||||||
|
secondPasswordTextField.focus = true;
|
||||||
|
}
|
||||||
|
function onLogin2PasswordErrorAbort(_) {
|
||||||
|
console.assert(stackLayout.currentIndex === 2, "Unexpected login2PasswordErrorAbort");
|
||||||
|
root.reset();
|
||||||
|
errorLabel.text = qsTr("Incorrect login credentials. Please try again.");
|
||||||
|
}
|
||||||
|
function onLogin2PasswordRequested() {
|
||||||
|
console.assert(stackLayout.currentIndex === 0 || stackLayout.currentIndex === 1, "Unexpected login2PasswordRequested");
|
||||||
|
stackLayout.currentIndex = 2;
|
||||||
|
secondPasswordTextField.focus = true;
|
||||||
|
}
|
||||||
|
function onLoginAlreadyLoggedIn(_) {
|
||||||
|
stackLayout.currentIndex = 0;
|
||||||
|
root.reset();
|
||||||
|
}
|
||||||
|
function onLoginConnectionError(_) {
|
||||||
|
if (stackLayout.currentIndex === 0) {
|
||||||
|
stackLayout.loginFailed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onLoginFinished(_) {
|
||||||
|
stackLayout.currentIndex = 0;
|
||||||
|
root.reset();
|
||||||
|
}
|
||||||
|
function onLoginFreeUserError() {
|
||||||
|
console.assert(stackLayout.currentIndex === 0, "Unexpected loginFreeUserError");
|
||||||
|
stackLayout.loginFailed();
|
||||||
|
}
|
||||||
|
function onLoginUsernamePasswordError(errorMsg) {
|
||||||
|
console.assert(stackLayout.currentIndex === 0, "Unexpected loginUsernamePasswordError");
|
||||||
|
stackLayout.loginFailed();
|
||||||
|
if (errorMsg !== "")
|
||||||
|
errorLabel.text = errorMsg;
|
||||||
|
else
|
||||||
|
errorLabel.text = qsTr("Incorrect login credentials");
|
||||||
|
}
|
||||||
|
|
||||||
|
target: Backend
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
id: loginNormalLayout
|
||||||
|
function reset() {
|
||||||
|
signInButton.loading = false;
|
||||||
|
errorLabel.text = "";
|
||||||
|
usernameTextField.enabled = true;
|
||||||
|
usernameTextField.error = false;
|
||||||
|
usernameTextField.errorString = "";
|
||||||
|
usernameTextField.focus = true;
|
||||||
|
passwordTextField.enabled = true;
|
||||||
|
passwordTextField.error = false;
|
||||||
|
passwordTextField.errorString = "";
|
||||||
|
passwordTextField.text = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.topMargin: 16
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
text: qsTr("Sign in")
|
||||||
|
type: Label.LabelType.Title
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
id: subTitle
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.topMargin: 8
|
||||||
|
color: root.colorScheme.text_weak
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
text: qsTr("Enter your Proton Account details.")
|
||||||
|
type: Label.LabelType.Body
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 36
|
||||||
|
spacing: 0
|
||||||
|
visible: errorLabel.text.length > 0
|
||||||
|
|
||||||
|
ColorImage {
|
||||||
|
color: root.colorScheme.signal_danger
|
||||||
|
height: errorLabel.lineHeight
|
||||||
|
source: "/qml/icons/ic-exclamation-circle-filled.svg"
|
||||||
|
sourceSize.height: errorLabel.lineHeight
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
id: errorLabel
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 4
|
||||||
|
color: root.colorScheme.signal_danger
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
type: root.error ? Label.LabelType.Caption_semibold : Label.LabelType.Caption
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: usernameTextField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 24
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
focus: true
|
||||||
|
label: qsTr("Email or username")
|
||||||
|
validateOnEditingFinished: false
|
||||||
|
validator: function (str) {
|
||||||
|
if (str.length === 0) {
|
||||||
|
return qsTr("Enter email or username");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccepted: passwordTextField.forceActiveFocus()
|
||||||
|
onTextChanged: {
|
||||||
|
// remove "invalid username / password error"
|
||||||
|
if (error || errorLabel.text.length > 0) {
|
||||||
|
errorLabel.text = "";
|
||||||
|
usernameTextField.error = false;
|
||||||
|
passwordTextField.error = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: passwordTextField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 8
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
label: qsTr("Password")
|
||||||
|
validateOnEditingFinished: false
|
||||||
|
validator: function (str) {
|
||||||
|
if (str.length === 0) {
|
||||||
|
return qsTr("Enter password");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccepted: signInButton.checkAndSignIn()
|
||||||
|
onTextChanged: {
|
||||||
|
// remove "invalid username / password error"
|
||||||
|
if (error || errorLabel.text.length > 0) {
|
||||||
|
errorLabel.text = "";
|
||||||
|
usernameTextField.error = false;
|
||||||
|
passwordTextField.error = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
id: signInButton
|
||||||
|
function checkAndSignIn() {
|
||||||
|
usernameTextField.validate();
|
||||||
|
passwordTextField.validate();
|
||||||
|
if (usernameTextField.error || passwordTextField.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
usernameTextField.enabled = false;
|
||||||
|
passwordTextField.enabled = false;
|
||||||
|
loading = true;
|
||||||
|
Backend.login(usernameTextField.text, Qt.btoa(passwordTextField.text));
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 24
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
enabled: !loading
|
||||||
|
text: loading ? qsTr("Signing in") : qsTr("Sign in")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
checkAndSignIn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.topMargin: 24
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
text: link("https://proton.me/mail/pricing", qsTr("Create or upgrade your account"))
|
||||||
|
textFormat: Text.StyledText
|
||||||
|
type: Label.LabelType.Body
|
||||||
|
|
||||||
|
onLinkActivated: {
|
||||||
|
Qt.openUrlExternally(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
id: login2FALayout
|
||||||
|
function reset() {
|
||||||
|
twoFAButton.loading = false;
|
||||||
|
twoFactorPasswordTextField.enabled = true;
|
||||||
|
twoFactorPasswordTextField.error = false;
|
||||||
|
twoFactorPasswordTextField.errorString = "";
|
||||||
|
twoFactorPasswordTextField.text = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
Layout.topMargin: 16
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
text: qsTr("Two-factor authentication")
|
||||||
|
type: Label.LabelType.Heading
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
id: twoFactorUsernameLabel
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
Layout.topMargin: 8
|
||||||
|
color: root.colorScheme.text_weak
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
type: Label.LabelType.Lead
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: twoFactorPasswordTextField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 32
|
||||||
|
assistiveText: qsTr("Enter the 6-digit code")
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
label: qsTr("Two-factor code")
|
||||||
|
validateOnEditingFinished: false
|
||||||
|
validator: function (str) {
|
||||||
|
if (str.length === 0) {
|
||||||
|
return qsTr("Enter the 6-digit code");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
twoFAButton.onClicked();
|
||||||
|
}
|
||||||
|
onTextChanged: {
|
||||||
|
if (text.length >= 6) {
|
||||||
|
twoFAButton.onClicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
id: twoFAButton
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 24
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
enabled: !loading
|
||||||
|
text: loading ? qsTr("Authenticating") : qsTr("Authenticate")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
twoFactorPasswordTextField.validate();
|
||||||
|
if (twoFactorPasswordTextField.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
twoFactorPasswordTextField.enabled = false;
|
||||||
|
loading = true;
|
||||||
|
Backend.login2FA(usernameTextField.text, Qt.btoa(twoFactorPasswordTextField.text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
id: login2PasswordLayout
|
||||||
|
function reset() {
|
||||||
|
secondPasswordButton.loading = false;
|
||||||
|
secondPasswordTextField.enabled = true;
|
||||||
|
secondPasswordTextField.error = false;
|
||||||
|
secondPasswordTextField.errorString = "";
|
||||||
|
secondPasswordTextField.text = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
Layout.topMargin: 16
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
text: qsTr("Unlock your mailbox")
|
||||||
|
type: Label.LabelType.Heading
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: secondPasswordTextField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 8 + implicitHeight + 24 + subTitle.implicitHeight
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
label: qsTr("Mailbox password")
|
||||||
|
validateOnEditingFinished: false
|
||||||
|
validator: function (str) {
|
||||||
|
if (str.length === 0) {
|
||||||
|
return qsTr("Enter password");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
secondPasswordButton.onClicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
id: secondPasswordButton
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 24
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
enabled: !loading
|
||||||
|
text: loading ? qsTr("Unlocking") : qsTr("Unlock")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
secondPasswordTextField.validate();
|
||||||
|
if (secondPasswordTextField.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
secondPasswordTextField.enabled = false;
|
||||||
|
loading = true;
|
||||||
|
Backend.login2Password(usernameTextField.text, Qt.btoa(secondPasswordTextField.text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
245
internal/frontend/bridge-gui/bridge-gui/qml/WelcomeGuide.qml
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property ColorScheme colorScheme
|
||||||
|
|
||||||
|
implicitHeight: children[0].implicitHeight
|
||||||
|
implicitWidth: children[0].implicitWidth
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "Page 1"
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
currentIndex: 0
|
||||||
|
target: signInItem
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "Page 2"
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
currentIndex: 1
|
||||||
|
target: signInItem
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "Page 3"
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
currentIndex: 2
|
||||||
|
target: signInItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: root.colorScheme.background_norm
|
||||||
|
implicitHeight: children[0].implicitHeight
|
||||||
|
implicitWidth: children[0].implicitWidth
|
||||||
|
visible: signInItem.currentIndex === 0
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
columnSpacing: 0
|
||||||
|
columns: 3
|
||||||
|
rowSpacing: 0
|
||||||
|
|
||||||
|
// top margin
|
||||||
|
Item {
|
||||||
|
Layout.columnSpan: 3
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
// Using binding component here instead of direct binding to avoid binding loop during construction of element
|
||||||
|
Binding on Layout.preferredHeight {
|
||||||
|
value: (parent.height - welcomeContentItem.height) / 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// left margin
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: 80
|
||||||
|
Layout.minimumWidth: 48
|
||||||
|
Layout.preferredHeight: welcomeContentItem.height
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
id: welcomeContentItem
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.topMargin: 16
|
||||||
|
source: colorScheme.welcome_img
|
||||||
|
sourceSize.height: 148
|
||||||
|
sourceSize.width: 264
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 16
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: qsTr("Welcome to\nProton Mail Bridge")
|
||||||
|
type: Label.LabelType.Heading
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
id: longTextLabel
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredWidth: 320
|
||||||
|
Layout.topMargin: 16
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: qsTr("Add your Proton Mail account to securely access and manage your messages in your favorite email client. Bridge runs in the background and encrypts and decrypts your messages seamlessly.")
|
||||||
|
type: Label.LabelType.Body
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right margin
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: 80
|
||||||
|
Layout.minimumWidth: 48
|
||||||
|
Layout.preferredHeight: welcomeContentItem.height
|
||||||
|
}
|
||||||
|
|
||||||
|
// bottom margin
|
||||||
|
Item {
|
||||||
|
Layout.columnSpan: 3
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: children[0].implicitHeight + children[0].anchors.bottomMargin + children[0].anchors.topMargin
|
||||||
|
implicitWidth: children[0].implicitWidth
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: logoImage
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 48
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.topMargin: 48
|
||||||
|
source: colorScheme.logo_img
|
||||||
|
sourceSize.height: 25
|
||||||
|
sourceSize.width: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: (signInItem.currentIndex == 0) ? root.colorScheme.background_weak : root.colorScheme.background_norm
|
||||||
|
implicitHeight: children[0].implicitHeight
|
||||||
|
implicitWidth: children[0].implicitWidth
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredWidth: signInItem.currentIndex == 0 ? 0 : parent.width / 4
|
||||||
|
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
||||||
|
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
||||||
|
|
||||||
|
Button {
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 80
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 80
|
||||||
|
anchors.rightMargin: 80
|
||||||
|
anchors.topMargin: 80
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
secondary: true
|
||||||
|
text: qsTr("Back")
|
||||||
|
visible: signInItem.currentIndex != 0
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
signInItem.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GridLayout {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
columnSpacing: 0
|
||||||
|
columns: 3
|
||||||
|
rowSpacing: 0
|
||||||
|
|
||||||
|
// top margin
|
||||||
|
Item {
|
||||||
|
Layout.columnSpan: 3
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
// Using binding component here instead of direct binding to avoid binding loop during construction of element
|
||||||
|
Binding on Layout.preferredHeight {
|
||||||
|
value: (parent.height - signInItem.height) / 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// left margin
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: 80
|
||||||
|
Layout.minimumWidth: 48
|
||||||
|
Layout.preferredHeight: signInItem.height
|
||||||
|
}
|
||||||
|
SignIn {
|
||||||
|
id: signInItem
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredWidth: 320
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
focus: true
|
||||||
|
username: Backend.users.count === 1 && Backend.users.get(0) && (Backend.users.get(0).state === EUserState.SignedOut) ? Backend.users.get(0).username : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right margin
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: 80
|
||||||
|
Layout.minimumWidth: 48
|
||||||
|
Layout.preferredHeight: signInItem.height
|
||||||
|
}
|
||||||
|
|
||||||
|
// bottom margin
|
||||||
|
Item {
|
||||||
|
Layout.columnSpan: 3
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.preferredWidth: signInItem.currentIndex === 0 ? 0 : parent.width / 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2022 Proton AG
|
||||||
//
|
//
|
||||||
// This file is part of Proton Mail Bridge.Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
//
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@ -15,23 +15,23 @@
|
|||||||
// 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/>.
|
||||||
|
|
||||||
package main
|
import QmlProject 1.1
|
||||||
|
|
||||||
import (
|
Project {
|
||||||
"fmt"
|
mainFile: "./MainWindow.qml"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v3/internal/user"
|
/* Include .qml, .js, and image files from current directory and subdirectories */
|
||||||
)
|
QmlFiles {
|
||||||
|
directory: "./"
|
||||||
func main() {
|
}
|
||||||
if len(os.Args) < 2 {
|
JavaScriptFiles {
|
||||||
fmt.Printf("Usage: %v <dump dir>\n", os.Args[0])
|
directory: "./"
|
||||||
return
|
}
|
||||||
}
|
ImageFiles {
|
||||||
|
directory: "./"
|
||||||
if err := user.TryBuildDebugMessage(os.Args[1]); err != nil {
|
}
|
||||||
fmt.Printf("%v\n", err.Error())
|
/* List of plugin directories passed to QML runtime */
|
||||||
return
|
importPaths: [
|
||||||
}
|
"./"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
@ -1,13 +0,0 @@
|
|||||||
<?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 128 128" 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(1.6,0,0,1.6,9.6,5.6)">
|
|
||||||
<path d="M34,0C15.497,0 0.5,14.852 0.5,33.176L0.5,66.572C0.5,70.122 3.406,73 6.991,73L61.702,73C64.903,73 67.5,70.428 67.5,67.258L67.5,33.176C67.5,14.855 52.503,0 34,0ZM53.283,32.992L38.641,45.207C35.998,47.413 32.133,47.413 29.49,45.207L14.848,32.992C14.848,22.625 23.334,14.221 33.802,14.221L34.329,14.221C44.796,14.221 53.283,22.625 53.283,32.992Z" style="fill:rgb(109,74,255);fill-rule:nonzero;"/>
|
|
||||||
<path d="M34,0C15.497,0 0.5,14.852 0.5,33.176L0.5,66.572C0.5,70.122 3.406,73 6.991,73L61.702,73C64.903,73 67.5,70.428 67.5,67.258L67.5,33.176C67.5,14.855 52.503,0 34,0ZM53.283,32.992L38.641,45.207C35.998,47.413 32.133,47.413 29.49,45.207L14.848,32.992C14.848,22.625 23.334,14.221 33.802,14.221L34.329,14.221C44.796,14.221 53.283,22.625 53.283,32.992Z" style="fill:url(#_Linear1);fill-rule:nonzero;"/>
|
|
||||||
<path d="M38.664,45.355C37.138,46.579 33.168,48.292 29.504,45.355C25.841,42.418 18.237,35.924 14.893,33.045L14.911,33.045L14.848,32.992C14.848,22.625 23.334,14.221 33.802,14.221L34.329,14.221C44.796,14.221 53.283,22.625 53.283,32.992L53.219,33.045L53.276,33.045L53.276,73L61.702,73C64.903,73 67.5,70.428 67.5,67.258L67.5,33.176C67.5,14.855 52.503,0 34,0C15.497,0 0.5,14.852 0.5,33.176L0.5,35.274L21.217,52.914C22.744,54.354 26.757,56.37 30.595,52.914C34.433,49.459 37.574,46.435 38.664,45.355Z" style="fill:url(#_Radial2);fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(9.85984,-28.1373,28.1373,9.85984,2.89896,81.4231)"><stop offset="0" style="stop-color:rgb(40,176,232);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(197,183,255);stop-opacity:0"/></linearGradient>
|
|
||||||
<radialGradient id="_Radial2" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-63.0498,-56.1536,45.8629,-51.4953,63.5497,79.0473)"><stop offset="0" style="stop-color:rgb(226,219,255);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(109,74,255);stop-opacity:1"/></radialGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.5 KiB |
@ -1,3 +0,0 @@
|
|||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.8363 13.87C11.0407 13.6842 11.0557 13.368 10.87 13.1637L6.17573 8L10.87 2.83633C11.0557 2.632 11.0407 2.31578 10.8363 2.13003C10.632 1.94427 10.3158 1.95933 10.13 2.16366L5.13003 7.66366C4.95666 7.85437 4.95666 8.14562 5.13003 8.33633L10.13 13.8363C10.3158 14.0407 10.632 14.0557 10.8363 13.87Z" fill="#0C0C14"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 469 B |
@ -1,4 +1,3 @@
|
|||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M12 4.72447L12 8.5C12 8.77614 11.7761 9 11.5 9C11.2239 9 11 8.77614 11 8.5V5.70711L5.35355 11.3536C5.15829 11.5488 4.84171 11.5488 4.64645 11.3536C4.45118 11.1583 4.45118 10.8417 4.64645 10.6464L10.2929 5H7.50002C7.22387 5 7.00002 4.77614 7.00002 4.5C7.00002 4.22386 7.22387 4 7.50002 4L11.2761 3.99999C11.3175 3.99994 11.3769 3.99987 11.4328 4.00452C11.5821 3.98436 11.7388 4.03167 11.8536 4.14645C11.9683 4.26124 12.0157 4.41796 11.9955 4.56731C12.0001 4.62322 12.0001 4.68283 12 4.72447Z" fill="white"/>
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 2L13.2427 2L13.2427 1.99998L13.2426 2L13 2L9 2V3H12.2426L5.76613 9.47651L6.47324 10.1836L13 3.65686V7H14V3V2ZM2 2H5V3H3L3 13L13 13V11H14V14H13H3H2V13V3V2Z" fill="white"/>
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3 1C1.89543 1 1 1.89543 1 3V13C1 14.1046 1.89543 15 3 15H13C14.1046 15 15 14.1046 15 13V3C15 1.89543 14.1046 1 13 1H3ZM13 2H3C2.44772 2 2 2.44772 2 3V13C2 13.5523 2.44772 14 3 14H13C13.5523 14 14 13.5523 14 13V3C14 2.44772 13.5523 2 13 2Z" fill="white"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 924 B After Width: | Height: | Size: 327 B |
|
Before Width: | Height: | Size: 436 KiB After Width: | Height: | Size: 14 KiB |
@ -1,11 +0,0 @@
|
|||||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M12.9864 4.0315C14.3445 1.75773 17.6381 1.75773 18.9961 4.0315L31.232 24.5182C32.6254 26.8512 30.9445 29.8129 28.2272 29.8129H3.75539C1.03801 29.8129 -0.642844 26.8512 0.75053 24.5182L12.9864 4.0315Z" fill="url(#paint0_linear_4081_29778)"/>
|
|
||||||
<path d="M14.5387 12.1944C14.5432 11.3954 15.1922 10.75 15.9912 10.75C16.7903 10.75 17.4392 11.3954 17.4437 12.1944L17.4829 19.25C17.4875 20.0771 16.8183 20.75 15.9912 20.75C15.1641 20.75 14.4949 20.0771 14.4995 19.25L14.5387 12.1944Z" fill="white"/>
|
|
||||||
<path d="M17.4912 23.75C17.4912 24.5784 16.8196 25.25 15.9912 25.25C15.1628 25.25 14.4912 24.5784 14.4912 23.75C14.4912 22.9216 15.1628 22.25 15.9912 22.25C16.8196 22.25 17.4912 22.9216 17.4912 23.75Z" fill="white"/>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="paint0_linear_4081_29778" x1="15.9913" y1="2.09253" x2="15.9913" y2="42.104" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#FFB800"/>
|
|
||||||
<stop offset="1" stop-color="#FF8419"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.0 KiB |
@ -1,151 +0,0 @@
|
|||||||
<svg width="270" height="104" viewBox="0 0 270 104" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M179.366 102.5H3.05083C1.33939 102.5 0 101.161 0 99.4493V97.7378C0 96.0264 1.33939 94.687 3.05083 94.687H179.366C181.077 94.687 182.417 96.0264 182.417 97.7378V99.4493C182.417 101.161 181.003 102.5 179.366 102.5Z" fill="#B0D4E5"/>
|
|
||||||
<path d="M164.229 94.6867H16.6963V9.42924C16.6963 4.51816 20.7144 0.5 25.6255 0.5H155.3C160.211 0.5 164.229 4.51816 164.229 9.42924V94.6867Z" fill="url(#paint0_radial_4147_15491)"/>
|
|
||||||
<path d="M157.311 94.6862H24.3628V10.8426C24.3628 9.65203 25.3301 8.68469 26.5207 8.68469H155.079C156.269 8.68469 157.237 9.65203 157.237 10.8426V94.6862H157.311Z" fill="url(#paint1_linear_4147_15491)"/>
|
|
||||||
<rect opacity="0.5" x="29" y="66" width="123" height="24" rx="3" fill="white"/>
|
|
||||||
<rect x="29" y="14" width="123" height="48" rx="3" fill="white"/>
|
|
||||||
<path d="M46.686 70.0266H55.3141C57.3414 70.0266 58.9735 71.6587 58.9735 73.686V82.3141C58.9735 84.3414 57.3414 85.9734 55.3141 85.9734H46.686C44.6587 85.9734 43.0266 84.3414 43.0266 82.3141V73.686C43.0266 71.6587 44.6587 70.0266 46.686 70.0266Z" fill="url(#paint2_linear_4147_15491)"/>
|
|
||||||
<path d="M46.5205 74.5615C46.4365 74.5615 46.3573 74.576 46.2824 74.6058L47.783 76.1506L49.3001 77.7232L49.3278 77.7564L49.4164 77.845L49.505 77.9391L50.8062 79.2735C50.8279 79.287 50.8907 79.3452 50.9397 79.3697C51.0029 79.4013 51.0714 79.4304 51.142 79.4329C51.2182 79.4356 51.296 79.4138 51.3646 79.3805C51.4159 79.3555 51.4387 79.3197 51.4984 79.2735L53.0045 77.7176L55.9945 74.639C55.9003 74.5879 55.796 74.5615 55.6844 74.5615H46.5205ZM46.0609 74.7497C45.9009 74.9014 45.8007 75.1293 45.8007 75.3865V80.4585C45.8007 80.6667 45.8676 80.8559 45.9779 81.0011L46.1883 80.8018L47.7553 79.2791L49.1451 77.9336L49.1174 77.9003L47.5947 76.3333L46.072 74.7608L46.0609 74.7497ZM56.1938 74.7996L53.1927 77.9003L53.165 77.928L54.6102 79.3289L56.1772 80.8516L56.2714 80.9402C56.3557 80.8048 56.4043 80.6381 56.4043 80.4585V75.3865C56.4043 75.1572 56.3248 74.9492 56.1938 74.7996ZM49.3223 78.1163L47.938 79.4618L46.3655 80.9845L46.1661 81.1783C46.2712 81.246 46.3908 81.2891 46.5205 81.2891H55.6844C55.8403 81.2891 55.9818 81.2291 56.0997 81.134L56 81.0343L54.4275 79.5116L52.9823 78.1163L51.6811 79.4563C51.6107 79.503 51.5636 79.5547 51.4949 79.5865C51.3842 79.6377 51.2629 79.681 51.141 79.6791C51.0187 79.6773 50.8988 79.6294 50.7891 79.5755C50.7341 79.5484 50.7047 79.5215 50.6401 79.4673L49.3223 78.1163Z" fill="white"/>
|
|
||||||
<g clip-path="url(#clip0_4147_15491)">
|
|
||||||
<path d="M126.645 73.4143H126.646C127.138 71.6949 129.281 70.8555 131.465 70.8555C132.974 70.8555 134.329 71.3334 135.258 72.0918C134.682 72.1203 134.135 72.2329 133.634 72.4146C134.385 72.6933 135.029 73.1225 135.507 73.6535C135.19 73.5987 134.861 73.5699 134.524 73.5699C134.488 73.5699 134.452 73.5702 134.415 73.5709C135.283 74.8295 135.792 76.3554 135.792 78C135.792 82.3135 132.295 85.8103 127.981 85.8103C123.734 85.8103 120.171 82.2543 120.171 78C120.171 77.3281 120.261 76.6341 120.434 75.9837C120.479 75.8471 120.543 75.7161 120.627 75.6681C120.732 75.6081 120.828 75.787 120.844 75.8452C120.958 76.275 121.112 76.6906 121.302 77.0887C121.286 76.1969 121.667 75.3848 122.191 74.6821C122.54 74.2138 122.864 73.7796 123.014 72.5271C123.024 72.443 123.103 72.3825 123.184 72.4089C124.321 72.7812 124.929 74.6748 124.834 76.2583C125.463 76.3481 125.46 75.6919 125.46 75.6919C125.259 75.0747 125.393 73.9275 126.643 73.4143H126.645Z" fill="url(#paint3_linear_4147_15491)"/>
|
|
||||||
<path opacity="0.9" d="M135.536 76.011C135.726 80.3067 132.195 84.0103 127.888 84.0103C123.857 84.0103 120.554 80.8945 120.255 76.9398C120.203 77.3052 120.174 77.6783 120.171 78.0576C120.202 82.2895 123.757 85.8103 127.981 85.8103C132.295 85.8103 135.792 82.3135 135.792 78C135.792 77.3126 135.703 76.646 135.536 76.011Z" fill="url(#paint4_radial_4147_15491)"/>
|
|
||||||
<g style="mix-blend-mode:screen">
|
|
||||||
<path d="M127.792 73.9835C127.707 73.8346 127.319 73.6144 127.149 73.576C127.792 71.5174 131.066 70.8854 133.07 71.2497C133.904 71.4012 134.942 71.8559 135.258 72.0918C134.33 71.3334 132.975 70.8555 131.466 70.8555C129.282 70.8555 127.138 71.6949 126.647 73.4143H126.645H126.643C125.393 73.9275 125.26 75.0751 125.46 75.6922C125.653 74.9561 126.571 74.0514 127.792 73.9835Z" fill="url(#paint5_radial_4147_15491)"/>
|
|
||||||
</g>
|
|
||||||
<path d="M130.061 72.5619C128.306 72.9071 127.733 73.0199 127.146 73.5781C127.805 71.8322 129.489 71.4785 131.494 72.2742C130.942 72.3884 130.47 72.4814 130.061 72.5619Z" fill="url(#paint6_linear_4147_15491)"/>
|
|
||||||
<path d="M120.595 75.7446C120.116 77.7069 120.486 80.0134 122.664 81.949C122.015 81.2402 121.224 78.6226 122.971 76.7525C123.088 76.6265 123.291 76.7192 123.297 76.8914C123.441 80.7778 126.577 83.1514 130.192 82.7076C129.072 82.6446 125.367 81.3471 128.123 80.8337C129.564 80.5654 131.822 80.1446 131.822 78.1182C131.822 74.8328 129.282 73.8722 127.742 74.0151C126.688 74.113 125.749 74.7819 125.46 75.6916C125.571 76.0503 125.129 76.3014 124.834 76.2593C124.929 74.6758 124.321 72.7812 123.184 72.4088C123.103 72.3825 123.024 72.443 123.014 72.5271C122.864 73.7796 122.54 74.2137 122.191 74.6821C121.667 75.3848 121.286 76.1968 121.302 77.0887C121.112 76.6906 120.958 76.2749 120.844 75.8452C120.831 75.797 120.761 75.6626 120.677 75.6562C120.631 75.6528 120.607 75.6974 120.595 75.7446Z" fill="url(#paint7_radial_4147_15491)"/>
|
|
||||||
<g style="mix-blend-mode:screen">
|
|
||||||
<path d="M127.156 81.0327C129.277 82.7547 133.542 81.4637 133.542 77.2765C131.82 79.8867 129.627 81.687 127.156 81.0327Z" fill="url(#paint8_linear_4147_15491)"/>
|
|
||||||
</g>
|
|
||||||
<g style="mix-blend-mode:screen">
|
|
||||||
<path d="M122.971 76.7525C123.015 76.7045 123.072 76.6881 123.126 76.6956C121.563 78.6023 122.824 81.9512 123.689 82.7739C123.738 82.9109 122.868 82.1985 122.749 82.0316C122.09 81.4731 121.147 78.7053 122.971 76.7525Z" fill="url(#paint9_linear_4147_15491)"/>
|
|
||||||
</g>
|
|
||||||
<path d="M127.982 81.1904C130.103 81.1904 131.823 79.7872 131.823 78.0562C131.823 76.3252 130.103 74.922 127.982 74.922C126.172 74.922 124.14 76.0994 124.14 78.102C124.141 81.1965 127.411 82.9768 130.197 82.7073C129.988 82.683 128.682 82.6136 127.799 81.6152C127.72 81.5252 127.582 81.3682 127.644 81.2661C127.707 81.1641 127.879 81.1904 127.982 81.1904Z" fill="url(#paint10_linear_4147_15491)"/>
|
|
||||||
<path opacity="0.6" d="M131.404 76.6322L128.369 79.5368C128.099 79.7282 127.812 79.7423 127.529 79.5685L124.552 76.6427C124.636 76.5075 124.732 76.3779 124.838 76.2549C124.945 76.3552 125.049 76.4525 125.15 76.5474C125.932 77.28 126.564 77.8721 127.455 78.6325C127.858 78.9758 127.983 78.969 128.377 78.6325C129.396 77.7623 130.142 77.1038 131.111 76.2385C131.22 76.3633 131.318 76.4948 131.404 76.6322Z" fill="white"/>
|
|
||||||
<mask id="mask0_4147_15491" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="124" y="76" width="8" height="7">
|
|
||||||
<path d="M131.823 78.0561C131.823 79.7871 130.103 81.1903 127.982 81.1903C127.88 81.1903 127.707 81.164 127.645 81.266C127.582 81.3681 127.72 81.5251 127.8 81.6151C128.629 82.5532 129.832 82.6711 130.148 82.7021C130.168 82.7041 130.185 82.7057 130.198 82.7072C127.411 82.9767 124.142 81.1964 124.141 78.1019C124.141 77.5512 124.294 77.0629 124.554 76.6443L127.546 79.3617C127.759 79.5551 128.118 79.5551 128.331 79.3617L131.379 76.5928C131.663 77.0296 131.823 77.5276 131.823 78.0561Z" fill="white"/>
|
|
||||||
</mask>
|
|
||||||
<g mask="url(#mask0_4147_15491)">
|
|
||||||
<path opacity="0.7" d="M132.96 74.4413H123.251V83.244H132.96V74.4413Z" fill="url(#paint11_linear_4147_15491)"/>
|
|
||||||
<g filter="url(#filter0_f_4147_15491)">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M127.091 79.3802C126.525 78.7914 124.831 76.8357 124.831 76.8357L124.962 76.8413L127.614 78.8223C127.813 78.9667 128.096 78.9652 128.293 78.8189L130.894 76.8439L131.032 76.8331C131.032 76.8331 129.393 78.7426 128.765 79.3717C128.138 80.0007 127.656 79.969 127.091 79.3802Z" fill="#458FCD"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path d="M128.52 73.3543C128.931 73.2248 128.895 72.8179 128.895 72.8179C128.895 72.8179 128.69 72.5759 128.282 72.71C127.901 72.8356 127.842 73.107 127.842 73.107C127.842 73.107 128.05 73.5023 128.52 73.3543Z" fill="white"/>
|
|
||||||
</g>
|
|
||||||
<path d="M96.2971 71H87.7017C87.3166 71 86.9998 71.3169 86.9998 71.702V72.5L91.8444 74L96.9991 72.5V71.702C96.9991 71.3169 96.6822 71 96.2971 71Z" fill="#0364B8"/>
|
|
||||||
<path d="M97.8241 78.7025C97.8973 78.4723 97.9558 78.2377 97.9991 78C97.9991 77.8811 97.9356 77.7709 97.8326 77.7115L97.8261 77.708L97.8241 77.7071L92.4054 74.62C92.382 74.6049 92.3578 74.591 92.3329 74.5785C92.1231 74.4745 91.8763 74.4745 91.6665 74.5785C91.6416 74.591 91.6174 74.6048 91.594 74.62L86.1753 77.7071L86.1733 77.708L86.1668 77.7115C86.0639 77.7709 86.0003 77.8811 86.0004 78C86.0436 78.2377 86.1021 78.4723 86.1753 78.7025L91.9209 82.905L97.8241 78.7025Z" fill="#0A2767"/>
|
|
||||||
<path d="M93.9993 72.5H90.4995L89.489 74.0001L90.4995 75.5L93.9993 78.5H96.999V75.5L93.9993 72.5Z" fill="#28A8EA"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M86.9998 72.5H90.4995V75.5H86.9998V72.5Z" fill="#0078D4"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M93.9993 72.5H96.9991V75.5H93.9993V72.5Z" fill="#50D9FF"/>
|
|
||||||
<path d="M93.9993 78.5L90.4995 75.5H86.9998V78.5L90.4995 81.5L95.9152 82.384L93.9993 78.5Z" fill="#0364B8"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M90.4995 75.5H93.9993V78.5H90.4995V75.5Z" fill="#0078D4"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M86.9998 78.5H90.4995V81.5H86.9998V78.5Z" fill="#064A8C"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M93.9993 78.5H96.9991V81.5H93.9993V78.5Z" fill="#0078D4"/>
|
|
||||||
<path d="M92.0949 82.6091L86.1982 78.3091L86.4452 77.8746C86.4452 77.8746 91.8174 80.9346 91.8993 80.9806C91.9673 81.0079 92.0436 81.0057 92.1099 80.9746C92.1863 80.9316 97.5755 77.8596 97.5755 77.8596L97.8235 78.2941L92.0949 82.6091Z" fill="#0A2767" fill-opacity="0.498039"/>
|
|
||||||
<path d="M97.8325 78.2886L97.826 78.2926L97.8245 78.2936L92.4059 81.3806C92.1872 81.5216 91.9103 81.5388 91.6759 81.4261L93.5632 83.9566L97.69 84.8551V84.8571C97.8846 84.7164 98 84.4903 98 84.2501V78.0001C98 78.119 97.9364 78.2292 97.8335 78.2886H97.8325Z" fill="#1490DF"/>
|
|
||||||
<path d="M97.999 84.2502V83.8813L93.0078 81.0372L92.4053 81.3802C92.1868 81.5212 91.9098 81.5385 91.6754 81.4257L93.5628 83.9563L97.6895 84.8547V84.8567C97.8841 84.7159 97.9995 84.4899 97.9995 84.2497L97.999 84.2502Z" fill="black" fill-opacity="0.047059"/>
|
|
||||||
<path d="M97.974 84.4417L92.5028 81.3247L92.4053 81.3802C92.1868 81.5212 91.9098 81.5385 91.6754 81.4257L93.5628 83.9562L97.6895 84.8547V84.8567C97.8295 84.7552 97.9303 84.6083 97.9745 84.4412L97.974 84.4417Z" fill="black" fill-opacity="0.098039"/>
|
|
||||||
<path d="M86.1746 78.2951V78.2901H86.1697L86.1546 78.2801C86.0576 78.2204 85.9987 78.114 85.9996 78.0001V84.2511C85.9996 84.662 86.3378 85.0001 86.7487 85.0001H97.249C97.3114 84.9995 97.3736 84.9911 97.434 84.9751C97.4653 84.9697 97.4956 84.9595 97.524 84.9451C97.5346 84.9441 97.5448 84.9406 97.554 84.9351C97.5948 84.9184 97.6335 84.8966 97.669 84.8701L97.689 84.8551L86.1746 78.2951Z" fill="#28A8EA"/>
|
|
||||||
<path d="M90.9995 82.3337V74.1672C90.9984 73.802 90.6982 73.5018 90.333 73.5007H87.0152V77.2287L86.1747 77.7077L86.1728 77.7087L86.1663 77.7122C86.0633 77.7717 85.9998 77.8819 85.9998 78.0007V83.0022V83.0002H90.333C90.6982 82.9992 90.9984 82.6989 90.9995 82.3337Z" fill="black" fill-opacity="0.098039"/>
|
|
||||||
<path d="M90.4995 82.8336V74.6671C90.4984 74.3019 90.1982 74.0018 89.8331 74.0006H87.0152V77.2286L86.1747 77.7076L86.1728 77.7086L86.1663 77.7121C86.0633 77.7716 85.9998 77.8818 85.9998 78.0006V83.5022V83.5001H89.8331C90.1982 83.499 90.4984 83.1989 90.4995 82.8336ZM90.4995 81.8336V74.6671C90.4984 74.3019 90.1982 74.0018 89.8331 74.0006H87.0152V77.2286L86.1747 77.7076L86.1728 77.7086L86.1663 77.7121C86.0633 77.7716 85.9998 77.8818 85.9998 78.0006V82.5021V82.5001H89.8331C90.1982 82.499 90.4984 82.1989 90.4995 81.8336ZM89.9995 81.8336V74.6671C89.9984 74.3019 89.6983 74.0018 89.3331 74.0006H87.0152V77.2286L86.1747 77.7076L86.1728 77.7086L86.1663 77.7121C86.0633 77.7716 85.9998 77.8818 85.9998 78.0006V82.5021V82.5001H89.3331C89.6983 82.499 89.9984 82.1989 89.9995 81.8336Z" fill="black" fill-opacity="0.2"/>
|
|
||||||
<path d="M82.6665 74.0002H89.3325C89.6981 74.0002 89.999 74.3011 89.999 74.6667V81.3332C89.999 81.6988 89.6981 81.9997 89.3325 81.9997H82.6665C82.3009 81.9997 82 81.6988 82 81.3332V74.6667C82 74.3011 82.3009 74.0002 82.6665 74.0002Z" fill="#0078D4"/>
|
|
||||||
<path d="M83.9334 76.7341C84.1105 76.3567 84.3964 76.0409 84.7543 75.8272C85.1509 75.6001 85.6025 75.487 86.0593 75.5002C86.4823 75.4909 86.8999 75.5981 87.2662 75.8102C87.6108 76.0154 87.8884 76.3165 88.0651 76.6767C88.2575 77.0735 88.3535 77.5102 88.3451 77.9512C88.3545 78.4121 88.2556 78.8688 88.0567 79.2847C87.8761 79.6576 87.5901 79.9696 87.2342 80.1817C86.8537 80.4004 86.4205 80.5106 85.9817 80.5002C85.5495 80.5105 85.1227 80.402 84.7479 80.1867C84.4005 79.981 84.1196 79.6796 83.9389 79.3187C83.7452 78.9275 83.6481 78.4956 83.6559 78.0592C83.6473 77.6022 83.7424 77.1492 83.9339 76.7341H83.9334ZM84.8083 78.8632C84.9028 79.1018 85.063 79.3089 85.2703 79.4601C85.4813 79.6079 85.7343 79.6842 85.9917 79.6777C86.266 79.6885 86.5365 79.6099 86.7622 79.4537C86.967 79.3024 87.123 79.0942 87.2107 78.8552C87.3093 78.5887 87.3579 78.3062 87.3542 78.0222C87.3572 77.7356 87.3116 77.4505 87.2192 77.1792C87.1377 76.935 86.9873 76.7194 86.7862 76.5587C86.5665 76.3946 86.2967 76.3113 86.0228 76.3227C85.7597 76.3159 85.5011 76.3925 85.2843 76.5417C85.0731 76.6935 84.9093 76.9023 84.8123 77.1437C84.5977 77.6964 84.5965 78.3097 84.8088 78.8632H84.8083Z" fill="white"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M37 24C37 22.3431 38.3431 21 40 21H42C43.6569 21 45 22.3431 45 24V26C45 27.6569 43.6569 29 42 29H40C38.3431 29 37 27.6569 37 26V24ZM47 22.5C47 21.6716 47.6716 21 48.5 21H89.5C90.3284 21 91 21.6716 91 22.5C91 23.3284 90.3284 24 89.5 24H48.5C47.6716 24 47 23.3284 47 22.5ZM48.5 26C47.6716 26 47 26.6716 47 27.5C47 28.3284 47.6716 29 48.5 29H81.5C82.3284 29 83 28.3284 83 27.5C83 26.6716 82.3284 26 81.5 26H48.5ZM37 36C37 34.3431 38.3431 33 40 33H42C43.6569 33 45 34.3431 45 36V38C45 39.6569 43.6569 41 42 41H40C38.3431 41 37 39.6569 37 38V36ZM40 45C38.3431 45 37 46.3431 37 48V50C37 51.6569 38.3431 53 40 53H42C43.6569 53 45 51.6569 45 50V48C45 46.3431 43.6569 45 42 45H40ZM47 34.5C47 33.6716 47.6716 33 48.5 33H89.5C90.3284 33 91 33.6716 91 34.5C91 35.3284 90.3284 36 89.5 36H48.5C47.6716 36 47 35.3284 47 34.5ZM48.5 45C47.6716 45 47 45.6716 47 46.5C47 47.3284 47.6716 48 48.5 48H89.5C90.3284 48 91 47.3284 91 46.5C91 45.6716 90.3284 45 89.5 45H48.5ZM47 39.5C47 38.6716 47.6716 38 48.5 38H81.5C82.3284 38 83 38.6716 83 39.5C83 40.3284 82.3284 41 81.5 41H48.5C47.6716 41 47 40.3284 47 39.5ZM48.5 50C47.6716 50 47 50.6716 47 51.5C47 52.3284 47.6716 53 48.5 53H81.5C82.3284 53 83 52.3284 83 51.5C83 50.6716 82.3284 50 81.5 50H48.5Z" fill="#B0D4E5"/>
|
|
||||||
<circle cx="176" cy="53.5" r="2" fill="#B0D4E5"/>
|
|
||||||
<circle cx="184" cy="53.5" r="2" fill="#B0D4E5"/>
|
|
||||||
<circle cx="192" cy="53.5" r="2" fill="#B0D4E5"/>
|
|
||||||
<circle cx="200" cy="53.5" r="2" fill="#B0D4E5"/>
|
|
||||||
<path d="M233.817 30.5C223.274 30.5 214.729 39.0451 214.729 49.5874V68.8014C214.729 70.8441 216.385 72.5 218.428 72.5H249.601C251.424 72.5 252.904 71.0203 252.904 69.1965V49.5874C252.904 39.0465 244.359 30.5 233.817 30.5ZM244.804 49.4814L236.461 56.5096C234.955 57.7787 232.753 57.7787 231.247 56.5096L222.904 49.4814C222.904 43.5172 227.74 38.6817 233.704 38.6817H234.004C239.968 38.6817 244.804 43.5172 244.804 49.4814Z" fill="#6D4AFF"/>
|
|
||||||
<path d="M233.817 30.5C223.274 30.5 214.729 39.0451 214.729 49.5874V68.8014C214.729 70.8441 216.385 72.5 218.428 72.5H249.601C251.424 72.5 252.904 71.0203 252.904 69.1965V49.5874C252.904 39.0465 244.359 30.5 233.817 30.5ZM244.804 49.4814L236.461 56.5096C234.955 57.7787 232.753 57.7787 231.247 56.5096L222.904 49.4814C222.904 43.5172 227.74 38.6817 233.704 38.6817H234.004C239.968 38.6817 244.804 43.5172 244.804 49.4814Z" fill="url(#paint12_linear_4147_15491)"/>
|
|
||||||
<g filter="url(#filter1_i_4147_15491)">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M236.474 56.5948C235.604 57.2989 233.343 58.2847 231.255 56.5948C229.168 54.9048 224.835 51.1687 222.93 49.5119H222.94L222.904 49.4814C222.904 43.5172 227.74 38.6817 233.704 38.6817H234.004C239.968 38.6817 244.804 43.5172 244.804 49.4814L244.767 49.5119H244.8V72.5H249.601C251.424 72.5 252.904 71.0203 252.904 69.1965V49.5874C252.904 39.0465 244.359 30.5 233.817 30.5C223.274 30.5 214.729 39.0451 214.729 49.5874V50.7946L226.533 60.9439C227.403 61.7723 229.69 62.9321 231.877 60.9439C234.064 58.9557 235.853 57.2161 236.474 56.5948Z" fill="url(#paint13_radial_4147_15491)"/>
|
|
||||||
</g>
|
|
||||||
<circle cx="254.278" cy="27.7778" r="15.2778" fill="url(#paint14_linear_4147_15491)"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M260.702 24.1676C261.113 24.5761 261.116 25.2412 260.707 25.653L253.128 33.2918C252.93 33.4906 252.662 33.6024 252.382 33.6024C252.102 33.6024 251.834 33.4906 251.636 33.2918L247.758 29.383C247.349 28.9713 247.352 28.3062 247.764 27.8976C248.175 27.489 248.84 27.4916 249.249 27.9034L252.382 31.0608L259.216 24.1733C259.625 23.7615 260.29 23.759 260.702 24.1676Z" fill="white"/>
|
|
||||||
<defs>
|
|
||||||
<filter id="filter0_f_4147_15491" x="111.934" y="63.936" width="31.9953" height="28.7937" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
||||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
||||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
||||||
<feGaussianBlur stdDeviation="6.44854" result="effect1_foregroundBlur_4147_15491"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="filter1_i_4147_15491" x="214.137" y="30.5" width="38.7676" height="43.6537" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
||||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
||||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
||||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
|
||||||
<feOffset dx="-0.592742" dy="1.65375"/>
|
|
||||||
<feGaussianBlur stdDeviation="4.44556"/>
|
|
||||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
|
||||||
<feColorMatrix type="matrix" values="0 0 0 0 0.462745 0 0 0 0 0.337255 0 0 0 0 1 0 0 0 0.24 0"/>
|
|
||||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_4147_15491"/>
|
|
||||||
</filter>
|
|
||||||
<radialGradient id="paint0_radial_4147_15491" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(160.195 7) rotate(145.641) scale(59.357 92.9759)">
|
|
||||||
<stop stop-color="#292842"/>
|
|
||||||
<stop offset="1" stop-color="#38385F"/>
|
|
||||||
</radialGradient>
|
|
||||||
<linearGradient id="paint1_linear_4147_15491" x1="21.9037" y1="100.487" x2="165.819" y2="14.2507" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#35168C"/>
|
|
||||||
<stop offset="0.317708" stop-color="#FF5454"/>
|
|
||||||
<stop offset="0.46875" stop-color="#FFDD64"/>
|
|
||||||
<stop offset="0.677083" stop-color="#BCE6FF"/>
|
|
||||||
<stop offset="0.911458" stop-color="#6983EF"/>
|
|
||||||
<stop offset="1" stop-color="#2395FF"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint2_linear_4147_15491" x1="51.1117" y1="85.9094" x2="51.1272" y2="70.2191" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#70EFFF"/>
|
|
||||||
<stop offset="1" stop-color="#5770FF"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint3_linear_4147_15491" x1="122.882" y1="73.4298" x2="133.892" y2="83.6998" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#1B91F3"/>
|
|
||||||
<stop offset="1" stop-color="#0B68CB"/>
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient id="paint4_radial_4147_15491" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(122.862 76.9635) rotate(66.5179) scale(6.60435 6.32678)">
|
|
||||||
<stop offset="0.525579" stop-color="#0B4186" stop-opacity="0"/>
|
|
||||||
<stop offset="1" stop-color="#0B4186" stop-opacity="0.45"/>
|
|
||||||
</radialGradient>
|
|
||||||
<radialGradient id="paint5_radial_4147_15491" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(128.183 74.2511) rotate(-127.994) scale(1.41191 2.33636)">
|
|
||||||
<stop stop-color="#EF3ACC" stop-opacity="0"/>
|
|
||||||
<stop offset="1" stop-color="#EF3ACC" stop-opacity="0.64"/>
|
|
||||||
</radialGradient>
|
|
||||||
<linearGradient id="paint6_linear_4147_15491" x1="125.946" y1="76.0588" x2="129.907" y2="71.6484" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#0F5DB0"/>
|
|
||||||
<stop offset="1" stop-color="#0F5DB0" stop-opacity="0"/>
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient id="paint7_radial_4147_15491" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(125.036 82.2554) rotate(-64.2627) scale(10.3034 12.7276)">
|
|
||||||
<stop offset="0.0160882" stop-color="#094188"/>
|
|
||||||
<stop offset="0.967387" stop-color="#0B4186" stop-opacity="0"/>
|
|
||||||
</radialGradient>
|
|
||||||
<linearGradient id="paint8_linear_4147_15491" x1="132.89" y1="79.2522" x2="131.048" y2="83.7752" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#E247C4" stop-opacity="0"/>
|
|
||||||
<stop offset="1" stop-color="#E247C4" stop-opacity="0.64"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint9_linear_4147_15491" x1="121.466" y1="75.1958" x2="123.089" y2="81.7279" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0.104632" stop-color="#EF3ACC"/>
|
|
||||||
<stop offset="1" stop-color="#EF3ACC" stop-opacity="0"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint10_linear_4147_15491" x1="127.982" y1="76.0698" x2="127.982" y2="82.6751" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="white"/>
|
|
||||||
<stop offset="0.90535" stop-color="#BEE1FE"/>
|
|
||||||
<stop offset="1" stop-color="#96CEFD"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint11_linear_4147_15491" x1="128.105" y1="79.8075" x2="128.105" y2="82.5745" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#BCE0FD"/>
|
|
||||||
<stop offset="1" stop-color="#88CCFC"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint12_linear_4147_15491" x1="216.096" y1="77.3462" x2="221.812" y2="61.1923" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#28B0E8"/>
|
|
||||||
<stop offset="1" stop-color="#C5B7FF" stop-opacity="0"/>
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient id="paint13_radial_4147_15491" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(250.653 75.9793) rotate(-138.034) scale(48.3148 39.5031)">
|
|
||||||
<stop stop-color="#E2DBFF"/>
|
|
||||||
<stop offset="1" stop-color="#6D4AFF"/>
|
|
||||||
</radialGradient>
|
|
||||||
<linearGradient id="paint14_linear_4147_15491" x1="255.861" y1="10.272" x2="256.004" y2="43.0557" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#2AF091"/>
|
|
||||||
<stop offset="1" stop-color="#00C5A1"/>
|
|
||||||
</linearGradient>
|
|
||||||
<clipPath id="clip0_4147_15491">
|
|
||||||
<rect width="16" height="16" fill="white" transform="translate(120 70)"/>
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 22 KiB |
@ -1,33 +0,0 @@
|
|||||||
<svg width="190" height="104" viewBox="0 0 190 104" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M95.6666 29C78.0961 29 63.8542 43.2418 63.8542 60.8123V92.8357C63.8542 96.2402 66.6141 99 70.0185 99H121.973C125.013 99 127.479 96.5338 127.479 93.4941V60.8123C127.479 43.2441 113.237 29 95.6666 29ZM113.978 60.6357L100.074 72.3494C97.5638 74.4646 93.8933 74.4646 91.3835 72.3494L77.4789 60.6357C77.4789 50.6953 85.5381 42.6362 95.4785 42.6362H95.9786C105.919 42.6362 113.978 50.6953 113.978 60.6357Z" fill="#6D4AFF"/>
|
|
||||||
<path d="M95.6666 29C78.0961 29 63.8542 43.2418 63.8542 60.8123V92.8357C63.8542 96.2402 66.6141 99 70.0185 99H121.973C125.013 99 127.479 96.5338 127.479 93.4941V60.8123C127.479 43.2441 113.237 29 95.6666 29ZM113.978 60.6357L100.074 72.3494C97.5638 74.4646 93.8933 74.4646 91.3835 72.3494L77.4789 60.6357C77.4789 50.6953 85.5381 42.6362 95.4785 42.6362H95.9786C105.919 42.6362 113.978 50.6953 113.978 60.6357Z" fill="url(#paint0_linear_4081_30418)"/>
|
|
||||||
<g filter="url(#filter0_i_4081_30418)">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M100.096 72.4913C98.6461 73.6649 94.8768 75.3079 91.3975 72.4913C87.9183 69.6747 80.6973 63.4479 77.5218 60.6866H77.5393L77.479 60.6357C77.479 50.6953 85.5382 42.6362 95.4785 42.6362H95.9786C105.919 42.6362 113.978 50.6953 113.978 60.6357L113.918 60.6866H113.972V99H121.973C125.013 99 127.479 96.5338 127.479 93.4941V60.8123C127.479 43.2441 113.237 29 95.6666 29C78.0961 29 63.8542 43.2418 63.8542 60.8123V62.8243L83.5277 79.7398C84.9774 81.1205 88.7881 83.0534 92.4331 79.7398C96.078 76.4262 99.0603 73.5268 100.096 72.4913Z" fill="url(#paint1_radial_4081_30418)"/>
|
|
||||||
</g>
|
|
||||||
<ellipse cx="128.5" cy="35.9999" rx="16" ry="15.9999" fill="url(#paint2_linear_4081_30418)"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M135.227 32.2191C135.659 32.647 135.661 33.3434 135.233 33.7747L127.295 41.7746C127.089 41.9828 126.808 42.0998 126.515 42.0998C126.221 42.0998 125.94 41.9828 125.734 41.7746L121.672 37.6811C121.244 37.2499 121.247 36.5534 121.678 36.1255C122.109 35.6976 122.806 35.7003 123.233 36.1315L126.515 39.4381L133.672 32.2251C134.1 31.7939 134.796 31.7912 135.227 32.2191Z" fill="white"/>
|
|
||||||
<defs>
|
|
||||||
<filter id="filter0_i_4081_30418" x="62.8663" y="29" width="64.6127" height="72.7562" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
||||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
||||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
||||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
|
||||||
<feOffset dx="-0.987903" dy="2.75625"/>
|
|
||||||
<feGaussianBlur stdDeviation="7.40927"/>
|
|
||||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
|
||||||
<feColorMatrix type="matrix" values="0 0 0 0 0.462745 0 0 0 0 0.337255 0 0 0 0 1 0 0 0 0.24 0"/>
|
|
||||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_4081_30418"/>
|
|
||||||
</filter>
|
|
||||||
<linearGradient id="paint0_linear_4081_30418" x1="66.1324" y1="107.077" x2="75.659" y2="80.1539" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#28B0E8"/>
|
|
||||||
<stop offset="1" stop-color="#C5B7FF" stop-opacity="0"/>
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient id="paint1_radial_4081_30418" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(123.728 104.799) rotate(-138.034) scale(80.5247 65.8384)">
|
|
||||||
<stop stop-color="#E2DBFF"/>
|
|
||||||
<stop offset="1" stop-color="#6D4AFF"/>
|
|
||||||
</radialGradient>
|
|
||||||
<linearGradient id="paint2_linear_4081_30418" x1="130.158" y1="17.6667" x2="130.308" y2="52" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#2AF091"/>
|
|
||||||
<stop offset="1" stop-color="#00C5A1"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 443 KiB |
@ -1,34 +0,0 @@
|
|||||||
<svg width="134" height="36" viewBox="0 0 134 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<g clip-path="url(#clip0_53_676)">
|
|
||||||
<path d="M38 21.259V24.924H40.5591V21.4191C40.5591 21.0789 40.6927 20.7502 40.9344 20.51C41.1732 20.2699 41.5002 20.1327 41.8386 20.1327H44.463C45.6857 20.1327 46.86 19.6438 47.7244 18.7719C48.5888 17.9028 49.075 16.7221 49.075 15.4928C49.075 14.2635 48.5888 13.0828 47.7244 12.2109C46.86 11.3418 45.6857 10.8529 44.4602 10.8529H38V15.4328H40.5591V13.2744H44.2896C44.8697 13.2744 45.4241 13.5059 45.8336 13.9176C46.243 14.3293 46.4733 14.8867 46.4733 15.4699C46.4733 16.0531 46.243 16.6106 45.8336 17.0223C45.4241 17.4339 44.8697 17.6655 44.2896 17.6655H41.5798C41.1107 17.6655 40.6444 17.757 40.2122 17.9399C39.7771 18.12 39.3847 18.3859 39.0521 18.7204C38.7194 19.0549 38.4578 19.4523 38.2758 19.8868C38.0938 20.3185 38 20.7873 38 21.259Z" fill="white"/>
|
|
||||||
<path d="M49.4703 24.924V19.3408C49.4703 17.0623 50.7925 15.2498 53.4397 15.2498C53.8633 15.2441 54.287 15.2898 54.7022 15.3899V17.6855C54.4008 17.6655 54.142 17.6655 54.0197 17.6655C52.6179 17.6655 52.0151 18.3116 52.0151 19.6209V24.924H49.4703Z" fill="white"/>
|
|
||||||
<path d="M55.4641 20.1899C55.4641 17.3882 57.5682 15.2527 60.4969 15.2527C63.4256 15.2527 65.5297 17.3882 65.5297 20.1899C65.5297 22.9915 63.4256 25.1471 60.4969 25.1471C57.5682 25.1471 55.4641 22.9886 55.4641 20.1899ZM63.0218 20.1899C63.0218 18.5975 61.9584 17.4683 60.4969 17.4683C59.0325 17.4683 57.972 18.5946 57.972 20.1899C57.972 21.8022 59.0354 22.9115 60.4969 22.9115C61.9612 22.9115 63.0218 21.7994 63.0218 20.1899Z" fill="white"/>
|
|
||||||
<path d="M73.6676 20.1899C73.6676 17.3882 75.7717 15.2527 78.7004 15.2527C81.6263 15.2527 83.7304 17.3882 83.7304 20.1899C83.7304 22.9915 81.6263 25.1471 78.7004 25.1471C75.7717 25.1471 73.6676 22.9886 73.6676 20.1899ZM81.2225 20.1899C81.2225 18.5975 80.1591 17.4683 78.6976 17.4683C77.2361 17.4683 76.1726 18.5946 76.1726 20.1899C76.1726 21.8022 77.2361 22.9115 78.6976 22.9115C80.1591 22.9115 81.2225 21.7994 81.2225 20.1899Z" fill="white"/>
|
|
||||||
<path d="M85.0526 24.9241V19.5438C85.0526 17.0452 86.6364 15.2498 89.4627 15.2498C92.2692 15.2498 93.8529 17.0423 93.8529 19.5438V24.9241H91.328V19.7439C91.328 18.3545 90.7053 17.4854 89.4627 17.4854C88.2202 17.4854 87.5975 18.3516 87.5975 19.7439V24.9241H85.0526Z" fill="white"/>
|
|
||||||
<path d="M72.9056 17.4881H70.1588V21.0159C70.1588 22.2452 70.5996 22.8084 71.862 22.8084C71.9815 22.8084 72.2829 22.8084 72.6639 22.7884V24.8639C72.1435 25.004 71.6829 25.0869 71.1796 25.0869C69.0556 25.0869 67.6112 23.7976 67.6112 21.359V17.4881H65.9051V15.4527H66.3316C66.4994 15.4527 66.6671 15.4184 66.8207 15.3555C66.9771 15.2897 67.1164 15.1954 67.2358 15.0753C67.3553 14.9552 67.4491 14.8152 67.5145 14.6579C67.5799 14.5007 67.6112 14.3349 67.6112 14.1662V12.2479H70.156V15.4527H72.9027V17.4881H72.9056Z" fill="white"/>
|
|
||||||
<path d="M98.8823 11.216H102.457L105.808 19.4391C106.107 20.1282 106.362 20.8315 106.577 21.5492H106.612C106.827 20.8315 107.083 20.1253 107.381 19.4391L110.732 11.216H114.307V24.9288H111.717V15.7421C111.714 15.4391 111.729 15.136 111.76 14.8358H111.717C111.637 15.1589 111.531 15.4762 111.396 15.7822L107.683 24.7687H105.535L101.811 15.7822C101.676 15.4734 101.561 15.1589 101.469 14.8358H101.429C101.458 15.136 101.472 15.4391 101.469 15.7421V24.9316H98.8823V11.216Z" fill="white"/>
|
|
||||||
<path d="M123.165 15.8823C123.915 16.274 124.535 16.8744 124.951 17.6092C125.396 18.407 125.62 19.3105 125.603 20.2225V24.9288H123.335L123.174 23.5164C122.878 24.0281 122.444 24.4456 121.922 24.7258C121.362 25.0146 120.736 25.1604 120.104 25.1461C119.297 25.1547 118.505 24.9402 117.813 24.5256C117.112 24.1025 116.544 23.4963 116.165 22.7758C115.751 21.981 115.545 21.0975 115.562 20.2025C115.55 19.3219 115.777 18.4556 116.217 17.6893C116.647 16.9459 117.27 16.334 118.025 15.9194C118.806 15.4877 119.685 15.2647 120.578 15.2761C121.477 15.259 122.367 15.4677 123.165 15.8823ZM122.335 22.2126C122.829 21.7436 123.073 21.0832 123.073 20.2025C123.105 19.482 122.849 18.7787 122.367 18.244C122.137 18.0038 121.862 17.8151 121.554 17.6836C121.247 17.5521 120.92 17.4863 120.587 17.4863C120.254 17.4863 119.923 17.5521 119.619 17.6836C119.312 17.8151 119.036 18.0038 118.806 18.244C118.347 18.7929 118.094 19.4849 118.094 20.1997C118.094 20.9145 118.347 21.6064 118.806 22.1554C119.033 22.4013 119.309 22.5928 119.619 22.7244C119.926 22.853 120.259 22.9188 120.592 22.9102C120.914 22.9159 121.236 22.8559 121.537 22.7358C121.833 22.6186 122.106 22.4384 122.335 22.2126Z" fill="white"/>
|
|
||||||
<path d="M127.001 13.5206C126.849 13.3805 126.728 13.2118 126.645 13.0231C126.562 12.8344 126.522 12.6314 126.525 12.4255C126.522 12.2196 126.565 12.0138 126.645 11.8222C126.728 11.6306 126.849 11.4591 127.001 11.319C127.303 11.0216 127.71 10.8529 128.135 10.8529C128.56 10.8529 128.968 11.0216 129.27 11.319C129.422 11.4619 129.542 11.6335 129.623 11.8222C129.703 12.0138 129.746 12.2168 129.743 12.4255C129.746 12.6314 129.706 12.8344 129.623 13.0231C129.542 13.2118 129.422 13.3805 129.27 13.5206C128.965 13.8122 128.557 13.9752 128.135 13.9752C127.71 13.9752 127.306 13.8122 127.001 13.5206ZM129.413 24.9288H126.863V15.4791H129.413V24.9288Z" fill="white"/>
|
|
||||||
<path d="M133.824 24.9288H131.271V11.216H133.824V24.9288Z" fill="white"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.7801 14.3609L17.7816 14.3622L10 23L0 11.9929V7.24491C0 6.69969 0.635467 6.4014 1.05491 6.74973L12.1495 15.9633C13.2223 16.8543 14.7777 16.8543 15.8506 15.9633L17.7801 14.3609Z" fill="url(#paint0_linear_53_676)"/>
|
|
||||||
<path d="M22 10.8565L17.7801 14.361L17.7816 14.3622L12.1943 19.2977C11.2425 20.1385 9.81982 20.1597 8.84341 19.3476L0 11.9929V26.1035C0 27.7032 1.29683 29.0001 2.89655 29.0001H22L24 19.9283L22 10.8565Z" fill="url(#paint1_radial_53_676)"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M22 10.86V29L25.1034 29C26.7032 29 28 27.7031 28 26.1035V7.24493C28 6.69971 27.3645 6.40136 26.9451 6.74976L22 10.86Z" fill="url(#paint2_linear_53_676)"/>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="paint0_linear_53_676" x1="10.507" y1="23.1523" x2="1.11629" y2="-9.46948" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#E3D9FF"/>
|
|
||||||
<stop offset="1" stop-color="#7341FF"/>
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient id="paint1_radial_53_676" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(23.8954 13.0774) scale(27.9882 26.381)">
|
|
||||||
<stop offset="0.5561" stop-color="#6D4AFF"/>
|
|
||||||
<stop offset="0.9944" stop-color="#AA8EFF"/>
|
|
||||||
</radialGradient>
|
|
||||||
<linearGradient id="paint2_linear_53_676" x1="37.0552" y1="43.5221" x2="15.4546" y2="-3.07472" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0.271" stop-color="#E3D9FF"/>
|
|
||||||
<stop offset="1" stop-color="#7341FF"/>
|
|
||||||
</linearGradient>
|
|
||||||
<clipPath id="clip0_53_676">
|
|
||||||
<rect width="134" height="36" fill="white"/>
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 6.6 KiB |
@ -1,34 +0,0 @@
|
|||||||
<svg width="134" height="36" viewBox="0 0 134 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<g clip-path="url(#clip0_22_69)">
|
|
||||||
<path d="M38 21.259V24.924H40.5591V21.4191C40.5591 21.0789 40.6927 20.7502 40.9344 20.51C41.1732 20.2699 41.5002 20.1327 41.8386 20.1327H44.463C45.6857 20.1327 46.86 19.6438 47.7244 18.7719C48.5888 17.9028 49.075 16.7221 49.075 15.4928C49.075 14.2635 48.5888 13.0828 47.7244 12.2109C46.86 11.3418 45.6857 10.8529 44.4602 10.8529H38V15.4328H40.5591V13.2744H44.2896C44.8697 13.2744 45.4241 13.5059 45.8336 13.9176C46.243 14.3293 46.4733 14.8867 46.4733 15.4699C46.4733 16.0531 46.243 16.6106 45.8336 17.0223C45.4241 17.4339 44.8697 17.6655 44.2896 17.6655H41.5798C41.1107 17.6655 40.6444 17.757 40.2122 17.9399C39.7771 18.12 39.3847 18.3859 39.0521 18.7204C38.7194 19.0549 38.4578 19.4523 38.2758 19.8868C38.0938 20.3185 38 20.7873 38 21.259Z" fill="#1B1340"/>
|
|
||||||
<path d="M49.4703 24.924V19.3408C49.4703 17.0623 50.7925 15.2498 53.4397 15.2498C53.8633 15.2441 54.287 15.2898 54.7022 15.3899V17.6855C54.4008 17.6655 54.142 17.6655 54.0197 17.6655C52.6179 17.6655 52.0151 18.3116 52.0151 19.6209V24.924H49.4703Z" fill="#1B1340"/>
|
|
||||||
<path d="M55.4641 20.1899C55.4641 17.3882 57.5682 15.2527 60.4969 15.2527C63.4256 15.2527 65.5297 17.3882 65.5297 20.1899C65.5297 22.9915 63.4256 25.1471 60.4969 25.1471C57.5682 25.1471 55.4641 22.9886 55.4641 20.1899ZM63.0218 20.1899C63.0218 18.5975 61.9584 17.4683 60.4969 17.4683C59.0325 17.4683 57.972 18.5946 57.972 20.1899C57.972 21.8022 59.0354 22.9115 60.4969 22.9115C61.9612 22.9115 63.0218 21.7994 63.0218 20.1899Z" fill="#1B1340"/>
|
|
||||||
<path d="M73.6676 20.1899C73.6676 17.3882 75.7717 15.2527 78.7004 15.2527C81.6263 15.2527 83.7304 17.3882 83.7304 20.1899C83.7304 22.9915 81.6263 25.1471 78.7004 25.1471C75.7717 25.1471 73.6676 22.9886 73.6676 20.1899ZM81.2225 20.1899C81.2225 18.5975 80.1591 17.4683 78.6976 17.4683C77.2361 17.4683 76.1726 18.5946 76.1726 20.1899C76.1726 21.8022 77.2361 22.9115 78.6976 22.9115C80.1591 22.9115 81.2225 21.7994 81.2225 20.1899Z" fill="#1B1340"/>
|
|
||||||
<path d="M85.0526 24.9241V19.5438C85.0526 17.0452 86.6364 15.2498 89.4627 15.2498C92.2692 15.2498 93.8529 17.0423 93.8529 19.5438V24.9241H91.328V19.7439C91.328 18.3545 90.7053 17.4854 89.4627 17.4854C88.2202 17.4854 87.5975 18.3516 87.5975 19.7439V24.9241H85.0526Z" fill="#1B1340"/>
|
|
||||||
<path d="M72.9056 17.4881H70.1588V21.0159C70.1588 22.2452 70.5996 22.8084 71.862 22.8084C71.9815 22.8084 72.2829 22.8084 72.6639 22.7884V24.8639C72.1435 25.004 71.6829 25.0869 71.1796 25.0869C69.0556 25.0869 67.6112 23.7976 67.6112 21.359V17.4881H65.9051V15.4527H66.3316C66.4994 15.4527 66.6671 15.4184 66.8207 15.3555C66.9771 15.2897 67.1164 15.1954 67.2358 15.0753C67.3553 14.9552 67.4491 14.8152 67.5145 14.6579C67.5799 14.5007 67.6112 14.3349 67.6112 14.1662V12.2479H70.156V15.4527H72.9027V17.4881H72.9056Z" fill="#1B1340"/>
|
|
||||||
<path d="M98.8823 11.216H102.457L105.808 19.4391C106.107 20.1282 106.362 20.8315 106.577 21.5492H106.612C106.827 20.8315 107.083 20.1253 107.381 19.4391L110.732 11.216H114.307V24.9288H111.717V15.7421C111.714 15.4391 111.729 15.136 111.76 14.8358H111.717C111.637 15.1589 111.531 15.4762 111.396 15.7822L107.683 24.7687H105.535L101.811 15.7822C101.676 15.4734 101.561 15.1589 101.469 14.8358H101.429C101.458 15.136 101.472 15.4391 101.469 15.7421V24.9316H98.8823V11.216Z" fill="#6D4AFF"/>
|
|
||||||
<path d="M123.165 15.8823C123.915 16.274 124.535 16.8744 124.951 17.6092C125.396 18.407 125.62 19.3105 125.603 20.2225V24.9288H123.335L123.174 23.5164C122.878 24.0281 122.444 24.4456 121.922 24.7258C121.362 25.0146 120.736 25.1604 120.104 25.1461C119.297 25.1547 118.505 24.9402 117.813 24.5256C117.112 24.1025 116.544 23.4963 116.165 22.7758C115.751 21.981 115.545 21.0975 115.562 20.2025C115.55 19.3219 115.777 18.4556 116.217 17.6893C116.647 16.9459 117.27 16.334 118.025 15.9194C118.806 15.4877 119.685 15.2647 120.578 15.2761C121.477 15.259 122.367 15.4677 123.165 15.8823ZM122.335 22.2126C122.829 21.7436 123.073 21.0832 123.073 20.2025C123.105 19.482 122.849 18.7787 122.367 18.244C122.137 18.0038 121.862 17.8151 121.554 17.6836C121.247 17.5521 120.92 17.4863 120.587 17.4863C120.254 17.4863 119.923 17.5521 119.619 17.6836C119.312 17.8151 119.036 18.0038 118.806 18.244C118.347 18.7929 118.094 19.4849 118.094 20.1997C118.094 20.9145 118.347 21.6064 118.806 22.1554C119.033 22.4013 119.309 22.5928 119.619 22.7244C119.926 22.853 120.259 22.9188 120.592 22.9102C120.914 22.9159 121.236 22.8559 121.537 22.7358C121.833 22.6186 122.106 22.4384 122.335 22.2126Z" fill="#6D4AFF"/>
|
|
||||||
<path d="M127.001 13.5206C126.849 13.3805 126.728 13.2118 126.645 13.0231C126.562 12.8344 126.522 12.6314 126.525 12.4255C126.522 12.2196 126.565 12.0138 126.645 11.8222C126.728 11.6306 126.849 11.4591 127.001 11.319C127.303 11.0216 127.71 10.8529 128.135 10.8529C128.56 10.8529 128.968 11.0216 129.27 11.319C129.422 11.4619 129.542 11.6335 129.623 11.8222C129.703 12.0138 129.746 12.2168 129.743 12.4255C129.746 12.6314 129.706 12.8344 129.623 13.0231C129.542 13.2118 129.422 13.3805 129.27 13.5206C128.965 13.8122 128.557 13.9752 128.135 13.9752C127.71 13.9752 127.306 13.8122 127.001 13.5206ZM129.413 24.9288H126.863V15.4791H129.413V24.9288Z" fill="#6D4AFF"/>
|
|
||||||
<path d="M133.824 24.9288H131.271V11.216H133.824V24.9288Z" fill="#6D4AFF"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.7801 14.3609L17.7816 14.3622L10 23L0 11.9929V7.24491C0 6.69969 0.635467 6.4014 1.05491 6.74973L12.1495 15.9633C13.2223 16.8543 14.7777 16.8543 15.8506 15.9633L17.7801 14.3609Z" fill="url(#paint0_linear_22_69)"/>
|
|
||||||
<path d="M22 10.8565L17.7801 14.3609L17.7816 14.3622L12.1943 19.2977C11.2425 20.1385 9.81982 20.1596 8.84341 19.3476L0 11.9929V26.1035C0 27.7032 1.29683 29.0001 2.89655 29.0001H22L24 19.9283L22 10.8565Z" fill="url(#paint1_radial_22_69)"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M22 10.86V29L25.1034 29C26.7032 29 28 27.7031 28 26.1035V7.24493C28 6.69971 27.3645 6.40136 26.9451 6.74976L22 10.86Z" fill="url(#paint2_linear_22_69)"/>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="paint0_linear_22_69" x1="10.507" y1="23.1523" x2="1.11629" y2="-9.46948" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#E3D9FF"/>
|
|
||||||
<stop offset="1" stop-color="#7341FF"/>
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient id="paint1_radial_22_69" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(23.8954 13.0774) scale(27.9882 26.381)">
|
|
||||||
<stop offset="0.5561" stop-color="#6D4AFF"/>
|
|
||||||
<stop offset="0.9944" stop-color="#AA8EFF"/>
|
|
||||||
</radialGradient>
|
|
||||||
<linearGradient id="paint2_linear_22_69" x1="37.0552" y1="43.5221" x2="15.4546" y2="-3.07472" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0.271" stop-color="#E3D9FF"/>
|
|
||||||
<stop offset="1" stop-color="#7341FF"/>
|
|
||||||
</linearGradient>
|
|
||||||
<clipPath id="clip0_22_69">
|
|
||||||
<rect width="134" height="36" fill="white"/>
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 74 KiB |
@ -0,0 +1,331 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="265"
|
||||||
|
height="148"
|
||||||
|
viewBox="0 0 265 148"
|
||||||
|
fill="none"
|
||||||
|
version="1.1"
|
||||||
|
id="svg110"
|
||||||
|
sodipodi:docname="img-welcome-dark.svg"
|
||||||
|
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
|
||||||
|
inkscape:export-filename="/home/dev/gopath/src/github.com/ProtonMail/proton-bridge/internal/frontend/qml/icons/img-welcome.png"
|
||||||
|
inkscape:export-xdpi="400"
|
||||||
|
inkscape:export-ydpi="400"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview112"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#ffffff"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:pageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:snap-global="true"
|
||||||
|
inkscape:snap-page="true"
|
||||||
|
inkscape:zoom="0.69351284"
|
||||||
|
inkscape:cx="93.004767"
|
||||||
|
inkscape:cy="115.35475"
|
||||||
|
inkscape:window-width="1916"
|
||||||
|
inkscape:window-height="1041"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="18"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg110" />
|
||||||
|
<rect
|
||||||
|
style="fill:#1c1b24;fill-opacity:1;stroke:none;stroke-width:27.9987;stroke-linecap:round;stroke-linejoin:round"
|
||||||
|
id="rect951"
|
||||||
|
width="265"
|
||||||
|
height="148"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
ry="0"
|
||||||
|
inkscape:export-filename="/home/dev/gopath/src/github.com/ProtonMail/proton-bridge/internal/frontend/qml/icons/img-welcome.png"
|
||||||
|
inkscape:export-xdpi="400"
|
||||||
|
inkscape:export-ydpi="400" />
|
||||||
|
<path
|
||||||
|
d="M221.171 147.001H44.8555C43.1441 147.001 41.8047 145.661 41.8047 143.95V142.238C41.8047 140.527 43.1441 139.188 44.8555 139.188H221.171C222.882 139.188 224.221 140.527 224.221 142.238V143.95C224.221 145.661 222.808 147.001 221.171 147.001Z"
|
||||||
|
fill="#B0D4E5"
|
||||||
|
id="path2" />
|
||||||
|
<path
|
||||||
|
d="M141.83 143.503H123.376C120.995 143.503 119.135 141.568 119.135 139.262H146.071C146.071 141.568 144.211 143.503 141.83 143.503Z"
|
||||||
|
fill="#DAF3FF"
|
||||||
|
id="path4" />
|
||||||
|
<path
|
||||||
|
d="M206.034 139.187H58.501V53.9292C58.501 49.0182 62.5191 45 67.4302 45H197.104C202.016 45 206.034 49.0182 206.034 53.9292V139.187Z"
|
||||||
|
fill="url(#paint0_radial_8674_44242)"
|
||||||
|
id="path6" />
|
||||||
|
<path
|
||||||
|
d="M199.115 139.187H66.167V55.3434C66.167 54.1529 67.1343 53.1855 68.3249 53.1855H196.883C198.074 53.1855 199.041 54.1529 199.041 55.3434V139.187H199.115Z"
|
||||||
|
fill="url(#paint1_linear_8674_44242)"
|
||||||
|
id="path8" />
|
||||||
|
<path
|
||||||
|
d="M190.805 131.286H76.1797V62.5185C76.1797 61.5234 77.028 60.7148 78.0721 60.7148H188.847C189.891 60.7148 190.739 61.5234 190.739 62.5185V131.286H190.805Z"
|
||||||
|
fill="url(#paint2_radial_8674_44242)"
|
||||||
|
id="path10" />
|
||||||
|
<path
|
||||||
|
d="M84.1558 70.7744C85.3064 70.7744 86.2392 69.8416 86.2392 68.6909C86.2392 67.5402 85.3064 66.6074 84.1558 66.6074C83.0051 66.6074 82.0723 67.5402 82.0723 68.6909C82.0723 69.8416 83.0051 70.7744 84.1558 70.7744Z"
|
||||||
|
fill="#B0D4E5"
|
||||||
|
id="path12" />
|
||||||
|
<path
|
||||||
|
d="M90.6304 70.7744C91.781 70.7744 92.7139 69.8416 92.7139 68.6909C92.7139 67.5402 91.781 66.6074 90.6304 66.6074C89.4797 66.6074 88.5469 67.5402 88.5469 68.6909C88.5469 69.8416 89.4797 70.7744 90.6304 70.7744Z"
|
||||||
|
fill="#B0D4E5"
|
||||||
|
id="path14" />
|
||||||
|
<path
|
||||||
|
d="M97.105 70.7744C98.2557 70.7744 99.1885 69.8416 99.1885 68.6909C99.1885 67.5402 98.2557 66.6074 97.105 66.6074C95.9543 66.6074 95.0215 67.5402 95.0215 68.6909C95.0215 69.8416 95.9543 70.7744 97.105 70.7744Z"
|
||||||
|
fill="#B0D4E5"
|
||||||
|
id="path16" />
|
||||||
|
<path
|
||||||
|
d="M242.633 76.3538H180.924C178.747 76.3538 177 74.5616 177 72.3891V33.9646C177 31.7922 178.774 30 180.924 30H242.606C244.783 30 246.53 31.7922 246.53 33.9646V72.3891C246.557 74.5887 244.783 76.3538 242.633 76.3538Z"
|
||||||
|
fill="url(#paint3_linear_8674_44242)"
|
||||||
|
id="path18" />
|
||||||
|
<path
|
||||||
|
d="M209.232 56.6899C210.689 57.8921 212.822 57.8921 214.28 56.6899L245.431 31.0419C244.729 30.4007 243.784 30 242.758 30H180.78C179.754 30 178.809 30.4007 178.107 31.0419L209.232 56.6899Z"
|
||||||
|
fill="url(#paint4_linear_8674_44242)"
|
||||||
|
id="path20" />
|
||||||
|
<path
|
||||||
|
d="M134.4 75C123.858 75 115.312 83.5451 115.312 94.0874V113.301C115.312 115.344 116.968 117 119.011 117H150.184C152.008 117 153.487 115.52 153.487 113.696V94.0874C153.487 83.5465 144.942 75 134.4 75ZM145.387 93.9814L137.044 101.01C135.538 102.279 133.336 102.279 131.83 101.01L123.487 93.9814C123.487 88.0172 128.323 83.1817 134.287 83.1817H134.587C140.551 83.1817 145.387 88.0172 145.387 93.9814Z"
|
||||||
|
fill="#6D4AFF"
|
||||||
|
id="path22" />
|
||||||
|
<path
|
||||||
|
d="M134.4 75C123.858 75 115.312 83.5451 115.312 94.0874V113.301C115.312 115.344 116.968 117 119.011 117H150.184C152.008 117 153.487 115.52 153.487 113.696V94.0874C153.487 83.5465 144.942 75 134.4 75ZM145.387 93.9814L137.044 101.01C135.538 102.279 133.336 102.279 131.83 101.01L123.487 93.9814C123.487 88.0172 128.323 83.1817 134.287 83.1817H134.587C140.551 83.1817 145.387 88.0172 145.387 93.9814Z"
|
||||||
|
fill="url(#paint5_linear_8674_44242)"
|
||||||
|
id="path24" />
|
||||||
|
<g
|
||||||
|
filter="url(#filter0_i_8674_44242)"
|
||||||
|
id="g28">
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M137.057 101.095C136.188 101.799 133.926 102.785 131.838 101.095C129.751 99.4048 125.418 95.6687 123.513 94.0119H123.524L123.487 93.9814C123.487 88.0172 128.323 83.1817 134.287 83.1817H134.587C140.551 83.1817 145.387 88.0172 145.387 93.9814L145.351 94.0119H145.383V117H150.184C152.008 117 153.487 115.52 153.487 113.696V94.0874C153.487 83.5465 144.942 75 134.4 75C123.858 75 115.312 83.5451 115.312 94.0874V95.2946L127.117 105.444C127.986 106.272 130.273 107.432 132.46 105.444C134.647 103.456 136.436 101.716 137.057 101.095Z"
|
||||||
|
fill="url(#paint6_radial_8674_44242)"
|
||||||
|
id="path26" />
|
||||||
|
</g>
|
||||||
|
<circle
|
||||||
|
cx="239.278"
|
||||||
|
cy="30.2778"
|
||||||
|
r="15.2778"
|
||||||
|
fill="url(#paint7_linear_8674_44242)"
|
||||||
|
id="circle30" />
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M245.702 26.668C246.113 27.0766 246.116 27.7417 245.707 28.1534L238.128 35.7923C237.93 35.9911 237.662 36.1028 237.382 36.1028C237.102 36.1028 236.834 35.9911 236.636 35.7923L232.758 31.8835C232.349 31.4718 232.352 30.8067 232.764 30.3981C233.175 29.9895 233.84 29.9921 234.249 30.4039L237.382 33.5613L244.216 26.6738C244.625 26.262 245.29 26.2595 245.702 26.668Z"
|
||||||
|
fill="white"
|
||||||
|
id="path32" />
|
||||||
|
<path
|
||||||
|
d="M0.878906 69.6212C0.878906 56.0233 11.9022 45 25.5001 45V45C39.0981 45 50.1214 56.0233 50.1214 69.6212V94.2425H25.5002C11.9022 94.2425 0.878906 83.2192 0.878906 69.6212V69.6212Z"
|
||||||
|
fill="url(#paint8_linear_8674_44242)"
|
||||||
|
id="path34" />
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M31.4987 62.28V64.8004H33.9547C34.7948 64.8004 35.5056 65.4789 35.5056 66.3513V79.5019C35.5056 80.342 34.8271 81.0529 33.9547 81.0529H17.3791C16.539 81.0529 15.8281 80.3743 15.8281 79.5019V66.3513C15.8281 65.5112 16.5067 64.8004 17.3791 64.8004H19.802V62.28C19.802 59.0488 22.4192 56.4316 25.6504 56.4316C28.8815 56.4316 31.4987 59.0488 31.4987 62.28ZM29.0361 62.28V64.8004H22.3292V62.28C22.3292 59.9536 24.1059 58.9265 25.6827 58.9265C27.2594 58.9265 29.0361 59.9536 29.0361 62.28ZM25.9832 69.195C27.1141 69.195 28.0188 70.0997 28.0188 71.2306C28.0188 72.006 27.5988 72.6846 26.9526 73.0077L27.7927 76.6265H24.1738L25.0139 73.0077C24.3677 72.6523 23.9476 72.006 23.9476 71.2306C23.9476 70.0997 24.8523 69.195 25.9832 69.195Z"
|
||||||
|
fill="white"
|
||||||
|
id="path36" />
|
||||||
|
<defs
|
||||||
|
id="defs108">
|
||||||
|
<filter
|
||||||
|
id="filter0_i_8674_44242"
|
||||||
|
x="114.72"
|
||||||
|
y="75"
|
||||||
|
width="38.7675"
|
||||||
|
height="43.6537"
|
||||||
|
filterUnits="userSpaceOnUse"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feFlood
|
||||||
|
flood-opacity="0"
|
||||||
|
result="BackgroundImageFix"
|
||||||
|
id="feFlood38" />
|
||||||
|
<feBlend
|
||||||
|
mode="normal"
|
||||||
|
in="SourceGraphic"
|
||||||
|
in2="BackgroundImageFix"
|
||||||
|
result="shape"
|
||||||
|
id="feBlend40" />
|
||||||
|
<feColorMatrix
|
||||||
|
in="SourceAlpha"
|
||||||
|
type="matrix"
|
||||||
|
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
||||||
|
result="hardAlpha"
|
||||||
|
id="feColorMatrix42" />
|
||||||
|
<feOffset
|
||||||
|
dx="-0.592742"
|
||||||
|
dy="1.65375"
|
||||||
|
id="feOffset44" />
|
||||||
|
<feGaussianBlur
|
||||||
|
stdDeviation="4.44556"
|
||||||
|
id="feGaussianBlur46" />
|
||||||
|
<feComposite
|
||||||
|
in2="hardAlpha"
|
||||||
|
operator="arithmetic"
|
||||||
|
k2="-1"
|
||||||
|
k3="1"
|
||||||
|
id="feComposite48" />
|
||||||
|
<feColorMatrix
|
||||||
|
type="matrix"
|
||||||
|
values="0 0 0 0 0.462745 0 0 0 0 0.337255 0 0 0 0 1 0 0 0 0.24 0"
|
||||||
|
id="feColorMatrix50" />
|
||||||
|
<feBlend
|
||||||
|
mode="normal"
|
||||||
|
in2="shape"
|
||||||
|
result="effect1_innerShadow_8674_44242"
|
||||||
|
id="feBlend52" />
|
||||||
|
</filter>
|
||||||
|
<radialGradient
|
||||||
|
id="paint0_radial_8674_44242"
|
||||||
|
cx="0"
|
||||||
|
cy="0"
|
||||||
|
r="1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="translate(202 51.5) rotate(145.641) scale(59.357 92.9759)">
|
||||||
|
<stop
|
||||||
|
stop-color="#292842"
|
||||||
|
id="stop55" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="#38385F"
|
||||||
|
id="stop57" />
|
||||||
|
</radialGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint1_linear_8674_44242"
|
||||||
|
x1="63.7079"
|
||||||
|
y1="144.988"
|
||||||
|
x2="207.623"
|
||||||
|
y2="58.7515"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop
|
||||||
|
stop-color="#35168C"
|
||||||
|
id="stop60" />
|
||||||
|
<stop
|
||||||
|
offset="0.317708"
|
||||||
|
stop-color="#FF5454"
|
||||||
|
id="stop62" />
|
||||||
|
<stop
|
||||||
|
offset="0.46875"
|
||||||
|
stop-color="#FFDD64"
|
||||||
|
id="stop64" />
|
||||||
|
<stop
|
||||||
|
offset="0.677083"
|
||||||
|
stop-color="#BCE6FF"
|
||||||
|
id="stop66" />
|
||||||
|
<stop
|
||||||
|
offset="0.911458"
|
||||||
|
stop-color="#6983EF"
|
||||||
|
id="stop68" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="#2395FF"
|
||||||
|
id="stop70" />
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient
|
||||||
|
id="paint2_radial_8674_44242"
|
||||||
|
cx="0"
|
||||||
|
cy="0"
|
||||||
|
r="1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="translate(188.5 63.5) rotate(135) scale(33.9411 55.1286)">
|
||||||
|
<stop
|
||||||
|
stop-color="#DDDBE3"
|
||||||
|
id="stop73" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="white"
|
||||||
|
id="stop75" />
|
||||||
|
</radialGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint3_linear_8674_44242"
|
||||||
|
x1="211.765"
|
||||||
|
y1="30"
|
||||||
|
x2="211.765"
|
||||||
|
y2="66.4208"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop
|
||||||
|
stop-color="#C1DEF8"
|
||||||
|
id="stop78" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="#ECFAFF"
|
||||||
|
id="stop80" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint4_linear_8674_44242"
|
||||||
|
x1="211.769"
|
||||||
|
y1="18.4116"
|
||||||
|
x2="211.769"
|
||||||
|
y2="50.4177"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop
|
||||||
|
stop-color="#DEEBF7"
|
||||||
|
id="stop83" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="white"
|
||||||
|
id="stop85" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint5_linear_8674_44242"
|
||||||
|
x1="116.679"
|
||||||
|
y1="121.846"
|
||||||
|
x2="122.395"
|
||||||
|
y2="105.692"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop
|
||||||
|
stop-color="#28B0E8"
|
||||||
|
id="stop88" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="#C5B7FF"
|
||||||
|
stop-opacity="0"
|
||||||
|
id="stop90" />
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient
|
||||||
|
id="paint6_radial_8674_44242"
|
||||||
|
cx="0"
|
||||||
|
cy="0"
|
||||||
|
r="1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="translate(151.237 120.479) rotate(-138.034) scale(48.3148 39.5031)">
|
||||||
|
<stop
|
||||||
|
stop-color="#E2DBFF"
|
||||||
|
id="stop93" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="#6D4AFF"
|
||||||
|
id="stop95" />
|
||||||
|
</radialGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint7_linear_8674_44242"
|
||||||
|
x1="240.861"
|
||||||
|
y1="12.772"
|
||||||
|
x2="241.004"
|
||||||
|
y2="45.5557"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop
|
||||||
|
stop-color="#2AF091"
|
||||||
|
id="stop98" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="#00C5A1"
|
||||||
|
id="stop100" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint8_linear_8674_44242"
|
||||||
|
x1="41.1252"
|
||||||
|
y1="56.3637"
|
||||||
|
x2="5.14027"
|
||||||
|
y2="101.345"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop
|
||||||
|
stop-color="#FFD66C"
|
||||||
|
id="stop103" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="#FF8E4F"
|
||||||
|
id="stop105" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 73 KiB |
@ -1,34 +1,331 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?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
|
||||||
<svg width="100%" height="100%" viewBox="0 0 265 148" 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;">
|
width="265"
|
||||||
<path id="path2" d="M221.171,147.001L44.856,147.001C43.144,147.001 41.805,145.661 41.805,143.95L41.805,142.238C41.805,140.527 43.144,139.188 44.856,139.188L221.171,139.188C222.882,139.188 224.221,140.527 224.221,142.238L224.221,143.95C224.221,145.661 222.808,147.001 221.171,147.001Z" style="fill:rgb(176,212,229);fill-rule:nonzero;"/>
|
height="148"
|
||||||
<path id="path4" d="M141.83,143.503L123.376,143.503C120.995,143.503 119.135,141.568 119.135,139.262L146.071,139.262C146.071,141.568 144.211,143.503 141.83,143.503Z" style="fill:rgb(218,243,255);fill-rule:nonzero;"/>
|
viewBox="0 0 265 148"
|
||||||
<path id="path6" d="M206.034,139.187L58.501,139.187L58.501,53.929C58.501,49.018 62.519,45 67.43,45L197.104,45C202.016,45 206.034,49.018 206.034,53.929L206.034,139.187Z" style="fill:url(#_Radial1);fill-rule:nonzero;"/>
|
fill="none"
|
||||||
<path id="path8" d="M199.115,139.187L66.167,139.187L66.167,55.343C66.167,54.153 67.134,53.186 68.325,53.186L196.883,53.186C198.074,53.186 199.041,54.153 199.041,55.343L199.041,139.187L199.115,139.187Z" style="fill:url(#_Linear2);fill-rule:nonzero;"/>
|
version="1.1"
|
||||||
<path id="path10" d="M190.805,131.286L76.18,131.286L76.18,62.519C76.18,61.523 77.028,60.715 78.072,60.715L188.847,60.715C189.891,60.715 190.739,61.523 190.739,62.519L190.739,131.286L190.805,131.286Z" style="fill:url(#_Radial3);fill-rule:nonzero;"/>
|
id="svg110"
|
||||||
<path id="path12" d="M84.156,70.774C85.306,70.774 86.239,69.842 86.239,68.691C86.239,67.54 85.306,66.607 84.156,66.607C83.005,66.607 82.072,67.54 82.072,68.691C82.072,69.842 83.005,70.774 84.156,70.774Z" style="fill:rgb(176,212,229);fill-rule:nonzero;"/>
|
sodipodi:docname="img-welcome.svg"
|
||||||
<path id="path14" d="M90.63,70.774C91.781,70.774 92.714,69.842 92.714,68.691C92.714,67.54 91.781,66.607 90.63,66.607C89.48,66.607 88.547,67.54 88.547,68.691C88.547,69.842 89.48,70.774 90.63,70.774Z" style="fill:rgb(176,212,229);fill-rule:nonzero;"/>
|
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
|
||||||
<path id="path16" d="M97.105,70.774C98.256,70.774 99.189,69.842 99.189,68.691C99.189,67.54 98.256,66.607 97.105,66.607C95.954,66.607 95.022,67.54 95.022,68.691C95.022,69.842 95.954,70.774 97.105,70.774Z" style="fill:rgb(176,212,229);fill-rule:nonzero;"/>
|
inkscape:export-filename="/home/dev/gopath/src/github.com/ProtonMail/proton-bridge/internal/frontend/qml/icons/img-welcome.png"
|
||||||
<path id="path18" d="M242.633,76.354L180.924,76.354C178.747,76.354 177,74.562 177,72.389L177,33.965C177,31.792 178.774,30 180.924,30L242.606,30C244.783,30 246.53,31.792 246.53,33.965L246.53,72.389C246.557,74.589 244.783,76.354 242.633,76.354Z" style="fill:url(#_Linear4);fill-rule:nonzero;"/>
|
inkscape:export-xdpi="400"
|
||||||
<path id="path20" d="M209.232,56.69C210.689,57.892 212.822,57.892 214.28,56.69L245.431,31.042C244.729,30.401 243.784,30 242.758,30L180.78,30C179.754,30 178.809,30.401 178.107,31.042L209.232,56.69Z" style="fill:url(#_Linear5);fill-rule:nonzero;"/>
|
inkscape:export-ydpi="400"
|
||||||
<path id="path22" d="M134.4,75C123.858,75 115.312,83.545 115.312,94.087L115.312,113.301C115.312,115.344 116.968,117 119.011,117L150.184,117C152.008,117 153.487,115.52 153.487,113.696L153.487,94.087C153.487,83.547 144.942,75 134.4,75ZM145.387,93.981L137.044,101.01C135.538,102.279 133.336,102.279 131.83,101.01L123.487,93.981C123.487,88.017 128.323,83.182 134.287,83.182L134.587,83.182C140.551,83.182 145.387,88.017 145.387,93.981Z" style="fill:rgb(109,74,255);fill-rule:nonzero;"/>
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
<path id="path24" d="M134.4,75C123.858,75 115.312,83.545 115.312,94.087L115.312,113.301C115.312,115.344 116.968,117 119.011,117L150.184,117C152.008,117 153.487,115.52 153.487,113.696L153.487,94.087C153.487,83.547 144.942,75 134.4,75ZM145.387,93.981L137.044,101.01C135.538,102.279 133.336,102.279 131.83,101.01L123.487,93.981C123.487,88.017 128.323,83.182 134.287,83.182L134.587,83.182C140.551,83.182 145.387,88.017 145.387,93.981Z" style="fill:url(#_Linear6);fill-rule:nonzero;"/>
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
<g id="g28">
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<path id="path26" d="M137.057,101.095C136.188,101.799 133.926,102.785 131.838,101.095C129.751,99.405 125.418,95.669 123.513,94.012L123.524,94.012L123.487,93.981C123.487,88.017 128.323,83.182 134.287,83.182L134.587,83.182C140.551,83.182 145.387,88.017 145.387,93.981L145.351,94.012L145.383,94.012L145.383,117L150.184,117C152.008,117 153.487,115.52 153.487,113.696L153.487,94.087C153.487,83.547 144.942,75 134.4,75C123.858,75 115.312,83.545 115.312,94.087L115.312,95.295L127.117,105.444C127.986,106.272 130.273,107.432 132.46,105.444C134.647,103.456 136.436,101.716 137.057,101.095Z" style="fill:url(#_Radial7);"/>
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
</g>
|
<sodipodi:namedview
|
||||||
<circle id="circle30" cx="239.278" cy="30.278" r="15.278" style="fill:url(#_Linear8);"/>
|
id="namedview112"
|
||||||
<path id="path32" d="M245.702,26.668C246.113,27.077 246.116,27.742 245.707,28.153L238.128,35.792C237.93,35.991 237.662,36.103 237.382,36.103C237.102,36.103 236.834,35.991 236.636,35.792L232.758,31.884C232.349,31.472 232.352,30.807 232.764,30.398C233.175,29.99 233.84,29.992 234.249,30.404L237.382,33.561L244.216,26.674C244.625,26.262 245.29,26.26 245.702,26.668Z" style="fill:white;"/>
|
pagecolor="#505050"
|
||||||
<path id="path34" d="M0.879,69.621C0.879,56.023 11.902,45 25.5,45C39.098,45 50.121,56.023 50.121,69.621L50.121,94.243L25.5,94.243C11.902,94.243 0.879,83.219 0.879,69.621Z" style="fill:url(#_Linear9);fill-rule:nonzero;"/>
|
bordercolor="#ffffff"
|
||||||
<path id="path36" d="M31.499,62.28L31.499,64.8L33.955,64.8C34.795,64.8 35.506,65.479 35.506,66.351L35.506,79.502C35.506,80.342 34.827,81.053 33.955,81.053L17.379,81.053C16.539,81.053 15.828,80.374 15.828,79.502L15.828,66.351C15.828,65.511 16.507,64.8 17.379,64.8L19.802,64.8L19.802,62.28C19.802,59.049 22.419,56.432 25.65,56.432C28.882,56.432 31.499,59.049 31.499,62.28ZM29.036,62.28L29.036,64.8L22.329,64.8L22.329,62.28C22.329,59.954 24.106,58.926 25.683,58.926C27.259,58.926 29.036,59.954 29.036,62.28ZM25.983,69.195C27.114,69.195 28.019,70.1 28.019,71.231C28.019,72.006 27.599,72.685 26.953,73.008L27.793,76.626L24.174,76.626L25.014,73.008C24.368,72.652 23.948,72.006 23.948,71.231C23.948,70.1 24.852,69.195 25.983,69.195Z" style="fill:white;"/>
|
borderopacity="1"
|
||||||
<defs>
|
inkscape:pageshadow="0"
|
||||||
<radialGradient id="_Radial1" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-49.0002,33.4997,-52.4734,-76.7532,202,51.5)"><stop offset="0" style="stop-color:rgb(41,40,66);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(56,56,95);stop-opacity:1"/></radialGradient>
|
inkscape:pageopacity="0"
|
||||||
<linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(143.915,-86.2365,86.2365,143.915,63.7079,144.988)"><stop offset="0" style="stop-color:rgb(53,22,140);stop-opacity:1"/><stop offset="0.32" style="stop-color:rgb(255,84,84);stop-opacity:1"/><stop offset="0.47" style="stop-color:rgb(255,221,100);stop-opacity:1"/><stop offset="0.68" style="stop-color:rgb(188,230,255);stop-opacity:1"/><stop offset="0.91" style="stop-color:rgb(105,131,239);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(35,149,255);stop-opacity:1"/></linearGradient>
|
inkscape:pagecheckerboard="1"
|
||||||
<radialGradient id="_Radial3" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-24,24,-38.9818,-38.9818,188.5,63.5)"><stop offset="0" style="stop-color:rgb(221,219,227);stop-opacity:1"/><stop offset="1" style="stop-color:white;stop-opacity:1"/></radialGradient>
|
showgrid="false"
|
||||||
<linearGradient id="_Linear4" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(2.23013e-15,36.4208,-36.4208,2.23013e-15,211.765,30)"><stop offset="0" style="stop-color:rgb(193,222,248);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(236,250,255);stop-opacity:1"/></linearGradient>
|
inkscape:snap-global="true"
|
||||||
<linearGradient id="_Linear5" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.95981e-15,32.0061,-32.0061,1.95981e-15,211.769,18.4116)"><stop offset="0" style="stop-color:rgb(222,235,247);stop-opacity:1"/><stop offset="1" style="stop-color:white;stop-opacity:1"/></linearGradient>
|
inkscape:snap-page="true"
|
||||||
<linearGradient id="_Linear6" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(5.716,-16.154,16.154,5.716,116.679,121.846)"><stop offset="0" style="stop-color:rgb(40,176,232);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(197,183,255);stop-opacity:0"/></linearGradient>
|
inkscape:zoom="0.69351284"
|
||||||
<radialGradient id="_Radial7" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-35.9241,-32.3076,26.4153,-29.3722,151.237,120.479)"><stop offset="0" style="stop-color:rgb(226,219,255);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(109,74,255);stop-opacity:1"/></radialGradient>
|
inkscape:cx="93.004767"
|
||||||
<linearGradient id="_Linear8" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.143,32.7837,-32.7837,0.143,240.861,12.772)"><stop offset="0" style="stop-color:rgb(42,240,145);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(0,197,161);stop-opacity:1"/></linearGradient>
|
inkscape:cy="115.35475"
|
||||||
<linearGradient id="_Linear9" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-35.9849,44.9813,-44.9813,-35.9849,41.1252,56.3637)"><stop offset="0" style="stop-color:rgb(255,214,108);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(255,142,79);stop-opacity:1"/></linearGradient>
|
inkscape:window-width="1916"
|
||||||
</defs>
|
inkscape:window-height="1041"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="18"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg110" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:27.9987;stroke-linecap:round;stroke-linejoin:round"
|
||||||
|
id="rect951"
|
||||||
|
width="265"
|
||||||
|
height="148"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
ry="0"
|
||||||
|
inkscape:export-filename="/home/dev/gopath/src/github.com/ProtonMail/proton-bridge/internal/frontend/qml/icons/img-welcome.png"
|
||||||
|
inkscape:export-xdpi="400"
|
||||||
|
inkscape:export-ydpi="400" />
|
||||||
|
<path
|
||||||
|
d="M221.171 147.001H44.8555C43.1441 147.001 41.8047 145.661 41.8047 143.95V142.238C41.8047 140.527 43.1441 139.188 44.8555 139.188H221.171C222.882 139.188 224.221 140.527 224.221 142.238V143.95C224.221 145.661 222.808 147.001 221.171 147.001Z"
|
||||||
|
fill="#B0D4E5"
|
||||||
|
id="path2" />
|
||||||
|
<path
|
||||||
|
d="M141.83 143.503H123.376C120.995 143.503 119.135 141.568 119.135 139.262H146.071C146.071 141.568 144.211 143.503 141.83 143.503Z"
|
||||||
|
fill="#DAF3FF"
|
||||||
|
id="path4" />
|
||||||
|
<path
|
||||||
|
d="M206.034 139.187H58.501V53.9292C58.501 49.0182 62.5191 45 67.4302 45H197.104C202.016 45 206.034 49.0182 206.034 53.9292V139.187Z"
|
||||||
|
fill="url(#paint0_radial_8674_44242)"
|
||||||
|
id="path6" />
|
||||||
|
<path
|
||||||
|
d="M199.115 139.187H66.167V55.3434C66.167 54.1529 67.1343 53.1855 68.3249 53.1855H196.883C198.074 53.1855 199.041 54.1529 199.041 55.3434V139.187H199.115Z"
|
||||||
|
fill="url(#paint1_linear_8674_44242)"
|
||||||
|
id="path8" />
|
||||||
|
<path
|
||||||
|
d="M190.805 131.286H76.1797V62.5185C76.1797 61.5234 77.028 60.7148 78.0721 60.7148H188.847C189.891 60.7148 190.739 61.5234 190.739 62.5185V131.286H190.805Z"
|
||||||
|
fill="url(#paint2_radial_8674_44242)"
|
||||||
|
id="path10" />
|
||||||
|
<path
|
||||||
|
d="M84.1558 70.7744C85.3064 70.7744 86.2392 69.8416 86.2392 68.6909C86.2392 67.5402 85.3064 66.6074 84.1558 66.6074C83.0051 66.6074 82.0723 67.5402 82.0723 68.6909C82.0723 69.8416 83.0051 70.7744 84.1558 70.7744Z"
|
||||||
|
fill="#B0D4E5"
|
||||||
|
id="path12" />
|
||||||
|
<path
|
||||||
|
d="M90.6304 70.7744C91.781 70.7744 92.7139 69.8416 92.7139 68.6909C92.7139 67.5402 91.781 66.6074 90.6304 66.6074C89.4797 66.6074 88.5469 67.5402 88.5469 68.6909C88.5469 69.8416 89.4797 70.7744 90.6304 70.7744Z"
|
||||||
|
fill="#B0D4E5"
|
||||||
|
id="path14" />
|
||||||
|
<path
|
||||||
|
d="M97.105 70.7744C98.2557 70.7744 99.1885 69.8416 99.1885 68.6909C99.1885 67.5402 98.2557 66.6074 97.105 66.6074C95.9543 66.6074 95.0215 67.5402 95.0215 68.6909C95.0215 69.8416 95.9543 70.7744 97.105 70.7744Z"
|
||||||
|
fill="#B0D4E5"
|
||||||
|
id="path16" />
|
||||||
|
<path
|
||||||
|
d="M242.633 76.3538H180.924C178.747 76.3538 177 74.5616 177 72.3891V33.9646C177 31.7922 178.774 30 180.924 30H242.606C244.783 30 246.53 31.7922 246.53 33.9646V72.3891C246.557 74.5887 244.783 76.3538 242.633 76.3538Z"
|
||||||
|
fill="url(#paint3_linear_8674_44242)"
|
||||||
|
id="path18" />
|
||||||
|
<path
|
||||||
|
d="M209.232 56.6899C210.689 57.8921 212.822 57.8921 214.28 56.6899L245.431 31.0419C244.729 30.4007 243.784 30 242.758 30H180.78C179.754 30 178.809 30.4007 178.107 31.0419L209.232 56.6899Z"
|
||||||
|
fill="url(#paint4_linear_8674_44242)"
|
||||||
|
id="path20" />
|
||||||
|
<path
|
||||||
|
d="M134.4 75C123.858 75 115.312 83.5451 115.312 94.0874V113.301C115.312 115.344 116.968 117 119.011 117H150.184C152.008 117 153.487 115.52 153.487 113.696V94.0874C153.487 83.5465 144.942 75 134.4 75ZM145.387 93.9814L137.044 101.01C135.538 102.279 133.336 102.279 131.83 101.01L123.487 93.9814C123.487 88.0172 128.323 83.1817 134.287 83.1817H134.587C140.551 83.1817 145.387 88.0172 145.387 93.9814Z"
|
||||||
|
fill="#6D4AFF"
|
||||||
|
id="path22" />
|
||||||
|
<path
|
||||||
|
d="M134.4 75C123.858 75 115.312 83.5451 115.312 94.0874V113.301C115.312 115.344 116.968 117 119.011 117H150.184C152.008 117 153.487 115.52 153.487 113.696V94.0874C153.487 83.5465 144.942 75 134.4 75ZM145.387 93.9814L137.044 101.01C135.538 102.279 133.336 102.279 131.83 101.01L123.487 93.9814C123.487 88.0172 128.323 83.1817 134.287 83.1817H134.587C140.551 83.1817 145.387 88.0172 145.387 93.9814Z"
|
||||||
|
fill="url(#paint5_linear_8674_44242)"
|
||||||
|
id="path24" />
|
||||||
|
<g
|
||||||
|
filter="url(#filter0_i_8674_44242)"
|
||||||
|
id="g28">
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M137.057 101.095C136.188 101.799 133.926 102.785 131.838 101.095C129.751 99.4048 125.418 95.6687 123.513 94.0119H123.524L123.487 93.9814C123.487 88.0172 128.323 83.1817 134.287 83.1817H134.587C140.551 83.1817 145.387 88.0172 145.387 93.9814L145.351 94.0119H145.383V117H150.184C152.008 117 153.487 115.52 153.487 113.696V94.0874C153.487 83.5465 144.942 75 134.4 75C123.858 75 115.312 83.5451 115.312 94.0874V95.2946L127.117 105.444C127.986 106.272 130.273 107.432 132.46 105.444C134.647 103.456 136.436 101.716 137.057 101.095Z"
|
||||||
|
fill="url(#paint6_radial_8674_44242)"
|
||||||
|
id="path26" />
|
||||||
|
</g>
|
||||||
|
<circle
|
||||||
|
cx="239.278"
|
||||||
|
cy="30.2778"
|
||||||
|
r="15.2778"
|
||||||
|
fill="url(#paint7_linear_8674_44242)"
|
||||||
|
id="circle30" />
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M245.702 26.668C246.113 27.0766 246.116 27.7417 245.707 28.1534L238.128 35.7923C237.93 35.9911 237.662 36.1028 237.382 36.1028C237.102 36.1028 236.834 35.9911 236.636 35.7923L232.758 31.8835C232.349 31.4718 232.352 30.8067 232.764 30.3981C233.175 29.9895 233.84 29.9921 234.249 30.4039L237.382 33.5613L244.216 26.6738C244.625 26.262 245.29 26.2595 245.702 26.668Z"
|
||||||
|
fill="white"
|
||||||
|
id="path32" />
|
||||||
|
<path
|
||||||
|
d="M0.878906 69.6212C0.878906 56.0233 11.9022 45 25.5001 45V45C39.0981 45 50.1214 56.0233 50.1214 69.6212V94.2425H25.5002C11.9022 94.2425 0.878906 83.2192 0.878906 69.6212V69.6212Z"
|
||||||
|
fill="url(#paint8_linear_8674_44242)"
|
||||||
|
id="path34" />
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M31.4987 62.28V64.8004H33.9547C34.7948 64.8004 35.5056 65.4789 35.5056 66.3513V79.5019C35.5056 80.342 34.8271 81.0529 33.9547 81.0529H17.3791C16.539 81.0529 15.8281 80.3743 15.8281 79.5019V66.3513C15.8281 65.5112 16.5067 64.8004 17.3791 64.8004H19.802V62.28C19.802 59.0488 22.4192 56.4316 25.6504 56.4316C28.8815 56.4316 31.4987 59.0488 31.4987 62.28ZM29.0361 62.28V64.8004H22.3292V62.28C22.3292 59.9536 24.1059 58.9265 25.6827 58.9265C27.2594 58.9265 29.0361 59.9536 29.0361 62.28ZM25.9832 69.195C27.1141 69.195 28.0188 70.0997 28.0188 71.2306C28.0188 72.006 27.5988 72.6846 26.9526 73.0077L27.7927 76.6265H24.1738L25.0139 73.0077C24.3677 72.6523 23.9476 72.006 23.9476 71.2306C23.9476 70.0997 24.8523 69.195 25.9832 69.195Z"
|
||||||
|
fill="white"
|
||||||
|
id="path36" />
|
||||||
|
<defs
|
||||||
|
id="defs108">
|
||||||
|
<filter
|
||||||
|
id="filter0_i_8674_44242"
|
||||||
|
x="114.72"
|
||||||
|
y="75"
|
||||||
|
width="38.7675"
|
||||||
|
height="43.6537"
|
||||||
|
filterUnits="userSpaceOnUse"
|
||||||
|
color-interpolation-filters="sRGB">
|
||||||
|
<feFlood
|
||||||
|
flood-opacity="0"
|
||||||
|
result="BackgroundImageFix"
|
||||||
|
id="feFlood38" />
|
||||||
|
<feBlend
|
||||||
|
mode="normal"
|
||||||
|
in="SourceGraphic"
|
||||||
|
in2="BackgroundImageFix"
|
||||||
|
result="shape"
|
||||||
|
id="feBlend40" />
|
||||||
|
<feColorMatrix
|
||||||
|
in="SourceAlpha"
|
||||||
|
type="matrix"
|
||||||
|
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
||||||
|
result="hardAlpha"
|
||||||
|
id="feColorMatrix42" />
|
||||||
|
<feOffset
|
||||||
|
dx="-0.592742"
|
||||||
|
dy="1.65375"
|
||||||
|
id="feOffset44" />
|
||||||
|
<feGaussianBlur
|
||||||
|
stdDeviation="4.44556"
|
||||||
|
id="feGaussianBlur46" />
|
||||||
|
<feComposite
|
||||||
|
in2="hardAlpha"
|
||||||
|
operator="arithmetic"
|
||||||
|
k2="-1"
|
||||||
|
k3="1"
|
||||||
|
id="feComposite48" />
|
||||||
|
<feColorMatrix
|
||||||
|
type="matrix"
|
||||||
|
values="0 0 0 0 0.462745 0 0 0 0 0.337255 0 0 0 0 1 0 0 0 0.24 0"
|
||||||
|
id="feColorMatrix50" />
|
||||||
|
<feBlend
|
||||||
|
mode="normal"
|
||||||
|
in2="shape"
|
||||||
|
result="effect1_innerShadow_8674_44242"
|
||||||
|
id="feBlend52" />
|
||||||
|
</filter>
|
||||||
|
<radialGradient
|
||||||
|
id="paint0_radial_8674_44242"
|
||||||
|
cx="0"
|
||||||
|
cy="0"
|
||||||
|
r="1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="translate(202 51.5) rotate(145.641) scale(59.357 92.9759)">
|
||||||
|
<stop
|
||||||
|
stop-color="#292842"
|
||||||
|
id="stop55" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="#38385F"
|
||||||
|
id="stop57" />
|
||||||
|
</radialGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint1_linear_8674_44242"
|
||||||
|
x1="63.7079"
|
||||||
|
y1="144.988"
|
||||||
|
x2="207.623"
|
||||||
|
y2="58.7515"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop
|
||||||
|
stop-color="#35168C"
|
||||||
|
id="stop60" />
|
||||||
|
<stop
|
||||||
|
offset="0.317708"
|
||||||
|
stop-color="#FF5454"
|
||||||
|
id="stop62" />
|
||||||
|
<stop
|
||||||
|
offset="0.46875"
|
||||||
|
stop-color="#FFDD64"
|
||||||
|
id="stop64" />
|
||||||
|
<stop
|
||||||
|
offset="0.677083"
|
||||||
|
stop-color="#BCE6FF"
|
||||||
|
id="stop66" />
|
||||||
|
<stop
|
||||||
|
offset="0.911458"
|
||||||
|
stop-color="#6983EF"
|
||||||
|
id="stop68" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="#2395FF"
|
||||||
|
id="stop70" />
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient
|
||||||
|
id="paint2_radial_8674_44242"
|
||||||
|
cx="0"
|
||||||
|
cy="0"
|
||||||
|
r="1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="translate(188.5 63.5) rotate(135) scale(33.9411 55.1286)">
|
||||||
|
<stop
|
||||||
|
stop-color="#DDDBE3"
|
||||||
|
id="stop73" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="white"
|
||||||
|
id="stop75" />
|
||||||
|
</radialGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint3_linear_8674_44242"
|
||||||
|
x1="211.765"
|
||||||
|
y1="30"
|
||||||
|
x2="211.765"
|
||||||
|
y2="66.4208"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop
|
||||||
|
stop-color="#C1DEF8"
|
||||||
|
id="stop78" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="#ECFAFF"
|
||||||
|
id="stop80" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint4_linear_8674_44242"
|
||||||
|
x1="211.769"
|
||||||
|
y1="18.4116"
|
||||||
|
x2="211.769"
|
||||||
|
y2="50.4177"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop
|
||||||
|
stop-color="#DEEBF7"
|
||||||
|
id="stop83" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="white"
|
||||||
|
id="stop85" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint5_linear_8674_44242"
|
||||||
|
x1="116.679"
|
||||||
|
y1="121.846"
|
||||||
|
x2="122.395"
|
||||||
|
y2="105.692"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop
|
||||||
|
stop-color="#28B0E8"
|
||||||
|
id="stop88" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="#C5B7FF"
|
||||||
|
stop-opacity="0"
|
||||||
|
id="stop90" />
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient
|
||||||
|
id="paint6_radial_8674_44242"
|
||||||
|
cx="0"
|
||||||
|
cy="0"
|
||||||
|
r="1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="translate(151.237 120.479) rotate(-138.034) scale(48.3148 39.5031)">
|
||||||
|
<stop
|
||||||
|
stop-color="#E2DBFF"
|
||||||
|
id="stop93" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="#6D4AFF"
|
||||||
|
id="stop95" />
|
||||||
|
</radialGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint7_linear_8674_44242"
|
||||||
|
x1="240.861"
|
||||||
|
y1="12.772"
|
||||||
|
x2="241.004"
|
||||||
|
y2="45.5557"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop
|
||||||
|
stop-color="#2AF091"
|
||||||
|
id="stop98" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="#00C5A1"
|
||||||
|
id="stop100" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint8_linear_8674_44242"
|
||||||
|
x1="41.1252"
|
||||||
|
y1="56.3637"
|
||||||
|
x2="5.14027"
|
||||||
|
y2="101.345"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop
|
||||||
|
stop-color="#FFD66C"
|
||||||
|
id="stop103" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
stop-color="#FF8E4F"
|
||||||
|
id="stop105" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 12 KiB |
@ -202,39 +202,6 @@ SPStreamEvent newReportBugErrorEvent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \return The event.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
SPStreamEvent newCertificateInstallSuccessEvent() {
|
|
||||||
auto event = new grpc::CertificateInstallSuccessEvent;
|
|
||||||
auto appEvent = new grpc::AppEvent;
|
|
||||||
appEvent->set_allocated_certificateinstallsuccess(event);
|
|
||||||
return wrapAppEvent(appEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \return The event.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
SPStreamEvent newCertificateInstallCanceledEvent() {
|
|
||||||
auto event = new grpc::CertificateInstallCanceledEvent;
|
|
||||||
auto appEvent = new grpc::AppEvent;
|
|
||||||
appEvent->set_allocated_certificateinstallcanceled(event);
|
|
||||||
return wrapAppEvent(appEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
/// \return The event.
|
|
||||||
//****************************************************************************************************************************************************
|
|
||||||
SPStreamEvent newCertificateInstallFailedEvent() {
|
|
||||||
auto event = new grpc::CertificateInstallFailedEvent;
|
|
||||||
auto appEvent = new grpc::AppEvent;
|
|
||||||
appEvent->set_allocated_certificateinstallfailed(event);
|
|
||||||
return wrapAppEvent(appEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
/// \return The event.
|
/// \return The event.
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
@ -278,9 +245,8 @@ SPStreamEvent newLoginTfaRequestedEvent(QString const &username) {
|
|||||||
/// \param[in] username The username.
|
/// \param[in] username The username.
|
||||||
/// \return The event.
|
/// \return The event.
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
SPStreamEvent newLoginTwoPasswordsRequestedEvent(QString const &username) {
|
SPStreamEvent newLoginTwoPasswordsRequestedEvent() {
|
||||||
auto event = new ::grpc::LoginTwoPasswordsRequestedEvent;
|
auto event = new ::grpc::LoginTwoPasswordsRequestedEvent;
|
||||||
event->set_username(username.toStdString());
|
|
||||||
auto loginEvent = new grpc::LoginEvent;
|
auto loginEvent = new grpc::LoginEvent;
|
||||||
loginEvent->set_allocated_twopasswordrequested(event);
|
loginEvent->set_allocated_twopasswordrequested(event);
|
||||||
return wrapLoginEvent(loginEvent);
|
return wrapLoginEvent(loginEvent);
|
||||||
|
|||||||