From e1dff67c1019630560a10dce6d2ee08fe509d587 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 16 Mar 2023 11:11:29 +0100 Subject: [PATCH 01/22] fix(GODT-2481): Fix DBUS Secert Service Fix the path we are checking for was not updated for V3. Ensure that we only inspect items that start with the correct prefix. Some implementation (e.g.: KeepassXC) return some values which are not valid. Finally, remove unnecessary attributes. --- pkg/keychain/helper_dbus_linux.go | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/pkg/keychain/helper_dbus_linux.go b/pkg/keychain/helper_dbus_linux.go index b759c7f4..bdd1f53e 100644 --- a/pkg/keychain/helper_dbus_linux.go +++ b/pkg/keychain/helper_dbus_linux.go @@ -20,6 +20,8 @@ package keychain import ( "strings" + "github.com/ProtonMail/proton-bridge/v3/internal/constants" + "github.com/bradenaw/juniper/xslices" "github.com/docker/docker-credential-helpers/credentials" "github.com/godbus/dbus" "github.com/keybase/go-keychain/secretservice" @@ -30,10 +32,13 @@ const ( labelAtt = "label" usernameAtt = "username" - defaulDomain = "protonmail/bridge/users/" - defaultLabel = "Docker Credentials" + defaultLabel = "Proton Mail Bridge Credentials" ) +func getDomain() string { + return hostURL(constants.KeyChainName) +} + func getSession() (*secretservice.SecretService, *secretservice.Session, error) { service, err := secretservice.NewService() if err != nil { @@ -73,8 +78,9 @@ func getItems(service *secretservice.SecretService, attributes map[string]string if err != nil { return nil, err } - - return items, err + return xslices.Filter(items, func(t dbus.ObjectPath) bool { + return strings.HasPrefix(string(t), "/org/freedesktop/secrets") + }), err } func unlock(service *secretservice.SecretService) error { @@ -105,11 +111,9 @@ func (s *SecretServiceDBusHelper) Add(creds *credentials.Credentials) error { } attributes := map[string]string{ - usernameAtt: creds.Username, - serverAtt: creds.ServerURL, - labelAtt: defaultLabel, - "xdg:schema": "io.docker.Credentials", - "docker_cli": "1", + usernameAtt: creds.Username, + serverAtt: creds.ServerURL, + labelAtt: defaultLabel, } return handleTimeout(func() error { @@ -203,13 +207,15 @@ func (s *SecretServiceDBusHelper) List() (map[string]string, error) { return nil, err } + defaultDomain := getDomain() + for _, it := range items { attributes, err := service.GetAttributes(it) if err != nil { return nil, err } - if !strings.HasPrefix(attributes[serverAtt], defaulDomain) { + if !strings.HasPrefix(attributes[serverAtt], defaultDomain) { continue } From 622b76b57b7103576a387c642d3b402298dceea2 Mon Sep 17 00:00:00 2001 From: Xavier Michelon Date: Wed, 15 Mar 2023 12:02:25 +0100 Subject: [PATCH 02/22] feat(GODT-2483): install cert without external tool on macOS. --- internal/certs/cert_store_darwin.go | 166 ++++++++++++++++------- internal/certs/cert_store_darwin_test.go | 44 ++++++ 2 files changed, 163 insertions(+), 47 deletions(-) create mode 100644 internal/certs/cert_store_darwin_test.go diff --git a/internal/certs/cert_store_darwin.go b/internal/certs/cert_store_darwin.go index c034adec..8e1f9570 100644 --- a/internal/certs/cert_store_darwin.go +++ b/internal/certs/cert_store_darwin.go @@ -17,69 +17,141 @@ package certs -import ( - "os" +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation -framework Security +#import +#import - "golang.org/x/sys/execabs" + +int installTrustedCert(char const *bytes, unsigned long long length) { + if (length == 0) { + return errSecInvalidData; + } + + NSData *der = [NSData dataWithBytes:bytes length:length]; + + // Step 1. Import the certificate in the keychain. + SecCertificateRef cert = SecCertificateCreateWithData(NULL, (CFDataRef) der); + NSDictionary* addQuery = @{ + (id)kSecValueRef: (__bridge id) cert, + (id)kSecClass: (id)kSecClassCertificate, + }; + + OSStatus status = SecItemAdd((__bridge CFDictionaryRef) addQuery, NULL); + if ((errSecSuccess != status) && (errSecDuplicateItem != status)) { + CFRelease(cert); + return status; + } + + // Step 2. Set the trust for the certificate. + SecPolicyRef policy = SecPolicyCreateSSL(true, NULL); // we limit our trust to SSL + NSDictionary *trustSettings = @{ + (id)kSecTrustSettingsResult: [NSNumber numberWithInt:kSecTrustSettingsResultTrustRoot], + (id)kSecTrustSettingsPolicy: (__bridge id) policy, + }; + status = SecTrustSettingsSetTrustSettings(cert, kSecTrustSettingsDomainAdmin, (__bridge CFTypeRef)(trustSettings)); + CFRelease(policy); + CFRelease(cert); + + return status; +} + + +int removeTrustedCert(char const *bytes, unsigned long long length) { + if (0 == length) { + return errSecInvalidData; + } + + NSData *der = [NSData dataWithBytes: bytes length: length]; + SecCertificateRef cert = SecCertificateCreateWithData(NULL, (CFDataRef) der); + + // Step 1. Unset the trust for the certificate. + SecPolicyRef policy = SecPolicyCreateSSL(true, NULL); + NSDictionary * trustSettings = @{ + (id)kSecTrustSettingsResult: [NSNumber numberWithInt:kSecTrustSettingsResultUnspecified], + (id)kSecTrustSettingsPolicy: (__bridge id) policy, + }; + OSStatus status = SecTrustSettingsSetTrustSettings(cert, kSecTrustSettingsDomainAdmin, (__bridge CFTypeRef)(trustSettings)); + CFRelease(policy); + if (errSecSuccess != status) { + CFRelease(cert); + return status; + } + + // Step 2. Remove the certificate from the keychain. + NSDictionary *query = @{ (id)kSecClass: (id)kSecClassCertificate, + (id)kSecMatchItemList: @[(__bridge id)cert], + (id)kSecMatchLimit: (id)kSecMatchLimitOne, + }; + status = SecItemDelete((__bridge CFDictionaryRef) query); + + CFRelease(cert); + return status; +} +*/ +import "C" + +import ( + "encoding/pem" + "errors" + "fmt" + "unsafe" ) +// some of the error codes returned by Apple's Security framework. +const ( + errSecSuccess = 0 + errAuthorizationCanceled = -60006 +) + +// certPEMToDER converts a certificate in PEM format to DER format, which is the format required by Apple's Security framework. +func certPEMToDER(certPEM []byte) ([]byte, error) { + + block, left := pem.Decode(certPEM) + if block == nil { + return []byte{}, errors.New("invalid PEM certificate") + } + + if len(left) > 0 { + return []byte{}, errors.New("trailing data found at the end of a PEM certificate") + } + + return block.Bytes, nil +} + func installCert(certPEM []byte) error { - name, err := writeToTempFile(certPEM) + certDER, err := certPEMToDER(certPEM) if err != nil { return err } - return addTrustedCert(name) + p := C.CBytes(certDER) + defer C.free(unsafe.Pointer(p)) + + errCode := C.installTrustedCert((*C.char)(p), (C.ulonglong)(len(certDER))) + switch errCode { + case errSecSuccess: + return nil + case errAuthorizationCanceled: + return fmt.Errorf("the user cancelled the authorization dialog") + default: + return fmt.Errorf("could not install certification into keychain (error %v)", errCode) + } } func uninstallCert(certPEM []byte) error { - name, err := writeToTempFile(certPEM) + certDER, err := certPEMToDER(certPEM) if err != nil { return err } - return removeTrustedCert(name) -} + p := C.CBytes(certDER) + defer C.free(unsafe.Pointer(p)) -func addTrustedCert(certPath string) error { - return execabs.Command( //nolint:gosec - "/usr/bin/security", - "execute-with-privileges", - "/usr/bin/security", - "add-trusted-cert", - "-d", - "-r", "trustRoot", - "-p", "ssl", - "-k", "/Library/Keychains/System.keychain", - certPath, - ).Run() -} - -func removeTrustedCert(certPath string) error { - return execabs.Command( //nolint:gosec - "/usr/bin/security", - "execute-with-privileges", - "/usr/bin/security", - "remove-trusted-cert", - "-d", - certPath, - ).Run() -} - -// writeToTempFile writes the given data to a temporary file and returns the path. -func writeToTempFile(data []byte) (string, error) { - f, err := os.CreateTemp("", "tls") - if err != nil { - return "", err + if errCode := C.removeTrustedCert((*C.char)(p), (C.ulonglong)(len(certDER))); errCode != 0 { + return fmt.Errorf("could not install certificate from keychain (error %v)", errCode) } - if _, err := f.Write(data); err != nil { - return "", err - } - - if err := f.Close(); err != nil { - return "", err - } - - return f.Name(), nil + return nil } diff --git a/internal/certs/cert_store_darwin_test.go b/internal/certs/cert_store_darwin_test.go new file mode 100644 index 00000000..9ac9c698 --- /dev/null +++ b/internal/certs/cert_store_darwin_test.go @@ -0,0 +1,44 @@ +// Copyright (c) 2023 Proton AG +// +// This file is part of Proton Mail Bridge. +// +// Proton Mail Bridge is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Proton Mail Bridge is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Proton Mail Bridge. If not, see . + +//go:build darwin + +package certs + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +// This test implies human interactions to enter password and is disabled by default. +func _TestTrustedCertsDarwin(t *testing.T) { + template, err := NewTLSTemplate() + require.NoError(t, err) + + certPEM, _, err := GenerateCert(template) + require.NoError(t, err) + + require.Error(t, installCert([]byte{0})) // Cannot install an invalid cert. + require.Error(t, uninstallCert(certPEM)) // Cannot uninstall a cert that is not installed. + require.NoError(t, installCert(certPEM)) // Can install a valid cert. + require.NoError(t, installCert(certPEM)) // Can install an already installed cert. + require.NoError(t, uninstallCert(certPEM)) // Can uninstall an installed cert. + require.Error(t, uninstallCert(certPEM)) // Cannot uninstall an already uninstalled cert. + require.NoError(t, installCert(certPEM)) // Can reinstall an uninstalled cert. + require.NoError(t, uninstallCert(certPEM)) // Can uninstall a reinstalled cert. +} From b3d0dfc60b3c4e10bd302f34eb4cb329efcfafb1 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 16 Mar 2023 14:37:50 +0100 Subject: [PATCH 03/22] fix(GODT-2497): Do not report EOF and network errors --- internal/user/user.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/internal/user/user.go b/internal/user/user.go index 168967ef..2d0769cb 100644 --- a/internal/user/user.go +++ b/internal/user/user.go @@ -678,11 +678,6 @@ func (user *User) doEventPoll(ctx context.Context) error { // Catch all for uncategorized net errors that may slip through. if netErr := new(net.OpError); errors.As(err, &netErr) { - user.eventCh.Enqueue(events.UncategorizedEventError{ - UserID: user.ID(), - Error: err, - }) - return fmt.Errorf("failed to handle event due to network issues (uncategorized): %w", err) } @@ -698,11 +693,6 @@ func (user *User) doEventPoll(ctx context.Context) error { // If the error is an unexpected EOF, return error to retry later. if errors.Is(err, io.ErrUnexpectedEOF) { - user.eventCh.Enqueue(events.UncategorizedEventError{ - UserID: user.ID(), - Error: err, - }) - return fmt.Errorf("failed to handle event due to EOF: %w", err) } From 02ca6428b5920c2662436f397a7269f3d93ecc84 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Fri, 17 Mar 2023 13:11:07 +0100 Subject: [PATCH 04/22] fix(GODT-2407): Replace invalid email addresses with emtpy for new Drafts --- internal/user/imap.go | 2 +- internal/user/smtp.go | 2 +- pkg/message/parser.go | 51 ++++++++++++++++++++++++++++++-------- pkg/message/parser_test.go | 29 +++++++++++++++++++++- 4 files changed, 70 insertions(+), 14 deletions(-) diff --git a/internal/user/imap.go b/internal/user/imap.go index fb5cdaa3..8a62ba3a 100644 --- a/internal/user/imap.go +++ b/internal/user/imap.go @@ -626,7 +626,7 @@ func (conn *imapConnector) createDraft(ctx context.Context, literal []byte, addr return proton.Message{}, fmt.Errorf("failed to create parser: %w", err) } - message, err := message.ParseWithParser(parser) + message, err := message.ParseWithParser(parser, true) if err != nil { return proton.Message{}, fmt.Errorf("failed to parse message: %w", err) } diff --git a/internal/user/smtp.go b/internal/user/smtp.go index 1a0dd706..27eee7e9 100644 --- a/internal/user/smtp.go +++ b/internal/user/smtp.go @@ -137,7 +137,7 @@ func (user *User) sendMail(authID string, from string, to []string, r io.Reader) } // Parse the message we want to send (after we have attached the public key). - message, err := message.ParseWithParser(parser) + message, err := message.ParseWithParser(parser, false) if err != nil { return fmt.Errorf("failed to parse message: %w", err) } diff --git a/pkg/message/parser.go b/pkg/message/parser.go index cba83aee..ff6472c2 100644 --- a/pkg/message/parser.go +++ b/pkg/message/parser.go @@ -73,6 +73,15 @@ type Attachment struct { // Parse parses an RFC822 message. func Parse(r io.Reader) (m Message, err error) { + return parseIOReaderImpl(r, false) +} + +// ParseAndAllowInvalidAddressLists parses an RFC822 message and allows email address lists to be invalid. +func ParseAndAllowInvalidAddressLists(r io.Reader) (m Message, err error) { + return parseIOReaderImpl(r, true) +} + +func parseIOReaderImpl(r io.Reader, allowInvalidAddressLists bool) (m Message, err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("panic while parsing message: %v", r) @@ -84,21 +93,21 @@ func Parse(r io.Reader) (m Message, err error) { return Message{}, errors.Wrap(err, "failed to create new parser") } - return parse(p) + return parse(p, allowInvalidAddressLists) } // ParseWithParser parses an RFC822 message using an existing parser. -func ParseWithParser(p *parser.Parser) (m Message, err error) { +func ParseWithParser(p *parser.Parser, allowInvalidAddressLists bool) (m Message, err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("panic while parsing message: %v", r) } }() - return parse(p) + return parse(p, allowInvalidAddressLists) } -func parse(p *parser.Parser) (Message, error) { +func parse(p *parser.Parser, allowInvalidAddressLists bool) (Message, error) { if err := convertEncodedTransferEncoding(p); err != nil { return Message{}, errors.Wrap(err, "failed to convert encoded transfer encoding") } @@ -107,7 +116,7 @@ func parse(p *parser.Parser) (Message, error) { return Message{}, errors.Wrap(err, "failed to convert foreign encodings") } - m, err := parseMessageHeader(p.Root().Header) + m, err := parseMessageHeader(p.Root().Header, allowInvalidAddressLists) if err != nil { return Message{}, errors.Wrap(err, "failed to parse message header") } @@ -433,7 +442,7 @@ func getPlainBody(part *parser.Part) []byte { } } -func parseMessageHeader(h message.Header) (Message, error) { +func parseMessageHeader(h message.Header, allowInvalidAddressLists bool) (Message, error) { var m Message for fields := h.Fields(); fields.Next(); { @@ -451,7 +460,11 @@ func parseMessageHeader(h message.Header) (Message, error) { case "from": sender, err := rfc5322.ParseAddressList(fields.Value()) if err != nil { - return Message{}, errors.Wrap(err, "failed to parse from") + if !allowInvalidAddressLists { + return Message{}, errors.Wrap(err, "failed to parse from") + } + + logrus.WithError(err).Warn("failed to parse from") } if len(sender) > 0 { @@ -461,7 +474,11 @@ func parseMessageHeader(h message.Header) (Message, error) { case "to": toList, err := rfc5322.ParseAddressList(fields.Value()) if err != nil { - return Message{}, errors.Wrap(err, "failed to parse to") + if !allowInvalidAddressLists { + return Message{}, errors.Wrap(err, "failed to parse to") + } + + logrus.WithError(err).Warn("failed to parse to") } m.ToList = toList @@ -469,7 +486,11 @@ func parseMessageHeader(h message.Header) (Message, error) { case "reply-to": replyTos, err := rfc5322.ParseAddressList(fields.Value()) if err != nil { - return Message{}, errors.Wrap(err, "failed to parse reply-to") + if !allowInvalidAddressLists { + return Message{}, errors.Wrap(err, "failed to parse reply-to") + } + + logrus.WithError(err).Warn("failed to parse reply-to") } m.ReplyTos = replyTos @@ -477,7 +498,11 @@ func parseMessageHeader(h message.Header) (Message, error) { case "cc": ccList, err := rfc5322.ParseAddressList(fields.Value()) if err != nil { - return Message{}, errors.Wrap(err, "failed to parse cc") + if !allowInvalidAddressLists { + return Message{}, errors.Wrap(err, "failed to parse cc") + } + + logrus.WithError(err).Warn("failed to parse cc") } m.CCList = ccList @@ -485,7 +510,11 @@ func parseMessageHeader(h message.Header) (Message, error) { case "bcc": bccList, err := rfc5322.ParseAddressList(fields.Value()) if err != nil { - return Message{}, errors.Wrap(err, "failed to parse bcc") + if !allowInvalidAddressLists { + return Message{}, errors.Wrap(err, "failed to parse bcc") + } + + logrus.WithError(err).Warn("failed to parse bcc") } m.BCCList = bccList diff --git a/pkg/message/parser_test.go b/pkg/message/parser_test.go index 6942deee..d536333b 100644 --- a/pkg/message/parser_test.go +++ b/pkg/message/parser_test.go @@ -23,6 +23,7 @@ import ( "io" "os" "path/filepath" + "strings" "testing" "github.com/ProtonMail/go-proton-api" @@ -444,7 +445,7 @@ func TestParseWithAttachedPublicKey(t *testing.T) { p, err := parser.New(f) require.NoError(t, err) - m, err := ParseWithParser(p) + m, err := ParseWithParser(p, false) require.NoError(t, err) p.AttachPublicKey("publickey", "publickeyname") @@ -636,6 +637,32 @@ func TestParseIcsAttachment(t *testing.T) { assert.Equal(t, string(m.Attachments[0].Data), "This is an ics calendar invite") } +func TestParseAllowInvalidAddress(t *testing.T) { + const literal = `To: foo + From: bar + BCC: fff + CC: FFF + Reply-To: AAA + Subject: Test +` + + // This will fail as the addresses are not valid. + { + _, err := Parse(strings.NewReader(literal)) + require.Error(t, err) + } + + // This will work as invalid addresses will be ignored. + m, err := ParseAndAllowInvalidAddressLists(strings.NewReader(literal)) + require.NoError(t, err) + + assert.Empty(t, m.ToList) + assert.Empty(t, m.Sender) + assert.Empty(t, m.CCList) + assert.Empty(t, m.BCCList) + assert.Empty(t, m.ReplyTos) +} + func TestParsePanic(t *testing.T) { var err error From 4f49c87bc6913de5a58476ea120deaee7211c405 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Fri, 17 Mar 2023 15:23:22 +0100 Subject: [PATCH 05/22] chore: Replace go-rfc5322 with gluon's rfc5322 parser Bumps Gluon version in order to get the fixes from https://github.com/ProtonMail/gluon/pull/327. --- COPYING_NOTES.md | 2 -- go.mod | 4 +--- go.sum | 9 ++------- internal/user/smtp.go | 2 +- pkg/message/build.go | 2 +- pkg/message/parser.go | 2 +- pkg/message/parser_test.go | 10 +++++----- tests/ctx_test.go | 22 ++++++++++++++++++++++ tests/environment_test.go | 3 ++- 9 files changed, 35 insertions(+), 21 deletions(-) diff --git a/COPYING_NOTES.md b/COPYING_NOTES.md index 171fa8fd..94fac254 100644 --- a/COPYING_NOTES.md +++ b/COPYING_NOTES.md @@ -26,7 +26,6 @@ Proton Mail Bridge includes the following 3rd party software: * [gluon](https://github.com/ProtonMail/gluon) available under [license](https://github.com/ProtonMail/gluon/blob/master/LICENSE) * [go-autostart](https://github.com/ProtonMail/go-autostart) available under [license](https://github.com/ProtonMail/go-autostart/blob/master/LICENSE) * [go-proton-api](https://github.com/ProtonMail/go-proton-api) available under [license](https://github.com/ProtonMail/go-proton-api/blob/master/LICENSE) -* [go-rfc5322](https://github.com/ProtonMail/go-rfc5322) available under [license](https://github.com/ProtonMail/go-rfc5322/blob/master/LICENSE) * [gopenpgp](https://github.com/ProtonMail/gopenpgp/v2) available under [license](https://github.com/ProtonMail/gopenpgp/v2/blob/master/LICENSE) * [goquery](https://github.com/PuerkitoBio/goquery) available under [license](https://github.com/PuerkitoBio/goquery/blob/master/LICENSE) * [ishell](https://github.com/abiosoft/ishell) available under [license](https://github.com/abiosoft/ishell/blob/master/LICENSE) @@ -77,7 +76,6 @@ Proton Mail Bridge includes the following 3rd party software: * [readline](https://github.com/abiosoft/readline) available under [license](https://github.com/abiosoft/readline/blob/master/LICENSE) * [levenshtein](https://github.com/agext/levenshtein) available under [license](https://github.com/agext/levenshtein/blob/master/LICENSE) * [cascadia](https://github.com/andybalholm/cascadia) available under [license](https://github.com/andybalholm/cascadia/blob/master/LICENSE) -* [antlr](https://github.com/antlr/antlr4/runtime/Go/antlr) available under [license](https://github.com/antlr/antlr4/runtime/Go/antlr/blob/master/LICENSE) * [go-textseg](https://github.com/apparentlymart/go-textseg/v13) available under [license](https://github.com/apparentlymart/go-textseg/v13/blob/master/LICENSE) * [sonic](https://github.com/bytedance/sonic) available under [license](https://github.com/bytedance/sonic/blob/master/LICENSE) * [base64x](https://github.com/chenzhuoyu/base64x) available under [license](https://github.com/chenzhuoyu/base64x/blob/master/LICENSE) diff --git a/go.mod b/go.mod index 06511858..372279e7 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,9 @@ go 1.18 require ( github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557 github.com/Masterminds/semver/v3 v3.2.0 - github.com/ProtonMail/gluon v0.15.1-0.20230310125443-f755e8ce082a + github.com/ProtonMail/gluon v0.15.1-0.20230317141727-9f7c827f39d2 github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a github.com/ProtonMail/go-proton-api v0.4.1-0.20230314144605-dd633a1d4739 - github.com/ProtonMail/go-rfc5322 v0.11.0 github.com/ProtonMail/gopenpgp/v2 v2.5.2 github.com/PuerkitoBio/goquery v1.8.1 github.com/abiosoft/ishell v2.0.0+incompatible @@ -62,7 +61,6 @@ require ( github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/andybalholm/cascadia v1.3.1 // indirect - github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect github.com/bytedance/sonic v1.8.1 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect diff --git a/go.sum b/go.sum index 9dee4d92..c024b799 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,8 @@ github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf h1:yc9daCCYUefEs github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf/go.mod h1:o0ESU9p83twszAU8LBeJKFAAMX14tISa0yk4Oo5TOqo= github.com/ProtonMail/docker-credential-helpers v1.1.0 h1:+kvUIpwWcbtP3WFv5sSvkFn/XLzSqPOB5AAthuk9xPk= github.com/ProtonMail/docker-credential-helpers v1.1.0/go.mod h1:mK0aBveCxhnQ756AmaTfXMZDeULvheYVhF/MWMErN5g= -github.com/ProtonMail/gluon v0.15.1-0.20230310125443-f755e8ce082a h1:o7gQKDCJMYju8svD4ufB/YfcUcWUPfQjau3MDNF2dQQ= -github.com/ProtonMail/gluon v0.15.1-0.20230310125443-f755e8ce082a/go.mod h1:yA4hk6CJw0BMo+YL8Y3ckCYs5L20sysu9xseshwY3QI= +github.com/ProtonMail/gluon v0.15.1-0.20230317141727-9f7c827f39d2 h1:WT74oKGqW6gCvzXUvgTK4LarotUvyBz9YsNJqftAO74= +github.com/ProtonMail/gluon v0.15.1-0.20230317141727-9f7c827f39d2/go.mod h1:yA4hk6CJw0BMo+YL8Y3ckCYs5L20sysu9xseshwY3QI= github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:D+aZah+k14Gn6kmL7eKxoo/4Dr/lK3ChBcwce2+SQP4= github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= @@ -42,8 +42,6 @@ github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08 h1:dS7r5z4iGS0q github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08/go.mod h1:qRZgbeASl2a9OwmsV85aWwRqic0NHPh+9ewGAzb4cgM= github.com/ProtonMail/go-proton-api v0.4.1-0.20230314144605-dd633a1d4739 h1:sJhJQKQcG06mDcqikvLLnMuTz2rngcGTiTvOnc7lQbU= github.com/ProtonMail/go-proton-api v0.4.1-0.20230314144605-dd633a1d4739/go.mod h1:4AXhqhB+AGVasVIlift9Lr1Btxg5S83xXPiyiT7mKUc= -github.com/ProtonMail/go-rfc5322 v0.11.0 h1:o5Obrm4DpmQEffvgsVqG6S4BKwC1Wat+hYwjIp2YcCY= -github.com/ProtonMail/go-rfc5322 v0.11.0/go.mod h1:6oOKr0jXvpoE6pwTx/HukigQpX2J9WUf6h0auplrFTw= github.com/ProtonMail/go-srp v0.0.5 h1:xhUioxZgDbCnpo9JehyFhwwsn9JLWkUGfB0oiKXgiGg= github.com/ProtonMail/go-srp v0.0.5/go.mod h1:06iYHtLXW8vjLtccWj++x3MKy65sIT8yZd7nrJF49rs= github.com/ProtonMail/gopenpgp/v2 v2.5.2 h1:97SjlWNAxXl9P22lgwgrZRshQdiEfAht0g3ZoiA1GCw= @@ -62,9 +60,6 @@ github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37 h1:2 github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37/go.mod h1:6AXRstqK+32jeFmw89QGL2748+dj34Av4xc/I9oo9BY= github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= -github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220816024939-bc8df83d7b9d/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= -github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves= -github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= diff --git a/internal/user/smtp.go b/internal/user/smtp.go index 27eee7e9..6ca03c47 100644 --- a/internal/user/smtp.go +++ b/internal/user/smtp.go @@ -30,9 +30,9 @@ import ( "time" "github.com/ProtonMail/gluon/reporter" + "github.com/ProtonMail/gluon/rfc5322" "github.com/ProtonMail/gluon/rfc822" "github.com/ProtonMail/go-proton-api" - "github.com/ProtonMail/go-rfc5322" "github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/proton-bridge/v3/internal/logging" "github.com/ProtonMail/proton-bridge/v3/internal/safe" diff --git a/pkg/message/build.go b/pkg/message/build.go index 57d7eb42..b67503e2 100644 --- a/pkg/message/build.go +++ b/pkg/message/build.go @@ -27,9 +27,9 @@ import ( "time" "unicode/utf8" + "github.com/ProtonMail/gluon/rfc5322" "github.com/ProtonMail/gluon/rfc822" "github.com/ProtonMail/go-proton-api" - "github.com/ProtonMail/go-rfc5322" "github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/proton-bridge/v3/pkg/algo" "github.com/bradenaw/juniper/xslices" diff --git a/pkg/message/parser.go b/pkg/message/parser.go index ff6472c2..ead63a8d 100644 --- a/pkg/message/parser.go +++ b/pkg/message/parser.go @@ -26,9 +26,9 @@ import ( "regexp" "strings" + "github.com/ProtonMail/gluon/rfc5322" "github.com/ProtonMail/gluon/rfc822" "github.com/ProtonMail/go-proton-api" - "github.com/ProtonMail/go-rfc5322" "github.com/ProtonMail/proton-bridge/v3/pkg/message/parser" pmmime "github.com/ProtonMail/proton-bridge/v3/pkg/mime" "github.com/emersion/go-message" diff --git a/pkg/message/parser_test.go b/pkg/message/parser_test.go index d536333b..5bbce163 100644 --- a/pkg/message/parser_test.go +++ b/pkg/message/parser_test.go @@ -639,11 +639,11 @@ func TestParseIcsAttachment(t *testing.T) { func TestParseAllowInvalidAddress(t *testing.T) { const literal = `To: foo - From: bar - BCC: fff - CC: FFF - Reply-To: AAA - Subject: Test +From: bar +BCC: fff +CC: FFF +Reply-To: AAA +Subject: Test ` // This will fail as the addresses are not valid. diff --git a/tests/ctx_test.go b/tests/ctx_test.go index 2089ea89..fd6be67c 100644 --- a/tests/ctx_test.go +++ b/tests/ctx_test.go @@ -366,6 +366,28 @@ func (t *testCtx) getLastCall(method, pathExp string) (server.Call, error) { return server.Call{}, fmt.Errorf("no call with method %q and path %q was made", method, pathExp) } +func (t *testCtx) getLastCallExcludingHTTPOverride(method, pathExp string) (server.Call, error) { + t.callsLock.RLock() + defer t.callsLock.RUnlock() + + root, err := url.Parse(t.api.GetHostURL()) + if err != nil { + return server.Call{}, err + } + + if matches := xslices.Filter(xslices.Join(t.calls...), func(call server.Call) bool { + if len(call.RequestHeader.Get("X-HTTP-Method-Override")) != 0 || len(call.RequestHeader.Get("X-Http-Method")) != 0 { + return false + } + + return call.Method == method && regexp.MustCompile("^"+pathExp+"$").MatchString(strings.TrimPrefix(call.URL.Path, root.Path)) + }); len(matches) > 0 { + return matches[len(matches)-1], nil + } + + return server.Call{}, fmt.Errorf("no call with method %q and path %q was made", method, pathExp) +} + func (t *testCtx) pushError(err error) { t.errorsLock.Lock() defer t.errorsLock.Unlock() diff --git a/tests/environment_test.go b/tests/environment_test.go index d3c1243d..da78767d 100644 --- a/tests/environment_test.go +++ b/tests/environment_test.go @@ -88,7 +88,8 @@ func (s *scenario) theHeaderInTheRequestToHasSetTo(method, path, key, value stri } func (s *scenario) theBodyInTheRequestToIs(method, path string, value *godog.DocString) error { - call, err := s.t.getLastCall(method, path) + // We have to exclude HTTP-Overrides to avoid race condition with the creating and sending of the draft message. + call, err := s.t.getLastCallExcludingHTTPOverride(method, path) if err != nil { return err } From d8bf2cc25e16d4957a843715578b6f11f38e021d Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 20 Mar 2023 13:19:05 +0100 Subject: [PATCH 06/22] fix(GODT-2418): Do not issue connector renames for inferiors Bump Gluon (https://github.com/ProtonMail/gluon/pull/328) and add test. --- go.mod | 2 +- go.sum | 4 ++-- .../imap/mailbox/rename_hiearchy.feature | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 tests/features/imap/mailbox/rename_hiearchy.feature diff --git a/go.mod b/go.mod index 372279e7..1cc1c247 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557 github.com/Masterminds/semver/v3 v3.2.0 - github.com/ProtonMail/gluon v0.15.1-0.20230317141727-9f7c827f39d2 + github.com/ProtonMail/gluon v0.15.1-0.20230320120726-45de5f1292d7 github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a github.com/ProtonMail/go-proton-api v0.4.1-0.20230314144605-dd633a1d4739 github.com/ProtonMail/gopenpgp/v2 v2.5.2 diff --git a/go.sum b/go.sum index c024b799..b9071ed5 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,8 @@ github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf h1:yc9daCCYUefEs github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf/go.mod h1:o0ESU9p83twszAU8LBeJKFAAMX14tISa0yk4Oo5TOqo= github.com/ProtonMail/docker-credential-helpers v1.1.0 h1:+kvUIpwWcbtP3WFv5sSvkFn/XLzSqPOB5AAthuk9xPk= github.com/ProtonMail/docker-credential-helpers v1.1.0/go.mod h1:mK0aBveCxhnQ756AmaTfXMZDeULvheYVhF/MWMErN5g= -github.com/ProtonMail/gluon v0.15.1-0.20230317141727-9f7c827f39d2 h1:WT74oKGqW6gCvzXUvgTK4LarotUvyBz9YsNJqftAO74= -github.com/ProtonMail/gluon v0.15.1-0.20230317141727-9f7c827f39d2/go.mod h1:yA4hk6CJw0BMo+YL8Y3ckCYs5L20sysu9xseshwY3QI= +github.com/ProtonMail/gluon v0.15.1-0.20230320120726-45de5f1292d7 h1:IV5UN40uycUfexsuMOY1TAuGLVjKof6V2ybHsNPq1CM= +github.com/ProtonMail/gluon v0.15.1-0.20230320120726-45de5f1292d7/go.mod h1:yA4hk6CJw0BMo+YL8Y3ckCYs5L20sysu9xseshwY3QI= github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:D+aZah+k14Gn6kmL7eKxoo/4Dr/lK3ChBcwce2+SQP4= github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= diff --git a/tests/features/imap/mailbox/rename_hiearchy.feature b/tests/features/imap/mailbox/rename_hiearchy.feature new file mode 100644 index 00000000..7bb0de8d --- /dev/null +++ b/tests/features/imap/mailbox/rename_hiearchy.feature @@ -0,0 +1,19 @@ +Feature: IMAP get mailbox info + Background: + Given there exists an account with username "[user:user]" and password "password" + And the account "[user:user]" has the following custom mailboxes: + | name | type | + | f1 | folder | + | f1/f2| folder | + And bridge starts + And the user logs in with username "[user:user]" and password "password" + And user "[user:user]" finishes syncing + And user "[user:user]" connects and authenticates IMAP client "1" + + Scenario: Rename folder with subfolders + When IMAP client "1" renames "Folders/f1" to "Folders/f3" + And it succeeds + Then IMAP client "1" sees "Folders/f3" + Then IMAP client "1" sees "Folders/f3/f2" + And IMAP client "1" does not see "Folders/f1" + And IMAP client "1" does not see "Folders/f1/f2" From a8250ff65ed1bcb146c8ffcee9a89fd9dc01aec9 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 21 Mar 2023 08:45:01 +0100 Subject: [PATCH 07/22] fix(GODT-2507): Memory consumption bug Fix was made in Gluon: https://github.com/ProtonMail/gluon/pull/329 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1cc1c247..678b6325 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557 github.com/Masterminds/semver/v3 v3.2.0 - github.com/ProtonMail/gluon v0.15.1-0.20230320120726-45de5f1292d7 + github.com/ProtonMail/gluon v0.15.1-0.20230321074233-2d09826346c1 github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a github.com/ProtonMail/go-proton-api v0.4.1-0.20230314144605-dd633a1d4739 github.com/ProtonMail/gopenpgp/v2 v2.5.2 diff --git a/go.sum b/go.sum index b9071ed5..c33dfd96 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,8 @@ github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf h1:yc9daCCYUefEs github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf/go.mod h1:o0ESU9p83twszAU8LBeJKFAAMX14tISa0yk4Oo5TOqo= github.com/ProtonMail/docker-credential-helpers v1.1.0 h1:+kvUIpwWcbtP3WFv5sSvkFn/XLzSqPOB5AAthuk9xPk= github.com/ProtonMail/docker-credential-helpers v1.1.0/go.mod h1:mK0aBveCxhnQ756AmaTfXMZDeULvheYVhF/MWMErN5g= -github.com/ProtonMail/gluon v0.15.1-0.20230320120726-45de5f1292d7 h1:IV5UN40uycUfexsuMOY1TAuGLVjKof6V2ybHsNPq1CM= -github.com/ProtonMail/gluon v0.15.1-0.20230320120726-45de5f1292d7/go.mod h1:yA4hk6CJw0BMo+YL8Y3ckCYs5L20sysu9xseshwY3QI= +github.com/ProtonMail/gluon v0.15.1-0.20230321074233-2d09826346c1 h1:zVKqnKO/vTMrVw1dleGsj1JlY9jbhK890X0htvGCbjY= +github.com/ProtonMail/gluon v0.15.1-0.20230321074233-2d09826346c1/go.mod h1:yA4hk6CJw0BMo+YL8Y3ckCYs5L20sysu9xseshwY3QI= github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:D+aZah+k14Gn6kmL7eKxoo/4Dr/lK3ChBcwce2+SQP4= github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= From 03c3e95b344166f970a4843ddbb279b957416cc5 Mon Sep 17 00:00:00 2001 From: Xavier Michelon Date: Tue, 21 Mar 2023 12:30:23 +0100 Subject: [PATCH 08/22] feat(GODT-2511): add bridge-gui switches to permanently select the QML rendering backend. --- .../frontend/bridge-gui/bridge-gui/CommandLine.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/internal/frontend/bridge-gui/bridge-gui/CommandLine.cpp b/internal/frontend/bridge-gui/bridge-gui/CommandLine.cpp index 5a892da0..420e7901 100644 --- a/internal/frontend/bridge-gui/bridge-gui/CommandLine.cpp +++ b/internal/frontend/bridge-gui/bridge-gui/CommandLine.cpp @@ -18,6 +18,7 @@ #include "Pch.h" #include "CommandLine.h" +#include "Settings.h" using namespace bridgepp; @@ -28,7 +29,10 @@ namespace { QString const launcherFlag = "--launcher"; ///< launcher flag parameter used for bridge. QString const noWindowFlag = "--no-window"; ///< The no-window command-line flag. -QString const softwareRendererFlag = "--software-renderer"; ///< The 'software-renderer' command-line flag. +QString const softwareRendererFlag = "--software-renderer"; ///< The 'software-renderer' command-line flag. enable software rendering for a single execution +QString const setSoftwareRendererFlag = "--set-software-renderer"; ///< The 'set-software-renderer' command-line flag. Software rendering will be used for all subsequent executions of the application. +QString const setHardwareRendererFlag = "--set-hardware-renderer"; ///< The 'set-hardware-renderer' command-line flag. Hardware rendering will be used for all subsequent executions of the application. + //**************************************************************************************************************************************************** /// \brief parse a command-line string argument as expected by go's CLI package. @@ -101,6 +105,14 @@ CommandLineOptions parseCommandLine(int argc, char *argv[]) { options.bridgeGuiArgs.append(arg); options.useSoftwareRenderer = true; } + if (arg == setSoftwareRendererFlag) { + app().settings().setUseSoftwareRenderer(true); + continue; // setting is permanent. no need to keep/pass it to bridge for restart. + } + if (arg == setHardwareRendererFlag) { + app().settings().setUseSoftwareRenderer(false); + continue; // setting is permanent. no need to keep/pass it to bridge for restart. + } if (arg == noWindowFlag) { options.noWindow = true; } From dbd156a272d6fcdc9d08734c92bf8864543bd2b3 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 21 Mar 2023 11:52:32 +0100 Subject: [PATCH 09/22] fix(GODT-2512): Catch unhandled API errors Bump GPA https://github.com/ProtonMail/go-proton-api/pull/63 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 678b6325..389b343d 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/Masterminds/semver/v3 v3.2.0 github.com/ProtonMail/gluon v0.15.1-0.20230321074233-2d09826346c1 github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a - github.com/ProtonMail/go-proton-api v0.4.1-0.20230314144605-dd633a1d4739 + github.com/ProtonMail/go-proton-api v0.4.1-0.20230321105122-1945ba8a46f9 github.com/ProtonMail/gopenpgp/v2 v2.5.2 github.com/PuerkitoBio/goquery v1.8.1 github.com/abiosoft/ishell v2.0.0+incompatible diff --git a/go.sum b/go.sum index c33dfd96..99e41542 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,8 @@ github.com/ProtonMail/go-message v0.0.0-20210611055058-fabeff2ec753 h1:I8IsYA297 github.com/ProtonMail/go-message v0.0.0-20210611055058-fabeff2ec753/go.mod h1:NBAn21zgCJ/52WLDyed18YvYFm5tEoeDauubFqLokM4= github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08 h1:dS7r5z4iGS0qCjM7UwWdsEMzQesUQbGcXdSm2/tWboA= github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08/go.mod h1:qRZgbeASl2a9OwmsV85aWwRqic0NHPh+9ewGAzb4cgM= -github.com/ProtonMail/go-proton-api v0.4.1-0.20230314144605-dd633a1d4739 h1:sJhJQKQcG06mDcqikvLLnMuTz2rngcGTiTvOnc7lQbU= -github.com/ProtonMail/go-proton-api v0.4.1-0.20230314144605-dd633a1d4739/go.mod h1:4AXhqhB+AGVasVIlift9Lr1Btxg5S83xXPiyiT7mKUc= +github.com/ProtonMail/go-proton-api v0.4.1-0.20230321105122-1945ba8a46f9 h1:RyOYt/rc3hQtIKFUDJObah2g8wouIFVTorou5mmIHQI= +github.com/ProtonMail/go-proton-api v0.4.1-0.20230321105122-1945ba8a46f9/go.mod h1:4AXhqhB+AGVasVIlift9Lr1Btxg5S83xXPiyiT7mKUc= github.com/ProtonMail/go-srp v0.0.5 h1:xhUioxZgDbCnpo9JehyFhwwsn9JLWkUGfB0oiKXgiGg= github.com/ProtonMail/go-srp v0.0.5/go.mod h1:06iYHtLXW8vjLtccWj++x3MKy65sIT8yZd7nrJF49rs= github.com/ProtonMail/gopenpgp/v2 v2.5.2 h1:97SjlWNAxXl9P22lgwgrZRshQdiEfAht0g3ZoiA1GCw= From ee5e87fe854e09fdbf2bcdb5b2634991da3b31da Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 21 Mar 2023 14:37:20 +0100 Subject: [PATCH 10/22] test: Add 503 request test for message create event Simulate 503 status during a message create event when the message data is being downloaded. --- internal/bridge/user_event_test.go | 41 ++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/internal/bridge/user_event_test.go b/internal/bridge/user_event_test.go index c4404f0b..3f08afe8 100644 --- a/internal/bridge/user_event_test.go +++ b/internal/bridge/user_event_test.go @@ -765,6 +765,47 @@ func TestBridge_User_HandleParentLabelRename(t *testing.T) { }) } +func TestBridge503DuringEventDoesNotCauseBadEvent(t *testing.T) { + withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, storeKey []byte) { + // Create a user. + userID, addrID, err := s.CreateUser("user", password) + require.NoError(t, err) + + labelID, err := s.CreateLabel(userID, "folder", "", proton.LabelTypeFolder) + require.NoError(t, err) + + // Create 10 messages for the user. + withClient(ctx, t, s, "user", password, func(ctx context.Context, c *proton.Client) { + createNumMessages(ctx, t, c, addrID, labelID, 10) + }) + + withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) { + userLoginAndSync(ctx, t, bridge, "user", password) + + var messageIDs []string + + // Create 10 more messages for the user, generating events. + withClient(ctx, t, s, "user", password, func(ctx context.Context, c *proton.Client) { + messageIDs = createNumMessages(ctx, t, c, addrID, labelID, 10) + }) + + mocks.Reporter.EXPECT().ReportMessageWithContext(gomock.Any(), gomock.Any()).MinTimes(1) + + s.AddStatusHook(func(req *http.Request) (int, bool) { + if xslices.Index(xslices.Map(messageIDs[0:5], func(messageID string) string { + return "/mail/v4/messages/" + messageID + }), req.URL.Path) < 0 { + return 0, false + } + + return http.StatusServiceUnavailable, true + }) + + userContinueEventProcess(ctx, t, s, bridge) + }) + }) +} + // userLoginAndSync logs in user and waits until user is fully synced. func userLoginAndSync( ctx context.Context, From 4c94606e20c4ac9115e4da2fb4f164d710286a08 Mon Sep 17 00:00:00 2001 From: Xavier Michelon Date: Wed, 22 Mar 2023 09:10:26 +0100 Subject: [PATCH 11/22] fix(GODT-2516): log error when the vault key cannot be created/loaded from the keychain. Backported from release/perth-narrows. --- internal/app/vault.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/app/vault.go b/internal/app/vault.go index 55b583b1..af5e1238 100644 --- a/internal/app/vault.go +++ b/internal/app/vault.go @@ -80,6 +80,7 @@ func newVault(locations *locations.Locations) (*vault.Vault, bool, bool, error) ) if key, err := loadVaultKey(vaultDir); err != nil { + logrus.WithError(err).Error("Could not load/create vault key") insecure = true // We store the insecure vault in a separate directory From 58df3312c1ba3ea07d5a0d8bdda66875d9f96be0 Mon Sep 17 00:00:00 2001 From: James Houlahan Date: Mon, 20 Mar 2023 14:55:49 +0100 Subject: [PATCH 12/22] feat(GODT-2509): Migrate TLS cert from v1/v2 location during upgrade to v3 --- internal/app/migration.go | 27 +++++- internal/app/migration_test.go | 95 +++++++++++++------ .../with_keys/protonmail/bridge/cert.pem | 1 + .../with_keys/protonmail/bridge/key.pem | 1 + .../protonmail/bridge}/prefs.json | 0 .../without_keys/protonmail/bridge/prefs.json | 31 ++++++ internal/vault/certs.go | 8 ++ 7 files changed, 131 insertions(+), 32 deletions(-) create mode 100644 internal/app/testdata/with_keys/protonmail/bridge/cert.pem create mode 100644 internal/app/testdata/with_keys/protonmail/bridge/key.pem rename internal/app/testdata/{ => with_keys/protonmail/bridge}/prefs.json (100%) create mode 100644 internal/app/testdata/without_keys/protonmail/bridge/prefs.json diff --git a/internal/app/migration.go b/internal/app/migration.go index 4c440e6b..c5197ad9 100644 --- a/internal/app/migration.go +++ b/internal/app/migration.go @@ -87,6 +87,11 @@ func migrateOldSettings(v *vault.Vault) error { return fmt.Errorf("failed to get user config dir: %w", err) } + return migrateOldSettingsWithDir(configDir, v) +} + +// nolint:gosec +func migrateOldSettingsWithDir(configDir string, v *vault.Vault) error { b, err := os.ReadFile(filepath.Join(configDir, "protonmail", "bridge", "prefs.json")) if errors.Is(err, fs.ErrNotExist) { return nil @@ -94,7 +99,27 @@ func migrateOldSettings(v *vault.Vault) error { return fmt.Errorf("failed to read old prefs file: %w", err) } - return migratePrefsToVault(v, b) + if err := migratePrefsToVault(v, b); err != nil { + return fmt.Errorf("failed to migrate prefs to vault: %w", err) + } + + logrus.Info("Migrating TLS certificate") + + certPEM, err := os.ReadFile(filepath.Join(configDir, "protonmail", "bridge", "cert.pem")) + if errors.Is(err, fs.ErrNotExist) { + return nil + } else if err != nil { + return fmt.Errorf("failed to read old cert file: %w", err) + } + + keyPEM, err := os.ReadFile(filepath.Join(configDir, "protonmail", "bridge", "key.pem")) + if errors.Is(err, fs.ErrNotExist) { + return nil + } else if err != nil { + return fmt.Errorf("failed to read old key file: %w", err) + } + + return v.SetBridgeTLSCertKey(certPEM, keyPEM) } func migrateOldAccounts(locations *locations.Locations, v *vault.Vault) error { diff --git a/internal/app/migration_test.go b/internal/app/migration_test.go index fd0b41e1..02dff7e1 100644 --- a/internal/app/migration_test.go +++ b/internal/app/migration_test.go @@ -38,51 +38,49 @@ import ( "github.com/stretchr/testify/require" ) -func TestMigratePrefsToVault(t *testing.T) { +func TestMigratePrefsToVaultWithKeys(t *testing.T) { // Create a new vault. vault, corrupt, err := vault.New(t.TempDir(), t.TempDir(), []byte("my secret key")) require.NoError(t, err) require.False(t, corrupt) // load the old prefs file. - b, err := os.ReadFile(filepath.Join("testdata", "prefs.json")) - require.NoError(t, err) + configDir := filepath.Join("testdata", "with_keys") // Migrate the old prefs file to the new vault. - require.NoError(t, migratePrefsToVault(vault, b)) + require.NoError(t, migrateOldSettingsWithDir(configDir, vault)) - // Check that the IMAP and SMTP prefs are migrated. - require.Equal(t, 2143, vault.GetIMAPPort()) - require.Equal(t, 2025, vault.GetSMTPPort()) - require.True(t, vault.GetSMTPSSL()) + // Check Json Settings + validateJSONPrefs(t, vault) - // Check that the update channel is migrated. - require.True(t, vault.GetAutoUpdate()) - require.Equal(t, updater.EarlyChannel, vault.GetUpdateChannel()) - require.Equal(t, 0.4849529004202015, vault.GetUpdateRollout()) + cert, key := vault.GetBridgeTLSCert() + // Check the keys were found and collected. + require.Equal(t, "-----BEGIN CERTIFICATE-----", string(cert)) + require.Equal(t, "-----BEGIN RSA PRIVATE KEY-----", string(key)) +} - // Check that the app settings have been migrated. - require.False(t, vault.GetFirstStart()) - require.Equal(t, "blablabla", vault.GetColorScheme()) - require.Equal(t, "2.3.0+git", vault.GetLastVersion().String()) - require.True(t, vault.GetAutostart()) - - // Check that the other app settings have been migrated. - require.False(t, vault.GetProxyAllowed()) - require.False(t, vault.GetShowAllMail()) - - // Check that the cookies have been migrated. - jar, err := cookiejar.New(nil) +func TestMigratePrefsToVaultWithoutKeys(t *testing.T) { + // Create a new vault. + vault, corrupt, err := vault.New(t.TempDir(), t.TempDir(), []byte("my secret key")) require.NoError(t, err) + require.False(t, corrupt) - cookies, err := cookies.NewCookieJar(jar, vault) - require.NoError(t, err) + // load the old prefs file. + configDir := filepath.Join("testdata", "without_keys") - url, err := url.Parse("https://api.protonmail.ch") - require.NoError(t, err) + // Migrate the old prefs file to the new vault. + require.NoError(t, migrateOldSettingsWithDir(configDir, vault)) - // There should be a cookie for the API. - require.NotEmpty(t, cookies.Cookies(url)) + // Migrate the old prefs file to the new vault. + require.NoError(t, migrateOldSettingsWithDir(configDir, vault)) + + // Check Json Settings + validateJSONPrefs(t, vault) + + // Check the keys were found and collected. + cert, key := vault.GetBridgeTLSCert() + require.NotEqual(t, []byte("-----BEGIN CERTIFICATE-----"), cert) + require.NotEqual(t, []byte("-----BEGIN RSA PRIVATE KEY-----"), key) } func TestKeychainMigration(t *testing.T) { @@ -99,7 +97,7 @@ func TestKeychainMigration(t *testing.T) { oldCacheDir := filepath.Join(tmpDir, "protonmail", "bridge") require.NoError(t, os.MkdirAll(oldCacheDir, 0o700)) - oldPrefs, err := os.ReadFile(filepath.Join("testdata", "prefs.json")) + oldPrefs, err := os.ReadFile(filepath.Join("testdata", "without_keys", "protonmail", "bridge", "prefs.json")) require.NoError(t, err) require.NoError(t, os.WriteFile( @@ -194,3 +192,38 @@ func TestUserMigration(t *testing.T) { require.Equal(t, vault.CombinedMode, u.AddressMode()) })) } + +func validateJSONPrefs(t *testing.T, vault *vault.Vault) { + // Check that the IMAP and SMTP prefs are migrated. + require.Equal(t, 2143, vault.GetIMAPPort()) + require.Equal(t, 2025, vault.GetSMTPPort()) + require.True(t, vault.GetSMTPSSL()) + + // Check that the update channel is migrated. + require.True(t, vault.GetAutoUpdate()) + require.Equal(t, updater.EarlyChannel, vault.GetUpdateChannel()) + require.Equal(t, 0.4849529004202015, vault.GetUpdateRollout()) + + // Check that the app settings have been migrated. + require.False(t, vault.GetFirstStart()) + require.Equal(t, "blablabla", vault.GetColorScheme()) + require.Equal(t, "2.3.0+git", vault.GetLastVersion().String()) + require.True(t, vault.GetAutostart()) + + // Check that the other app settings have been migrated. + require.False(t, vault.GetProxyAllowed()) + require.False(t, vault.GetShowAllMail()) + + // Check that the cookies have been migrated. + jar, err := cookiejar.New(nil) + require.NoError(t, err) + + cookies, err := cookies.NewCookieJar(jar, vault) + require.NoError(t, err) + + url, err := url.Parse("https://api.protonmail.ch") + require.NoError(t, err) + + // There should be a cookie for the API. + require.NotEmpty(t, cookies.Cookies(url)) +} diff --git a/internal/app/testdata/with_keys/protonmail/bridge/cert.pem b/internal/app/testdata/with_keys/protonmail/bridge/cert.pem new file mode 100644 index 00000000..75f3c322 --- /dev/null +++ b/internal/app/testdata/with_keys/protonmail/bridge/cert.pem @@ -0,0 +1 @@ +-----BEGIN CERTIFICATE----- \ No newline at end of file diff --git a/internal/app/testdata/with_keys/protonmail/bridge/key.pem b/internal/app/testdata/with_keys/protonmail/bridge/key.pem new file mode 100644 index 00000000..f05debd2 --- /dev/null +++ b/internal/app/testdata/with_keys/protonmail/bridge/key.pem @@ -0,0 +1 @@ +-----BEGIN RSA PRIVATE KEY----- \ No newline at end of file diff --git a/internal/app/testdata/prefs.json b/internal/app/testdata/with_keys/protonmail/bridge/prefs.json similarity index 100% rename from internal/app/testdata/prefs.json rename to internal/app/testdata/with_keys/protonmail/bridge/prefs.json diff --git a/internal/app/testdata/without_keys/protonmail/bridge/prefs.json b/internal/app/testdata/without_keys/protonmail/bridge/prefs.json new file mode 100644 index 00000000..b6a16d46 --- /dev/null +++ b/internal/app/testdata/without_keys/protonmail/bridge/prefs.json @@ -0,0 +1,31 @@ +{ + "allow_proxy": "false", + "attachment_workers": "16", + "autostart": "true", + "autoupdate": "true", + "cache_compression": "true", + "cache_concurrent_read": "16", + "cache_concurrent_write": "16", + "cache_enabled": "true", + "cache_location": "/home/user/.config/protonmail/bridge/cache/c11/messages", + "cache_min_free_abs": "250000000", + "cache_min_free_rat": "", + "color_scheme": "blablabla", + "cookies": "{\"https://api.protonmail.ch\":[{\"Name\":\"Session-Id\",\"Value\":\"blablablablablablablablabla\",\"Path\":\"/\",\"Domain\":\"protonmail.ch\",\"Expires\":\"2023-02-19T00:20:40.269424437+01:00\",\"RawExpires\":\"\",\"MaxAge\":7776000,\"Secure\":true,\"HttpOnly\":true,\"SameSite\":0,\"Raw\":\"Session-Id=blablablablablablablablabla; Domain=protonmail.ch; Path=/; HttpOnly; Secure; Max-Age=7776000\",\"Unparsed\":null},{\"Name\":\"Tag\",\"Value\":\"default\",\"Path\":\"/\",\"Domain\":\"\",\"Expires\":\"2023-02-19T00:20:40.269428627+01:00\",\"RawExpires\":\"\",\"MaxAge\":7776000,\"Secure\":true,\"HttpOnly\":false,\"SameSite\":0,\"Raw\":\"Tag=default; Path=/; Secure; Max-Age=7776000\",\"Unparsed\":null}],\"https://protonmail.com\":[{\"Name\":\"Session-Id\",\"Value\":\"blablablablablablablablabla\",\"Path\":\"/\",\"Domain\":\"protonmail.com\",\"Expires\":\"2023-02-19T00:20:18.315084712+01:00\",\"RawExpires\":\"\",\"MaxAge\":7776000,\"Secure\":true,\"HttpOnly\":true,\"SameSite\":0,\"Raw\":\"Session-Id=Y3q2Mh-ClvqL6LWeYdfyPgAAABI; Domain=protonmail.com; Path=/; HttpOnly; Secure; Max-Age=7776000\",\"Unparsed\":null},{\"Name\":\"Tag\",\"Value\":\"redirect\",\"Path\":\"/\",\"Domain\":\"\",\"Expires\":\"2023-02-19T00:20:18.315087646+01:00\",\"RawExpires\":\"\",\"MaxAge\":7776000,\"Secure\":true,\"HttpOnly\":false,\"SameSite\":0,\"Raw\":\"Tag=redirect; Path=/; Secure; Max-Age=7776000\",\"Unparsed\":null}]}", + "fetch_workers": "16", + "first_time_start": "false", + "first_time_start_gui": "true", + "imap_workers": "16", + "is_all_mail_visible": "false", + "last_heartbeat": "325", + "last_used_version": "2.3.0+git", + "preferred_keychain": "secret-service", + "rebranding_migrated": "true", + "report_outgoing_email_without_encryption": "false", + "rollout": "0.4849529004202015", + "user_port_api": "1042", + "update_channel": "early", + "user_port_imap": "2143", + "user_port_smtp": "2025", + "user_ssl_smtp": "true" +} \ No newline at end of file diff --git a/internal/vault/certs.go b/internal/vault/certs.go index 75294293..24be5f38 100644 --- a/internal/vault/certs.go +++ b/internal/vault/certs.go @@ -53,6 +53,14 @@ func (vault *Vault) SetBridgeTLSCertPath(certPath, keyPath string) error { }) } +// SetBridgeTLSCertKey sets the path to PEM-encoded certificates for the bridge. +func (vault *Vault) SetBridgeTLSCertKey(cert, key []byte) error { + return vault.mod(func(data *Data) { + data.Certs.Bridge.Cert = cert + data.Certs.Bridge.Key = key + }) +} + func (vault *Vault) GetCertsInstalled() bool { return vault.get().Certs.Installed } From 74203e07a42930ace0391a341ef510e3599abb59 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 22 Mar 2023 13:19:15 +0100 Subject: [PATCH 13/22] fix(GODT-2513): Scanner Crash in Gluon Gluon MR: https://github.com/ProtonMail/gluon/pull/330 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 389b343d..4f78c989 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557 github.com/Masterminds/semver/v3 v3.2.0 - github.com/ProtonMail/gluon v0.15.1-0.20230321074233-2d09826346c1 + github.com/ProtonMail/gluon v0.15.1-0.20230322121010-574da2df3546 github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a github.com/ProtonMail/go-proton-api v0.4.1-0.20230321105122-1945ba8a46f9 github.com/ProtonMail/gopenpgp/v2 v2.5.2 diff --git a/go.sum b/go.sum index 99e41542..d8365eb1 100644 --- a/go.sum +++ b/go.sum @@ -30,6 +30,8 @@ github.com/ProtonMail/docker-credential-helpers v1.1.0 h1:+kvUIpwWcbtP3WFv5sSvkF github.com/ProtonMail/docker-credential-helpers v1.1.0/go.mod h1:mK0aBveCxhnQ756AmaTfXMZDeULvheYVhF/MWMErN5g= github.com/ProtonMail/gluon v0.15.1-0.20230321074233-2d09826346c1 h1:zVKqnKO/vTMrVw1dleGsj1JlY9jbhK890X0htvGCbjY= github.com/ProtonMail/gluon v0.15.1-0.20230321074233-2d09826346c1/go.mod h1:yA4hk6CJw0BMo+YL8Y3ckCYs5L20sysu9xseshwY3QI= +github.com/ProtonMail/gluon v0.15.1-0.20230322121010-574da2df3546 h1:eJ8gO99EjpuGoUDI0R2VZSzQ7SGPD0ggTbjJaA0xtzE= +github.com/ProtonMail/gluon v0.15.1-0.20230322121010-574da2df3546/go.mod h1:yA4hk6CJw0BMo+YL8Y3ckCYs5L20sysu9xseshwY3QI= github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:D+aZah+k14Gn6kmL7eKxoo/4Dr/lK3ChBcwce2+SQP4= github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= From f1fb525dc8cb8b37c3f703fc7f446f6a666dbc32 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Thu, 23 Mar 2023 13:39:20 +0100 Subject: [PATCH 14/22] fix(GODT-2504): Fix missing attachments in imported message https://github.com/ProtonMail/go-proton-api/pull/64 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 4f78c989..d8a836cf 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/Masterminds/semver/v3 v3.2.0 github.com/ProtonMail/gluon v0.15.1-0.20230322121010-574da2df3546 github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a - github.com/ProtonMail/go-proton-api v0.4.1-0.20230321105122-1945ba8a46f9 + github.com/ProtonMail/go-proton-api v0.4.1-0.20230323120945-5a6ef5a2ecdd github.com/ProtonMail/gopenpgp/v2 v2.5.2 github.com/PuerkitoBio/goquery v1.8.1 github.com/abiosoft/ishell v2.0.0+incompatible diff --git a/go.sum b/go.sum index d8365eb1..0f43a368 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,8 @@ github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08 h1:dS7r5z4iGS0q github.com/ProtonMail/go-mime v0.0.0-20221031134845-8fd9bc37cf08/go.mod h1:qRZgbeASl2a9OwmsV85aWwRqic0NHPh+9ewGAzb4cgM= github.com/ProtonMail/go-proton-api v0.4.1-0.20230321105122-1945ba8a46f9 h1:RyOYt/rc3hQtIKFUDJObah2g8wouIFVTorou5mmIHQI= github.com/ProtonMail/go-proton-api v0.4.1-0.20230321105122-1945ba8a46f9/go.mod h1:4AXhqhB+AGVasVIlift9Lr1Btxg5S83xXPiyiT7mKUc= +github.com/ProtonMail/go-proton-api v0.4.1-0.20230323120945-5a6ef5a2ecdd h1:Y/XKWw0s7DDJn5R1lJmsREeGyumLcn/RDcvLozYnB88= +github.com/ProtonMail/go-proton-api v0.4.1-0.20230323120945-5a6ef5a2ecdd/go.mod h1:4AXhqhB+AGVasVIlift9Lr1Btxg5S83xXPiyiT7mKUc= github.com/ProtonMail/go-srp v0.0.5 h1:xhUioxZgDbCnpo9JehyFhwwsn9JLWkUGfB0oiKXgiGg= github.com/ProtonMail/go-srp v0.0.5/go.mod h1:06iYHtLXW8vjLtccWj++x3MKy65sIT8yZd7nrJF49rs= github.com/ProtonMail/gopenpgp/v2 v2.5.2 h1:97SjlWNAxXl9P22lgwgrZRshQdiEfAht0g3ZoiA1GCw= From ef1940d22747ece6e0064a7fc72bae50c6071d10 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 27 Mar 2023 08:31:47 +0200 Subject: [PATCH 15/22] fix(GODT-2508): Handle Address Updated for none-existing address If we receive an address update and the address does not exist, attempt to create it. --- go.mod | 2 +- go.sum | 4 ++++ internal/bridge/user_event_test.go | 18 ++++++++++++++++++ internal/user/events.go | 15 +++++++++++++-- 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d8a836cf..931bbcc0 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/Masterminds/semver/v3 v3.2.0 github.com/ProtonMail/gluon v0.15.1-0.20230322121010-574da2df3546 github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a - github.com/ProtonMail/go-proton-api v0.4.1-0.20230323120945-5a6ef5a2ecdd + github.com/ProtonMail/go-proton-api v0.4.1-0.20230327062918-71c20587e0fc github.com/ProtonMail/gopenpgp/v2 v2.5.2 github.com/PuerkitoBio/goquery v1.8.1 github.com/abiosoft/ishell v2.0.0+incompatible diff --git a/go.sum b/go.sum index 0f43a368..c693df2c 100644 --- a/go.sum +++ b/go.sum @@ -46,6 +46,10 @@ github.com/ProtonMail/go-proton-api v0.4.1-0.20230321105122-1945ba8a46f9 h1:RyOY github.com/ProtonMail/go-proton-api v0.4.1-0.20230321105122-1945ba8a46f9/go.mod h1:4AXhqhB+AGVasVIlift9Lr1Btxg5S83xXPiyiT7mKUc= github.com/ProtonMail/go-proton-api v0.4.1-0.20230323120945-5a6ef5a2ecdd h1:Y/XKWw0s7DDJn5R1lJmsREeGyumLcn/RDcvLozYnB88= github.com/ProtonMail/go-proton-api v0.4.1-0.20230323120945-5a6ef5a2ecdd/go.mod h1:4AXhqhB+AGVasVIlift9Lr1Btxg5S83xXPiyiT7mKUc= +github.com/ProtonMail/go-proton-api v0.4.1-0.20230324123811-83e98cb35c9a h1:sKcw8YlNxqO9iDDYdbZNVUCqk6Ta6liyfFBUR0Lai7o= +github.com/ProtonMail/go-proton-api v0.4.1-0.20230324123811-83e98cb35c9a/go.mod h1:4AXhqhB+AGVasVIlift9Lr1Btxg5S83xXPiyiT7mKUc= +github.com/ProtonMail/go-proton-api v0.4.1-0.20230327062918-71c20587e0fc h1:D0F4mxNVwIzYcjt8SEuIh4EzJhWgWw1f4eNc1iWrXnQ= +github.com/ProtonMail/go-proton-api v0.4.1-0.20230327062918-71c20587e0fc/go.mod h1:4AXhqhB+AGVasVIlift9Lr1Btxg5S83xXPiyiT7mKUc= github.com/ProtonMail/go-srp v0.0.5 h1:xhUioxZgDbCnpo9JehyFhwwsn9JLWkUGfB0oiKXgiGg= github.com/ProtonMail/go-srp v0.0.5/go.mod h1:06iYHtLXW8vjLtccWj++x3MKy65sIT8yZd7nrJF49rs= github.com/ProtonMail/gopenpgp/v2 v2.5.2 h1:97SjlWNAxXl9P22lgwgrZRshQdiEfAht0g3ZoiA1GCw= diff --git a/internal/bridge/user_event_test.go b/internal/bridge/user_event_test.go index 3f08afe8..a80592d3 100644 --- a/internal/bridge/user_event_test.go +++ b/internal/bridge/user_event_test.go @@ -329,6 +329,24 @@ func TestBridge_User_AddressEvents_NoBadEvent(t *testing.T) { }) } +func TestBridge_User_AddressEventUpdatedForAddressThatDoesNotExist_NoBadEvent(t *testing.T) { + withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, storeKey []byte) { + // Create a user. + userID, _, err := s.CreateUser("user", password) + require.NoError(t, err) + + withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, _ *bridge.Mocks) { + userLoginAndSync(ctx, t, bridge, "user", password) + }) + + withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, _ *bridge.Mocks) { + _, err := s.CreateAddressAsUpdate(userID, "another@pm.me", password) + require.NoError(t, err) + userContinueEventProcess(ctx, t, s, bridge) + }) + }) +} + func TestBridge_User_Network_NoBadEvents(t *testing.T) { withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, storeKey []byte) { retVal := int32(0) diff --git a/internal/user/events.go b/internal/user/events.go index 72131a3b..f980454a 100644 --- a/internal/user/events.go +++ b/internal/user/events.go @@ -170,6 +170,16 @@ func (user *User) handleAddressEvents(ctx context.Context, addressEvents []proto case proton.EventUpdate, proton.EventUpdateFlags: if err := user.handleUpdateAddressEvent(ctx, event); err != nil { + if errors.Is(err, ErrAddressDoesNotExist) { + logrus.Debugf("Address %v does not exist, will try create instead", event.Address.ID) + if createErr := user.handleCreateAddressEvent(ctx, event); createErr != nil { + user.reportError("Failed to apply address update event (with create)", createErr) + return fmt.Errorf("failed to handle update address event (with create): %w", createErr) + } + + return nil + } + user.reportError("Failed to apply address update event", err) return fmt.Errorf("failed to handle update address event: %w", err) } @@ -245,6 +255,8 @@ func (user *User) handleCreateAddressEvent(ctx context.Context, event proton.Add }, user.apiAddrsLock, user.apiLabelsLock, user.updateChLock) } +var ErrAddressDoesNotExist = errors.New("address does not exist") + func (user *User) handleUpdateAddressEvent(_ context.Context, event proton.AddressEvent) error { //nolint:unparam return safe.LockRet(func() error { user.log.WithFields(logrus.Fields{ @@ -254,8 +266,7 @@ func (user *User) handleUpdateAddressEvent(_ context.Context, event proton.Addre oldAddr, ok := user.apiAddrs[event.Address.ID] if !ok { - user.log.Debugf("Address %q does not exist", event.Address.ID) - return nil + return ErrAddressDoesNotExist } user.apiAddrs[event.Address.ID] = event.Address From 421da99cd881e86d2d3bdcff5d7bca7de86a09a3 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 27 Mar 2023 09:44:51 +0200 Subject: [PATCH 16/22] fix(GODT-2524): Preserve old vault values Keep the record of old vault settings alive to avoid issues when one downgrades from a 3.1 release to a 3.0.x release. --- internal/vault/types_settings.go | 21 +++++++++++++++++++++ internal/vault/types_user.go | 6 ++++++ 2 files changed, 27 insertions(+) diff --git a/internal/vault/types_settings.go b/internal/vault/types_settings.go index bb6946a3..8f83423f 100644 --- a/internal/vault/types_settings.go +++ b/internal/vault/types_settings.go @@ -19,6 +19,7 @@ package vault import ( "math/rand" + "runtime" "github.com/ProtonMail/proton-bridge/v3/internal/updater" ) @@ -44,11 +45,29 @@ type Settings struct { FirstStart bool MaxSyncMemory uint64 + + // **WARNING**: These entry can't be removed until they vault has proper migration support. + SyncWorkers int + SyncAttPool int } const DefaultMaxSyncMemory = 2 * 1024 * uint64(1024*1024) +func GetDefaultSyncWorkerCount() int { + const minSyncWorkers = 16 + + syncWorkers := runtime.NumCPU() * 4 + + if syncWorkers < minSyncWorkers { + syncWorkers = minSyncWorkers + } + + return syncWorkers +} + func newDefaultSettings(gluonDir string) Settings { + syncWorkers := GetDefaultSyncWorkerCount() + return Settings{ GluonDir: gluonDir, @@ -70,5 +89,7 @@ func newDefaultSettings(gluonDir string) Settings { FirstStart: true, MaxSyncMemory: DefaultMaxSyncMemory, + SyncWorkers: syncWorkers, + SyncAttPool: syncWorkers, } } diff --git a/internal/vault/types_user.go b/internal/vault/types_user.go index af1958e3..a41085fb 100644 --- a/internal/vault/types_user.go +++ b/internal/vault/types_user.go @@ -17,6 +17,8 @@ package vault +import "github.com/ProtonMail/gluon/imap" + // UserData holds information about a single bridge user. // The user may or may not be logged in. type UserData struct { @@ -35,6 +37,9 @@ type UserData struct { SyncStatus SyncStatus EventID string + + // **WARNING**: This value can't be removed until we have vault migration support. + UIDValidity map[string]imap.UID } type AddressMode int @@ -76,6 +81,7 @@ func newDefaultUser(userID, username, primaryEmail, authUID, authRef string, key GluonKey: newRandomToken(32), GluonIDs: make(map[string]string), + UIDValidity: make(map[string]imap.UID), BridgePass: newRandomToken(16), AddressMode: CombinedMode, From 7124e7c9a0b6b2f37ba479e6ed704aa59ba69766 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Mon, 27 Mar 2023 16:07:54 +0200 Subject: [PATCH 17/22] fix(GODT-2514): Apply Retry-After to 503 https://github.com/ProtonMail/go-proton-api/pull/67 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 931bbcc0..2f7d8c93 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/Masterminds/semver/v3 v3.2.0 github.com/ProtonMail/gluon v0.15.1-0.20230322121010-574da2df3546 github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a - github.com/ProtonMail/go-proton-api v0.4.1-0.20230327062918-71c20587e0fc + github.com/ProtonMail/go-proton-api v0.4.1-0.20230327135835-2751384cef6f github.com/ProtonMail/gopenpgp/v2 v2.5.2 github.com/PuerkitoBio/goquery v1.8.1 github.com/abiosoft/ishell v2.0.0+incompatible diff --git a/go.sum b/go.sum index c693df2c..909d47fe 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,8 @@ github.com/ProtonMail/go-proton-api v0.4.1-0.20230324123811-83e98cb35c9a h1:sKcw github.com/ProtonMail/go-proton-api v0.4.1-0.20230324123811-83e98cb35c9a/go.mod h1:4AXhqhB+AGVasVIlift9Lr1Btxg5S83xXPiyiT7mKUc= github.com/ProtonMail/go-proton-api v0.4.1-0.20230327062918-71c20587e0fc h1:D0F4mxNVwIzYcjt8SEuIh4EzJhWgWw1f4eNc1iWrXnQ= github.com/ProtonMail/go-proton-api v0.4.1-0.20230327062918-71c20587e0fc/go.mod h1:4AXhqhB+AGVasVIlift9Lr1Btxg5S83xXPiyiT7mKUc= +github.com/ProtonMail/go-proton-api v0.4.1-0.20230327135835-2751384cef6f h1:lqf3DlFQMvfDi7zGVKTlciyxXfvkoi6CCISu9nx8nak= +github.com/ProtonMail/go-proton-api v0.4.1-0.20230327135835-2751384cef6f/go.mod h1:4AXhqhB+AGVasVIlift9Lr1Btxg5S83xXPiyiT7mKUc= github.com/ProtonMail/go-srp v0.0.5 h1:xhUioxZgDbCnpo9JehyFhwwsn9JLWkUGfB0oiKXgiGg= github.com/ProtonMail/go-srp v0.0.5/go.mod h1:06iYHtLXW8vjLtccWj++x3MKy65sIT8yZd7nrJF49rs= github.com/ProtonMail/gopenpgp/v2 v2.5.2 h1:97SjlWNAxXl9P22lgwgrZRshQdiEfAht0g3ZoiA1GCw= From aa957f3314a1e49b7ef76c1433e3438151666fd1 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 28 Mar 2023 14:25:32 +0200 Subject: [PATCH 18/22] test: Disable TestBridge503DuringEventDoesNotCauseBadEvent Changes to GPA 503 handling is now causing GPA client to treat this as context cancelled rather than producing 503. To be re-enabled as part of GODT-2527. --- internal/bridge/user_event_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/bridge/user_event_test.go b/internal/bridge/user_event_test.go index a80592d3..f3e4b64d 100644 --- a/internal/bridge/user_event_test.go +++ b/internal/bridge/user_event_test.go @@ -783,7 +783,8 @@ func TestBridge_User_HandleParentLabelRename(t *testing.T) { }) } -func TestBridge503DuringEventDoesNotCauseBadEvent(t *testing.T) { +// TBD: GODT-2527. +func _TestBridge503DuringEventDoesNotCauseBadEvent(t *testing.T) { //nolint:unused,deadcode withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, storeKey []byte) { // Create a user. userID, addrID, err := s.CreateUser("user", password) From 86fd7961a19ab66c9a54d39f289660bde7e4a955 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 29 Mar 2023 14:49:36 +0200 Subject: [PATCH 19/22] fix(GODT-2526): Fix high memory usage with fetch/search https://github.com/ProtonMail/gluon/pull/333 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 2f7d8c93..8e93ee44 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557 github.com/Masterminds/semver/v3 v3.2.0 - github.com/ProtonMail/gluon v0.15.1-0.20230322121010-574da2df3546 + github.com/ProtonMail/gluon v0.15.1-0.20230329124608-19b8f7b4e7b0 github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a github.com/ProtonMail/go-proton-api v0.4.1-0.20230327135835-2751384cef6f github.com/ProtonMail/gopenpgp/v2 v2.5.2 diff --git a/go.sum b/go.sum index 909d47fe..3d09459d 100644 --- a/go.sum +++ b/go.sum @@ -32,6 +32,8 @@ github.com/ProtonMail/gluon v0.15.1-0.20230321074233-2d09826346c1 h1:zVKqnKO/vTM github.com/ProtonMail/gluon v0.15.1-0.20230321074233-2d09826346c1/go.mod h1:yA4hk6CJw0BMo+YL8Y3ckCYs5L20sysu9xseshwY3QI= github.com/ProtonMail/gluon v0.15.1-0.20230322121010-574da2df3546 h1:eJ8gO99EjpuGoUDI0R2VZSzQ7SGPD0ggTbjJaA0xtzE= github.com/ProtonMail/gluon v0.15.1-0.20230322121010-574da2df3546/go.mod h1:yA4hk6CJw0BMo+YL8Y3ckCYs5L20sysu9xseshwY3QI= +github.com/ProtonMail/gluon v0.15.1-0.20230329124608-19b8f7b4e7b0 h1:FASrdEaNRJSHFjrOZ5WO4CKuYZb2zoCyFNqplrsZmOQ= +github.com/ProtonMail/gluon v0.15.1-0.20230329124608-19b8f7b4e7b0/go.mod h1:yA4hk6CJw0BMo+YL8Y3ckCYs5L20sysu9xseshwY3QI= github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:D+aZah+k14Gn6kmL7eKxoo/4Dr/lK3ChBcwce2+SQP4= github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= From 0a53dc1da79fc61a873ca78b5c1faf3260438610 Mon Sep 17 00:00:00 2001 From: Xavier Michelon Date: Thu, 23 Mar 2023 16:55:35 +0100 Subject: [PATCH 20/22] feat(GODT-2523): use software QML rendering backend by default on Windows. (cherry picked from commit 934749b278e95a9d69818ddf6b45ee7bb896af03) (cherry picked from commit 7448b814e8e6da90335b8db465fd64e3d4f08bdd) --- internal/frontend/bridge-gui/bridge-gui/Settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/frontend/bridge-gui/bridge-gui/Settings.cpp b/internal/frontend/bridge-gui/bridge-gui/Settings.cpp index e7acb259..270ac1dd 100644 --- a/internal/frontend/bridge-gui/bridge-gui/Settings.cpp +++ b/internal/frontend/bridge-gui/bridge-gui/Settings.cpp @@ -44,7 +44,7 @@ Settings::Settings() /// \return The value for the 'Use software renderer' setting. //**************************************************************************************************************************************************** bool Settings::useSoftwareRenderer() const { - return settings_.value(keyUseSoftwareRenderer, false).toBool(); + return settings_.value(keyUseSoftwareRenderer, onWindows()).toBool(); } From 132322936204e2e2c3db0a302086ab80267a77d6 Mon Sep 17 00:00:00 2001 From: Xavier Michelon Date: Mon, 3 Apr 2023 15:18:16 +0200 Subject: [PATCH 21/22] feat(GODT-2239): introduce GoogleTest unit tests for bridgepp. --- .../bridge-gui/bridgepp/CMakeLists.txt | 35 +++++++ .../bridgepp/Test/Exception/TestException.cpp | 95 +++++++++++++++++++ .../bridgepp/bridgepp/BridgeUtils.h | 2 +- .../bridgepp/bridgepp/Exception/Exception.cpp | 9 ++ .../bridgepp/bridgepp/Exception/Exception.h | 1 + .../bridgepp/bridgepp/Log/LogUtils.cpp | 4 +- 6 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 internal/frontend/bridge-gui/bridgepp/Test/Exception/TestException.cpp diff --git a/internal/frontend/bridge-gui/bridgepp/CMakeLists.txt b/internal/frontend/bridge-gui/bridgepp/CMakeLists.txt index af03519a..12df7a85 100644 --- a/internal/frontend/bridge-gui/bridgepp/CMakeLists.txt +++ b/internal/frontend/bridge-gui/bridgepp/CMakeLists.txt @@ -160,3 +160,38 @@ target_link_libraries(bridgepp ) target_precompile_headers(bridgepp PRIVATE Pch.h) + +#***************************************************************************************************************************************************** +# GoogleTest +#***************************************************************************************************************************************************** + +if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") + cmake_policy(SET CMP0135 NEW) # avoid warning DOWNLOAD_EXTRACT_TIMESTAMP +endif() + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/b796f7d44681514f58a683a3a71ff17c94edb0c1.zip +) + +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + +FetchContent_MakeAvailable(googletest) + +enable_testing() + +#***************************************************************************************************************************************************** +# Tests +#***************************************************************************************************************************************************** +add_executable(bridgepp-test Test/Exception/TestException.cpp) +add_dependencies(bridgepp-test bridgepp) +target_precompile_headers(bridgepp-test PRIVATE Pch.h) +target_link_libraries(bridgepp-test + GTest::gtest_main + bridgepp + ) + +include(GoogleTest) +gtest_discover_tests(bridgepp-test) diff --git a/internal/frontend/bridge-gui/bridgepp/Test/Exception/TestException.cpp b/internal/frontend/bridge-gui/bridgepp/Test/Exception/TestException.cpp new file mode 100644 index 00000000..9c006f56 --- /dev/null +++ b/internal/frontend/bridge-gui/bridgepp/Test/Exception/TestException.cpp @@ -0,0 +1,95 @@ +// Copyright (c) 2023 Proton AG +// +// This file is part of Proton Mail Bridge. +// +// Proton Mail Bridge is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Proton Mail Bridge is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Proton Mail Bridge. If not, see . + +#include +#include + + +using namespace bridgepp; + + +namespace { + QString const testQWhat = "What"; + QString const testDetails = "Some details"; + QString const testFunction = "function"; + QByteArray const testAttachment = QString("Some data").toLocal8Bit(); + Exception const testException(testQWhat, testDetails, testFunction, testAttachment); +} + + +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +TEST(Exceptions, ExceptionConstructor) { + // Default exception + Exception const emptyException; + EXPECT_TRUE(emptyException.qwhat().isEmpty()); + EXPECT_EQ(strlen(emptyException.what()), 0); + EXPECT_EQ(emptyException.attachment().size(), 0); + EXPECT_TRUE(emptyException.details().isEmpty()); + EXPECT_TRUE(emptyException.detailedWhat().isEmpty()); + + // Fully detailed exception + EXPECT_EQ(testException.qwhat(), testQWhat); + EXPECT_EQ(QString::fromLocal8Bit(testException.what()), testQWhat); + EXPECT_EQ(testException.details(), testDetails); + EXPECT_EQ(testException.attachment(), testAttachment); + QString const detailed = testException.detailedWhat(); + EXPECT_TRUE(detailed.contains(testQWhat)); + EXPECT_TRUE(detailed.contains(testFunction)); + EXPECT_TRUE(detailed.contains(testDetails)); +} + + +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +TEST(Exceptions, ExceptionCopyMoveConstructors) { + Exception const e(testQWhat, testDetails, testFunction, testAttachment); + + // Check copy-constructor + Exception eCopied(e); + EXPECT_EQ(eCopied.qwhat(), testQWhat); + EXPECT_EQ(eCopied.details(), testDetails); + EXPECT_EQ(eCopied.function(), testFunction); + EXPECT_EQ(eCopied.attachment(), testAttachment); + + // Check move-constructor + Exception eMoved(std::move(eCopied)); + EXPECT_EQ(eMoved.qwhat(), testQWhat); + EXPECT_EQ(eMoved.details(), testDetails); + EXPECT_EQ(eMoved.function(), testFunction); + EXPECT_EQ(eMoved.attachment(), testAttachment); +} + + +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +TEST(Exceptions, ExceptionThrow) { + std::function t = []() { throw testException; }; + EXPECT_THROW(t(), Exception); + EXPECT_THROW(t(), std::exception); + bool caught = false; + try { + t(); + } catch (Exception const &e) { + caught = true; + EXPECT_EQ(e.detailedWhat(), testException.detailedWhat()); + } + EXPECT_TRUE(caught); +} diff --git a/internal/frontend/bridge-gui/bridgepp/bridgepp/BridgeUtils.h b/internal/frontend/bridge-gui/bridgepp/bridgepp/BridgeUtils.h index 280a0de9..1ca92901 100644 --- a/internal/frontend/bridge-gui/bridgepp/bridgepp/BridgeUtils.h +++ b/internal/frontend/bridge-gui/bridgepp/bridgepp/BridgeUtils.h @@ -20,7 +20,7 @@ #define BRIDGE_PP_TESTER_BRIDGE_UTILS_H -#include +#include "User/User.h" namespace bridgepp { diff --git a/internal/frontend/bridge-gui/bridgepp/bridgepp/Exception/Exception.cpp b/internal/frontend/bridge-gui/bridgepp/bridgepp/Exception/Exception.cpp index dc614b88..db43cac0 100644 --- a/internal/frontend/bridge-gui/bridgepp/bridgepp/Exception/Exception.cpp +++ b/internal/frontend/bridge-gui/bridgepp/bridgepp/Exception/Exception.cpp @@ -87,6 +87,14 @@ QString Exception::details() const noexcept { } +//**************************************************************************************************************************************************** +/// \return The function that threw the exception. +//**************************************************************************************************************************************************** +QString Exception::function() const noexcept { + return function_; +} + + //**************************************************************************************************************************************************** /// \return The attachment for the exception. //**************************************************************************************************************************************************** @@ -109,4 +117,5 @@ QString Exception::detailedWhat() const { return result; } + } // namespace bridgepp diff --git a/internal/frontend/bridge-gui/bridgepp/bridgepp/Exception/Exception.h b/internal/frontend/bridge-gui/bridgepp/bridgepp/Exception/Exception.h index 21a1689d..3b78821f 100644 --- a/internal/frontend/bridge-gui/bridgepp/bridgepp/Exception/Exception.h +++ b/internal/frontend/bridge-gui/bridgepp/bridgepp/Exception/Exception.h @@ -42,6 +42,7 @@ public: // member functions QString qwhat() const noexcept; ///< Return the description of the exception as a QString const char *what() const noexcept override; ///< Return the description of the exception as C style string QString details() const noexcept; ///< Return the details for the exception + QString function() const noexcept; ///< Return the function that threw the exception. QByteArray attachment() const noexcept; ///< Return the attachment for the exception. QString detailedWhat() const; ///< Return the detailed description of the message (i.e. including the function name and the details). diff --git a/internal/frontend/bridge-gui/bridgepp/bridgepp/Log/LogUtils.cpp b/internal/frontend/bridge-gui/bridgepp/bridgepp/Log/LogUtils.cpp index ee3780b2..eb825792 100644 --- a/internal/frontend/bridge-gui/bridgepp/bridgepp/Log/LogUtils.cpp +++ b/internal/frontend/bridge-gui/bridgepp/bridgepp/Log/LogUtils.cpp @@ -17,8 +17,8 @@ #include "LogUtils.h" -#include -#include +#include "../BridgeUtils.h" +#include "../Exception/Exception.h" namespace bridgepp { From 3735d4b32746500ad26296ad12ac8ec349fddade Mon Sep 17 00:00:00 2001 From: Xavier Michelon Date: Wed, 5 Apr 2023 10:13:49 +0200 Subject: [PATCH 22/22] feat(GODT-2239): unit tests for BridgeUtils.cpp in bridgepp. --- .../bridge-gui/bridgepp/CMakeLists.txt | 5 +- .../Test/Exception/TestBridgeUtils.cpp | 111 ++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 internal/frontend/bridge-gui/bridgepp/Test/Exception/TestBridgeUtils.cpp diff --git a/internal/frontend/bridge-gui/bridgepp/CMakeLists.txt b/internal/frontend/bridge-gui/bridgepp/CMakeLists.txt index 12df7a85..9c5eb38f 100644 --- a/internal/frontend/bridge-gui/bridgepp/CMakeLists.txt +++ b/internal/frontend/bridge-gui/bridgepp/CMakeLists.txt @@ -185,7 +185,10 @@ enable_testing() #***************************************************************************************************************************************************** # Tests #***************************************************************************************************************************************************** -add_executable(bridgepp-test Test/Exception/TestException.cpp) +add_executable(bridgepp-test + Test/Exception/TestBridgeUtils.cpp + Test/Exception/TestException.cpp + ) add_dependencies(bridgepp-test bridgepp) target_precompile_headers(bridgepp-test PRIVATE Pch.h) target_link_libraries(bridgepp-test diff --git a/internal/frontend/bridge-gui/bridgepp/Test/Exception/TestBridgeUtils.cpp b/internal/frontend/bridge-gui/bridgepp/Test/Exception/TestBridgeUtils.cpp new file mode 100644 index 00000000..a990eb66 --- /dev/null +++ b/internal/frontend/bridge-gui/bridgepp/Test/Exception/TestBridgeUtils.cpp @@ -0,0 +1,111 @@ +// Copyright (c) 2023 Proton AG +// +// This file is part of Proton Mail Bridge. +// +// Proton Mail Bridge is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Proton Mail Bridge is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Proton Mail Bridge. If not, see . + + +#include +#include + + +using namespace bridgepp; + + +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +TEST(BridgeUtils, OS) { +#ifdef Q_OS_MACOS + EXPECT_EQ(os(), OS::MacOS); + EXPECT_FALSE(onLinux()); + EXPECT_TRUE(onMacOS()); + EXPECT_FALSE(onWindows()); + EXPECT_EQ(goos(), "darwin"); + return; +#endif + +#ifdef Q_OS_WIN + EXPECT_EQ(os(), OS::Windows); + EXPECT_FALSE(onLinux()); + EXPECT_FALSE(onMacOS()); + EXPECT_TRUE(onWindows()); + EXPECT_EQ(goos(), "windows"); + return; +#endif + +#ifdef Q_OS_LINUX + EXPECT_EQ(os(), OS::Linux); + EXPECT_TRUE(onLinux()); + EXPECT_FALSE(onMacOS()); + EXPECT_FALSE(onWindows()); + EXPECT_EQ(goos(), "linux"); + return; +#endif + + EXPECT_TRUE(false); // should be unreachable. +} + +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +TEST(BridgeUtils, UserFolders) { + typedef QString (*dirFunction)(); + QList functions = { userConfigDir, userCacheDir, userDataDir, sentryCacheDir }; + QString path; + for (dirFunction f: functions) { + EXPECT_NO_THROW(path = f()); + EXPECT_FALSE(path.isEmpty()); + EXPECT_TRUE(QDir(path).exists()); + } +} + + +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +TEST(BridgeUtils, Random) { + qint32 repeatCount = 1000; + qint32 const maxValue = 5; + for (qint32 i = 0; i < repeatCount; ++i) { + qint64 n = 0; + EXPECT_NO_THROW(n = randN(maxValue)); + EXPECT_TRUE((n >= 0) && (n < maxValue)); + QString name; + EXPECT_NO_THROW(name = randomFirstName()); + EXPECT_FALSE(name.isEmpty()); + EXPECT_NO_THROW(name = randomLastName()); + EXPECT_FALSE(name.isEmpty()); + EXPECT_NO_THROW(randomUser()); + } +} + + +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +TEST(BridgeUtils, ElideLongString) { + std::function const test = [](QString const &input, qint32 maxLength, QString const &expected) -> bool { + QString output; + EXPECT_NO_THROW(output = elideLongString(input, maxLength)); + return output == expected; + }; + + EXPECT_TRUE(test( "", 0, "")); + EXPECT_TRUE(test("1234", 4, "1234")); + EXPECT_TRUE(test("123", 2, "...")); + EXPECT_TRUE(test("1234567890", 8, "12...90")); + EXPECT_TRUE(test("1234567890", 10, "1234567890")); + EXPECT_TRUE(test("1234567890", 100, "1234567890")); +}