Coverage Report

Created: 2023-06-07 06:15

/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
}