Compare commits

...

100 Commits

Author SHA1 Message Date
4e6236611a chore: merge Helix to master 2025-02-27 10:36:11 +00:00
0800aeea50 chore: Helix Bridge 3.18.0 changelog. 2025-02-18 23:44:44 +01:00
f4ddf43ac7 chore: Grunwald Bridge 3.17.0 changelog. 2025-02-18 17:11:46 +01:00
da0f51ce5f feat(BRIDGE-309): Update to the bridge updater logic corresponding to the version file restructure 2025-02-17 15:43:15 +00:00
d711d9f562 feat(BRIDGE-154): include access token when refreshing 2025-02-17 15:10:05 +01:00
b230f2ece6 chore: merge XXX to master 2025-02-12 08:37:01 +00:00
d44c488ed5 chore: minor comment just so we have a new commit 2025-02-11 10:28:05 +01:00
fe39d23cf8 chore(BRIDGE-315): silence crypto/internal/nistec vuln 2025-02-10 12:53:07 +01:00
dbb84f2ae2 chore(BRIDGE-315): silence govulncheck vulns 2025-01-31 10:36:50 +01:00
8237129670 chore: merge Grunwald to master 2025-01-29 15:52:34 +00:00
8e634995c5 chore: Grunwald Bridge 3.17.0 changelog. 2025-01-21 14:42:52 +01:00
a2c1da9748 chore(BRIDGE-73): bumping GPA; includes changes from BRIDGE-93 and BRIDGE-73 (GPA); 2025-01-21 13:08:29 +01:00
b8cc71fdd8 chore(BRIDGE-287): bump x/net x/crypto dependencies; remove ignore statements from govulncheck 2025-01-20 16:56:25 +00:00
1949e89053 chore(BRIDGE-303): update govulncheck to use latest release 2025-01-20 16:09:32 +01:00
ae5469fc81 chore: remove export loop ref and loop-scope assignments (changed with go 1.22) 2025-01-20 14:54:34 +01:00
105ea4de0d feat(BRIDGE-226): bump version Go 1.23.4 Qt 6.4.3. 2025-01-20 11:12:35 +00:00
b21d126ab0 feat(BRIDGE-288): extended sync message update handler; observability tweaks; gluon bump; 2025-01-16 15:08:52 +01:00
7bc7a5e7b3 chore: downgraded govulncheck package 2025-01-16 14:43:44 +01:00
10a685a123 chore: Prepare for issue tracker removal 2025-01-14 10:48:03 +01:00
896f50c754 chore: FF devel into master 2025-01-14 10:35:25 +01:00
edbc7d0e3d chore: Flavien Bridge 3.16.0 changelog. 2025-01-09 14:49:17 +01:00
b7b1043b88 chore: Erasmus Bridge 3.15.1 changelog. 2025-01-09 14:48:38 +01:00
0641c63377 fix(BRIDGE-291): use correct field for user plan type 2025-01-08 10:21:10 +00:00
74a990c69a feat(BRIDGE-271): report to sentry when version file check fails 2025-01-08 08:59:29 +00:00
e340e9f845 fix(BRIDGE-143): added missing qml component attribute; cut/paste disabled on read only text areas 2025-01-08 08:29:21 +00:00
082849dc6c chore: year bump 2025-01-02 14:03:49 +01:00
6878b3b5e0 test(BRIDGE-247): Automate Bridge 0% update rollout 2024-12-30 16:01:10 +00:00
16245a372e test(BRIDGE-248): Additional Bridge UI e2e automation tests 2024-12-30 14:17:10 +00:00
28b0dbd051 chore: suppress vulnerability warnings in x/net package 2024-12-23 13:39:44 +01:00
60633fc09c chore: merge Flavien to master 2024-12-17 15:20:30 +00:00
9c5b5c2ac3 chore: FF devel into master 2024-12-16 12:22:45 +01:00
0e6df4ce73 chore: Prepare for issue tracker removal 2024-12-16 10:47:24 +00:00
a4772ee4e0 chore: added CODEOWNER group 2024-12-12 10:46:17 +01:00
ef779a23c1 chore: fix linter issues. 2024-12-05 15:06:20 +01:00
4f4a2c3fd8 chore: merge Erasmus to master 2024-12-05 11:35:19 +00:00
120a7b3626 chore: Erasmus Bridge 3.15.1 changelog. 2024-12-04 14:44:25 +01:00
7cf3b6fb7b feat(BRIDGE-281): disable keychain test on macOS.
(cherry picked from commit 3f78f4d672)
2024-12-04 14:09:50 +01:00
61ca604ace chore: merge Erasmus to master 2024-11-13 09:30:24 +00:00
a8caec560e chore: Erasmus Bridge 3.15.0 changelog. 2024-10-29 10:47:33 +01:00
df78e29234 chore: merge Dragon to master 2024-09-30 09:05:11 +00:00
6105f32c75 chore: Dragon Bridge 3.14.0 changelog. 2024-09-25 10:47:40 +02:00
da76784290 chore: merge Colorado to master 2024-09-10 12:05:30 +00:00
43cbedafb8 chore: Colorado Bridge 3.13.0 changelog. 2024-08-30 15:35:30 +02:00
0d33cc5000 chore: merge Bastei to master 2024-06-19 06:06:24 +00:00
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
b84663dd7a chore: merge Alcantara to master 2024-05-21 09:32:21 +00:00
cd8db6fd1c chore: Alcantara Bridge 3.11.1 changelog. 2024-05-16 15:12:56 +02:00
a5e0f85a58 fix(BRIDGE-70): hotfix for blocked smtp/imap port causing bridge to quit 2024-05-16 09:51:32 +02:00
6cbe51138a chore: merge Alcantara to master 2024-04-29 12:31:37 +00:00
82607efe1c chore: Alcantara Bridge 3.11.0 changelog. 2024-04-23 17:07:24 +02:00
961dc9435f fix(BRIDGE-15): Apple Mail profile install page was not properly reset before showing. 2024-04-23 15:58:22 +02:00
b574ccb6ea chore: Alcantara Bridge 3.11.0 changelog. 2024-04-22 10:37:47 +02:00
2569e83e51 chore: Alcantara Bridge 3.11.0 changelog. 2024-04-22 09:27:43 +02:00
f34a7ff0ed chore: merge Zaehringen to master 2024-03-12 12:27:21 +00:00
da069a0155 chore: Zaehringen Bridge 3.10.0 changelog. 2024-03-06 10:33:17 +01:00
384fa4eb4b chore: merge Ypsilon to master 2024-02-12 11:19:51 +00:00
0c6e4ffa35 chore: merge Xikou to master 2024-02-03 00:14:41 +01:00
4951244400 chore: Xikou Bridge 3.8.2 changelog. 2024-02-02 19:32:58 +01:00
d65d6ee2e5 fix(GODT-3235): use release xikou for trigger build 2024-02-02 18:37:38 +01:00
097d6f86d3 fix(GODT-3235): update bridge update key 2024-02-02 17:34:32 +01:00
9894cf9744 chore: merge Ypsilon to master 2024-01-31 11:00:11 +00:00
f84067de3e chore: merge Xikou to master 2023-12-12 13:39:06 +01:00
f885bfbcf4 chore: merge Xikou to master 2023-12-11 17:04:00 +01:00
f3aac09ecb chore: merge wakato release to master 2023-11-22 12:52:24 +01:00
38d692ebfb chore: merge wakato release to master 2023-11-14 11:32:39 +01:00
1acc7eb7db chore: merge release/vasco_da_gama to master 2023-11-03 17:10:42 +01:00
248fbf5e33 chore: Vasco da Gama Bridge 3.6.1 changelog. 2023-10-18 15:41:01 +02:00
8b12a454ea fix(GODT-3033): Unable to receive new mail
If the IMAP service happened to finish syncing and wanted to reset the
user event service at a time the latter was publishing an event a
deadlock would occur and the user would not receive any new messages.

This change puts the request to revert the event id in a separate
go-routine to avoid this situation from re-occurring. The operational
flow remains unchanged as the event service will only process this
request once the current set of events have been published.
2023-10-18 14:46:14 +02:00
310fcffc7b chore: merge release/vasco_da_gama to master 2023-10-17 11:54:05 +02:00
318ad16378 chore: merge Umshiang release to master 2023-10-13 08:40:01 +02:00
8be4246f7e chore: Vasco da Gama Bridge 3.6.0 changelog. 2023-10-11 16:09:55 +02:00
e580f89106 feat(GODT-3004): update gopenpgp and dependencies. 2023-10-11 15:29:52 +02:00
01043e033e chore: Umshiang Bridge 3.5.3 changelog. 2023-10-11 08:37:28 +02:00
94b44b383a feat(GODT-3004): update gopenpgp and dependencies. 2023-10-11 08:26:58 +02:00
a3b8fabb26 chore: merge Umshiang to master 2023-10-10 13:46:07 +02:00
275b30e518 chore: Vasco da Gama Bridge 3.6.0 changelog. 2023-10-10 11:29:36 +02:00
bf244e5c86 fix(GODT-3003): Ensure IMAP State is reset after vault corruption
After we detect that the user has suffered the GODT-3003 bug due the
vault corruption not ensuring that a previous sync state would be
erased, we patch the gluon db directly and then reset the sync state.

After the account is added, the sync is automatically triggered and the
account state fixes itself.
2023-10-10 11:24:06 +02:00
cf9651bb94 fix(GODT-3001): Only create system labels during system label sync 2023-10-10 11:23:32 +02:00
ba65ffdbc7 chore: Umshiang Bridge 3.5.2 changelog. 2023-10-10 11:22:41 +02:00
4b95ef4d82 chore: Umshiang Bridge 3.5.2 changelog. 2023-10-09 13:25:44 +02:00
951c7c27fb fix(GODT-3003): Ensure IMAP State is reset after vault corruption
After we detect that the user has suffered the GODT-3003 bug due the
vault corruption not ensuring that a previous sync state would be
erased, we patch the gluon db directly and then reset the sync state.

After the account is added, the sync is automatically triggered and the
account state fixes itself.
2023-10-09 11:19:36 +01:00
e7423a9519 fix(GODT-3001): Only create system labels during system label sync 2023-10-09 11:05:59 +01:00
d3582fa981 chore: Vasco da Gama Bridge 3.6.0 changelog. 2023-10-03 16:43:33 +02:00
80c852a5b2 fix(GODT-2992): fix link in 'no account view' in main window after 2FA or TOTP are cancelled.
(cherry picked from commit 1c344211d1)
2023-10-03 11:08:52 +02:00
51498e3e37 chore: merge master with release/umshiang 2023-09-28 14:19:45 +02:00
b7ef6e1486 chore: Umshiang Bridge 3.5.1 changelog. 2023-09-27 13:18:23 +02:00
0d03f84711 fix(GODT-2963): Use multi error to report file removal errors
Do not abort removing files on first error. Collect errors and try to
remove as many as possible. This would cause some state files to not be
removed on windows.
2023-09-27 12:34:07 +02:00
949666724d chore: Umshiang Bridge 3.5.1 changelog. 2023-09-27 10:54:50 +02:00
bbe19bf960 fix(GODT-2956): Restore old deletion rules
When unlabeling a message from trash we have to check if this message is
present in another folder before perma-deleting.
2023-09-26 14:06:31 +02:00
bfe25e3a46 fix(GODT-2951): Negative WaitGroup Counter
Do not defer call to `wg.Done()` in `job.onJobFinished`. If there is an
error it will also call `wg.Done()`.
2023-09-26 13:58:46 +02:00
236c958703 fix(GODT-2590): Fix send on closed channel
Ensure periodic user tasks are terminated before the other user
services. The panic triggered due to the fact that the telemetry service
was shutdown before this periodic task.
2023-09-26 13:58:18 +02:00
e6b312b437 fix(GODT-2949): Fix close of close channel in event service
This issue is triggered due to the `Service.Close()` call after the
go-routine for the event service exists. It is possible that during this
period a recently added subscriber with `pendingOpAdd` gets cancelled
and closed.

However, the subscriber later also enqueues a `pendingOpRemove` which
gets processed again with a call in `user.eventService.Close()` leading
to the double close panic.

This patch simply removes the `s.Close()` from the service, and leaves
the cleanup to called externally from user.Close() or user.Logout().
2023-09-26 13:58:07 +02:00
384154c767 chore: merge 'trift' into umshiang 2023-09-14 14:48:03 +02:00
45d2e9ea63 chore: update changelog. 2023-09-13 10:25:47 +02:00
86e8a566c7 chore: Umshiang Bridge 3.5.0 changelog. 2023-09-12 07:45:08 +02:00
a80fd92018 chore: Trift Bridge 3.4.2 changelog. 2023-09-01 15:12:34 +02:00
71063ac5ee fix(GODT-2902): do not check for changed values. Related to GODT-2857. 2023-09-01 14:44:27 +02:00
642 changed files with 3615 additions and 932 deletions

View File

@ -1,41 +0,0 @@
---
name: General issue template
about: Template for detailed report of issues
title: ''
labels: ''
assignees: ''
---
Issue tracker is ONLY used for reporting bugs with technical details. "It doesn't work" or new features should be discussed with our customer support. Please use bug report function in Bridge or contact bridge@protonmail.ch.
<!--- Provide a general summary of the issue in the Title above -->
## Expected Behavior
<!--- Tell us what should happen -->
## Current Behavior
<!--- Tell us what happens instead of the expected behavior -->
## Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
## Steps to Reproduce
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
<!--- reproduce this bug. Include code to reproduce, if relevant -->
1.
2.
3.
4.
## Version Information
<!--- Which version of the app(s) were you using when you experienced this issue? -->
## Context (Environment)
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
## Detailed Description
<!--- Provide a detailed description of the change or addition you are proposing -->
## Possible Implementation
<!--- Not obligatory, but suggest an idea for implementing addition or change -->

1
.gitlab/CODEOWNERS Normal file
View File

@ -0,0 +1 @@
* @go/bridge-ppl/devs

View File

@ -87,7 +87,6 @@ linters:
- asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers [fast: true, auto-fix: false]
- durationcheck # check for two durations multiplied together [fast: false, auto-fix: false]
- exhaustive # check exhaustiveness of enum switch statements [fast: false, auto-fix: false]
- exportloopref # checks for pointers to enclosing loop variables [fast: false, auto-fix: false]
- copyloopvar # detects places where loop variables are copied.
- forcetypeassert # finds forced type assertions [fast: true, auto-fix: false]
- godot # Check if comments end in a period [fast: true, auto-fix: true]

View File

@ -3,7 +3,7 @@
## Prerequisites
* 64-bit OS:
- the go-rfc5322 module cannot currently be compiled for 32-bit OSes
* Go 1.21.9
* Go 1.23.4
* 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/)
* GCC (Linux), msvc (Windows) or Xcode (macOS)

View File

@ -1,10 +1,78 @@
# Contribution Policy
# Contributing guidelines
The following document describes how to contribute to the project. In this context, contribution does not only mean code contribution but also reporting issues, requesting new features, or just asking for help.
## Reporting issues
In case you experience issues while using the application, our request is to contact Proton customer support directly.
The benefits of using Proton customer support are
- Available 24/7/365.
- Provides priority support based on subscription type.
- Will escalate the issue to the developers every time it becomes too technical or they do not know the answer to a question.
- Easier to detect systematic issues by connecting similar reports.
- Possible to quickly derive frequency of an issue.
- Can assist you to transfer sensitive information safely to us.
To speed up the communication with customer support, consider the following:
- Whenever is possible, use the in-app bug report feature. It provides an application specific guide compared to using the generic report form on web.
- Whenever is possible, proactively attach logs to your report. Reporting an issue from the application can help you in that.
- Check whether your system is officially supported by Proton, including the source of the installer. We cannot provide help when the application is packaged by a third party or when the application is used on systems that we do not prepare to support.
- If your report is a feature request, see the Feature request section. In case it is an issue related to application security, see the Security vulnerabilities section.
In the past, we used GitHub issue tracker for more technical issues in parallel to Proton customer support, but we run into limitations with this approach:
- Monitoring GitHub issue tracker took development time as it was managed by the development team.
- It made issue frequency tracking challenging because we did not have a single point of entry for issues.
- Users were confused what technical issue means, and used the GitHub issue tracker for feature requests, or non-technical discussions.
- Users sometimes shared sensitive data through the GitHub issue tracker.
For the above reasons, we do not use GitHub issue tracker anymore but ask you to contact our customer support in case you run into a problem.
### Security vulnerabilities
Proton runs a bug bounty program for security vulnerabilities. They differ from normal bug reports in the following ways:
- These reports go directly to our security team.
- They expect deeper explanation of the issue.
- Depending on the finding, they may be financially rewarded.
More information about the program can be found [here](https://proton.me/security/bug-bounty).
## Feature requests
What someone considers as a bug is sometimes a feature, and sometimes, a missing feature is considered as a bug. Instead of reporting feature requests as bugs, we setup a UserVoice page to allow our users to share their preferences. UserVoice also makes it possible to vote on other feature requests, making the community preference public.
Our product team frequently monitors UserVoice, and the features listed there are taken into account in our planning.
Examples for UserVoice requests:
- Extending the officially supported environments (e.g., operating systems, clients, or computer architectures).
- Requesting new features.
- Integration with non-Proton services.
UserVoice is available [here](https://protonmail.uservoice.com/).
## Asking for help
The best ways to get answer for generic questions or to get help with setting up the system is to interact with our active community on [Reddit](https://reddit.com/r/ProtonMail/) or to contact customer support.
## Code contribution
We are grateful if you can contribute directly with code. In that case there is nothing else to do than to open a pull request.
The following is worthwhile noting
- The project is primarily developed on an internal repository, and the one on GitHub is only a mirror of it. For that reason, the merge request will not be merged on GitHub but added to the project internally. We are keeping the original author in the change set to respect the contribution.
- The application is used on numerous platforms and by many third party clients. To have higher chance your change to be accepted, consider all supported dependencies.
- Give detailed description of the issue, preferably with test steps to reproduce the original issue, and to verify the fix. It is even better if you also extend the automated tests.
### Contribution policy
By making a contribution to this project:
1. I assign any and all copyright related to the contribution to Proton AG;
2. I certify that the contribution was created in whole by me;
3. I understand and agree that this project and the contribution are public
and that a record of the contribution (including all personal information I
submit with it) is maintained indefinitely and may be redistributed with
this project or the open source license(s) involved.
1. You assign any and all copyright related to the contribution to Proton AG;
2. You certify that the contribution was created in whole by you;
3. You understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information you submit with it) is maintained indefinitely and may be redistributed with this project or the open source license(s) involved.

View File

@ -3,6 +3,32 @@
Changelog [format](http://keepachangelog.com/en/1.0.0/)
## Helix Bridge 3.18.0
### Changed
* BRIDGE-309: Revised update logic and structure.
* BRIDGE-154: Added access token to expiry refresh request.
## Grunwald Bridge 3.17.0
### Added
* BRIDGE-271: Report version file check failure to Sentry.
* BRIDGE-247: Test: Automate Bridge 0% update rollout.
* BRIDGE-248: Test: Additional Bridge UI e2e automation tests.
### Changed
* BRIDGE-73: Update goopenpgp.
* BRIDGE-287: Update x/net and x/crypto dependencies.
* BRIDGE-303: Update govulncheck to latest release.
* BRIDGE-226: Bump Go version to 1.23.4.
* BRIDGE-288: Extension to synchronization update handler, observability tweaks and gluon update.
### Fixed
* BRIDGE-291: Use correct field for user plan type.
* BRIDGE-143: Add missing QML component attribute, cut/paste disabled on read-only text areas.
## Flavien Bridge 3.16.0
### Added
@ -24,6 +50,12 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/)
* BRIDGE-258: Fixed issue with draft updates and sending during synchronization.
## Erasmus Bridge 3.15.1
### Changed
* BRIDGE-281: Disable keychain test on macOS.
## Erasmus Bridge 3.15.0
### Added

View File

@ -12,7 +12,7 @@ ROOT_DIR:=$(realpath .)
.PHONY: build build-gui build-nogui build-launcher versioner hasher
# Keep version hardcoded so app build works also without Git repository.
BRIDGE_APP_VERSION?=3.16.0+git
BRIDGE_APP_VERSION?=3.18.0+git
APP_VERSION:=${BRIDGE_APP_VERSION}
APP_FULL_NAME:=Proton Mail Bridge
APP_VENDOR:=Proton AG

View File

@ -1,5 +1,5 @@
# Proton Mail Bridge
Copyright (c) 2024 Proton AG
Copyright (c) 2025 Proton AG
This repository holds the Proton Mail Bridge application.
For a detailed build information see [BUILDS](./BUILDS.md).

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

30
go.mod
View File

@ -1,16 +1,16 @@
module github.com/ProtonMail/proton-bridge/v3
go 1.21
go 1.23
toolchain go1.21.9
toolchain go1.23.4
require (
github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557
github.com/Masterminds/semver/v3 v3.2.0
github.com/ProtonMail/gluon v0.17.1-0.20241121121545-aa1cfd19b4b2
github.com/ProtonMail/gluon v0.17.1-0.20250116113909-2ebd96ec0bc2
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a
github.com/ProtonMail/go-proton-api v0.4.1-0.20240918100656-b4860af56d47
github.com/ProtonMail/gopenpgp/v2 v2.7.4-proton
github.com/ProtonMail/go-proton-api v0.4.1-0.20250217140732-2e531f21de4c
github.com/ProtonMail/gopenpgp/v2 v2.8.2-proton
github.com/PuerkitoBio/goquery v1.8.1
github.com/abiosoft/ishell v2.0.0+incompatible
github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37
@ -30,7 +30,7 @@ require (
github.com/go-resty/resty/v2 v2.7.0
github.com/godbus/dbus v4.1.0+incompatible
github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.5.9
github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.3.0
github.com/hashicorp/go-multierror v1.1.1
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba
@ -46,10 +46,10 @@ require (
github.com/vmihailenco/msgpack/v5 v5.3.5
go.uber.org/goleak v1.2.1
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
golang.org/x/net v0.24.0
golang.org/x/net v0.34.0
golang.org/x/oauth2 v0.7.0
golang.org/x/sys v0.19.0
golang.org/x/text v0.14.0
golang.org/x/sys v0.29.0
golang.org/x/text v0.21.0
google.golang.org/api v0.114.0
google.golang.org/grpc v1.56.3
google.golang.org/protobuf v1.33.0
@ -60,7 +60,7 @@ require (
cloud.google.com/go/compute v1.19.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230717121622-edf196117233 // indirect
github.com/ProtonMail/go-crypto v1.1.4-proton // indirect
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect
github.com/ProtonMail/go-srp v0.0.7 // indirect
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect
@ -68,7 +68,7 @@ require (
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/chzyer/test v1.0.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/cloudflare/circl v1.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/cronokirby/saferith v0.33.0 // indirect
github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect
@ -121,10 +121,10 @@ require (
gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/tools v0.6.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

60
go.sum
View File

@ -34,25 +34,27 @@ 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-20211005172633-e235017c1baf h1:yc9daCCYUefEs69zUkSzubzjBbL+cmOXgnmt9Fyd9ug=
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf/go.mod h1:o0ESU9p83twszAU8LBeJKFAAMX14tISa0yk4Oo5TOqo=
github.com/ProtonMail/gluon v0.17.1-0.20241121121545-aa1cfd19b4b2 h1:iZjKvjb6VkGb52ZaBBiXC1MGYJN4C/S97JfppdzpMHQ=
github.com/ProtonMail/gluon v0.17.1-0.20241121121545-aa1cfd19b4b2/go.mod h1:0/c03TzZPNiSgY5UDJK1iRDkjlDPwWugxTT6et2qDu8=
github.com/ProtonMail/gluon v0.17.1-0.20250116113909-2ebd96ec0bc2 h1:lDgMidI/9j2eedavcy7YICv8+F73ooVTUoUGBE4dO0s=
github.com/ProtonMail/gluon v0.17.1-0.20250116113909-2ebd96ec0bc2/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/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-20230717121622-edf196117233 h1:bdoKdh0f66/lrgVfYlxw0aqISY/KOqXmFJyGt7rGmnc=
github.com/ProtonMail/go-crypto v0.0.0-20230717121622-edf196117233/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/ProtonMail/go-crypto v1.1.4-proton h1:KIo9uNlk3vzlwI7o5VjhiEjI4Ld1TDixOMnoNZyfpFE=
github.com/ProtonMail/go-crypto v1.1.4-proton/go.mod h1:zNoyBJW3p/yVWiHNZgfTF9VsjwqYof5YY0M9kt2QaX0=
github.com/ProtonMail/go-message v0.13.1-0.20240919135104-3bc88e6a9423 h1:p8nBDxvRnvDOyrcePKkPpErWGhDoTqpX8a1c54CcSu0=
github.com/ProtonMail/go-message v0.13.1-0.20240919135104-3bc88e6a9423/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/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
github.com/ProtonMail/go-proton-api v0.4.1-0.20240918100656-b4860af56d47 h1:a+3dOyIxJEslN5HxyICM8flY9lnCyJupXNcv6fUaivA=
github.com/ProtonMail/go-proton-api v0.4.1-0.20240918100656-b4860af56d47/go.mod h1:3A0cpdo0BIenIPjTG6u8EbzJ8uuJy7rVvM/NaynjCKA=
github.com/ProtonMail/go-proton-api v0.4.1-0.20250121114701-67bd01ad0bc3 h1:YYnLBVcg7WrEbYVmF1PBr4AEQlob9rCphsMHAmF4CAo=
github.com/ProtonMail/go-proton-api v0.4.1-0.20250121114701-67bd01ad0bc3/go.mod h1:RYgagBFkA3zFrSt7/vviFFwjZxBo6pGzcTwFsLwsnyc=
github.com/ProtonMail/go-proton-api v0.4.1-0.20250217140732-2e531f21de4c h1:dxnbB+ov77BDj1LC35fKZ14hLoTpU6OTpZySwxarVx0=
github.com/ProtonMail/go-proton-api v0.4.1-0.20250217140732-2e531f21de4c/go.mod h1:RYgagBFkA3zFrSt7/vviFFwjZxBo6pGzcTwFsLwsnyc=
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-srp v0.0.7 h1:Sos3Qk+th4tQR64vsxGIxYpN3rdnG9Wf9K4ZloC1JrI=
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.4-proton/go.mod h1:omVkSsfPAhmptzPF/piMXb16wKIWUvVhZbVW7sJKh0A=
github.com/ProtonMail/gopenpgp/v2 v2.8.2-proton h1:MMVgE6nk5Ulh9Ud5L1Xc5iaPKE85FbfKQV17ZMucrR0=
github.com/ProtonMail/gopenpgp/v2 v2.8.2-proton/go.mod h1:+PjybET6fgcLzldFy1hpy7s8VibZ0T1hLFbxnnMk0lo=
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
github.com/abiosoft/ishell v2.0.0+incompatible h1:zpwIuEHc37EzrsIYah3cpevrIc8Oma7oZPxr03tlmmw=
@ -77,7 +79,6 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm
github.com/bradenaw/juniper v0.12.0 h1:Q/7icpPQD1nH/La5DobQfNEtwyrBSiSu47jOQx7lJEM=
github.com/bradenaw/juniper v0.12.0/go.mod h1:Z2B7aJlQ7xbfWsnMLROj5t/5FQ94/MkIdKC30J4WvzI=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
@ -95,9 +96,8 @@ 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/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.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/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
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=
@ -235,8 +235,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@ -498,11 +498,10 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
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.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
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-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -527,8 +526,9 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -551,15 +551,14 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
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.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.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
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/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
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-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -574,8 +573,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -609,18 +608,15 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.6.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.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
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.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
@ -634,13 +630,12 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5-0.20201125200606-c27b9fd57aec/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.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.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.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
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.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
@ -669,8 +664,9 @@ golang.org/x/tools v0.0.0-20200328031815-3db5fc6bac03/go.mod h1:Sl4aGygMT6LrqrWc
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//
@ -50,10 +50,12 @@ import (
"github.com/ProtonMail/proton-bridge/v3/internal/services/syncservice"
"github.com/ProtonMail/proton-bridge/v3/internal/telemetry"
"github.com/ProtonMail/proton-bridge/v3/internal/unleash"
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
"github.com/ProtonMail/proton-bridge/v3/internal/user"
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
"github.com/ProtonMail/proton-bridge/v3/pkg/keychain"
"github.com/bradenaw/juniper/xslices"
"github.com/elastic/go-sysinfo/types"
"github.com/go-resty/resty/v2"
"github.com/sirupsen/logrus"
)
@ -80,8 +82,9 @@ type Bridge struct {
imapEventCh chan imapEvents.Event
// updater is the bridge's updater.
updater Updater
installCh chan installJob
updater Updater
installChLegacy chan installJobLegacy
installCh chan installJob
// heartbeat is the telemetry heartbeat for metrics.
heartbeat *heartBeatState
@ -148,6 +151,9 @@ type Bridge struct {
// notificationStore is used for notification deduplication
notificationStore *notifications.Store
// getHostVersion primarily used for testing the update logic - it should return an OS version
getHostVersion func(host types.Host) string
}
var logPkg = logrus.WithField("pkg", "bridge") //nolint:gochecknoglobals
@ -282,8 +288,9 @@ func newBridge(
tlsConfig: tlsConfig,
imapEventCh: imapEventCh,
updater: updater,
installCh: make(chan installJob),
updater: updater,
installChLegacy: make(chan installJobLegacy),
installCh: make(chan installJob),
curVersion: curVersion,
newVersion: curVersion,
@ -315,6 +322,8 @@ func newBridge(
observabilityService: observabilityService,
notificationStore: notifications.NewStore(locator.ProvideNotificationsCachePath),
getHostVersion: func(host types.Host) string { return host.Info().OS.Version },
}
bridge.serverManager = imapsmtpserver.NewService(context.Background(),
@ -435,17 +444,46 @@ func (bridge *Bridge) init(tlsReporter TLSReporter) error {
// Check for updates when triggered.
bridge.goUpdate = bridge.tasks.PeriodicOrTrigger(constants.UpdateCheckInterval, 0, func(ctx context.Context) {
logPkg.Info("Checking for updates")
var versionLegacy updater.VersionInfoLegacy
var version updater.VersionInfo
var err error
useOldUpdateLogic := bridge.GetFeatureFlagValue(unleash.UpdateUseNewVersionFileStructureDisabled)
if useOldUpdateLogic {
versionLegacy, err = bridge.updater.GetVersionInfoLegacy(ctx, bridge.api, bridge.vault.GetUpdateChannel())
} else {
version, err = bridge.updater.GetVersionInfo(ctx, bridge.api)
}
version, err := bridge.updater.GetVersionInfo(ctx, bridge.api, bridge.vault.GetUpdateChannel())
if err != nil {
bridge.publish(events.UpdateCheckFailed{Error: err})
if errors.Is(err, updater.ErrVersionFileDownloadOrVerify) {
logPkg.WithError(err).Error("Cannot download or verify the version file")
if reporterErr := bridge.reporter.ReportMessageWithContext(
"Cannot download or verify the version file",
reporter.Context{"error": err},
); reporterErr != nil {
logPkg.WithError(reporterErr).Error("Failed to report version file check error")
}
}
} else {
bridge.handleUpdate(version)
if useOldUpdateLogic {
bridge.handleUpdateLegacy(versionLegacy)
} else {
bridge.handleUpdate(version)
}
}
})
defer bridge.goUpdate()
// Install updates when available.
// Install updates when available - based on old update logic
bridge.tasks.Once(func(ctx context.Context) {
async.RangeContext(ctx, bridge.installChLegacy, func(job installJobLegacy) {
bridge.installUpdateLegacy(ctx, job)
})
})
// Install updates when available - based on new update logic
bridge.tasks.Once(func(ctx context.Context) {
async.RangeContext(ctx, bridge.installCh, func(job installJob) {
bridge.installUpdate(ctx, job)
@ -636,14 +674,6 @@ func loadTLSConfig(vault *vault.Vault) (*tls.Config, error) {
}, nil
}
func min(a, b time.Duration) time.Duration { //nolint:predeclared
if a < b {
return a
}
return b
}
func (bridge *Bridge) HasAPIConnection() bool {
return bridge.api.GetStatus() == proton.StatusUp
}
@ -738,3 +768,19 @@ func (bridge *Bridge) ReportMessageWithContext(message string, messageCtx report
func (bridge *Bridge) GetUsers() map[string]*user.User {
return bridge.users
}
// SetCurrentVersionTest - sets the current version of bridge; should only be used for tests.
func (bridge *Bridge) SetCurrentVersionTest(version *semver.Version) {
bridge.curVersion = version
bridge.newVersion = version
}
// SetHostVersionGetterTest - sets the OS version helper func; only used for testing.
func (bridge *Bridge) SetHostVersionGetterTest(fn func(host types.Host) string) {
bridge.getHostVersion = fn
}
// SetRolloutPercentageTest - sets the rollout percentage; should only be used for testing.
func (bridge *Bridge) SetRolloutPercentageTest(rollout float64) error {
return bridge.vault.SetUpdateRollout(rollout)
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//
@ -45,6 +45,7 @@ import (
"github.com/ProtonMail/proton-bridge/v3/internal/focus"
"github.com/ProtonMail/proton-bridge/v3/internal/locations"
"github.com/ProtonMail/proton-bridge/v3/internal/services/imapsmtpserver"
"github.com/ProtonMail/proton-bridge/v3/internal/unleash"
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
"github.com/ProtonMail/proton-bridge/v3/internal/user"
"github.com/ProtonMail/proton-bridge/v3/internal/useragent"
@ -383,9 +384,14 @@ func TestBridge_Cookies(t *testing.T) {
})
}
func TestBridge_CheckUpdate(t *testing.T) {
func TestBridge_CheckUpdate_Legacy(t *testing.T) {
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, vaultKey []byte) {
unleash.ModifyPollPeriodAndJitter(500*time.Millisecond, 0)
s.PushFeatureFlag(unleash.UpdateUseNewVersionFileStructureDisabled)
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Wait for FF poll.
time.Sleep(600 * time.Millisecond)
// Disable autoupdate for this test.
require.NoError(t, bridge.SetAutoUpdate(false))
@ -400,7 +406,7 @@ func TestBridge_CheckUpdate(t *testing.T) {
require.Equal(t, events.UpdateNotAvailable{}, <-noUpdateCh)
// Simulate a new version being available.
mocks.Updater.SetLatestVersion(v2_4_0, v2_3_0)
mocks.Updater.SetLatestVersionLegacy(v2_4_0, v2_3_0)
// Get a stream of update available events.
updateCh, done := bridge.GetEvents(events.UpdateAvailable{})
@ -411,7 +417,7 @@ func TestBridge_CheckUpdate(t *testing.T) {
// We should receive an event indicating that an update is available.
require.Equal(t, events.UpdateAvailable{
Version: updater.VersionInfo{
VersionLegacy: updater.VersionInfoLegacy{
Version: v2_4_0,
MinAuto: v2_3_0,
RolloutProportion: 1.0,
@ -423,25 +429,30 @@ func TestBridge_CheckUpdate(t *testing.T) {
})
}
func TestBridge_AutoUpdate(t *testing.T) {
func TestBridge_AutoUpdate_Legacy(t *testing.T) {
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, vaultKey []byte) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
unleash.ModifyPollPeriodAndJitter(500*time.Millisecond, 0)
s.PushFeatureFlag(unleash.UpdateUseNewVersionFileStructureDisabled)
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(b *bridge.Bridge, mocks *bridge.Mocks) {
// Wait for FF poll.
time.Sleep(600 * time.Millisecond)
// Enable autoupdate for this test.
require.NoError(t, bridge.SetAutoUpdate(true))
require.NoError(t, b.SetAutoUpdate(true))
// Get a stream of update events.
updateCh, done := bridge.GetEvents(events.UpdateInstalled{})
updateCh, done := b.GetEvents(events.UpdateInstalled{})
defer done()
// Simulate a new version being available.
mocks.Updater.SetLatestVersion(v2_4_0, v2_3_0)
mocks.Updater.SetLatestVersionLegacy(v2_4_0, v2_3_0)
// Check for updates.
bridge.CheckForUpdates()
b.CheckForUpdates()
// We should receive an event indicating that the update was silently installed.
require.Equal(t, events.UpdateInstalled{
Version: updater.VersionInfo{
VersionLegacy: updater.VersionInfoLegacy{
Version: v2_4_0,
MinAuto: v2_3_0,
RolloutProportion: 1.0,
@ -452,9 +463,14 @@ func TestBridge_AutoUpdate(t *testing.T) {
})
}
func TestBridge_ManualUpdate(t *testing.T) {
func TestBridge_ManualUpdate_Legacy(t *testing.T) {
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, vaultKey []byte) {
unleash.ModifyPollPeriodAndJitter(500*time.Millisecond, 0)
s.PushFeatureFlag(unleash.UpdateUseNewVersionFileStructureDisabled)
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
// Wait for FF poll.
time.Sleep(600 * time.Millisecond)
// Disable autoupdate for this test.
require.NoError(t, bridge.SetAutoUpdate(false))
@ -463,14 +479,14 @@ func TestBridge_ManualUpdate(t *testing.T) {
defer done()
// Simulate a new version being available, but it's too new for us.
mocks.Updater.SetLatestVersion(v2_4_0, v2_4_0)
mocks.Updater.SetLatestVersionLegacy(v2_4_0, v2_4_0)
// Check for updates.
bridge.CheckForUpdates()
// We should receive an event indicating an update is available, but we can't install it.
require.Equal(t, events.UpdateAvailable{
Version: updater.VersionInfo{
VersionLegacy: updater.VersionInfoLegacy{
Version: v2_4_0,
MinAuto: v2_4_0,
RolloutProportion: 1.0,
@ -484,7 +500,12 @@ func TestBridge_ManualUpdate(t *testing.T) {
func TestBridge_ForceUpdate(t *testing.T) {
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, vaultKey []byte) {
unleash.ModifyPollPeriodAndJitter(500*time.Millisecond, 0)
s.PushFeatureFlag(unleash.UpdateUseNewVersionFileStructureDisabled)
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridge.Bridge, _ *bridge.Mocks) {
// Wait for FF poll.
time.Sleep(600 * time.Millisecond)
// Get a stream of update events.
updateCh, done := bridge.GetEvents(events.UpdateForced{})
defer done()

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -119,13 +119,14 @@ func (provider *TestLocationsProvider) UserCache() string {
}
type TestUpdater struct {
latest updater.VersionInfo
lock sync.RWMutex
latest updater.VersionInfoLegacy
releases updater.VersionInfo
lock sync.RWMutex
}
func NewTestUpdater(version, minAuto *semver.Version) *TestUpdater {
return &TestUpdater{
latest: updater.VersionInfo{
latest: updater.VersionInfoLegacy{
Version: version,
MinAuto: minAuto,
@ -134,11 +135,11 @@ func NewTestUpdater(version, minAuto *semver.Version) *TestUpdater {
}
}
func (testUpdater *TestUpdater) SetLatestVersion(version, minAuto *semver.Version) {
func (testUpdater *TestUpdater) SetLatestVersionLegacy(version, minAuto *semver.Version) {
testUpdater.lock.Lock()
defer testUpdater.lock.Unlock()
testUpdater.latest = updater.VersionInfo{
testUpdater.latest = updater.VersionInfoLegacy{
Version: version,
MinAuto: minAuto,
@ -146,17 +147,35 @@ func (testUpdater *TestUpdater) SetLatestVersion(version, minAuto *semver.Versio
}
}
func (testUpdater *TestUpdater) GetVersionInfo(_ context.Context, _ updater.Downloader, _ updater.Channel) (updater.VersionInfo, error) {
func (testUpdater *TestUpdater) GetVersionInfoLegacy(_ context.Context, _ updater.Downloader, _ updater.Channel) (updater.VersionInfoLegacy, error) {
testUpdater.lock.RLock()
defer testUpdater.lock.RUnlock()
return testUpdater.latest, nil
}
func (testUpdater *TestUpdater) InstallUpdate(_ context.Context, _ updater.Downloader, _ updater.VersionInfo) error {
func (testUpdater *TestUpdater) InstallUpdateLegacy(_ context.Context, _ updater.Downloader, _ updater.VersionInfoLegacy) error {
return nil
}
func (testUpdater *TestUpdater) RemoveOldUpdates() error {
return nil
}
func (testUpdater *TestUpdater) SetLatestVersion(releases updater.VersionInfo) {
testUpdater.lock.Lock()
defer testUpdater.lock.Unlock()
testUpdater.releases = releases
}
func (testUpdater *TestUpdater) GetVersionInfo(_ context.Context, _ updater.Downloader) (updater.VersionInfo, error) {
testUpdater.lock.RLock()
defer testUpdater.lock.RUnlock()
return testUpdater.releases, nil
}
func (testUpdater *TestUpdater) InstallUpdate(_ context.Context, _ updater.Downloader, _ updater.Release) error {
return nil
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//
@ -52,7 +52,9 @@ type Autostarter interface {
}
type Updater interface {
GetVersionInfo(context.Context, updater.Downloader, updater.Channel) (updater.VersionInfo, error)
InstallUpdate(context.Context, updater.Downloader, updater.VersionInfo) error
GetVersionInfoLegacy(context.Context, updater.Downloader, updater.Channel) (updater.VersionInfoLegacy, error)
InstallUpdateLegacy(context.Context, updater.Downloader, updater.VersionInfoLegacy) error
RemoveOldUpdates() error
GetVersionInfo(context.Context, updater.Downloader) (updater.VersionInfo, error)
InstallUpdate(context.Context, updater.Downloader, updater.Release) error
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//
@ -21,22 +21,168 @@ import (
"context"
"errors"
"github.com/Masterminds/semver/v3"
"github.com/ProtonMail/gluon/reporter"
"github.com/ProtonMail/proton-bridge/v3/internal/events"
"github.com/ProtonMail/proton-bridge/v3/internal/safe"
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
"github.com/elastic/go-sysinfo"
"github.com/sirupsen/logrus"
"golang.org/x/exp/slices"
)
func (bridge *Bridge) CheckForUpdates() {
bridge.goUpdate()
}
func (bridge *Bridge) InstallUpdate(version updater.VersionInfo) {
bridge.installCh <- installJob{version: version, silent: false}
func (bridge *Bridge) InstallUpdateLegacy(version updater.VersionInfoLegacy) {
bridge.installChLegacy <- installJobLegacy{version: version, silent: false}
}
func (bridge *Bridge) InstallUpdate(release updater.Release) {
bridge.installCh <- installJob{Release: release, Silent: false}
}
func (bridge *Bridge) handleUpdate(version updater.VersionInfo) {
updateChannel := bridge.vault.GetUpdateChannel()
updateRollout := bridge.vault.GetUpdateRollout()
autoUpdateEnabled := bridge.vault.GetAutoUpdate()
checkSystemVersion := true
hostInfo, err := sysinfo.Host()
// If we're unable to get host system information we skip the update's minimum/maximum OS version checks
if err != nil {
checkSystemVersion = false
logrus.WithError(err).Error("Failed to obtain host system info while handling updates")
if reporterErr := bridge.reporter.ReportMessageWithContext(
"Failed to obtain host system info while handling updates",
reporter.Context{"error": err},
); reporterErr != nil {
logrus.WithError(reporterErr).Error("Failed to report update error")
}
}
if len(version.Releases) > 0 {
// Update latest is only used to update the release notes and landing page URL
bridge.publish(events.UpdateLatest{Release: version.Releases[0]})
}
// minAutoUpdateEvent - used to determine the highest compatible update that satisfies the Minimum Bridge version
minAutoUpdateEvent := events.UpdateAvailable{
Release: updater.Release{Version: &semver.Version{}},
Compatible: false,
Silent: false,
}
// We assume that the version file is always created in descending order
// where newer versions are prepended to the top of the releases
// The logic for checking update eligibility is as follows:
// 1. Check release channel.
// 2. Check whether release version is greater.
// 3. Check if rollout is larger.
// 4. Check OS Version restrictions (provided that restrictions are provided, and we can extract the OS version).
// 5. Check Minimum Compatible Bridge Version.
// 6. Check if an update package is provided.
// 7. Check auto-update.
for _, release := range version.Releases {
log := logrus.WithFields(logrus.Fields{
"current": bridge.curVersion,
"channel": updateChannel,
"update_version": release.Version,
"update_channel": release.ReleaseCategory,
"update_min_auto": release.MinAuto,
"update_rollout": release.RolloutProportion,
"update_min_os_version": release.SystemVersion.Minimum,
"update_max_os_version": release.SystemVersion.Maximum,
})
log.Debug("Checking update release")
if !release.ReleaseCategory.UpdateEligible(updateChannel) {
log.Debug("Update does not satisfy update channel requirement")
continue
}
if !release.Version.GreaterThan(bridge.curVersion) {
log.Debug("Update version is not greater than current version")
continue
}
if release.RolloutProportion < updateRollout {
log.Debug("Update has not been rolled out yet")
continue
}
if checkSystemVersion {
shouldContinue, err := release.SystemVersion.IsHostVersionEligible(log, hostInfo, bridge.getHostVersion)
if err != nil && shouldContinue {
log.WithError(err).Error(
"Failed to verify host system version compatibility during release check." +
"Error is non-fatal continuing with checks",
)
} else if err != nil {
log.WithError(err).Error("Failed to verify host system version compatibility during update check")
continue
}
if !shouldContinue {
log.Debug("Host version does not satisfy system requirements for update")
continue
}
}
if release.MinAuto != nil && bridge.curVersion.LessThan(release.MinAuto) {
log.Debug("Update is available but is incompatible with this Bridge version")
if release.Version.GreaterThan(minAutoUpdateEvent.Release.Version) {
minAutoUpdateEvent.Release = release
}
continue
}
// Check if we have a provided installer package
if found := slices.IndexFunc(release.File, func(file updater.File) bool {
return file.Identifier == updater.PackageIdentifier
}); found == -1 {
log.Error("Update is available but does not contain update package")
if reporterErr := bridge.reporter.ReportMessageWithContext(
"Available update does not contain update package",
reporter.Context{"update_version": release.Version},
); reporterErr != nil {
log.WithError(reporterErr).Error("Failed to report update error")
}
continue
}
if !autoUpdateEnabled {
log.Info("An update is available but auto-update is disabled")
bridge.publish(events.UpdateAvailable{
Release: release,
Compatible: true,
Silent: false,
})
return
}
// If we've gotten to this point that means an automatic update is available and we should install it
safe.RLock(func() {
bridge.installCh <- installJob{Release: release, Silent: true}
}, bridge.newVersionLock)
return
}
// If there's a release with a minAuto requirement that we satisfy (alongside all other checks)
// then notify the user that a manual update is needed
if !minAutoUpdateEvent.Release.Version.Equal(&semver.Version{}) {
bridge.publish(minAutoUpdateEvent)
}
bridge.publish(events.UpdateNotAvailable{})
}
func (bridge *Bridge) handleUpdateLegacy(version updater.VersionInfoLegacy) {
log := logrus.WithFields(logrus.Fields{
"version": version.Version,
"current": bridge.curVersion,
@ -44,7 +190,7 @@ func (bridge *Bridge) handleUpdate(version updater.VersionInfo) {
})
bridge.publish(events.UpdateLatest{
Version: version,
VersionLegacy: version,
})
switch {
@ -62,33 +208,33 @@ func (bridge *Bridge) handleUpdate(version updater.VersionInfo) {
log.Info("An update is available but is incompatible with this version")
bridge.publish(events.UpdateAvailable{
Version: version,
Compatible: false,
Silent: false,
VersionLegacy: version,
Compatible: false,
Silent: false,
})
case !bridge.vault.GetAutoUpdate():
log.Info("An update is available but auto-update is disabled")
bridge.publish(events.UpdateAvailable{
Version: version,
Compatible: true,
Silent: false,
VersionLegacy: version,
Compatible: true,
Silent: false,
})
default:
safe.RLock(func() {
bridge.installCh <- installJob{version: version, silent: true}
bridge.installChLegacy <- installJobLegacy{version: version, silent: true}
}, bridge.newVersionLock)
}
}
type installJob struct {
version updater.VersionInfo
type installJobLegacy struct {
version updater.VersionInfoLegacy
silent bool
}
func (bridge *Bridge) installUpdate(ctx context.Context, job installJob) {
func (bridge *Bridge) installUpdateLegacy(ctx context.Context, job installJobLegacy) {
safe.Lock(func() {
log := logrus.WithFields(logrus.Fields{
"version": job.version.Version,
@ -103,17 +249,12 @@ func (bridge *Bridge) installUpdate(ctx context.Context, job installJob) {
log.WithField("silent", job.silent).Info("An update is available")
bridge.publish(events.UpdateAvailable{
Version: job.version,
Compatible: true,
Silent: job.silent,
VersionLegacy: job.version,
Compatible: true,
Silent: job.silent,
})
bridge.publish(events.UpdateInstalling{
Version: job.version,
Silent: job.silent,
})
err := bridge.updater.InstallUpdate(ctx, bridge.api, job.version)
err := bridge.updater.InstallUpdateLegacy(ctx, bridge.api, job.version)
switch {
case errors.Is(err, updater.ErrDownloadVerify):
@ -134,8 +275,79 @@ func (bridge *Bridge) installUpdate(ctx context.Context, job installJob) {
log.WithError(err).Error("The update could not be installed")
bridge.publish(events.UpdateFailed{
Version: job.version,
Silent: job.silent,
VersionLegacy: job.version,
Silent: job.silent,
Error: err,
})
default:
log.Info("The update was installed successfully")
bridge.publish(events.UpdateInstalled{
VersionLegacy: job.version,
Silent: job.silent,
})
bridge.newVersion = job.version.Version
}
}, bridge.newVersionLock)
}
type installJob struct {
Release updater.Release
Silent bool
}
func (bridge *Bridge) installUpdate(ctx context.Context, job installJob) {
safe.Lock(func() {
log := logrus.WithFields(logrus.Fields{
"version": job.Release.Version,
"current": bridge.curVersion,
"channel": bridge.vault.GetUpdateChannel(),
})
if !job.Release.Version.GreaterThan(bridge.newVersion) {
return
}
log.WithField("silent", job.Silent).Info("An update is available")
bridge.publish(events.UpdateAvailable{
Release: job.Release,
Compatible: true,
Silent: job.Silent,
})
err := bridge.updater.InstallUpdate(ctx, bridge.api, job.Release)
switch {
case errors.Is(err, updater.ErrReleaseUpdatePackageMissing):
log.WithError(err).Error("The update could not be installed but we will fail silently")
if reporterErr := bridge.reporter.ReportExceptionWithContext(
"Cannot download update, update package is missing",
reporter.Context{"error": err},
); reporterErr != nil {
log.WithError(reporterErr).Error("Failed to report update error")
}
case errors.Is(err, updater.ErrDownloadVerify):
// BRIDGE-207: if download or verification fails, we do not want to trigger a manual update. We report in the log and to Sentry
// and we fail silently.
log.WithError(err).Error("The update could not be installed, but we will fail silently")
if reporterErr := bridge.reporter.ReportMessageWithContext(
"Cannot download or verify update",
reporter.Context{"error": err},
); reporterErr != nil {
log.WithError(reporterErr).Error("Failed to report update error")
}
case errors.Is(err, updater.ErrUpdateAlreadyInstalled):
log.Info("The update was already installed")
case err != nil:
log.WithError(err).Error("The update could not be installed")
bridge.publish(events.UpdateFailed{
Release: job.Release,
Silent: job.Silent,
Error: err,
})
@ -143,11 +355,11 @@ func (bridge *Bridge) installUpdate(ctx context.Context, job installJob) {
log.Info("The update was installed successfully")
bridge.publish(events.UpdateInstalled{
Version: job.version,
Silent: job.silent,
Release: job.Release,
Silent: job.Silent,
})
bridge.newVersion = job.version.Version
bridge.newVersion = job.Release.Version
}
}, bridge.newVersionLock)
}

View File

@ -0,0 +1,700 @@
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Proton Mail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
package bridge_test
import (
"context"
"runtime"
"testing"
"time"
"github.com/Masterminds/semver/v3"
"github.com/ProtonMail/go-proton-api"
"github.com/ProtonMail/go-proton-api/server"
bridgePkg "github.com/ProtonMail/proton-bridge/v3/internal/bridge"
"github.com/ProtonMail/proton-bridge/v3/internal/events"
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
"github.com/ProtonMail/proton-bridge/v3/internal/updater/versioncompare"
"github.com/elastic/go-sysinfo/types"
"github.com/stretchr/testify/require"
)
// NOTE: we always assume the highest version is always the first in the release json array
func Test_Update_BetaEligible(t *testing.T) {
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridgePkg.Locator, vaultKey []byte) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridgePkg.Bridge, mocks *bridgePkg.Mocks) {
updateCh, done := bridge.GetEvents(events.UpdateInstalled{})
defer done()
err := bridge.SetUpdateChannel(updater.EarlyChannel)
require.NoError(t, err)
bridge.SetCurrentVersionTest(semver.MustParse("2.1.1"))
expectedRelease := updater.Release{
ReleaseCategory: updater.EarlyAccessReleaseCategory,
Version: semver.MustParse("2.1.2"),
SystemVersion: versioncompare.SystemVersion{},
RolloutProportion: 1.0,
MinAuto: &semver.Version{},
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
}
updaterData := updater.VersionInfo{Releases: []updater.Release{
expectedRelease,
}}
go func() {
time.Sleep(1 * time.Second)
mocks.Updater.SetLatestVersion(updaterData)
bridge.CheckForUpdates()
}()
select {
case update := <-updateCh:
require.Equal(t, events.UpdateInstalled{
Release: expectedRelease,
Silent: true,
}, update)
case <-time.After(2 * time.Second):
t.Fatal("timeout waiting for update")
}
})
})
}
func Test_Update_Stable(t *testing.T) {
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridgePkg.Locator, vaultKey []byte) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridgePkg.Bridge, mocks *bridgePkg.Mocks) {
updateCh, done := bridge.GetEvents(events.UpdateInstalled{})
defer done()
err := bridge.SetUpdateChannel(updater.StableChannel)
require.NoError(t, err)
bridge.SetCurrentVersionTest(semver.MustParse("2.1.1"))
expectedRelease := updater.Release{
ReleaseCategory: updater.StableReleaseCategory,
Version: semver.MustParse("2.1.3"),
SystemVersion: versioncompare.SystemVersion{},
RolloutProportion: 1.0,
MinAuto: &semver.Version{},
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
}
updaterData := updater.VersionInfo{Releases: []updater.Release{
{
ReleaseCategory: updater.EarlyAccessReleaseCategory,
Version: semver.MustParse("2.1.4"),
SystemVersion: versioncompare.SystemVersion{},
RolloutProportion: 1.0,
MinAuto: &semver.Version{},
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
},
expectedRelease,
}}
mocks.Updater.SetLatestVersion(updaterData)
bridge.CheckForUpdates()
require.Equal(t, events.UpdateInstalled{
Release: expectedRelease,
Silent: true,
}, <-updateCh)
})
})
}
func Test_Update_CurrentReleaseNewest(t *testing.T) {
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridgePkg.Locator, vaultKey []byte) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridgePkg.Bridge, mocks *bridgePkg.Mocks) {
updateCh, done := bridge.GetEvents(events.UpdateNotAvailable{})
defer done()
err := bridge.SetUpdateChannel(updater.StableChannel)
require.NoError(t, err)
bridge.SetCurrentVersionTest(semver.MustParse("2.1.5"))
expectedRelease := updater.Release{
ReleaseCategory: updater.StableReleaseCategory,
Version: semver.MustParse("2.1.3"),
SystemVersion: versioncompare.SystemVersion{},
RolloutProportion: 1.0,
MinAuto: &semver.Version{},
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
}
updaterData := updater.VersionInfo{Releases: []updater.Release{
{
ReleaseCategory: updater.EarlyAccessReleaseCategory,
Version: semver.MustParse("2.1.4"),
SystemVersion: versioncompare.SystemVersion{},
RolloutProportion: 1.0,
MinAuto: &semver.Version{},
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
},
expectedRelease,
}}
mocks.Updater.SetLatestVersion(updaterData)
bridge.CheckForUpdates()
require.Equal(t, events.UpdateNotAvailable{}, <-updateCh)
})
})
}
func Test_Update_NotRolledOutYet(t *testing.T) {
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridgePkg.Locator, vaultKey []byte) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridgePkg.Bridge, mocks *bridgePkg.Mocks) {
require.NoError(t, bridge.SetUpdateChannel(updater.EarlyChannel))
bridge.SetCurrentVersionTest(semver.MustParse("2.0.0"))
require.NoError(t, bridge.SetRolloutPercentageTest(1.0))
updaterData := updater.VersionInfo{Releases: []updater.Release{
{
ReleaseCategory: updater.StableReleaseCategory,
Version: semver.MustParse("2.1.5"),
SystemVersion: versioncompare.SystemVersion{},
RolloutProportion: 0.5,
MinAuto: &semver.Version{},
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
},
{
ReleaseCategory: updater.StableReleaseCategory,
Version: semver.MustParse("2.1.4"),
SystemVersion: versioncompare.SystemVersion{},
RolloutProportion: 0.5,
MinAuto: &semver.Version{},
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
},
}}
mocks.Updater.SetLatestVersion(updaterData)
updateCh, done := bridge.GetEvents(events.UpdateNotAvailable{})
defer done()
bridge.CheckForUpdates()
require.Equal(t, events.UpdateNotAvailable{}, <-updateCh)
})
})
}
func Test_Update_CheckOSVersion_NoUpdate(t *testing.T) {
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridgePkg.Locator, vaultKey []byte) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridgePkg.Bridge, mocks *bridgePkg.Mocks) {
require.NoError(t, bridge.SetAutoUpdate(true))
require.NoError(t, bridge.SetUpdateChannel(updater.StableChannel))
currentBridgeVersion := semver.MustParse("2.1.5")
bridge.SetCurrentVersionTest(currentBridgeVersion)
// Override the OS version check
bridge.SetHostVersionGetterTest(func(_ types.Host) string {
return "10.0.0"
})
updateNotAvailableCh, done := bridge.GetEvents(events.UpdateNotAvailable{})
defer done()
updateCh, updateChDone := bridge.GetEvents(events.UpdateInstalled{})
defer updateChDone()
expectedRelease := updater.Release{
ReleaseCategory: updater.StableReleaseCategory,
Version: semver.MustParse("2.4.0"),
SystemVersion: versioncompare.SystemVersion{
Minimum: "12.0.0",
Maximum: "13.0.0",
},
RolloutProportion: 1.0,
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
}
updaterData := updater.VersionInfo{Releases: []updater.Release{
expectedRelease,
{
ReleaseCategory: updater.StableReleaseCategory,
Version: semver.MustParse("2.3.0"),
SystemVersion: versioncompare.SystemVersion{
Minimum: "10.1.0",
Maximum: "11.5",
},
RolloutProportion: 1.0,
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
},
}}
mocks.Updater.SetLatestVersion(updaterData)
bridge.CheckForUpdates()
if runtime.GOOS == "darwin" {
require.Equal(t, events.UpdateNotAvailable{}, <-updateNotAvailableCh)
} else {
require.Equal(t, events.UpdateInstalled{
Release: expectedRelease,
Silent: true,
}, <-updateCh)
}
})
})
}
func Test_Update_CheckOSVersion_HasUpdate(t *testing.T) {
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridgePkg.Locator, vaultKey []byte) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridgePkg.Bridge, mocks *bridgePkg.Mocks) {
require.NoError(t, bridge.SetAutoUpdate(true))
require.NoError(t, bridge.SetUpdateChannel(updater.StableChannel))
updateCh, done := bridge.GetEvents(events.UpdateInstalled{})
defer done()
currentBridgeVersion := semver.MustParse("2.1.5")
bridge.SetCurrentVersionTest(currentBridgeVersion)
// Override the OS version check
bridge.SetHostVersionGetterTest(func(_ types.Host) string {
return "10.0.0"
})
expectedUpdateRelease := updater.Release{
ReleaseCategory: updater.StableReleaseCategory,
Version: semver.MustParse("2.2.0"),
SystemVersion: versioncompare.SystemVersion{
Minimum: "10.0.0",
Maximum: "10.1.12",
},
RolloutProportion: 1.0,
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
}
expectedUpdateReleaseWindowsLinux := updater.Release{
ReleaseCategory: updater.StableReleaseCategory,
Version: semver.MustParse("2.4.0"),
SystemVersion: versioncompare.SystemVersion{
Minimum: "12.0.0",
},
RolloutProportion: 1.0,
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
}
updaterData := updater.VersionInfo{Releases: []updater.Release{
expectedUpdateReleaseWindowsLinux,
{
ReleaseCategory: updater.StableReleaseCategory,
Version: semver.MustParse("2.3.0"),
SystemVersion: versioncompare.SystemVersion{
Minimum: "11.0.0",
},
RolloutProportion: 1.0,
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
},
expectedUpdateRelease,
{
ReleaseCategory: updater.StableReleaseCategory,
Version: semver.MustParse("2.1.0"),
SystemVersion: versioncompare.SystemVersion{},
RolloutProportion: 1.0,
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
},
}}
mocks.Updater.SetLatestVersion(updaterData)
bridge.CheckForUpdates()
if runtime.GOOS == "darwin" {
require.Equal(t, events.UpdateInstalled{
Release: expectedUpdateRelease,
Silent: true,
}, <-updateCh)
} else {
require.Equal(t, events.UpdateInstalled{
Release: expectedUpdateReleaseWindowsLinux,
Silent: true,
}, <-updateCh)
}
})
})
}
func Test_Update_UpdateFromMinVer_UpdateAvailable(t *testing.T) {
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridgePkg.Locator, vaultKey []byte) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridgePkg.Bridge, mocks *bridgePkg.Mocks) {
require.NoError(t, bridge.SetAutoUpdate(true))
require.NoError(t, bridge.SetUpdateChannel(updater.StableChannel))
currentBridgeVersion := semver.MustParse("2.1.5")
bridge.SetCurrentVersionTest(currentBridgeVersion)
updateCh, done := bridge.GetEvents(events.UpdateInstalled{})
defer done()
expectedUpdateRelease := updater.Release{
ReleaseCategory: updater.StableReleaseCategory,
Version: semver.MustParse("2.2.0"),
SystemVersion: versioncompare.SystemVersion{},
RolloutProportion: 1.0,
MinAuto: currentBridgeVersion,
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
}
updaterData := updater.VersionInfo{Releases: []updater.Release{
{
ReleaseCategory: updater.StableReleaseCategory,
Version: semver.MustParse("2.3.0"),
SystemVersion: versioncompare.SystemVersion{},
RolloutProportion: 1.0,
MinAuto: semver.MustParse("2.2.1"),
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
},
{
ReleaseCategory: updater.StableReleaseCategory,
Version: semver.MustParse("2.2.1"),
SystemVersion: versioncompare.SystemVersion{},
RolloutProportion: 1.0,
MinAuto: semver.MustParse("2.2.0"),
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
},
expectedUpdateRelease,
}}
mocks.Updater.SetLatestVersion(updaterData)
bridge.CheckForUpdates()
require.Equal(t, events.UpdateInstalled{
Release: expectedUpdateRelease,
Silent: true,
}, <-updateCh)
})
})
}
// Test_Update_UpdateFromMinVer_NoCompatibleVersionForceManual -
// if we have an update, but we don't satisfy minVersion, a manual update to the highest possible version should be performed.
func Test_Update_UpdateFromMinVer_NoCompatibleVersionForceManual(t *testing.T) {
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridgePkg.Locator, vaultKey []byte) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridgePkg.Bridge, mocks *bridgePkg.Mocks) {
require.NoError(t, bridge.SetAutoUpdate(true))
require.NoError(t, bridge.SetUpdateChannel(updater.StableChannel))
currentBridgeVersion := semver.MustParse("2.1.5")
bridge.SetCurrentVersionTest(currentBridgeVersion)
updateCh, done := bridge.GetEvents(events.UpdateAvailable{})
defer done()
expectedUpdateRelease := updater.Release{
ReleaseCategory: updater.StableReleaseCategory,
Version: semver.MustParse("2.3.0"),
SystemVersion: versioncompare.SystemVersion{},
RolloutProportion: 1.0,
MinAuto: semver.MustParse("2.2.1"),
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
}
updaterData := updater.VersionInfo{Releases: []updater.Release{
{
ReleaseCategory: updater.StableReleaseCategory,
Version: semver.MustParse("2.2.1"),
SystemVersion: versioncompare.SystemVersion{},
RolloutProportion: 1.0,
MinAuto: semver.MustParse("2.2.0"),
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
},
{
ReleaseCategory: updater.StableReleaseCategory,
Version: semver.MustParse("2.2.0"),
SystemVersion: versioncompare.SystemVersion{},
RolloutProportion: 1.0,
MinAuto: semver.MustParse("2.1.6"),
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
},
expectedUpdateRelease,
}}
mocks.Updater.SetLatestVersion(updaterData)
bridge.CheckForUpdates()
require.Equal(t, events.UpdateAvailable{
Release: expectedUpdateRelease,
Silent: false,
Compatible: false,
}, <-updateCh)
})
})
}
// Test_Update_UpdateFromMinVer_NoCompatibleVersionForceManual_BetaMismatch - only Beta updates are available
// nor do we satisfy the minVersion, we can't do anything in this case.
func Test_Update_UpdateFromMinVer_NoCompatibleVersionForceManual_BetaMismatch(t *testing.T) {
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridgePkg.Locator, vaultKey []byte) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(bridge *bridgePkg.Bridge, mocks *bridgePkg.Mocks) {
require.NoError(t, bridge.SetAutoUpdate(true))
require.NoError(t, bridge.SetUpdateChannel(updater.StableChannel))
currentBridgeVersion := semver.MustParse("2.1.5")
bridge.SetCurrentVersionTest(currentBridgeVersion)
updateCh, done := bridge.GetEvents(events.UpdateNotAvailable{})
defer done()
expectedUpdateRelease := updater.Release{
ReleaseCategory: updater.EarlyAccessReleaseCategory,
Version: semver.MustParse("2.3.0"),
SystemVersion: versioncompare.SystemVersion{},
RolloutProportion: 1.0,
MinAuto: semver.MustParse("2.2.1"),
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
}
updaterData := updater.VersionInfo{Releases: []updater.Release{
{
ReleaseCategory: updater.EarlyAccessReleaseCategory,
Version: semver.MustParse("2.2.1"),
SystemVersion: versioncompare.SystemVersion{},
RolloutProportion: 1.0,
MinAuto: semver.MustParse("2.2.0"),
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
},
{
ReleaseCategory: updater.EarlyAccessReleaseCategory,
Version: semver.MustParse("2.2.0"),
SystemVersion: versioncompare.SystemVersion{},
RolloutProportion: 1.0,
MinAuto: semver.MustParse("2.1.6"),
File: []updater.File{
{
URL: "RANDOM_INSTALLER_URL",
Identifier: updater.InstallerIdentifier,
},
{
URL: "RANDOM_PACKAGE_URL",
Identifier: updater.PackageIdentifier,
},
},
},
expectedUpdateRelease,
}}
mocks.Updater.SetLatestVersion(updaterData)
bridge.CheckForUpdates()
require.Equal(t, events.UpdateNotAvailable{}, <-updateCh)
})
})
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024 Proton AG
// Copyright (c) 2025 Proton AG
//
// This file is part of Proton Mail Bridge.
//

Some files were not shown because too many files have changed in this diff Show More