mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-12 05:36:43 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6bb7162263 | |||
| 0a48323043 | |||
| b347a926c2 | |||
| d2a179f58b | |||
| 3330cdc69b | |||
| dd1e2b2100 | |||
| 2566bdcedf | |||
| 1ff3fe194e | |||
| 6e2476df02 | |||
| e3fe33245e |
@ -38,7 +38,6 @@ stages:
|
|||||||
- cache
|
- cache
|
||||||
- test
|
- test
|
||||||
- build
|
- build
|
||||||
- check
|
|
||||||
- mirror
|
- mirror
|
||||||
|
|
||||||
# Stage: CACHE
|
# Stage: CACHE
|
||||||
@ -108,8 +107,7 @@ test-integration:
|
|||||||
dependency-updates:
|
dependency-updates:
|
||||||
stage: test
|
stage: test
|
||||||
script:
|
script:
|
||||||
- "echo 'NOTE: Do not run on go1.15 ( 'if...' can be removed once fully updated to go1.18)'"
|
- make updates
|
||||||
- if [ 18 -le $(go version | cut -d. -f2 | cut -d " " -f1) ]; then make updates; fi
|
|
||||||
|
|
||||||
# Stage: BUILD
|
# Stage: BUILD
|
||||||
|
|
||||||
@ -140,6 +138,9 @@ build-qml:
|
|||||||
script:
|
script:
|
||||||
- make build
|
- make build
|
||||||
- git diff && git diff-index --quiet HEAD
|
- git diff && git diff-index --quiet HEAD
|
||||||
|
- curl -L https://services.nvd.nist.gov/rest/json/cves/1.0/
|
||||||
|
- gobinsec -verbose -wait -config utils/gobinsec_conf.yml
|
||||||
|
cmd/Desktop-Bridge/deploy/linux/proton-bridge
|
||||||
artifacts:
|
artifacts:
|
||||||
# Note: The latest artifacts for refs are locked against deletion, and kept
|
# Note: The latest artifacts for refs are locked against deletion, and kept
|
||||||
# regardless of the expiry time. Introduced in GitLab 13.0 behind a
|
# regardless of the expiry time. Introduced in GitLab 13.0 behind a
|
||||||
@ -234,27 +235,6 @@ build-windows-qa:
|
|||||||
artifacts:
|
artifacts:
|
||||||
name: "bridge-windows-qa-$CI_COMMIT_SHORT_SHA"
|
name: "bridge-windows-qa-$CI_COMMIT_SHORT_SHA"
|
||||||
|
|
||||||
# Stage: CHECK
|
|
||||||
check-gobinsec:
|
|
||||||
stage: check
|
|
||||||
only:
|
|
||||||
- branches
|
|
||||||
cache:
|
|
||||||
key: gobinsec-cache
|
|
||||||
paths:
|
|
||||||
- gobinsec-cache.yml
|
|
||||||
policy: pull-push
|
|
||||||
before_script:
|
|
||||||
- mkdir build
|
|
||||||
- tar -xzf bridge_linux_*.tgz -C build
|
|
||||||
- "echo api-key: \"${GOBINSEC_NVD_API_KEY}\" >> utils/gobinsec_conf.yml"
|
|
||||||
script:
|
|
||||||
- "[ ! -f ./gobinsec-cache.yml ] && wget bridgeteam.protontech.ch/bridgeteam/gobinsec-cache.yml"
|
|
||||||
- cat ./gobinsec-cache.yml
|
|
||||||
- gobinsec -wait -cache -config utils/gobinsec_conf.yml build/proton-bridge
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Stage: MIRROR
|
# Stage: MIRROR
|
||||||
|
|
||||||
mirror-repo:
|
mirror-repo:
|
||||||
|
|||||||
31
Changelog.md
31
Changelog.md
@ -2,37 +2,6 @@
|
|||||||
|
|
||||||
Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
||||||
|
|
||||||
## [Bridge 2.3.0] Nihonbashi
|
|
||||||
|
|
||||||
### Added
|
|
||||||
* GODT-1739: Opt-out All Mail visibility in settings file.
|
|
||||||
* GODT-1794: CLI wording.
|
|
||||||
* GODT-1794: Add confirmation dialog and change wording.
|
|
||||||
* GODT-1741: GUI and CLI settings to change visibility of All Mail folder.
|
|
||||||
* GODT-1740: Opt-out All Mail visibility in settings file.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
* GODT-1737: Improve logging during import.
|
|
||||||
* GODT-1754: Add logs for unilateral updates and SEARCH.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
* GODT-1840: Use Safe map for mailboxID cache.
|
|
||||||
* GODT-1795: Fix automatic installation of profile for AppleMail on macOS Ventura beta (qt 5).
|
|
||||||
* GODT-1833: Fix gobinsec cache.
|
|
||||||
* GODT-1799: Fix dependency link.
|
|
||||||
* Other: Update SSL certificate fingerprint for test.
|
|
||||||
|
|
||||||
|
|
||||||
## [Bridge 2.2.2] Millau
|
|
||||||
|
|
||||||
### Added
|
|
||||||
* Introduced gobinsec cache.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
* GODT-1743: Terminate running bridge if has old version.
|
|
||||||
* GODT-1743: Quit bridge when opening manual install.
|
|
||||||
|
|
||||||
|
|
||||||
## [Bridge 2.2.1] Millau
|
## [Bridge 2.2.1] Millau
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
4
Makefile
4
Makefile
@ -10,7 +10,7 @@ TARGET_OS?=${GOOS}
|
|||||||
.PHONY: build build-nogui build-launcher versioner hasher
|
.PHONY: build build-nogui build-launcher versioner hasher
|
||||||
|
|
||||||
# Keep version hardcoded so app build works also without Git repository.
|
# Keep version hardcoded so app build works also without Git repository.
|
||||||
BRIDGE_APP_VERSION?=2.3.0+git
|
BRIDGE_APP_VERSION?=2.2.1+git
|
||||||
APP_VERSION:=${BRIDGE_APP_VERSION}
|
APP_VERSION:=${BRIDGE_APP_VERSION}
|
||||||
SRC_ICO:=bridge.ico
|
SRC_ICO:=bridge.ico
|
||||||
SRC_ICNS:=Bridge.icns
|
SRC_ICNS:=Bridge.icns
|
||||||
@ -166,7 +166,7 @@ update-qt-docs:
|
|||||||
LINTVER:="v1.39.0"
|
LINTVER:="v1.39.0"
|
||||||
LINTSRC:="https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh"
|
LINTSRC:="https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh"
|
||||||
|
|
||||||
install-dev-dependencies: install-devel-tools install-linter
|
install-dev-dependencies: install-devel-tools install-linter install-go-mod-outdated
|
||||||
|
|
||||||
install-devel-tools: check-has-go
|
install-devel-tools: check-has-go
|
||||||
go get -v github.com/golang/mock/gomock
|
go get -v github.com/golang/mock/gomock
|
||||||
|
|||||||
@ -24,7 +24,6 @@ package api
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/bridge"
|
"github.com/ProtonMail/proton-bridge/v2/internal/bridge"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||||
@ -58,9 +57,8 @@ func (api *apiServer) ListenAndServe() {
|
|||||||
|
|
||||||
addr := api.getAddress()
|
addr := api.getAddress()
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Handler: mux,
|
Handler: mux,
|
||||||
ReadHeaderTimeout: 5 * time.Second, // fix gosec G112 (vulnerability to [Slowloris](https://www.cloudflare.com/en-gb/learning/ddos/ddos-attack-tools/slowloris/) attack).
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("API listening at ", addr)
|
log.Info("API listening at ", addr)
|
||||||
|
|||||||
@ -57,6 +57,7 @@ import (
|
|||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/keychain"
|
"github.com/ProtonMail/proton-bridge/v2/pkg/keychain"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/listener"
|
"github.com/ProtonMail/proton-bridge/v2/pkg/listener"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
||||||
|
"github.com/allan-simon/go-singleinstance"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
@ -152,9 +153,9 @@ func New( //nolint:funlen
|
|||||||
}
|
}
|
||||||
settingsObj := settings.New(settingsPath)
|
settingsObj := settings.New(settingsPath)
|
||||||
|
|
||||||
lock, err := checkSingleInstance(locations.GetLockFile(), settingsObj)
|
lock, err := singleinstance.CreateLockFile(locations.GetLockFile())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithError(err).Warnf("%v is already running", appName)
|
logrus.Warnf("%v is already running", appName)
|
||||||
return nil, api.CheckOtherInstanceAndFocus(settingsObj.GetInt(settings.APIPortKey))
|
return nil, api.CheckOtherInstanceAndFocus(settingsObj.GetInt(settings.APIPortKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,9 +21,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIncrementRestartFlag(t *testing.T) {
|
func TestIncrementRestartFlag(t *testing.T) {
|
||||||
@ -49,15 +47,3 @@ func TestIncrementRestartFlag(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVersionLessThan(t *testing.T) {
|
|
||||||
r := require.New(t)
|
|
||||||
|
|
||||||
old := semver.MustParse("1.1.0")
|
|
||||||
current := semver.MustParse("1.1.1")
|
|
||||||
newer := semver.MustParse("1.1.2")
|
|
||||||
|
|
||||||
r.True(old.LessThan(current))
|
|
||||||
r.False(current.LessThan(current))
|
|
||||||
r.False(newer.LessThan(current))
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,101 +0,0 @@
|
|||||||
// Copyright (c) 2022 Proton AG
|
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//go:build !windows
|
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package base
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
|
|
||||||
"github.com/allan-simon/go-singleinstance"
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
// checkSingleInstance returns error if a bridge instance is already running
|
|
||||||
// This instance should be stop and window of running window should be brought
|
|
||||||
// to focus.
|
|
||||||
//
|
|
||||||
// For macOS and Linux when already running version is older than this instance
|
|
||||||
// it will kill old and continue with this new bridge (i.e. no error returned).
|
|
||||||
func checkSingleInstance(lockFilePath string, settingsObj *settings.Settings) (*os.File, error) {
|
|
||||||
if lock, err := singleinstance.CreateLockFile(lockFilePath); err == nil {
|
|
||||||
// Bridge is not runnig, continue normally
|
|
||||||
return lock, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := runningVersionIsOlder(settingsObj); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
pid, err := getPID(lockFilePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := unix.Kill(pid, unix.SIGTERM); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need to wait some time to release file lock
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
return singleinstance.CreateLockFile(lockFilePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPID(lockFilePath string) (int, error) {
|
|
||||||
file, err := os.Open(filepath.Clean(lockFilePath))
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
defer func() { _ = file.Close() }()
|
|
||||||
|
|
||||||
rawPID := make([]byte, 10) // PID is probably up to 7 digits long, 10 should be enough
|
|
||||||
n, err := file.Read(rawPID)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return strconv.Atoi(strings.TrimSpace(string(rawPID[:n])))
|
|
||||||
}
|
|
||||||
|
|
||||||
func runningVersionIsOlder(settingsObj *settings.Settings) error {
|
|
||||||
currentVer, err := semver.StrictNewVersion(constants.Version)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
runningVer, err := semver.StrictNewVersion(settingsObj.Get(settings.LastVersionKey))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !runningVer.LessThan(currentVer) {
|
|
||||||
return errors.New("running version is not older")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
// Copyright (c) 2022 Proton AG
|
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//go:build windows
|
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package base
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
|
||||||
"github.com/allan-simon/go-singleinstance"
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkSingleInstance(lockFilePath string, _ *settings.Settings) (*os.File, error) {
|
|
||||||
return singleinstance.CreateLockFile(lockFilePath)
|
|
||||||
}
|
|
||||||
@ -57,9 +57,8 @@ type Bridge struct {
|
|||||||
// Bridge's global errors list.
|
// Bridge's global errors list.
|
||||||
errors []error
|
errors []error
|
||||||
|
|
||||||
isAllMailVisible bool
|
isFirstStart bool
|
||||||
isFirstStart bool
|
lastVersion string
|
||||||
lastVersion string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(
|
func New(
|
||||||
@ -93,16 +92,15 @@ func New(
|
|||||||
)
|
)
|
||||||
|
|
||||||
b := &Bridge{
|
b := &Bridge{
|
||||||
Users: u,
|
Users: u,
|
||||||
locations: locations,
|
locations: locations,
|
||||||
settings: setting,
|
settings: setting,
|
||||||
clientManager: clientManager,
|
clientManager: clientManager,
|
||||||
updater: updater,
|
updater: updater,
|
||||||
versioner: versioner,
|
versioner: versioner,
|
||||||
cacheProvider: cacheProvider,
|
cacheProvider: cacheProvider,
|
||||||
autostart: autostart,
|
autostart: autostart,
|
||||||
isFirstStart: false,
|
isFirstStart: false,
|
||||||
isAllMailVisible: setting.GetBool(settings.IsAllMailVisible),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if setting.GetBool(settings.FirstStartKey) {
|
if setting.GetBool(settings.FirstStartKey) {
|
||||||
@ -304,14 +302,3 @@ func (b *Bridge) GetLastVersion() string {
|
|||||||
func (b *Bridge) IsFirstStart() bool {
|
func (b *Bridge) IsFirstStart() bool {
|
||||||
return b.isFirstStart
|
return b.isFirstStart
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAllMailVisible can be called extensively by IMAP. Therefore, it is better
|
|
||||||
// to cache the value instead of reading from settings file.
|
|
||||||
func (b *Bridge) IsAllMailVisible() bool {
|
|
||||||
return b.isAllMailVisible
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bridge) SetIsAllMailVisible(isVisible bool) {
|
|
||||||
b.settings.SetBool(settings.IsAllMailVisible, isVisible)
|
|
||||||
b.isAllMailVisible = isVisible
|
|
||||||
}
|
|
||||||
|
|||||||
@ -55,7 +55,6 @@ const (
|
|||||||
AttachmentWorkers = "attachment_workers"
|
AttachmentWorkers = "attachment_workers"
|
||||||
ColorScheme = "color_scheme"
|
ColorScheme = "color_scheme"
|
||||||
RebrandingMigrationKey = "rebranding_migrated"
|
RebrandingMigrationKey = "rebranding_migrated"
|
||||||
IsAllMailVisible = "is_all_mail_visible"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Settings struct {
|
type Settings struct {
|
||||||
@ -111,6 +110,4 @@ func (s *Settings) setDefaultValues() {
|
|||||||
|
|
||||||
// By default, stick to STARTTLS. If the user uses catalina+applemail they'll have to change to SSL.
|
// By default, stick to STARTTLS. If the user uses catalina+applemail they'll have to change to SSL.
|
||||||
s.setDefault(SMTPSSLKey, "false")
|
s.setDefault(SMTPSSLKey, "false")
|
||||||
|
|
||||||
s.setDefault(IsAllMailVisible, "true")
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,24 +24,18 @@ import (
|
|||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsCatalinaOrNewer checks whether the host is macOS Catalina 10.15.x or higher.
|
// IsCatalinaOrNewer checks whether the host is MacOS Catalina 10.15.x or higher.
|
||||||
func IsCatalinaOrNewer() bool {
|
func IsCatalinaOrNewer() bool {
|
||||||
return isThisDarwinNewerOrEqual(getMinCatalina())
|
return isThisDarwinNewerOrEqual(getMinCatalina())
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsBigSurOrNewer checks whether the host is macOS BigSur 10.16.x or higher.
|
// IsBigSurOrNewer checks whether the host is MacOS BigSur 10.16.x or higher.
|
||||||
func IsBigSurOrNewer() bool {
|
func IsBigSurOrNewer() bool {
|
||||||
return isThisDarwinNewerOrEqual(getMinBigSur())
|
return isThisDarwinNewerOrEqual(getMinBigSur())
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsVenturaOrNewer checks whether the host is macOS BigSur 13.x or higher.
|
|
||||||
func IsVenturaOrNewer() bool {
|
|
||||||
return isThisDarwinNewerOrEqual(getMinVentura())
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMinCatalina() *semver.Version { return semver.MustParse("19.0.0") }
|
func getMinCatalina() *semver.Version { return semver.MustParse("19.0.0") }
|
||||||
func getMinBigSur() *semver.Version { return semver.MustParse("20.0.0") }
|
func getMinBigSur() *semver.Version { return semver.MustParse("20.0.0") }
|
||||||
func getMinVentura() *semver.Version { return semver.MustParse("22.0.0") }
|
|
||||||
|
|
||||||
func isThisDarwinNewerOrEqual(minVersion *semver.Version) bool {
|
func isThisDarwinNewerOrEqual(minVersion *semver.Version) bool {
|
||||||
if runtime.GOOS != "darwin" {
|
if runtime.GOOS != "darwin" {
|
||||||
|
|||||||
@ -137,23 +137,6 @@ func New( //nolint:funlen
|
|||||||
})
|
})
|
||||||
fe.AddCmd(dohCmd)
|
fe.AddCmd(dohCmd)
|
||||||
|
|
||||||
// All mail visibility commands.
|
|
||||||
allMailCmd := &ishell.Cmd{
|
|
||||||
Name: "all-mail-visibility",
|
|
||||||
Help: "choose not to list the All Mail folder in your local client",
|
|
||||||
}
|
|
||||||
allMailCmd.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "hide",
|
|
||||||
Help: "All Mail folder will not be listed in your local client",
|
|
||||||
Func: fe.hideAllMail,
|
|
||||||
})
|
|
||||||
allMailCmd.AddCmd(&ishell.Cmd{
|
|
||||||
Name: "show",
|
|
||||||
Help: "All Mail folder will be listed in your local client",
|
|
||||||
Func: fe.showAllMail,
|
|
||||||
})
|
|
||||||
fe.AddCmd(allMailCmd)
|
|
||||||
|
|
||||||
// Cache-On-Disk commands.
|
// Cache-On-Disk commands.
|
||||||
codCmd := &ishell.Cmd{
|
codCmd := &ishell.Cmd{
|
||||||
Name: "local-cache",
|
Name: "local-cache",
|
||||||
|
|||||||
@ -152,32 +152,6 @@ func (f *frontendCLI) disallowProxy(c *ishell.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *frontendCLI) hideAllMail(c *ishell.Context) {
|
|
||||||
if !f.bridge.IsAllMailVisible() {
|
|
||||||
f.Println("All Mail folder is not listed in your local client.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Println("All Mail folder is listed in your client right now.")
|
|
||||||
|
|
||||||
if f.yesNoQuestion("Do you want to hide All Mail folder") {
|
|
||||||
f.bridge.SetIsAllMailVisible(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *frontendCLI) showAllMail(c *ishell.Context) {
|
|
||||||
if f.bridge.IsAllMailVisible() {
|
|
||||||
f.Println("All Mail folder is listed in your local client.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Println("All Mail folder is not listed in your client right now.")
|
|
||||||
|
|
||||||
if f.yesNoQuestion("Do you want to show All Mail folder") {
|
|
||||||
f.bridge.SetIsAllMailVisible(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *frontendCLI) enableCacheOnDisk(c *ishell.Context) {
|
func (f *frontendCLI) enableCacheOnDisk(c *ishell.Context) {
|
||||||
if f.settings.GetBool(settings.CacheEnabledKey) {
|
if f.settings.GetBool(settings.CacheEnabledKey) {
|
||||||
f.Println("The local cache is already enabled.")
|
f.Println("The local cache is already enabled.")
|
||||||
|
|||||||
@ -36,8 +36,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
bigSurPreferencesPane = "/System/Library/PreferencePanes/Profiles.prefPane"
|
bigSurPreferncesPane = "/System/Library/PreferencePanes/Profiles.prefPane"
|
||||||
venturaPreferencesPane = "x-apple.systempreferences:com.apple.preferences.configurationprofiles"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() { //nolint:gochecknoinit
|
func init() { //nolint:gochecknoinit
|
||||||
@ -57,13 +56,7 @@ func (c *appleMail) Configure(imapPort, smtpPort int, imapSSL, smtpSSL bool, use
|
|||||||
}
|
}
|
||||||
|
|
||||||
if useragent.IsBigSurOrNewer() {
|
if useragent.IsBigSurOrNewer() {
|
||||||
prefPane := bigSurPreferencesPane
|
return execabs.Command("open", bigSurPreferncesPane, confPath).Run() //nolint:gosec G204: open command is safe, mobileconfig is generated by us
|
||||||
|
|
||||||
if useragent.IsVenturaOrNewer() {
|
|
||||||
prefPane = venturaPreferencesPane
|
|
||||||
}
|
|
||||||
|
|
||||||
return execabs.Command("open", prefPane, confPath).Run() //nolint:gosec // G204 open command is safe, mobileconfig is generated by us
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return execabs.Command("open", confPath).Run() //nolint:gosec G204: open command is safe, mobileconfig is generated by us
|
return execabs.Command("open", confPath).Run() //nolint:gosec G204: open command is safe, mobileconfig is generated by us
|
||||||
|
|||||||
@ -672,10 +672,6 @@ Window {
|
|||||||
Label {colorScheme: root.colorScheme; text: "DoH:"}
|
Label {colorScheme: root.colorScheme; text: "DoH:"}
|
||||||
Toggle {colorScheme: root.colorScheme; checked: root.isDoHEnabled; onClicked: root.isDoHEnabled = !root.isDoHEnabled}
|
Toggle {colorScheme: root.colorScheme; checked: root.isDoHEnabled; onClicked: root.isDoHEnabled = !root.isDoHEnabled}
|
||||||
}
|
}
|
||||||
RowLayout {
|
|
||||||
Label {colorScheme: root.colorScheme; text: "All Mail disabled:"}
|
|
||||||
Toggle {colorScheme: root.colorScheme; checked: root.isAllMailVisible; onClicked: root.isAllMailVisible = !root.isAllMailVisible}
|
|
||||||
}
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Label {colorScheme: root.colorScheme; text: "Ports:"}
|
Label {colorScheme: root.colorScheme; text: "Ports:"}
|
||||||
TextField {
|
TextField {
|
||||||
@ -815,13 +811,6 @@ Window {
|
|||||||
root.isDoHEnabled = makeItActive
|
root.isDoHEnabled = makeItActive
|
||||||
}
|
}
|
||||||
|
|
||||||
property bool isAllMailVisible : true
|
|
||||||
function changeIsAllMailVisible(isVisible){
|
|
||||||
console.debug("-> All Mail Visible", isVisible, root.isAllMailVisible)
|
|
||||||
root.isAllMailVisible = isVisible
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
property bool useSSLforSMTP: false
|
property bool useSSLforSMTP: false
|
||||||
function toggleUseSSLforSMTP(makeItActive){
|
function toggleUseSSLforSMTP(makeItActive){
|
||||||
console.debug("-> SMTP SSL", makeItActive, root.useSSLforSMTP)
|
console.debug("-> SMTP SSL", makeItActive, root.useSSLforSMTP)
|
||||||
|
|||||||
@ -156,19 +156,6 @@ SettingsView {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsItem {
|
|
||||||
id: allMail
|
|
||||||
visible: root._isAdvancedShown
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: qsTr("Show All Mail")
|
|
||||||
description: qsTr("Choose to list the All Mail folder in your local client.")
|
|
||||||
type: SettingsItem.Toggle
|
|
||||||
checked: root.backend.isAllMailVisible
|
|
||||||
onClicked: root.notifications.askChangeAllMailVisibility(root.backend.isAllMailVisible)
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsItem {
|
SettingsItem {
|
||||||
id: ports
|
id: ports
|
||||||
visible: root._isAdvancedShown
|
visible: root._isAdvancedShown
|
||||||
|
|||||||
@ -110,11 +110,6 @@ Item {
|
|||||||
notification: root.notifications.resetBridge
|
notification: root.notifications.resetBridge
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
notification: root.notifications.changeAllMailVisibility
|
|
||||||
}
|
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.deleteAccount
|
notification: root.notifications.deleteAccount
|
||||||
|
|||||||
@ -34,7 +34,6 @@ QtObject {
|
|||||||
signal askDisableLocalCache()
|
signal askDisableLocalCache()
|
||||||
signal askEnableLocalCache(var path)
|
signal askEnableLocalCache(var path)
|
||||||
signal askResetBridge()
|
signal askResetBridge()
|
||||||
signal askChangeAllMailVisibility(var isVisibleNow)
|
|
||||||
signal askDeleteAccount(var user)
|
signal askDeleteAccount(var user)
|
||||||
|
|
||||||
enum Group {
|
enum Group {
|
||||||
@ -73,7 +72,6 @@ QtObject {
|
|||||||
root.disableLocalCache,
|
root.disableLocalCache,
|
||||||
root.enableLocalCache,
|
root.enableLocalCache,
|
||||||
root.resetBridge,
|
root.resetBridge,
|
||||||
root.changeAllMailVisibility,
|
|
||||||
root.deleteAccount,
|
root.deleteAccount,
|
||||||
root.noKeychain,
|
root.noKeychain,
|
||||||
root.rebuildKeychain,
|
root.rebuildKeychain,
|
||||||
@ -195,7 +193,6 @@ QtObject {
|
|||||||
onTriggered: {
|
onTriggered: {
|
||||||
Qt.openUrlExternally(root.backend.landingPageLink)
|
Qt.openUrlExternally(root.backend.landingPageLink)
|
||||||
root.updateManualError.active = false
|
root.updateManualError.active = false
|
||||||
root.backend.quit()
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Action {
|
Action {
|
||||||
@ -842,47 +839,6 @@ QtObject {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
property Notification changeAllMailVisibility: Notification {
|
|
||||||
title: root.changeAllMailVisibility.isVisibleNow ?
|
|
||||||
qsTr("Hide All Mail folder?") :
|
|
||||||
qsTr("Show All Mail folder?")
|
|
||||||
brief: title
|
|
||||||
icon: "./icons/ic-info-circle-filled.svg"
|
|
||||||
description: qsTr("Switching between showing and hiding the All Mail folder will require you to restart your client.")
|
|
||||||
type: Notification.NotificationType.Info
|
|
||||||
group: Notifications.Group.Configuration | Notifications.Group.Dialogs
|
|
||||||
|
|
||||||
property var isVisibleNow
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
onAskChangeAllMailVisibility: {
|
|
||||||
root.changeAllMailVisibility.isVisibleNow = isVisibleNow
|
|
||||||
root.changeAllMailVisibility.active = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
action: [
|
|
||||||
Action {
|
|
||||||
id: allMail_change
|
|
||||||
text: root.changeAllMailVisibility.isVisibleNow ?
|
|
||||||
qsTr("Hide All Mail folder") :
|
|
||||||
qsTr("Show All Mail folder")
|
|
||||||
onTriggered: {
|
|
||||||
root.backend.changeIsAllMailVisible(!root.changeAllMailVisibility.isVisibleNow)
|
|
||||||
root.changeAllMailVisibility.active = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Action {
|
|
||||||
id: allMail_cancel
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
onTriggered: {
|
|
||||||
root.changeAllMailVisibility.active = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
property Notification deleteAccount: Notification {
|
property Notification deleteAccount: Notification {
|
||||||
title: qsTr("Remove this account?")
|
title: qsTr("Remove this account?")
|
||||||
brief: title
|
brief: title
|
||||||
|
|||||||
@ -155,9 +155,6 @@ type QMLBackend struct {
|
|||||||
_ func() `signal:apiCertIssue`
|
_ func() `signal:apiCertIssue`
|
||||||
|
|
||||||
_ func(userID string) `signal:userChanged`
|
_ func(userID string) `signal:userChanged`
|
||||||
|
|
||||||
_ bool `property:"isAllMailVisible"`
|
|
||||||
_ func(isDisabled bool) `slot:"changeIsAllMailVisible"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *QMLBackend) setup(f *FrontendQt) {
|
func (q *QMLBackend) setup(f *FrontendQt) {
|
||||||
@ -307,11 +304,4 @@ func (q *QMLBackend) setup(f *FrontendQt) {
|
|||||||
f.changeKeychain(k)
|
f.changeKeychain(k)
|
||||||
}()
|
}()
|
||||||
})
|
})
|
||||||
|
|
||||||
q.SetIsAllMailVisible(f.bridge.IsAllMailVisible())
|
|
||||||
q.ConnectChangeIsAllMailVisible(func(isVisible bool) {
|
|
||||||
f.bridge.SetIsAllMailVisible(isVisible)
|
|
||||||
f.qml.SetIsAllMailVisible(isVisible)
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -92,8 +92,6 @@ type Bridger interface {
|
|||||||
DisableAutostart() error
|
DisableAutostart() error
|
||||||
GetLastVersion() string
|
GetLastVersion() string
|
||||||
IsFirstStart() bool
|
IsFirstStart() bool
|
||||||
IsAllMailVisible() bool
|
|
||||||
SetIsAllMailVisible(bool)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type bridgeWrap struct {
|
type bridgeWrap struct {
|
||||||
|
|||||||
@ -93,9 +93,10 @@ func newIMAPBackend(
|
|||||||
eventListener listener.Listener,
|
eventListener listener.Listener,
|
||||||
listWorkers int,
|
listWorkers int,
|
||||||
) *imapBackend {
|
) *imapBackend {
|
||||||
ib := &imapBackend{
|
return &imapBackend{
|
||||||
panicHandler: panicHandler,
|
panicHandler: panicHandler,
|
||||||
bridge: bridge,
|
bridge: bridge,
|
||||||
|
updates: newIMAPUpdates(),
|
||||||
eventListener: eventListener,
|
eventListener: eventListener,
|
||||||
|
|
||||||
users: map[string]*imapUser{},
|
users: map[string]*imapUser{},
|
||||||
@ -105,8 +106,6 @@ func newIMAPBackend(
|
|||||||
imapCacheLock: &sync.RWMutex{},
|
imapCacheLock: &sync.RWMutex{},
|
||||||
listWorkers: listWorkers,
|
listWorkers: listWorkers,
|
||||||
}
|
}
|
||||||
ib.updates = newIMAPUpdates(ib)
|
|
||||||
return ib
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ib *imapBackend) getUser(address string) (*imapUser, error) {
|
func (ib *imapBackend) getUser(address string) (*imapUser, error) {
|
||||||
|
|||||||
@ -31,7 +31,6 @@ type cacheProvider interface {
|
|||||||
type bridger interface {
|
type bridger interface {
|
||||||
GetUser(query string) (bridgeUser, error)
|
GetUser(query string) (bridgeUser, error)
|
||||||
HasError(err error) bool
|
HasError(err error) bool
|
||||||
IsAllMailVisible() bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type bridgeUser interface {
|
type bridgeUser interface {
|
||||||
|
|||||||
@ -197,7 +197,7 @@ func (im *imapMailbox) labelExistingMessage(msg storeMessageProvider) error { //
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (im *imapMailbox) importMessage(kr *crypto.KeyRing, hdr textproto.Header, body []byte, imapFlags []string, date time.Time) error { //nolint:funlen
|
func (im *imapMailbox) importMessage(kr *crypto.KeyRing, hdr textproto.Header, body []byte, imapFlags []string, date time.Time) error { //nolint:funlen
|
||||||
im.log.WithField("size", len(body)).Info("Importing external message")
|
im.log.Info("Importing external message")
|
||||||
|
|
||||||
var (
|
var (
|
||||||
seen bool
|
seen bool
|
||||||
@ -251,7 +251,6 @@ func (im *imapMailbox) importMessage(kr *crypto.KeyRing, hdr textproto.Header, b
|
|||||||
|
|
||||||
messageID, err := targetMailbox.ImportMessage(enc, seen, labelIDs, flags, time)
|
messageID, err := targetMailbox.ImportMessage(enc, seen, labelIDs, flags, time)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithField("enc.size", len(enc)).Error("Import failed")
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -332,16 +332,7 @@ func (im *imapMailbox) labelMessages(uid bool, seqSet *imap.SeqSet, targetLabel
|
|||||||
|
|
||||||
// SearchMessages searches messages. The returned list must contain UIDs if
|
// SearchMessages searches messages. The returned list must contain UIDs if
|
||||||
// uid is set to true, or sequence numbers otherwise.
|
// uid is set to true, or sequence numbers otherwise.
|
||||||
func (im *imapMailbox) SearchMessages(isUID bool, criteria *imap.SearchCriteria) (ids []uint32, err error) {
|
func (im *imapMailbox) SearchMessages(isUID bool, criteria *imap.SearchCriteria) (ids []uint32, err error) { //nolint:gocyclo,funlen
|
||||||
err = im.logCommand(func() error {
|
|
||||||
var searchError error
|
|
||||||
ids, searchError = im.searchMessages(isUID, criteria)
|
|
||||||
return searchError
|
|
||||||
}, "SEARCH", isUID, criteria.Format())
|
|
||||||
return ids, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (im *imapMailbox) searchMessages(isUID bool, criteria *imap.SearchCriteria) (ids []uint32, err error) { //nolint:gocyclo,funlen
|
|
||||||
// Called from go-imap in goroutines - we need to handle panics for each function.
|
// Called from go-imap in goroutines - we need to handle panics for each function.
|
||||||
defer im.panicHandler.HandlePanic()
|
defer im.panicHandler.HandlePanic()
|
||||||
|
|
||||||
|
|||||||
@ -1,44 +0,0 @@
|
|||||||
// Copyright (c) 2022 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 imap
|
|
||||||
|
|
||||||
import "sync"
|
|
||||||
|
|
||||||
type safeMapOfStrings struct {
|
|
||||||
data map[string]string
|
|
||||||
mutex sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSafeMapOfString() safeMapOfStrings {
|
|
||||||
return safeMapOfStrings{
|
|
||||||
data: map[string]string{},
|
|
||||||
mutex: sync.RWMutex{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *safeMapOfStrings) get(key string) string {
|
|
||||||
m.mutex.RLock()
|
|
||||||
defer m.mutex.RUnlock()
|
|
||||||
return m.data[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *safeMapOfStrings) set(key, value string) {
|
|
||||||
m.mutex.Lock()
|
|
||||||
defer m.mutex.Unlock()
|
|
||||||
m.data[key] = value
|
|
||||||
}
|
|
||||||
@ -23,7 +23,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/store"
|
"github.com/ProtonMail/proton-bridge/v2/internal/store"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/algo"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/message"
|
"github.com/ProtonMail/proton-bridge/v2/pkg/message"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
||||||
imap "github.com/emersion/go-imap"
|
imap "github.com/emersion/go-imap"
|
||||||
@ -43,16 +42,14 @@ type imapUpdates struct {
|
|||||||
blocking map[string]bool
|
blocking map[string]bool
|
||||||
delayedExpunges map[string][]chan struct{}
|
delayedExpunges map[string][]chan struct{}
|
||||||
ch chan goIMAPBackend.Update
|
ch chan goIMAPBackend.Update
|
||||||
ib *imapBackend
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newIMAPUpdates(ib *imapBackend) *imapUpdates {
|
func newIMAPUpdates() *imapUpdates {
|
||||||
return &imapUpdates{
|
return &imapUpdates{
|
||||||
lock: &sync.Mutex{},
|
lock: &sync.Mutex{},
|
||||||
blocking: map[string]bool{},
|
blocking: map[string]bool{},
|
||||||
delayedExpunges: map[string][]chan struct{}{},
|
delayedExpunges: map[string][]chan struct{}{},
|
||||||
ch: make(chan goIMAPBackend.Update),
|
ch: make(chan goIMAPBackend.Update),
|
||||||
ib: ib,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,8 +113,6 @@ func (iu *imapUpdates) CanDelete(mailboxID string) (bool, func()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (iu *imapUpdates) Notice(address, notice string) {
|
func (iu *imapUpdates) Notice(address, notice string) {
|
||||||
l := iu.updateLog(address, "")
|
|
||||||
l.Info("Notice")
|
|
||||||
update := new(goIMAPBackend.StatusUpdate)
|
update := new(goIMAPBackend.StatusUpdate)
|
||||||
update.Update = goIMAPBackend.NewUpdate(address, "")
|
update.Update = goIMAPBackend.NewUpdate(address, "")
|
||||||
update.StatusResp = &imap.StatusResp{
|
update.StatusResp = &imap.StatusResp{
|
||||||
@ -125,7 +120,7 @@ func (iu *imapUpdates) Notice(address, notice string) {
|
|||||||
Code: imap.CodeAlert,
|
Code: imap.CodeAlert,
|
||||||
Info: notice,
|
Info: notice,
|
||||||
}
|
}
|
||||||
iu.sendIMAPUpdate(l, update, false)
|
iu.sendIMAPUpdate(update, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iu *imapUpdates) UpdateMessage(
|
func (iu *imapUpdates) UpdateMessage(
|
||||||
@ -133,14 +128,14 @@ func (iu *imapUpdates) UpdateMessage(
|
|||||||
uid, sequenceNumber uint32,
|
uid, sequenceNumber uint32,
|
||||||
msg *pmapi.Message, hasDeletedFlag bool,
|
msg *pmapi.Message, hasDeletedFlag bool,
|
||||||
) {
|
) {
|
||||||
l := iu.updateLog(address, mailboxName).
|
log.WithFields(logrus.Fields{
|
||||||
WithFields(logrus.Fields{
|
"address": address,
|
||||||
"seqNum": sequenceNumber,
|
"mailbox": mailboxName,
|
||||||
"uid": uid,
|
"seqNum": sequenceNumber,
|
||||||
"flags": message.GetFlags(msg),
|
"uid": uid,
|
||||||
"deleted": hasDeletedFlag,
|
"flags": message.GetFlags(msg),
|
||||||
})
|
"deleted": hasDeletedFlag,
|
||||||
l.Info("IDLE update")
|
}).Trace("IDLE update")
|
||||||
update := new(goIMAPBackend.MessageUpdate)
|
update := new(goIMAPBackend.MessageUpdate)
|
||||||
update.Update = goIMAPBackend.NewUpdate(address, mailboxName)
|
update.Update = goIMAPBackend.NewUpdate(address, mailboxName)
|
||||||
update.Message = imap.NewMessage(sequenceNumber, []imap.FetchItem{imap.FetchFlags, imap.FetchUid})
|
update.Message = imap.NewMessage(sequenceNumber, []imap.FetchItem{imap.FetchFlags, imap.FetchUid})
|
||||||
@ -149,22 +144,26 @@ func (iu *imapUpdates) UpdateMessage(
|
|||||||
update.Message.Flags = append(update.Message.Flags, imap.DeletedFlag)
|
update.Message.Flags = append(update.Message.Flags, imap.DeletedFlag)
|
||||||
}
|
}
|
||||||
update.Message.Uid = uid
|
update.Message.Uid = uid
|
||||||
iu.sendIMAPUpdate(l, update, iu.isBlocking(address, mailboxName, operationUpdateMessage))
|
iu.sendIMAPUpdate(update, iu.isBlocking(address, mailboxName, operationUpdateMessage))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iu *imapUpdates) DeleteMessage(address, mailboxName string, sequenceNumber uint32) {
|
func (iu *imapUpdates) DeleteMessage(address, mailboxName string, sequenceNumber uint32) {
|
||||||
l := iu.updateLog(address, mailboxName).
|
log.WithFields(logrus.Fields{
|
||||||
WithField("seqNum", sequenceNumber)
|
"address": address,
|
||||||
l.Info("IDLE delete")
|
"mailbox": mailboxName,
|
||||||
|
"seqNum": sequenceNumber,
|
||||||
|
}).Trace("IDLE delete")
|
||||||
update := new(goIMAPBackend.ExpungeUpdate)
|
update := new(goIMAPBackend.ExpungeUpdate)
|
||||||
update.Update = goIMAPBackend.NewUpdate(address, mailboxName)
|
update.Update = goIMAPBackend.NewUpdate(address, mailboxName)
|
||||||
update.SeqNum = sequenceNumber
|
update.SeqNum = sequenceNumber
|
||||||
iu.sendIMAPUpdate(l, update, iu.isBlocking(address, mailboxName, operationDeleteMessage))
|
iu.sendIMAPUpdate(update, iu.isBlocking(address, mailboxName, operationDeleteMessage))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iu *imapUpdates) MailboxCreated(address, mailboxName string) {
|
func (iu *imapUpdates) MailboxCreated(address, mailboxName string) {
|
||||||
l := iu.updateLog(address, mailboxName)
|
log.WithFields(logrus.Fields{
|
||||||
l.Info("IDLE mailbox info")
|
"address": address,
|
||||||
|
"mailbox": mailboxName,
|
||||||
|
}).Trace("IDLE mailbox info")
|
||||||
update := new(goIMAPBackend.MailboxInfoUpdate)
|
update := new(goIMAPBackend.MailboxInfoUpdate)
|
||||||
update.Update = goIMAPBackend.NewUpdate(address, "")
|
update.Update = goIMAPBackend.NewUpdate(address, "")
|
||||||
update.MailboxInfo = &imap.MailboxInfo{
|
update.MailboxInfo = &imap.MailboxInfo{
|
||||||
@ -172,30 +171,29 @@ func (iu *imapUpdates) MailboxCreated(address, mailboxName string) {
|
|||||||
Delimiter: store.PathDelimiter,
|
Delimiter: store.PathDelimiter,
|
||||||
Name: mailboxName,
|
Name: mailboxName,
|
||||||
}
|
}
|
||||||
iu.sendIMAPUpdate(l, update, false)
|
iu.sendIMAPUpdate(update, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iu *imapUpdates) MailboxStatus(address, mailboxName string, total, unread, unreadSeqNum uint32) {
|
func (iu *imapUpdates) MailboxStatus(address, mailboxName string, total, unread, unreadSeqNum uint32) {
|
||||||
l := iu.updateLog(address, mailboxName).
|
log.WithFields(logrus.Fields{
|
||||||
WithFields(logrus.Fields{
|
"address": address,
|
||||||
"total": total,
|
"mailbox": mailboxName,
|
||||||
"unread": unread,
|
"total": total,
|
||||||
"unreadSeqNum": unreadSeqNum,
|
"unread": unread,
|
||||||
})
|
"unreadSeqNum": unreadSeqNum,
|
||||||
l.Info("IDLE status")
|
}).Trace("IDLE status")
|
||||||
update := new(goIMAPBackend.MailboxUpdate)
|
update := new(goIMAPBackend.MailboxUpdate)
|
||||||
update.Update = goIMAPBackend.NewUpdate(address, mailboxName)
|
update.Update = goIMAPBackend.NewUpdate(address, mailboxName)
|
||||||
update.MailboxStatus = imap.NewMailboxStatus(mailboxName, []imap.StatusItem{imap.StatusMessages, imap.StatusUnseen})
|
update.MailboxStatus = imap.NewMailboxStatus(mailboxName, []imap.StatusItem{imap.StatusMessages, imap.StatusUnseen})
|
||||||
update.MailboxStatus.Messages = total
|
update.MailboxStatus.Messages = total
|
||||||
update.MailboxStatus.Unseen = unread
|
update.MailboxStatus.Unseen = unread
|
||||||
update.MailboxStatus.UnseenSeqNum = unreadSeqNum
|
update.MailboxStatus.UnseenSeqNum = unreadSeqNum
|
||||||
iu.sendIMAPUpdate(l, update, true)
|
iu.sendIMAPUpdate(update, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iu *imapUpdates) sendIMAPUpdate(updateLog *logrus.Entry, update goIMAPBackend.Update, isBlocking bool) {
|
func (iu *imapUpdates) sendIMAPUpdate(update goIMAPBackend.Update, isBlocking bool) {
|
||||||
l := updateLog.WithField("blocking", isBlocking)
|
|
||||||
if iu.ch == nil {
|
if iu.ch == nil {
|
||||||
l.Info("IMAP IDLE unavailable")
|
log.Trace("IMAP IDLE unavailable")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +201,7 @@ func (iu *imapUpdates) sendIMAPUpdate(updateLog *logrus.Entry, update goIMAPBack
|
|||||||
go func() {
|
go func() {
|
||||||
select {
|
select {
|
||||||
case <-time.After(1 * time.Second):
|
case <-time.After(1 * time.Second):
|
||||||
l.Warn("IMAP update could not be sent (timeout)")
|
log.Warn("IMAP update could not be sent (timeout)")
|
||||||
return
|
return
|
||||||
case iu.ch <- update:
|
case iu.ch <- update:
|
||||||
}
|
}
|
||||||
@ -216,35 +214,7 @@ func (iu *imapUpdates) sendIMAPUpdate(updateLog *logrus.Entry, update goIMAPBack
|
|||||||
select {
|
select {
|
||||||
case <-done:
|
case <-done:
|
||||||
case <-time.After(1 * time.Second):
|
case <-time.After(1 * time.Second):
|
||||||
l.Warn("IMAP update could not be delivered (timeout)")
|
log.Warn("IMAP update could not be delivered (timeout)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iu *imapUpdates) getIDs(address, mailboxName string) (addressID, mailboxID string) {
|
|
||||||
addressID = "unknown-" + algo.HashBase64SHA256(address)
|
|
||||||
mailboxID = "unknown-" + algo.HashBase64SHA256(mailboxName)
|
|
||||||
|
|
||||||
if iu == nil || iu.ib == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
user, err := iu.ib.getUser(address)
|
|
||||||
if err != nil || user == nil || user.storeAddress == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
addressID = user.addressID
|
|
||||||
|
|
||||||
if v := user.mailboxIDs.get(mailboxName); v != "" {
|
|
||||||
mailboxID = v
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iu *imapUpdates) updateLog(address, mailboxName string) *logrus.Entry {
|
|
||||||
addressID, mailboxID := iu.getIDs(address, mailboxName)
|
|
||||||
return log.
|
|
||||||
WithField("address", addressID).
|
|
||||||
WithField("mailbox", mailboxID)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestUpdatesCanDelete(t *testing.T) {
|
func TestUpdatesCanDelete(t *testing.T) {
|
||||||
u := newIMAPUpdates(nil)
|
u := newIMAPUpdates()
|
||||||
|
|
||||||
can, _ := u.CanDelete("mbox")
|
can, _ := u.CanDelete("mbox")
|
||||||
require.True(t, can)
|
require.True(t, can)
|
||||||
@ -38,7 +38,7 @@ func TestUpdatesCanDelete(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdatesCannotDelete(t *testing.T) {
|
func TestUpdatesCannotDelete(t *testing.T) {
|
||||||
u := newIMAPUpdates(nil)
|
u := newIMAPUpdates()
|
||||||
|
|
||||||
u.forbidExpunge("mbox")
|
u.forbidExpunge("mbox")
|
||||||
can, wait := u.CanDelete("mbox")
|
can, wait := u.CanDelete("mbox")
|
||||||
|
|||||||
@ -53,9 +53,6 @@ type imapUser struct {
|
|||||||
// not cause huge slow down as EXPUNGE is implicitly called also after
|
// not cause huge slow down as EXPUNGE is implicitly called also after
|
||||||
// UNSELECT, CLOSE, or LOGOUT.
|
// UNSELECT, CLOSE, or LOGOUT.
|
||||||
appendExpungeLock sync.Mutex
|
appendExpungeLock sync.Mutex
|
||||||
|
|
||||||
addressID string // cached value for logs to avoid lock
|
|
||||||
mailboxIDs safeMapOfStrings // cached values for logs to avoid lock
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newIMAPUser returns struct implementing go-imap/user interface.
|
// newIMAPUser returns struct implementing go-imap/user interface.
|
||||||
@ -87,8 +84,6 @@ func newIMAPUser(
|
|||||||
storeAddress: storeAddress,
|
storeAddress: storeAddress,
|
||||||
|
|
||||||
currentAddressLowercase: strings.ToLower(address),
|
currentAddressLowercase: strings.ToLower(address),
|
||||||
addressID: addressID,
|
|
||||||
mailboxIDs: newSafeMapOfString(),
|
|
||||||
}, err
|
}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,12 +128,6 @@ func (iu *imapUser) ListMailboxes(showOnlySubcribed bool) ([]goIMAPBackend.Mailb
|
|||||||
|
|
||||||
mailboxes := []goIMAPBackend.Mailbox{}
|
mailboxes := []goIMAPBackend.Mailbox{}
|
||||||
for _, storeMailbox := range iu.storeAddress.ListMailboxes() {
|
for _, storeMailbox := range iu.storeAddress.ListMailboxes() {
|
||||||
iu.mailboxIDs.set(storeMailbox.Name(), storeMailbox.LabelID())
|
|
||||||
|
|
||||||
if storeMailbox.LabelID() == pmapi.AllMailLabel && !iu.backend.bridge.IsAllMailVisible() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if showOnlySubcribed && !iu.isSubscribed(storeMailbox.LabelID()) {
|
if showOnlySubcribed && !iu.isSubscribed(storeMailbox.LabelID()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@ -107,7 +107,7 @@ func (l *Locations) getLicenseFilePath() string {
|
|||||||
|
|
||||||
// GetDependencyLicensesLink returns link to page listing dependencies.
|
// GetDependencyLicensesLink returns link to page listing dependencies.
|
||||||
func (l *Locations) GetDependencyLicensesLink() string {
|
func (l *Locations) GetDependencyLicensesLink() string {
|
||||||
return "https://github.com/ProtonMail/proton-bridge/blob/master/COPYING_NOTES.md#dependencies"
|
return "https://github.com/ProtonMail/proton-bridge/v2/blob/master/COPYING_NOTES.md#dependencies"
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProvideSettingsPath returns a location for user settings (e.g. ~/.config/<company>/<app>).
|
// ProvideSettingsPath returns a location for user settings (e.g. ~/.config/<company>/<app>).
|
||||||
|
|||||||
14
internal/store/cache/disk.go
vendored
14
internal/store/cache/disk.go
vendored
@ -21,6 +21,7 @@ import (
|
|||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -28,7 +29,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/algo"
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/semaphore"
|
"github.com/ProtonMail/proton-bridge/v2/pkg/semaphore"
|
||||||
"github.com/ricochet2200/go-disk-usage/du"
|
"github.com/ricochet2200/go-disk-usage/du"
|
||||||
)
|
)
|
||||||
@ -100,7 +100,13 @@ func (c *onDiskCache) Lock(userID string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *onDiskCache) Unlock(userID string, passphrase []byte) error {
|
func (c *onDiskCache) Unlock(userID string, passphrase []byte) error {
|
||||||
aes, err := aes.NewCipher(algo.Hash256(passphrase))
|
hash := sha256.New()
|
||||||
|
|
||||||
|
if _, err := hash.Write(passphrase); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
aes, err := aes.NewCipher(hash.Sum(nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -273,9 +279,9 @@ func (c *onDiskCache) update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *onDiskCache) getUserPath(userID string) string {
|
func (c *onDiskCache) getUserPath(userID string) string {
|
||||||
return filepath.Join(c.path, algo.HashHexSHA256(userID))
|
return filepath.Join(c.path, getHash(userID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *onDiskCache) getMessagePath(userID, messageID string) string {
|
func (c *onDiskCache) getMessagePath(userID, messageID string) string {
|
||||||
return filepath.Join(c.getUserPath(userID), algo.HashHexSHA256(messageID))
|
return filepath.Join(c.getUserPath(userID), getHash(messageID))
|
||||||
}
|
}
|
||||||
|
|||||||
23
pkg/algo/hash.go → internal/store/cache/hash.go
vendored
23
pkg/algo/hash.go → internal/store/cache/hash.go
vendored
@ -15,27 +15,20 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package algo
|
package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Hash256(b []byte) []byte {
|
func getHash(name string) string {
|
||||||
h := sha256.Sum256(b)
|
hash := sha256.New()
|
||||||
return h[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func HashBase64SHA256(s string) string {
|
if _, err := hash.Write([]byte(name)); err != nil {
|
||||||
return base64.StdEncoding.EncodeToString(
|
// sha256.Write always returns nill err so this should never happen
|
||||||
Hash256([]byte(s)),
|
panic(err)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func HashHexSHA256(s string) string {
|
return hex.EncodeToString(hash.Sum(nil))
|
||||||
return hex.EncodeToString(
|
|
||||||
Hash256([]byte(s)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
@ -18,7 +18,8 @@
|
|||||||
package message
|
package message
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/algo"
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
)
|
)
|
||||||
|
|
||||||
type boundary struct {
|
type boundary struct {
|
||||||
@ -30,6 +31,13 @@ func newBoundary(seed string) *boundary {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bw *boundary) gen() string {
|
func (bw *boundary) gen() string {
|
||||||
bw.val = algo.HashHexSHA256(bw.val)
|
hash := sha256.New()
|
||||||
|
|
||||||
|
if _, err := hash.Write([]byte(bw.val)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bw.val = hex.EncodeToString(hash.Sum(nil))
|
||||||
|
|
||||||
return bw.val
|
return bw.val
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,13 +18,13 @@
|
|||||||
package pmapi
|
package pmapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/algo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrTLSMismatch indicates that no TLS fingerprint match could be found.
|
// ErrTLSMismatch indicates that no TLS fingerprint match could be found.
|
||||||
@ -63,5 +63,6 @@ func (p *pinChecker) checkCertificate(conn net.Conn) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func certFingerprint(cert *x509.Certificate) string {
|
func certFingerprint(cert *x509.Certificate) string {
|
||||||
return fmt.Sprintf(`pin-sha256=%q`, algo.HashBase64SHA256(string(cert.RawSubjectPublicKeyInfo)))
|
hash := sha256.Sum256(cert.RawSubjectPublicKeyInfo)
|
||||||
|
return fmt.Sprintf(`pin-sha256=%q`, base64.StdEncoding.EncodeToString(hash[:]))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,7 +88,7 @@ func TestTLSSignedCertTrustedPublicKey(t *testing.T) {
|
|||||||
|
|
||||||
_, dialer, _ := createClientWithPinningDialer("")
|
_, dialer, _ := createClientWithPinningDialer("")
|
||||||
copyTrustedPins(dialer.pinChecker)
|
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="2opdB7b5INED5jS7duIDR7dM8Er99i7trnwKuW3GMCY="`)
|
||||||
_, err := dialer.DialTLS("tcp", "rsa4096.badssl.com:443")
|
_, 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")
|
r.NoError(t, err, "expected dial to succeed because public key is known and cert is signed by CA")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -90,14 +90,7 @@ type ImportMsgRes struct {
|
|||||||
|
|
||||||
// Import imports messages to the user's account.
|
// Import imports messages to the user's account.
|
||||||
func (c *client) Import(ctx context.Context, reqs ImportMsgReqs) ([]*ImportMsgRes, error) {
|
func (c *client) Import(ctx context.Context, reqs ImportMsgReqs) ([]*ImportMsgRes, error) {
|
||||||
if len(reqs) == 0 {
|
|
||||||
return nil, errors.New("missing import requests")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(reqs) > MaxImportMessageRequestLength {
|
if len(reqs) > MaxImportMessageRequestLength {
|
||||||
log.
|
|
||||||
WithField("count", len(reqs)).
|
|
||||||
Warn("Importing too many messages at once.")
|
|
||||||
return nil, errors.New("request is too long")
|
return nil, errors.New("request is too long")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,10 +98,6 @@ func (c *client) Import(ctx context.Context, reqs ImportMsgReqs) ([]*ImportMsgRe
|
|||||||
for _, req := range reqs {
|
for _, req := range reqs {
|
||||||
remainingSize -= len(req.Message)
|
remainingSize -= len(req.Message)
|
||||||
if remainingSize < 0 {
|
if remainingSize < 0 {
|
||||||
log.
|
|
||||||
WithField("count", len(reqs)).
|
|
||||||
WithField("size", MaxImportMessageRequestLength-remainingSize).
|
|
||||||
Warn("Importing too big message(s)")
|
|
||||||
return nil, errors.New("request size is too big")
|
return nil, errors.New("request size is too big")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
.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 GO111MODULE=on
|
||||||
export BRIDGE_VERSION:=2.3.0+integrationtests
|
export BRIDGE_VERSION:=2.2.1+integrationtests
|
||||||
export VERBOSITY?=fatal
|
export VERBOSITY?=fatal
|
||||||
export TEST_DATA=testdata
|
export TEST_DATA=testdata
|
||||||
|
|
||||||
|
|||||||
@ -24,8 +24,6 @@ import (
|
|||||||
func BridgeActionsFeatureContext(s *godog.ScenarioContext) {
|
func BridgeActionsFeatureContext(s *godog.ScenarioContext) {
|
||||||
s.Step(`^bridge starts$`, bridgeStarts)
|
s.Step(`^bridge starts$`, bridgeStarts)
|
||||||
s.Step(`^bridge syncs "([^"]*)"$`, bridgeSyncsUser)
|
s.Step(`^bridge syncs "([^"]*)"$`, bridgeSyncsUser)
|
||||||
s.Step(`^All mail mailbox is hidden$`, allMailMailboxIsHidden)
|
|
||||||
s.Step(`^All mail mailbox is visible$`, allMailMailboxIsVisible)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func bridgeStarts() error {
|
func bridgeStarts() error {
|
||||||
@ -44,13 +42,3 @@ func bridgeSyncsUser(bddUserID string) error {
|
|||||||
ctx.SetLastError(ctx.GetTestingError())
|
ctx.SetLastError(ctx.GetTestingError())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func allMailMailboxIsHidden() error {
|
|
||||||
ctx.GetBridge().SetIsAllMailVisible(false)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func allMailMailboxIsVisible() error {
|
|
||||||
ctx.GetBridge().SetIsAllMailVisible(true)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@ -80,10 +80,8 @@ func (ctl *Controller) AddUserLabel(username string, label *pmapi.Label) error {
|
|||||||
ctl.labelsByUsername[username] = []*pmapi.Label{}
|
ctl.labelsByUsername[username] = []*pmapi.Label{}
|
||||||
}
|
}
|
||||||
|
|
||||||
userLabels := ctl.labelsByUsername[username]
|
|
||||||
|
|
||||||
labelName := getLabelNameWithoutPrefix(label.Name)
|
labelName := getLabelNameWithoutPrefix(label.Name)
|
||||||
for _, existingLabel := range userLabels {
|
for _, existingLabel := range ctl.labelsByUsername[username] {
|
||||||
if existingLabel.Name == labelName {
|
if existingLabel.Name == labelName {
|
||||||
return fmt.Errorf("folder or label %s already exists", label.Name)
|
return fmt.Errorf("folder or label %s already exists", label.Name)
|
||||||
}
|
}
|
||||||
@ -99,9 +97,7 @@ func (ctl *Controller) AddUserLabel(username string, label *pmapi.Label) error {
|
|||||||
if label.Path == "" {
|
if label.Path == "" {
|
||||||
label.Path = label.Name
|
label.Path = label.Name
|
||||||
}
|
}
|
||||||
userLabels = append(userLabels, label)
|
ctl.labelsByUsername[username] = append(ctl.labelsByUsername[username], label)
|
||||||
|
|
||||||
ctl.labelsByUsername[username] = userLabels
|
|
||||||
ctl.resetUsers()
|
ctl.resetUsers()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,6 @@ package fakeapi
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
||||||
)
|
)
|
||||||
@ -88,23 +87,16 @@ func (api *FakePMAPI) listLabels(_ context.Context, labeType string, route strin
|
|||||||
if err := api.checkAndRecordCall(GET, route+"/"+labeType, nil); err != nil {
|
if err := api.checkAndRecordCall(GET, route+"/"+labeType, nil); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return append([]*pmapi.Label{}, api.labels...), nil
|
return api.labels, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *FakePMAPI) createLabel(_ context.Context, label *pmapi.Label, route string) (*pmapi.Label, error) {
|
func (api *FakePMAPI) createLabel(_ context.Context, label *pmapi.Label, route string) (*pmapi.Label, error) {
|
||||||
if err := api.checkAndRecordCall(POST, route, &pmapi.LabelReq{Label: label}); err != nil {
|
if err := api.checkAndRecordCall(POST, route, &pmapi.LabelReq{Label: label}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// API blocks certain names
|
|
||||||
switch strings.ToLower(label.Name) {
|
|
||||||
case "inbox", "drafts", "trash", "spam", "starred":
|
|
||||||
return nil, fmt.Errorf("Invalid name") //nolint:stylecheck
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, existingLabel := range api.labels {
|
for _, existingLabel := range api.labels {
|
||||||
if existingLabel.Name == label.Name {
|
if existingLabel.Name == label.Name {
|
||||||
return nil, fmt.Errorf("A label or folder with this name already exists") //nolint:stylecheck
|
return nil, fmt.Errorf("folder or label %s already exists", label.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prefix := "label"
|
prefix := "label"
|
||||||
|
|||||||
@ -17,34 +17,11 @@ Feature: IMAP create mailbox
|
|||||||
And "user" does not have mailbox "Folders/mbox"
|
And "user" does not have mailbox "Folders/mbox"
|
||||||
And "user" has mailbox "Labels/mbox"
|
And "user" has mailbox "Labels/mbox"
|
||||||
|
|
||||||
Scenario: Creating label with existing name is not possible
|
|
||||||
Given there is "user" with mailbox "Folders/mbox"
|
|
||||||
When IMAP client creates mailbox "Labels/mbox"
|
|
||||||
Then IMAP response is "IMAP error: NO A label or folder with this name already exists"
|
|
||||||
And "user" has mailbox "Folders/mbox"
|
|
||||||
And "user" does not have mailbox "Labels/mbox"
|
|
||||||
|
|
||||||
Scenario: Creating folder with existing name is not possible
|
|
||||||
Given there is "user" with mailbox "Labels/mbox"
|
|
||||||
When IMAP client creates mailbox "Folders/mbox"
|
|
||||||
Then IMAP response is "IMAP error: NO A label or folder with this name already exists"
|
|
||||||
And "user" has mailbox "Labels/mbox"
|
|
||||||
And "user" does not have mailbox "Folders/mbox"
|
|
||||||
|
|
||||||
Scenario: Creating system mailbox is not possible
|
Scenario: Creating system mailbox is not possible
|
||||||
When IMAP client creates mailbox "INBOX"
|
When IMAP client creates mailbox "INBOX"
|
||||||
Then IMAP response is "IMAP error: NO mailbox INBOX already exists"
|
Then IMAP response is "IMAP error: NO mailbox INBOX already exists"
|
||||||
When IMAP client creates mailbox "Folders/INBOX"
|
|
||||||
Then IMAP response is "IMAP error: NO Invalid name"
|
|
||||||
# API allows you to create custom folder with naem `All Mail`
|
|
||||||
#When IMAP client creates mailbox "Folders/All mail"
|
|
||||||
#Then IMAP response is "IMAP error: NO mailbox All Mail already exists"
|
|
||||||
|
|
||||||
Scenario: Creating mailbox without prefix is not possible
|
Scenario: Creating mailbox without prefix is not possible
|
||||||
When IMAP client creates mailbox "mbox"
|
When IMAP client creates mailbox "mbox"
|
||||||
Then IMAP response is "OK"
|
Then IMAP response is "OK"
|
||||||
And "user" does not have mailbox "mbox"
|
And "user" does not have mailbox "mbox"
|
||||||
When All mail mailbox is hidden
|
|
||||||
And IMAP client creates mailbox "All mail"
|
|
||||||
Then IMAP response is "OK"
|
|
||||||
And "user" does not have mailbox "All mail"
|
|
||||||
|
|||||||
@ -15,120 +15,6 @@ Feature: IMAP list mailboxes
|
|||||||
Then IMAP response contains "Folders/mbox1"
|
Then IMAP response contains "Folders/mbox1"
|
||||||
Then IMAP response contains "Labels/mbox2"
|
Then IMAP response contains "Labels/mbox2"
|
||||||
|
|
||||||
Scenario: List mailboxes without All Mail
|
|
||||||
Given there is IMAP client logged in as "user"
|
|
||||||
When IMAP client lists mailboxes
|
|
||||||
Then IMAP response contains "INBOX"
|
|
||||||
Then IMAP response contains "Sent"
|
|
||||||
Then IMAP response contains "Archive"
|
|
||||||
Then IMAP response contains "Trash"
|
|
||||||
Then IMAP response contains "All Mail"
|
|
||||||
When All mail mailbox is hidden
|
|
||||||
And IMAP client lists mailboxes
|
|
||||||
Then IMAP response contains "INBOX"
|
|
||||||
Then IMAP response contains "Sent"
|
|
||||||
Then IMAP response contains "Archive"
|
|
||||||
Then IMAP response contains "Trash"
|
|
||||||
Then IMAP response doesn't contain "All Mail"
|
|
||||||
When All mail mailbox is visible
|
|
||||||
And IMAP client lists mailboxes
|
|
||||||
Then IMAP response contains "INBOX"
|
|
||||||
Then IMAP response contains "Sent"
|
|
||||||
Then IMAP response contains "Archive"
|
|
||||||
Then IMAP response contains "Trash"
|
|
||||||
Then IMAP response contains "All Mail"
|
|
||||||
|
|
||||||
Scenario: List multiple times in parallel without crash
|
|
||||||
Given there is "user" with mailboxes
|
|
||||||
| Folders/mbox1 |
|
|
||||||
| Folders/mbox2 |
|
|
||||||
| Folders/mbox3 |
|
|
||||||
| Folders/mbox4 |
|
|
||||||
| Folders/mbox5 |
|
|
||||||
| Folders/mbox6 |
|
|
||||||
| Folders/mbox7 |
|
|
||||||
| Folders/mbox8 |
|
|
||||||
| Folders/mbox9 |
|
|
||||||
| Folders/mbox10 |
|
|
||||||
| Folders/mbox11 |
|
|
||||||
| Folders/mbox12 |
|
|
||||||
| Folders/mbox13 |
|
|
||||||
| Folders/mbox14 |
|
|
||||||
| Folders/mbox15 |
|
|
||||||
| Folders/mbox16 |
|
|
||||||
| Folders/mbox17 |
|
|
||||||
| Folders/mbox18 |
|
|
||||||
| Folders/mbox19 |
|
|
||||||
| Folders/mbox20 |
|
|
||||||
| Labels/lab1 |
|
|
||||||
| Labels/lab2 |
|
|
||||||
| Labels/lab3 |
|
|
||||||
| Labels/lab4 |
|
|
||||||
| Labels/lab5 |
|
|
||||||
| Labels/lab6 |
|
|
||||||
| Labels/lab7 |
|
|
||||||
| Labels/lab8 |
|
|
||||||
| Labels/lab9 |
|
|
||||||
| Labels/lab10 |
|
|
||||||
| Labels/lab11 |
|
|
||||||
| Labels/lab12 |
|
|
||||||
| Labels/lab13 |
|
|
||||||
| Labels/lab14 |
|
|
||||||
| Labels/lab15 |
|
|
||||||
| Labels/lab16 |
|
|
||||||
| Labels/lab17 |
|
|
||||||
| Labels/lab18 |
|
|
||||||
| Labels/lab19 |
|
|
||||||
| Labels/lab20 |
|
|
||||||
| Labels/lab1.1 |
|
|
||||||
| Labels/lab1.2 |
|
|
||||||
| Labels/lab1.3 |
|
|
||||||
| Labels/lab1.4 |
|
|
||||||
| Labels/lab1.5 |
|
|
||||||
| Labels/lab1.6 |
|
|
||||||
| Labels/lab1.7 |
|
|
||||||
| Labels/lab1.8 |
|
|
||||||
| Labels/lab1.9 |
|
|
||||||
| Labels/lab1.10 |
|
|
||||||
| Labels/lab1.11 |
|
|
||||||
| Labels/lab1.12 |
|
|
||||||
| Labels/lab1.13 |
|
|
||||||
| Labels/lab1.14 |
|
|
||||||
| Labels/lab1.15 |
|
|
||||||
| Labels/lab1.16 |
|
|
||||||
| Labels/lab1.17 |
|
|
||||||
| Labels/lab1.18 |
|
|
||||||
| Labels/lab1.19 |
|
|
||||||
| Labels/lab1.20 |
|
|
||||||
| Labels/lab2.1 |
|
|
||||||
| Labels/lab2.2 |
|
|
||||||
| Labels/lab2.3 |
|
|
||||||
| Labels/lab2.4 |
|
|
||||||
| Labels/lab2.5 |
|
|
||||||
| Labels/lab2.6 |
|
|
||||||
| Labels/lab2.7 |
|
|
||||||
| Labels/lab2.8 |
|
|
||||||
| Labels/lab2.9 |
|
|
||||||
| Labels/lab2.10 |
|
|
||||||
| Labels/lab2.11 |
|
|
||||||
| Labels/lab2.12 |
|
|
||||||
| Labels/lab2.13 |
|
|
||||||
| Labels/lab2.14 |
|
|
||||||
| Labels/lab2.15 |
|
|
||||||
| Labels/lab2.16 |
|
|
||||||
| Labels/lab2.17 |
|
|
||||||
| Labels/lab2.18 |
|
|
||||||
| Labels/lab2.19 |
|
|
||||||
| Labels/lab2.20 |
|
|
||||||
And there is IMAP client "A" logged in as "user"
|
|
||||||
And there is IMAP client "B" logged in as "user"
|
|
||||||
When IMAP client "A" lists mailboxes
|
|
||||||
And IMAP client "B" lists mailboxes
|
|
||||||
Then IMAP response to "A" is "OK"
|
|
||||||
And IMAP response to "A" contains "mbox1"
|
|
||||||
And IMAP response to "A" contains "mbox10"
|
|
||||||
And IMAP response to "A" contains "mbox20"
|
|
||||||
|
|
||||||
@ignore-live
|
@ignore-live
|
||||||
Scenario: List mailboxes with subfolders
|
Scenario: List mailboxes with subfolders
|
||||||
# Escaped slash in the name contains slash in the name.
|
# Escaped slash in the name contains slash in the name.
|
||||||
|
|||||||
@ -28,7 +28,6 @@ func IMAPActionsMailboxFeatureContext(s *godog.ScenarioContext) {
|
|||||||
s.Step(`^IMAP client renames mailbox "([^"]*)" to "([^"]*)"$`, imapClientRenamesMailboxTo)
|
s.Step(`^IMAP client renames mailbox "([^"]*)" to "([^"]*)"$`, imapClientRenamesMailboxTo)
|
||||||
s.Step(`^IMAP client deletes mailbox "([^"]*)"$`, imapClientDeletesMailbox)
|
s.Step(`^IMAP client deletes mailbox "([^"]*)"$`, imapClientDeletesMailbox)
|
||||||
s.Step(`^IMAP client lists mailboxes$`, imapClientListsMailboxes)
|
s.Step(`^IMAP client lists mailboxes$`, imapClientListsMailboxes)
|
||||||
s.Step(`^IMAP client "([^"]*)" lists mailboxes$`, imapClientNamedListsMailboxes)
|
|
||||||
s.Step(`^IMAP client selects "([^"]*)"$`, imapClientSelects)
|
s.Step(`^IMAP client selects "([^"]*)"$`, imapClientSelects)
|
||||||
s.Step(`^IMAP client gets info of "([^"]*)"$`, imapClientGetsInfoOf)
|
s.Step(`^IMAP client gets info of "([^"]*)"$`, imapClientGetsInfoOf)
|
||||||
s.Step(`^IMAP client "([^"]*)" gets info of "([^"]*)"$`, imapClientNamedGetsInfoOf)
|
s.Step(`^IMAP client "([^"]*)" gets info of "([^"]*)"$`, imapClientNamedGetsInfoOf)
|
||||||
@ -64,12 +63,8 @@ func imapClientDeletesMailbox(mailboxName string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func imapClientListsMailboxes() error {
|
func imapClientListsMailboxes() error {
|
||||||
return imapClientNamedListsMailboxes("imap")
|
res := ctx.GetIMAPClient("imap").ListMailboxes()
|
||||||
}
|
ctx.SetIMAPLastResponse("imap", res)
|
||||||
|
|
||||||
func imapClientNamedListsMailboxes(clientName string) error {
|
|
||||||
res := ctx.GetIMAPClient(clientName).ListMailboxes()
|
|
||||||
ctx.SetIMAPLastResponse(clientName, res)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -33,7 +33,7 @@ func IMAPChecksFeatureContext(s *godog.ScenarioContext) {
|
|||||||
s.Step(`^IMAP response to "([^"]*)" contains "([^"]*)"$`, imapResponseNamedContains)
|
s.Step(`^IMAP response to "([^"]*)" contains "([^"]*)"$`, imapResponseNamedContains)
|
||||||
s.Step(`^IMAP response has (\d+) message(?:s)?$`, imapResponseHasNumberOfMessages)
|
s.Step(`^IMAP response has (\d+) message(?:s)?$`, imapResponseHasNumberOfMessages)
|
||||||
s.Step(`^IMAP response to "([^"]*)" has (\d+) message(?:s)?$`, imapResponseNamedHasNumberOfMessages)
|
s.Step(`^IMAP response to "([^"]*)" has (\d+) message(?:s)?$`, imapResponseNamedHasNumberOfMessages)
|
||||||
s.Step(`^IMAP response does(?: not|n't) contain "([^"]*)"$`, imapResponseDoesNotContain)
|
s.Step(`^IMAP response does not contain "([^"]*)"$`, imapResponseDoesNotContain)
|
||||||
s.Step(`^IMAP response to "([^"]*)" does not contain "([^"]*)"$`, imapResponseNamedDoesNotContain)
|
s.Step(`^IMAP response to "([^"]*)" does not contain "([^"]*)"$`, imapResponseNamedDoesNotContain)
|
||||||
s.Step(`^IMAP client receives update marking message seq "([^"]*)" as read within (\d+) seconds$`, imapClientReceivesUpdateMarkingMessageSeqAsReadWithin)
|
s.Step(`^IMAP client receives update marking message seq "([^"]*)" as read within (\d+) seconds$`, imapClientReceivesUpdateMarkingMessageSeqAsReadWithin)
|
||||||
s.Step(`^IMAP client "([^"]*)" receives update marking message seq "([^"]*)" as read within (\d+) seconds$`, imapClientNamedReceivesUpdateMarkingMessageSeqAsReadWithin)
|
s.Step(`^IMAP client "([^"]*)" receives update marking message seq "([^"]*)" as read within (\d+) seconds$`, imapClientNamedReceivesUpdateMarkingMessageSeqAsReadWithin)
|
||||||
|
|||||||
@ -1,9 +1,5 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
file:
|
|
||||||
name: "./gobinsec-cache.yml"
|
|
||||||
expiration: 24h
|
|
||||||
|
|
||||||
ignore:
|
ignore:
|
||||||
# golang.org/x/net wrong match, we are using 2871e0cb, fixed by 37e1c6af
|
# golang.org/x/net wrong match, we are using 2871e0cb, fixed by 37e1c6af
|
||||||
- "CVE-2021-33194"
|
- "CVE-2021-33194"
|
||||||
|
|||||||
Reference in New Issue
Block a user