1package config
2
3import (
4 "encoding/json"
5 "os"
6 "path/filepath"
7 "strings"
8 "time"
9)
10
11// CachedFolders stores folder names for a single account.
12type CachedFolders struct {
13 AccountID string `json:"account_id"`
14 Folders []string `json:"folders"`
15 UpdatedAt time.Time `json:"updated_at"`
16}
17
18// FolderCache stores cached folders for all accounts.
19type FolderCache struct {
20 Accounts []CachedFolders `json:"accounts"`
21 UpdatedAt time.Time `json:"updated_at"`
22}
23
24// folderCacheFile returns the full path to the folder cache file.
25func folderCacheFile() (string, error) {
26 dir, err := cacheDir()
27 if err != nil {
28 return "", err
29 }
30 return filepath.Join(dir, "folder_cache.json"), nil
31}
32
33// SaveFolderCache saves the folder cache to disk.
34func SaveFolderCache(cache *FolderCache) error {
35 path, err := folderCacheFile()
36 if err != nil {
37 return err
38 }
39 if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
40 return err
41 }
42 cache.UpdatedAt = time.Now()
43 data, err := json.MarshalIndent(cache, "", " ")
44 if err != nil {
45 return err
46 }
47 return SecureWriteFile(path, data, 0600)
48}
49
50// LoadFolderCache loads the folder cache from disk.
51func LoadFolderCache() (*FolderCache, error) {
52 path, err := folderCacheFile()
53 if err != nil {
54 return nil, err
55 }
56 data, err := SecureReadFile(path)
57 if err != nil {
58 return nil, err
59 }
60 var cache FolderCache
61 if err := json.Unmarshal(data, &cache); err != nil {
62 return nil, err
63 }
64 return &cache, nil
65}
66
67// GetCachedFolders returns cached folder names for a specific account.
68func GetCachedFolders(accountID string) []string {
69 cache, err := LoadFolderCache()
70 if err != nil {
71 return nil
72 }
73 for _, acc := range cache.Accounts {
74 if acc.AccountID == accountID {
75 return acc.Folders
76 }
77 }
78 return nil
79}
80
81// SaveAccountFolders saves folder names for a specific account, merging into the existing cache.
82func SaveAccountFolders(accountID string, folders []string) error {
83 cache, err := LoadFolderCache()
84 if err != nil {
85 cache = &FolderCache{}
86 }
87
88 found := false
89 for i, acc := range cache.Accounts {
90 if acc.AccountID == accountID {
91 cache.Accounts[i].Folders = folders
92 cache.Accounts[i].UpdatedAt = time.Now()
93 found = true
94 break
95 }
96 }
97
98 if !found {
99 cache.Accounts = append(cache.Accounts, CachedFolders{
100 AccountID: accountID,
101 Folders: folders,
102 UpdatedAt: time.Now(),
103 })
104 }
105
106 return SaveFolderCache(cache)
107}
108
109// --- Per-folder email cache ---
110
111// FolderEmailCache stores cached emails for a specific folder.
112type FolderEmailCache struct {
113 FolderName string `json:"folder_name"`
114 Emails []CachedEmail `json:"emails"`
115 UpdatedAt time.Time `json:"updated_at"`
116}
117
118// folderEmailCacheDir returns the directory for folder email cache files.
119func folderEmailCacheDir() (string, error) {
120 dir, err := cacheDir()
121 if err != nil {
122 return "", err
123 }
124 return filepath.Join(dir, "folder_emails"), nil
125}
126
127// folderEmailCacheFile returns the file path for a folder's email cache.
128// Uses a sanitized folder name to avoid filesystem issues.
129func folderEmailCacheFile(folderName string) (string, error) {
130 dir, err := folderEmailCacheDir()
131 if err != nil {
132 return "", err
133 }
134 // Sanitize folder name for use as filename
135 safe := sanitizeFolderName(folderName)
136 return filepath.Join(dir, safe+".json"), nil
137}
138
139func sanitizeFolderName(name string) string {
140 // Replace path separators and other problematic chars
141 replacer := strings.NewReplacer("/", "_", "\\", "_", ":", "_", " ", "_")
142 return replacer.Replace(name)
143}
144
145// SaveFolderEmailCache saves emails for a folder to disk.
146func SaveFolderEmailCache(folderName string, emails []CachedEmail) error {
147 path, err := folderEmailCacheFile(folderName)
148 if err != nil {
149 return err
150 }
151 if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
152 return err
153 }
154 cache := FolderEmailCache{
155 FolderName: folderName,
156 Emails: emails,
157 UpdatedAt: time.Now(),
158 }
159 data, err := json.Marshal(cache)
160 if err != nil {
161 return err
162 }
163 return SecureWriteFile(path, data, 0600)
164}
165
166// LoadFolderEmailCache loads cached emails for a folder from disk.
167func LoadFolderEmailCache(folderName string) ([]CachedEmail, error) {
168 path, err := folderEmailCacheFile(folderName)
169 if err != nil {
170 return nil, err
171 }
172 data, err := SecureReadFile(path)
173 if err != nil {
174 return nil, err
175 }
176 var cache FolderEmailCache
177 if err := json.Unmarshal(data, &cache); err != nil {
178 return nil, err
179 }
180 return cache.Emails, nil
181}