Compare commits

...

22 Commits

Author SHA1 Message Date
ed5adb18fb chore: Bastei Bridge 3.12.0 changelog. 2024-06-17 11:19:49 +02:00
85a91c5572 feat(BRIDGE-97): added repair button telemetry 2024-06-14 13:01:07 +00:00
56d4bfbb71 feat(BRIDGE-79): update to the KB suggestion list. 2024-06-13 10:05:23 +02:00
48a75b0dd7 chore: Bastei Bridge 3.12.0 changelog. 2024-06-06 10:10:36 +02:00
8688277ee6 ci: supress govulncheck vulns 2024-06-05 12:36:43 +00:00
63eb67760e fix(BRIDGE-90): disable repair button when bridge cannot connect to proton servers; bump GPA 2024-06-05 12:36:43 +00:00
cffab028b2 chore: cherry picked changelog from 3.11 release branch.
chore: Alcantara Bridge 3.11.0 changelog.

(cherry picked from commit 2569e83e51)

chore: Alcantara Bridge 3.11.0 changelog.

(cherry picked from commit b574ccb6ea)

chore: Alcantara Bridge 3.11.0 changelog.

(cherry picked from commit 82607efe1c)

chore: Alcantara Bridge 3.11.1 changelog.

(cherry picked from commit cd8db6fd1c)
2024-06-04 14:01:16 +00:00
8ea712b052 fix(BRIDGE-15): Apple Mail profile install page was not properly reset before showing.
(cherry picked from commit 961dc9435f)
2024-06-04 11:11:02 +02:00
ff0615167b feat(BRIDGE-75): Bridge repair button/feature implemented 2024-06-03 12:37:23 +00:00
e2b361b9a6 feat(BRIDGE-79): Add New Outlook for Mac KB disclaimer.
Update submitted by Laze Dimitrovski
2024-05-30 17:49:22 +02:00
1c6bbf1fae chore: enable GO-2024-2687 in govulncheck 2024-05-29 16:48:02 +02:00
e7713fa785 fix(BRIDGE-69): explicitly handle semver panic for last bridge version from vault 2024-05-22 10:54:38 +00:00
28ae54b5ca feat(BRIDGE-16): bump version Go 1.21.9 Qt 6.4.3. 2024-05-21 08:51:28 +02:00
00aff40160 fix(BRIDGE-70): hotfix for blocked smtp/imap port causing bridge to quit 2024-05-17 12:35:07 +02:00
ab289e6e01 fix(BRIDGE-29): bump gluon version 2024-05-14 15:41:10 +02:00
a28dc9f2f3 fix(BRIDGE-49): Configure gitleaks baseline and grype config 2024-05-02 10:59:43 +00:00
8a859082cd ci: added gitleaks and grype 2024-04-29 13:58:48 +02:00
1d972835ff fix(BRIDGE-21): missing panic handling 2024-04-26 13:24:02 +02:00
8469e0a661 fix(BRIDGE-17): broken telemetry heartbeat test 2024-04-25 11:16:13 +00:00
6ea970bf97 feat(BRIDGE-23): update gluon to go 1.21. 2024-04-23 14:52:31 +02:00
a05b90e803 feat(BRIDGE-22): update gpa to go 1.21. 2024-04-23 14:50:46 +02:00
239ad8b946 fix(BRIDGE-10): bumped gluon version 2024-04-23 12:42:08 +02:00
42 changed files with 1858 additions and 1086 deletions

View File

@ -25,10 +25,14 @@ variables:
GOMAXPROCS: $(( ${CI_TAG_CPU} / 2 )) GOMAXPROCS: $(( ${CI_TAG_CPU} / 2 ))
before_script: before_script:
- apt update && apt-get -y install libsecret-1-dev - |
- git config --global url.https://gitlab-ci-token:${CI_JOB_TOKEN}@${CI_SERVER_HOST}.insteadOf https://${CI_SERVER_HOST} if [ "$CI_JOB_NAME" != "grype-scan-code-dependencies" ]; then
apt update && apt-get -y install libsecret-1-dev
git config --global url.https://gitlab-ci-token:${CI_JOB_TOKEN}@${CI_SERVER_HOST}.insteadOf https://${CI_SERVER_HOST}
fi
stages: stages:
- analyse
- test - test
- build - build
@ -38,4 +42,11 @@ include:
- local: ci/env.yml - local: ci/env.yml
- local: ci/test.yml - local: ci/test.yml
- local: ci/build.yml - local: ci/build.yml
- component: gitlab.protontech.ch/proton/devops/cicd-components/devsecops/gitleaks/scan-repository@~latest
inputs:
stage: analyse
cli-args: "--baseline-path $GITLEAKS_BASELINE"
- component: gitlab.protontech.ch/proton/devops/cicd-components/devsecops/grype/scan-code@~latest
inputs:
stage: analyse

2
.grype.yaml Normal file
View File

@ -0,0 +1,2 @@
# Check out for configuration details: https://github.com/anchore/grype?tab=readme-ov-file#configuration
fail-on-severity: "medium"

View File

@ -3,7 +3,7 @@
## Prerequisites ## Prerequisites
* 64-bit OS: * 64-bit OS:
- the go-rfc5322 module cannot currently be compiled for 32-bit OSes - the go-rfc5322 module cannot currently be compiled for 32-bit OSes
* Go 1.21.6 * Go 1.21.9
* Bash with basic build utils: make, gcc, sed, find, grep, ... * Bash with basic build utils: make, gcc, sed, find, grep, ...
- For Windows, it is recommended to use MinGW 64bit shell from [MSYS2](https://www.msys2.org/) - For Windows, it is recommended to use MinGW 64bit shell from [MSYS2](https://www.msys2.org/)
* GCC (Linux), msvc (Windows) or Xcode (macOS) * GCC (Linux), msvc (Windows) or Xcode (macOS)

View File

@ -3,6 +3,52 @@
Changelog [format](http://keepachangelog.com/en/1.0.0/) Changelog [format](http://keepachangelog.com/en/1.0.0/)
## Bastei Bridge 3.12.0
### Added
* BRIDGE-75: Bridge repair button.
* BRIDGE-79: Add New Outlook for Mac KB disclaimer.
### Changed
* BRIDGE-16: Bump version Go 1.21.9 Qt 6.4.3.
* BRIDGE-23: Update gluon to go 1.21.
* BRIDGE-22: Update gpa to go 1.21.
### Fixed
* BRIDGE-90: Disable repair button when bridge cannot connect to proton servers; bump GPA.
* BRIDGE-69: Explicitly handle semver panic for last bridge version from vault.
* BRIDGE-29: Bump gluon version.
* BRIDGE-49: Configure gitleaks baseline and grype config.
* BRIDGE-21: Missing panic handling.
* BRIDGE-17: Broken telemetry heartbeat test.
* BRIDGE-10: Bumped gluon version.
## Alcantara Bridge 3.11.1
### Fixed
* BRIDGE-70: Hotfix for blocked smtp/imap port causing bridge to quit.
## Alcantara Bridge 3.11.0
### Added
* GODT-3185: Report cases which leads to wrong address key used.
### Changed
* BRIDGE-14: HV3 implementation.
* BRIDGE-15: Certificate install is now also done during Outlook setup on macOS.
* GODT-3146: Start servers on startup, keep running even when no users are active.
* BRIDGE-19: Update checksum validation use warning instead of error on non-existing files.
### Fixed
* BRIDGE-8: Fix bridge double sessionID issue in logs.
* BRIDGE-7: Modify keychain test on macOS.
* BRIDGE-4: Logs not being created when invalid flag is passed.
* BRIDGE-5: Add tooltip to tray icon.
* GODT-3163: Filter MBOX format delimiter.
## Zaehringen Bridge 3.10.0 ## Zaehringen Bridge 3.10.0
### Added ### Added

View File

@ -12,7 +12,7 @@ ROOT_DIR:=$(realpath .)
.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.10.0+git BRIDGE_APP_VERSION?=3.12.0+git
APP_VERSION:=${BRIDGE_APP_VERSION} APP_VERSION:=${BRIDGE_APP_VERSION}
APP_FULL_NAME:=Proton Mail Bridge APP_FULL_NAME:=Proton Mail Bridge
APP_VENDOR:=Proton AG APP_VENDOR:=Proton AG

18
go.mod
View File

@ -2,12 +2,14 @@ module github.com/ProtonMail/proton-bridge/v3
go 1.21 go 1.21
toolchain go1.21.9
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.20240227105633-3734c7694bcd github.com/ProtonMail/gluon v0.17.1-0.20240514133734-79cdd0fec41c
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.20240405124415-8f966ca60436 github.com/ProtonMail/go-proton-api v0.4.1-0.20240605113119-1a81ec7dc72d
github.com/ProtonMail/gopenpgp/v2 v2.7.4-proton github.com/ProtonMail/gopenpgp/v2 v2.7.4-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
@ -44,11 +46,11 @@ 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.24.0
golang.org/x/sys v0.16.0 golang.org/x/sys v0.19.0
golang.org/x/text v0.14.0 golang.org/x/text v0.14.0
google.golang.org/grpc v1.56.3 google.golang.org/grpc v1.56.3
google.golang.org/protobuf v1.31.0 google.golang.org/protobuf v1.33.0
howett.net/plist v1.0.0 howett.net/plist v1.0.0
) )
@ -62,7 +64,7 @@ require (
github.com/bytedance/sonic v1.9.1 // indirect github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/chzyer/test v1.0.0 // indirect github.com/chzyer/test v1.0.0 // indirect
github.com/cloudflare/circl v1.3.3 // indirect github.com/cloudflare/circl v1.3.7 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/cronokirby/saferith v0.33.0 // indirect github.com/cronokirby/saferith v0.33.0 // indirect
github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect
@ -93,7 +95,7 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mattn/go-sqlite3 v1.14.17 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect
@ -111,7 +113,7 @@ require (
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 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.18.0 // indirect golang.org/x/crypto v0.22.0 // indirect
golang.org/x/mod v0.8.0 // indirect golang.org/x/mod v0.8.0 // indirect
golang.org/x/sync v0.3.0 // indirect golang.org/x/sync v0.3.0 // indirect
golang.org/x/tools v0.6.0 // indirect golang.org/x/tools v0.6.0 // indirect

36
go.sum
View File

@ -27,8 +27,10 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
github.com/ProtonMail/bcrypt v0.0.0-20210511135022-227b4adcab57/go.mod h1:HecWFHognK8GfRDGnFQbW/LiV7A3MX3gZVs45vk5h8I= github.com/ProtonMail/bcrypt v0.0.0-20210511135022-227b4adcab57/go.mod h1:HecWFHognK8GfRDGnFQbW/LiV7A3MX3gZVs45vk5h8I=
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf h1:yc9daCCYUefEs69zUkSzubzjBbL+cmOXgnmt9Fyd9ug= github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf h1:yc9daCCYUefEs69zUkSzubzjBbL+cmOXgnmt9Fyd9ug=
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/gluon v0.17.1-0.20240227105633-3734c7694bcd h1:AjJsf5xQGmZPg6GLn+wB+eBoGRopJlG70lQBfSyfX+M= github.com/ProtonMail/gluon v0.17.1-0.20240423123310-0266b0f75d41 h1:Lu2hKO4fcHeMcbZOon129iM1dAy0ERwZkJtuNQCLlOQ=
github.com/ProtonMail/gluon v0.17.1-0.20240227105633-3734c7694bcd/go.mod h1:Og5/Dz1MiGpCJn51XujZwxiLG7WzvvjE5PRpZBQmAHo= github.com/ProtonMail/gluon v0.17.1-0.20240423123310-0266b0f75d41/go.mod h1:0/c03TzZPNiSgY5UDJK1iRDkjlDPwWugxTT6et2qDu8=
github.com/ProtonMail/gluon v0.17.1-0.20240514133734-79cdd0fec41c h1:P3SvCACt13Zqdj0IRDB4bgwqI68+oMB2j0uVuPQyoTw=
github.com/ProtonMail/gluon v0.17.1-0.20240514133734-79cdd0fec41c/go.mod h1:0/c03TzZPNiSgY5UDJK1iRDkjlDPwWugxTT6et2qDu8=
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=
@ -38,10 +40,10 @@ 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.20240226161523-ec58ed7ea4b9 h1:tcQpGQljNsZmfuA6L4hAzio8/AIx5OXcU2JUdyX/qxw= github.com/ProtonMail/go-proton-api v0.4.1-0.20240423123404-a6163268401c h1:3U245DPGyL+LeAcJzFSg+E2lShXx+z/lBHM2v9P5mEg=
github.com/ProtonMail/go-proton-api v0.4.1-0.20240226161523-ec58ed7ea4b9/go.mod h1:t+hb0BfkmZ9fpvzVRpHC7limoowym6ln/j0XL9a8DDw= github.com/ProtonMail/go-proton-api v0.4.1-0.20240423123404-a6163268401c/go.mod h1:3A0cpdo0BIenIPjTG6u8EbzJ8uuJy7rVvM/NaynjCKA=
github.com/ProtonMail/go-proton-api v0.4.1-0.20240405124415-8f966ca60436 h1:ej+W9+UQlb2owkT5arCegmUFkicwesMyFHgBp/wwNg8= github.com/ProtonMail/go-proton-api v0.4.1-0.20240605113119-1a81ec7dc72d h1:B9/ZLubPWIY4uvATviFoCUoLauq98C3Bbt4v0A2VEdU=
github.com/ProtonMail/go-proton-api v0.4.1-0.20240405124415-8f966ca60436/go.mod h1:t+hb0BfkmZ9fpvzVRpHC7limoowym6ln/j0XL9a8DDw= github.com/ProtonMail/go-proton-api v0.4.1-0.20240605113119-1a81ec7dc72d/go.mod h1:3A0cpdo0BIenIPjTG6u8EbzJ8uuJy7rVvM/NaynjCKA=
github.com/ProtonMail/go-smtp v0.0.0-20231109081432-2b3d50599865 h1:EP1gnxLL5Z7xBSymE9nSTM27nRYINuvssAtDmG0suD8= github.com/ProtonMail/go-smtp v0.0.0-20231109081432-2b3d50599865 h1:EP1gnxLL5Z7xBSymE9nSTM27nRYINuvssAtDmG0suD8=
github.com/ProtonMail/go-smtp v0.0.0-20231109081432-2b3d50599865/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= github.com/ProtonMail/go-smtp v0.0.0-20231109081432-2b3d50599865/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
github.com/ProtonMail/go-srp v0.0.7 h1:Sos3Qk+th4tQR64vsxGIxYpN3rdnG9Wf9K4ZloC1JrI= github.com/ProtonMail/go-srp v0.0.7 h1:Sos3Qk+th4tQR64vsxGIxYpN3rdnG9Wf9K4ZloC1JrI=
@ -89,8 +91,9 @@ github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@ -314,8 +317,8 @@ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
@ -467,8 +470,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
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/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
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=
@ -522,8 +525,9 @@ 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/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/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
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=
@ -577,8 +581,8 @@ 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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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=
@ -662,8 +666,8 @@ google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
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.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -539,6 +539,49 @@ func (bridge *Bridge) onStatusDown(ctx context.Context) {
} }
} }
func (bridge *Bridge) Repair() {
var wg sync.WaitGroup
userIDS := bridge.GetUserIDs()
for _, userID := range userIDS {
logPkg.Info("Initiating repair for userID:", userID)
userInfo, err := bridge.GetUserInfo(userID)
if err != nil {
logPkg.WithError(err).Error("Failed getting user info for repair; ID:", userID)
continue
}
if userInfo.State != Connected {
logPkg.Info("User is not connected. Repair will be executed on following successful log in.", userID)
if err := bridge.vault.GetUser(userID, func(user *vault.User) {
if err := user.SetShouldSync(true); err != nil {
logPkg.WithError(err).Error("Failed setting vault should sync for user:", userID)
}
}); err != nil {
logPkg.WithError(err).Error("Unable to get user vault when scheduling repair:", userID)
}
continue
}
bridgeUser, ok := bridge.users[userID]
if !ok {
logPkg.Info("UserID does not exist in bridge user map", userID)
continue
}
wg.Add(1)
go func(userID string) {
defer wg.Done()
if err = bridgeUser.TriggerRepair(); err != nil {
logPkg.WithError(err).Error("Failed re-syncing IMAP for userID", userID)
}
}(userID)
}
wg.Wait()
}
func loadTLSConfig(vault *vault.Vault) (*tls.Config, error) { func loadTLSConfig(vault *vault.Vault) (*tls.Config, error) {
cert, err := tls.X509KeyPair(vault.GetBridgeTLSCert()) cert, err := tls.X509KeyPair(vault.GetBridgeTLSCert())
if err != nil { if err != nil {
@ -558,3 +601,7 @@ func min(a, b time.Duration) time.Duration {
return b return b
} }
func (bridge *Bridge) HasAPIConnection() bool {
return bridge.api.GetStatus() == proton.StatusUp
}

View File

@ -606,6 +606,8 @@ func (bridge *Bridge) addUserWithVault(
// As we need at least one user to send heartbeat, try to send it. // As we need at least one user to send heartbeat, try to send it.
bridge.heartbeat.start() bridge.heartbeat.start()
user.PublishEvent(ctx, events.UserLoadedCheckResync{UserID: user.ID()})
return nil return nil
} }

View File

@ -36,6 +36,9 @@ func (bridge *Bridge) handleUserEvent(ctx context.Context, user *user.User, even
case events.UserBadEvent: case events.UserBadEvent:
bridge.handleUserBadEvent(ctx, user, event) bridge.handleUserBadEvent(ctx, user, event)
case events.UserLoadedCheckResync:
user.VerifyResyncAndExecute()
case events.UncategorizedEventError: case events.UncategorizedEventError:
bridge.handleUncategorizedErrorEvent(event) bridge.handleUncategorizedErrorEvent(event)
} }

View File

@ -64,7 +64,8 @@ func TestTLSPinInvalid(t *testing.T) {
checkTLSIssueHandler(t, 1, called) checkTLSIssueHandler(t, 1, called)
} }
func TestTLSPinNoMatch(t *testing.T) { // Disabled for now we'll need to patch this up.
func _TestTLSPinNoMatch(t *testing.T) { //nolint:unused
skipIfProxyIsSet(t) skipIfProxyIsSet(t)
called, _, reporter, checker, cm := createClientWithPinningDialer(getRootURL()) called, _, reporter, checker, cm := createClientWithPinningDialer(getRootURL())

View File

@ -202,3 +202,13 @@ type UncategorizedEventError struct {
func (event UncategorizedEventError) String() string { func (event UncategorizedEventError) String() string {
return fmt.Sprintf("UncategorizedEventError: UserID: %s, Source:%T, Error: %s", event.UserID, event.Error, event.Error) return fmt.Sprintf("UncategorizedEventError: UserID: %s, Source:%T, Error: %s", event.UserID, event.Error, event.Error)
} }
type UserLoadedCheckResync struct {
eventBase
UserID string
}
func (event UserLoadedCheckResync) String() string {
return fmt.Sprintf("UserLoadedCheckResync: UserID: %s", event.UserID)
}

View File

@ -1328,6 +1328,8 @@ void QMLBackend::connectGrpcEvents() {
connect(client, &GRPCClient::certificateInstallFailed, this, &QMLBackend::certificateInstallFailed); 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"); });
connect(client, &GRPCClient::knowledgeBasSuggestionsReceived, this, &QMLBackend::receivedKnowledgeBaseSuggestions); connect(client, &GRPCClient::knowledgeBasSuggestionsReceived, this, &QMLBackend::receivedKnowledgeBaseSuggestions);
connect(client, &GRPCClient::repairStarted, this, &QMLBackend::repairStarted);
connect(client, &GRPCClient::allUsersLoaded, this, &QMLBackend::allUsersLoaded);
// cache events // cache events
connect(client, &GRPCClient::cantMoveDiskCache, this, &QMLBackend::cantMoveDiskCache); connect(client, &GRPCClient::cantMoveDiskCache, this, &QMLBackend::cantMoveDiskCache);
@ -1410,3 +1412,9 @@ void QMLBackend::displayBadEventDialog(QString const &userID) {
emit showMainWindow(); emit showMainWindow();
) )
} }
void QMLBackend::triggerRepair() const {
HANDLE_EXCEPTION(
app().grpc().triggerRepair();
)
}

View File

@ -208,6 +208,7 @@ public slots: // slot for signals received from QML -> To be forwarded to Bridge
void notifyReportBugClicked() const; ///< Slot for the ReportBugClicked gRPC event. void notifyReportBugClicked() const; ///< Slot for the ReportBugClicked gRPC event.
void notifyAutoconfigClicked(QString const &client) const; ///< Slot for gAutoconfigClicked gRPC event. void notifyAutoconfigClicked(QString const &client) const; ///< Slot for gAutoconfigClicked gRPC event.
void notifyExternalLinkClicked(QString const &article) const; ///< Slot for KBArticleClicked gRPC event. void notifyExternalLinkClicked(QString const &article) const; ///< Slot for KBArticleClicked gRPC event.
void triggerRepair() const; ///< Slot for the triggering of the bridge repair function i.e. 'resync'.
public slots: // slots for functions that need to be processed locally. public slots: // slots for functions that need to be processed locally.
void setNormalTrayIcon(); ///< Set the tray icon to normal. void setNormalTrayIcon(); ///< Set the tray icon to normal.
@ -282,7 +283,9 @@ signals: // Signals received from the Go backend, to be forwarded to QML
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.
void receivedKnowledgeBaseSuggestions(QList<bridgepp::KnowledgeBaseSuggestion> const& suggestions); ///< Signal for the reception of knowledgebase article suggestions. void receivedKnowledgeBaseSuggestions(QList<bridgepp::KnowledgeBaseSuggestion> const& suggestions); ///< Signal for the reception of knowledge base article suggestions.
void repairStarted(); ///< Signal for the 'repairStarted' gRPC stream event.
void allUsersLoaded(); ///< Signal for the 'allUsersLoaded' gRPC stream event
// This signal is emitted when an exception is intercepted is calls triggered by QML. QML engine would intercept the exception otherwise. // This signal is emitted when an exception is intercepted is calls triggered by QML. QML engine would intercept the exception otherwise.
void fatalError(bridgepp::Exception const& e) const; ///< Signal emitted when an fatal error occurs. void fatalError(bridgepp::Exception const& e) const; ///< Signal emitted when an fatal error occurs.

View File

@ -21,6 +21,8 @@ SettingsView {
property bool _isAdvancedShown: false property bool _isAdvancedShown: false
property var notifications property var notifications
property var allUsersLoaded: false
property var hasInternetConnection: true
fillHeight: false fillHeight: false
@ -219,6 +221,37 @@ SettingsView {
Backend.exportTLSCertificates(); Backend.exportTLSCertificates();
} }
} }
SettingsItem {
id: repair
Layout.fillWidth: true
actionText: qsTr("Repair")
colorScheme: root.colorScheme
description: qsTr("Reload all accounts, cached data, and download all emails again. Email clients stay connected to Bridge.")
text: qsTr("Repair Bridge")
type: SettingsItem.Button
visible: root._isAdvancedShown
enabled: root.allUsersLoaded && Backend.users.count && root.hasInternetConnection
onClicked: {
root.notifications.askRepairBridge();
}
Connections {
function onInternetOff() {
root.hasInternetConnection = false;
repair.description = qsTr("This feature requires internet access to the Proton servers.")
}
function onInternetOn() {
root.hasInternetConnection = true;
repair.description = qsTr("Reload all accounts, cached data, and download all emails again. Email clients stay connected to Bridge.")
}
function onAllUsersLoaded() {
root.allUsersLoaded = true;
}
target: Backend
}
}
SettingsItem { SettingsItem {
id: reset id: reset
Layout.fillWidth: true Layout.fillWidth: true

View File

@ -105,4 +105,8 @@ Item {
colorScheme: root.colorScheme colorScheme: root.colorScheme
notification: root.notifications.genericQuestion notification: root.notifications.genericQuestion
} }
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.repairBridge
}
} }

View File

@ -13,6 +13,8 @@
import QtQml import QtQml
import Qt.labs.platform import Qt.labs.platform
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts
import QtQuick
import "../" import "../"
QtObject { QtObject {
@ -60,7 +62,7 @@ QtObject {
target: Backend target: Backend
} }
} }
property var all: [root.noInternet, root.imapPortStartupError, root.smtpPortStartupError, root.imapPortChangeError, root.smtpPortChangeError, root.imapConnectionModeChangeError, root.smtpConnectionModeChangeError, root.updateManualReady, root.updateManualRestartNeeded, root.updateManualError, root.updateForce, root.updateForceError, root.updateSilentRestartNeeded, root.updateSilentError, root.updateIsLatestVersion, root.loginConnectionError, root.onlyPaidUsers, root.alreadyLoggedIn, root.enableBeta, root.bugReportSendSuccess, root.bugReportSendError, root.bugReportSendFallback, root.cacheCantMove, root.cacheLocationChangeSuccess, root.enableSplitMode, root.resetBridge, root.changeAllMailVisibility, root.deleteAccount, root.noKeychain, root.rebuildKeychain, root.addressChanged, root.apiCertIssue, root.userBadEvent, root.imapLoginWhileSignedOut, root.genericError, root.genericQuestion, root.hvErrorEvent] property var all: [root.noInternet, root.imapPortStartupError, root.smtpPortStartupError, root.imapPortChangeError, root.smtpPortChangeError, root.imapConnectionModeChangeError, root.smtpConnectionModeChangeError, root.updateManualReady, root.updateManualRestartNeeded, root.updateManualError, root.updateForce, root.updateForceError, root.updateSilentRestartNeeded, root.updateSilentError, root.updateIsLatestVersion, root.loginConnectionError, root.onlyPaidUsers, root.alreadyLoggedIn, root.enableBeta, root.bugReportSendSuccess, root.bugReportSendError, root.bugReportSendFallback, root.cacheCantMove, root.cacheLocationChangeSuccess, root.enableSplitMode, root.resetBridge, root.changeAllMailVisibility, root.deleteAccount, root.noKeychain, root.rebuildKeychain, root.addressChanged, root.apiCertIssue, root.userBadEvent, root.imapLoginWhileSignedOut, root.genericError, root.genericQuestion, root.hvErrorEvent, root.repairBridge]
property Notification alreadyLoggedIn: Notification { property Notification alreadyLoggedIn: Notification {
brief: qsTr("Already signed in") brief: qsTr("Already signed in")
description: qsTr("This account is already signed in.") description: qsTr("This account is already signed in.")
@ -1150,6 +1152,52 @@ QtObject {
target: Backend target: Backend
} }
}
property Notification repairBridge: Notification {
brief: title
description: qsTr("This action will reload all accounts, cached data, and re-download emails. Messages may temporarily disappear but will reappear progressively. Email clients stay connected to Bridge.")
group: Notifications.Group.Configuration | Notifications.Group.Dialogs
icon: "./icons/ic-exclamation-circle-filled.svg"
title: qsTr("Repair Bridge?")
type: Notification.NotificationType.Danger
action: [
Action {
id: repairBridge_cancel
text: qsTr("Cancel")
onTriggered: {
root.repairBridge.active = false;
}
},
Action {
id: repairBridge_repair
text: qsTr("Repair")
onTriggered: {
repairBridge_repair.loading = true;
repairBridge_repair.enabled = false;
repairBridge_cancel.enabled = false;
Backend.triggerRepair();
}
}
]
Connections {
function onAskRepairBridge() {
root.repairBridge.active = true;
}
target: root
}
Connections {
function onRepairStarted() {
root.repairBridge.active = false;
repairBridge_repair.loading = false;
repairBridge_repair.enabled = true;
repairBridge_cancel.enabled = true;
}
target: Backend
}
} }
signal askChangeAllMailVisibility(var isVisibleNow) signal askChangeAllMailVisibility(var isVisibleNow)
@ -1158,4 +1206,5 @@ QtObject {
signal askEnableSplitMode(var user) signal askEnableSplitMode(var user)
signal askQuestion(var title, var description, var option1, var option2, var action1, var action2) signal askQuestion(var title, var description, var option1, var option2, var action1, var action2)
signal askResetBridge signal askResetBridge
signal askRepairBridge
} }

View File

@ -96,6 +96,7 @@ Item {
function showAppleMailAutoConfig() { function showAppleMailAutoConfig() {
backAction = _showClientConfig; backAction = _showClientConfig;
rootStackLayout.currentIndex = SetupWizard.RootStack.TwoPanesView; rootStackLayout.currentIndex = SetupWizard.RootStack.TwoPanesView;
clientConfigAppleMail.reset()
rightContent.currentIndex = SetupWizard.ContentStack.ClientConfigAppleMail; rightContent.currentIndex = SetupWizard.ContentStack.ClientConfigAppleMail;
leftContent.showAppleMailAutoconfigProfileInstall(); leftContent.showAppleMailAutoconfigProfileInstall();
} }

View File

@ -1198,6 +1198,14 @@ void GRPCClient::processAppEvent(AppEvent const &event) {
emit knowledgeBasSuggestionsReceived(suggestions); emit knowledgeBasSuggestionsReceived(suggestions);
break; break;
} }
case AppEvent::kRepairStarted:
this->logTrace("App event received: RepairStarted.");
emit repairStarted();
break;
case AppEvent::kAllUsersLoaded:
this->logTrace("App event received: AllUsersLoaded");
emit allUsersLoaded();
break;
default: default:
this->logError("Unknown App event received."); this->logError("Unknown App event received.");
} }
@ -1580,5 +1588,12 @@ grpc::Status GRPCClient::externalLinkClicked(QString const &link) {
return this->logGRPCCallStatus(stub_->ExternalLinkClicked(this->clientContext().get(), s, &empty), __FUNCTION__); return this->logGRPCCallStatus(stub_->ExternalLinkClicked(this->clientContext().get(), s, &empty), __FUNCTION__);
} }
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
grpc::Status GRPCClient::triggerRepair() {
return this->logGRPCCallStatus(stub_->TriggerRepair(this->clientContext().get(), empty, &empty), __FUNCTION__ );
}
} // namespace bridgepp } // namespace bridgepp

View File

@ -108,6 +108,7 @@ public: // member functions.
grpc::Status landingPageLink(QUrl &outUrl); ///< Performs the 'landingPageLink' call. grpc::Status landingPageLink(QUrl &outUrl); ///< Performs the 'landingPageLink' call.
grpc::Status hostname(QString &outHostname); ///< Performs the 'Hostname' call. grpc::Status hostname(QString &outHostname); ///< Performs the 'Hostname' call.
grpc::Status requestKnowledgeBaseSuggestions(QString const &input); ///< Performs the 'RequestKnowledgeBaseSuggestions' call. grpc::Status requestKnowledgeBaseSuggestions(QString const &input); ///< Performs the 'RequestKnowledgeBaseSuggestions' call.
grpc::Status triggerRepair(); ///< Performs the triggerRepair gRPC call.
signals: // app related signals signals: // app related signals
void internetStatus(bool isOn); void internetStatus(bool isOn);
@ -122,6 +123,8 @@ signals: // app related signals
void certificateInstallFailed(); void certificateInstallFailed();
void showMainWindow(); void showMainWindow();
void knowledgeBasSuggestionsReceived(QList<KnowledgeBaseSuggestion> const& suggestions); void knowledgeBasSuggestionsReceived(QList<KnowledgeBaseSuggestion> const& suggestions);
void repairStarted();
void allUsersLoaded();
public: // cache related calls public: // cache related calls

View File

@ -306,6 +306,12 @@ func New(
Aliases: []string{"del", "rm", "remove"}, Aliases: []string{"del", "rm", "remove"},
Completer: fe.completeUsernames, Completer: fe.completeUsernames,
}) })
fe.AddCmd(&ishell.Cmd{
Name: "repair",
Help: "reload all accounts and cached data, re-download emails. Email clients remain connected. Logged out users will be repaired on next login. (aliases: rep)",
Func: fe.repair,
Aliases: []string{"rep"},
})
badEventCmd := &ishell.Cmd{ badEventCmd := &ishell.Cmd{
Name: "bad-event", Name: "bad-event",

View File

@ -359,3 +359,13 @@ func (f *frontendCLI) isFile(location string) bool {
return !stat.IsDir() return !stat.IsDir()
} }
func (f *frontendCLI) repair(_ *ishell.Context) {
if f.bridge.HasAPIConnection() {
if f.yesNoQuestion("Are you sure you want to initialize a repair, this may take a while") {
f.bridge.Repair()
}
} else {
f.Println("Bridge cannot connect to the Proton servers. A connection is required to utilize this feature.")
}
}

File diff suppressed because it is too large Load Diff

View File

@ -111,6 +111,9 @@ service Bridge {
// Server -> Client event stream // Server -> Client event stream
rpc RunEventStream(EventStreamRequest) returns (stream StreamEvent); // Keep streaming until StopEventStream is called. rpc RunEventStream(EventStreamRequest) returns (stream StreamEvent); // Keep streaming until StopEventStream is called.
rpc StopEventStream(google.protobuf.Empty) returns (google.protobuf.Empty); rpc StopEventStream(google.protobuf.Empty) returns (google.protobuf.Empty);
// Repair
rpc TriggerRepair(google.protobuf.Empty) returns (google.protobuf.Empty);
} }
//********************************************************************************************************************** //**********************************************************************************************************************
@ -272,6 +275,8 @@ message AppEvent {
CertificateInstallCanceledEvent certificateInstallCanceled = 10; CertificateInstallCanceledEvent certificateInstallCanceled = 10;
CertificateInstallFailedEvent certificateInstallFailed = 11; CertificateInstallFailedEvent certificateInstallFailed = 11;
KnowledgeBaseSuggestionsEvent knowledgeBaseSuggestions = 12; KnowledgeBaseSuggestionsEvent knowledgeBaseSuggestions = 12;
RepairStartedEvent repairStarted = 13;
AllUsersLoadedEvent allUsersLoaded = 14;
} }
} }
@ -289,6 +294,8 @@ message ReportBugFallbackEvent {}
message CertificateInstallSuccessEvent {} message CertificateInstallSuccessEvent {}
message CertificateInstallCanceledEvent {} message CertificateInstallCanceledEvent {}
message CertificateInstallFailedEvent {} message CertificateInstallFailedEvent {}
message RepairStartedEvent {}
message AllUsersLoadedEvent {}
message KnowledgeBaseSuggestion { message KnowledgeBaseSuggestion {
string url = 1; string url = 1;

View File

@ -101,6 +101,7 @@ const (
Bridge_ExportTLSCertificates_FullMethodName = "/grpc.Bridge/ExportTLSCertificates" Bridge_ExportTLSCertificates_FullMethodName = "/grpc.Bridge/ExportTLSCertificates"
Bridge_RunEventStream_FullMethodName = "/grpc.Bridge/RunEventStream" Bridge_RunEventStream_FullMethodName = "/grpc.Bridge/RunEventStream"
Bridge_StopEventStream_FullMethodName = "/grpc.Bridge/StopEventStream" Bridge_StopEventStream_FullMethodName = "/grpc.Bridge/StopEventStream"
Bridge_TriggerRepair_FullMethodName = "/grpc.Bridge/TriggerRepair"
) )
// BridgeClient is the client API for Bridge service. // BridgeClient is the client API for Bridge service.
@ -180,6 +181,8 @@ type BridgeClient interface {
// Server -> Client event stream // Server -> Client event stream
RunEventStream(ctx context.Context, in *EventStreamRequest, opts ...grpc.CallOption) (Bridge_RunEventStreamClient, error) RunEventStream(ctx context.Context, in *EventStreamRequest, opts ...grpc.CallOption) (Bridge_RunEventStreamClient, error)
StopEventStream(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) StopEventStream(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
// Repair
TriggerRepair(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
} }
type bridgeClient struct { type bridgeClient struct {
@ -780,6 +783,15 @@ func (c *bridgeClient) StopEventStream(ctx context.Context, in *emptypb.Empty, o
return out, nil return out, nil
} }
func (c *bridgeClient) TriggerRepair(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
err := c.cc.Invoke(ctx, Bridge_TriggerRepair_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// BridgeServer is the server API for Bridge service. // BridgeServer is the server API for Bridge service.
// All implementations must embed UnimplementedBridgeServer // All implementations must embed UnimplementedBridgeServer
// for forward compatibility // for forward compatibility
@ -857,6 +869,8 @@ type BridgeServer interface {
// Server -> Client event stream // Server -> Client event stream
RunEventStream(*EventStreamRequest, Bridge_RunEventStreamServer) error RunEventStream(*EventStreamRequest, Bridge_RunEventStreamServer) error
StopEventStream(context.Context, *emptypb.Empty) (*emptypb.Empty, error) StopEventStream(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
// Repair
TriggerRepair(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
mustEmbedUnimplementedBridgeServer() mustEmbedUnimplementedBridgeServer()
} }
@ -1053,6 +1067,9 @@ func (UnimplementedBridgeServer) RunEventStream(*EventStreamRequest, Bridge_RunE
func (UnimplementedBridgeServer) StopEventStream(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { func (UnimplementedBridgeServer) StopEventStream(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method StopEventStream not implemented") return nil, status.Errorf(codes.Unimplemented, "method StopEventStream not implemented")
} }
func (UnimplementedBridgeServer) TriggerRepair(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method TriggerRepair not implemented")
}
func (UnimplementedBridgeServer) mustEmbedUnimplementedBridgeServer() {} func (UnimplementedBridgeServer) mustEmbedUnimplementedBridgeServer() {}
// UnsafeBridgeServer may be embedded to opt out of forward compatibility for this service. // UnsafeBridgeServer may be embedded to opt out of forward compatibility for this service.
@ -2203,6 +2220,24 @@ func _Bridge_StopEventStream_Handler(srv interface{}, ctx context.Context, dec f
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Bridge_TriggerRepair_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(emptypb.Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(BridgeServer).TriggerRepair(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Bridge_TriggerRepair_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BridgeServer).TriggerRepair(ctx, req.(*emptypb.Empty))
}
return interceptor(ctx, in, info, handler)
}
// Bridge_ServiceDesc is the grpc.ServiceDesc for Bridge service. // Bridge_ServiceDesc is the grpc.ServiceDesc for Bridge service.
// It's only intended for direct use with grpc.RegisterService, // It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy) // and not to be introspected or modified (even as a copy)
@ -2458,6 +2493,10 @@ var Bridge_ServiceDesc = grpc.ServiceDesc{
MethodName: "StopEventStream", MethodName: "StopEventStream",
Handler: _Bridge_StopEventStream_Handler, Handler: _Bridge_StopEventStream_Handler,
}, },
{
MethodName: "TriggerRepair",
Handler: _Bridge_TriggerRepair_Handler,
},
}, },
Streams: []grpc.StreamDesc{ Streams: []grpc.StreamDesc{
{ {

View File

@ -241,6 +241,14 @@ func NewGenericErrorEvent(errorCode ErrorCode) *StreamEvent {
return genericErrorEvent(&GenericErrorEvent{Code: errorCode}) return genericErrorEvent(&GenericErrorEvent{Code: errorCode})
} }
func NewRepairStartedEvent() *StreamEvent {
return appEvent(&AppEvent{Event: &AppEvent_RepairStarted{RepairStarted: &RepairStartedEvent{}}})
}
func NewAllUsersLoadedEvent() *StreamEvent {
return appEvent(&AppEvent{Event: &AppEvent_AllUsersLoaded{AllUsersLoaded: &AllUsersLoadedEvent{}}})
}
// Event category factory functions. // Event category factory functions.
func appEvent(appEvent *AppEvent) *StreamEvent { func appEvent(appEvent *AppEvent) *StreamEvent {

View File

@ -401,6 +401,9 @@ func (s *Service) watchEvents() {
case events.TLSIssue: case events.TLSIssue:
_ = s.SendEvent(NewMailApiCertIssue()) _ = s.SendEvent(NewMailApiCertIssue())
case events.AllUsersLoaded:
_ = s.SendEvent(NewAllUsersLoadedEvent())
} }
} }
} }

View File

@ -30,6 +30,8 @@ import (
) )
func (s *Service) IsTLSCertificateInstalled(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) { func (s *Service) IsTLSCertificateInstalled(context.Context, *emptypb.Empty) (*wrapperspb.BoolValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Info("IsTLSCertificateInstalled") s.log.Info("IsTLSCertificateInstalled")
cert, _ := s.bridge.GetBridgeTLSCert() cert, _ := s.bridge.GetBridgeTLSCert()

View File

@ -45,6 +45,7 @@ import (
// CheckTokens implements the CheckToken gRPC service call. // CheckTokens implements the CheckToken gRPC service call.
func (s *Service) CheckTokens(_ context.Context, clientConfigPath *wrapperspb.StringValue) (*wrapperspb.StringValue, error) { func (s *Service) CheckTokens(_ context.Context, clientConfigPath *wrapperspb.StringValue) (*wrapperspb.StringValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("CheckTokens") s.log.Debug("CheckTokens")
path := clientConfigPath.Value path := clientConfigPath.Value
@ -63,6 +64,7 @@ func (s *Service) CheckTokens(_ context.Context, clientConfigPath *wrapperspb.St
} }
func (s *Service) AddLogEntry(_ context.Context, request *AddLogEntryRequest) (*emptypb.Empty, error) { func (s *Service) AddLogEntry(_ context.Context, request *AddLogEntryRequest) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
entry := s.log entry := s.log
if len(request.Package) > 0 { if len(request.Package) > 0 {
@ -91,6 +93,7 @@ func (s *Service) AddLogEntry(_ context.Context, request *AddLogEntryRequest) (*
// GuiReady implement the GuiReady gRPC service call. // GuiReady implement the GuiReady gRPC service call.
func (s *Service) GuiReady(_ context.Context, _ *emptypb.Empty) (*GuiReadyResponse, error) { func (s *Service) GuiReady(_ context.Context, _ *emptypb.Empty) (*GuiReadyResponse, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("GuiReady") s.log.Debug("GuiReady")
s.initializationDone.Do(s.initializing.Done) s.initializationDone.Do(s.initializing.Done)
@ -105,6 +108,7 @@ func (s *Service) GuiReady(_ context.Context, _ *emptypb.Empty) (*GuiReadyRespon
// Quit implement the Quit gRPC service call. // Quit implement the Quit gRPC service call.
func (s *Service) Quit(_ context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) { func (s *Service) Quit(_ context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("Quit") s.log.Debug("Quit")
return &emptypb.Empty{}, s.quit() return &emptypb.Empty{}, s.quit()
} }
@ -134,6 +138,7 @@ func (s *Service) quit() error {
// Restart implement the Restart gRPC service call. // Restart implement the Restart gRPC service call.
func (s *Service) Restart(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) { func (s *Service) Restart(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("Restart") s.log.Debug("Restart")
s.restarter.Set(true, false) s.restarter.Set(true, false)
@ -141,12 +146,14 @@ func (s *Service) Restart(ctx context.Context, empty *emptypb.Empty) (*emptypb.E
} }
func (s *Service) ShowOnStartup(_ context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) { func (s *Service) ShowOnStartup(_ context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("ShowOnStartup") s.log.Debug("ShowOnStartup")
return wrapperspb.Bool(s.showOnStartup), nil return wrapperspb.Bool(s.showOnStartup), nil
} }
func (s *Service) SetIsAutostartOn(_ context.Context, isOn *wrapperspb.BoolValue) (*emptypb.Empty, error) { func (s *Service) SetIsAutostartOn(_ context.Context, isOn *wrapperspb.BoolValue) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.log.WithField("show", isOn.Value).Debug("SetIsAutostartOn") s.log.WithField("show", isOn.Value).Debug("SetIsAutostartOn")
defer func() { _ = s.SendEvent(NewToggleAutostartFinishedEvent()) }() defer func() { _ = s.SendEvent(NewToggleAutostartFinishedEvent()) }()
@ -167,12 +174,14 @@ func (s *Service) SetIsAutostartOn(_ context.Context, isOn *wrapperspb.BoolValue
} }
func (s *Service) IsAutostartOn(_ context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) { func (s *Service) IsAutostartOn(_ context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("IsAutostartOn") s.log.Debug("IsAutostartOn")
return wrapperspb.Bool(s.bridge.GetAutostart()), nil return wrapperspb.Bool(s.bridge.GetAutostart()), nil
} }
func (s *Service) SetIsBetaEnabled(_ context.Context, isEnabled *wrapperspb.BoolValue) (*emptypb.Empty, error) { func (s *Service) SetIsBetaEnabled(_ context.Context, isEnabled *wrapperspb.BoolValue) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.log.WithField("isEnabled", isEnabled.Value).Debug("SetIsBetaEnabled") s.log.WithField("isEnabled", isEnabled.Value).Debug("SetIsBetaEnabled")
channel := updater.StableChannel channel := updater.StableChannel
@ -189,12 +198,14 @@ func (s *Service) SetIsBetaEnabled(_ context.Context, isEnabled *wrapperspb.Bool
} }
func (s *Service) IsBetaEnabled(_ context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) { func (s *Service) IsBetaEnabled(_ context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("IsBetaEnabled") s.log.Debug("IsBetaEnabled")
return wrapperspb.Bool(s.bridge.GetUpdateChannel() == updater.EarlyChannel), nil return wrapperspb.Bool(s.bridge.GetUpdateChannel() == updater.EarlyChannel), nil
} }
func (s *Service) SetIsAllMailVisible(_ context.Context, isVisible *wrapperspb.BoolValue) (*emptypb.Empty, error) { func (s *Service) SetIsAllMailVisible(_ context.Context, isVisible *wrapperspb.BoolValue) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.log.WithField("isVisible", isVisible.Value).Debug("SetIsAllMailVisible") s.log.WithField("isVisible", isVisible.Value).Debug("SetIsAllMailVisible")
if err := s.bridge.SetShowAllMail(isVisible.Value); err != nil { if err := s.bridge.SetShowAllMail(isVisible.Value); err != nil {
@ -206,12 +217,14 @@ func (s *Service) SetIsAllMailVisible(_ context.Context, isVisible *wrapperspb.B
} }
func (s *Service) IsAllMailVisible(_ context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) { func (s *Service) IsAllMailVisible(_ context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("IsAllMailVisible") s.log.Debug("IsAllMailVisible")
return wrapperspb.Bool(s.bridge.GetShowAllMail()), nil return wrapperspb.Bool(s.bridge.GetShowAllMail()), nil
} }
func (s *Service) SetIsTelemetryDisabled(_ context.Context, isDisabled *wrapperspb.BoolValue) (*emptypb.Empty, error) { func (s *Service) SetIsTelemetryDisabled(_ context.Context, isDisabled *wrapperspb.BoolValue) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.log.WithField("isEnabled", isDisabled.Value).Debug("SetIsTelemetryDisabled") s.log.WithField("isEnabled", isDisabled.Value).Debug("SetIsTelemetryDisabled")
if err := s.bridge.SetTelemetryDisabled(isDisabled.Value); err != nil { if err := s.bridge.SetTelemetryDisabled(isDisabled.Value); err != nil {
@ -223,12 +236,14 @@ func (s *Service) SetIsTelemetryDisabled(_ context.Context, isDisabled *wrappers
} }
func (s *Service) IsTelemetryDisabled(_ context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) { func (s *Service) IsTelemetryDisabled(_ context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("IsTelemetryDisabled") s.log.Debug("IsTelemetryDisabled")
return wrapperspb.Bool(s.bridge.GetTelemetryDisabled()), nil return wrapperspb.Bool(s.bridge.GetTelemetryDisabled()), nil
} }
func (s *Service) GoOs(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) { func (s *Service) GoOs(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("GoOs") // TO-DO We can probably get rid of this and use QSysInfo::product name s.log.Debug("GoOs") // TO-DO We can probably get rid of this and use QSysInfo::product name
return wrapperspb.String(runtime.GOOS), nil return wrapperspb.String(runtime.GOOS), nil
@ -246,12 +261,14 @@ func (s *Service) TriggerReset(_ context.Context, _ *emptypb.Empty) (*emptypb.Em
} }
func (s *Service) Version(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) { func (s *Service) Version(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("Version") s.log.Debug("Version")
return wrapperspb.String(s.bridge.GetCurrentVersion().Original()), nil return wrapperspb.String(s.bridge.GetCurrentVersion().Original()), nil
} }
func (s *Service) LogsPath(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) { func (s *Service) LogsPath(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("LogsPath") s.log.Debug("LogsPath")
path, err := s.bridge.GetLogsPath() path, err := s.bridge.GetLogsPath()
@ -263,30 +280,40 @@ func (s *Service) LogsPath(_ context.Context, _ *emptypb.Empty) (*wrapperspb.Str
} }
func (s *Service) LicensePath(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) { func (s *Service) LicensePath(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("LicensePath") s.log.Debug("LicensePath")
return wrapperspb.String(s.bridge.GetLicenseFilePath()), nil return wrapperspb.String(s.bridge.GetLicenseFilePath()), nil
} }
func (s *Service) DependencyLicensesLink(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) { func (s *Service) DependencyLicensesLink(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
defer async.HandlePanic(s.panicHandler)
return wrapperspb.String(s.bridge.GetDependencyLicensesLink()), nil return wrapperspb.String(s.bridge.GetDependencyLicensesLink()), nil
} }
func (s *Service) ReleaseNotesPageLink(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) { func (s *Service) ReleaseNotesPageLink(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
s.latestLock.RLock() s.latestLock.RLock()
defer s.latestLock.RUnlock() defer func() {
async.HandlePanic(s.panicHandler)
s.latestLock.RUnlock()
}()
return wrapperspb.String(s.latest.ReleaseNotesPage), nil return wrapperspb.String(s.latest.ReleaseNotesPage), nil
} }
func (s *Service) LandingPageLink(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) { func (s *Service) LandingPageLink(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
s.latestLock.RLock() s.latestLock.RLock()
defer s.latestLock.RUnlock() defer func() {
async.HandlePanic(s.panicHandler)
s.latestLock.RUnlock()
}()
return wrapperspb.String(s.latest.LandingPage), nil return wrapperspb.String(s.latest.LandingPage), nil
} }
func (s *Service) SetColorSchemeName(_ context.Context, name *wrapperspb.StringValue) (*emptypb.Empty, error) { func (s *Service) SetColorSchemeName(_ context.Context, name *wrapperspb.StringValue) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.log.WithField("ColorSchemeName", name.Value).Debug("SetColorSchemeName") s.log.WithField("ColorSchemeName", name.Value).Debug("SetColorSchemeName")
if !theme.IsAvailable(theme.Theme(name.Value)) { if !theme.IsAvailable(theme.Theme(name.Value)) {
@ -303,6 +330,8 @@ func (s *Service) SetColorSchemeName(_ context.Context, name *wrapperspb.StringV
} }
func (s *Service) ColorSchemeName(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) { func (s *Service) ColorSchemeName(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("ColorSchemeName") s.log.Debug("ColorSchemeName")
current := s.bridge.GetColorScheme() current := s.bridge.GetColorScheme()
@ -318,6 +347,8 @@ func (s *Service) ColorSchemeName(_ context.Context, _ *emptypb.Empty) (*wrapper
} }
func (s *Service) CurrentEmailClient(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) { func (s *Service) CurrentEmailClient(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("CurrentEmailClient") s.log.Debug("CurrentEmailClient")
return wrapperspb.String(s.bridge.GetCurrentUserAgent()), nil return wrapperspb.String(s.bridge.GetCurrentUserAgent()), nil
@ -361,6 +392,8 @@ func (s *Service) ReportBug(_ context.Context, report *ReportBugRequest) (*empty
} }
func (s *Service) ForceLauncher(_ context.Context, launcher *wrapperspb.StringValue) (*emptypb.Empty, error) { func (s *Service) ForceLauncher(_ context.Context, launcher *wrapperspb.StringValue) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.log.WithField("launcher", launcher.Value).Debug("ForceLauncher") s.log.WithField("launcher", launcher.Value).Debug("ForceLauncher")
s.restarter.Override(launcher.Value) s.restarter.Override(launcher.Value)
@ -369,6 +402,8 @@ func (s *Service) ForceLauncher(_ context.Context, launcher *wrapperspb.StringVa
} }
func (s *Service) SetMainExecutable(_ context.Context, exe *wrapperspb.StringValue) (*emptypb.Empty, error) { func (s *Service) SetMainExecutable(_ context.Context, exe *wrapperspb.StringValue) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.log.WithField("executable", exe.Value).Debug("SetMainExecutable") s.log.WithField("executable", exe.Value).Debug("SetMainExecutable")
s.restarter.AddFlags("--wait", exe.Value) s.restarter.AddFlags("--wait", exe.Value)
@ -590,6 +625,8 @@ func (s *Service) InstallUpdate(_ context.Context, _ *emptypb.Empty) (*emptypb.E
} }
func (s *Service) SetIsAutomaticUpdateOn(_ context.Context, isOn *wrapperspb.BoolValue) (*emptypb.Empty, error) { func (s *Service) SetIsAutomaticUpdateOn(_ context.Context, isOn *wrapperspb.BoolValue) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.log.WithField("isOn", isOn.Value).Debug("SetIsAutomaticUpdateOn") s.log.WithField("isOn", isOn.Value).Debug("SetIsAutomaticUpdateOn")
if currentlyOn := s.bridge.GetAutoUpdate(); currentlyOn == isOn.Value { if currentlyOn := s.bridge.GetAutoUpdate(); currentlyOn == isOn.Value {
@ -605,12 +642,16 @@ func (s *Service) SetIsAutomaticUpdateOn(_ context.Context, isOn *wrapperspb.Boo
} }
func (s *Service) IsAutomaticUpdateOn(_ context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) { func (s *Service) IsAutomaticUpdateOn(_ context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("IsAutomaticUpdateOn") s.log.Debug("IsAutomaticUpdateOn")
return wrapperspb.Bool(s.bridge.GetAutoUpdate()), nil return wrapperspb.Bool(s.bridge.GetAutoUpdate()), nil
} }
func (s *Service) DiskCachePath(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) { func (s *Service) DiskCachePath(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("DiskCachePath") s.log.Debug("DiskCachePath")
return wrapperspb.String(s.bridge.GetGluonCacheDir()), nil return wrapperspb.String(s.bridge.GetGluonCacheDir()), nil
@ -648,6 +689,8 @@ func (s *Service) SetDiskCachePath(_ context.Context, newPath *wrapperspb.String
} }
func (s *Service) SetIsDoHEnabled(_ context.Context, isEnabled *wrapperspb.BoolValue) (*emptypb.Empty, error) { func (s *Service) SetIsDoHEnabled(_ context.Context, isEnabled *wrapperspb.BoolValue) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.log.WithField("isEnabled", isEnabled.Value).Debug("SetIsDohEnabled") s.log.WithField("isEnabled", isEnabled.Value).Debug("SetIsDohEnabled")
if err := s.bridge.SetProxyAllowed(isEnabled.Value); err != nil { if err := s.bridge.SetProxyAllowed(isEnabled.Value); err != nil {
@ -659,12 +702,16 @@ func (s *Service) SetIsDoHEnabled(_ context.Context, isEnabled *wrapperspb.BoolV
} }
func (s *Service) IsDoHEnabled(_ context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) { func (s *Service) IsDoHEnabled(_ context.Context, _ *emptypb.Empty) (*wrapperspb.BoolValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("IsDohEnabled") s.log.Debug("IsDohEnabled")
return wrapperspb.Bool(s.bridge.GetProxyAllowed()), nil return wrapperspb.Bool(s.bridge.GetProxyAllowed()), nil
} }
func (s *Service) MailServerSettings(_ context.Context, _ *emptypb.Empty) (*ImapSmtpSettings, error) { func (s *Service) MailServerSettings(_ context.Context, _ *emptypb.Empty) (*ImapSmtpSettings, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("ConnectionMode") s.log.Debug("ConnectionMode")
return &ImapSmtpSettings{ return &ImapSmtpSettings{
@ -728,24 +775,32 @@ func (s *Service) SetMailServerSettings(_ context.Context, settings *ImapSmtpSet
} }
func (s *Service) Hostname(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) { func (s *Service) Hostname(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("Hostname") s.log.Debug("Hostname")
return wrapperspb.String(constants.Host), nil return wrapperspb.String(constants.Host), nil
} }
func (s *Service) IsPortFree(_ context.Context, port *wrapperspb.Int32Value) (*wrapperspb.BoolValue, error) { func (s *Service) IsPortFree(_ context.Context, port *wrapperspb.Int32Value) (*wrapperspb.BoolValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("IsPortFree") s.log.Debug("IsPortFree")
return wrapperspb.Bool(ports.IsPortFree(int(port.Value))), nil return wrapperspb.Bool(ports.IsPortFree(int(port.Value))), nil
} }
func (s *Service) AvailableKeychains(_ context.Context, _ *emptypb.Empty) (*AvailableKeychainsResponse, error) { func (s *Service) AvailableKeychains(_ context.Context, _ *emptypb.Empty) (*AvailableKeychainsResponse, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("AvailableKeychains") s.log.Debug("AvailableKeychains")
return &AvailableKeychainsResponse{Keychains: s.bridge.GetHelpersNames()}, nil return &AvailableKeychainsResponse{Keychains: s.bridge.GetHelpersNames()}, nil
} }
func (s *Service) SetCurrentKeychain(ctx context.Context, keychain *wrapperspb.StringValue) (*emptypb.Empty, error) { func (s *Service) SetCurrentKeychain(ctx context.Context, keychain *wrapperspb.StringValue) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.log.WithField("keychain", keychain.Value).Debug("SetCurrentKeyChain") // we do not check validity. s.log.WithField("keychain", keychain.Value).Debug("SetCurrentKeyChain") // we do not check validity.
defer func() { _, _ = s.Restart(ctx, &emptypb.Empty{}) }() defer func() { _, _ = s.Restart(ctx, &emptypb.Empty{}) }()
@ -770,6 +825,8 @@ func (s *Service) SetCurrentKeychain(ctx context.Context, keychain *wrapperspb.S
} }
func (s *Service) CurrentKeychain(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) { func (s *Service) CurrentKeychain(_ context.Context, _ *emptypb.Empty) (*wrapperspb.StringValue, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("CurrentKeychain") s.log.Debug("CurrentKeychain")
helper, err := s.bridge.GetKeychainApp() helper, err := s.bridge.GetKeychainApp()
@ -781,6 +838,20 @@ func (s *Service) CurrentKeychain(_ context.Context, _ *emptypb.Empty) (*wrapper
return wrapperspb.String(helper), nil return wrapperspb.String(helper), nil
} }
func (s *Service) TriggerRepair(_ context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) {
s.log.Debug("TriggerRepair")
go func() {
defer func() {
async.HandlePanic(s.panicHandler)
_ = s.SendEvent(NewRepairStartedEvent())
}()
s.bridge.Repair()
}()
return &emptypb.Empty{}, nil
}
func base64Decode(in []byte) ([]byte, error) { func base64Decode(in []byte) ([]byte, error) {
out := make([]byte, base64.StdEncoding.DecodedLen(len(in))) out := make([]byte, base64.StdEncoding.DecodedLen(len(in)))

View File

@ -20,21 +20,25 @@ package grpc
import ( import (
"context" "context"
"github.com/ProtonMail/gluon/async"
"google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/wrapperspb" "google.golang.org/protobuf/types/known/wrapperspb"
) )
func (s *Service) ReportBugClicked(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { func (s *Service) ReportBugClicked(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.bridge.ReportBugClicked() s.bridge.ReportBugClicked()
return &emptypb.Empty{}, nil return &emptypb.Empty{}, nil
} }
func (s *Service) AutoconfigClicked(_ context.Context, client *wrapperspb.StringValue) (*emptypb.Empty, error) { func (s *Service) AutoconfigClicked(_ context.Context, client *wrapperspb.StringValue) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.bridge.AutoconfigUsed(client.Value) s.bridge.AutoconfigUsed(client.Value)
return &emptypb.Empty{}, nil return &emptypb.Empty{}, nil
} }
func (s *Service) ExternalLinkClicked(_ context.Context, article *wrapperspb.StringValue) (*emptypb.Empty, error) { func (s *Service) ExternalLinkClicked(_ context.Context, article *wrapperspb.StringValue) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.bridge.ExternalLinkClicked(article.Value) s.bridge.ExternalLinkClicked(article.Value)
return &emptypb.Empty{}, nil return &emptypb.Empty{}, nil
} }

View File

@ -29,6 +29,7 @@ import (
) )
func (s *Service) GetUserList(_ context.Context, _ *emptypb.Empty) (*UserListResponse, error) { func (s *Service) GetUserList(_ context.Context, _ *emptypb.Empty) (*UserListResponse, error) {
defer async.HandlePanic(s.panicHandler)
s.log.Debug("GetUserList") s.log.Debug("GetUserList")
userIDs := s.bridge.GetUserIDs() userIDs := s.bridge.GetUserIDs()
@ -52,6 +53,7 @@ func (s *Service) GetUserList(_ context.Context, _ *emptypb.Empty) (*UserListRes
} }
func (s *Service) GetUser(_ context.Context, userID *wrapperspb.StringValue) (*User, error) { func (s *Service) GetUser(_ context.Context, userID *wrapperspb.StringValue) (*User, error) {
defer async.HandlePanic(s.panicHandler)
s.log.WithField("userID", userID).Debug("GetUser") s.log.WithField("userID", userID).Debug("GetUser")
user, err := s.bridge.GetUserInfo(userID.Value) user, err := s.bridge.GetUserInfo(userID.Value)
@ -63,6 +65,7 @@ func (s *Service) GetUser(_ context.Context, userID *wrapperspb.StringValue) (*U
} }
func (s *Service) SetUserSplitMode(_ context.Context, splitMode *UserSplitModeRequest) (*emptypb.Empty, error) { func (s *Service) SetUserSplitMode(_ context.Context, splitMode *UserSplitModeRequest) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.log.WithField("UserID", splitMode.UserID).WithField("Active", splitMode.Active).Debug("SetUserSplitMode") s.log.WithField("UserID", splitMode.UserID).WithField("Active", splitMode.Active).Debug("SetUserSplitMode")
user, err := s.bridge.GetUserInfo(splitMode.UserID) user, err := s.bridge.GetUserInfo(splitMode.UserID)
@ -97,6 +100,7 @@ func (s *Service) SetUserSplitMode(_ context.Context, splitMode *UserSplitModeRe
} }
func (s *Service) SendBadEventUserFeedback(_ context.Context, feedback *UserBadEventFeedbackRequest) (*emptypb.Empty, error) { func (s *Service) SendBadEventUserFeedback(_ context.Context, feedback *UserBadEventFeedbackRequest) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
l := s.log.WithField("UserID", feedback.UserID).WithField("doResync", feedback.DoResync) l := s.log.WithField("UserID", feedback.UserID).WithField("doResync", feedback.DoResync)
l.Debug("SendBadEventUserFeedback") l.Debug("SendBadEventUserFeedback")
@ -115,6 +119,7 @@ func (s *Service) SendBadEventUserFeedback(_ context.Context, feedback *UserBadE
} }
func (s *Service) LogoutUser(_ context.Context, userID *wrapperspb.StringValue) (*emptypb.Empty, error) { func (s *Service) LogoutUser(_ context.Context, userID *wrapperspb.StringValue) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.log.WithField("UserID", userID.Value).Debug("LogoutUser") s.log.WithField("UserID", userID.Value).Debug("LogoutUser")
if _, err := s.bridge.GetUserInfo(userID.Value); err != nil { if _, err := s.bridge.GetUserInfo(userID.Value); err != nil {
@ -133,6 +138,7 @@ func (s *Service) LogoutUser(_ context.Context, userID *wrapperspb.StringValue)
} }
func (s *Service) RemoveUser(_ context.Context, userID *wrapperspb.StringValue) (*emptypb.Empty, error) { func (s *Service) RemoveUser(_ context.Context, userID *wrapperspb.StringValue) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.log.WithField("UserID", userID.Value).Debug("RemoveUser") s.log.WithField("UserID", userID.Value).Debug("RemoveUser")
go func() { go func() {
@ -148,6 +154,7 @@ func (s *Service) RemoveUser(_ context.Context, userID *wrapperspb.StringValue)
} }
func (s *Service) ConfigureUserAppleMail(ctx context.Context, request *ConfigureAppleMailRequest) (*emptypb.Empty, error) { func (s *Service) ConfigureUserAppleMail(ctx context.Context, request *ConfigureAppleMailRequest) (*emptypb.Empty, error) {
defer async.HandlePanic(s.panicHandler)
s.log.WithField("UserID", request.UserID).WithField("Address", request.Address).Debug("ConfigureUserAppleMail") s.log.WithField("UserID", request.UserID).WithField("Address", request.Address).Debug("ConfigureUserAppleMail")
sslWasEnabled := s.bridge.GetSMTPSSL() sslWasEnabled := s.bridge.GetSMTPSSL()

View File

@ -262,7 +262,8 @@
"keywords": [ "keywords": [
"Outlook", "Outlook",
"setup", "setup",
"configuration" "configuration",
"We encountered an error while adding account"
] ]
}, },
{ {
@ -421,5 +422,38 @@
"Apple Mail", "Apple Mail",
"macOS" "macOS"
] ]
},
{
"index": 37,
"url": "https://proton.me/support/protonmail-bridge-clients-macos-new-outlook#may-17",
"title": "Important notice regarding the New Outlook for Mac and issues you might face",
"keywords": [
"Receiving",
"Sending",
"Outlook",
"Configuration",
"Sync",
"New Outlook"
]
},
{
"index": 38,
"url": "https://proton.me/support/proton-mail-bridge-new-outlook-for-windows-set-up-guide",
"title": "What is the Recovered Messages folder in Bridge (and your email client)?",
"keywords": [
"recovered messages",
"recovered messages folder"
]
},
{
"index": 39,
"url": "https://proton.me/support/proton-mail-bridge-new-outlook-for-windows-set-up-guide",
"title": "Proton Mail Bridge New Outlook for Windows set up guide",
"keywords": [
"app password",
"INVALIDCREDENTIALS",
"TEMPORARILYUNAVAILABLE",
"New Outlook"
]
} }
] ]

View File

@ -108,13 +108,13 @@ func (sm *Service) Init(ctx context.Context, group *async.Group, subscription ev
}) })
if err := sm.serveIMAP(ctx); err != nil { if err := sm.serveIMAP(ctx); err != nil {
sm.log.WithError(err).Error("Failed to start IMAP server") sm.log.WithError(err).Error("Failed to start IMAP server on bridge start")
return err sm.imapListener = nil
} }
if err := sm.serveSMTP(ctx); err != nil { if err := sm.serveSMTP(ctx); err != nil {
sm.log.WithError(err).Error("Failed to start SMTP server") sm.log.WithError(err).Error("Failed to start SMTP server on bridge start")
return err sm.smtpListener = nil
} }
return nil return nil

View File

@ -65,8 +65,10 @@ func TestHeartbeat_default_heartbeat(t *testing.T) {
func TestHeartbeat_already_sent_heartbeat(t *testing.T) { func TestHeartbeat_already_sent_heartbeat(t *testing.T) {
withHeartbeat(t, 1143, 1025, "/tmp", "defaultKeychain", func(hb *telemetry.Heartbeat, mock *mocks.MockHeartbeatManager) { withHeartbeat(t, 1143, 1025, "/tmp", "defaultKeychain", func(hb *telemetry.Heartbeat, mock *mocks.MockHeartbeatManager) {
mock.EXPECT().IsTelemetryAvailable(context.Background()).Return(true) mock.EXPECT().IsTelemetryAvailable(context.Background()).Return(true)
mock.EXPECT().GetLastHeartbeatSent().Return(time.Now().Truncate(24 * time.Hour)) mock.EXPECT().GetLastHeartbeatSent().DoAndReturn(func() time.Time {
curTime := time.Now()
return time.Date(curTime.Year(), curTime.Month(), curTime.Day(), 0, 0, 0, 0, curTime.Location())
})
hb.TrySending(context.Background()) hb.TrySending(context.Background())
}) })
} }

View File

@ -0,0 +1,43 @@
// Copyright (c) 2024 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Proton Mail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
package telemetry
type RepairData struct {
MeasurementGroup string
Event string
Values map[string]string
Dimensions map[string]string
}
func NewRepairTriggerData() RepairData {
return RepairData{
MeasurementGroup: "bridge.any.repair",
Event: "repair_trigger",
Values: map[string]string{},
Dimensions: map[string]string{},
}
}
func NewRepairDeferredTriggerData() RepairData {
return RepairData{
MeasurementGroup: "bridge.any.repair",
Event: "repair_deferred_trigger",
Values: map[string]string{},
Dimensions: map[string]string{},
}
}

View File

@ -0,0 +1,65 @@
// Copyright (c) 2024 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Proton Mail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
package user
import (
"context"
"encoding/json"
"github.com/ProtonMail/proton-bridge/v3/internal/telemetry"
)
func (user *User) SendRepairTrigger(ctx context.Context) {
if !user.IsTelemetryEnabled(ctx) {
return
}
triggerData := telemetry.NewRepairTriggerData()
data, err := json.Marshal(triggerData)
if err != nil {
user.log.WithError(err).Error("Failed to parse repair trigger data.")
return
}
if err := user.SendTelemetry(ctx, data); err != nil {
user.log.WithError(err).Error("Failed to send repair trigger event.")
return
}
user.log.Info("Repair trigger event successfully sent.")
}
func (user *User) SendRepairDeferredTrigger(ctx context.Context) {
if !user.IsTelemetryEnabled(ctx) {
return
}
deferredTriggerData := telemetry.NewRepairDeferredTriggerData()
data, err := json.Marshal(deferredTriggerData)
if err != nil {
user.log.WithError(err).Error("Failed to parse deferred repair trigger data.")
return
}
if err := user.SendTelemetry(ctx, data); err != nil {
user.log.WithError(err).Error("Failed to send deferred repair trigger event.")
return
}
user.log.Info("Deferred repair trigger event successfully sent.")
}

View File

@ -717,3 +717,28 @@ func (user *User) protonAddresses() []proton.Address {
return addresses return addresses
} }
func (user *User) VerifyResyncAndExecute() {
user.log.Info("Checking whether logged in user should re-sync. UserID:", user.ID())
if user.vault.GetShouldResync() {
user.log.Info("User should re-sync, starting re-sync process. UserID:", user.ID())
if err := user.vault.SetShouldSync(false); err != nil {
user.log.WithError(err).Error("Failed to disable re-sync flag in user vault. UserID:", user.ID())
}
user.SendRepairDeferredTrigger(context.Background())
if err := user.resyncIMAP(); err != nil {
user.log.WithError(err).Error("Failed re-syncing IMAP for userID", user.ID())
}
}
}
func (user *User) TriggerRepair() error {
user.SendRepairTrigger(context.Background())
return user.resyncIMAP()
}
func (user *User) resyncIMAP() error {
return user.imapService.Resync(context.Background())
}

View File

@ -18,11 +18,13 @@
package vault package vault
import ( import (
"fmt"
"math" "math"
"math/rand" "math/rand"
"time" "time"
"github.com/Masterminds/semver/v3" "github.com/Masterminds/semver/v3"
"github.com/ProtonMail/proton-bridge/v3/internal/constants"
"github.com/ProtonMail/proton-bridge/v3/internal/updater" "github.com/ProtonMail/proton-bridge/v3/internal/updater"
"github.com/ProtonMail/proton-bridge/v3/internal/useragent" "github.com/ProtonMail/proton-bridge/v3/internal/useragent"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -200,7 +202,14 @@ func (vault *Vault) SetTelemetryDisabled(telemetryDisabled bool) error {
// GetLastVersion returns the last version of the bridge that was run. // GetLastVersion returns the last version of the bridge that was run.
func (vault *Vault) GetLastVersion() *semver.Version { func (vault *Vault) GetLastVersion() *semver.Version {
return semver.MustParse(vault.getSafe().Settings.LastVersion) lastVersion := vault.getSafe().Settings.LastVersion
version, err := semver.NewVersion(lastVersion)
if err != nil {
logrus.WithError(err).Error(fmt.Sprintf("Error encountered when trying to get last version from vault: %s", lastVersion))
version, _ = semver.NewVersion(constants.Version)
}
return version
} }
// SetLastVersion sets the last version of the bridge that was run. // SetLastVersion sets the last version of the bridge that was run.

View File

@ -40,6 +40,8 @@ type UserData struct {
// **WARNING**: This value can't be removed until we have vault migration support. // **WARNING**: This value can't be removed until we have vault migration support.
UIDValidity map[string]imap.UID UIDValidity map[string]imap.UID
ShouldResync bool // Whether user should re-sync on log-in (this is triggered by the `repair` button)
} }
type AddressMode int type AddressMode int
@ -88,5 +90,7 @@ func newDefaultUser(userID, username, primaryEmail, authUID, authRef string, key
AuthUID: authUID, AuthUID: authUID,
AuthRef: authRef, AuthRef: authRef,
KeyPass: keyPass, KeyPass: keyPass,
ShouldResync: false,
} }
} }

View File

@ -232,3 +232,13 @@ func (user *User) Clear() error {
func (user *User) Close() error { func (user *User) Close() error {
return user.vault.detachUser(user.userID) return user.vault.detachUser(user.userID)
} }
func (user *User) SetShouldSync(shouldResync bool) error {
return user.vault.modUser(user.userID, func(data *UserData) {
data.ShouldResync = shouldResync
})
}
func (user *User) GetShouldResync() bool {
return user.vault.getUser(user.userID).ShouldResync
}

View File

@ -61,6 +61,7 @@ func TestUser_New(t *testing.T) {
// Check the user's initial sync status. // Check the user's initial sync status.
require.False(t, user.SyncStatus().HasLabels) require.False(t, user.SyncStatus().HasLabels)
require.False(t, user.SyncStatus().HasMessages) require.False(t, user.SyncStatus().HasMessages)
require.False(t, user.GetShouldResync())
} }
func TestUser_Clear(t *testing.T) { func TestUser_Clear(t *testing.T) {
@ -239,3 +240,34 @@ func TestUser_ForEach(t *testing.T) {
// The store should have no users again. // The store should have no users again.
require.Empty(t, s.GetUserIDs()) require.Empty(t, s.GetUserIDs())
} }
func TestUser_ShouldResync(t *testing.T) {
// Replace the token generator with a dummy one.
vault.RandomToken = func(size int) ([]byte, error) {
return []byte("token"), nil
}
// Create a new test vault.
s := newVault(t)
// There should be no users in the store.
require.Empty(t, s.GetUserIDs())
// Create a new user.
user, err := s.AddUser("userID", "username", "username@pm.me", "authUID", "authRef", []byte("keyPass"))
require.NoError(t, err)
// The user should be listed in the store.
require.ElementsMatch(t, []string{"userID"}, s.GetUserIDs())
// The shouldResync field is supposed to be false for new users.
require.False(t, user.GetShouldResync())
// Set it to true
if err := user.SetShouldSync(true); err != nil {
t.Fatalf("Failed to set should-sync: %v", err)
}
// Check whether it matches the correct value
require.True(t, user.GetShouldResync())
}

View File

@ -28,12 +28,8 @@ main(){
jq -r '.finding | select( (.osv != null) and (.trace[0].function != null) ) | .osv ' < vulns.json > vulns_osv_ids.txt jq -r '.finding | select( (.osv != null) and (.trace[0].function != null) ) | .osv ' < vulns.json > vulns_osv_ids.txt
ignore GO-2023-2328 "GODT-3124 RESTY race condition" ignore GO-2023-2328 "GODT-3124 RESTY race condition"
ignore GO-2024-2598 "BRIDGE-16 Update Go to 1.21.9" ignore GO-2024-2887 "BRIDGE-95 net/http vulnerability"
ignore GO-2024-2599 "BRIDGE-16 Update Go to 1.21.9" ignore GO-2024-2888 "BRIDGE-95 archive/zip vulnerability"
ignore GO-2024-2600 "BRIDGE-16 Update Go to 1.21.9"
ignore GO-2024-2609 "BRIDGE-16 Update Go to 1.21.9"
ignore GO-2024-2610 "BRIDGE-16 Update Go to 1.21.9"
ignore GO-2024-2687 "BRIDGE-16 Update Go to 1.21.9"
has_vulns has_vulns