1 /**
2 * @file
3 * Manipulate the flags in an email header
4 *
5 * @authors
6 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.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 neo_flags Manipulate the flags in an email header
25 *
26 * Manipulate the flags in an email header
27 */
28
29 #include "config.h"
30 #include <stddef.h>
31 #include <stdbool.h>
32 #include "mutt/lib.h"
33 #include "config/lib.h"
34 #include "email/lib.h"
35 #include "core/lib.h"
36 #include "gui/lib.h"
37 #include "mutt.h"
38 #include "index/lib.h"
39 #include "keymap.h"
40 #include "mutt_thread.h"
41 #include "protos.h"
42
43 /**
44 * mutt_set_flag_update - Set a flag on an email
45 * @param m Mailbox
46 * @param e Email
47 * @param flag Flag to set, e.g. #MUTT_DELETE
48 * @param bf true: set the flag; false: clear the flag
49 * @param upd_mbox true: update the Mailbox
50 */
mutt_set_flag_update(struct Mailbox * m,struct Email * e,enum MessageType flag,bool bf,bool upd_mbox)51 void mutt_set_flag_update(struct Mailbox *m, struct Email *e,
52 enum MessageType flag, bool bf, bool upd_mbox)
53 {
54 if (!m || !e)
55 return;
56
57 bool changed = e->changed;
58 int deleted = m->msg_deleted;
59 int tagged = m->msg_tagged;
60 int flagged = m->msg_flagged;
61 int update = false;
62
63 if (m->readonly && (flag != MUTT_TAG))
64 return; /* don't modify anything if we are read-only */
65
66 switch (flag)
67 {
68 case MUTT_DELETE:
69
70 if (!(m->rights & MUTT_ACL_DELETE))
71 return;
72
73 if (bf)
74 {
75 const bool c_flag_safe = cs_subset_bool(NeoMutt->sub, "flag_safe");
76 if (!e->deleted && !m->readonly && (!e->flagged || !c_flag_safe))
77 {
78 e->deleted = true;
79 update = true;
80 if (upd_mbox)
81 m->msg_deleted++;
82 #ifdef USE_IMAP
83 /* deleted messages aren't treated as changed elsewhere so that the
84 * purge-on-sync option works correctly. This isn't applicable here */
85 if (m->type == MUTT_IMAP)
86 {
87 e->changed = true;
88 if (upd_mbox)
89 m->changed = true;
90 }
91 #endif
92 }
93 }
94 else if (e->deleted)
95 {
96 e->deleted = false;
97 update = true;
98 if (upd_mbox)
99 m->msg_deleted--;
100 #ifdef USE_IMAP
101 /* see my comment above */
102 if (m->type == MUTT_IMAP)
103 {
104 e->changed = true;
105 if (upd_mbox)
106 m->changed = true;
107 }
108 #endif
109 /* If the user undeletes a message which is marked as
110 * "trash" in the maildir folder on disk, the folder has
111 * been changed, and is marked accordingly. However, we do
112 * _not_ mark the message itself changed, because trashing
113 * is checked in specific code in the maildir folder
114 * driver. */
115 if ((m->type == MUTT_MAILDIR) && upd_mbox && e->trash)
116 m->changed = true;
117 }
118 break;
119
120 case MUTT_PURGE:
121
122 if (!(m->rights & MUTT_ACL_DELETE))
123 return;
124
125 if (bf)
126 {
127 if (!e->purge && !m->readonly)
128 e->purge = true;
129 }
130 else if (e->purge)
131 e->purge = false;
132 break;
133
134 case MUTT_NEW:
135
136 if (!(m->rights & MUTT_ACL_SEEN))
137 return;
138
139 if (bf)
140 {
141 if (e->read || e->old)
142 {
143 update = true;
144 e->old = false;
145 if (upd_mbox)
146 m->msg_new++;
147 if (e->read)
148 {
149 e->read = false;
150 if (upd_mbox)
151 m->msg_unread++;
152 }
153 e->changed = true;
154 if (upd_mbox)
155 m->changed = true;
156 }
157 }
158 else if (!e->read)
159 {
160 update = true;
161 if (!e->old)
162 if (upd_mbox)
163 m->msg_new--;
164 e->read = true;
165 if (upd_mbox)
166 m->msg_unread--;
167 e->changed = true;
168 if (upd_mbox)
169 m->changed = true;
170 }
171 break;
172
173 case MUTT_OLD:
174
175 if (!(m->rights & MUTT_ACL_SEEN))
176 return;
177
178 if (bf)
179 {
180 if (!e->old)
181 {
182 update = true;
183 e->old = true;
184 if (!e->read)
185 if (upd_mbox)
186 m->msg_new--;
187 e->changed = true;
188 if (upd_mbox)
189 m->changed = true;
190 }
191 }
192 else if (e->old)
193 {
194 update = true;
195 e->old = false;
196 if (!e->read)
197 if (upd_mbox)
198 m->msg_new++;
199 e->changed = true;
200 if (upd_mbox)
201 m->changed = true;
202 }
203 break;
204
205 case MUTT_READ:
206
207 if (!(m->rights & MUTT_ACL_SEEN))
208 return;
209
210 if (bf)
211 {
212 if (!e->read)
213 {
214 update = true;
215 e->read = true;
216 if (upd_mbox)
217 m->msg_unread--;
218 if (!e->old)
219 if (upd_mbox)
220 m->msg_new--;
221 e->changed = true;
222 if (upd_mbox)
223 m->changed = true;
224 }
225 }
226 else if (e->read)
227 {
228 update = true;
229 e->read = false;
230 if (upd_mbox)
231 m->msg_unread++;
232 if (!e->old)
233 if (upd_mbox)
234 m->msg_new++;
235 e->changed = true;
236 if (upd_mbox)
237 m->changed = true;
238 }
239 break;
240
241 case MUTT_REPLIED:
242
243 if (!(m->rights & MUTT_ACL_WRITE))
244 return;
245
246 if (bf)
247 {
248 if (!e->replied)
249 {
250 update = true;
251 e->replied = true;
252 if (!e->read)
253 {
254 e->read = true;
255 if (upd_mbox)
256 m->msg_unread--;
257 if (!e->old)
258 if (upd_mbox)
259 m->msg_new--;
260 }
261 e->changed = true;
262 if (upd_mbox)
263 m->changed = true;
264 }
265 }
266 else if (e->replied)
267 {
268 update = true;
269 e->replied = false;
270 e->changed = true;
271 if (upd_mbox)
272 m->changed = true;
273 }
274 break;
275
276 case MUTT_FLAG:
277
278 if (!(m->rights & MUTT_ACL_WRITE))
279 return;
280
281 if (bf)
282 {
283 if (!e->flagged)
284 {
285 update = true;
286 e->flagged = bf;
287 if (upd_mbox)
288 m->msg_flagged++;
289 e->changed = true;
290 if (upd_mbox)
291 m->changed = true;
292 }
293 }
294 else if (e->flagged)
295 {
296 update = true;
297 e->flagged = false;
298 if (upd_mbox)
299 m->msg_flagged--;
300 e->changed = true;
301 if (upd_mbox)
302 m->changed = true;
303 }
304 break;
305
306 case MUTT_TAG:
307 if (bf)
308 {
309 if (!e->tagged)
310 {
311 update = true;
312 e->tagged = true;
313 if (upd_mbox)
314 m->msg_tagged++;
315 }
316 }
317 else if (e->tagged)
318 {
319 update = true;
320 e->tagged = false;
321 if (upd_mbox)
322 m->msg_tagged--;
323 }
324 break;
325
326 default:
327 break;
328 }
329
330 if (update)
331 {
332 mutt_set_header_color(m, e);
333 struct EventMailbox ev_m = { m };
334 notify_send(m->notify, NT_MAILBOX, NT_MAILBOX_CHANGE, &ev_m);
335 }
336
337 /* if the message status has changed, we need to invalidate the cached
338 * search results so that any future search will match the current status
339 * of this message and not what it was at the time it was last searched. */
340 if (e->searched && ((changed != e->changed) || (deleted != m->msg_deleted) ||
341 (tagged != m->msg_tagged) || (flagged != m->msg_flagged)))
342 {
343 e->searched = false;
344 }
345 }
346
347 /**
348 * mutt_emails_set_flag - Set flag on messages
349 * @param m Mailbox
350 * @param el List of Emails to flag
351 * @param flag Flag to set, e.g. #MUTT_DELETE
352 * @param bf true: set the flag; false: clear the flag
353 */
mutt_emails_set_flag(struct Mailbox * m,struct EmailList * el,enum MessageType flag,bool bf)354 void mutt_emails_set_flag(struct Mailbox *m, struct EmailList *el,
355 enum MessageType flag, bool bf)
356 {
357 if (!m || !el || STAILQ_EMPTY(el))
358 return;
359
360 struct EmailNode *en = NULL;
361 STAILQ_FOREACH(en, el, entries)
362 {
363 mutt_set_flag(m, en->email, flag, bf);
364 }
365 }
366
367 /**
368 * mutt_thread_set_flag - Set a flag on an entire thread
369 * @param m Mailbox
370 * @param e Email
371 * @param flag Flag to set, e.g. #MUTT_DELETE
372 * @param bf true: set the flag; false: clear the flag
373 * @param subthread If true apply to all of the thread
374 * @retval 0 Success
375 * @retval -1 Failure
376 */
mutt_thread_set_flag(struct Mailbox * m,struct Email * e,enum MessageType flag,bool bf,bool subthread)377 int mutt_thread_set_flag(struct Mailbox *m, struct Email *e,
378 enum MessageType flag, bool bf, bool subthread)
379 {
380 struct MuttThread *start = NULL;
381 struct MuttThread *cur = e->thread;
382
383 if (!mutt_using_threads())
384 {
385 mutt_error(_("Threading is not enabled"));
386 return -1;
387 }
388
389 if (!subthread)
390 while (cur->parent)
391 cur = cur->parent;
392 start = cur;
393
394 if (cur->message && (cur != e->thread))
395 mutt_set_flag(m, cur->message, flag, bf);
396
397 cur = cur->child;
398 if (!cur)
399 goto done;
400
401 while (true)
402 {
403 if (cur->message && (cur != e->thread))
404 mutt_set_flag(m, cur->message, flag, bf);
405
406 if (cur->child)
407 cur = cur->child;
408 else if (cur->next)
409 cur = cur->next;
410 else
411 {
412 while (!cur->next)
413 {
414 cur = cur->parent;
415 if (cur == start)
416 goto done;
417 }
418 cur = cur->next;
419 }
420 }
421 done:
422 cur = e->thread;
423 if (cur->message)
424 mutt_set_flag(m, cur->message, flag, bf);
425 return 0;
426 }
427
428 /**
429 * mutt_change_flag - Change the flag on a Message
430 * @param m Mailbox
431 * @param el List of Emails to change
432 * @param bf true: set the flag; false: clear the flag
433 * @retval 0 Success
434 * @retval -1 Failure
435 */
mutt_change_flag(struct Mailbox * m,struct EmailList * el,bool bf)436 int mutt_change_flag(struct Mailbox *m, struct EmailList *el, bool bf)
437 {
438 struct MuttWindow *win = msgwin_get_window();
439 if (!win)
440 return -1;
441
442 if (!m || !el || STAILQ_EMPTY(el))
443 return -1;
444
445 enum MessageType flag = MUTT_NONE;
446 struct KeyEvent event;
447
448 struct MuttWindow *old_focus = window_set_focus(win);
449
450 mutt_window_mvprintw(win, 0, 0, "%s? (D/N/O/r/*/!): ", bf ? _("Set flag") : _("Clear flag"));
451 mutt_window_clrtoeol(win);
452 window_redraw(NULL);
453
454 do
455 {
456 event = mutt_getch();
457 } while (event.ch == -2); // Timeout
458
459 window_set_focus(old_focus);
460 msgwin_clear_text();
461
462 if (event.ch < 0) // SIGINT, Abort key (Ctrl-G)
463 return -1;
464
465 switch (event.ch)
466 {
467 case 'd':
468 case 'D':
469 if (!bf)
470 mutt_emails_set_flag(m, el, MUTT_PURGE, bf);
471 flag = MUTT_DELETE;
472 break;
473
474 case 'N':
475 case 'n':
476 flag = MUTT_NEW;
477 break;
478
479 case 'o':
480 case 'O':
481 mutt_emails_set_flag(m, el, MUTT_READ, !bf);
482 flag = MUTT_OLD;
483 break;
484
485 case 'r':
486 case 'R':
487 flag = MUTT_REPLIED;
488 break;
489
490 case '*':
491 flag = MUTT_TAG;
492 break;
493
494 case '!':
495 flag = MUTT_FLAG;
496 break;
497
498 default:
499 mutt_beep(false);
500 return -1;
501 }
502
503 mutt_emails_set_flag(m, el, flag, bf);
504 return 0;
505 }
506