]> granicus.if.org Git - neomutt/blob - context.c
Fix mutt_write_mime_body() application/pgp-encrypted handling
[neomutt] / context.c
1 /**
2  * @file
3  * The "currently-open" mailbox
4  *
5  * @authors
6  * Copyright (C) 2018 Richard Russon <rich@flatcap.org>
7  *
8  * @copyright
9  * This program is free software: you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the Free Software
11  * Foundation, either version 2 of the License, or (at your option) any later
12  * version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22
23 /**
24  * @page ctx The "currently-open" mailbox
25  *
26  * The "currently-open" mailbox
27  */
28
29 #include "config.h"
30 #include <string.h>
31 #include "mutt/mutt.h"
32 #include "config/lib.h"
33 #include "email/lib.h"
34 #include "core/lib.h"
35 #include "context.h"
36 #include "globals.h"
37 #include "mutt_header.h"
38 #include "mutt_thread.h"
39 #include "mx.h"
40 #include "ncrypt/ncrypt.h"
41 #include "pattern.h"
42 #include "score.h"
43 #include "sort.h"
44
45 /**
46  * ctx_free - Free a Context
47  * @param[out] ptr Context to free
48  */
49 void ctx_free(struct Context **ptr)
50 {
51   if (!ptr || !*ptr)
52     return;
53
54   struct Context *ctx = *ptr;
55
56   struct EventContext ev_ctx = { ctx };
57   notify_send(ctx->notify, NT_CONTEXT, NT_CONTEXT_CLOSE, IP & ev_ctx);
58
59   if (ctx->mailbox)
60     notify_observer_remove(ctx->mailbox->notify, ctx_mailbox_observer, IP ctx);
61
62   notify_free(&ctx->notify);
63
64   FREE(ptr);
65 }
66
67 /**
68  * ctx_new - Create a new Context
69  * @retval ptr New Context
70  */
71 struct Context *ctx_new(void)
72 {
73   struct Context *ctx = mutt_mem_calloc(1, sizeof(struct Context));
74
75   ctx->notify = notify_new(ctx, NT_CONTEXT);
76   notify_set_parent(ctx->notify, NeoMutt->notify);
77
78   return ctx;
79 }
80
81 /**
82  * ctx_cleanup - Release memory and initialize a Context object
83  * @param ctx Context to cleanup
84  */
85 void ctx_cleanup(struct Context *ctx)
86 {
87   FREE(&ctx->pattern);
88   mutt_pattern_free(&ctx->limit_pattern);
89   if (ctx->mailbox)
90     notify_observer_remove(ctx->mailbox->notify, ctx_mailbox_observer, IP ctx);
91
92   struct Notify *notify = ctx->notify;
93   memset(ctx, 0, sizeof(struct Context));
94   ctx->notify = notify;
95 }
96
97 /**
98  * ctx_update - Update the Context's message counts
99  * @param ctx          Mailbox
100  *
101  * this routine is called to update the counts in the context structure
102  */
103 void ctx_update(struct Context *ctx)
104 {
105   if (!ctx || !ctx->mailbox)
106     return;
107
108   struct Mailbox *m = ctx->mailbox;
109
110   mutt_hash_free(&m->subj_hash);
111   mutt_hash_free(&m->id_hash);
112
113   /* reset counters */
114   m->msg_unread = 0;
115   m->msg_flagged = 0;
116   m->msg_new = 0;
117   m->msg_deleted = 0;
118   m->msg_tagged = 0;
119   m->vcount = 0;
120   m->changed = false;
121
122   mutt_clear_threads(ctx);
123
124   struct Email *e = NULL;
125   for (int msgno = 0; msgno < m->msg_count; msgno++)
126   {
127     e = m->emails[msgno];
128
129     if (WithCrypto)
130     {
131       /* NOTE: this _must_ be done before the check for mailcap! */
132       e->security = crypt_query(e->content);
133     }
134
135     if (!ctx->pattern)
136     {
137       m->v2r[m->vcount] = msgno;
138       e->vnum = m->vcount++;
139     }
140     else
141       e->vnum = -1;
142     e->msgno = msgno;
143
144     if (e->env->supersedes)
145     {
146       struct Email *e2 = NULL;
147
148       if (!m->id_hash)
149         m->id_hash = mutt_make_id_hash(m);
150
151       e2 = mutt_hash_find(m->id_hash, e->env->supersedes);
152       if (e2)
153       {
154         e2->superseded = true;
155         if (C_Score)
156           mutt_score_message(ctx->mailbox, e2, true);
157       }
158     }
159
160     /* add this message to the hash tables */
161     if (m->id_hash && e->env->message_id)
162       mutt_hash_insert(m->id_hash, e->env->message_id, e);
163     if (m->subj_hash && e->env->real_subj)
164       mutt_hash_insert(m->subj_hash, e->env->real_subj, e);
165     mutt_label_hash_add(m, e);
166
167     if (C_Score)
168       mutt_score_message(ctx->mailbox, e, false);
169
170     if (e->changed)
171       m->changed = true;
172     if (e->flagged)
173       m->msg_flagged++;
174     if (e->deleted)
175       m->msg_deleted++;
176     if (!e->read)
177     {
178       m->msg_unread++;
179       if (!e->old)
180         m->msg_new++;
181     }
182   }
183
184   mutt_sort_headers(ctx, true); /* rethread from scratch */
185 }
186
187 /**
188  * ctx_update_tables - Update a Context structure's internal tables
189  * @param ctx        Mailbox
190  * @param committing Commit the changes?
191  */
192 void ctx_update_tables(struct Context *ctx, bool committing)
193 {
194   if (!ctx || !ctx->mailbox)
195     return;
196
197   struct Mailbox *m = ctx->mailbox;
198
199   int i, j, padding;
200
201   /* update memory to reflect the new state of the mailbox */
202   m->vcount = 0;
203   ctx->vsize = 0;
204   m->msg_tagged = 0;
205   m->msg_deleted = 0;
206   m->msg_new = 0;
207   m->msg_unread = 0;
208   m->changed = false;
209   m->msg_flagged = 0;
210   padding = mx_msg_padding_size(m);
211   for (i = 0, j = 0; i < m->msg_count; i++)
212   {
213     if (!m->emails[i]->quasi_deleted &&
214         ((committing && (!m->emails[i]->deleted || ((m->magic == MUTT_MAILDIR) && C_MaildirTrash))) ||
215          (!committing && m->emails[i]->active)))
216     {
217       if (i != j)
218       {
219         m->emails[j] = m->emails[i];
220         m->emails[i] = NULL;
221       }
222       m->emails[j]->msgno = j;
223       if (m->emails[j]->vnum != -1)
224       {
225         m->v2r[m->vcount] = j;
226         m->emails[j]->vnum = m->vcount++;
227         struct Body *b = m->emails[j]->content;
228         ctx->vsize += b->length + b->offset - b->hdr_offset + padding;
229       }
230
231       if (committing)
232       {
233         m->emails[j]->changed = false;
234         m->emails[j]->env->changed = false;
235       }
236       else if (m->emails[j]->changed)
237         m->changed = true;
238
239       if (!committing || ((m->magic == MUTT_MAILDIR) && C_MaildirTrash))
240       {
241         if (m->emails[j]->deleted)
242           m->msg_deleted++;
243       }
244
245       if (m->emails[j]->tagged)
246         m->msg_tagged++;
247       if (m->emails[j]->flagged)
248         m->msg_flagged++;
249       if (!m->emails[j]->read)
250       {
251         m->msg_unread++;
252         if (!m->emails[j]->old)
253           m->msg_new++;
254       }
255
256       j++;
257     }
258     else
259     {
260       if ((m->magic == MUTT_NOTMUCH) || (m->magic == MUTT_MH) ||
261           (m->magic == MUTT_MAILDIR) || (m->magic == MUTT_IMAP))
262       {
263         mailbox_size_sub(m, m->emails[i]);
264       }
265       /* remove message from the hash tables */
266       if (m->subj_hash && m->emails[i]->env->real_subj)
267         mutt_hash_delete(m->subj_hash, m->emails[i]->env->real_subj, m->emails[i]);
268       if (m->id_hash && m->emails[i]->env->message_id)
269         mutt_hash_delete(m->id_hash, m->emails[i]->env->message_id, m->emails[i]);
270       mutt_label_hash_remove(m, m->emails[i]);
271       /* The path mx_mbox_check() -> imap_check_mailbox() ->
272        *          imap_expunge_mailbox() -> ctx_update_tables()
273        * can occur before a call to mx_mbox_sync(), resulting in
274        * last_tag being stale if it's not reset here.  */
275       if (ctx->last_tag == m->emails[i])
276         ctx->last_tag = NULL;
277       email_free(&m->emails[i]);
278     }
279   }
280   m->msg_count = j;
281 }
282
283 /**
284  * ctx_mailbox_observer - Watch for changes affecting the Context - Implements ::observer_t
285  */
286 int ctx_mailbox_observer(struct NotifyCallback *nc)
287 {
288   if (!nc)
289     return -1;
290   if ((nc->obj_type != NT_MAILBOX) || (nc->event_type != NT_MAILBOX))
291     return 0;
292   struct Context *ctx = (struct Context *) nc->data;
293   if (!ctx)
294     return -1;
295
296   switch (nc->event_subtype)
297   {
298     case MBN_CLOSED:
299       mutt_clear_threads(ctx);
300       ctx_cleanup(ctx);
301       break;
302     case MBN_INVALID:
303       ctx_update(ctx);
304       break;
305     case MBN_UPDATE:
306       ctx_update_tables(ctx, true);
307       break;
308     case MBN_RESORT:
309       mutt_sort_headers(ctx, true);
310       break;
311     case MBN_UNTAG:
312       if (ctx->last_tag && ctx->last_tag->deleted)
313         ctx->last_tag = NULL;
314       break;
315   }
316
317   return 0;
318 }
319
320 /**
321  * message_is_visible - Is a message in the index within limit
322  * @param ctx   Open mailbox
323  * @param index Message ID (index into `ctx->emails[]`
324  * @retval true The message is within limit
325  *
326  * If no limit is in effect, all the messages are visible.
327  */
328 bool message_is_visible(struct Context *ctx, int index)
329 {
330   if (!ctx || !ctx->mailbox->emails || (index >= ctx->mailbox->msg_count))
331     return false;
332
333   return !ctx->pattern || ctx->mailbox->emails[index]->limited;
334 }
335
336 /**
337  * message_is_tagged - Is a message in the index tagged (and within limit)
338  * @param ctx   Open mailbox
339  * @param index Message ID (index into `ctx->emails[]`
340  * @retval true The message is both tagged and within limit
341  *
342  * If a limit is in effect, the message must be visible within it.
343  */
344 bool message_is_tagged(struct Context *ctx, int index)
345 {
346   return message_is_visible(ctx, index) && ctx->mailbox->emails[index]->tagged;
347 }
348
349 /**
350  * el_add_tagged - Get a list of the tagged Emails
351  * @param el         Empty EmailList to populate
352  * @param ctx        Current Mailbox
353  * @param e          Current Email
354  * @param use_tagged Use tagged Emails
355  * @retval num Number of selected emails
356  * @retval -1  Error
357  */
358 int el_add_tagged(struct EmailList *el, struct Context *ctx, struct Email *e, bool use_tagged)
359 {
360   int count = 0;
361
362   if (use_tagged)
363   {
364     if (!ctx || !ctx->mailbox || !ctx->mailbox->emails)
365       return -1;
366
367     for (size_t i = 0; i < ctx->mailbox->msg_count; i++)
368     {
369       if (!message_is_tagged(ctx, i))
370         continue;
371
372       struct EmailNode *en = mutt_mem_calloc(1, sizeof(*en));
373       en->email = ctx->mailbox->emails[i];
374       STAILQ_INSERT_TAIL(el, en, entries);
375       count++;
376     }
377   }
378   else
379   {
380     if (!e)
381       return -1;
382
383     struct EmailNode *en = mutt_mem_calloc(1, sizeof(*en));
384     en->email = e;
385     STAILQ_INSERT_TAIL(el, en, entries);
386     count = 1;
387   }
388
389   return count;
390 }
391
392 /**
393  * el_add_email - Get a list of the selected Emails
394  * @param e  Current Email
395  * @param el EmailList to add to
396  * @retval  0 Success
397  * @retval -1 Error
398  */
399 int el_add_email(struct EmailList *el, struct Email *e)
400 {
401   if (!el || !e)
402     return -1;
403
404   struct EmailNode *en = mutt_mem_calloc(1, sizeof(*en));
405   en->email = e;
406   STAILQ_INSERT_TAIL(el, en, entries);
407
408   return 0;
409 }