1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: flag.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2013-2017 Eduardo Chappa
8 * Copyright 2006-2007 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
21 Implements Pine message flag management routines
25 #include "../pith/headers.h"
26 #include "../pith/flag.h"
27 #include "../pith/pineelt.h"
28 #include "../pith/icache.h"
29 #include "../pith/mailindx.h"
30 #include "../pith/mailcmd.h"
31 #include "../pith/msgno.h"
32 #include "../pith/thread.h"
33 #include "../pith/sort.h"
34 #include "../pith/news.h"
35 #include "../pith/sequence.h"
41 void flag_search(MAILSTREAM
*, int, MsgNo
, MSGNO_S
*, long (*)(MAILSTREAM
*));
42 long flag_search_sequence(MAILSTREAM
*, MSGNO_S
*, long, int);
46 /*----------------------------------------------------------------------
47 Return sequence number based on given index that are search-worthy
52 flags -- flags for msgline_hidden
54 Result: 0 : index not search-worthy
55 -1 : index out of bounds
56 1 - stream->nmsgs : sequence number to flag search
60 flag_search_sequence(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
, int flags
)
64 return((msgno
> stream
->nmsgs
66 || (msgmap
&& !(rawno
= mn_m2raw(msgmap
, msgno
))))
67 ? -1L /* out of range! */
68 : ((get_lflag(stream
, NULL
, rawno
, MN_EXLD
)
69 || (msgmap
&& msgline_hidden(stream
, msgmap
, msgno
, flags
)))
70 ? 0L /* NOT interesting! */
76 /*----------------------------------------------------------------------
77 Perform mail_search based on flag bits
84 Result: if no set_* specified, call mail_search to light the searched
85 bit for all the messages matching the given flags. If set_start
86 specified, it is an index (possibly into set_msgmap) telling
87 us where to search for invalid flag state hence when we
88 return everything with the searched bit is interesting and
89 everything with the valid bit lit is believably valid.
93 flag_search(MAILSTREAM
*stream
, int flags
, MsgNo set_start
, MSGNO_S
*set_msgmap
,
94 long int (*ping
)(MAILSTREAM
*))
99 SEARCHSET
*full_set
= NULL
, **set
;
101 extern MAILSTREAM
*mm_search_stream
;
106 new = sp_new_mail_count(stream
);
108 /* Anything we don't already have flags for? */
111 * Use elt's sequence bit to coalesce runs in ascending
114 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
115 if((mc
= mail_elt(stream
, i
)) != NULL
)
119 (n
= flag_search_sequence(stream
, set_msgmap
, i
, MH_ANYTHD
)) >= 0L;
120 (flags
& F_SRCHBACK
) ? i
-- : i
++)
121 if(n
> 0L && n
<= stream
->nmsgs
122 && (mc
= mail_elt(stream
, n
)) && !mc
->valid
)
125 /* Unroll searchset in ascending sequence order */
127 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
128 if((mc
= mail_elt(stream
, i
)) && mc
->sequence
){
130 if(((*set
)->last
? (*set
)->last
: (*set
)->first
)+1L == i
)
137 *set
= mail_newsearchset();
143 * No search-worthy messsages?, prod the server for
144 * any flag updates and clear the searched bits...
147 if(full_set
->first
== 1
148 && full_set
->last
== stream
->nmsgs
149 && full_set
->next
== NULL
)
150 mail_free_searchset(&full_set
);
153 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
154 if((mc
= mail_elt(stream
, i
)) != NULL
)
158 (*ping
)(stream
); /* prod server for any flag updates */
160 if(!(flags
& F_NOFILT
) && new != sp_new_mail_count(stream
)){
161 process_filter_patterns(stream
, sp_msgmap(stream
),
162 sp_new_mail_count(stream
));
164 refresh_sort(stream
, sp_msgmap(stream
), SRT_NON
);
165 flag_search(stream
, flags
, set_start
, set_msgmap
, ping
);
172 if((!is_imap_stream(stream
) || modern_imap_stream(stream
))
173 && !(IS_NEWS(stream
))){
174 pgm
= mail_newsearchpgm();
200 if(flags
& (F_FWD
| F_UNFWD
)){
203 for(slpp
= (flags
& F_FWD
) ? &pgm
->keyword
: &pgm
->unkeyword
;
205 slpp
= &(*slpp
)->next
)
208 *slpp
= mail_newstringlist();
209 (*slpp
)->text
.data
= (unsigned char *) cpystr(FORWARDED_FLAG
);
210 (*slpp
)->text
.size
= (unsigned long) strlen(FORWARDED_FLAG
);
216 if(flags
& F_OR_SEEN
){
217 SEARCHPGM
*pgm2
= mail_newsearchpgm();
218 pgm2
->or = mail_newsearchor();
219 pgm2
->or->first
= pgm
;
220 pgm2
->or->second
= mail_newsearchpgm();
221 pgm2
->or->second
->seen
= 1;
225 if(flags
& F_OR_UNSEEN
){
226 SEARCHPGM
*pgm2
= mail_newsearchpgm();
227 pgm2
->or = mail_newsearchor();
228 pgm2
->or->first
= pgm
;
229 pgm2
->or->second
= mail_newsearchpgm();
230 pgm2
->or->second
->unseen
= 1;
234 if(flags
& F_OR_DEL
){
235 SEARCHPGM
*pgm2
= mail_newsearchpgm();
236 pgm2
->or = mail_newsearchor();
237 pgm2
->or->first
= pgm
;
238 pgm2
->or->second
= mail_newsearchpgm();
239 pgm2
->or->second
->deleted
= 1;
243 if(flags
& F_OR_UNDEL
){
244 SEARCHPGM
*pgm2
= mail_newsearchpgm();
245 pgm2
->or = mail_newsearchor();
246 pgm2
->or->first
= pgm
;
247 pgm2
->or->second
= mail_newsearchpgm();
248 pgm2
->or->second
->undeleted
= 1;
252 if(flags
& F_OR_FLAG
){
253 SEARCHPGM
*pgm2
= mail_newsearchpgm();
254 pgm2
->or = mail_newsearchor();
255 pgm2
->or->first
= pgm
;
256 pgm2
->or->second
= mail_newsearchpgm();
257 pgm2
->or->second
->flagged
= 1;
261 if(flags
& F_OR_UNFLAG
){
262 SEARCHPGM
*pgm2
= mail_newsearchpgm();
263 pgm2
->or = mail_newsearchor();
264 pgm2
->or->first
= pgm
;
265 pgm2
->or->second
= mail_newsearchpgm();
266 pgm2
->or->second
->unflagged
= 1;
270 if(flags
& F_OR_ANS
){
271 SEARCHPGM
*pgm2
= mail_newsearchpgm();
272 pgm2
->or = mail_newsearchor();
273 pgm2
->or->first
= pgm
;
274 pgm2
->or->second
= mail_newsearchpgm();
275 pgm2
->or->second
->answered
= 1;
279 if(flags
& F_OR_UNANS
){
280 SEARCHPGM
*pgm2
= mail_newsearchpgm();
281 pgm2
->or = mail_newsearchor();
282 pgm2
->or->first
= pgm
;
283 pgm2
->or->second
= mail_newsearchpgm();
284 pgm2
->or->second
->unanswered
= 1;
288 if(flags
& (F_OR_FWD
| F_OR_UNFWD
)){
290 SEARCHPGM
*pgm2
= mail_newsearchpgm();
291 pgm2
->or = mail_newsearchor();
292 pgm2
->or->first
= pgm
;
293 pgm2
->or->second
= mail_newsearchpgm();
295 for(slpp
= (flags
& F_OR_FWD
)
296 ? &pgm2
->or->second
->keyword
297 : &pgm2
->or->second
->unkeyword
;
299 slpp
= &(*slpp
)->next
)
302 *slpp
= mail_newstringlist();
303 (*slpp
)->text
.data
= (unsigned char *) cpystr(FORWARDED_FLAG
);
304 (*slpp
)->text
.size
= (unsigned long) strlen(FORWARDED_FLAG
);
309 if(flags
& F_OR_RECENT
){
310 SEARCHPGM
*pgm2
= mail_newsearchpgm();
311 pgm2
->or = mail_newsearchor();
312 pgm2
->or->first
= pgm
;
313 pgm2
->or->second
= mail_newsearchpgm();
314 pgm2
->or->second
->recent
= 1;
318 pgm
->msgno
= full_set
;
320 pine_mail_search_full(mm_search_stream
= stream
, NULL
,
321 pgm
, SE_NOPREFETCH
| SE_FREE
);
323 if(!(flags
& F_NOFILT
) && new != sp_new_mail_count(stream
)){
324 process_filter_patterns(stream
, sp_msgmap(stream
),
325 sp_new_mail_count(stream
));
327 flag_search(stream
, flags
, set_start
, set_msgmap
, ping
);
332 /* sequence bits of interesting msgs set */
333 mail_free_searchset(&full_set
);
336 /* light sequence bits of interesting msgs */
338 (n
= flag_search_sequence(stream
, set_msgmap
, i
, MH_ANYTHD
)) >= 0L;
340 if(n
> 0L && n
<= stream
->nmsgs
341 && (mc
= mail_elt(stream
, n
)) && !mc
->valid
)
347 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
348 if((mc
= mail_elt(stream
, i
)) != NULL
)
351 if((seq
= build_sequence(stream
, NULL
, NULL
)) != NULL
){
352 pine_mail_fetch_flags(stream
, seq
, 0L);
353 fs_give((void **) &seq
);
360 /*----------------------------------------------------------------------
361 count messages on stream with specified system flag attributes
363 Args: stream -- The stream/folder to look at message status
364 flags -- flags on folder/stream to examine
366 Result: count of messages flagged as requested
368 Task: return count of message flagged as requested while being
369 as server/network friendly as possible.
371 Strategy: run thru flags to make sure they're all valid. If any
372 invalid, do a search starting with the invalid message.
373 If all valid, ping the server to let it know we're
374 receptive to flag updates. At this
376 ----------------------------------------------------------------------*/
378 count_flagged(MAILSTREAM
*stream
, long int flags
)
386 flag_search(stream
, flags
, 1, NULL
, pine_mail_ping
);
388 /* Paw thru once more since all should be updated */
389 for(n
= 1L, count
= 0L; n
<= stream
->nmsgs
; n
++)
390 if((((mc
= mail_elt(stream
, n
)) && mc
->searched
)
391 || (mc
&& mc
->valid
&& FLAG_MATCH(flags
, mc
, stream
)))
392 && !get_lflag(stream
, NULL
, n
, MN_EXLD
)){
393 mc
->searched
= 1; /* caller may be interested! */
402 /*----------------------------------------------------------------------
403 Find the first message with the specified flags set
405 Args: flags -- Flags in messagecache to match on
406 stream -- The stream/folder to look at message status
408 Result: Message number of first message with specified flags set or the
409 number of the last message if none found.
410 ----------------------------------------------------------------------*/
412 first_sorted_flagged(long unsigned int flags
, MAILSTREAM
*stream
, long int set_start
, int opts
)
414 MsgNo i
, n
, start_with
, winner
= 0L;
419 msgmap
= sp_msgmap(stream
);
421 last
= (opts
& FSF_LAST
);
423 /* set_start only affects which search bits we light */
424 start_with
= set_start
? set_start
425 : (flags
& F_SRCHBACK
)
426 ? mn_get_total(msgmap
) : 1L;
427 flag_search(stream
, flags
, start_with
, msgmap
, NULL
);
430 (n
= flag_search_sequence(stream
, msgmap
, i
,
431 (opts
& FSF_SKIP_CHID
) ? 0 : MH_ANYTHD
)) >= 0L;
432 (flags
& F_SRCHBACK
) ? i
-- : i
++)
433 if(n
> 0L && n
<= stream
->nmsgs
434 && (((mc
= mail_elt(stream
, n
)) && mc
->searched
)
435 || (mc
&& mc
->valid
&& FLAG_MATCH(flags
, mc
, stream
)))){
441 if(winner
== 0L && flags
!= F_UNDEL
&& flags
!= F_NONE
){
443 "First_sorted_flagged didn't find a winner, look for undeleted\n"));
444 winner
= first_sorted_flagged(F_UNDEL
, stream
, 0L,
445 opts
| (mn_get_revsort(msgmap
) ? 0 : FSF_LAST
));
448 if(winner
== 0L && flags
!= F_NONE
){
450 "First_sorted_flagged didn't find an undeleted, look for visible\n"));
451 winner
= first_sorted_flagged(F_NONE
, stream
, 0L,
452 opts
| (mn_get_revsort(msgmap
) ? 0 : FSF_LAST
));
456 "First_sorted_flagged returning winner = %ld\n", winner
));
457 return(winner
? winner
458 : (mn_get_revsort(msgmap
)
459 ? 1L : mn_get_total(msgmap
)));
464 /*----------------------------------------------------------------------
465 Find the next message with specified flags set
467 Args: flags -- Flags in messagecache to match on
468 stream -- The stream/folder to look at message status
469 start -- Start looking after this message
470 opts -- These bits are both input and output. On input the bit
471 NSF_TRUST_FLAGS tells us whether we need to ping or not.
472 On input, the bit NSF_SEARCH_BACK tells us that we want to
473 know about matches <= start if we don't find any > start.
474 On output, NSF_FLAG_MATCH is set if we matched a message.
475 Returns: Message number of the matched message, if any; else the start # or
476 the max_msgno if the mailbox changed dramatically.
477 ----------------------------------------------------------------------*/
479 next_sorted_flagged(long unsigned int flags
, MAILSTREAM
*stream
, long int start
, int *opts
)
483 int rev
, fss_flags
= 0;
486 msgmap
= sp_msgmap(stream
);
489 * Search for the next thing the caller's interested in...
492 fss_flags
= (opts
&& *opts
& NSF_SKIP_CHID
) ? 0 : MH_ANYTHD
;
493 rev
= (opts
&& *opts
& NSF_SEARCH_BACK
);
494 dir
= (rev
? -1L : 1L);
496 flag_search(stream
, flags
| (rev
? F_SRCHBACK
: 0), start
+ dir
,
498 (opts
&& ((*opts
) & NSF_TRUST_FLAGS
)) ? NULL
: pine_mail_ping
);
501 (n
= flag_search_sequence(stream
, msgmap
,
502 i
, fss_flags
)) >= 0L;
504 if(n
> 0L && n
<= stream
->nmsgs
505 && (((mc
= mail_elt(stream
, n
)) && mc
->searched
)
506 || (mc
&& mc
->valid
&& FLAG_MATCH(flags
, mc
, stream
)))){
507 /* actually found a msg matching the flags */
509 (*opts
) |= NSF_FLAG_MATCH
;
515 return(MIN(start
, mn_get_total(msgmap
)));
520 /*----------------------------------------------------------------------
521 get the requested LOCAL flag bits for the given pine message number
523 Accepts: msgs - pointer to message manipulation struct
524 n - message number to get
525 f - bitmap of interesting flags
526 Returns: non-zero if flag set, 0 if not set or no elt (error?)
528 NOTE: this can be used to test system flags
531 get_lflag(MAILSTREAM
*stream
, MSGNO_S
*msgs
, long int n
, int f
)
537 rawno
= msgs
? mn_m2raw(msgs
, n
) : n
;
538 if(!stream
|| rawno
< 1L || rawno
> stream
->nmsgs
)
541 mc
= mail_elt(stream
, rawno
);
542 if(!mc
|| (pelt
= (PINELT_S
*) mc
->sparep
) == NULL
)
546 ? !(pelt
->hidden
|| pelt
->excluded
|| pelt
->selected
||
547 pelt
->colhid
|| pelt
->collapsed
|| pelt
->searched
)
548 : (((f
& MN_HIDE
) ? pelt
->hidden
: 0)
549 || ((f
& MN_EXLD
) ? pelt
->excluded
: 0)
550 || ((f
& MN_SLCT
) ? pelt
->selected
: 0)
551 || ((f
& MN_STMP
) ? pelt
->tmp
: 0)
552 || ((f
& MN_USOR
) ? pelt
->unsorted
: 0)
553 || ((f
& MN_COLL
) ? pelt
->collapsed
: 0)
554 || ((f
& MN_CHID
) ? pelt
->colhid
: 0)
555 || ((f
& MN_CHID2
) ? pelt
->colhid2
: 0)
556 || ((f
& MN_SRCH
) ? pelt
->searched
: 0)));
561 /*----------------------------------------------------------------------
562 set the requested LOCAL flag bits for the given pine message number
564 Accepts: msgs - pointer to message manipulation struct
565 n - message number to set
566 f - bitmap of interesting flags
567 v - value (on or off) flag should get
568 Returns: our index number of first
570 NOTE: this isn't to be used for setting IMAP system flags
573 set_lflag(MAILSTREAM
*stream
, MSGNO_S
*msgs
, long int n
, int f
, int v
)
577 PINETHRD_S
*thrd
, *topthrd
= NULL
;
578 PINELT_S
**peltp
, *pelt
;
580 if(n
< 1L || n
> mn_get_total(msgs
))
583 if((rawno
=mn_m2raw(msgs
, n
)) > 0L && stream
&& rawno
<= stream
->nmsgs
584 && (mc
= mail_elt(stream
, rawno
))){
585 int was_invisible
, is_invisible
;
586 int chk_thrd_cnt
= 0, thrd_was_visible
, was_hidden
, is_hidden
;
588 if(*(peltp
= (PINELT_S
**) &mc
->sparep
) == NULL
){
589 *peltp
= (PINELT_S
*) fs_get(sizeof(PINELT_S
));
590 memset(*peltp
, 0, sizeof(PINELT_S
));
595 was_invisible
= (pelt
->hidden
|| pelt
->colhid
) ? 1 : 0;
597 if((chk_thrd_cnt
= ((msgs
->visible_threads
>= 0L)
598 && THRD_INDX_ENABLED() && (f
& MN_HIDE
) && (pelt
->hidden
!= v
))) != 0){
599 thrd
= fetch_thread(stream
, rawno
);
600 if(thrd
&& thrd
->top
){
601 if(thrd
->top
== thrd
->rawno
)
604 topthrd
= fetch_thread(stream
, thrd
->top
);
608 thrd_was_visible
= thread_has_some_visible(stream
, topthrd
);
609 was_hidden
= pelt
->hidden
? 1 : 0;
613 if((f
& MN_HIDE
) && pelt
->hidden
!= v
){
615 msgs
->flagged_hid
+= (v
) ? 1L : -1L;
617 if(pelt
->hidden
&& THREADING() && !THRD_INDX()
618 && stream
== ps_global
->mail_stream
619 && ps_global
->thread_disp_style
== THREAD_MUTTLIKE
)
620 clear_index_cache_for_thread(stream
, fetch_thread(stream
, rawno
),
624 if((f
& MN_CHID
) && pelt
->colhid
!= v
){
626 msgs
->flagged_chid
+= (v
) ? 1L : -1L;
629 if((f
& MN_CHID2
) && pelt
->colhid2
!= v
){
631 msgs
->flagged_chid2
+= (v
) ? 1L : -1L;
634 if((f
& MN_COLL
) && pelt
->collapsed
!= v
){
636 msgs
->flagged_coll
+= (v
) ? 1L : -1L;
639 if((f
& MN_USOR
) && pelt
->unsorted
!= v
){
641 msgs
->flagged_usor
+= (v
) ? 1L : -1L;
644 if((f
& MN_EXLD
) && pelt
->excluded
!= v
){
646 msgs
->flagged_exld
+= (v
) ? 1L : -1L;
649 if((f
& MN_SLCT
) && pelt
->selected
!= v
){
651 msgs
->flagged_tmp
+= (v
) ? 1L : -1L;
654 if((f
& MN_SRCH
) && pelt
->searched
!= v
){
656 msgs
->flagged_srch
+= (v
) ? 1L : -1L;
659 if((f
& MN_STMP
) && pelt
->tmp
!= v
){
661 msgs
->flagged_stmp
+= (v
) ? 1L : -1L;
664 is_invisible
= (pelt
->hidden
|| pelt
->colhid
) ? 1 : 0;
666 if(was_invisible
!= is_invisible
)
667 msgs
->flagged_invisible
+= (v
) ? 1L : -1L;
670 * visible_threads keeps track of how many of the max_thrdno threads
671 * are visible and how many are MN_HIDE-hidden.
673 if(chk_thrd_cnt
&& topthrd
674 && (was_hidden
!= (is_hidden
= pelt
->hidden
? 1 : 0))){
675 if(!thrd_was_visible
&& !is_hidden
){
676 /* it is visible now, increase count by one */
677 msgs
->visible_threads
++;
679 else if(thrd_was_visible
&& is_hidden
){
680 /* thread may have been hidden, check */
681 if(!thread_has_some_visible(stream
, topthrd
))
682 msgs
->visible_threads
--;
693 * Copy value of flag from to flag to.
696 copy_lflags(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int from
, int to
)
701 hide
= ((to
== MN_SLCT
) && (any_lflagged(msgmap
, MN_HIDE
) > 0L));
703 set_lflags(stream
, msgmap
, to
, 0);
705 if(any_lflagged(msgmap
, from
)){
706 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++)
707 if(get_lflag(stream
, msgmap
, i
, from
))
708 set_lflag(stream
, msgmap
, i
, to
, 1);
710 set_lflag(stream
, msgmap
, i
, MN_HIDE
, 1);
716 * Set flag f to value v in all message.
719 set_lflags(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int f
, int v
)
723 if((v
== 0 && any_lflagged(msgmap
, f
)) || v
)
724 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++)
725 set_lflag(stream
, msgmap
, i
, f
, v
);
730 /*----------------------------------------------------------------------
731 return whether the given flag is set somewhere in the folder
733 Accepts: msgs - pointer to message manipulation struct
734 f - flag bitmap to act on
735 Returns: number of messages with the given flag set.
736 NOTE: the sum, if multiple flags tested, is bogus
739 any_lflagged(MSGNO_S
*msgs
, int f
)
745 return(!(msgs
->flagged_hid
|| msgs
->flagged_exld
|| msgs
->flagged_tmp
||
746 msgs
->flagged_coll
|| msgs
->flagged_chid
|| msgs
->flagged_srch
));
747 else if(f
== (MN_HIDE
| MN_CHID
))
748 return(msgs
->flagged_invisible
); /* special non-bogus case */
750 return(((f
& MN_HIDE
) ? msgs
->flagged_hid
: 0L)
751 + ((f
& MN_EXLD
) ? msgs
->flagged_exld
: 0L)
752 + ((f
& MN_SLCT
) ? msgs
->flagged_tmp
: 0L)
753 + ((f
& MN_SRCH
) ? msgs
->flagged_srch
: 0L)
754 + ((f
& MN_STMP
) ? msgs
->flagged_stmp
: 0L)
755 + ((f
& MN_COLL
) ? msgs
->flagged_coll
: 0L)
756 + ((f
& MN_USOR
) ? msgs
->flagged_usor
: 0L)
757 + ((f
& MN_CHID
) ? msgs
->flagged_chid
: 0L)
758 + ((f
& MN_CHID2
) ? msgs
->flagged_chid2
: 0L));