// Copyright (c) 2022 Proton AG // // This file is part of Proton Mail Bridge. // // Proton Mail Bridge is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Proton Mail Bridge is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Proton Mail Bridge. If not, see . //go:build darwin // +build darwin package clientconfig import ( "io/ioutil" "os" "os/exec" "path/filepath" "strconv" "strings" "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[AppleMailClient] = &appleMail{} } type appleMail struct{} func (c *appleMail) Name() string { return AppleMailClient } func (c *appleMail) Configure(imapPort, smtpPort int, imapSSL, smtpSSL bool, user types.User, address string) error { mc := prepareMobileConfig(imapPort, smtpPort, imapSSL, smtpSSL, user, address) 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, address string) *mobileconfig.Config { displayName := address addresses := address if user.IsCombinedAddressMode() { displayName = user.GetPrimaryAddress() addresses = strings.Join(user.GetAddresses(), ",") } timestamp := strconv.FormatInt(time.Now().Unix(), 10) return &mobileconfig.Config{ EmailAddress: addresses, DisplayName: displayName, Identifier: "protonmail " + displayName + timestamp, IMAP: &mobileconfig.IMAP{ Hostname: bridge.Host, Port: imapPort, TLS: imapSSL, Username: displayName, Password: user.GetBridgePassword(), }, SMTP: &mobileconfig.SMTP{ Hostname: bridge.Host, Port: smtpPort, TLS: smtpSSL, Username: displayName, }, } } func saveConfigTemporarily(mc *mobileconfig.Config) (fname string, err error) { dir, err := ioutil.TempDir("", "protonmail-autoconfig") if err != nil { return } // Make sure the temporary file is deleted. go func() { <-time.After(10 * time.Minute) _ = os.RemoveAll(dir) }() // Make sure the file is only readable for the current user. fname = filepath.Clean(filepath.Join(dir, "protonmail.mobileconfig")) f, err := os.OpenFile(fname, os.O_RDWR|os.O_CREATE, 0600) if err != nil { return } if err = mc.WriteOut(f); err != nil { _ = f.Close() return } _ = f.Close() return }