1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: sequence.c 1012 2008-03-26 00:44:22Z 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 #include "../pith/headers.h"
20 #include "../pith/sequence.h"
21 #include "../pith/state.h"
22 #include "../pith/conf.h"
23 #include "../pith/msgno.h"
24 #include "../pith/flag.h"
25 #include "../pith/thread.h"
26 #include "../pith/icache.h"
34 /*----------------------------------------------------------------------
35 Build comma delimited list of selected messages
37 Args: stream -- mail stream to use for flag testing
38 msgmap -- message number struct of to build selected messages in
39 count -- pointer to place to write number of comma delimited
40 mark -- mark manually undeleted so we don't refilter right away
42 Returns: malloc'd string containing sequence, else NULL if
43 no messages in msgmap with local "selected" flag.
46 selected_sequence(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int *count
, int mark
)
55 * The plan here is to use the c-client elt's "sequence" bit
56 * to work around any orderings or exclusions in pine's internal
57 * mapping that might cause the sequence to be artificially
58 * lengthy. It's probably cheaper to run down the elt list
59 * twice rather than call nm_raw2m() for each message as
60 * we run down the elt list once...
62 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
63 if((mc
= mail_elt(stream
, i
)) != NULL
)
66 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++)
67 if(get_lflag(stream
, msgmap
, i
, MN_SLCT
)){
72 * Forget we knew about it, and set "add to sequence"
75 clear_index_cache_ent(stream
, i
, 0);
76 rawno
= mn_m2raw(msgmap
, i
);
77 if(rawno
> 0L && rawno
<= stream
->nmsgs
78 && (mc
= mail_elt(stream
, rawno
)))
82 * Mark this message manually flagged so we don't re-filter it
83 * with a filter which only sets flags.
86 if(msgno_exceptions(stream
, rawno
, "0", &exbits
, FALSE
))
87 exbits
|= MSG_EX_MANUNDEL
;
89 exbits
= MSG_EX_MANUNDEL
;
91 msgno_exceptions(stream
, rawno
, "0", &exbits
, TRUE
);
95 return(build_sequence(stream
, NULL
, count
));
99 /*----------------------------------------------------------------------
100 Build comma delimited list of messages current in msgmap which have all
101 flags matching the arguments
103 Args: stream -- mail stream to use for flag testing
104 msgmap -- only consider messages selected in this msgmap
105 flag -- flags to match against
106 count -- pointer to place to return number of comma delimited
107 mark -- mark index cache entry changed, and count state change
108 kw_on -- if flag contains F_KEYWORD, this is
109 the array of keywords to be checked
110 kw_off -- if flag contains F_UNKEYWORD, this is
111 the array of keywords to be checked
113 Returns: malloc'd string containing sequence, else NULL if
114 no messages in msgmap with local "selected" flag (a flag
115 of zero means all current msgs).
118 currentf_sequence(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int flag
,
119 long int *count
, int mark
, char **kw_on
, char **kw_off
)
129 /* First, make sure elts are valid for all the interesting messages */
130 if((seq
= invalid_elt_sequence(stream
, msgmap
)) != NULL
){
131 pine_mail_fetch_flags(stream
, seq
, NIL
);
132 fs_give((void **) &seq
);
135 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
136 if((mc
= mail_elt(stream
, i
)) != NULL
)
137 mc
->sequence
= 0; /* clear "sequence" bits */
139 for(i
= mn_first_cur(msgmap
); i
> 0L; i
= mn_next_cur(msgmap
)){
140 /* if not already set, go on... */
141 rawno
= mn_m2raw(msgmap
, i
);
142 mc
= (rawno
> 0L && rawno
<= stream
->nmsgs
)
143 ? mail_elt(stream
, rawno
) : NULL
;
148 || ((flag
& F_DEL
) && mc
->deleted
)
149 || ((flag
& F_UNDEL
) && !mc
->deleted
)
150 || ((flag
& F_SEEN
) && mc
->seen
)
151 || ((flag
& F_UNSEEN
) && !mc
->seen
)
152 || ((flag
& F_ANS
) && mc
->answered
)
153 || ((flag
& F_UNANS
) && !mc
->answered
)
154 || ((flag
& F_FLAG
) && mc
->flagged
)
155 || ((flag
& F_UNFLAG
) && !mc
->flagged
)){
156 mc
->sequence
= 1; /* set "sequence" flag */
159 /* check for forwarded status */
162 && user_flag_is_set(stream
, rawno
, FORWARDED_FLAG
)){
168 && !user_flag_is_set(stream
, rawno
, FORWARDED_FLAG
)){
172 /* check for user keywords or not */
173 if(!mc
->sequence
&& flag
& F_KEYWORD
&& kw_on
){
174 for(t
= kw_on
; !mc
->sequence
&& *t
; t
++)
175 if(user_flag_is_set(stream
, rawno
, *t
))
178 else if(!mc
->sequence
&& flag
& F_UNKEYWORD
&& kw_off
){
179 for(t
= kw_off
; !mc
->sequence
&& *t
; t
++)
180 if(!user_flag_is_set(stream
, rawno
, *t
))
190 /* clear thread index line instead of index index line */
191 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, i
));
193 && (thrd
=fetch_thread(stream
,thrd
->top
))
194 && (t
= mn_raw2m(msgmap
, thrd
->rawno
)))
195 clear_index_cache_ent(stream
, t
, 0);
198 clear_index_cache_ent(stream
, i
, 0);/* force new index line */
201 * Mark this message manually flagged so we don't re-filter it
202 * with a filter which only sets flags.
205 if(msgno_exceptions(stream
, rawno
, "0", &exbits
, FALSE
))
206 exbits
|= MSG_EX_MANUNDEL
;
208 exbits
= MSG_EX_MANUNDEL
;
210 msgno_exceptions(stream
, rawno
, "0", &exbits
, TRUE
);
215 return(build_sequence(stream
, NULL
, count
));
219 /*----------------------------------------------------------------------
220 Return sequence numbers of messages with invalid MESSAGECACHEs
222 Args: stream -- mail stream to use for flag testing
223 msgmap -- message number struct of to build selected messages in
225 Returns: malloc'd string containing sequence, else NULL if
226 no messages in msgmap with local "selected" flag (a flag
227 of zero means all current msgs).
230 invalid_elt_sequence(MAILSTREAM
*stream
, MSGNO_S
*msgmap
)
238 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
239 if((mc
= mail_elt(stream
, i
)) != NULL
)
240 mc
->sequence
= 0; /* clear "sequence" bits */
242 for(i
= mn_first_cur(msgmap
); i
> 0L; i
= mn_next_cur(msgmap
))
243 if((rawno
= mn_m2raw(msgmap
, i
)) > 0L && rawno
<= stream
->nmsgs
244 && (mc
= mail_elt(stream
, rawno
)) && !mc
->valid
)
247 return(build_sequence(stream
, NULL
, NULL
));
251 /*----------------------------------------------------------------------
252 Build comma delimited list of messages with elt "sequence" bit set
254 Args: stream -- mail stream to use for flag testing
255 msgmap -- struct containing sort to build sequence in
256 count -- pointer to place to write number of comma delimited
257 NOTE: if non-zero, it's a clue as to how many messages
258 have the sequence bit lit.
260 Returns: malloc'd string containing sequence, else NULL if
261 no messages in msgmap with elt's "sequence" bit set
264 build_sequence(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int *count
)
266 #define SEQ_INCREMENT 128
267 long n
= 0L, i
, x
, lastn
= 0L, runstart
= 0L;
268 size_t size
= SEQ_INCREMENT
;
269 char *seq
= NULL
, *p
;
277 size
= MAX(size
, MIN((*count
) * 4, 16384));
282 for(x
= 1L; x
<= stream
->nmsgs
; x
++){
284 if((i
= mn_m2raw(msgmap
, x
)) == 0L)
290 if(i
> 0L && i
<= stream
->nmsgs
291 && (mc
= mail_elt(stream
, i
)) && mc
->sequence
){
293 if(!seq
) /* initialize if needed */
294 seq
= p
= fs_get(size
);
297 * This code will coalesce the ascending runs of
298 * sequence numbers, but fails to break sequences
299 * into a reasonably sensible length for imapd's to
300 * swallow (reasonable addtition to c-client?)...
302 if(lastn
){ /* if may be in a run */
303 if(lastn
+ 1L == i
){ /* and its the next raw num */
304 lastn
= i
; /* skip writing anything... */
307 else if(runstart
!= lastn
){
308 *p
++ = (runstart
+ 1L == lastn
) ? ',' : ':';
309 sstrncpy(&p
, long2string(lastn
), size
-(p
-seq
));
310 } /* wrote end of run */
313 runstart
= lastn
= i
; /* remember last raw num */
315 if(n
> 1L && (p
-seq
) < size
) /* !first num, write delim */
318 if(size
- (p
- seq
) < 16){ /* room for two more nums? */
319 size_t offset
= p
- seq
; /* grow the sequence array */
320 size
+= SEQ_INCREMENT
;
321 fs_resize((void **)&seq
, size
);
325 sstrncpy(&p
, long2string(i
), size
-(p
-seq
)); /* write raw number */
329 if(lastn
&& runstart
!= lastn
){ /* were in a run? */
330 if((p
-seq
) < size
) /* !first num, write delim */
331 *p
++ = (runstart
+ 1L == lastn
) ? ',' : ':';
333 sstrncpy(&p
, long2string(lastn
), size
-(p
-seq
)); /* write the trailing num */
336 if(seq
&& (p
-seq
) < size
) /* if sequence, tie it off */
349 /*----------------------------------------------------------------------
350 If any messages flagged "selected", fake the "currently selected" array
352 Args: map -- message number struct of to build selected messages in
354 OK folks, here's the tradeoff: either all the functions have to
355 know if the user want's to deal with the "current" hilited message
356 or the list of currently "selected" messages, *or* we just
357 wrap the call to these functions with some glue that tweeks
358 what these functions see as the "current" message list, and let them
362 pseudo_selected(MAILSTREAM
*stream
, MSGNO_S
*map
)
366 if(any_lflagged(map
, MN_SLCT
)){
367 map
->hilited
= mn_m2raw(map
, mn_get_cur(map
));
369 for(i
= 1L; i
<= mn_get_total(map
); i
++)
370 if(get_lflag(stream
, map
, i
, MN_SLCT
)){
386 /*----------------------------------------------------------------------
387 Antidote for the monkey business committed above
389 Args: map -- message number struct of to build selected messages in
393 restore_selected(MSGNO_S
*map
)
396 mn_reset_cur(map
, mn_raw2m(map
, map
->hilited
));
403 * Return a search set which can be used to limit the search to a smaller set,
404 * for performance reasons. The caller must still work correctly if we return
405 * the whole set (or NULL) here, because we may not be able to send the full
406 * search set over IMAP. In cases where the search set description is getting
407 * too large we send a search set which contains all of the relevant messages.
408 * It may contain more.
411 * narrow -- If set, we are narrowing our selection (restrict to
412 * messages with MN_SLCT already set) or if not set,
413 * we are broadening (so we may look only at messages
414 * with MN_SLCT not set)
416 * Returns - allocated search set or NULL. Caller is responsible for freeing it.
419 limiting_searchset(MAILSTREAM
*stream
, int narrow
)
423 SEARCHSET
*full_set
= NULL
, **set
;
426 * If we're talking to anything other than a server older than
427 * imap 4rev1, build a searchset otherwise it'll choke.
429 if(!(is_imap_stream(stream
) && !modern_imap_stream(stream
))){
430 for(n
= 1L, set
= &full_set
, run
= 0L; n
<= stream
->nmsgs
; n
++)
431 /* test for end of run */
432 if(get_lflag(stream
, NULL
, n
, MN_EXLD
)
433 || (narrow
&& !get_lflag(stream
, NULL
, n
, MN_SLCT
))
434 || (!narrow
&& get_lflag(stream
, NULL
, n
, MN_SLCT
))){
435 if(run
){ /* previous selected? */
440 else if(run
++){ /* next in run */
443 else{ /* start of run */
444 *set
= mail_newsearchset();
445 (*set
)->first
= (*set
)->last
= n
;
448 * Make this last set cover the rest of the messages.
449 * We could be fancier about this but it probably isn't
453 (*set
)->last
= stream
->nmsgs
;