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