mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-10 12:46:46 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9eb4703d7a | |||
| 105752fc65 | |||
| 2747e93316 | |||
| 9548f984eb | |||
| cb871ce4bc | |||
| 8ca849b7a8 | |||
| 4bb29b1b5c | |||
| e55e893c94 | |||
| 5ab63a290e | |||
| 7c3414b86f | |||
| cec8829032 | |||
| 78f9f49a8a | |||
| 5a7722fd18 | |||
| d111a979f7 | |||
| 31514c8e31 | |||
| af5ce101ef | |||
| 075da27d13 | |||
| 7b19fb44a4 | |||
| c991946ea7 | |||
| f960a3ae38 | |||
| 73f8811a4b | |||
| bc6ec2579a | |||
| 35bc7263da | |||
| cc3db00a06 | |||
| 7f7961ae0c | |||
| aae60b2ef8 | |||
| ab700543b9 | |||
| 413488f5f4 |
30
Changelog.md
30
Changelog.md
@ -2,10 +2,36 @@
|
||||
|
||||
Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
||||
|
||||
## [Bridge 2.4.5] Osney
|
||||
|
||||
### Changed
|
||||
* GODT-2015: Bridge-gui logs to file until gRPC connection is established.
|
||||
* GODT-2016: Added more logging of gRPC events at info level.
|
||||
* GODT-2013: CLI flag for frontend is required.
|
||||
|
||||
### Fixed
|
||||
* GODT-2020: Fix xdg_{home,cache}_home variables.
|
||||
* GODT-2014: Bridge quit if gRPC client ends stream.
|
||||
|
||||
## [Bridge 2.4.4] Osney
|
||||
|
||||
### Changed
|
||||
* GODT-1751: Switch from protonmail.com to proton.me domain.
|
||||
|
||||
### Fixed
|
||||
* Other: Fix make run-cli for Darwin.
|
||||
* GODT-1645: Fix CI pipeline.
|
||||
* GODT-1938: Account details box values wrap.
|
||||
* Other: Also install vcpkg ARM64 on Intel mac hosts.
|
||||
* Other: Fix minor typo.
|
||||
* GODT-1939: removed vertical overshoot when scrolling.
|
||||
* GODT-1479: fix 'Open Bridge' button still hovered when status windows opens for Windows.
|
||||
* GODT-1519: Move back to account view after sending bug report.
|
||||
* Other: fix QML error with Qt 6.4 and a typo.
|
||||
|
||||
## [Bridge 2.4.3] Osney
|
||||
|
||||
## Changed
|
||||
### Changed
|
||||
* Other: implemented tokens in bridge-gui-tester.
|
||||
* GODT-1853:
|
||||
* Upgrade dependencies (including x/crypto).
|
||||
@ -28,7 +54,7 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
||||
|
||||
|
||||
|
||||
## Fixed
|
||||
### Fixed
|
||||
* GUI issues:
|
||||
* GODT-1894: Fixed typo in alreadyLoggedIn event error message.
|
||||
* GODT-1479: Fix hover on “Open Bridge” in status window on macOS.
|
||||
|
||||
13
Makefile
13
Makefile
@ -11,7 +11,7 @@ ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
.PHONY: build build-gui build-nogui build-launcher versioner hasher
|
||||
|
||||
# Keep version hardcoded so app build works also without Git repository.
|
||||
BRIDGE_APP_VERSION?=2.4.3+git
|
||||
BRIDGE_APP_VERSION?=2.4.5+git
|
||||
APP_VERSION:=${BRIDGE_APP_VERSION}
|
||||
APP_FULL_NAME:=Proton Mail Bridge
|
||||
APP_VENDOR:=Proton AG
|
||||
@ -80,6 +80,9 @@ build: build-gui
|
||||
build-gui: ${TGZ_TARGET}
|
||||
|
||||
build-nogui: ${EXE_NAME} build-launcher
|
||||
ifeq "${TARGET_OS}" "darwin"
|
||||
mv ${BRIDGE_EXE} ${BRIDGE_EXE_NAME}
|
||||
endif
|
||||
|
||||
go-build=go build $(1) -o $(2) $(3)
|
||||
go-build-finalize=${go-build}
|
||||
@ -254,6 +257,13 @@ lint-golang:
|
||||
$(info linting with GOMAXPROCS=${GOMAXPROCS})
|
||||
golangci-lint run ./...
|
||||
|
||||
gobinsec: gobinsec-cache.yml build
|
||||
gobinsec -wait -cache -config utils/gobinsec_conf.yml ${EXE_TARGET} ${DEPLOY_DIR}/${TARGET_OS}/${LAUNCHER_EXE}
|
||||
|
||||
gobinsec-cache.yml:
|
||||
./utils/gobinsec_update.sh
|
||||
cp ./utils/gobinsec_update/gobinsec-cache-valid.yml ./gobinsec-cache.yml
|
||||
|
||||
updates: install-go-mod-outdated
|
||||
# Uncomment the "-ci" to fail the job if something can be updated.
|
||||
go list -u -m -json all | go-mod-outdated -update -direct #-ci
|
||||
@ -313,6 +323,7 @@ clean: clean-vendor clean-gui clean-vcpkg
|
||||
rm -f ./*.syso
|
||||
rm -f release-notes/bridge.html
|
||||
rm -f release-notes/import-export.html
|
||||
rm -f ${LAUNCHER_EXE} ${BRIDGE_EXE} ${BRIDGE_EXE_NAME}
|
||||
|
||||
|
||||
.PHONY: generate
|
||||
|
||||
@ -22,7 +22,7 @@ to start Bridge on startup is enabled by default.
|
||||
When the main window is closed, Bridge will continue to run in the
|
||||
background.
|
||||
|
||||
More details [on the public website](https://protonmail.com/bridge).
|
||||
More details [on the public website](https://proton.me/mail/bridge).
|
||||
|
||||
## Launchers
|
||||
Launchers are binaries used to run the Proton Mail Bridge or Import-Export apps.
|
||||
|
||||
8
go.mod
8
go.mod
@ -50,9 +50,9 @@ require (
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5
|
||||
go.etcd.io/bbolt v1.3.6
|
||||
golang.org/x/exp v0.0.0-20220921164117-439092de6870
|
||||
golang.org/x/net v0.0.0-20220921203646-d300de134e69
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8
|
||||
golang.org/x/text v0.3.7
|
||||
golang.org/x/net v0.1.0
|
||||
golang.org/x/sys v0.1.0
|
||||
golang.org/x/text v0.4.0
|
||||
google.golang.org/grpc v1.49.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
howett.net/plist v1.0.0
|
||||
@ -93,7 +93,7 @@ require (
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 // indirect
|
||||
golang.org/x/crypto v0.1.0 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737 // indirect
|
||||
|
||||
16
go.sum
16
go.sum
@ -375,8 +375,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 h1:a5Yg6ylndHHYJqIPrdq0AhvR6KTvDTAvgBtaidhEevY=
|
||||
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
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=
|
||||
@ -424,8 +424,8 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220921203646-d300de134e69 h1:hUJpGDpnfwdJW8iNypFjmSY0sCBEL+spFTZ2eO+Sfps=
|
||||
golang.org/x/net v0.0.0-20220921203646-d300de134e69/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
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=
|
||||
@ -465,8 +465,8 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -474,8 +474,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5-0.20201125200606-c27b9fd57aec/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
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/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
||||
@ -69,12 +69,13 @@ const (
|
||||
flagMemProfileShort = "m"
|
||||
flagLogLevel = "log-level"
|
||||
flagLogLevelShort = "l"
|
||||
// FlagCLI indicate to start with command line interface.
|
||||
FlagCLI = "cli"
|
||||
flagCLIShort = "c"
|
||||
flagRestart = "restart"
|
||||
FlagLauncher = "launcher"
|
||||
FlagNoWindow = "no-window"
|
||||
FlagGRPC = "grpc" // FlagGRPC starts the gRPC frontend
|
||||
FlagGRPCShort = "g"
|
||||
FlagCLI = "cli" // FlagCLI indicate to start with command line interface.
|
||||
flagCLIShort = "c"
|
||||
flagRestart = "restart"
|
||||
FlagLauncher = "launcher"
|
||||
FlagNoWindow = "no-window"
|
||||
)
|
||||
|
||||
type Base struct {
|
||||
@ -299,6 +300,11 @@ func (b *Base) NewApp(mainLoop func(*Base, *cli.Context) error) *cli.App {
|
||||
Aliases: []string{flagLogLevelShort},
|
||||
Usage: "Set the log level (one of panic, fatal, error, warn, info, debug)",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: FlagGRPC,
|
||||
Aliases: []string{FlagGRPCShort},
|
||||
Usage: "Start the gRPC service",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: FlagCLI,
|
||||
Aliases: []string{flagCLIShort},
|
||||
|
||||
@ -40,10 +40,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
flagLogIMAP = "log-imap"
|
||||
flagLogSMTP = "log-smtp"
|
||||
flagNonInteractive = "noninteractive"
|
||||
|
||||
flagLogIMAP = "log-imap"
|
||||
flagLogSMTP = "log-smtp"
|
||||
flagNonInteractive = "noninteractive"
|
||||
flagNonInteractiveShort = "n"
|
||||
// Memory cache was estimated by empirical usage in the past, and it was set to 100MB.
|
||||
// NOTE: This value must not be less than maximal size of one email (~30MB).
|
||||
inMemoryCacheLimit = 100 * (1 << 20)
|
||||
@ -62,8 +62,9 @@ func New(base *base.Base) *cli.App {
|
||||
Usage: "Enable logging of SMTP communications (may contain decrypted data!)",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flagNonInteractive,
|
||||
Usage: "Start Bridge entirely non-interactively",
|
||||
Name: flagNonInteractive,
|
||||
Aliases: []string{flagNonInteractiveShort},
|
||||
Usage: "Start Bridge entirely non-interactively",
|
||||
},
|
||||
}...)
|
||||
|
||||
@ -72,6 +73,11 @@ func New(base *base.Base) *cli.App {
|
||||
|
||||
func main(b *base.Base, c *cli.Context) error { //nolint:funlen
|
||||
frontendType := getFrontendTypeFromCLIParams(c)
|
||||
if frontendType == frontend.Unknown {
|
||||
_ = cli.ShowAppHelp(c)
|
||||
return errors.New("no frontend was specified. Use --grpc, --cli or --noninteractive")
|
||||
}
|
||||
|
||||
f := frontend.New(
|
||||
frontendType,
|
||||
!c.Bool(base.FlagNoWindow),
|
||||
@ -171,12 +177,14 @@ func main(b *base.Base, c *cli.Context) error { //nolint:funlen
|
||||
|
||||
func getFrontendTypeFromCLIParams(c *cli.Context) frontend.Type {
|
||||
switch {
|
||||
case c.Bool(base.FlagGRPC):
|
||||
return frontend.GRPC
|
||||
case c.Bool(base.FlagCLI):
|
||||
return frontend.CLI
|
||||
case c.Bool(flagNonInteractive):
|
||||
return frontend.NonInteractive
|
||||
default:
|
||||
return frontend.GRPC
|
||||
return frontend.Unknown
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -43,7 +43,9 @@ void QMLBackend::init(GRPCConfig const &serviceConfig)
|
||||
{
|
||||
users_ = new UserList(this);
|
||||
|
||||
app().grpc().setLog(&app().log());
|
||||
Log& log = app().log();
|
||||
log.info(QString("Connecting to gRPC service"));
|
||||
app().grpc().setLog(&log);
|
||||
this->connectGrpcEvents();
|
||||
|
||||
QString error;
|
||||
|
||||
@ -54,7 +54,6 @@ BRIDGE_APP_FULL_NAME=${BRIDGE_APP_FULL_NAME:-"Proton Mail Bridge"}
|
||||
BRIDGE_VENDOR=${BRIDGE_VENDOR:-"Proton AG"}
|
||||
BUILD_CONFIG=${BRIDGE_GUI_BUILD_CONFIG:-Debug}
|
||||
BUILD_DIR=$(echo "./cmake-build-${BUILD_CONFIG}" | tr '[:upper:]' '[:lower:]')
|
||||
VCPKG_OSX_DEPLOYMENT_TARGET=11.0
|
||||
VCPKG_ROOT="${BRIDGE_REPO_ROOT}/extern/vcpkg"
|
||||
|
||||
git submodule update --init --recursive ${VCPKG_ROOT}
|
||||
@ -70,10 +69,8 @@ ${VCPKG_BOOTSTRAP} -disableMetrics
|
||||
check_exit "Failed to bootstrap vcpkg."
|
||||
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
if [[ "$(uname -m)" == "arm64" ]]; then
|
||||
${VCPKG_EXE} install grpc:arm64-osx-min-11-0 --overlay-triplets=vcpkg/triplets --clean-after-build
|
||||
check_exit "Failed installing gRPC for macOS / Apple Silicon"
|
||||
fi
|
||||
${VCPKG_EXE} install grpc:arm64-osx-min-11-0 --overlay-triplets=vcpkg/triplets --clean-after-build
|
||||
check_exit "Failed installing gRPC for macOS / Apple Silicon"
|
||||
${VCPKG_EXE} install grpc:x64-osx-min-11-0 --overlay-triplets=vcpkg/triplets --clean-after-build
|
||||
check_exit "Failed installing gRPC for macOS / Intel x64"
|
||||
elif [[ "$OSTYPE" == "linux"* ]]; then
|
||||
|
||||
@ -80,6 +80,18 @@ Log &initLog()
|
||||
{
|
||||
Log &log = app().log();
|
||||
log.registerAsQtMessageHandler();
|
||||
log.setEchoInConsole(true);
|
||||
|
||||
// remove old gui log files
|
||||
QDir const logsDir(userLogsDir());
|
||||
for (QFileInfo const fileInfo: logsDir.entryInfoList({ "gui_v*.log" }, QDir::Filter::Files)) // entryInfolist apparently only support wildcards, not regex.
|
||||
QFile(fileInfo.absoluteFilePath()).remove();
|
||||
|
||||
// create new GUI log file
|
||||
QString error;
|
||||
if (!log.startWritingToFile(logsDir.absoluteFilePath(QString("gui_v%1_%2.log").arg(PROJECT_VER).arg(QDateTime::currentSecsSinceEpoch())), &error))
|
||||
log.error(error);
|
||||
|
||||
return log;
|
||||
}
|
||||
|
||||
@ -200,7 +212,7 @@ void launchBridge(QStringList const &args)
|
||||
else
|
||||
app().log().debug(QString("Bridge executable path: %1").arg(QDir::toNativeSeparators(bridgeExePath)));
|
||||
|
||||
overseer = std::make_unique<Overseer>(new ProcessMonitor(bridgeExePath, args, nullptr), nullptr);
|
||||
overseer = std::make_unique<Overseer>(new ProcessMonitor(bridgeExePath, QStringList("--grpc") + args, nullptr), nullptr);
|
||||
overseer->startWorker(true);
|
||||
}
|
||||
|
||||
@ -258,7 +270,6 @@ int main(int argc, char *argv[])
|
||||
// In attached mode, we do not intercept stderr and stdout of bridge, as we did not launch it ourselves, so we output the log to the console.
|
||||
// When not in attached mode, log entries are forwarded to bridge, which output it on stdout/stderr. bridge-gui's process monitor intercept
|
||||
// these outputs and output them on the command-line.
|
||||
log.setEchoInConsole(attach);
|
||||
log.setLevel(logLevel);
|
||||
|
||||
if (!attach)
|
||||
@ -268,12 +279,17 @@ int main(int argc, char *argv[])
|
||||
launchBridge(args);
|
||||
}
|
||||
|
||||
|
||||
log.debug(QString("Server configuration file will be loaded from '%1'").arg(QDir::toNativeSeparators(grpcServerConfigPath())));
|
||||
log.info(QString("Retrieving gRPC service configuration from '%1'").arg(QDir::toNativeSeparators(grpcServerConfigPath())));
|
||||
app().backend().init(GRPCClient::waitAndRetrieveServiceConfig(attach ? 0 : grpcServiceConfigWaitDelayMs));
|
||||
if (!attach)
|
||||
GRPCClient::removeServiceConfigFile();
|
||||
log.debug("Backend was successfully initialized.");
|
||||
|
||||
// gRPC communication is established. From now on, log events will be sent to bridge via gRPC. bridge will write these to file,
|
||||
// and will output then on console if appropriate. If we are not running in attached mode we intercept bridge stdout & stderr and
|
||||
// display it in our own output and error, so we only continue to log directly to console if we are running in attached mode.
|
||||
log.setEchoInConsole(attach);
|
||||
log.info("Backend was successfully initialized.");
|
||||
log.stopWritingToFile();
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
std::unique_ptr<QQmlComponent> rootComponent(createRootQmlComponent(engine));
|
||||
|
||||
@ -43,6 +43,7 @@ Item {
|
||||
clip: true
|
||||
|
||||
anchors.fill: parent
|
||||
Component.onCompleted: contentItem.boundsBehavior = Flickable.StopAtBounds // Disable the springy effect when scroll reaches top/bottom.
|
||||
|
||||
Item {
|
||||
// can't use parent here because parent is not ScrollView (Flickable inside contentItem inside ScrollView)
|
||||
|
||||
@ -852,9 +852,9 @@ Window {
|
||||
property string version: "2.0.X-BridePreview"
|
||||
property url logsPath: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
|
||||
property url licensePath: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
|
||||
property url releaseNotesLink: Qt.resolvedUrl("https://protonmail.com/download/bridge/early_releases.html")
|
||||
property url releaseNotesLink: Qt.resolvedUrl("https://proton.me/download/bridge/early_releases.html")
|
||||
property url dependencyLicensesLink: Qt.resolvedUrl("https://github.com/ProtonMail/proton-bridge/v2/blob/master/COPYING_NOTES.md#dependencies")
|
||||
property url landingPageLink: Qt.resolvedUrl("https://protonmail.com/bridge")
|
||||
property url landingPageLink: Qt.resolvedUrl("https://proton.me/mail/bridge#download")
|
||||
|
||||
property string colorSchemeName: "light"
|
||||
function changeColorScheme(newScheme){
|
||||
|
||||
@ -28,6 +28,8 @@ SettingsView {
|
||||
|
||||
property var selectedAddress
|
||||
|
||||
signal bugReportWasSent()
|
||||
|
||||
Label {
|
||||
text: qsTr("Report a problem")
|
||||
colorScheme: root.colorScheme
|
||||
@ -61,7 +63,7 @@ SettingsView {
|
||||
}
|
||||
|
||||
onTextChanged: {
|
||||
// Rise max length error imidiatly while typing
|
||||
// Rise max length error immediately while typing
|
||||
if (description.text.length > description._maxLength) {
|
||||
validate()
|
||||
}
|
||||
@ -164,6 +166,7 @@ SettingsView {
|
||||
Connections {
|
||||
target: Backend
|
||||
function onReportBugFinished() { sendButton.loading = false }
|
||||
function onBugReportSendSuccess() { root.bugReportWasSent() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -53,6 +53,8 @@ Item {
|
||||
selectByMouse: true
|
||||
selectByKeyboard: true
|
||||
selectionColor: root.colorScheme.text_weak
|
||||
wrapMode: Text.WrapAnywhere
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -359,6 +359,10 @@ Item {
|
||||
onBack: {
|
||||
rightContent.showHelpView()
|
||||
}
|
||||
|
||||
onBugReportWasSent: {
|
||||
rightContent.showAccount()
|
||||
}
|
||||
}
|
||||
|
||||
function showAccount(index) {
|
||||
|
||||
@ -41,7 +41,7 @@ SettingsView {
|
||||
actionIcon: "/qml/icons/ic-external-link.svg"
|
||||
description: qsTr("Get help setting up your client with our instructions and FAQs.")
|
||||
type: SettingsItem.PrimaryButton
|
||||
onClicked: {Qt.openUrlExternally("https://protonmail.com/support/categories/bridge/")}
|
||||
onClicked: {Qt.openUrlExternally("https://proton.me/support/mail")}
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
@ -289,7 +289,7 @@ QtObject {
|
||||
|
||||
property Notification updateForceError: Notification {
|
||||
title: qsTr("Bridge coudn’t update")
|
||||
description: qsTr("You must update manually. Go to: https:/protonmail.com/bridge/download")
|
||||
description: qsTr("You must update manually. Go to: https://proton.me/mail/bridge#download")
|
||||
brief: title
|
||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||
type: Notification.NotificationType.Danger
|
||||
@ -997,7 +997,7 @@ QtObject {
|
||||
type: Notification.NotificationType.Danger
|
||||
group: Notifications.Group.Dialogs | Notifications.Group.Configuration
|
||||
|
||||
property var supportLink: "https://protonmail.com/support/knowledge-base/macos-keychain-corrupted"
|
||||
property var supportLink: "https://proton.me/support/mail"
|
||||
|
||||
|
||||
Connections {
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQuick
|
||||
import QtQml
|
||||
|
||||
QtObject {
|
||||
@ -22,7 +23,7 @@ QtObject {
|
||||
property var prominent
|
||||
|
||||
// Primary
|
||||
property color primay_norm
|
||||
property color primary_norm
|
||||
|
||||
// Interaction-norm
|
||||
property color interaction_norm
|
||||
|
||||
@ -30,7 +30,7 @@ QtObject {
|
||||
// https://doc.qt.io/qt-5/qtqml-documents-definetypes.html#inline-components
|
||||
|
||||
// component ColorScheme: QtObject {
|
||||
// property color primay_norm
|
||||
// property color primary_norm
|
||||
// ...
|
||||
// }
|
||||
|
||||
@ -40,7 +40,7 @@ QtObject {
|
||||
prominent: lightProminentStyle
|
||||
|
||||
// Primary
|
||||
primay_norm: "#6D4AFF"
|
||||
primary_norm: "#6D4AFF"
|
||||
|
||||
// Interaction-norm
|
||||
interaction_norm: "#6D4AFF"
|
||||
@ -115,7 +115,7 @@ QtObject {
|
||||
prominent: this
|
||||
|
||||
// Primary
|
||||
primay_norm: "#8A6EFF"
|
||||
primary_norm: "#8A6EFF"
|
||||
|
||||
// Interaction-norm
|
||||
interaction_norm: "#6D4AFF"
|
||||
@ -190,7 +190,7 @@ QtObject {
|
||||
prominent: darkProminentStyle
|
||||
|
||||
// Primary
|
||||
primay_norm: "#8A6EFF"
|
||||
primary_norm: "#8A6EFF"
|
||||
|
||||
// Interaction-norm
|
||||
interaction_norm: "#6D4AFF"
|
||||
@ -265,7 +265,7 @@ QtObject {
|
||||
prominent: this
|
||||
|
||||
// Primary
|
||||
primay_norm: "#8A6EFF"
|
||||
primary_norm: "#8A6EFF"
|
||||
|
||||
// Interaction-norm
|
||||
interaction_norm: "#6D4AFF"
|
||||
|
||||
@ -44,6 +44,7 @@ Item {
|
||||
clip: true
|
||||
|
||||
anchors.fill: parent
|
||||
Component.onCompleted: contentItem.boundsBehavior = Flickable.StopAtBounds // Disable the springy effect when scroll reaches top/bottom.
|
||||
|
||||
Item {
|
||||
// can't use parent here because parent is not ScrollView (Flickable inside contentItem inside ScrollView)
|
||||
|
||||
@ -42,7 +42,7 @@ Item {
|
||||
property string name : "Apple Mail"
|
||||
property string iconSource : "/qml/icons/ic-apple-mail.svg"
|
||||
property bool haveAutoSetup: true
|
||||
property string link: "https://protonmail.com/bridge/applemail"
|
||||
property string link: "https://proton.me/support/protonmail-bridge-clients-apple-mail"
|
||||
|
||||
Component.onCompleted : {
|
||||
if (Backend.goos == "darwin") {
|
||||
@ -50,13 +50,13 @@ Item {
|
||||
"name" : "Apple Mail",
|
||||
"iconSource" : "/qml/icons/ic-apple-mail.svg",
|
||||
"haveAutoSetup" : true,
|
||||
"link" : "https://protonmail.com/bridge/applemail"
|
||||
"link" : "https://proton.me/support/protonmail-bridge-clients-apple-mail"
|
||||
})
|
||||
append({
|
||||
"name" : "Microsoft Outlook",
|
||||
"iconSource" : "/qml/icons/ic-microsoft-outlook.svg",
|
||||
"haveAutoSetup" : false,
|
||||
"link" : "https://protonmail.com/bridge/outlook2019-mac"
|
||||
"link" : "https://proton.me/support/protonmail-bridge-clients-macos-outlook-2019"
|
||||
})
|
||||
}
|
||||
if (Backend.goos == "windows") {
|
||||
@ -64,7 +64,7 @@ Item {
|
||||
"name" : "Microsoft Outlook",
|
||||
"iconSource" : "/qml/icons/ic-microsoft-outlook.svg",
|
||||
"haveAutoSetup" : false,
|
||||
"link" : "https://protonmail.com/bridge/outlook2019"
|
||||
"link" : "https://proton.me/support/protonmail-bridge-clients-windows-outlook-2019"
|
||||
})
|
||||
}
|
||||
|
||||
@ -72,14 +72,14 @@ Item {
|
||||
"name" : "Mozilla Thunderbird",
|
||||
"iconSource" : "/qml/icons/ic-mozilla-thunderbird.svg",
|
||||
"haveAutoSetup" : false,
|
||||
"link" : "https://protonmail.com/bridge/thunderbird"
|
||||
"link" : "https://proton.me/support/protonmail-bridge-clients-windows-thunderbird"
|
||||
})
|
||||
|
||||
append({
|
||||
"name" : "Other",
|
||||
"iconSource" : "/qml/icons/ic-other-mail-clients.svg",
|
||||
"haveAutoSetup" : false,
|
||||
"link" : "https://protonmail.com/bridge/clients"
|
||||
"link" : "https://proton.me/support/protonmail-bridge-configure-client"
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@ -288,7 +288,7 @@ FocusScope {
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
textFormat: Text.StyledText
|
||||
text: link("https://protonmail.com/signup", qsTr("Create or upgrade your account"))
|
||||
text: link("https://proton.me/mail/pricing", qsTr("Create or upgrade your account"))
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: 24
|
||||
type: Label.LabelType.Body
|
||||
|
||||
@ -43,10 +43,23 @@ Window {
|
||||
signal showSignIn(string username)
|
||||
signal quit()
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
}
|
||||
|
||||
function enableHoverOnOpenBridgeButton() {
|
||||
openBridgeButton.hoverEnabled = true
|
||||
mouseArea.positionChanged.disconnect(enableHoverOnOpenBridgeButton)
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) { // GODT-1479 restore the hover-able status that may have been disabled when clicking on the 'Open Bridge' button.
|
||||
openBridgeButton.hoverEnabled = true
|
||||
if (visible) { // GODT-1479 To avoid a visual glitch where the 'Open bridge button' would appear hovered when the status windows opens,
|
||||
// we've disabled hover on it when it was last closed. Re-enabling hover here will not work on all platforms. so we temporarily connect
|
||||
// mouse move event over the window's mouseArea to a function that will re-enable hover on the open bridge button.
|
||||
openBridgeButton.focus = false
|
||||
mouseArea.positionChanged.connect(enableHoverOnOpenBridgeButton)
|
||||
} else {
|
||||
menu.close()
|
||||
}
|
||||
|
||||
@ -84,11 +84,13 @@ QString userConfigDir()
|
||||
dir += "/Library/Application Support";
|
||||
#else
|
||||
dir = qgetenv ("XDG_CONFIG_HOME");
|
||||
if (dir.isEmpty())
|
||||
dir = qgetenv ("HOME");
|
||||
if (dir.isEmpty())
|
||||
{
|
||||
dir = qgetenv ("HOME");
|
||||
if (dir.isEmpty())
|
||||
throw Exception("neither $XDG_CONFIG_HOME nor $HOME are defined");
|
||||
dir += "/.config";
|
||||
}
|
||||
#endif
|
||||
QString const folder = QDir(dir).absoluteFilePath(configFolder);
|
||||
QDir().mkpath(folder);
|
||||
@ -115,11 +117,13 @@ QString userCacheDir()
|
||||
dir += "/Library/Caches";
|
||||
#else
|
||||
dir = qgetenv ("XDG_CACHE_HOME");
|
||||
if (dir.isEmpty())
|
||||
dir = qgetenv ("HOME");
|
||||
if (dir.isEmpty())
|
||||
{
|
||||
dir = qgetenv ("HOME");
|
||||
if (dir.isEmpty())
|
||||
throw Exception("neither XDG_CACHE_HOME nor $HOME are defined");
|
||||
dir += "/.cache";
|
||||
}
|
||||
#endif
|
||||
|
||||
QString const folder = QDir(dir).absoluteFilePath(configFolder);
|
||||
@ -129,6 +133,17 @@ QString userCacheDir()
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \return user logs directory used by bridge.
|
||||
//****************************************************************************************************************************************************
|
||||
QString userLogsDir()
|
||||
{
|
||||
QString const path = QDir(userCacheDir()).absoluteFilePath("logs");
|
||||
QDir().mkpath(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \return The value GOOS would return for the current platform.
|
||||
//****************************************************************************************************************************************************
|
||||
|
||||
@ -29,6 +29,7 @@ namespace bridgepp
|
||||
|
||||
QString userConfigDir(); ///< Get the path of the user configuration folder.
|
||||
QString userCacheDir(); ///< Get the path of the user cache folder.
|
||||
QString userLogsDir(); ///< Get the path of the user logs folder.
|
||||
QString goos(); ///< return the value of Go's GOOS for the current platform ("darwin", "linux" and "windows" are supported).
|
||||
qint64 randN(qint64 n); ///< return a random integer in the half open range [0,n)
|
||||
QString randomFirstName(); ///< Get a random first name from a pre-determined list.
|
||||
|
||||
@ -123,7 +123,7 @@ bool GRPCClient::connectToServer(GRPCConfig const &config, QString &outError)
|
||||
int i = 0;
|
||||
while (true)
|
||||
{
|
||||
this->logDebug(QString("Connection to gRPC server at %1. attempt #%2").arg(address).arg(++i));
|
||||
this->logInfo(QString("Connection to gRPC server at %1. attempt #%2").arg(address).arg(++i));
|
||||
|
||||
if (channel_->WaitForConnected(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(grpcConnectionRetryDelayMs, GPR_TIMESPAN))))
|
||||
break; // connection established.
|
||||
@ -135,13 +135,13 @@ bool GRPCClient::connectToServer(GRPCConfig const &config, QString &outError)
|
||||
if (channel_->GetState(true) != GRPC_CHANNEL_READY)
|
||||
throw Exception("connection check failed.");
|
||||
|
||||
this->logDebug("Successfully connected to gRPC server.");
|
||||
this->logInfo("Successfully connected to gRPC server.");
|
||||
|
||||
QString const clientToken = QUuid::createUuid().toString();
|
||||
QString clientConfigPath = createClientConfigFile(clientToken);
|
||||
if (clientConfigPath.isEmpty())
|
||||
throw Exception("gRPC client config could not be saved.");
|
||||
this->logDebug(QString("Client config file was saved to '%1'").arg(QDir::toNativeSeparators(clientConfigPath)));
|
||||
this->logInfo(QString("Client config file was saved to '%1'").arg(QDir::toNativeSeparators(clientConfigPath)));
|
||||
|
||||
QString returnedClientToken;
|
||||
grpc::Status status = this->checkTokens(QDir::toNativeSeparators(clientConfigPath), returnedClientToken);
|
||||
@ -153,7 +153,7 @@ bool GRPCClient::connectToServer(GRPCConfig const &config, QString &outError)
|
||||
if (!status.ok())
|
||||
throw Exception(QString::fromStdString(status.error_message()));
|
||||
|
||||
log_->debug("gRPC token was validated");
|
||||
log_->info("gRPC token was validated");
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -933,6 +933,15 @@ void GRPCClient::logError(QString const &message)
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] message The event message.
|
||||
//****************************************************************************************************************************************************
|
||||
void GRPCClient::logInfo(QString const &message)
|
||||
{
|
||||
this->log(Log::Level::Info, message);
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] status The status
|
||||
/// \param[in] callName The call name.
|
||||
|
||||
@ -211,6 +211,8 @@ private:
|
||||
void logTrace(QString const &message); ///< Log a trace event.
|
||||
void logDebug(QString const &message); ///< Log a debug event.
|
||||
void logError(QString const &message); ///< Log an error event.
|
||||
void logInfo(QString const &message); ///< Log an info event.
|
||||
|
||||
grpc::Status logGRPCCallStatus(grpc::Status const &status, QString const &callName, QList<grpc::StatusCode> allowedErrors = {}); ///< Log the status of a gRPC code.
|
||||
grpc::Status simpleMethod(SimpleMethod method); ///< perform a gRPC call to a bool setter.
|
||||
grpc::Status setBool(BoolSetter setter, bool value); ///< perform a gRPC call to a bool setter.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
|
||||
#include "Log.h"
|
||||
#include "../Exception/Exception.h"
|
||||
|
||||
|
||||
namespace bridgepp
|
||||
@ -204,6 +205,35 @@ bool Log::echoInConsole() const
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] path The path of the file to write to.
|
||||
/// \param[out] outError if an error occurs and this pointer in not null, on exit it contains a description of the error.
|
||||
/// \return true if and only if the operation was successful.
|
||||
//****************************************************************************************************************************************************
|
||||
bool Log::startWritingToFile(QString const &path, QString *outError)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
file_ = std::make_unique<QFile>(path);
|
||||
if (file_->open(QIODevice::WriteOnly | QIODevice::Text))
|
||||
return true;
|
||||
|
||||
if (outError)
|
||||
*outError = QString("Could not open log file '%1' for writing.");
|
||||
file_.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
///
|
||||
//****************************************************************************************************************************************************
|
||||
void Log::stopWritingToFile()
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
file_.reset();
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] message The message.
|
||||
//****************************************************************************************************************************************************
|
||||
@ -278,12 +308,22 @@ void Log::addEntry(Log::Level level, QString const &message)
|
||||
return;
|
||||
emit entryAdded(level, message);
|
||||
|
||||
if (!(echoInConsole_ || file_))
|
||||
return;
|
||||
|
||||
QString const entryStr = logEntryToString(level, message) + "\n";
|
||||
if (echoInConsole_)
|
||||
{
|
||||
QTextStream &stream = (qint32(level) <= (qint32(Level::Warn))) ? stderr_ : stdout_;
|
||||
stream << logEntryToString(level, message) << "\n";
|
||||
stream << entryStr;
|
||||
stream.flush();
|
||||
}
|
||||
|
||||
if (file_)
|
||||
{
|
||||
file_->write(entryStr.toLocal8Bit());
|
||||
file_->flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -63,6 +63,8 @@ public: // member functions.
|
||||
Level level() const; ///< Get the log level.
|
||||
void setEchoInConsole(bool value); ///< Set if the log entries should be echoed in STDOUT/STDERR.
|
||||
bool echoInConsole() const; ///< Check if the log entries should be echoed in STDOUT/STDERR.
|
||||
bool startWritingToFile(QString const& path, QString *outError = nullptr); ///< Start writing the log to file. Concerns only future entries.
|
||||
void stopWritingToFile();
|
||||
void registerAsQtMessageHandler(); ///< Install the Qt message handler.
|
||||
|
||||
public slots:
|
||||
@ -83,7 +85,7 @@ private: // data members
|
||||
mutable QMutex mutex_; ///< The mutex.
|
||||
Level level_ { defaultLevel }; ///< The log level
|
||||
bool echoInConsole_ { false }; ///< Set if the log messages should be sent to STDOUT/STDERR.
|
||||
|
||||
std::unique_ptr<QFile> file_; ///< The file to write the log to.
|
||||
QTextStream stdout_; ///< The stdout stream.
|
||||
QTextStream stderr_; ///< The stderr stream.
|
||||
};
|
||||
|
||||
@ -25,6 +25,7 @@ import (
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/locations"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
||||
"github.com/ProtonMail/proton-bridge/v2/pkg/listener"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Type describes the available types of frontend.
|
||||
@ -34,6 +35,7 @@ const (
|
||||
CLI Type = iota
|
||||
GRPC
|
||||
NonInteractive
|
||||
Unknown
|
||||
)
|
||||
|
||||
type Frontend interface {
|
||||
@ -45,7 +47,7 @@ type Frontend interface {
|
||||
WaitUntilFrontendIsReady()
|
||||
}
|
||||
|
||||
// New returns initialized frontend based on `frontendType`, which can be `CLI` or `GRPC`.
|
||||
// New returns initialized frontend based on `frontendType`, which can be `CLI` or `GRPC`. Non-interactive will return a nil frontend.
|
||||
func New(
|
||||
frontendType Type,
|
||||
showWindowOnStart bool,
|
||||
@ -75,9 +77,13 @@ func New(
|
||||
)
|
||||
|
||||
case NonInteractive:
|
||||
return nil
|
||||
|
||||
case Unknown:
|
||||
fallthrough
|
||||
|
||||
default:
|
||||
return nil
|
||||
logrus.Panicf("Unexpected frontend value %v", frontendType)
|
||||
return nil // return statement is required by compiler, although the above call will panic.
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,12 +59,13 @@ const (
|
||||
// Service is the RPC service struct.
|
||||
type Service struct { // nolint:structcheck
|
||||
UnimplementedBridgeServer
|
||||
grpcServer *grpc.Server // the gGRPC server
|
||||
listener net.Listener
|
||||
eventStreamCh chan *StreamEvent
|
||||
eventStreamDoneCh chan struct{}
|
||||
eventQueue []*StreamEvent
|
||||
eventQueueMutex sync.Mutex
|
||||
grpcServer *grpc.Server // the gGRPC server
|
||||
listener net.Listener
|
||||
eventStreamCh chan *StreamEvent
|
||||
eventStreamChMutex sync.RWMutex
|
||||
eventStreamDoneCh chan struct{}
|
||||
eventQueue []*StreamEvent
|
||||
eventQueueMutex sync.Mutex
|
||||
|
||||
panicHandler types.PanicHandler
|
||||
eventListener listener.Listener
|
||||
@ -124,6 +125,7 @@ func NewService(
|
||||
}
|
||||
|
||||
func (s *Service) startGRPCServer() {
|
||||
s.log.Info("Starting gRPC server")
|
||||
tlsConfig, pemCert, err := s.generateTLSConfig()
|
||||
if err != nil {
|
||||
s.log.WithError(err).Panic("Could not generate gRPC TLS config")
|
||||
@ -147,7 +149,7 @@ func (s *Service) startGRPCServer() {
|
||||
if path, err := s.saveGRPCServerConfigFile(); err != nil {
|
||||
s.log.WithError(err).WithField("path", path).Panic("Could not write gRPC service config file")
|
||||
} else {
|
||||
s.log.WithField("path", path).Debug("Successfully saved gRPC service config file")
|
||||
s.log.WithField("path", path).Info("Successfully saved gRPC service config file")
|
||||
}
|
||||
|
||||
s.log.Info("gRPC server listening at ", s.listener.Addr())
|
||||
|
||||
@ -95,12 +95,15 @@ func (s *Service) GuiReady(ctx context.Context, _ *emptypb.Empty) (*emptypb.Empt
|
||||
// Quit implement the Quit gRPC service call.
|
||||
func (s *Service) Quit(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
s.log.Debug("Quit")
|
||||
return &emptypb.Empty{}, s.quit()
|
||||
}
|
||||
|
||||
func (s *Service) quit() error {
|
||||
// Windows is notably slow at Quitting. We do it in a goroutine to speed things up a bit.
|
||||
go func() {
|
||||
var err error
|
||||
if s.eventStreamCh != nil {
|
||||
if _, err = s.StopEventStream(ctx, empty); err != nil {
|
||||
if s.isStreamingEvents() {
|
||||
if err = s.stopEventStream(); err != nil {
|
||||
s.log.WithError(err).Error("Quit failed.")
|
||||
}
|
||||
}
|
||||
@ -109,7 +112,7 @@ func (s *Service) Quit(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empt
|
||||
s.grpcServer.GracefulStop()
|
||||
}()
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Restart implement the Restart gRPC service call.
|
||||
|
||||
@ -29,19 +29,19 @@ import (
|
||||
func (s *Service) RunEventStream(request *EventStreamRequest, server Bridge_RunEventStreamServer) error {
|
||||
s.log.Debug("Starting Event stream")
|
||||
|
||||
if s.eventStreamCh != nil {
|
||||
if s.isStreamingEvents() {
|
||||
return status.Errorf(codes.AlreadyExists, "the service is already streaming") // TO-DO GODT-1667 decide if we want to kill the existing stream.
|
||||
}
|
||||
|
||||
s.bridge.SetCurrentPlatform(request.ClientPlatform)
|
||||
|
||||
s.eventStreamCh = make(chan *StreamEvent)
|
||||
s.createEventStreamChannel()
|
||||
s.eventStreamDoneCh = make(chan struct{})
|
||||
|
||||
// TO-DO GODT-1667 We should have a safer we to close this channel? What if an event occur while we are closing?
|
||||
defer func() {
|
||||
close(s.eventStreamCh)
|
||||
s.eventStreamCh = nil
|
||||
s.deleteEventStreamChannel()
|
||||
close(s.eventStreamDoneCh)
|
||||
s.eventStreamDoneCh = nil
|
||||
}()
|
||||
@ -70,24 +70,34 @@ func (s *Service) RunEventStream(request *EventStreamRequest, server Bridge_RunE
|
||||
s.log.Debug("Stop Event stream")
|
||||
return err
|
||||
}
|
||||
case <-server.Context().Done():
|
||||
s.log.Debug("Client closed the stream, exiting")
|
||||
return s.quit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StopEventStream stops the event stream.
|
||||
func (s *Service) StopEventStream(ctx context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
func (s *Service) StopEventStream(_ context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
return &emptypb.Empty{}, s.stopEventStream()
|
||||
}
|
||||
|
||||
func (s *Service) stopEventStream() error {
|
||||
s.eventStreamChMutex.RLock()
|
||||
defer s.eventStreamChMutex.RUnlock()
|
||||
|
||||
if s.eventStreamCh == nil {
|
||||
return nil, status.Errorf(codes.NotFound, "The service is not streaming")
|
||||
return status.Errorf(codes.NotFound, "The service is not streaming")
|
||||
}
|
||||
|
||||
s.eventStreamDoneCh <- struct{}{}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendEvent sends an event to the via the gRPC event stream.
|
||||
func (s *Service) SendEvent(event *StreamEvent) error {
|
||||
if s.eventStreamCh == nil { // nobody is connected to the event stream, we queue events
|
||||
if !s.isStreamingEvents() { // nobody is connected to the event stream, we queue events
|
||||
s.queueEvent(event)
|
||||
return nil
|
||||
}
|
||||
@ -174,3 +184,24 @@ func (s *Service) queueEvent(event *StreamEvent) {
|
||||
s.eventQueue = append(s.eventQueue, event)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) isStreamingEvents() bool {
|
||||
s.eventStreamChMutex.RLock()
|
||||
defer s.eventStreamChMutex.RUnlock()
|
||||
|
||||
return s.eventStreamCh != nil
|
||||
}
|
||||
|
||||
func (s *Service) createEventStreamChannel() {
|
||||
s.eventStreamChMutex.Lock()
|
||||
defer s.eventStreamChMutex.Unlock()
|
||||
|
||||
s.eventStreamCh = make(chan *StreamEvent)
|
||||
}
|
||||
|
||||
func (s *Service) deleteEventStreamChannel() {
|
||||
s.eventStreamChMutex.Lock()
|
||||
defer s.eventStreamChMutex.Unlock()
|
||||
|
||||
s.eventStreamCh = nil
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@ func newGoIMAPServer(tls *tls.Config, backend backend.Backend, address string, u
|
||||
serverID := imapid.ID{
|
||||
imapid.FieldName: "Proton Mail Bridge",
|
||||
imapid.FieldVendor: "Proton AG",
|
||||
imapid.FieldSupportURL: "https://protonmail.com/support",
|
||||
imapid.FieldSupportURL: "https://proton.me/support/mail",
|
||||
}
|
||||
|
||||
server.EnableAuth(sasl.Login, func(conn imapserver.Conn) sasl.Server {
|
||||
|
||||
@ -91,7 +91,7 @@ func (c *controller) ListenAndServe() {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
l.WithError(err).Error("Cannot start listner.")
|
||||
l.WithError(err).Error("Cannot start listener.")
|
||||
c.signals.Emit(events.ErrorEvent, string(c.server.Protocol())+" failed: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@ -153,7 +153,7 @@ func (q *sendRecorder) deleteExpiredKeys() {
|
||||
for key, value := range q.hashes {
|
||||
// It's hard to find a good expiration time.
|
||||
// On the one hand, a user could set up some cron job sending the same message over and over again (heartbeat).
|
||||
// On the the other, a user could put the device into sleep mode while sending.
|
||||
// On the other, a user could put the device into sleep mode while sending.
|
||||
// Changing the expiration time will always make one of the edge cases worse.
|
||||
// But both edge cases are something we don't care much about. Important thing is we don't send the same message many times.
|
||||
if time.Since(value.time) > 30*time.Minute {
|
||||
|
||||
@ -245,7 +245,7 @@ func (store *Store) createOrUpdateMessageEvent(msg *pmapi.Message) error {
|
||||
func (store *Store) createOrUpdateMessagesEvent(msgs []*pmapi.Message) error { //nolint:funlen
|
||||
store.log.WithField("msgs", msgs).Trace("Creating or updating messages in the store")
|
||||
|
||||
// Strip non meta first to reduce memory (no need to keep all old msg ID data during update).
|
||||
// Strip non-meta first to reduce memory (no need to keep all old msg ID data during update).
|
||||
err := store.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket(metadataBucket)
|
||||
for _, msg := range msgs {
|
||||
@ -321,7 +321,7 @@ func clearNonMetadata(onlyMeta *pmapi.Message) {
|
||||
onlyMeta.Attachments = nil
|
||||
}
|
||||
|
||||
// txUpdateMetadataFromDB changes the the onlyMeta data.
|
||||
// txUpdateMetadataFromDB changes the onlyMeta data.
|
||||
// If there is stored message in metaBucket the size, header and MIMEType are
|
||||
// not changed if already set. To change these:
|
||||
// * size must be updated by Message.SetSize
|
||||
|
||||
@ -20,4 +20,4 @@
|
||||
|
||||
package updater
|
||||
|
||||
const Host = "https://protonmail.com/download"
|
||||
const Host = "https://proton.me/download"
|
||||
|
||||
@ -38,7 +38,7 @@ type VersionInfo struct {
|
||||
// Installers are the locations of installer files (for manual installation).
|
||||
Installers []string
|
||||
|
||||
// LandingPage is the address of the app landing page on protonmail.com.
|
||||
// LandingPage is the address of the app landing page on proton.me
|
||||
LandingPage string
|
||||
|
||||
// ReleaseNotesPage is the address of the page containing the release notes.
|
||||
@ -54,26 +54,26 @@ type VersionInfo struct {
|
||||
// {
|
||||
// "stable": {
|
||||
// "Version": "2.3.4",
|
||||
// "Package": "https://protonmail.com/.../bridge_2.3.4_linux.tgz",
|
||||
// "Package": "https://proton.me/.../bridge_2.3.4_linux.tgz",
|
||||
// "Installers": [
|
||||
// "https://protonmail.com/.../something.deb",
|
||||
// "https://protonmail.com/.../something.rpm",
|
||||
// "https://protonmail.com/.../PKGBUILD"
|
||||
// "https://proton.me/.../something.deb",
|
||||
// "https://proton.me/.../something.rpm",
|
||||
// "https://proton.me/.../PKGBUILD"
|
||||
// ],
|
||||
// "LandingPage": "https://protonmail.com/bridge",
|
||||
// "ReleaseNotesPage": "https://protonmail.com/.../release_notes.html",
|
||||
// "LandingPage": "https://proton.me/mail/bridge#download",
|
||||
// "ReleaseNotesPage": "https://proton.me/download/{ie,bridge}/{stable,early}_releases.html",
|
||||
// "RolloutProportion": 0.5
|
||||
// },
|
||||
// "early": {
|
||||
// "Version": "2.4.0",
|
||||
// "Package": "https://protonmail.com/.../bridge_2.4.0_linux.tgz",
|
||||
// "Package": "https://proton.me/.../bridge_2.4.0_linux.tgz",
|
||||
// "Installers": [
|
||||
// "https://protonmail.com/.../something.deb",
|
||||
// "https://protonmail.com/.../something.rpm",
|
||||
// "https://protonmail.com/.../PKGBUILD"
|
||||
// "https://proton.me/.../something.deb",
|
||||
// "https://proton.me/.../something.rpm",
|
||||
// "https://proton.me/.../PKGBUILD"
|
||||
// ],
|
||||
// "LandingPage": "https://protonmail.com/bridge",
|
||||
// "ReleaseNotesPage": "https://protonmail.com/.../release_notes.html",
|
||||
// "LandingPage": "https://proton.me/mail/bridge#download",
|
||||
// "ReleaseNotesPage": "https://proton.me/download/{ie,bridge}/{stable,early}_releases.html",
|
||||
// "RolloutProportion": 0.5
|
||||
// },
|
||||
// "...": {
|
||||
@ -84,8 +84,8 @@ type VersionMap map[string]VersionInfo
|
||||
|
||||
// getVersionFileURL returns the URL of the version file.
|
||||
// For example:
|
||||
// - https://protonmail.com/download/bridge/version_linux.json
|
||||
// - https://protonmail.com/download/ie/version_linux.json
|
||||
// - https://proton.me/download/bridge/version_linux.json
|
||||
// - https://proton.me/download/ie/version_linux.json
|
||||
func (u *Updater) getVersionFileURL() string {
|
||||
return fmt.Sprintf("%v/%v/version_%v.json", Host, u.updateURLName, u.platform)
|
||||
}
|
||||
|
||||
@ -33,10 +33,16 @@ var TrustedAPIPins = []string{ //nolint:gochecknoglobals
|
||||
`pin-sha256="AfMENBVvOS8MnISprtvyPsjKlPooqh8nMB/pvCrpJpw="`, // cold backup
|
||||
|
||||
// protonmail.com
|
||||
// \todo remove when sure no one is using it.
|
||||
`pin-sha256="8joiNBdqaYiQpKskgtkJsqRxF7zN0C0aqfi8DacknnI="`, // current
|
||||
`pin-sha256="JMI8yrbc6jB1FYGyyWRLFTmDNgIszrNEMGlgy972e7w="`, // hot backup
|
||||
`pin-sha256="Iu44zU84EOCZ9vx/vz67/MRVrxF1IO4i4NIa8ETwiIY="`, // cold backup
|
||||
|
||||
// proton.me
|
||||
`pin-sha256="CT56BhOTmj5ZIPgb/xD5mH8rY3BLo/MlhP7oPyJUEDo="`, // current
|
||||
`pin-sha256="35Dx28/uzN3LeltkCBQ8RHK0tlNSa2kCpCRGNp34Gxc="`, // hot backup
|
||||
`pin-sha256="qYIukVc63DEITct8sFT7ebIq5qsWmuscaIKeJx+5J5A="`, // col backup
|
||||
|
||||
// proxies
|
||||
`pin-sha256="EU6TS9MO0L/GsDHvVc9D5fChYLNy5JdGYpJw0ccgetM="`, // main
|
||||
`pin-sha256="iKPIHPnDNqdkvOnTClQ8zQAIKG0XavaPkcEo0LBAABA="`, // backup 1
|
||||
|
||||
@ -88,7 +88,7 @@ func TestTLSSignedCertTrustedPublicKey(t *testing.T) {
|
||||
|
||||
_, dialer, _ := createClientWithPinningDialer("")
|
||||
copyTrustedPins(dialer.pinChecker)
|
||||
dialer.pinChecker.trustedPins = append(dialer.pinChecker.trustedPins, `pin-sha256="SA4v9d2YY4vX5YQOQ1qZHYTBMCTSD/sxPvyj+JL6+vI="`)
|
||||
dialer.pinChecker.trustedPins = append(dialer.pinChecker.trustedPins, `pin-sha256="LwnIKjNLV3z243ap8y0yXNPghsqE76J08Eq3COvUt2E="`)
|
||||
_, err := dialer.DialTLS("tcp", "rsa4096.badssl.com:443")
|
||||
r.NoError(t, err, "expected dial to succeed because public key is known and cert is signed by CA")
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ func newManager(cfg Config) *manager {
|
||||
// The resty is increasing the delay between retries up to 1 minute
|
||||
// (SetRetryMaxWaitTime) so for 10 retries the cumulative delay can be
|
||||
// up to 5min.
|
||||
m.rc.SetRetryCount(5)
|
||||
m.rc.SetRetryCount(3)
|
||||
m.rc.SetRetryMaxWaitTime(time.Minute)
|
||||
m.rc.SetRetryAfter(catchRetryAfter)
|
||||
m.rc.AddRetryCondition(m.shouldRetry)
|
||||
|
||||
@ -62,6 +62,10 @@ func (m *manager) NewClientWithLogin(ctx context.Context, username string, passw
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Do not retry requests after this point. The ephemeral from auth info
|
||||
// won't be valid any more
|
||||
ctx = ContextWithoutRetry(ctx)
|
||||
|
||||
auth, err := m.auth(ctx, AuthReq{
|
||||
Username: username,
|
||||
ClientProof: base64.StdEncoding.EncodeToString(proofs.ClientProof),
|
||||
|
||||
@ -105,17 +105,27 @@ func logConnReuse(_ *resty.Client, res *resty.Response) error {
|
||||
func catchRetryAfter(_ *resty.Client, res *resty.Response) (time.Duration, error) {
|
||||
if res.StatusCode() == http.StatusTooManyRequests {
|
||||
if after := res.Header().Get("Retry-After"); after != "" {
|
||||
l := log.
|
||||
WithField("statusCode", res.StatusCode()).
|
||||
WithField("url", res.Request.URL).
|
||||
WithField("verb", res.Request.Method)
|
||||
|
||||
seconds, err := strconv.Atoi(after)
|
||||
if err != nil {
|
||||
log.WithError(err).Warning("Cannot convert Retry-After to number")
|
||||
l.WithError(err).Warning("Cannot convert Retry-After to number")
|
||||
seconds = 10
|
||||
}
|
||||
|
||||
// To avoid spikes when all clients retry at the same time, we add some random wait.
|
||||
seconds += rand.Intn(10) //nolint:gosec // It is OK to use weak random number generator here.
|
||||
l = l.WithField("seconds", seconds).WithField("start", time.Now().Unix())
|
||||
|
||||
log.Warningf("Retrying %s after %ds induced by http code %d", res.Request.URL, seconds, res.StatusCode())
|
||||
return time.Duration(seconds) * time.Second, nil
|
||||
// Maximum retry time in client is is one minute. But
|
||||
// here wait times can be longer e.g. high API load
|
||||
l.Warn("Retrying after induced by http code. Waiting now...")
|
||||
time.Sleep(time.Duration(seconds) * time.Second)
|
||||
l.Warn("Wait done")
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
.PHONY: check-go check-godog install-godog test test-bridge test-live test-live-bridge test-stage test-debug test-live-debug bench
|
||||
|
||||
export GO111MODULE=on
|
||||
export BRIDGE_VERSION:=2.4.3+integrationtests
|
||||
export BRIDGE_VERSION:=2.4.5+integrationtests
|
||||
export VERBOSITY?=fatal
|
||||
export TEST_DATA=testdata
|
||||
|
||||
@ -14,6 +14,7 @@ check-godog:
|
||||
@which godog || $(MAKE) install-godog
|
||||
install-godog: check-go
|
||||
go install github.com/cucumber/godog/cmd/godog@v0.12.5
|
||||
go install github.com/cucumber/godog/cmd/godog@upd-go1.18
|
||||
|
||||
test: test-bridge
|
||||
test-bridge: FEATURES ?= features
|
||||
@ -25,7 +26,11 @@ test-bridge: check-godog
|
||||
test-live: test-live-bridge test-live-ie
|
||||
test-live-bridge: FEATURES ?= features
|
||||
test-live-bridge: check-godog
|
||||
TEST_ENV=live godog --tags="~@ignore && ~@ignore-live" $(FEATURES)
|
||||
TEST_ENV=live godog --tags="~@ignore && ~@ignore-live && ~@ignore-live-auth" $(FEATURES)
|
||||
|
||||
test-live-bridge-auth: check-godog
|
||||
TEST_ENV=live godog --tags="@ignore-live-auth" $(FEATURES)
|
||||
|
||||
|
||||
# Doesn't work in parallel!
|
||||
# Provide TEST_ACCOUNTS with your accounts.
|
||||
|
||||
@ -21,6 +21,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
@ -29,6 +30,7 @@ import (
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/users"
|
||||
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -57,9 +59,15 @@ func (ctx *TestContext) LoginUser(username string, password, mailboxPassword []b
|
||||
return errors.Wrap(err, "failed to finish login")
|
||||
}
|
||||
|
||||
ctx.addCleanupChecked(func() error {
|
||||
return ctx.bridge.LogoutUser(userID)
|
||||
}, "Logging out user")
|
||||
ctx.addCleanupChecked(
|
||||
func() error {
|
||||
if os.Getenv(EnvName) == EnvLive {
|
||||
logrus.Warn("Pausing user.Logout by 2 minutes to not hit issues with too many login attempts.")
|
||||
time.Sleep(2 * time.Minute)
|
||||
}
|
||||
return ctx.bridge.LogoutUser(userID)
|
||||
}, "Logging out user",
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -9,11 +9,13 @@ Feature: IMAP auth
|
||||
When IMAP client authenticates "user" with bad password
|
||||
Then IMAP response is "IMAP error: NO backend/credentials: incorrect password"
|
||||
|
||||
@ignore-live-auth
|
||||
Scenario: Authenticates with disconnected user
|
||||
Given there is disconnected user "user"
|
||||
When IMAP client authenticates "user"
|
||||
Then IMAP response is "IMAP error: NO account is logged out, use the app to login again"
|
||||
|
||||
@ignore-live-auth
|
||||
Scenario: Authenticates with connected user that was loaded without internet
|
||||
Given there is user "user" which just logged in
|
||||
And there is no internet connection
|
||||
@ -27,12 +29,14 @@ Feature: IMAP auth
|
||||
And 2 seconds pass
|
||||
Then "user" is connected
|
||||
|
||||
@ignore-live-auth
|
||||
Scenario: Authenticates with freshly logged-out user
|
||||
Given there is user "user" which just logged in
|
||||
When "user" logs out
|
||||
And IMAP client authenticates "user"
|
||||
Then IMAP response is "IMAP error: NO account is logged out, use the app to login again"
|
||||
|
||||
@ignore-live-auth
|
||||
Scenario: Authenticates user which was re-logged in
|
||||
Given there is user "user" which just logged in
|
||||
When "user" logs out
|
||||
|
||||
@ -125,6 +125,7 @@ Feature: IMAP fetch messages
|
||||
| 1:* |
|
||||
| * |
|
||||
|
||||
@ignore-live
|
||||
Scenario: Fetch of big mailbox
|
||||
Given there are 100 messages in mailbox "Folders/mbox" for "user"
|
||||
And there is IMAP client logged in as "user"
|
||||
|
||||
@ -1,34 +1,43 @@
|
||||
Feature: Delete user
|
||||
@ignore-live-auth
|
||||
Scenario: Deleting connected user
|
||||
Given there is user "user" which just logged in
|
||||
When user deletes "user"
|
||||
Then last response is "OK"
|
||||
And "user" has database file
|
||||
|
||||
@ignore-live-auth
|
||||
Scenario: Deleting connected user with cache
|
||||
Given there is user "user" which just logged in
|
||||
When user deletes "user" with cache
|
||||
Then last response is "OK"
|
||||
And "user" does not have database file
|
||||
|
||||
@ignore-live-auth
|
||||
Scenario: Deleting connected user without database file
|
||||
Given there is user "user" which just logged in
|
||||
And there is no database file for "user"
|
||||
When user deletes "user" with cache
|
||||
Then last response is "OK"
|
||||
|
||||
# THIS IS BLOCKED BY ANTI-ABUSE
|
||||
@ignore-live
|
||||
Scenario: Deleting disconnected user
|
||||
Given there is disconnected user "user"
|
||||
When user deletes "user"
|
||||
Then last response is "OK"
|
||||
And "user" has database file
|
||||
|
||||
# THIS IS BLOCKED BY ANTI-ABUSE
|
||||
@ignore-live
|
||||
Scenario: Deleting disconnected user with cache
|
||||
Given there is disconnected user "user"
|
||||
When user deletes "user" with cache
|
||||
Then last response is "OK"
|
||||
And "user" does not have database file
|
||||
|
||||
# THIS IS BLOCKED BY ANTI-ABUSE
|
||||
@ignore-live
|
||||
Scenario: Deleting disconnected user without database file
|
||||
Given there is disconnected user "user"
|
||||
And there is no database file for "user"
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
Feature: Login for the first time
|
||||
@ignore-live-auth
|
||||
Scenario: Normal login
|
||||
Given there is user "user"
|
||||
When "user" logs in
|
||||
@ -19,6 +20,7 @@ Feature: Login for the first time
|
||||
When "user" logs in with bad password
|
||||
Then last response is "failed to login: Incorrect login credentials. Please try again"
|
||||
|
||||
@ignore-live-auth
|
||||
Scenario: Login without internet connection
|
||||
Given there is no internet connection
|
||||
When "user" logs in
|
||||
@ -34,6 +36,7 @@ Feature: Login for the first time
|
||||
And "user2fa" has running event loop
|
||||
And "user2fa" has non-zero space
|
||||
|
||||
@ignore-live-auth
|
||||
Scenario: Login user with capital letters in address
|
||||
Given there is user "userAddressWithCapitalLetter"
|
||||
When "userAddressWithCapitalLetter" logs in
|
||||
@ -43,6 +46,7 @@ Feature: Login for the first time
|
||||
And "userAddressWithCapitalLetter" has running event loop
|
||||
And "userAddressWithCapitalLetter" has non-zero space
|
||||
|
||||
@ignore-live-auth
|
||||
Scenario: Login user with more addresses
|
||||
Given there is user "userMoreAddresses"
|
||||
When "userMoreAddresses" logs in
|
||||
@ -62,6 +66,7 @@ Feature: Login for the first time
|
||||
And "userDisabledPrimaryAddress" has running event loop
|
||||
And "userDisabledPrimaryAddress" has non-zero space
|
||||
|
||||
@ignore-live-auth
|
||||
Scenario: Login two users
|
||||
Given there is user "user"
|
||||
And there is user "userMoreAddresses"
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
Feature: Re-login
|
||||
@ignore-live-auth
|
||||
Scenario: Re-login with connected user and database file
|
||||
Given there is user "user" which just logged in
|
||||
And there is database file for "user"
|
||||
@ -18,6 +19,7 @@ Feature: Re-login
|
||||
And "user" has database file
|
||||
And "user" has running event loop
|
||||
|
||||
@ignore-live-auth
|
||||
Scenario: Re-login with disconnected user and database file
|
||||
Given there is disconnected user "user"
|
||||
And there is database file for "user"
|
||||
@ -27,6 +29,7 @@ Feature: Re-login
|
||||
And "user" has running event loop
|
||||
And "user" has non-zero space
|
||||
|
||||
@ignore-live-auth
|
||||
Scenario: Re-login with disconnected user and no database file
|
||||
Given there is disconnected user "user"
|
||||
And there is no database file for "user"
|
||||
|
||||
@ -1,11 +1,21 @@
|
||||
---
|
||||
|
||||
wait: true
|
||||
strict: true
|
||||
|
||||
file:
|
||||
name: "./gobinsec-cache.yml"
|
||||
expiration: 24h
|
||||
expiration: "24h"
|
||||
|
||||
ignore:
|
||||
# golang.org/x/net wrong match, we are using 2871e0cb, fixed by 37e1c6af
|
||||
- "CVE-2021-33194"
|
||||
# golang.org/x/net wrong match, we are using v0.1.0, fixed by 37e1c6af in v0.0.xxx
|
||||
- "CVE-2021-33194"
|
||||
# golang.org/x/crypto wrong match, we are using v0.1.0 all of this have been fixed in vO.O.xx
|
||||
- "CVE-2019-11840"
|
||||
- "CVE-2020-29652"
|
||||
- "CVE-2021-43565"
|
||||
- "CVE-2022-27191"
|
||||
- "CVE-2020-9283"
|
||||
- "CVE-2017-3204"
|
||||
# golang.org/x/text wrong match, we are using v0.4.0, fixed in a previous version
|
||||
- "CVE-2020-14040"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
##!/bin/bash
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2022 Proton AG
|
||||
#
|
||||
|
||||
@ -7,9 +7,9 @@ require github.com/intercloud/gobinsec v0.10.2
|
||||
require (
|
||||
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/memcachier/gomemcache v0.0.0-20170425125614-d027381f7653 // indirect
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
golang.org/x/sys v0.1.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@ -7,9 +7,13 @@ github.com/intercloud/gobinsec v0.10.2/go.mod h1:Y/AMKT0aQM40WDkTqlEe18W/IL6ZUuu
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/memcachier/gomemcache v0.0.0-20170425125614-d027381f7653 h1:222emoxOt/bCmNHp8Xt0Pr5Am3gIbqRKFpb4CQ9O2SI=
|
||||
github.com/memcachier/gomemcache v0.0.0-20170425125614-d027381f7653/go.mod h1:KoYVbOQexD45AOLfn+gsFB6c3o4ANzP1QKzjE6tZbK0=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -18,7 +22,12 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
|
||||
|
||||
# Generate HTML release notes
|
||||
# hosted at https://protonmail.com/download/{ie,bridge}/{stable,early}_releases.html
|
||||
# hosted at https://proton.me/download/{ie,bridge}/{stable,early}_releases.html
|
||||
INFILE=$1
|
||||
OUTFILE=${INFILE//.md/.html}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user