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 */
mailbox_gen(void)58 int mailbox_gen(void)
59 {
60 static int gen = 0;
61 return gen++;
62 }
63
64 /**
65 * mailbox_new - Create a new Mailbox
66 * @retval ptr New Mailbox
67 */
mailbox_new(void)68 struct Mailbox *mailbox_new(void)
69 {
70 struct Mailbox *m = mutt_mem_calloc(1, sizeof(struct Mailbox));
71
72 mutt_buffer_init(&m->pathbuf);
73 m->notify = notify_new();
74
75 m->email_max = 25;
76 m->emails = mutt_mem_calloc(m->email_max, sizeof(struct Email *));
77 m->v2r = mutt_mem_calloc(m->email_max, sizeof(int));
78 m->gen = mailbox_gen();
79
80 return m;
81 }
82
83 /**
84 * mailbox_free - Free a Mailbox
85 * @param[out] ptr Mailbox to free
86 */
mailbox_free(struct Mailbox ** ptr)87 void mailbox_free(struct Mailbox **ptr)
88 {
89 if (!ptr || !*ptr)
90 return;
91
92 struct Mailbox *m = *ptr;
93
94 mutt_debug(LL_NOTIFY, "NT_MAILBOX_DELETE: %s %p\n", mailbox_get_type_name(m->type), m);
95 struct EventMailbox ev_m = { m };
96 notify_send(m->notify, NT_MAILBOX, NT_MAILBOX_DELETE, &ev_m);
97
98 mutt_debug(LL_NOTIFY, "NT_EMAIL_DELETE_ALL\n");
99 struct EventEmail ev_e = { 0, NULL };
100 notify_send(m->notify, NT_EMAIL, NT_EMAIL_DELETE_ALL, &ev_e);
101
102 for (size_t i = 0; i < m->email_max; i++)
103 email_free(&m->emails[i]);
104
105 if (m->mdata_free && m->mdata)
106 m->mdata_free(&m->mdata);
107
108 mutt_buffer_dealloc(&m->pathbuf);
109 cs_subset_free(&m->sub);
110 FREE(&m->name);
111 FREE(&m->realpath);
112 FREE(&m->emails);
113 FREE(&m->v2r);
114 notify_free(&m->notify);
115 mailbox_gc_run();
116
117 /* The NT_MAILBOX_DELETE notification might already have caused *ptr to be NULL,
118 * so call free() on the m pointer */
119 *ptr = NULL;
120 FREE(&m);
121 }
122
123 /**
124 * mailbox_find - Find the mailbox with a given path
125 * @param path Path to match
126 * @retval ptr Matching Mailbox
127 */
mailbox_find(const char * path)128 struct Mailbox *mailbox_find(const char *path)
129 {
130 if (!path)
131 return NULL;
132
133 struct stat st = { 0 };
134 struct stat st_tmp = { 0 };
135
136 if (stat(path, &st) != 0)
137 return NULL;
138
139 struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
140 neomutt_mailboxlist_get_all(&ml, NeoMutt, MUTT_MAILBOX_ANY);
141 struct MailboxNode *np = NULL;
142 struct Mailbox *m = NULL;
143 STAILQ_FOREACH(np, &ml, entries)
144 {
145 if ((stat(mailbox_path(np->mailbox), &st_tmp) == 0) &&
146 (st.st_dev == st_tmp.st_dev) && (st.st_ino == st_tmp.st_ino))
147 {
148 m = np->mailbox;
149 break;
150 }
151 }
152 neomutt_mailboxlist_clear(&ml);
153
154 return m;
155 }
156
157 /**
158 * mailbox_find_name - Find the mailbox with a given name
159 * @param name Name to match
160 * @retval ptr Matching Mailbox
161 * @retval NULL No matching mailbox found
162 *
163 * @note This searches across all Accounts
164 */
mailbox_find_name(const char * name)165 struct Mailbox *mailbox_find_name(const char *name)
166 {
167 if (!name)
168 return NULL;
169
170 struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
171 neomutt_mailboxlist_get_all(&ml, NeoMutt, MUTT_MAILBOX_ANY);
172 struct MailboxNode *np = NULL;
173 struct Mailbox *m = NULL;
174 STAILQ_FOREACH(np, &ml, entries)
175 {
176 if (mutt_str_equal(np->mailbox->name, name))
177 {
178 m = np->mailbox;
179 break;
180 }
181 }
182 neomutt_mailboxlist_clear(&ml);
183
184 return m;
185 }
186
187 /**
188 * mailbox_update - Get the mailbox's current size
189 * @param m Mailbox to check
190 *
191 * @note Only applies to local Mailboxes
192 */
mailbox_update(struct Mailbox * m)193 void mailbox_update(struct Mailbox *m)
194 {
195 struct stat st = { 0 };
196
197 if (!m)
198 return;
199
200 if (stat(mailbox_path(m), &st) == 0)
201 m->size = (off_t) st.st_size;
202 else
203 m->size = 0;
204 }
205
206 /**
207 * mailbox_changed - Notify observers of a change to a Mailbox
208 * @param m Mailbox
209 * @param action Change to Mailbox
210 */
mailbox_changed(struct Mailbox * m,enum NotifyMailbox action)211 void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
212 {
213 if (!m)
214 return;
215
216 mutt_debug(LL_NOTIFY, "NT_MAILBOX_CHANGE: %s %p\n", mailbox_get_type_name(m->type), m);
217 struct EventMailbox ev_m = { m };
218 notify_send(m->notify, NT_MAILBOX, action, &ev_m);
219 }
220
221 /**
222 * mailbox_size_add - Add an email's size to the total size of a Mailbox
223 * @param m Mailbox
224 * @param e Email
225 */
mailbox_size_add(struct Mailbox * m,const struct Email * e)226 void mailbox_size_add(struct Mailbox *m, const struct Email *e)
227 {
228 m->size += email_size(e);
229 }
230
231 /**
232 * mailbox_size_sub - Subtract an email's size from the total size of a Mailbox
233 * @param m Mailbox
234 * @param e Email
235 */
mailbox_size_sub(struct Mailbox * m,const struct Email * e)236 void mailbox_size_sub(struct Mailbox *m, const struct Email *e)
237 {
238 m->size -= email_size(e);
239 }
240
241 /**
242 * mailbox_set_subset - Set a Mailbox's Config Subset
243 * @param m Mailbox
244 * @param sub Parent Config Subset
245 * @retval true Success
246 */
mailbox_set_subset(struct Mailbox * m,struct ConfigSubset * sub)247 bool mailbox_set_subset(struct Mailbox *m, struct ConfigSubset *sub)
248 {
249 if (!m || m->sub || !sub)
250 return false;
251
252 m->sub = cs_subset_new(m->name, sub, m->notify);
253 m->sub->scope = SET_SCOPE_MAILBOX;
254 return true;
255 }
256
257 /**
258 * struct EmailGarbageCollector - Email garbage collection
259 */
260 static struct EmailGarbageCollector
261 {
262 struct Email *arr[10]; ///< Array of Emails to be deleted
263 size_t idx; ///< Current position
264 } gc = { 0 };
265
266 /**
267 * mailbox_gc_add - Add an Email to the garbage-collection set
268 * @param e Email
269 * @pre e != NULL
270 */
mailbox_gc_add(struct Email * e)271 void mailbox_gc_add(struct Email *e)
272 {
273 assert(e);
274 if (gc.idx == mutt_array_size(gc.arr))
275 {
276 mailbox_gc_run();
277 }
278 gc.arr[gc.idx++] = e;
279 }
280
281 /**
282 * mailbox_gc_run - Run the garbage-collection
283 */
mailbox_gc_run(void)284 void mailbox_gc_run(void)
285 {
286 for (size_t i = 0; i < gc.idx; i++)
287 {
288 email_free(&gc.arr[i]);
289 }
290 gc.idx = 0;
291 }
292
293 /**
294 * mailbox_get_type_name - Get the type of a Mailbox
295 * @param type Mailbox type, e.g. #MUTT_IMAP
296 * @retval ptr String describing Mailbox type
297 */
mailbox_get_type_name(enum MailboxType type)298 const char *mailbox_get_type_name(enum MailboxType type)
299 {
300 const char *name = mutt_map_get_name(type, MailboxTypes);
301 if (name)
302 return name;
303 return "UNKNOWN";
304 }
305