1package config
2
3import (
4 "encoding/json"
5 "os"
6 "path/filepath"
7
8 "github.com/google/uuid"
9)
10
11// Account stores the configuration for a single email account.
12type Account struct {
13 ID string `json:"id"`
14 Name string `json:"name"`
15 Email string `json:"email"`
16 Password string `json:"password"`
17 ServiceProvider string `json:"service_provider"` // "gmail", "icloud", or "custom"
18
19 // Custom server settings (used when ServiceProvider is "custom")
20 IMAPServer string `json:"imap_server,omitempty"`
21 IMAPPort int `json:"imap_port,omitempty"`
22 SMTPServer string `json:"smtp_server,omitempty"`
23 SMTPPort int `json:"smtp_port,omitempty"`
24}
25
26// Config stores the user's email configuration with multiple accounts.
27type Config struct {
28 Accounts []Account `json:"accounts"`
29}
30
31// GetIMAPServer returns the IMAP server address for the account.
32func (a *Account) GetIMAPServer() string {
33 switch a.ServiceProvider {
34 case "gmail":
35 return "imap.gmail.com"
36 case "icloud":
37 return "imap.mail.me.com"
38 case "custom":
39 return a.IMAPServer
40 default:
41 return ""
42 }
43}
44
45// GetIMAPPort returns the IMAP port for the account.
46func (a *Account) GetIMAPPort() int {
47 switch a.ServiceProvider {
48 case "gmail", "icloud":
49 return 993
50 case "custom":
51 if a.IMAPPort != 0 {
52 return a.IMAPPort
53 }
54 return 993 // Default IMAP SSL port
55 default:
56 return 993
57 }
58}
59
60// GetSMTPServer returns the SMTP server address for the account.
61func (a *Account) GetSMTPServer() string {
62 switch a.ServiceProvider {
63 case "gmail":
64 return "smtp.gmail.com"
65 case "icloud":
66 return "smtp.mail.me.com"
67 case "custom":
68 return a.SMTPServer
69 default:
70 return ""
71 }
72}
73
74// GetSMTPPort returns the SMTP port for the account.
75func (a *Account) GetSMTPPort() int {
76 switch a.ServiceProvider {
77 case "gmail", "icloud":
78 return 587
79 case "custom":
80 if a.SMTPPort != 0 {
81 return a.SMTPPort
82 }
83 return 587 // Default SMTP TLS port
84 default:
85 return 587
86 }
87}
88
89// configDir returns the path to the configuration directory.
90func configDir() (string, error) {
91 home, err := os.UserHomeDir()
92 if err != nil {
93 return "", err
94 }
95 return filepath.Join(home, ".config", "matcha"), nil
96}
97
98// configFile returns the full path to the configuration file.
99func configFile() (string, error) {
100 dir, err := configDir()
101 if err != nil {
102 return "", err
103 }
104 return filepath.Join(dir, "config.json"), nil
105}
106
107// SaveConfig saves the given configuration to the config file.
108func SaveConfig(config *Config) error {
109 path, err := configFile()
110 if err != nil {
111 return err
112 }
113 if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
114 return err
115 }
116 data, err := json.MarshalIndent(config, "", " ")
117 if err != nil {
118 return err
119 }
120 return os.WriteFile(path, data, 0600)
121}
122
123// LoadConfig loads the configuration from the config file.
124func LoadConfig() (*Config, error) {
125 path, err := configFile()
126 if err != nil {
127 return nil, err
128 }
129 data, err := os.ReadFile(path)
130 if err != nil {
131 return nil, err
132 }
133 var config Config
134 if err := json.Unmarshal(data, &config); err != nil {
135 // Try to load legacy single-account config
136 var legacyConfig legacyConfigFormat
137 if legacyErr := json.Unmarshal(data, &legacyConfig); legacyErr == nil && legacyConfig.Email != "" {
138 // Convert legacy config to new format
139 config = Config{
140 Accounts: []Account{
141 {
142 ID: uuid.New().String(),
143 Name: legacyConfig.Name,
144 Email: legacyConfig.Email,
145 Password: legacyConfig.Password,
146 ServiceProvider: legacyConfig.ServiceProvider,
147 },
148 },
149 }
150 // Save the migrated config
151 if saveErr := SaveConfig(&config); saveErr != nil {
152 return nil, saveErr
153 }
154 return &config, nil
155 }
156 return nil, err
157 }
158 return &config, nil
159}
160
161// legacyConfigFormat represents the old single-account configuration format.
162type legacyConfigFormat struct {
163 ServiceProvider string `json:"service_provider"`
164 Email string `json:"email"`
165 Password string `json:"password"`
166 Name string `json:"name"`
167}
168
169// AddAccount adds a new account to the configuration.
170func (c *Config) AddAccount(account Account) {
171 if account.ID == "" {
172 account.ID = uuid.New().String()
173 }
174 c.Accounts = append(c.Accounts, account)
175}
176
177// RemoveAccount removes an account by its ID.
178func (c *Config) RemoveAccount(id string) bool {
179 for i, acc := range c.Accounts {
180 if acc.ID == id {
181 c.Accounts = append(c.Accounts[:i], c.Accounts[i+1:]...)
182 return true
183 }
184 }
185 return false
186}
187
188// GetAccountByID returns an account by its ID.
189func (c *Config) GetAccountByID(id string) *Account {
190 for i := range c.Accounts {
191 if c.Accounts[i].ID == id {
192 return &c.Accounts[i]
193 }
194 }
195 return nil
196}
197
198// GetAccountByEmail returns an account by its email address.
199func (c *Config) GetAccountByEmail(email string) *Account {
200 for i := range c.Accounts {
201 if c.Accounts[i].Email == email {
202 return &c.Accounts[i]
203 }
204 }
205 return nil
206}
207
208// HasAccounts returns true if there are any configured accounts.
209func (c *Config) HasAccounts() bool {
210 return len(c.Accounts) > 0
211}
212
213// GetFirstAccount returns the first account or nil if none exist.
214func (c *Config) GetFirstAccount() *Account {
215 if len(c.Accounts) > 0 {
216 return &c.Accounts[0]
217 }
218 return nil
219}