diff --git a/internal/config/useragent/platform.go b/internal/config/useragent/platform.go index bca20121..6ffa63db 100644 --- a/internal/config/useragent/platform.go +++ b/internal/config/useragent/platform.go @@ -25,8 +25,20 @@ import ( "github.com/Masterminds/semver/v3" ) -// IsCatalinaOrNewer checks whether 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 { + return isThisDarwinNewerOrEqual(getMinCatalina()) +} + +// IsBigSurOrNewer checks whether the host is MacOS BigSur 10.16.x or higher. +func IsBigSurOrNewer() bool { + return isThisDarwinNewerOrEqual(getMinBigSur()) +} + +func getMinCatalina() *semver.Version { return semver.MustParse("10.15.0") } +func getMinBigSur() *semver.Version { return semver.MustParse("10.16.0") } + +func isThisDarwinNewerOrEqual(minVersion *semver.Version) bool { if runtime.GOOS != "darwin" { return false } @@ -36,16 +48,14 @@ func IsCatalinaOrNewer() bool { return false } - return isVersionCatalinaOrNewer(strings.TrimSpace(string(rawVersion))) + return isVersionEqualOrNewer(minVersion, strings.TrimSpace(string(rawVersion))) } -func isVersionCatalinaOrNewer(rawVersion string) bool { +// isVersionEqualOrNewer is separated to be able to run test on other than darwin. +func isVersionEqualOrNewer(minVersion *semver.Version, rawVersion string) bool { semVersion, err := semver.NewVersion(rawVersion) if err != nil { return false } - - minVersion := semver.MustParse("10.15.0") - return semVersion.GreaterThan(minVersion) || semVersion.Equal(minVersion) } diff --git a/internal/config/useragent/platform_test.go b/internal/config/useragent/platform_test.go index 6bfb589b..2de005e7 100644 --- a/internal/config/useragent/platform_test.go +++ b/internal/config/useragent/platform_test.go @@ -38,7 +38,27 @@ func TestIsVersionCatalinaOrNewer(t *testing.T) { } for args, exp := range testData { - got := isVersionCatalinaOrNewer(args.version) + got := isVersionEqualOrNewer(getMinCatalina(), args.version) + assert.Equal(t, exp, got, "version %v", args.version) + } +} + +func TestIsVersionBigSurOrNewer(t *testing.T) { + testData := map[struct{ version string }]bool{ + {""}: false, + {"9.0.0"}: false, + {"9.15.0"}: false, + {"10.13.0"}: false, + {"10.14.0"}: false, + {"10.14.99"}: false, + {"10.15.0"}: false, + {"10.16.0"}: true, + {"11.0.0"}: true, + {"11.1"}: true, + } + + for args, exp := range testData { + got := isVersionEqualOrNewer(getMinBigSur(), args.version) assert.Equal(t, exp, got, "version %v", args.version) } } diff --git a/internal/frontend/autoconfig/applemail.go b/internal/frontend/autoconfig/applemail.go index 022d8734..b125bd38 100644 --- a/internal/frontend/autoconfig/applemail.go +++ b/internal/frontend/autoconfig/applemail.go @@ -29,10 +29,15 @@ import ( "time" "github.com/ProtonMail/proton-bridge/internal/bridge" + "github.com/ProtonMail/proton-bridge/internal/config/useragent" "github.com/ProtonMail/proton-bridge/internal/frontend/types" "github.com/ProtonMail/proton-bridge/pkg/mobileconfig" ) +const ( + bigSurPreferncesPane = "/System/Library/PreferencePanes/Profiles.prefPane" +) + func init() { //nolint[gochecknoinit] available = append(available, &appleMail{}) } @@ -43,7 +48,22 @@ func (c *appleMail) Name() string { return "Apple Mail" } -func (c *appleMail) Configure(imapPort, smtpPort int, imapSSL, smtpSSL bool, user types.User, addressIndex int) error { //nolint[funlen] +func (c *appleMail) Configure(imapPort, smtpPort int, imapSSL, smtpSSL bool, user types.User, addressIndex int) error { + mc := prepareMobileConfig(imapPort, smtpPort, imapSSL, smtpSSL, user, addressIndex) + + confPath, err := saveConfigTemporarily(mc) + if err != nil { + return err + } + + if useragent.IsBigSurOrNewer() { + return exec.Command("open", bigSurPreferncesPane, confPath).Run() //nolint[gosec] G204: open command is safe, mobileconfig is generated by us + } + + return exec.Command("open", confPath).Run() //nolint[gosec] G204: open command is safe, mobileconfig is generated by us +} + +func prepareMobileConfig(imapPort, smtpPort int, imapSSL, smtpSSL bool, user types.User, addressIndex int) *mobileconfig.Config { var addresses string var displayName string @@ -62,7 +82,7 @@ func (c *appleMail) Configure(imapPort, smtpPort int, imapSSL, smtpSSL bool, use timestamp := strconv.FormatInt(time.Now().Unix(), 10) - mc := &mobileconfig.Config{ + return &mobileconfig.Config{ EmailAddress: addresses, DisplayName: displayName, Identifier: "protonmail " + displayName + timestamp, @@ -80,10 +100,12 @@ func (c *appleMail) Configure(imapPort, smtpPort int, imapSSL, smtpSSL bool, use Username: displayName, }, } +} +func saveConfigTemporarily(mc *mobileconfig.Config) (fname string, err error) { dir, err := ioutil.TempDir("", "protonmail-autoconfig") if err != nil { - return err + return } // Make sure the temporary file is deleted. @@ -93,16 +115,17 @@ func (c *appleMail) Configure(imapPort, smtpPort int, imapSSL, smtpSSL bool, use })() // Make sure the file is only readable for the current user. - f, err := os.OpenFile(filepath.Clean(filepath.Join(dir, "protonmail.mobileconfig")), os.O_RDWR|os.O_CREATE, 0600) + fname = filepath.Clean(filepath.Join(dir, "protonmail.mobileconfig")) + f, err := os.OpenFile(fname, os.O_RDWR|os.O_CREATE, 0600) if err != nil { - return err + return } - if err := mc.WriteOut(f); err != nil { + if err = mc.WriteOut(f); err != nil { _ = f.Close() - return err + return } _ = f.Close() - return exec.Command("open", f.Name()).Run() // nolint[gosec] + return }