2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2007 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 /*======================================================================
17 Implements Pine message flag management routines
21 #include "../pith/headers.h"
22 #include "../pith/flag.h"
23 #include "../pith/pineelt.h"
24 #include "../pith/icache.h"
25 #include "../pith/mailindx.h"
26 #include "../pith/mailcmd.h"
27 #include "../pith/msgno.h"
28 #include "../pith/thread.h"
29 #include "../pith/sort.h"
30 #include "../pith/news.h"
31 #include "../pith/sequence.h"
37 void flag_search(MAILSTREAM
*, int, MsgNo
, MSGNO_S
*, long (*)(MAILSTREAM
*));
38 long flag_search_sequence(MAILSTREAM
*, MSGNO_S
*, long, int);
42 /*----------------------------------------------------------------------
43 Return sequence number based on given index that are search-worthy
48 flags -- flags for msgline_hidden
50 Result: 0 : index not search-worthy
51 -1 : index out of bounds
52 1 - stream->nmsgs : sequence number to flag search
56 flag_search_sequence(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
, int flags
)
60 return((msgno
> stream
->nmsgs
62 || (msgmap
&& !(rawno
= mn_m2raw(msgmap
, msgno
))))
63 ? -1L /* out of range! */
64 : ((get_lflag(stream
, NULL
, rawno
, MN_EXLD
)
65 || (msgmap
&& msgline_hidden(stream
, msgmap
, msgno
, flags
)))
66 ? 0L /* NOT interesting! */
72 /*----------------------------------------------------------------------
73 Perform mail_search based on flag bits
80 Result: if no set_* specified, call mail_search to light the searched
81 bit for all the messages matching the given flags. If set_start
82 specified, it is an index (possibly into set_msgmap) telling
83 us where to search for invalid flag state hence when we
84 return everything with the searched bit is interesting and
85 everything with the valid bit lit is believably valid.
89 flag_search(MAILSTREAM
*stream
, int flags
, MsgNo set_start
, MSGNO_S
*set_msgmap
,
90 long int (*ping
)(MAILSTREAM
*))
95 SEARCHSET
*full_set
= NULL
, **set
;
97 extern MAILSTREAM
*mm_search_stream
;
102 new = sp_new_mail_count(stream
);
104 /* Anything we don't already have flags for? */
107 * Use elt's sequence bit to coalesce runs in ascending
110 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
111 if((mc
= mail_elt(stream
, i
)) != NULL
)
115 (n
= flag_search_sequence(stream
, set_msgmap
, i
, MH_ANYTHD
)) >= 0L;
116 (flags
& F_SRCHBACK
) ? i
-- : i
++)
117 if(n
> 0L && n
<= stream
->nmsgs
118 && (mc
= mail_elt(stream
, n
)) && !mc
->valid
)
121 /* Unroll searchset in ascending sequence order */
123 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
124 if((mc
= mail_elt(stream
, i
)) && mc
->sequence
){
126 if(((*set
)->last
? (*set
)->last
: (*set
)->first
)+1L == i
)
133 *set
= mail_newsearchset();
139 * No search-worthy messages?, prod the server for
140 * any flag updates and clear the searched bits...
143 if(full_set
->first
== 1
144 && full_set
->last
== stream
->nmsgs
145 && full_set
->next
== NULL
)
146 mail_free_searchset(&full_set
);
149 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
150 if((mc
= mail_elt(stream
, i
)) != NULL
)
154 (*ping
)(stream
); /* prod server for any flag updates */
156 if(!(flags
& F_NOFILT
) && new != sp_new_mail_count(stream
)){
157 process_filter_patterns(stream
, sp_msgmap(stream
),
158 sp_new_mail_count(stream
));
160 refresh_sort(stream
, sp_msgmap(stream
), SRT_NON
);
161 flag_search(stream
, flags
, set_start
, set_msgmap
, ping
);
168 if((!is_imap_stream(stream
) || modern_imap_stream(stream
))
169 && !(IS_NEWS(stream
))){
170 pgm
= mail_newsearchpgm();
196 if(flags
& (F_FWD
| F_UNFWD
)){
199 for(slpp
= (flags
& F_FWD
) ? &pgm
->keyword
: &pgm
->unkeyword
;
201 slpp
= &(*slpp
)->next
)
204 *slpp
= mail_newstringlist();
205 (*slpp
)->text
.data
= (unsigned char *) cpystr(FORWARDED_FLAG
);
206 (*slpp
)->text
.size
= (unsigned long) strlen(FORWARDED_FLAG
);
212 if(flags
& F_OR_SEEN
){
213 SEARCHPGM
*pgm2
= mail_newsearchpgm();
214 pgm2
->or = mail_newsearchor();
215 pgm2
->or->first
= pgm
;
216 pgm2
->or->second
= mail_newsearchpgm();
217 pgm2
->or->second
->seen
= 1;
221 if(flags
& F_OR_UNSEEN
){
222 SEARCHPGM
*pgm2
= mail_newsearchpgm();
223 pgm2
->or = mail_newsearchor();
224 pgm2
->or->first
= pgm
;
225 pgm2
->or->second
= mail_newsearchpgm();
226 pgm2
->or->second
->unseen
= 1;
230 if(flags
& F_OR_DEL
){
231 SEARCHPGM
*pgm2
= mail_newsearchpgm();
232 pgm2
->or = mail_newsearchor();
233 pgm2
->or->first
= pgm
;
234 pgm2
->or->second
= mail_newsearchpgm();
235 pgm2
->or->second
->deleted
= 1;
239 if(flags
& F_OR_UNDEL
){
240 SEARCHPGM
*pgm2
= mail_newsearchpgm();
241 pgm2
->or = mail_newsearchor();
242 pgm2
->or->first
= pgm
;
243 pgm2
->or->second
= mail_newsearchpgm();
244 pgm2
->or->second
->undeleted
= 1;
248 if(flags
& F_OR_FLAG
){
249 SEARCHPGM
*pgm2
= mail_newsearchpgm();
250 pgm2
->or = mail_newsearchor();
251 pgm2
->or->first
= pgm
;
252 pgm2
->or->second
= mail_newsearchpgm();
253 pgm2
->or->second
->flagged
= 1;
257 if(flags
& F_OR_UNFLAG
){
258 SEARCHPGM
*pgm2
= mail_newsearchpgm();
259 pgm2
->or = mail_newsearchor();
260 pgm2
->or->first
= pgm
;
261 pgm2
->or->second
= mail_newsearchpgm();
262 pgm2
->or->second
->unflagged
= 1;
266 if(flags
& F_OR_ANS
){
267 SEARCHPGM
*pgm2
= mail_newsearchpgm();
268 pgm2
->or = mail_newsearchor();
269 pgm2
->or->first
= pgm
;
270 pgm2
->or->second
= mail_newsearchpgm();
271 pgm2
->or->second
->answered
= 1;
275 if(flags
& F_OR_UNANS
){
276 SEARCHPGM
*pgm2
= mail_newsearchpgm();
277 pgm2
->or = mail_newsearchor();
278 pgm2
->or->first
= pgm
;
279 pgm2
->or->second
= mail_newsearchpgm();
280 pgm2
->or->second
->unanswered
= 1;
284 if(flags
& (F_OR_FWD
| F_OR_UNFWD
)){
286 SEARCHPGM
*pgm2
= mail_newsearchpgm();
287 pgm2
->or = mail_newsearchor();
288 pgm2
->or->first
= pgm
;
289 pgm2
->or->second
= mail_newsearchpgm();
291 for(slpp
= (flags
& F_OR_FWD
)
292 ? &pgm2
->or->second
->keyword
293 : &pgm2
->or->second
->unkeyword
;
295 slpp
= &(*slpp
)->next
)
298 *slpp
= mail_newstringlist();
299 (*slpp
)->text
.data
= (unsigned char *) cpystr(FORWARDED_FLAG
);
300 (*slpp
)->text
.size
= (unsigned long) strlen(FORWARDED_FLAG
);
305 if(flags
& F_OR_RECENT
){
306 SEARCHPGM
*pgm2
= mail_newsearchpgm();
307 pgm2
->or = mail_newsearchor();
308 pgm2
->or->first
= pgm
;
309 pgm2
->or->second
= mail_newsearchpgm();
310 pgm2
->or->second
->recent
= 1;
314 pgm
->msgno
= full_set
;
316 pine_mail_search_full(mm_search_stream
= stream
, NULL
,
317 pgm
, SE_NOPREFETCH
| SE_FREE
);
319 if(!(flags
& F_NOFILT
) && new != sp_new_mail_count(stream
)){
320 process_filter_patterns(stream
, sp_msgmap(stream
),
321 sp_new_mail_count(stream
));
323 flag_search(stream
, flags
, set_start
, set_msgmap
, ping
);
328 /* sequence bits of interesting msgs set */
329 mail_free_searchset(&full_set
);
332 /* light sequence bits of interesting msgs */
334 (n
= flag_search_sequence(stream
, set_msgmap
, i
, MH_ANYTHD
)) >= 0L;
336 if(n
> 0L && n
<= stream
->nmsgs
337 && (mc
= mail_elt(stream
, n
)) != NULL
)
338 mc
->sequence
= !mc
->valid
? 1 : 0;
341 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
342 if((mc
= mail_elt(stream
, i
)) != NULL
)
345 if((seq
= build_sequence(stream
, NULL
, NULL
)) != NULL
){
346 pine_mail_fetch_flags(stream
, seq
, 0L);
347 fs_give((void **) &seq
);
354 /*----------------------------------------------------------------------
355 count messages on stream with specified system flag attributes
357 Args: stream -- The stream/folder to look at message status
358 flags -- flags on folder/stream to examine
360 Result: count of messages flagged as requested
362 Task: return count of message flagged as requested while being
363 as server/network friendly as possible.
365 Strategy: run thru flags to make sure they're all valid. If any
366 invalid, do a search starting with the invalid message.
367 If all valid, ping the server to let it know we're
368 receptive to flag updates. At this
370 ----------------------------------------------------------------------*/
372 count_flagged(MAILSTREAM
*stream
, long int flags
)
380 flag_search(stream
, flags
, 1, NULL
, pine_mail_ping
);
382 /* Paw thru once more since all should be updated */
383 for(n
= 1L, count
= 0L; n
<= stream
->nmsgs
; n
++)
384 if((((mc
= mail_elt(stream
, n
)) && mc
->searched
)
385 || (mc
&& mc
->valid
&& FLAG_MATCH(flags
, mc
, stream
)))
386 && !get_lflag(stream
, NULL
, n
, MN_EXLD
)){
387 mc
->searched
= 1; /* caller may be interested! */
396 /*----------------------------------------------------------------------
397 Find the first message with the specified flags set
399 Args: flags -- Flags in messagecache to match on
400 stream -- The stream/folder to look at message status
402 Result: Message number of first message with specified flags set or the
403 number of the last message if none found.
404 ----------------------------------------------------------------------*/
406 first_sorted_flagged(long unsigned int flags
, MAILSTREAM
*stream
, long int set_start
, int opts
)
408 MsgNo i
, n
, start_with
, winner
= 0L;
413 msgmap
= sp_msgmap(stream
);
415 last
= (opts
& FSF_LAST
);
417 /* set_start only affects which search bits we light */
418 start_with
= set_start
? set_start
419 : (flags
& F_SRCHBACK
)
420 ? mn_get_total(msgmap
) : 1L;
421 flag_search(stream
, flags
, start_with
, msgmap
, NULL
);
424 (n
= flag_search_sequence(stream
, msgmap
, i
,
425 (opts
& FSF_SKIP_CHID
) ? 0 : MH_ANYTHD
)) >= 0L;
426 (flags
& F_SRCHBACK
) ? i
-- : i
++)
427 if(n
> 0L && n
<= stream
->nmsgs
428 && (((mc
= mail_elt(stream
, n
)) && mc
->searched
)
429 || (mc
&& mc
->valid
&& FLAG_MATCH(flags
, mc
, stream
)))){
435 if(winner
== 0L && flags
!= F_UNDEL
&& flags
!= F_NONE
){
437 "First_sorted_flagged didn't find a winner, look for undeleted\n"));
438 winner
= first_sorted_flagged(F_UNDEL
, stream
, 0L,
439 opts
| (mn_get_revsort(msgmap
) ? 0 : FSF_LAST
));
442 if(winner
== 0L && flags
!= F_NONE
){
444 "First_sorted_flagged didn't find an undeleted, look for visible\n"));
445 winner
= first_sorted_flagged(F_NONE
, stream
, 0L,
446 opts
| (mn_get_revsort(msgmap
) ? 0 : FSF_LAST
));
450 "First_sorted_flagged returning winner = %ld\n", winner
));
451 return(winner
? winner
452 : (mn_get_revsort(msgmap
)
453 ? 1L : mn_get_total(msgmap
)));
458 /*----------------------------------------------------------------------
459 Find the next message with specified flags set
461 Args: flags -- Flags in messagecache to match on
462 stream -- The stream/folder to look at message status
463 start -- Start looking after this message
464 opts -- These bits are both input and output. On input the bit
465 NSF_TRUST_FLAGS tells us whether we need to ping or not.
466 On input, the bit NSF_SEARCH_BACK tells us that we want to
467 know about matches <= start if we don't find any > start.
468 On output, NSF_FLAG_MATCH is set if we matched a message.
469 Returns: Message number of the matched message, if any; else the start # or
470 the max_msgno if the mailbox changed dramatically.
471 ----------------------------------------------------------------------*/
473 next_sorted_flagged(long unsigned int flags
, MAILSTREAM
*stream
, long int start
, int *opts
)
477 int rev
, fss_flags
= 0;
480 msgmap
= sp_msgmap(stream
);
483 * Search for the next thing the caller's interested in...
486 fss_flags
= (opts
&& *opts
& NSF_SKIP_CHID
) ? 0 : MH_ANYTHD
;
487 rev
= (opts
&& *opts
& NSF_SEARCH_BACK
);
488 dir
= (rev
? -1L : 1L);
490 flag_search(stream
, flags
| (rev
? F_SRCHBACK
: 0), start
+ dir
,
492 (opts
&& ((*opts
) & NSF_TRUST_FLAGS
)) ? NULL
: pine_mail_ping
);
495 (n
= flag_search_sequence(stream
, msgmap
,
496 i
, fss_flags
)) >= 0L;
498 if(n
> 0L && n
<= stream
->nmsgs
499 && (((mc
= mail_elt(stream
, n
)) && mc
->searched
)
500 || (mc
&& mc
->valid
&& FLAG_MATCH(flags
, mc
, stream
)))){
501 /* actually found a msg matching the flags */
503 (*opts
) |= NSF_FLAG_MATCH
;
509 return(MIN(start
, mn_get_total(msgmap
)));
514 /*----------------------------------------------------------------------
515 get the requested LOCAL flag bits for the given pine message number
517 Accepts: msgs - pointer to message manipulation struct
518 n - message number to get
519 f - bitmap of interesting flags
520 Returns: non-zero if flag set, 0 if not set or no elt (error?)
522 NOTE: this can be used to test system flags
525 get_lflag(MAILSTREAM
*stream
, MSGNO_S
*msgs
, long int n
, int f
)
531 rawno
= msgs
? mn_m2raw(msgs
, n
) : n
;
532 if(!stream
|| rawno
< 1L || rawno
> stream
->nmsgs
)
535 mc
= mail_elt(stream
, rawno
);
536 if(!mc
|| (pelt
= (PINELT_S
*) mc
->sparep
) == NULL
)
540 ? !(pelt
->hidden
|| pelt
->excluded
|| pelt
->selected
||
541 pelt
->colhid
|| pelt
->collapsed
|| pelt
->searched
)
542 : (((f
& MN_HIDE
) ? pelt
->hidden
: 0)
543 || ((f
& MN_EXLD
) ? pelt
->excluded
: 0)
544 || ((f
& MN_SLCT
) ? pelt
->selected
: 0)
545 || ((f
& MN_STMP
) ? pelt
->tmp
: 0)
546 || ((f
& MN_USOR
) ? pelt
->unsorted
: 0)
547 || ((f
& MN_COLL
) ? pelt
->collapsed
: 0)
548 || ((f
& MN_CHID
) ? pelt
->colhid
: 0)
549 || ((f
& MN_CHID2
) ? pelt
->colhid2
: 0)
550 || ((f
& MN_SRCH
) ? pelt
->searched
: 0)));
555 /*----------------------------------------------------------------------
556 set the requested LOCAL flag bits for the given pine message number
558 Accepts: msgs - pointer to message manipulation struct
559 n - message number to set
560 f - bitmap of interesting flags
561 v - value (on or off) flag should get
562 Returns: our index number of first
564 NOTE: this isn't to be used for setting IMAP system flags
567 set_lflag(MAILSTREAM
*stream
, MSGNO_S
*msgs
, long int n
, int f
, int v
)
571 PINETHRD_S
*thrd
, *topthrd
= NULL
;
572 PINELT_S
**peltp
, *pelt
;
574 if(n
< 1L || n
> mn_get_total(msgs
))
577 if((rawno
=mn_m2raw(msgs
, n
)) > 0L && stream
&& rawno
<= stream
->nmsgs
578 && (mc
= mail_elt(stream
, rawno
))){
579 int was_invisible
, is_invisible
;
580 int chk_thrd_cnt
= 0, thrd_was_visible
= 0, was_hidden
= 0, is_hidden
;
582 if(*(peltp
= (PINELT_S
**) &mc
->sparep
) == NULL
){
583 *peltp
= (PINELT_S
*) fs_get(sizeof(PINELT_S
));
584 memset(*peltp
, 0, sizeof(PINELT_S
));
589 was_invisible
= (pelt
->hidden
|| pelt
->colhid
) ? 1 : 0;
591 if((chk_thrd_cnt
= ((msgs
->visible_threads
>= 0L)
592 && THRD_INDX_ENABLED() && (f
& MN_HIDE
) && (pelt
->hidden
!= v
))) != 0){
593 thrd
= fetch_thread(stream
, rawno
);
594 if(thrd
&& thrd
->top
){
595 if(thrd
->top
== thrd
->rawno
)
598 topthrd
= fetch_thread(stream
, thrd
->top
);
602 thrd_was_visible
= thread_has_some_visible(stream
, topthrd
);
603 was_hidden
= pelt
->hidden
? 1 : 0;
607 if((f
& MN_HIDE
) && pelt
->hidden
!= v
){
609 msgs
->flagged_hid
+= (v
) ? 1L : -1L;
611 if(pelt
->hidden
&& THREADING() && !THRD_INDX()
612 && stream
== ps_global
->mail_stream
613 && ps_global
->thread_disp_style
== THREAD_MUTTLIKE
)
614 clear_index_cache_for_thread(stream
, fetch_thread(stream
, rawno
),
618 if((f
& MN_CHID
) && pelt
->colhid
!= v
){
620 msgs
->flagged_chid
+= (v
) ? 1L : -1L;
623 if((f
& MN_CHID2
) && pelt
->colhid2
!= v
){
625 msgs
->flagged_chid2
+= (v
) ? 1L : -1L;
628 if((f
& MN_COLL
) && pelt
->collapsed
!= v
){
630 msgs
->flagged_coll
+= (v
) ? 1L : -1L;
633 if((f
& MN_USOR
) && pelt
->unsorted
!= v
){
635 msgs
->flagged_usor
+= (v
) ? 1L : -1L;
638 if((f
& MN_EXLD
) && pelt
->excluded
!= v
){
640 msgs
->flagged_exld
+= (v
) ? 1L : -1L;
643 if((f
& MN_SLCT
) && pelt
->selected
!= v
){
645 msgs
->flagged_tmp
+= (v
) ? 1L : -1L;
648 if((f
& MN_SRCH
) && pelt
->searched
!= v
){
650 msgs
->flagged_srch
+= (v
) ? 1L : -1L;
653 if((f
& MN_STMP
) && pelt
->tmp
!= v
){
655 msgs
->flagged_stmp
+= (v
) ? 1L : -1L;
658 is_invisible
= (pelt
->hidden
|| pelt
->colhid
) ? 1 : 0;
660 if(was_invisible
!= is_invisible
)
661 msgs
->flagged_invisible
+= (v
) ? 1L : -1L;
664 * visible_threads keeps track of how many of the max_thrdno threads
665 * are visible and how many are MN_HIDE-hidden.
667 if(chk_thrd_cnt
&& topthrd
668 && (was_hidden
!= (is_hidden
= pelt
->hidden
? 1 : 0))){
669 if(!thrd_was_visible
&& !is_hidden
){
670 /* it is visible now, increase count by one */
671 msgs
->visible_threads
++;
673 else if(thrd_was_visible
&& is_hidden
){
674 /* thread may have been hidden, check */
675 if(!thread_has_some_visible(stream
, topthrd
))
676 msgs
->visible_threads
--;
687 * Copy value of flag from to flag to.
690 copy_lflags(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int from
, int to
)
695 hide
= ((to
== MN_SLCT
) && (any_lflagged(msgmap
, MN_HIDE
) > 0L));
697 set_lflags(stream
, msgmap
, to
, 0);
699 if(any_lflagged(msgmap
, from
)){
700 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++)
701 if(get_lflag(stream
, msgmap
, i
, from
))
702 set_lflag(stream
, msgmap
, i
, to
, 1);
704 set_lflag(stream
, msgmap
, i
, MN_HIDE
, 1);
710 * Set flag f to value v in all message.
713 set_lflags(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int f
, int v
)
717 if((v
== 0 && any_lflagged(msgmap
, f
)) || v
)
718 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++)
719 set_lflag(stream
, msgmap
, i
, f
, v
);
724 /*----------------------------------------------------------------------
725 return whether the given flag is set somewhere in the folder
727 Accepts: msgs - pointer to message manipulation struct
728 f - flag bitmap to act on
729 Returns: number of messages with the given flag set.
730 NOTE: the sum, if multiple flags tested, is bogus
733 any_lflagged(MSGNO_S
*msgs
, int f
)
739 return(!(msgs
->flagged_hid
|| msgs
->flagged_exld
|| msgs
->flagged_tmp
||
740 msgs
->flagged_coll
|| msgs
->flagged_chid
|| msgs
->flagged_srch
));
741 else if(f
== (MN_HIDE
| MN_CHID
))
742 return(msgs
->flagged_invisible
); /* special non-bogus case */
744 return(((f
& MN_HIDE
) ? msgs
->flagged_hid
: 0L)
745 + ((f
& MN_EXLD
) ? msgs
->flagged_exld
: 0L)
746 + ((f
& MN_SLCT
) ? msgs
->flagged_tmp
: 0L)
747 + ((f
& MN_SRCH
) ? msgs
->flagged_srch
: 0L)
748 + ((f
& MN_STMP
) ? msgs
->flagged_stmp
: 0L)
749 + ((f
& MN_COLL
) ? msgs
->flagged_coll
: 0L)
750 + ((f
& MN_USOR
) ? msgs
->flagged_usor
: 0L)
751 + ((f
& MN_CHID
) ? msgs
->flagged_chid
: 0L)
752 + ((f
& MN_CHID2
) ? msgs
->flagged_chid2
: 0L));