proton-bridge/pkg/config/config.go

250 lines
7.5 KiB
Go

// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.
//
// ProtonMail 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.
//
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package config
import (
"io/ioutil"
"os"
"path/filepath"
"github.com/ProtonMail/go-appdir"
"github.com/hashicorp/go-multierror"
"github.com/sirupsen/logrus"
)
var (
log = logrus.WithField("pkg", "config") //nolint[gochecknoglobals]
)
type appDirProvider interface {
UserConfig() string
UserCache() string
UserLogs() string
}
type Config struct {
appName string
version string
revision string
cacheVersion string
appDirs appDirProvider
appDirsVersion appDirProvider
}
// New returns fully initialized config struct.
// `appName` should be in camelCase format for folder or file names. It's also used in API
// as `AppVersion` which is converted to CamelCase.
// `version` is the version of the app (e.g. v1.2.3).
// `cacheVersion` is the version of the cache files (setting a different number will remove the old ones).
func New(appName, version, revision, cacheVersion string) *Config {
appDirs := appdir.New(filepath.Join("protonmail", appName))
appDirsVersion := appdir.New(filepath.Join("protonmail", appName, cacheVersion))
return newConfig(appName, version, revision, cacheVersion, appDirs, appDirsVersion)
}
func newConfig(appName, version, revision, cacheVersion string, appDirs, appDirsVersion appDirProvider) *Config {
return &Config{
appName: appName,
version: version,
revision: revision,
cacheVersion: cacheVersion,
appDirs: appDirs,
appDirsVersion: appDirsVersion,
}
}
// CreateDirs creates all folders that are necessary for bridge to properly function.
func (c *Config) CreateDirs() error {
// Log files.
if err := os.MkdirAll(c.appDirs.UserLogs(), 0700); err != nil {
return err
}
// TLS files.
if err := os.MkdirAll(c.appDirs.UserConfig(), 0750); err != nil {
return err
}
// Lock, events, preferences, user_info, db files.
if err := os.MkdirAll(c.appDirsVersion.UserCache(), 0750); err != nil {
return err
}
return nil
}
// ClearData removes all files except the lock file.
// The lock file will be removed when the Bridge stops.
func (c *Config) ClearData() error {
dirs := []string{
c.appDirs.UserLogs(),
c.appDirs.UserConfig(),
c.appDirs.UserCache(),
}
shouldRemove := func(filePath string) bool {
return filePath != c.GetLockPath()
}
return c.removeAllExcept(dirs, shouldRemove)
}
// ClearOldData removes all old files, such as old log files or old versions of cache and so on.
func (c *Config) ClearOldData() error {
// `appDirs` is parent for `appDirsVersion`.
// `dir` then contains all subfolders and only `cacheVersion` should stay.
// But on Windows all files (dirs) are in the same one - we cannot remove log, lock or tls files.
dir := c.appDirs.UserCache()
return c.removeExcept(dir, func(filePath string) bool {
fileName := filepath.Base(filePath)
return (fileName != c.cacheVersion &&
!logFileRgx.MatchString(fileName) &&
filePath != c.GetLogDir() &&
filePath != c.GetTLSCertPath() &&
filePath != c.GetTLSKeyPath() &&
filePath != c.GetEventsPath() &&
filePath != c.GetIMAPCachePath() &&
filePath != c.GetLockPath() &&
filePath != c.GetPreferencesPath())
})
}
func (c *Config) removeAllExcept(dirs []string, shouldRemove func(string) bool) error {
var result *multierror.Error
for _, dir := range dirs {
if err := c.removeExcept(dir, shouldRemove); err != nil {
result = multierror.Append(result, err)
}
}
return result.ErrorOrNil()
}
func (c *Config) removeExcept(dir string, shouldRemove func(string) bool) error {
files, err := ioutil.ReadDir(dir)
if err != nil {
return err
}
var result *multierror.Error
for _, file := range files {
filePath := filepath.Join(dir, file.Name())
if !shouldRemove(filePath) {
continue
}
if !file.IsDir() {
if err := os.RemoveAll(filePath); err != nil {
result = multierror.Append(result, err)
}
continue
}
subDir := filepath.Join(dir, file.Name())
if err := c.removeExcept(subDir, shouldRemove); err != nil {
result = multierror.Append(result, err)
} else {
// Remove dir itself only if it's empty.
subFiles, err := ioutil.ReadDir(subDir)
if err != nil {
result = multierror.Append(result, err)
} else if len(subFiles) == 0 {
if err := os.RemoveAll(subDir); err != nil {
result = multierror.Append(result, err)
}
}
}
}
return result.ErrorOrNil()
}
// IsDevMode should be used for development conditions such us whether to send sentry reports.
func (c *Config) IsDevMode() bool {
return os.Getenv("PROTONMAIL_ENV") == "dev"
}
// GetVersion returns the version.
func (c *Config) GetVersion() string {
return c.version
}
// GetLogDir returns folder for log files.
func (c *Config) GetLogDir() string {
return c.appDirs.UserLogs()
}
// GetLogPrefix returns prefix for log files. Bridge uses format vVERSION.
func (c *Config) GetLogPrefix() string {
return "v" + c.version + "_" + c.revision
}
// GetTLSCertPath returns path to certificate; used for TLS servers (IMAP, SMTP and API).
func (c *Config) GetTLSCertPath() string {
return filepath.Join(c.appDirs.UserConfig(), "cert.pem")
}
// GetTLSKeyPath returns path to private key; used for TLS servers (IMAP, SMTP and API).
func (c *Config) GetTLSKeyPath() string {
return filepath.Join(c.appDirs.UserConfig(), "key.pem")
}
// GetDBDir returns folder for db files.
func (c *Config) GetDBDir() string {
return filepath.Join(c.appDirsVersion.UserCache())
}
// GetEventsPath returns path to events file containing the last processed event IDs.
func (c *Config) GetEventsPath() string {
return filepath.Join(c.appDirsVersion.UserCache(), "events.json")
}
// GetIMAPCachePath returns path to file with IMAP status.
func (c *Config) GetIMAPCachePath() string {
return filepath.Join(c.appDirsVersion.UserCache(), "user_info.json")
}
// GetLockPath returns path to lock file to check if bridge is already running.
func (c *Config) GetLockPath() string {
return filepath.Join(c.appDirsVersion.UserCache(), c.appName+".lock")
}
// GetUpdateDir returns folder for update files; such as new binary.
func (c *Config) GetUpdateDir() string {
return filepath.Join(c.appDirsVersion.UserCache(), "updates")
}
// GetPreferencesPath returns path to preference file.
func (c *Config) GetPreferencesPath() string {
return filepath.Join(c.appDirsVersion.UserCache(), "prefs.json")
}
// GetTransferDir returns folder for import/export rule and report files.
func (c *Config) GetTransferDir() string {
return filepath.Join(c.appDirsVersion.UserCache())
}
// GetDefaultAPIPort returns default Bridge local API port.
func (c *Config) GetDefaultAPIPort() int {
return 1042
}
// GetDefaultIMAPPort returns default Bridge IMAP port.
func (c *Config) GetDefaultIMAPPort() int {
return 1143
}
// GetDefaultSMTPPort returns default Bridge SMTP port.
func (c *Config) GetDefaultSMTPPort() int {
return 1025
}