/src/neomutt/core/mailbox.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file |
3 | | * Representation of a mailbox |
4 | | * |
5 | | * @authors |
6 | | * Copyright (C) 1996-2000,2010,2013 Michael R. Elkins <me@mutt.org> |
7 | | * Copyright (C) 2016-2017 Kevin J. McCarthy <kevin@8t8.us> |
8 | | * Copyright (C) 2018-2019 Richard Russon <rich@flatcap.org> |
9 | | * |
10 | | * @copyright |
11 | | * This program is free software: you can redistribute it and/or modify it under |
12 | | * the terms of the GNU General Public License as published by the Free Software |
13 | | * Foundation, either version 2 of the License, or (at your option) any later |
14 | | * version. |
15 | | * |
16 | | * This program is distributed in the hope that it will be useful, but WITHOUT |
17 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
18 | | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
19 | | * details. |
20 | | * |
21 | | * You should have received a copy of the GNU General Public License along with |
22 | | * this program. If not, see <http://www.gnu.org/licenses/>. |
23 | | */ |
24 | | |
25 | | /** |
26 | | * @page core_mailbox Mailbox object |
27 | | * |
28 | | * Representation of a Mailbox |
29 | | */ |
30 | | |
31 | | #include "config.h" |
32 | | #include <assert.h> |
33 | | #include <sys/stat.h> |
34 | | #include "config/lib.h" |
35 | | #include "email/lib.h" |
36 | | #include "mailbox.h" |
37 | | #include "neomutt.h" |
38 | | |
39 | | /// Lookups for Mailbox types |
40 | | static const struct Mapping MailboxTypes[] = { |
41 | | // clang-format off |
42 | | { "compressed", MUTT_COMPRESSED }, |
43 | | { "imap", MUTT_IMAP }, |
44 | | { "maildir", MUTT_MAILDIR }, |
45 | | { "mbox", MUTT_MBOX }, |
46 | | { "mh", MUTT_MH }, |
47 | | { "mmdf", MUTT_MMDF }, |
48 | | { "nntp", MUTT_NNTP }, |
49 | | { "notmuch", MUTT_NOTMUCH }, |
50 | | { "pop", MUTT_POP }, |
51 | | { NULL, 0 }, |
52 | | // clang-format on |
53 | | }; |
54 | | |
55 | | /** |
56 | | * mailbox_gen - Get the next generation number |
57 | | * @retval num Unique number |
58 | | */ |
59 | | int mailbox_gen(void) |
60 | 0 | { |
61 | 0 | static int gen = 0; |
62 | 0 | return gen++; |
63 | 0 | } |
64 | | |
65 | | /** |
66 | | * mailbox_new - Create a new Mailbox |
67 | | * @retval ptr New Mailbox |
68 | | */ |
69 | | struct Mailbox *mailbox_new(void) |
70 | 0 | { |
71 | 0 | struct Mailbox *m = mutt_mem_calloc(1, sizeof(struct Mailbox)); |
72 | |
|
73 | 0 | buf_init(&m->pathbuf); |
74 | 0 | m->notify = notify_new(); |
75 | |
|
76 | 0 | m->email_max = 25; |
77 | 0 | m->emails = mutt_mem_calloc(m->email_max, sizeof(struct Email *)); |
78 | 0 | m->v2r = mutt_mem_calloc(m->email_max, sizeof(int)); |
79 | 0 | m->gen = mailbox_gen(); |
80 | |
|
81 | 0 | return m; |
82 | 0 | } |
83 | | |
84 | | /** |
85 | | * mailbox_free - Free a Mailbox |
86 | | * @param[out] ptr Mailbox to free |
87 | | */ |
88 | | void mailbox_free(struct Mailbox **ptr) |
89 | 0 | { |
90 | 0 | if (!ptr || !*ptr) |
91 | 0 | return; |
92 | | |
93 | 0 | struct Mailbox *m = *ptr; |
94 | |
|
95 | 0 | const bool do_free = (m->opened == 0) && !m->visible; |
96 | |
|
97 | 0 | mutt_debug(LL_DEBUG3, "%sfreeing %s mailbox %s with refcount %d\n", |
98 | 0 | do_free ? "" : "not ", m->visible ? "visible" : "invisible", |
99 | 0 | buf_string(&m->pathbuf), m->opened); |
100 | |
|
101 | 0 | if (!do_free) |
102 | 0 | { |
103 | 0 | return; |
104 | 0 | } |
105 | | |
106 | 0 | mutt_debug(LL_NOTIFY, "NT_MAILBOX_DELETE: %s %p\n", mailbox_get_type_name(m->type), m); |
107 | 0 | struct EventMailbox ev_m = { m }; |
108 | 0 | notify_send(m->notify, NT_MAILBOX, NT_MAILBOX_DELETE, &ev_m); |
109 | |
|
110 | 0 | mutt_debug(LL_NOTIFY, "NT_EMAIL_DELETE_ALL\n"); |
111 | 0 | struct EventEmail ev_e = { 0, NULL }; |
112 | 0 | notify_send(m->notify, NT_EMAIL, NT_EMAIL_DELETE_ALL, &ev_e); |
113 | |
|
114 | 0 | for (size_t i = 0; i < m->email_max; i++) |
115 | 0 | email_free(&m->emails[i]); |
116 | |
|
117 | 0 | if (m->mdata_free && m->mdata) |
118 | 0 | m->mdata_free(&m->mdata); |
119 | |
|
120 | 0 | buf_dealloc(&m->pathbuf); |
121 | 0 | cs_subset_free(&m->sub); |
122 | 0 | FREE(&m->name); |
123 | 0 | FREE(&m->realpath); |
124 | 0 | FREE(&m->emails); |
125 | 0 | FREE(&m->v2r); |
126 | 0 | notify_free(&m->notify); |
127 | 0 | mailbox_gc_run(); |
128 | | |
129 | | /* The NT_MAILBOX_DELETE notification might already have caused *ptr to be NULL, |
130 | | * so call free() on the m pointer */ |
131 | 0 | *ptr = NULL; |
132 | 0 | FREE(&m); |
133 | 0 | } |
134 | | |
135 | | /** |
136 | | * mailbox_find - Find the mailbox with a given path |
137 | | * @param path Path to match |
138 | | * @retval ptr Matching Mailbox |
139 | | */ |
140 | | struct Mailbox *mailbox_find(const char *path) |
141 | 0 | { |
142 | 0 | if (!path) |
143 | 0 | return NULL; |
144 | | |
145 | 0 | struct stat st = { 0 }; |
146 | 0 | struct stat st_tmp = { 0 }; |
147 | |
|
148 | 0 | if (stat(path, &st) != 0) |
149 | 0 | return NULL; |
150 | | |
151 | 0 | struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml); |
152 | 0 | neomutt_mailboxlist_get_all(&ml, NeoMutt, MUTT_MAILBOX_ANY); |
153 | 0 | struct MailboxNode *np = NULL; |
154 | 0 | struct Mailbox *m = NULL; |
155 | 0 | STAILQ_FOREACH(np, &ml, entries) |
156 | 0 | { |
157 | 0 | if ((stat(mailbox_path(np->mailbox), &st_tmp) == 0) && |
158 | 0 | (st.st_dev == st_tmp.st_dev) && (st.st_ino == st_tmp.st_ino)) |
159 | 0 | { |
160 | 0 | m = np->mailbox; |
161 | 0 | break; |
162 | 0 | } |
163 | 0 | } |
164 | 0 | neomutt_mailboxlist_clear(&ml); |
165 | |
|
166 | 0 | return m; |
167 | 0 | } |
168 | | |
169 | | /** |
170 | | * mailbox_find_name - Find the mailbox with a given name |
171 | | * @param name Name to match |
172 | | * @retval ptr Matching Mailbox |
173 | | * @retval NULL No matching mailbox found |
174 | | * |
175 | | * @note This searches across all Accounts |
176 | | */ |
177 | | struct Mailbox *mailbox_find_name(const char *name) |
178 | 0 | { |
179 | 0 | if (!name) |
180 | 0 | return NULL; |
181 | | |
182 | 0 | struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml); |
183 | 0 | neomutt_mailboxlist_get_all(&ml, NeoMutt, MUTT_MAILBOX_ANY); |
184 | 0 | struct MailboxNode *np = NULL; |
185 | 0 | struct Mailbox *m = NULL; |
186 | 0 | STAILQ_FOREACH(np, &ml, entries) |
187 | 0 | { |
188 | 0 | if (mutt_str_equal(np->mailbox->name, name)) |
189 | 0 | { |
190 | 0 | m = np->mailbox; |
191 | 0 | break; |
192 | 0 | } |
193 | 0 | } |
194 | 0 | neomutt_mailboxlist_clear(&ml); |
195 | |
|
196 | 0 | return m; |
197 | 0 | } |
198 | | |
199 | | /** |
200 | | * mailbox_update - Get the mailbox's current size |
201 | | * @param m Mailbox to check |
202 | | * |
203 | | * @note Only applies to local Mailboxes |
204 | | */ |
205 | | void mailbox_update(struct Mailbox *m) |
206 | 0 | { |
207 | 0 | struct stat st = { 0 }; |
208 | |
|
209 | 0 | if (!m) |
210 | 0 | return; |
211 | | |
212 | 0 | if (stat(mailbox_path(m), &st) == 0) |
213 | 0 | m->size = (off_t) st.st_size; |
214 | 0 | else |
215 | 0 | m->size = 0; |
216 | 0 | } |
217 | | |
218 | | /** |
219 | | * mailbox_changed - Notify observers of a change to a Mailbox |
220 | | * @param m Mailbox |
221 | | * @param action Change to Mailbox |
222 | | */ |
223 | | void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action) |
224 | 0 | { |
225 | 0 | if (!m) |
226 | 0 | return; |
227 | | |
228 | 0 | mutt_debug(LL_NOTIFY, "NT_MAILBOX_CHANGE: %s %p\n", mailbox_get_type_name(m->type), m); |
229 | 0 | struct EventMailbox ev_m = { m }; |
230 | 0 | notify_send(m->notify, NT_MAILBOX, action, &ev_m); |
231 | 0 | } |
232 | | |
233 | | /** |
234 | | * mailbox_size_add - Add an email's size to the total size of a Mailbox |
235 | | * @param m Mailbox |
236 | | * @param e Email |
237 | | */ |
238 | | void mailbox_size_add(struct Mailbox *m, const struct Email *e) |
239 | 0 | { |
240 | 0 | m->size += email_size(e); |
241 | 0 | } |
242 | | |
243 | | /** |
244 | | * mailbox_size_sub - Subtract an email's size from the total size of a Mailbox |
245 | | * @param m Mailbox |
246 | | * @param e Email |
247 | | */ |
248 | | void mailbox_size_sub(struct Mailbox *m, const struct Email *e) |
249 | 0 | { |
250 | 0 | m->size -= email_size(e); |
251 | 0 | } |
252 | | |
253 | | /** |
254 | | * mailbox_set_subset - Set a Mailbox's Config Subset |
255 | | * @param m Mailbox |
256 | | * @param sub Parent Config Subset |
257 | | * @retval true Success |
258 | | */ |
259 | | bool mailbox_set_subset(struct Mailbox *m, struct ConfigSubset *sub) |
260 | 0 | { |
261 | 0 | if (!m || m->sub || !sub) |
262 | 0 | return false; |
263 | | |
264 | 0 | m->sub = cs_subset_new(m->name, sub, m->notify); |
265 | 0 | m->sub->scope = SET_SCOPE_MAILBOX; |
266 | 0 | return true; |
267 | 0 | } |
268 | | |
269 | | /** |
270 | | * struct EmailGarbageCollector - Email garbage collection |
271 | | */ |
272 | | struct EmailGarbageCollector |
273 | | { |
274 | | struct Email *arr[10]; ///< Array of Emails to be deleted |
275 | | size_t idx; ///< Current position |
276 | | }; |
277 | | |
278 | | /// Set of Emails to be deleted |
279 | | static struct EmailGarbageCollector GC = { 0 }; |
280 | | |
281 | | /** |
282 | | * mailbox_gc_add - Add an Email to the garbage-collection set |
283 | | * @param e Email |
284 | | * |
285 | | * @pre e is not NULL |
286 | | */ |
287 | | void mailbox_gc_add(struct Email *e) |
288 | 0 | { |
289 | 0 | assert(e); |
290 | 0 | if (GC.idx == mutt_array_size(GC.arr)) |
291 | 0 | { |
292 | 0 | mailbox_gc_run(); |
293 | 0 | } |
294 | 0 | GC.arr[GC.idx] = e; |
295 | 0 | GC.idx++; |
296 | 0 | } |
297 | | |
298 | | /** |
299 | | * mailbox_gc_run - Run the garbage-collection |
300 | | */ |
301 | | void mailbox_gc_run(void) |
302 | 0 | { |
303 | 0 | for (size_t i = 0; i < GC.idx; i++) |
304 | 0 | { |
305 | 0 | email_free(&GC.arr[i]); |
306 | 0 | } |
307 | 0 | GC.idx = 0; |
308 | 0 | } |
309 | | |
310 | | /** |
311 | | * mailbox_get_type_name - Get the type of a Mailbox |
312 | | * @param type Mailbox type, e.g. #MUTT_IMAP |
313 | | * @retval ptr String describing Mailbox type |
314 | | */ |
315 | | const char *mailbox_get_type_name(enum MailboxType type) |
316 | 0 | { |
317 | 0 | const char *name = mutt_map_get_name(type, MailboxTypes); |
318 | 0 | if (name) |
319 | 0 | return name; |
320 | 0 | return "UNKNOWN"; |
321 | 0 | } |