summaryrefslogtreecommitdiff
path: root/pith/sequence.c
diff options
context:
space:
mode:
authorEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
committerEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
commit094ca96844842928810f14844413109fc6cdd890 (patch)
treee60efbb980f38ba9308ccb4fb2b77b87bbc115f3 /pith/sequence.c
downloadalpine-094ca96844842928810f14844413109fc6cdd890.tar.xz
Initial Alpine Version
Diffstat (limited to 'pith/sequence.c')
-rw-r--r--pith/sequence.c460
1 files changed, 460 insertions, 0 deletions
diff --git a/pith/sequence.c b/pith/sequence.c
new file mode 100644
index 00000000..577041dc
--- /dev/null
+++ b/pith/sequence.c
@@ -0,0 +1,460 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: sequence.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/sequence.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/msgno.h"
+#include "../pith/flag.h"
+#include "../pith/thread.h"
+#include "../pith/icache.h"
+
+
+/*
+ * Internal prototypes
+ */
+
+
+/*----------------------------------------------------------------------
+ Build comma delimited list of selected messages
+
+ Args: stream -- mail stream to use for flag testing
+ msgmap -- message number struct of to build selected messages in
+ count -- pointer to place to write number of comma delimited
+ mark -- mark manually undeleted so we don't refilter right away
+
+ Returns: malloc'd string containing sequence, else NULL if
+ no messages in msgmap with local "selected" flag.
+ ----*/
+char *
+selected_sequence(MAILSTREAM *stream, MSGNO_S *msgmap, long int *count, int mark)
+{
+ long i;
+ MESSAGECACHE *mc;
+
+ if(!stream)
+ return(NULL);
+
+ /*
+ * The plan here is to use the c-client elt's "sequence" bit
+ * to work around any orderings or exclusions in pine's internal
+ * mapping that might cause the sequence to be artificially
+ * lengthy. It's probably cheaper to run down the elt list
+ * twice rather than call nm_raw2m() for each message as
+ * we run down the elt list once...
+ */
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->sequence = 0;
+
+ for(i = 1L; i <= mn_get_total(msgmap); i++)
+ if(get_lflag(stream, msgmap, i, MN_SLCT)){
+ long rawno;
+ int exbits = 0;
+
+ /*
+ * Forget we knew about it, and set "add to sequence"
+ * bit...
+ */
+ clear_index_cache_ent(stream, i, 0);
+ rawno = mn_m2raw(msgmap, i);
+ if(rawno > 0L && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, rawno)))
+ mc->sequence = 1;
+
+ /*
+ * Mark this message manually flagged so we don't re-filter it
+ * with a filter which only sets flags.
+ */
+ if(mark){
+ if(msgno_exceptions(stream, rawno, "0", &exbits, FALSE))
+ exbits |= MSG_EX_MANUNDEL;
+ else
+ exbits = MSG_EX_MANUNDEL;
+
+ msgno_exceptions(stream, rawno, "0", &exbits, TRUE);
+ }
+ }
+
+ return(build_sequence(stream, NULL, count));
+}
+
+
+/*----------------------------------------------------------------------
+ Build comma delimited list of messages current in msgmap which have all
+ flags matching the arguments
+
+ Args: stream -- mail stream to use for flag testing
+ msgmap -- only consider messages selected in this msgmap
+ flag -- flags to match against
+ count -- pointer to place to return number of comma delimited
+ mark -- mark index cache entry changed, and count state change
+ kw_on -- if flag contains F_KEYWORD, this is
+ the array of keywords to be checked
+ kw_off -- if flag contains F_UNKEYWORD, this is
+ the array of keywords to be checked
+
+ Returns: malloc'd string containing sequence, else NULL if
+ no messages in msgmap with local "selected" flag (a flag
+ of zero means all current msgs).
+ ----*/
+char *
+currentf_sequence(MAILSTREAM *stream, MSGNO_S *msgmap, long int flag,
+ long int *count, int mark, char **kw_on, char **kw_off)
+{
+ char *seq, **t;
+ long i, rawno;
+ int exbits;
+ MESSAGECACHE *mc;
+
+ if(!stream)
+ return(NULL);
+
+ /* First, make sure elts are valid for all the interesting messages */
+ if((seq = invalid_elt_sequence(stream, msgmap)) != NULL){
+ pine_mail_fetch_flags(stream, seq, NIL);
+ fs_give((void **) &seq);
+ }
+
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->sequence = 0; /* clear "sequence" bits */
+
+ for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap)){
+ /* if not already set, go on... */
+ rawno = mn_m2raw(msgmap, i);
+ mc = (rawno > 0L && rawno <= stream->nmsgs)
+ ? mail_elt(stream, rawno) : NULL;
+ if(!mc)
+ continue;
+
+ if((flag == 0)
+ || ((flag & F_DEL) && mc->deleted)
+ || ((flag & F_UNDEL) && !mc->deleted)
+ || ((flag & F_SEEN) && mc->seen)
+ || ((flag & F_UNSEEN) && !mc->seen)
+ || ((flag & F_ANS) && mc->answered)
+ || ((flag & F_UNANS) && !mc->answered)
+ || ((flag & F_FLAG) && mc->flagged)
+ || ((flag & F_UNFLAG) && !mc->flagged)){
+ mc->sequence = 1; /* set "sequence" flag */
+ }
+
+ /* check for forwarded status */
+ if(!mc->sequence
+ && (flag & F_FWD)
+ && user_flag_is_set(stream, rawno, FORWARDED_FLAG)){
+ mc->sequence = 1;
+ }
+
+ if(!mc->sequence
+ && (flag & F_UNFWD)
+ && !user_flag_is_set(stream, rawno, FORWARDED_FLAG)){
+ mc->sequence = 1;
+ }
+
+ /* check for user keywords or not */
+ if(!mc->sequence && flag & F_KEYWORD && kw_on){
+ for(t = kw_on; !mc->sequence && *t; t++)
+ if(user_flag_is_set(stream, rawno, *t))
+ mc->sequence = 1;
+ }
+ else if(!mc->sequence && flag & F_UNKEYWORD && kw_off){
+ for(t = kw_off; !mc->sequence && *t; t++)
+ if(!user_flag_is_set(stream, rawno, *t))
+ mc->sequence = 1;
+ }
+
+ if(mc->sequence){
+ if(mark){
+ if(THRD_INDX()){
+ PINETHRD_S *thrd;
+ long t;
+
+ /* clear thread index line instead of index index line */
+ thrd = fetch_thread(stream, mn_m2raw(msgmap, i));
+ if(thrd && thrd->top
+ && (thrd=fetch_thread(stream,thrd->top))
+ && (t = mn_raw2m(msgmap, thrd->rawno)))
+ clear_index_cache_ent(stream, t, 0);
+ }
+ else
+ clear_index_cache_ent(stream, i, 0);/* force new index line */
+
+ /*
+ * Mark this message manually flagged so we don't re-filter it
+ * with a filter which only sets flags.
+ */
+ exbits = 0;
+ if(msgno_exceptions(stream, rawno, "0", &exbits, FALSE))
+ exbits |= MSG_EX_MANUNDEL;
+ else
+ exbits = MSG_EX_MANUNDEL;
+
+ msgno_exceptions(stream, rawno, "0", &exbits, TRUE);
+ }
+ }
+ }
+
+ return(build_sequence(stream, NULL, count));
+}
+
+
+/*----------------------------------------------------------------------
+ Return sequence numbers of messages with invalid MESSAGECACHEs
+
+ Args: stream -- mail stream to use for flag testing
+ msgmap -- message number struct of to build selected messages in
+
+ Returns: malloc'd string containing sequence, else NULL if
+ no messages in msgmap with local "selected" flag (a flag
+ of zero means all current msgs).
+ ----*/
+char *
+invalid_elt_sequence(MAILSTREAM *stream, MSGNO_S *msgmap)
+{
+ long i, rawno;
+ MESSAGECACHE *mc;
+
+ if(!stream)
+ return(NULL);
+
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->sequence = 0; /* clear "sequence" bits */
+
+ for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap))
+ if((rawno = mn_m2raw(msgmap, i)) > 0L && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, rawno)) && !mc->valid)
+ mc->sequence = 1;
+
+ return(build_sequence(stream, NULL, NULL));
+}
+
+
+/*----------------------------------------------------------------------
+ Build comma delimited list of messages with elt "sequence" bit set
+
+ Args: stream -- mail stream to use for flag testing
+ msgmap -- struct containing sort to build sequence in
+ count -- pointer to place to write number of comma delimited
+ NOTE: if non-zero, it's a clue as to how many messages
+ have the sequence bit lit.
+
+ Returns: malloc'd string containing sequence, else NULL if
+ no messages in msgmap with elt's "sequence" bit set
+ ----*/
+char *
+build_sequence(MAILSTREAM *stream, MSGNO_S *msgmap, long int *count)
+{
+#define SEQ_INCREMENT 128
+ long n = 0L, i, x, lastn = 0L, runstart = 0L;
+ size_t size = SEQ_INCREMENT;
+ char *seq = NULL, *p;
+ MESSAGECACHE *mc;
+
+ if(!stream)
+ return(NULL);
+
+ if(count){
+ if(*count > 0L)
+ size = MAX(size, MIN((*count) * 4, 16384));
+
+ *count = 0L;
+ }
+
+ for(x = 1L; x <= stream->nmsgs; x++){
+ if(msgmap){
+ if((i = mn_m2raw(msgmap, x)) == 0L)
+ continue;
+ }
+ else
+ i = x;
+
+ if(i > 0L && i <= stream->nmsgs
+ && (mc = mail_elt(stream, i)) && mc->sequence){
+ n++;
+ if(!seq) /* initialize if needed */
+ seq = p = fs_get(size);
+
+ /*
+ * This code will coalesce the ascending runs of
+ * sequence numbers, but fails to break sequences
+ * into a reasonably sensible length for imapd's to
+ * swallow (reasonable addtition to c-client?)...
+ */
+ if(lastn){ /* if may be in a run */
+ if(lastn + 1L == i){ /* and its the next raw num */
+ lastn = i; /* skip writing anything... */
+ continue;
+ }
+ else if(runstart != lastn){
+ *p++ = (runstart + 1L == lastn) ? ',' : ':';
+ sstrncpy(&p, long2string(lastn), size-(p-seq));
+ } /* wrote end of run */
+ }
+
+ runstart = lastn = i; /* remember last raw num */
+
+ if(n > 1L && (p-seq) < size) /* !first num, write delim */
+ *p++ = ',';
+
+ if(size - (p - seq) < 16){ /* room for two more nums? */
+ size_t offset = p - seq; /* grow the sequence array */
+ size += SEQ_INCREMENT;
+ fs_resize((void **)&seq, size);
+ p = seq + offset;
+ }
+
+ sstrncpy(&p, long2string(i), size-(p-seq)); /* write raw number */
+ }
+ }
+
+ if(lastn && runstart != lastn){ /* were in a run? */
+ if((p-seq) < size) /* !first num, write delim */
+ *p++ = (runstart + 1L == lastn) ? ',' : ':';
+
+ sstrncpy(&p, long2string(lastn), size-(p-seq)); /* write the trailing num */
+ }
+
+ if(seq && (p-seq) < size) /* if sequence, tie it off */
+ *p = '\0';
+
+ if(seq)
+ seq[size-1] = '\0';
+
+ if(count)
+ *count = n;
+
+ return(seq);
+}
+
+
+/*----------------------------------------------------------------------
+ If any messages flagged "selected", fake the "currently selected" array
+
+ Args: map -- message number struct of to build selected messages in
+
+ OK folks, here's the tradeoff: either all the functions have to
+ know if the user want's to deal with the "current" hilited message
+ or the list of currently "selected" messages, *or* we just
+ wrap the call to these functions with some glue that tweeks
+ what these functions see as the "current" message list, and let them
+ do their thing.
+ ----*/
+int
+pseudo_selected(MAILSTREAM *stream, MSGNO_S *map)
+{
+ long i, later = 0L;
+
+ if(any_lflagged(map, MN_SLCT)){
+ map->hilited = mn_m2raw(map, mn_get_cur(map));
+
+ for(i = 1L; i <= mn_get_total(map); i++)
+ if(get_lflag(stream, map, i, MN_SLCT)){
+ if(!later++){
+ mn_set_cur(map, i);
+ }
+ else{
+ mn_add_cur(map, i);
+ }
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ Antidote for the monkey business committed above
+
+ Args: map -- message number struct of to build selected messages in
+
+ ----*/
+void
+restore_selected(MSGNO_S *map)
+{
+ if(map->hilited){
+ mn_reset_cur(map, mn_raw2m(map, map->hilited));
+ map->hilited = 0L;
+ }
+}
+
+
+/*
+ * Return a search set which can be used to limit the search to a smaller set,
+ * for performance reasons. The caller must still work correctly if we return
+ * the whole set (or NULL) here, because we may not be able to send the full
+ * search set over IMAP. In cases where the search set description is getting
+ * too large we send a search set which contains all of the relevant messages.
+ * It may contain more.
+ *
+ * Args stream
+ * narrow -- If set, we are narrowing our selection (restrict to
+ * messages with MN_SLCT already set) or if not set,
+ * we are broadening (so we may look only at messages
+ * with MN_SLCT not set)
+ *
+ * Returns - allocated search set or NULL. Caller is responsible for freeing it.
+ */
+SEARCHSET *
+limiting_searchset(MAILSTREAM *stream, int narrow)
+{
+ long n, run;
+ int cnt = 0;
+ SEARCHSET *full_set = NULL, **set;
+
+ /*
+ * If we're talking to anything other than a server older than
+ * imap 4rev1, build a searchset otherwise it'll choke.
+ */
+ if(!(is_imap_stream(stream) && !modern_imap_stream(stream))){
+ for(n = 1L, set = &full_set, run = 0L; n <= stream->nmsgs; n++)
+ /* test for end of run */
+ if(get_lflag(stream, NULL, n, MN_EXLD)
+ || (narrow && !get_lflag(stream, NULL, n, MN_SLCT))
+ || (!narrow && get_lflag(stream, NULL, n, MN_SLCT))){
+ if(run){ /* previous selected? */
+ set = &(*set)->next;
+ run = 0L;
+ }
+ }
+ else if(run++){ /* next in run */
+ (*set)->last = n;
+ }
+ else{ /* start of run */
+ *set = mail_newsearchset();
+ (*set)->first = (*set)->last = n;
+
+ /*
+ * Make this last set cover the rest of the messages.
+ * We could be fancier about this but it probably isn't
+ * worth the trouble.
+ */
+ if(++cnt > 100){
+ (*set)->last = stream->nmsgs;
+ break;
+ }
+ }
+ }
+
+ return(full_set);
+}