123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- package config
- import (
- "bufio"
- "errors"
- "fmt"
- "log"
- "os"
- "path"
- "strings"
- "git.sr.ht/~blallo/papero/worker"
- "github.com/mitchellh/go-homedir"
- )
- var ErrPortOutOfRange = errors.New("port out of range")
- var ErrMissingPassword = errors.New("password has not been set")
- var ErrMissingDefaultAccount = errors.New("default_account is missing from config")
- // CastingErrors is a simple wrapper around map[string]error. It's endowed with a utility
- // function to check if there are any errors.
- type CastingErrors struct {
- GeneralErrors []error
- AccountErrors map[string]error
- }
- func initCastingErrors() *CastingErrors {
- c := CastingErrors{}
- c.AccountErrors = make(map[string]error)
- return &c
- }
- func (c *CastingErrors) appendGeneralError(e error) {
- c.GeneralErrors = append(c.GeneralErrors, e)
- }
- func (c *CastingErrors) String() string {
- out := "CastingErrors{"
- out += fmt.Sprintf("GeneralErrors: %s, ", c.GeneralErrors)
- out += fmt.Sprintf("AccountErrors: %s", c.AccountErrors)
- out += "}"
- return out
- }
- // Check if there are any errors, i.e. if any value of the map is not nil.
- func (c CastingErrors) Check() bool {
- if len(c.GeneralErrors) != 0 {
- return false
- }
- for _, err := range c.GeneralErrors {
- if err != nil {
- return false
- }
- }
- return true
- }
- // MemConfig is the data structure that will be used program-wise. It holds all the
- // necessary information to authenticate and manage each provided account.
- type MemConfig struct {
- Default string
- Accounts map[string]*AccountData
- Workers map[string]*worker.Worker
- }
- func initMemConfig() MemConfig {
- m := MemConfig{}
- m.Accounts = make(map[string]*AccountData)
- m.Workers = make(map[string]*worker.Worker)
- return m
- }
- // AccountData holds the data for the single account.
- type AccountData struct {
- Host string
- Port int
- Username string
- Password string
- ExcludedFolders []string
- MailboxPath string
- Messages int
- }
- func (a *AccountData) String() string {
- var res string
- res = "{"
- res += fmt.Sprintf("Host: %s, ", a.Host)
- res += fmt.Sprintf("Port: %d, ", a.Port)
- res += fmt.Sprintf("Username: %s, ", a.Username)
- res += fmt.Sprintf("Password: ********, ")
- res += fmt.Sprintf("ExcludedFolders: %s, ", a.ExcludedFolders)
- res += fmt.Sprintf("MailboxPath: %s}", a.MailboxPath)
- res += fmt.Sprintf("Messages: %d}", a.Messages)
- return res
- }
- // parseConfig translates a *fileConfig, as obtained from a file read, into a usable
- // *MemConfig that could be used in computation, together with a custom *CastingErrors
- // type, that holds the translation errors for each account.
- func parseConfig(fileConfig *fileConfig) (*MemConfig, *CastingErrors) {
- errors := initCastingErrors()
- outConfig := initMemConfig()
- defaultAccount := fileConfig.Default
- if defaultAccount == "" {
- errors.appendGeneralError(ErrMissingDefaultAccount)
- }
- outConfig.Default = defaultAccount
- basePath, err := getOrDefaultMailbox(fileConfig.MailboxPath)
- defaultMessages := getOrDefaultMessages(fileConfig.DefaultMessages)
- if err != nil {
- log.Fatal("Could not determine base path")
- }
- for _, account := range fileConfig.Accounts {
- outConfig.Accounts[account.Name], errors.AccountErrors[account.Name] = parseData(&account, basePath, defaultMessages)
- outConfig.Workers[account.Name] = &worker.Worker{}
- }
- return &outConfig, errors
- }
- func getOrDefaultMailbox(mailboxPath string) (string, error) {
- if mailboxPath == "" {
- if homePath, err := homedir.Dir(); err != nil {
- return "", err
- } else {
- return path.Join(homePath, ".papero"), nil
- }
- }
- return mailboxPath, nil
- }
- func getOrDefaultMessages(messages maybeInt) int {
- if messages.empty() {
- return 50
- }
- return messages.value()
- }
- func valueOrEmptySlice(value []string) []string {
- if value == nil {
- return []string{}
- }
- return value
- }
- func valueOrDefaultInt(value maybeInt, defaultVal int) int {
- if value.empty() {
- return defaultVal
- }
- return value.value()
- }
- func parseData(a *account, basePath string, defaultMessages int) (*AccountData, error) {
- port, err := validatePort(a.ConnectionInfo.Port)
- if err != nil {
- return nil, err
- }
- password, err := getPassword(a.ConnectionInfo)
- if err != nil {
- return nil, err
- }
- accountData := AccountData{
- Host: a.ConnectionInfo.Host,
- Port: port,
- Username: a.ConnectionInfo.Username,
- Password: password,
- ExcludedFolders: valueOrEmptySlice(a.ExcludedFolders),
- MailboxPath: getMailboxPath(a.Name, a.MailboxPath, basePath),
- Messages: valueOrDefaultInt(a.Messages, defaultMessages),
- }
- return &accountData, nil
- }
- func validatePort(port int) (int, error) {
- if port > 0 && port < 2<<15 {
- return port, nil
- }
- return 0, ErrPortOutOfRange
- }
- func getPassword(connection *connectionInfo) (string, error) {
- if connection.PasswordExec.Present() {
- pass, err := connection.PasswordExec.Run()
- if err != nil {
- return "", err
- }
- return strings.TrimRight(pass, "\n"), nil
- }
- if connection.PasswordFile != "" {
- file, err := os.Open(connection.PasswordFile)
- if err != nil {
- return "", err
- }
- reader := bufio.NewReader(file)
- line, _, err := reader.ReadLine()
- return string(line), err
- }
- if connection.Password == "" {
- return "", ErrMissingPassword
- }
- return connection.Password, nil
- }
- func getMailboxPath(name, configPath, basePath string) string {
- if configPath != "" {
- if info, err := os.Stat(configPath); err == nil || err != os.ErrNotExist && info != nil {
- if info.IsDir() {
- return configPath
- }
- }
- }
- return path.Join(basePath, name)
- }
|