1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: folder.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2016 Eduardo Chappa
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 "../c-client/utf8aux.h"
21 #include "../pith/folder.h"
22 #include "../pith/state.h"
23 #include "../pith/context.h"
24 #include "../pith/init.h"
25 #include "../pith/conf.h"
26 #include "../pith/stream.h"
27 #include "../pith/imap.h"
28 #include "../pith/util.h"
29 #include "../pith/flag.h"
30 #include "../pith/status.h"
31 #include "../pith/busy.h"
32 #include "../pith/mailindx.h"
35 typedef struct _build_folder_list_data
{
36 long mask
; /* bitmap of responses to ignore */
50 void mail_list_exists(MAILSTREAM
*, char *, int, long, void *, unsigned);
51 void init_incoming_folder_list(struct pine
*, CONTEXT_S
*);
52 void mail_list_filter(MAILSTREAM
*, char *, int, long, void *, unsigned);
53 void mail_lsub_filter(MAILSTREAM
*, char *, int, long, void *, unsigned);
54 int mail_list_in_collection(char **, char *, char *, char *);
55 char *folder_last_cmpnt(char *, int);
56 void free_folder_entries(FLIST
**);
57 int folder_insert_sorted(int, int, int, FOLDER_S
*, FLIST
*,
58 int (*)(FOLDER_S
*, FOLDER_S
*));
59 void folder_insert_index(FOLDER_S
*, int, FLIST
*);
60 void resort_folder_list(FLIST
*flist
);
61 int compare_folders_alpha_qsort(const qsort_t
*a1
, const qsort_t
*a2
);
62 int compare_folders_dir_alpha_qsort(const qsort_t
*a1
, const qsort_t
*a2
);
63 int compare_folders_alpha_dir_qsort(const qsort_t
*a1
, const qsort_t
*a2
);
64 int compare_names(const qsort_t
*, const qsort_t
*);
65 void init_incoming_unseen_data(struct pine
*, FOLDER_S
*f
);
69 folder_lister_desc(CONTEXT_S
*cntxt
, FDIR_S
*fdp
)
74 q
= ((p
= strstr(cntxt
->context
, "%s")) && !*(p
+2)
75 && !strncmp(fdp
->ref
, cntxt
->context
, p
- cntxt
->context
))
76 ? fdp
->ref
+ (p
- cntxt
->context
) : fdp
->ref
;
77 fname
= folder_name_decoded((unsigned char *) q
);
78 /* Provide context in new collection header */
79 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Dir: %s", fname
? (char *) fname
: q
);
80 if(fname
) fs_give((void **)&fname
);
82 return(cpystr(tmp_20k_buf
));
87 reset_context_folders(CONTEXT_S
*cntxt
)
91 for(tc
= cntxt
; tc
&& tc
->prev
; tc
= tc
->prev
)
92 ; /* start at beginning */
94 for( ; tc
; tc
= tc
->next
){
98 FDIR_S
*tp
= tc
->dir
->prev
;
99 free_fdir(&tc
->dir
, 0);
107 * next_folder_dir - return a directory structure with the folders it
111 next_folder_dir(CONTEXT_S
*context
, char *new_dir
, int build_list
, MAILSTREAM
**streamp
)
113 char tmp
[MAILTMPLEN
], dir
[3];
116 fp
= (FDIR_S
*) fs_get(sizeof(FDIR_S
));
117 memset(fp
, 0, sizeof(FDIR_S
));
118 (void) context_apply(tmp
, context
, new_dir
, MAILTMPLEN
);
119 dir
[0] = context
->dir
->delim
;
121 strncat(tmp
, dir
, sizeof(tmp
)-1-strlen(tmp
));
122 fp
->ref
= cpystr(tmp
);
123 fp
->delim
= context
->dir
->delim
;
124 fp
->view
.internal
= cpystr(NEWS_TEST(context
) ? "*" : "%");
125 fp
->folders
= init_folder_entries();
126 fp
->status
= CNTXT_NOFIND
;
127 tmp_fp
= context
->dir
; /* temporarily rebind */
131 build_folder_list(streamp
, context
, NULL
, NULL
,
132 NEWS_TEST(context
) ? BFL_LSUB
: BFL_NONE
);
134 context
->dir
= tmp_fp
;
140 * Return which pinerc incoming folder #index is in.
143 config_containing_inc_fldr(FOLDER_S
*folder
)
146 int i
, keep_going
= 1, inheriting
= 0;
147 struct variable
*v
= &ps_global
->vars
[V_INCOMING_FOLDERS
];
152 /* is it in exceptions config? */
153 if(v
->post_user_val
.l
){
154 for(i
= 0, t
=v
->post_user_val
.l
; t
[i
]; i
++){
155 if(expand_variables(tmp_20k_buf
, SIZEOF_20KBUF
, t
[i
], 0)){
158 if(!strcmp(tmp_20k_buf
, INHERIT
))
160 else if(folder
->varhash
== line_hash(tmp_20k_buf
))
169 /* is it in main config? */
170 if(keep_going
&& v
->main_user_val
.l
){
171 for(i
= 0, t
=v
->main_user_val
.l
; t
[i
]; i
++){
172 if(expand_variables(tmp_20k_buf
, SIZEOF_20KBUF
, t
[i
], 0) &&
173 folder
->varhash
== line_hash(tmp_20k_buf
))
182 /*----------------------------------------------------------------------
183 Format the given folder name for display for the user
185 Args: folder -- The folder name to fix up
187 Not sure this always makes it prettier. It could do nice truncation if we
188 passed in a length. Right now it adds the path name of the mail
189 subdirectory if appropriate.
193 pretty_fn(char *folder
)
195 if(!strucmp(folder
, ps_global
->inbox_name
))
196 return(ps_global
->inbox_name
);
202 /*----------------------------------------------------------------------
203 Return the path delimiter for the given folder on the given server
205 Args: folder -- folder type for delimiter
209 get_folder_delimiter(char *folder
)
215 memset(mm_list_info
= &ldata
, 0, sizeof(MM_LIST_S
));
216 ldata
.filter
= mail_list_response
;
217 memset(ldata
.data
= &response
, 0, sizeof(LISTRES_S
));
220 && !(ldata
.stream
= sp_stream_get(folder
, SP_MATCH
))
221 && !(ldata
.stream
= sp_stream_get(folder
, SP_SAME
))){
222 if((ldata
.stream
= pine_mail_open(NULL
,folder
,
223 OP_HALFOPEN
|OP_SILENT
|SP_USEPOOL
|SP_TEMPUSE
,
232 pine_mail_list(ldata
.stream
, folder
, "", NULL
);
235 pine_mail_close(ldata
.stream
);
237 return(response
.delim
);
241 /*----------------------------------------------------------------------
242 Check to see if folder exists in given context
244 Args: cntxt -- context inwhich to interpret "file" arg
245 file -- name of folder to check
247 Result: returns FEX_ISFILE if the folder exists and is a folder
248 FEX_ISDIR if the folder exists and is a directory
249 FEX_NOENT if it doesn't exist
252 The two existence return values above may be logically OR'd
254 Uses mail_list to sniff out the existence of the requested folder.
255 The context string is just here for convenience. Checking for
256 folder's existence within a given context is probably more efficiently
257 handled outside this function for now using build_folder_list().
261 folder_exists(CONTEXT_S
*cntxt
, char *file
)
263 return(folder_name_exists(cntxt
, file
, NULL
));
267 /*----------------------------------------------------------------------
268 Check to see if folder exists in given context
270 Args: cntxt -- context in which to interpret "file" arg
271 file -- name of folder to check
272 name -- name of folder folder with context applied
274 Result: returns FEX_ISFILE if the folder exists and is a folder
275 FEX_ISDIR if the folder exists and is a directory
276 FEX_NOENT if it doesn't exist
279 The two existence return values above may be logically OR'd
281 Uses mail_list to sniff out the existence of the requested folder.
282 The context string is just here for convenience. Checking for
283 folder's existence within a given context is probably more efficiently
284 handled outside this function for now using build_folder_list().
288 folder_name_exists(CONTEXT_S
*cntxt
, char *file
, char **fullpath
)
292 int we_cancel
= 0, res
;
293 char *p
, reference
[MAILTMPLEN
], tmp
[MAILTMPLEN
], *tfolder
= NULL
;
296 * No folder means "inbox".
298 if(*file
== '{' && (p
= strchr(file
, '}')) && (!*(p
+1))){
301 l
= strlen(file
)+strlen("inbox");
302 tfolder
= (char *) fs_get((l
+1) * sizeof(char));
303 snprintf(tfolder
, l
+1, "%s%s", file
, "inbox");
308 mm_list_info
= &ldata
; /* tie down global reference */
309 memset(&ldata
, 0, sizeof(ldata
));
310 ldata
.filter
= mail_list_exists
;
312 ldata
.stream
= sp_stream_get(context_apply(tmp
, cntxt
, file
, sizeof(tmp
)),
315 memset(ldata
.data
= &parms
, 0, sizeof(EXISTDATA_S
));
318 * If no preset reference string, must be at top of context
320 if(cntxt
&& context_isambig(file
)){
321 /* inbox in first context is the real inbox */
322 if(ps_global
->context_list
== cntxt
&& !strucmp(file
, ps_global
->inbox_name
)){
324 parms
.args
.reference
= reference
;
326 else if(!(parms
.args
.reference
= cntxt
->dir
->ref
)){
329 if((p
= strstr(cntxt
->context
, "%s")) != NULL
){
330 strncpy(parms
.args
.reference
= reference
,
332 MIN(p
- cntxt
->context
, sizeof(reference
)-1));
333 reference
[MIN(p
- cntxt
->context
, sizeof(reference
)-1)] = '\0';
338 parms
.args
.reference
= cntxt
->context
;
341 parms
.fullname
= fullpath
;
344 ps_global
->mm_log_error
= 0;
345 ps_global
->noshow_error
= 1;
347 we_cancel
= busy_cue(NULL
, NULL
, 1);
349 parms
.args
.name
= file
;
351 res
= pine_mail_list(ldata
.stream
, parms
.args
.reference
, parms
.args
.name
,
357 ps_global
->noshow_error
= 0;
359 if(cntxt
&& cntxt
->dir
&& parms
.response
.delim
)
360 cntxt
->dir
->delim
= parms
.response
.delim
;
363 fs_give((void **)&tfolder
);
364 return(((res
== FALSE
) || ps_global
->mm_log_error
)
366 : (((parms
.response
.isfile
)
368 | ((parms
.response
.isdir
)
370 | ((parms
.response
.ismarked
)
372 | ((parms
.response
.unmarked
)
373 ? FEX_UNMARKED
: 0)));
378 mail_list_exists(MAILSTREAM
*stream
, char *mailbox
, int delim
, long int attribs
,
379 void *data
, unsigned int options
)
382 ((EXISTDATA_S
*) data
)->response
.delim
= delim
;
384 if(mailbox
&& *mailbox
){
385 if(!(attribs
& LATT_NOSELECT
)){
386 ((EXISTDATA_S
*) data
)->response
.isfile
= 1;
387 ((EXISTDATA_S
*) data
)->response
.count
+= 1;
390 if(!(attribs
& LATT_NOINFERIORS
)){
391 ((EXISTDATA_S
*) data
)->response
.isdir
= 1;
392 ((EXISTDATA_S
*) data
)->response
.count
+= 1;
395 if(attribs
& LATT_MARKED
)
396 ((EXISTDATA_S
*) data
)->response
.ismarked
= 1;
398 /* don't mark #move folders unmarked */
399 if(attribs
& LATT_UNMARKED
&& !(options
& PML_IS_MOVE_MBOX
))
400 ((EXISTDATA_S
*) data
)->response
.unmarked
= 1;
402 if(attribs
& LATT_HASCHILDREN
)
403 ((EXISTDATA_S
*) data
)->response
.haschildren
= 1;
405 if(attribs
& LATT_HASNOCHILDREN
)
406 ((EXISTDATA_S
*) data
)->response
.hasnochildren
= 1;
408 if(((EXISTDATA_S
*) data
)->fullname
409 && ((((EXISTDATA_S
*) data
)->args
.reference
[0] != '\0')
410 ? struncmp(((EXISTDATA_S
*) data
)->args
.reference
, mailbox
,
411 strlen(((EXISTDATA_S
*)data
)->args
.reference
))
412 : struncmp(((EXISTDATA_S
*) data
)->args
.name
, mailbox
,
413 strlen(((EXISTDATA_S
*) data
)->args
.name
)))){
416 size_t len
= (((stream
&& stream
->mailbox
)
417 ? strlen(stream
->mailbox
) : 0)
418 + strlen(((EXISTDATA_S
*) data
)->args
.reference
)
419 + strlen(((EXISTDATA_S
*) data
)->args
.name
)
420 + strlen(mailbox
)) * sizeof(char);
423 * Fully qualify (in the c-client name structure sense)
424 * anything that's not in the context of the "reference"...
426 if(*((EXISTDATA_S
*) data
)->fullname
)
427 fs_give((void **) ((EXISTDATA_S
*) data
)->fullname
);
430 *((EXISTDATA_S
*) data
)->fullname
= (char *) fs_get(alloclen
);
432 && stream
&& stream
->mailbox
&& *stream
->mailbox
== '{'
433 && (p
= strindex(stream
->mailbox
, '}'))){
434 len
= (p
- stream
->mailbox
) + 1;
435 strncpy(*((EXISTDATA_S
*) data
)->fullname
,
436 stream
->mailbox
, MIN(len
,alloclen
));
437 p
= *((EXISTDATA_S
*) data
)->fullname
+ len
;
440 p
= *((EXISTDATA_S
*) data
)->fullname
;
442 strncpy(p
, mailbox
, alloclen
-(p
-(*((EXISTDATA_S
*) data
)->fullname
)));
443 (*((EXISTDATA_S
*) data
)->fullname
)[alloclen
-1] = '\0';;
450 mail_list_response(MAILSTREAM
*stream
, char *mailbox
, int delim
, long int attribs
,
451 void *data
, unsigned int options
)
456 ((LISTRES_S
*) data
)->delim
= delim
;
458 if(mailbox
&& *mailbox
){
459 if(!(attribs
& LATT_NOSELECT
)){
461 ((LISTRES_S
*) data
)->isfile
= 1;
462 ((LISTRES_S
*) data
)->count
+= 1;
465 if(!(attribs
& LATT_NOINFERIORS
)){
466 ((LISTRES_S
*) data
)->isdir
= 1;
469 ((LISTRES_S
*) data
)->count
+= 1;
472 if(attribs
& LATT_HASCHILDREN
)
473 ((LISTRES_S
*) data
)->haschildren
= 1;
475 if(attribs
& LATT_HASNOCHILDREN
)
476 ((LISTRES_S
*) data
)->hasnochildren
= 1;
482 folder_as_breakout(CONTEXT_S
*cntxt
, char *name
)
484 if(context_isambig(name
)){ /* if simple check doesn't pan out */
485 char tmp
[2*MAILTMPLEN
], *p
, *f
; /* look harder */
487 if(!cntxt
->dir
->delim
){
488 (void) context_apply(tmp
, cntxt
, "", sizeof(tmp
)/2);
489 cntxt
->dir
->delim
= get_folder_delimiter(tmp
);
492 if((p
= strindex(name
, cntxt
->dir
->delim
)) != NULL
){
493 if(p
== name
){ /* assumption 6,321: delim is root */
494 if(cntxt
->context
[0] == '{'
495 && (p
= strindex(cntxt
->context
, '}'))){
496 strncpy(tmp
, cntxt
->context
,
497 MIN((p
- cntxt
->context
) + 1, sizeof(tmp
)/2));
498 tmp
[MIN((p
- cntxt
->context
) + 1, sizeof(tmp
)/2)] = '\0';
499 strncpy(&tmp
[MIN((p
- cntxt
->context
) + 1, sizeof(tmp
)/2)],
500 name
, sizeof(tmp
)/2-strlen(tmp
));
501 tmp
[sizeof(tmp
)-1] = '\0';
505 return(cpystr(name
));
507 else{ /* assumption 6,322: no create ~foo */
508 strncpy(tmp
, name
, MIN(p
- name
, MAILTMPLEN
));
509 /* lop off trailingpath */
510 tmp
[MIN(p
- name
, sizeof(tmp
)/2)] = '\0';
512 (void)folder_name_exists(cntxt
, tmp
, &f
);
514 snprintf(tmp
, sizeof(tmp
), "%s%s",f
,p
);
515 tmp
[sizeof(tmp
)-1] = '\0';
516 fs_give((void **) &f
);
527 /*----------------------------------------------------------------------
528 Initialize global list of contexts for folder collections.
530 Interprets collections defined in the pinerc and orders them for
531 pine's use. Parses user-provided context labels and sets appropriate
532 use flags and the default prototype for that collection.
533 (See find_folders for how the actual folder list is found).
537 init_folders(struct pine
*ps
)
539 CONTEXT_S
*tc
, *top
= NULL
, **clist
;
545 * If no incoming folders are config'd, but the user asked for
546 * them via feature, make sure at least "inbox" ends up there...
548 if(F_ON(F_ENABLE_INCOMING
, ps
) && !ps
->VAR_INCOMING_FOLDERS
){
549 ps
->VAR_INCOMING_FOLDERS
= (char **)fs_get(2 * sizeof(char *));
550 ps
->VAR_INCOMING_FOLDERS
[0] = cpystr(ps
->inbox_name
);
551 ps
->VAR_INCOMING_FOLDERS
[1] = NULL
;
555 * Build context that's a list of folders the user's defined
556 * as receiveing new messages. At some point, this should
557 * probably include adding a prefix with the new message count.
558 * fake new context...
560 if(ps
->VAR_INCOMING_FOLDERS
&& ps
->VAR_INCOMING_FOLDERS
[0]
561 && (tc
= new_context("Incoming-Folders []", NULL
))){
562 tc
->dir
->status
&= ~CNTXT_NOFIND
;
563 tc
->use
|= CNTXT_INCMNG
; /* mark this as incoming collection */
565 fs_give((void **) &tc
->label
);
567 /* TRANSLATORS: a label */
568 tc
->label
= cpystr(_("Incoming Message Folders"));
573 init_incoming_folder_list(ps
, tc
);
577 * Build list of folder collections. Because of the way init.c
578 * works, we're guaranteed at least a default. Also write any
579 * "bogus format" messages...
581 for(i
= 0; ps
->VAR_FOLDER_SPEC
&& ps
->VAR_FOLDER_SPEC
[i
] ; i
++)
582 if((tc
= new_context(ps
->VAR_FOLDER_SPEC
[i
], &prime
)) != NULL
){
583 *clist
= tc
; /* add it to list */
584 clist
= &tc
->next
; /* prepare for next */
585 tc
->var
.v
= &ps
->vars
[V_FOLDER_SPEC
];
591 * Whoah cowboy!!! Guess we couldn't find a valid folder
595 alpine_panic(_("No folder collections defined"));
598 * At this point, insert the INBOX mapping as the leading
599 * folder entry of the first collection...
601 init_inbox_mapping(ps
->VAR_INBOX_PATH
, top
);
603 set_news_spec_current_val(TRUE
, TRUE
);
606 * If news groups, loop thru list adding to collection list
608 for(i
= 0; ps
->VAR_NEWS_SPEC
&& ps
->VAR_NEWS_SPEC
[i
] ; i
++)
609 if(ps
->VAR_NEWS_SPEC
[i
][0]
610 && (tc
= new_context(ps
->VAR_NEWS_SPEC
[i
], NULL
))){
611 *clist
= tc
; /* add it to list */
612 clist
= &tc
->next
; /* prepare for next */
613 tc
->var
.v
= &ps
->vars
[V_NEWS_SPEC
];
617 ps
->context_list
= top
; /* init pointers */
618 ps
->context_current
= (top
->use
& CNTXT_INCMNG
) ? top
->next
: top
;
619 ps
->context_last
= NULL
;
620 /* Tie up all the previous pointers */
621 for(; top
; top
= top
->next
)
623 top
->next
->prev
= top
;
632 * Add incoming list of folders to context.
635 init_incoming_folder_list(struct pine
*ps
, CONTEXT_S
*cntxt
)
638 char *folder_string
, *nickname
;
641 for(i
= 0; ps
->VAR_INCOMING_FOLDERS
[i
] ; i
++){
643 * Parse folder line for nickname and folder name.
644 * No nickname on line is OK.
646 get_pair(ps
->VAR_INCOMING_FOLDERS
[i
], &nickname
, &folder_string
,0,0);
649 * Allow for inbox to be specified in the incoming list, but
650 * don't let it show up along side the one magically inserted
653 if(!folder_string
|| !strucmp(ps
->inbox_name
, folder_string
)){
655 fs_give((void **)&folder_string
);
658 fs_give((void **)&nickname
);
662 else if(update_bboard_spec(folder_string
, tmp_20k_buf
, SIZEOF_20KBUF
)){
663 fs_give((void **) &folder_string
);
664 folder_string
= cpystr(tmp_20k_buf
);
667 f
= new_folder(folder_string
,
668 line_hash(ps
->VAR_INCOMING_FOLDERS
[i
]));
670 fs_give((void **)&folder_string
);
673 if(strucmp(ps
->inbox_name
, nickname
)){
674 f
->nickname
= nickname
;
675 f
->name_len
= strlen(f
->nickname
);
678 fs_give((void **)&nickname
);
681 init_incoming_unseen_data(ps
, f
);
683 folder_insert(f
->nickname
684 && (strucmp(f
->nickname
, ps
->inbox_name
) == 0)
685 ? -1 : folder_total(FOLDERS(cntxt
)),
692 init_incoming_unseen_data(struct pine
*ps
, FOLDER_S
*f
)
694 int j
, check_this
= 0;
699 /* see if this folder is in the monitoring list */
700 if(F_ON(F_ENABLE_INCOMING_CHECKING
, ps
)){
701 if(!ps
->VAR_INCCHECKLIST
)
702 check_this
++; /* everything in by default */
704 for(j
= 0; !check_this
&& ps
->VAR_INCCHECKLIST
[j
]; j
++){
705 if((f
->nickname
&& !strucmp(ps
->VAR_INCCHECKLIST
[j
],f
->nickname
))
706 || (f
->name
&& !strucmp(ps
->VAR_INCCHECKLIST
[j
],f
->name
)))
713 f
->last_unseen_update
= LUU_INIT
;
715 f
->last_unseen_update
= LUU_NEVERCHK
;
720 reinit_incoming_folder_list(struct pine
*ps
, CONTEXT_S
*context
)
722 free_folder_entries(&(FOLDERS(context
)));
723 FOLDERS(context
) = init_folder_entries();
724 init_incoming_folder_list(ps_global
, context
);
725 init_inbox_mapping(ps_global
->VAR_INBOX_PATH
, context
);
733 init_inbox_mapping(char *path
, CONTEXT_S
*cntxt
)
739 * If mapping already exists, blast it and replace it below...
741 if((f
= folder_entry(0, FOLDERS(cntxt
)))
742 && f
->nickname
&& !strcmp(f
->nickname
, ps_global
->inbox_name
))
743 folder_delete(0, FOLDERS(cntxt
));
746 f
= new_folder(path
, 0);
747 f
->nickname
= cpystr(ps_global
->inbox_name
);
748 f
->name_len
= strlen(f
->nickname
);
751 f
= new_folder(ps_global
->inbox_name
, 0);
755 /* see if this folder is in the monitoring list */
757 if(F_ON(F_ENABLE_INCOMING_CHECKING
, ps_global
) && ps_global
->VAR_INCOMING_FOLDERS
&& ps_global
->VAR_INCOMING_FOLDERS
[0]){
758 if(!ps_global
->VAR_INCCHECKLIST
)
759 check_this
++; /* everything in by default */
761 for(j
= 0; !check_this
&& ps_global
->VAR_INCCHECKLIST
[j
]; j
++){
762 if((f
->nickname
&& !strucmp(ps_global
->VAR_INCCHECKLIST
[j
],f
->nickname
))
763 || (f
->name
&& !strucmp(ps_global
->VAR_INCCHECKLIST
[j
],f
->name
)))
770 f
->last_unseen_update
= LUU_INIT
;
772 f
->last_unseen_update
= LUU_NEVERCHK
;
774 folder_insert(0, f
, FOLDERS(cntxt
));
779 new_fdir(char *ref
, char *view
, int wildcard
)
781 FDIR_S
*rv
= (FDIR_S
*) fs_get(sizeof(FDIR_S
));
783 memset((void *) rv
, 0, sizeof(FDIR_S
));
785 /* Monkey with the view to make sure it has wildcard? */
787 rv
->view
.user
= cpystr(view
);
790 * This is sorta hairy since, for simplicity we allow
791 * users to use '*' in the view, but for mail
792 * we really mean '%' as def'd in 2060...
794 if(wildcard
== '*'){ /* must be #news. */
795 if(strindex(view
, wildcard
))
796 rv
->view
.internal
= cpystr(view
);
798 else{ /* must be mail */
801 if((p
= strpbrk(view
, "*%")) != NULL
){
802 rv
->view
.internal
= p
= cpystr(view
);
803 while((p
= strpbrk(p
, "*%")) != NULL
)
804 *p
++ = '%'; /* convert everything to '%' */
808 if(!rv
->view
.internal
){
812 rv
->view
.internal
= (char *) fs_get((l
+1) * sizeof(char));
813 snprintf(rv
->view
.internal
, l
+1, "%c%s%c", wildcard
, view
, wildcard
);
817 rv
->view
.internal
= (char *) fs_get(2 * sizeof(char));
818 snprintf(rv
->view
.internal
, 2, "%c", wildcard
);
824 rv
->folders
= init_folder_entries();
825 rv
->status
= CNTXT_NOFIND
;
831 free_fdir(FDIR_S
**f
, int recur
)
834 if((*f
)->prev
&& recur
)
835 free_fdir(&(*f
)->prev
, 1);
838 fs_give((void **)&(*f
)->ref
);
841 fs_give((void **)&(*f
)->view
.user
);
843 if((*f
)->view
.internal
)
844 fs_give((void **)&(*f
)->view
.internal
);
847 fs_give((void **)&(*f
)->desc
);
849 free_folder_entries(&(*f
)->folders
);
850 fs_give((void **) f
);
856 update_bboard_spec(char *bboard
, char *buf
, size_t buflen
)
858 char *p
= NULL
, *origbuf
;
859 int nntp
= 0, bracket
= 0;
864 || (*bboard
== '{' && (p
= strindex(bboard
, '}')) && *(p
+1) == '*')){
866 if(p
|| (*(bboard
+1) == '{' && (p
= strindex(++bboard
, '}'))))
867 while(bboard
<= p
&& (buf
-origbuf
< buflen
)) /* copy it */
868 if((*buf
++ = *bboard
++) == '/' && !strncmp(bboard
, "nntp", 4))
876 * See if path portion looks newsgroup-ish while being aware
877 * of the "view" portion of the spec...
879 for(p
= bboard
; *p
; p
++)
881 if(bracket
) /* only one set allowed! */
887 if(bracket
!= 1) /* must be closing bracket */
892 else if(!(isalnum((unsigned char) *p
) || strindex(".-", *p
)))
896 snprintf(buf
, buflen
-(buf
-origbuf
), "%s%s%s",
897 (!nntp
&& *p
) ? "#public" : "#news.",
898 (!nntp
&& *p
&& *bboard
!= '/') ? "/" : "",
909 * build_folder_list - call mail_list to fetch us a list of folders
910 * from the given context.
913 build_folder_list(MAILSTREAM
**stream
, CONTEXT_S
*context
, char *pat
, char *content
, int flags
)
917 int local_open
= 0, we_cancel
= 0, resort
= 0;
918 char reference
[2*MAILTMPLEN
], *p
;
920 if(!(context
->dir
->status
& CNTXT_NOFIND
)
921 || (context
->dir
->status
& CNTXT_PARTFIND
))
922 return; /* find already done! */
924 dprint((7, "build_folder_list: %s %s\n",
925 context
? context
->context
: "NULL",
926 pat
? pat
: "NULL"));
928 we_cancel
= busy_cue(NULL
, NULL
, 1);
931 * Set up the pattern of folder name's to match within the
934 if(!pat
|| ((*pat
== '*' || *pat
== '%') && *(pat
+1) == '\0')){
935 context
->dir
->status
&= ~CNTXT_NOFIND
; /* let'em know we tried */
936 pat
= context
->dir
->view
.internal
;
939 context
->use
|= CNTXT_PARTFIND
; /* or are in a partial find */
941 memset(mm_list_info
= &ldata
, 0, sizeof(MM_LIST_S
));
942 ldata
.filter
= (NEWS_TEST(context
)) ? mail_lsub_filter
: mail_list_filter
;
943 memset(ldata
.data
= &response
, 0, sizeof(BFL_DATA_S
));
944 response
.list
= FOLDERS(context
);
946 if(flags
& BFL_FLDRONLY
)
947 response
.mask
= LATT_NOSELECT
;
950 * if context is associated with a server, prepare a stream for
951 * sending our request.
953 if(*context
->context
== '{'){
955 * Try using a stream we've already got open...
958 && !(ldata
.stream
= same_stream(context
->context
, *stream
))){
959 pine_mail_close(*stream
);
964 ldata
.stream
= sp_stream_get(context
->context
, SP_MATCH
);
967 ldata
.stream
= sp_stream_get(context
->context
, SP_SAME
);
969 /* gotta open a new one? */
970 if((F_OFF(F_CMBND_FOLDER_DISP
, ps_global
)
971 || context
->update
== LUU_INIT
) && !ldata
.stream
){
972 ldata
.stream
= mail_cmd_stream(context
, &local_open
);
974 *stream
= ldata
.stream
;
977 dprint((ldata
.stream
? 7 : 1, "build_folder_list: mail_open(%s) %s.\n",
978 context
->server
? context
->server
: "?",
979 ldata
.stream
? "OK" : "FAILED"));
982 context
->use
&= ~CNTXT_PARTFIND
; /* unset partial find bit */
983 context
->update
= LUU_NOMORECHK
;
990 else if(stream
&& *stream
){ /* no server, simple case */
991 if(!sp_flagged(*stream
, SP_LOCKED
))
992 pine_mail_close(*stream
);
998 * If preset reference string, we're somewhere in the hierarchy.
999 * ELSE we must be at top of context...
1001 response
.args
.name
= pat
;
1002 if(!(response
.args
.reference
= context
->dir
->ref
)){
1003 if((p
= strstr(context
->context
, "%s")) != NULL
){
1004 strncpy(response
.args
.reference
= reference
,
1006 MIN(p
- context
->context
, sizeof(reference
)/2));
1007 reference
[MIN(p
- context
->context
, sizeof(reference
)/2)] = '\0';
1009 response
.args
.tail
= p
;
1012 response
.args
.reference
= context
->context
;
1015 if(flags
& BFL_SCAN
)
1016 mail_scan(ldata
.stream
, response
.args
.reference
,
1017 response
.args
.name
, content
);
1018 else if(flags
& BFL_LSUB
)
1019 mail_lsub(ldata
.stream
, response
.args
.reference
, response
.args
.name
);
1021 set_read_predicted(1);
1022 pine_mail_list(ldata
.stream
, response
.args
.reference
, response
.args
.name
,
1024 set_read_predicted(0);
1027 context
->update
= LUU_INIT
;
1028 if(context
->dir
&& response
.response
.delim
)
1029 context
->dir
->delim
= response
.response
.delim
;
1031 if(!(flags
& (BFL_LSUB
|BFL_SCAN
)) && F_ON(F_QUELL_EMPTY_DIRS
, ps_global
)){
1036 for(i
= 0; i
< folder_total(response
.list
); i
++)
1037 if((f
= folder_entry(i
, FOLDERS(context
)))->isdir
){
1039 * we don't need to do a list if we know already
1040 * whether there are children or not.
1042 if(!f
->haschildren
&& !f
->hasnochildren
){
1043 memset(ldata
.data
= &listres
, 0, sizeof(LISTRES_S
));
1044 ldata
.filter
= mail_list_response
;
1046 if(context
->dir
->ref
)
1047 snprintf(reference
, sizeof(reference
), "%s%s", context
->dir
->ref
, f
->name
);
1049 context_apply(reference
, context
, f
->name
, sizeof(reference
)/2);
1051 /* append the delimiter to the reference */
1052 for(p
= reference
; *p
; p
++)
1055 *p
++ = context
->dir
->delim
;
1058 pine_mail_list(ldata
.stream
, reference
, "%", NULL
);
1060 /* anything interesting inside? */
1061 f
->hasnochildren
= (listres
.count
<= 1L);
1062 f
->haschildren
= !f
->hasnochildren
;;
1065 if(f
->hasnochildren
){
1068 if(ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_FIRST
1069 || ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_LAST
)
1073 * We don't want to hide directories
1074 * that are only directories even if they are
1075 * empty. We only want to hide the directory
1076 * piece of a dual-use folder when there are
1077 * no children in the directory (and the user
1078 * most likely thinks of it as a folder instead
1079 * of a folder and a directory).
1081 else if(f
->isdir
&& f
->isdual
){
1082 folder_delete(i
, FOLDERS(context
));
1090 resort_folder_list(response
.list
);
1092 if(local_open
&& !stream
)
1093 pine_mail_close(ldata
.stream
);
1095 if(context
->use
& CNTXT_PRESRV
)
1096 folder_select_restore(context
);
1098 context
->use
&= ~CNTXT_PARTFIND
; /* unset partial list bit */
1100 cancel_busy_cue(-1);
1105 * Validate LIST response to command issued from build_folder_list
1109 mail_list_filter(MAILSTREAM
*stream
, char *mailbox
, int delim
, long int attribs
, void *data
, unsigned int options
)
1111 BFL_DATA_S
*ld
= (BFL_DATA_S
*) data
;
1112 FOLDER_S
*new_f
= NULL
, *dual_f
= NULL
;
1113 int suppress_folder_add
= 0;
1119 ld
->response
.delim
= delim
;
1121 /* test against mask of DIS-allowed attributes */
1122 if((ld
->mask
& attribs
)){
1123 dprint((3, "mail_list_filter: failed attribute test"));
1128 * First, make sure response fits our "reference" arg
1129 * NOTE: build_folder_list can't supply breakout?
1131 if(!mail_list_in_collection(&mailbox
, ld
->args
.reference
,
1132 ld
->args
.name
, ld
->args
.tail
))
1135 /* ignore dotfolders unless told not to */
1136 if(F_OFF(F_ENABLE_DOT_FOLDERS
, ps_global
) && *mailbox
== '.'){
1137 dprint((3, "mail_list_filter: dotfolder disallowed"));
1142 * If this response is INBOX we have to handle it specially.
1143 * The special cases are:
1145 * Incoming folders are enabled and this INBOX is in the Incoming
1146 * folders list already. We don't want to list it here, as well,
1147 * unless it is also a directory.
1149 * Incoming folders are not enabled, but we inserted INBOX into
1150 * this primary collection (with init_inbox_mapping()) so it is
1151 * already accounted for unless it is also a directory.
1153 * Incoming folders are not enabled, but we inserted INBOX into
1154 * the primary collection (which we are not looking at here) so
1155 * it is already accounted for there. We'll add it as a directory
1156 * as well here if that is what is called for.
1158 if(!strucmp(mailbox
, ps_global
->inbox_name
)){
1161 char fullname
[1000], tmp1
[1000], tmp2
[1000], *l1
, *l2
;
1163 /* fullname is the name of the mailbox in the list response */
1164 if(ld
->args
.reference
){
1165 strncpy(fullname
, ld
->args
.reference
, sizeof(fullname
)-1);
1166 fullname
[sizeof(fullname
)-1] = '\0';
1171 strncat(fullname
, mailbox
, sizeof(fullname
)-strlen(fullname
)-1);
1172 fullname
[sizeof(fullname
)-1] = '\0';
1174 /* check if Incoming Folders are enabled */
1175 if(ps_global
->context_list
&& ps_global
->context_list
->use
& CNTXT_INCMNG
){
1176 int this_inbox_is_in_incoming
= 0;
1179 * Figure out if this INBOX is already in the Incoming list.
1182 /* compare fullname to each incoming folder */
1183 ftotal
= folder_total(FOLDERS(ps_global
->context_list
));
1184 for(i
= 0; i
< ftotal
&& !this_inbox_is_in_incoming
; i
++){
1185 f
= folder_entry(i
, FOLDERS(ps_global
->context_list
));
1187 if(same_remote_mailboxes(fullname
, f
->name
)
1188 || ((!IS_REMOTE(fullname
) && !IS_REMOTE(f
->name
))
1189 && (l1
=mailboxfile(tmp1
,fullname
))
1190 && (l2
=mailboxfile(tmp2
,f
->name
))
1192 this_inbox_is_in_incoming
++;
1196 if(this_inbox_is_in_incoming
){
1198 * Don't add a folder for this, only a directory if called for.
1199 * If it isn't a directory, skip it.
1201 if(!(delim
&& !(attribs
& LATT_NOINFERIORS
)))
1204 suppress_folder_add
++;
1208 int inbox_is_in_this_collection
= 0;
1210 /* is INBOX already in this folder list? */
1211 ftotal
= folder_total(ld
->list
);
1212 for(i
= 0; i
< ftotal
&& !inbox_is_in_this_collection
; i
++){
1213 f
= folder_entry(i
, ld
->list
);
1214 if(!strucmp(FLDR_NAME(f
), ps_global
->inbox_name
))
1215 inbox_is_in_this_collection
++;
1218 if(inbox_is_in_this_collection
){
1221 * Inbox is already inserted in this collection. Unless
1222 * it is also a directory, we are done.
1224 if(!(delim
&& !(attribs
& LATT_NOINFERIORS
)))
1228 * Since it is also a directory, what we do depends on
1229 * the F_SEP feature. If that feature is not set we just
1230 * want to mark the existing entry dual-use. If F_SEP is
1231 * set we want to add a new directory entry.
1233 if(F_ON(F_SEPARATE_FLDR_AS_DIR
, ps_global
)){
1236 f
->hasnochildren
= 0;
1237 /* fall through and add a new directory */
1240 /* mark existing entry dual-use and return */
1241 ld
->response
.count
++;
1242 ld
->response
.isdir
= 1;
1244 if(attribs
& LATT_HASCHILDREN
)
1247 if(attribs
& LATT_HASNOCHILDREN
)
1248 f
->hasnochildren
= 1;
1253 suppress_folder_add
++;
1257 int this_inbox_is_primary_inbox
= 0;
1260 * See if this INBOX is the same as the INBOX we inserted
1261 * in the primary collection.
1264 /* first find the existing INBOX entry */
1265 ftotal
= folder_total(FOLDERS(ps_global
->context_list
));
1266 for(i
= 0; i
< ftotal
&& !found_it
; i
++){
1267 f
= folder_entry(i
, FOLDERS(ps_global
->context_list
));
1268 if(!strucmp(FLDR_NAME(f
), ps_global
->inbox_name
))
1272 if(found_it
&& f
&& f
->name
){
1273 if(same_remote_mailboxes(fullname
, f
->name
)
1274 || ((!IS_REMOTE(fullname
) && !IS_REMOTE(f
->name
))
1275 && (l1
=mailboxfile(tmp1
,fullname
))
1276 && (l2
=mailboxfile(tmp2
,f
->name
))
1278 this_inbox_is_primary_inbox
++;
1281 if(this_inbox_is_primary_inbox
){
1283 * Don't add a folder for this, only a directory if called for.
1284 * If it isn't a directory, skip it.
1286 if(!(delim
&& !(attribs
& LATT_NOINFERIORS
)))
1289 suppress_folder_add
++;
1295 /* is it a mailbox? */
1296 if(!(attribs
& LATT_NOSELECT
) && !suppress_folder_add
){
1297 ld
->response
.count
++;
1298 ld
->response
.isfile
= 1;
1299 new_f
= new_folder(mailbox
, 0);
1300 new_f
->isfolder
= 1;
1302 if(F_ON(F_SEPARATE_FLDR_AS_DIR
, ps_global
)){
1303 folder_insert(-1, new_f
, ld
->list
);
1310 if(delim
&& !(attribs
& LATT_NOINFERIORS
)){
1311 ld
->response
.count
++;
1312 ld
->response
.isdir
= 1;
1315 new_f
= new_folder(mailbox
, 0);
1318 if(attribs
& LATT_HASCHILDREN
)
1319 new_f
->haschildren
= 1;
1320 if(attribs
& LATT_HASNOCHILDREN
)
1321 new_f
->hasnochildren
= 1;
1324 * When we have F_SEPARATE_FLDR_AS_DIR we still want to know
1325 * whether the name really represents both so that we don't
1326 * inadvertently delete both when the user meant one or the
1330 if(attribs
& LATT_HASCHILDREN
)
1331 dual_f
->haschildren
= 1;
1333 if(attribs
& LATT_HASNOCHILDREN
)
1334 dual_f
->hasnochildren
= 1;
1342 folder_insert(-1, new_f
, ld
->list
);
1344 if(attribs
& LATT_MARKED
)
1345 ld
->response
.ismarked
= 1;
1347 /* don't mark #move folders unmarked */
1348 if(attribs
& LATT_UNMARKED
&& !(options
& PML_IS_MOVE_MBOX
))
1349 ld
->response
.unmarked
= 1;
1351 if(attribs
& LATT_HASCHILDREN
)
1352 ld
->response
.haschildren
= 1;
1354 if(attribs
& LATT_HASNOCHILDREN
)
1355 ld
->response
.hasnochildren
= 1;
1360 * Validate LSUB response to command issued from build_folder_list
1364 mail_lsub_filter(MAILSTREAM
*stream
, char *mailbox
, int delim
, long int attribs
,
1365 void *data
, unsigned int options
)
1367 BFL_DATA_S
*ld
= (BFL_DATA_S
*) data
;
1368 FOLDER_S
*new_f
= NULL
;
1372 ld
->response
.delim
= delim
;
1374 /* test against mask of DIS-allowed attributes */
1375 if((ld
->mask
& attribs
)){
1376 dprint((3, "mail_lsub_filter: failed attribute test"));
1380 /* Normalize mailbox and reference strings re: namespace */
1381 if(!strncmp(mailbox
, "#news.", 6))
1384 if(!strncmp(ref
= ld
->args
.reference
, "#news.", 6))
1387 if(!mail_list_in_collection(&mailbox
, ref
, ld
->args
.name
, ld
->args
.tail
))
1390 if(!(attribs
& LATT_NOSELECT
)){
1391 ld
->response
.count
++;
1392 ld
->response
.isfile
= 1;
1393 new_f
= new_folder(mailbox
, 0);
1394 new_f
->isfolder
= 1;
1395 folder_insert(F_ON(F_READ_IN_NEWSRC_ORDER
, ps_global
)
1396 ? folder_total(ld
->list
) : -1,
1400 /* We don't support directories in #news */
1403 /* human readable name for a folder. memory freed by caller
1404 * This is a jacket to conversion from modified utf7 to utf8.
1406 unsigned char *folder_name_decoded(unsigned char *mailbox
)
1409 s
= (unsigned char *) utf8_from_mutf7((unsigned char *) mailbox
);
1410 if (s
== NULL
) s
= (unsigned char *) cpystr((char *)mailbox
);
1414 /* mutf7 encoded name of a folder, from its name in utf8.
1415 * memory freed by caller.
1417 unsigned char *folder_name_encoded(unsigned char *mailbox
)
1420 if ((s
= utf8_to_mutf7(mailbox
)) == NULL
)
1421 s
= (unsigned char *) cpystr((char *) mailbox
);
1426 mail_list_in_collection(char **mailbox
, char *ref
, char *name
, char *tail
)
1428 int boxlen
, reflen
, taillen
;
1431 boxlen
= strlen(*mailbox
);
1432 reflen
= ref
? strlen(ref
) : 0;
1433 taillen
= tail
? strlen(tail
) : 0;
1436 && (reflen
? !struncmp(*mailbox
, ref
, reflen
)
1437 : (p
= strpbrk(name
, "%*"))
1438 ? !struncmp(*mailbox
, name
, p
- name
)
1439 : !strucmp(*mailbox
,name
))
1441 || (taillen
< boxlen
- reflen
1442 && !strucmp(&(*mailbox
)[boxlen
- taillen
], tail
)))){
1444 (*mailbox
)[boxlen
- taillen
] = '\0';
1446 if(*(*mailbox
+= reflen
))
1450 * else don't worry about context "breakouts" since
1451 * build_folder_list doesn't let the user introduce
1460 * rebuild_folder_list -- free up old list and re-issue commands to build
1464 refresh_folder_list(CONTEXT_S
*context
, int nodirs
, int startover
, MAILSTREAM
**streamp
)
1467 free_folder_list(context
);
1469 build_folder_list(streamp
, context
, NULL
, NULL
,
1470 (NEWS_TEST(context
) ? BFL_LSUB
: BFL_NONE
)
1471 | ((nodirs
) ? BFL_FLDRONLY
: BFL_NONE
));
1476 * free_folder_list - loop thru the context's lists of folders
1477 * clearing all entries without nicknames
1478 * (as those were user provided) AND reset the
1479 * context's find flag.
1481 * NOTE: if fetched() information (e.g., like message counts come back
1482 * in bboard collections), we may want to have a check before
1483 * executing the loop and setting the FIND flag.
1486 free_folder_list(CONTEXT_S
*cntxt
)
1491 * In this case, don't blast the list as it was given to us by the
1492 * user and not the result of a mail_list call...
1494 if(cntxt
->use
& CNTXT_INCMNG
)
1497 if(cntxt
->use
& CNTXT_PRESRV
)
1498 folder_select_preserve(cntxt
);
1500 for(n
= folder_total(FOLDERS(cntxt
)), i
= 0; n
> 0; n
--)
1501 if(folder_entry(i
, FOLDERS(cntxt
))->nickname
)
1502 i
++; /* entry wasn't from LIST */
1504 folder_delete(i
, FOLDERS(cntxt
));
1506 cntxt
->dir
->status
|= CNTXT_NOFIND
; /* do find next time... */
1507 /* or add the fake entry */
1508 cntxt
->use
&= ~(CNTXT_PSEUDO
| CNTXT_PRESRV
| CNTXT_ZOOM
);
1513 * default_save_context - return the default context for saved messages
1516 default_save_context(CONTEXT_S
*cntxt
)
1519 if((cntxt
->use
) & CNTXT_SAVEDFLT
)
1522 cntxt
= cntxt
->next
;
1530 * folder_complete - foldername completion routine
1532 * Result: returns 0 if the folder doesn't have a any completetion
1533 * 1 if the folder has a completion (*AND* "name" is
1534 * replaced with the completion)
1538 folder_complete(CONTEXT_S
*context
, char *name
, size_t namelen
, int *completions
)
1540 return(folder_complete_internal(context
, name
, namelen
, completions
, FC_NONE
));
1548 folder_complete_internal(CONTEXT_S
*context
, char *name
, size_t namelen
,
1549 int *completions
, int flags
)
1551 int i
, match
= -1, ftotal
;
1552 char tmp
[MAXFOLDER
+2], *a
, *b
, *fn
, *pat
;
1558 if(*name
== '\0' || !context_isambig(name
))
1561 if(!((context
->use
& CNTXT_INCMNG
) || ALL_FOUND(context
))){
1563 * Build the folder list from scratch since we may need to
1564 * traverse hierarchy...
1567 free_folder_list(context
);
1568 snprintf(tmp
, sizeof(tmp
), "%s%c", name
, NEWS_TEST(context
) ? '*' : '%');
1569 build_folder_list(NULL
, context
, tmp
, NULL
,
1570 (NEWS_TEST(context
) & !(flags
& FC_FORCE_LIST
))
1571 ? BFL_LSUB
: BFL_NONE
);
1574 *tmp
= '\0'; /* find uniq substring */
1575 ftotal
= folder_total(FOLDERS(context
));
1576 for(i
= 0; i
< ftotal
; i
++){
1577 f
= folder_entry(i
, FOLDERS(context
));
1580 if(!(NEWS_TEST(context
) || (context
->use
& CNTXT_INCMNG
))){
1581 fn
= folder_last_cmpnt(fn
, context
->dir
->delim
);
1582 pat
= folder_last_cmpnt(pat
, context
->dir
->delim
);
1585 if(!strncmp(fn
, pat
, strlen(pat
))){
1586 if(match
!= -1){ /* oh well, do best we can... */
1589 f
= folder_entry(match
, FOLDERS(context
));
1591 if(!NEWS_TEST(context
))
1592 fn
= folder_last_cmpnt(fn
, context
->dir
->delim
);
1594 strncpy(tmp
, fn
, sizeof(tmp
)-1);
1595 tmp
[sizeof(tmp
)-1] = '\0';
1599 b
= tmp
; /* remember largest common text */
1600 while(*a
&& *b
&& *a
== *b
)
1606 match
= i
; /* bingo?? */
1610 if(match
>= 0){ /* found! */
1611 f
= folder_entry(match
, FOLDERS(context
));
1613 if(!(NEWS_TEST(context
) || (context
->use
& CNTXT_INCMNG
)))
1614 fn
= folder_last_cmpnt(fn
, context
->dir
->delim
);
1616 strncpy(pat
, fn
, namelen
-(pat
-name
));
1617 name
[namelen
-1] = '\0';
1618 if(f
->isdir
&& !f
->isfolder
){
1619 name
[i
= strlen(name
)] = context
->dir
->delim
;
1623 else if(match
== -2){ /* closest we could find */
1624 strncpy(pat
, tmp
, namelen
-(pat
-name
));
1625 name
[namelen
-1] = '\0';
1629 *completions
= ftotal
;
1631 if(!((context
->use
& CNTXT_INCMNG
) || ALL_FOUND(context
)))
1632 free_folder_list(context
);
1634 return((match
>= 0) ? ftotal
: 0);
1639 folder_last_cmpnt(char *s
, int d
)
1644 for(p
= s
; (p
= strindex(p
, d
)); s
= ++p
)
1652 * init_folder_entries - return a piece of memory suitable for attaching
1653 * a list of folders...
1657 init_folder_entries(void)
1659 FLIST
*flist
= (FLIST
*) fs_get(sizeof(FLIST
));
1660 flist
->folders
= (FOLDER_S
**) fs_get(FCHUNK
* sizeof(FOLDER_S
*));
1661 memset((void *)flist
->folders
, 0, (FCHUNK
* sizeof(FOLDER_S
*)));
1662 flist
->allocated
= FCHUNK
;
1669 * new_folder - return a brand new folder entry, with the given name
1672 * NOTE: THIS IS THE ONLY WAY TO PUT A NAME INTO A FOLDER ENTRY!!!
1673 * STRCPY WILL NOT WORK!!!
1676 new_folder(char *name
, long unsigned int hash
)
1679 size_t l
= strlen(name
);
1681 tmp
= (FOLDER_S
*)fs_get(sizeof(FOLDER_S
) + (l
* sizeof(char)));
1682 memset((void *)tmp
, 0, sizeof(FOLDER_S
));
1683 strncpy(tmp
->name
, name
, l
);
1684 tmp
->name
[l
] = '\0';
1685 tmp
->name_len
= (unsigned char) l
;
1686 tmp
->varhash
= hash
;
1692 * folder_entry - folder struct access routine. Permit reference to
1693 * folder structs via index number. Serves two purposes:
1694 * 1) easy way for callers to access folder data
1696 * 2) allows for a custom manager to limit memory use
1697 * under certain rather limited "operating systems"
1698 * who shall renameless, but whose initials are DOS
1703 folder_entry(int i
, FLIST
*flist
)
1705 return((i
>= flist
->used
) ? NULL
:flist
->folders
[i
]);
1710 * free_folder_entries - release all resources associated with the given
1711 * list of folder entries
1714 free_folder_entries(FLIST
**flist
)
1718 if(!(flist
&& *flist
))
1723 if((*flist
)->folders
[i
]->nickname
)
1724 fs_give((void **) &(*flist
)->folders
[i
]->nickname
);
1726 fs_give((void **) &((*flist
)->folders
[i
]));
1729 fs_give((void **) &((*flist
)->folders
));
1730 fs_give((void **) flist
);
1735 * return the number of folders associated with the given folder list
1738 folder_total(FLIST
*flist
)
1740 return((int) flist
->used
);
1745 * return the index number of the given name in the given folder list
1748 folder_index(char *name
, CONTEXT_S
*cntxt
, int flags
)
1754 for(i
= 0; (f
= folder_entry(i
, FOLDERS(cntxt
))); i
++)
1755 if(((flags
& FI_FOLDER
) && (f
->isfolder
|| (cntxt
->use
& CNTXT_INCMNG
)))
1756 || ((flags
& FI_DIR
) && f
->isdir
)){
1757 fname
= FLDR_NAME(f
);
1758 #if defined(DOS) || defined(OS2)
1759 if(flags
& FI_RENAME
){ /* case-dependent for rename */
1760 if(*name
== *fname
&& strcmp(name
, fname
) == 0)
1764 if(toupper((unsigned char)(*name
))
1765 == toupper((unsigned char)(*fname
)) && strucmp(name
, fname
) == 0)
1769 if(*name
== *fname
&& strcmp(name
, fname
) == 0)
1779 * folder_is_nick - check to see if the given name is a nickname
1780 * for some folder in the given context...
1782 * NOTE: no need to check if mm_list_names has been done as
1783 * nicknames can only be set by configuration...
1786 folder_is_nick(char *nickname
, FLIST
*flist
, int flags
)
1791 if(!(nickname
&& *nickname
&& flist
))
1794 while((f
= folder_entry(i
, flist
)) != NULL
){
1796 * The second part of the OR is checking in a case-indep
1797 * way for INBOX. It should be restricted to the context
1798 * to which we add the INBOX folder, which would be either
1799 * the Incoming Folders collection or the first collection
1800 * if there is no Incoming collection. We don't need to check
1801 * the collection because nickname assignment has already
1802 * done that for us. Most folders don't have nicknames, only
1803 * incoming folders and folders like inbox if not in incoming.
1806 && (!strcmp(nickname
, f
->nickname
)
1807 || (!strucmp(nickname
, f
->nickname
)
1808 && !strucmp(nickname
, ps_global
->inbox_name
)))){
1809 char source
[MAILTMPLEN
], *target
= NULL
;
1812 * If f is a maildrop, then we want to return the
1813 * destination folder, not the whole #move thing.
1815 if(!(flags
& FN_WHOLE_NAME
)
1816 && check_for_move_mbox(f
->name
, source
, sizeof(source
), &target
))
1830 folder_is_target_of_nick(char *longname
, CONTEXT_S
*cntxt
)
1834 FLIST
*flist
= NULL
;
1836 if(cntxt
&& cntxt
== ps_global
->context_list
)
1837 flist
= FOLDERS(cntxt
);
1839 if(!(longname
&& *longname
&& flist
))
1842 while((f
= folder_entry(i
, flist
)) != NULL
){
1843 if(f
->nickname
&& f
->name
&& !strcmp(longname
, f
->name
))
1844 return(f
->nickname
);
1853 /*----------------------------------------------------------------------
1854 Insert the given folder name into the sorted folder list
1855 associated with the given context. Only allow ambiguous folder
1856 names IF associated with a nickname.
1858 Args: index -- Index to insert at, OR insert in sorted order if -1
1859 folder -- folder structure to insert into list
1860 flist -- folder list to insert folder into
1863 DON'T count on the folder pointer being valid after this returns
1864 *** ALL FOLDER ELEMENT READS SHOULD BE THRU folder_entry() ***
1868 folder_insert(int index
, FOLDER_S
*folder
, FLIST
*flist
)
1870 /* requested index < 0 means add to sorted list */
1871 if(index
< 0 && (index
= folder_total(flist
)) > 0)
1872 index
= folder_insert_sorted(index
/ 2, 0, index
, folder
, flist
,
1873 (ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_FIRST
)
1874 ? compare_folders_dir_alpha
1875 : (ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_LAST
)
1876 ? compare_folders_alpha_dir
1877 : compare_folders_alpha
);
1879 folder_insert_index(folder
, index
, flist
);
1885 * folder_insert_sorted - Insert given folder struct into given list
1886 * observing sorting specified by given
1887 * comparison function
1890 folder_insert_sorted(int index
, int min_index
, int max_index
, FOLDER_S
*folder
,
1891 FLIST
*flist
, int (*compf
)(FOLDER_S
*, FOLDER_S
*))
1895 return(((i
= (*compf
)(folder_entry(index
, flist
), folder
)) == 0)
1898 ? ((++index
>= max_index
)
1900 : ((*compf
)(folder_entry(index
, flist
), folder
) > 0)
1902 : folder_insert_sorted((index
+ max_index
) / 2, index
,
1903 max_index
, folder
, flist
, compf
))
1904 : ((index
<= min_index
)
1906 : folder_insert_sorted((min_index
+ index
) / 2, min_index
, index
,
1907 folder
, flist
, compf
)));
1912 * folder_insert_index - Insert the given folder struct into the global list
1913 * at the given index.
1916 folder_insert_index(FOLDER_S
*folder
, int index
, FLIST
*flist
)
1918 register FOLDER_S
**flp
, **iflp
;
1920 /* if index is beyond size, place at end of list */
1921 index
= MIN(index
, flist
->used
);
1924 if(flist
->used
+ 1 > flist
->allocated
){
1925 flist
->allocated
+= FCHUNK
;
1926 fs_resize((void **)&(flist
->folders
),
1927 flist
->allocated
* sizeof(FOLDER_S
*));
1930 /* shift array left */
1931 iflp
= &((flist
->folders
)[index
]);
1932 for(flp
= &((flist
->folders
)[flist
->used
]);
1936 flist
->folders
[index
] = folder
;
1942 resort_folder_list(FLIST
*flist
)
1944 if(flist
&& folder_total(flist
) > 1 && flist
->folders
)
1945 qsort(flist
->folders
, folder_total(flist
), sizeof(flist
->folders
[0]),
1946 (ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_FIRST
)
1947 ? compare_folders_dir_alpha_qsort
1948 : (ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_LAST
)
1949 ? compare_folders_alpha_dir_qsort
1950 : compare_folders_alpha_qsort
);
1954 /*----------------------------------------------------------------------
1955 Removes a folder at the given index in the given context's
1958 Args: index -- Index in folder list of folder to be removed
1959 flist -- folder list
1962 folder_delete(int index
, FLIST
*flist
)
1968 && (index
< 0 || index
>= flist
->used
))
1969 return; /* bogus call! */
1971 if((f
= folder_entry(index
, flist
))->nickname
)
1972 fs_give((void **)&(f
->nickname
));
1974 fs_give((void **) &(flist
->folders
[index
]));
1975 for(i
= index
; i
< flist
->used
- 1; i
++)
1976 flist
->folders
[i
] = flist
->folders
[i
+1];
1983 /*----------------------------------------------------------------------
1984 compare two names for qsort, case independent
1986 Args: pointers to strings to compare
1988 Result: integer result of strcmp of the names. Uses simple
1989 efficiency hack to speed the string comparisons up a bit.
1991 ----------------------------------------------------------------------*/
1993 compare_names(const qsort_t
*x
, const qsort_t
*y
)
1995 char *a
= *(char **)x
, *b
= *(char **)y
;
1997 #define CMPI(X,Y) ((X)[0] - (Y)[0])
1998 #define UCMPI(X,Y) ((isupper((unsigned char)((X)[0])) \
1999 ? (X)[0] - 'A' + 'a' : (X)[0]) \
2000 - (isupper((unsigned char)((Y)[0])) \
2001 ? (Y)[0] - 'A' + 'a' : (Y)[0]))
2003 /*---- Inbox always sorts to the top ----*/
2004 if(UCMPI(a
, ps_global
->inbox_name
) == 0
2005 && strucmp(a
, ps_global
->inbox_name
) == 0)
2006 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : -1);
2007 else if((UCMPI(b
, ps_global
->inbox_name
)) == 0
2008 && strucmp(b
, ps_global
->inbox_name
) == 0)
2009 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : 1);
2011 /*----- The sent-mail folder, is always next unless... ---*/
2012 else if(F_OFF(F_SORT_DEFAULT_FCC_ALPHA
, ps_global
)
2013 && CMPI(a
, ps_global
->VAR_DEFAULT_FCC
) == 0
2014 && strcmp(a
, ps_global
->VAR_DEFAULT_FCC
) == 0)
2015 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : -1);
2016 else if(F_OFF(F_SORT_DEFAULT_FCC_ALPHA
, ps_global
)
2017 && CMPI(b
, ps_global
->VAR_DEFAULT_FCC
) == 0
2018 && strcmp(b
, ps_global
->VAR_DEFAULT_FCC
) == 0)
2019 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : 1);
2021 /*----- The saved-messages folder, is always next unless... ---*/
2022 else if(F_OFF(F_SORT_DEFAULT_SAVE_ALPHA
, ps_global
)
2023 && CMPI(a
, ps_global
->VAR_DEFAULT_SAVE_FOLDER
) == 0
2024 && strcmp(a
, ps_global
->VAR_DEFAULT_SAVE_FOLDER
) == 0)
2025 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : -1);
2026 else if(F_OFF(F_SORT_DEFAULT_SAVE_ALPHA
, ps_global
)
2027 && CMPI(b
, ps_global
->VAR_DEFAULT_SAVE_FOLDER
) == 0
2028 && strcmp(b
, ps_global
->VAR_DEFAULT_SAVE_FOLDER
) == 0)
2029 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : 1);
2032 return((r
= CMPI(a
, b
)) ? r
: strcmp(a
, b
));
2036 /*----------------------------------------------------------------------
2037 compare two folder structs for ordering alphabetically
2039 Args: pointers to folder structs to compare
2041 Result: integer result of dir-bit and strcmp of the folders.
2042 ----------------------------------------------------------------------*/
2044 compare_folders_alpha(FOLDER_S
*f1
, FOLDER_S
*f2
)
2047 char *f1name
= FLDR_NAME(f1
),
2048 *f2name
= FLDR_NAME(f2
);
2050 return(((i
= compare_names(&f1name
, &f2name
)) != 0)
2051 ? i
: (f2
->isdir
- f1
->isdir
));
2056 compare_folders_alpha_qsort(const qsort_t
*a1
, const qsort_t
*a2
)
2058 FOLDER_S
*f1
= *((FOLDER_S
**) a1
);
2059 FOLDER_S
*f2
= *((FOLDER_S
**) a2
);
2061 return(compare_folders_alpha(f1
, f2
));
2065 /*----------------------------------------------------------------------
2066 compare two folder structs alphabetically with dirs first
2068 Args: pointers to folder structs to compare
2070 Result: integer result of dir-bit and strcmp of the folders.
2071 ----------------------------------------------------------------------*/
2073 compare_folders_dir_alpha(FOLDER_S
*f1
, FOLDER_S
*f2
)
2077 if((i
= (f2
->isdir
- f1
->isdir
)) == 0){
2078 char *f1name
= FLDR_NAME(f1
),
2079 *f2name
= FLDR_NAME(f2
);
2081 return(compare_names(&f1name
, &f2name
));
2089 compare_folders_dir_alpha_qsort(const qsort_t
*a1
, const qsort_t
*a2
)
2091 FOLDER_S
*f1
= *((FOLDER_S
**) a1
);
2092 FOLDER_S
*f2
= *((FOLDER_S
**) a2
);
2094 return(compare_folders_dir_alpha(f1
, f2
));
2098 /*----------------------------------------------------------------------
2099 compare two folder structs alphabetically with dirs last
2101 Args: pointers to folder structs to compare
2103 Result: integer result of dir-bit and strcmp of the folders.
2104 ----------------------------------------------------------------------*/
2106 compare_folders_alpha_dir(FOLDER_S
*f1
, FOLDER_S
*f2
)
2110 if((i
= (f1
->isdir
- f2
->isdir
)) == 0){
2111 char *f1name
= FLDR_NAME(f1
),
2112 *f2name
= FLDR_NAME(f2
);
2114 return(compare_names(&f1name
, &f2name
));
2122 compare_folders_alpha_dir_qsort(const qsort_t
*a1
, const qsort_t
*a2
)
2124 FOLDER_S
*f1
= *((FOLDER_S
**) a1
);
2125 FOLDER_S
*f2
= *((FOLDER_S
**) a2
);
2127 return(compare_folders_alpha_dir(f1
, f2
));
2132 * Find incoming folders and update the unseen counts
2136 folder_unseen_count_updater(unsigned long flags
)
2139 time_t oldest
, started_checking
;
2140 int ftotal
, i
, first
= -1;
2144 * We would only do this if there is an incoming collection, the
2145 * user wants us to monitor, and we're in the folder screen.
2147 if(ps_global
->in_folder_screen
&& F_ON(F_ENABLE_INCOMING_CHECKING
, ps_global
)
2148 && (ctxt
=ps_global
->context_list
) && ctxt
->use
& CNTXT_INCMNG
2149 && (ftotal
= folder_total(FOLDERS(ctxt
)))){
2151 * Search through the last_unseen_update times to find
2152 * the one that was updated longest ago, and start with
2153 * that one. We don't want to delay long doing these
2154 * checks so may only check some of them each time we
2155 * get called. An update time equal to 1 means don't check
2156 * this folder at all.
2158 for(i
= 0; i
< ftotal
; i
++){
2159 f
= folder_entry(i
, FOLDERS(ctxt
));
2160 if(f
&& LUU_YES(f
->last_unseen_update
)){
2162 oldest
= f
->last_unseen_update
;
2168 * Now first is the first in the list that
2169 * should ever be checked. Next find the
2170 * one that should be checked next, the one
2171 * that was checked longest ago.
2174 for(i
= 1; i
< ftotal
; i
++){
2175 f
= folder_entry(i
, FOLDERS(ctxt
));
2176 if(f
&& LUU_YES(f
->last_unseen_update
) && f
->last_unseen_update
< oldest
){
2178 oldest
= f
->last_unseen_update
;
2183 /* now first is the next one to be checked */
2185 started_checking
= time(0);
2187 for(i
= first
; i
< ftotal
; i
++){
2188 /* update the next one */
2189 f
= folder_entry(i
, FOLDERS(ctxt
));
2190 if(f
&& LUU_YES(f
->last_unseen_update
)
2191 && (flags
& UFU_FORCE
2192 /* or it's been long enough and we've not been in this function too long */
2193 || (((time(0) - f
->last_unseen_update
) >= ps_global
->inc_check_interval
)
2194 && ((time(0) - started_checking
) < MIN(4,ps_global
->inc_check_timeout
)))))
2195 update_folder_unseen(f
, ctxt
, flags
, NULL
);
2198 for(i
= 0; i
< first
; i
++){
2199 f
= folder_entry(i
, FOLDERS(ctxt
));
2200 if(f
&& LUU_YES(f
->last_unseen_update
)
2201 && (flags
& UFU_FORCE
2202 || (((time(0) - f
->last_unseen_update
) >= ps_global
->inc_check_interval
)
2203 && ((time(0) - started_checking
) < MIN(4,ps_global
->inc_check_timeout
)))))
2204 update_folder_unseen(f
, ctxt
, flags
, NULL
);
2211 * Update the count of unseen in the FOLDER_S struct
2212 * for this folder. This will update if the time
2213 * interval has passed or if the FORCE flag is set.
2216 update_folder_unseen(FOLDER_S
*f
, CONTEXT_S
*ctxt
, unsigned long flags
,
2217 MAILSTREAM
*this_is_the_stream
)
2221 int use_imap_interval
= 0;
2222 int stream_is_open
= 0;
2223 unsigned long orig_unseen
, orig_new
, orig_tot
;
2224 char mailbox_name
[MAILTMPLEN
];
2225 char *target
= NULL
;
2228 if(!f
|| !LUU_YES(f
->last_unseen_update
))
2232 context_apply(mailbox_name
, ctxt
, f
->name
, MAILTMPLEN
);
2234 if(!mailbox_name
[0])
2237 if(check_for_move_mbox(mailbox_name
, NULL
, 0, &target
)){
2241 * If this maildrop is the currently open stream use that.
2242 * I'm not altogether sure that this is a good way to
2246 && ((strm
=ps_global
->mail_stream
)
2248 && (!strcmp(target
,strm
->mailbox
)
2249 || !strcmp(target
,strm
->original_mailbox
)))){
2254 MAILSTREAM
*m
= NULL
;
2256 stream_is_open
= (this_is_the_stream
2257 || (m
=sp_stream_get(mailbox_name
, SP_MATCH
| SP_RO_OK
))
2258 || ((m
=ps_global
->mail_stream
) && !sp_dead_stream(m
)
2259 && same_stream_and_mailbox(mailbox_name
,m
))
2260 || (!IS_REMOTE(mailbox_name
)
2261 && (m
=already_open_stream(mailbox_name
, AOS_NONE
)))) ? 1 : 0;
2264 if(!this_is_the_stream
)
2265 this_is_the_stream
= m
;
2269 * If it's IMAP or local we use a shorter interval.
2271 d
= mail_valid(NIL
, mailbox_name
, (char *) NIL
);
2272 if((d
&& !strcmp(d
->name
, "imap")) || !IS_REMOTE(mailbox_name
))
2273 use_imap_interval
++;
2278 * Update if forced, or if it's been a while, or if we have a
2279 * stream open to this mailbox already.
2281 if(flags
& UFU_FORCE
2283 || ((use_imap_interval
2284 && (now
- f
->last_unseen_update
) >= ps_global
->inc_check_interval
)
2285 || ((now
- f
->last_unseen_update
) >= ps_global
->inc_second_check_interval
))){
2286 unsigned long tot
, uns
, new;
2287 unsigned long *totp
= NULL
, *unsp
= NULL
, *newp
= NULL
;
2289 orig_valid
= f
->unseen_valid
;
2290 orig_unseen
= f
->unseen
;
2292 orig_tot
= f
->total
;
2294 if(F_ON(F_INCOMING_CHECKING_RECENT
, ps_global
))
2299 if(F_ON(F_INCOMING_CHECKING_TOTAL
, ps_global
))
2302 f
->unseen_valid
= 0;
2304 dprint((9, "update_folder_unseen(%s)", FLDR_NAME(f
)));
2305 if(get_recent_in_folder(mailbox_name
, newp
, unsp
, totp
, this_is_the_stream
)){
2306 f
->last_unseen_update
= time(0);
2307 f
->unseen_valid
= 1;
2318 dprint((9, "update_folder_unseen(%s): original: %s%s%s%s",
2320 F_ON(F_INCOMING_CHECKING_RECENT
,ps_global
) ? "new=" : "unseen=",
2321 F_ON(F_INCOMING_CHECKING_RECENT
,ps_global
) ? comatose(f
->new) : comatose(f
->unseen
),
2322 F_ON(F_INCOMING_CHECKING_TOTAL
,ps_global
) ? " tot=" : "",
2323 F_ON(F_INCOMING_CHECKING_TOTAL
,ps_global
) ? comatose(f
->total
) : ""));
2327 && ((F_ON(F_INCOMING_CHECKING_RECENT
, ps_global
)
2328 && orig_new
!= f
->new)
2330 (F_OFF(F_INCOMING_CHECKING_RECENT
, ps_global
)
2331 && orig_unseen
!= f
->unseen
)
2333 (F_ON(F_INCOMING_CHECKING_TOTAL
, ps_global
)
2334 && orig_tot
!= f
->total
))){
2336 if(ps_global
->in_folder_screen
)
2337 ps_global
->noticed_change_in_unseen
= 1;
2339 dprint((9, "update_folder_unseen(%s): changed: %s%s%s%s",
2341 F_ON(F_INCOMING_CHECKING_RECENT
,ps_global
) ? "new=" : "unseen=",
2342 F_ON(F_INCOMING_CHECKING_RECENT
,ps_global
) ? comatose(f
->new) : comatose(f
->unseen
),
2343 F_ON(F_INCOMING_CHECKING_TOTAL
,ps_global
) ? " tot=" : "",
2344 F_ON(F_INCOMING_CHECKING_TOTAL
,ps_global
) ? comatose(f
->total
) : ""));
2346 if(flags
& UFU_ANNOUNCE
2347 && ((F_ON(F_INCOMING_CHECKING_RECENT
, ps_global
)
2348 && orig_new
< f
->new)
2350 (F_OFF(F_INCOMING_CHECKING_RECENT
, ps_global
)
2351 && orig_unseen
< f
->unseen
))){
2352 if(F_ON(F_INCOMING_CHECKING_RECENT
, ps_global
))
2353 q_status_message3(SM_ASYNC
, 1, 3, "%s: %s %s",
2354 FLDR_NAME(f
), comatose(f
->new),
2357 q_status_message3(SM_ASYNC
, 1, 3, "%s: %s %s",
2358 FLDR_NAME(f
), comatose(f
->unseen
),
2364 f
->last_unseen_update
= LUU_NOMORECHK
; /* no further checking */
2370 update_folder_unseen_by_stream(MAILSTREAM
*strm
, unsigned long flags
)
2374 char mailbox_name
[MAILTMPLEN
], *target
;
2375 char *cn
, tmp
[MAILTMPLEN
];
2379 * Attempt to figure out which incoming folder this stream
2380 * is open to, if any, so we can update the unseen counters.
2383 && F_ON(F_ENABLE_INCOMING_CHECKING
, ps_global
)
2384 && (ctxt
=ps_global
->context_list
) && ctxt
->use
& CNTXT_INCMNG
2385 && (ftotal
= folder_total(FOLDERS(ctxt
)))){
2386 for(i
= 0; i
< ftotal
; i
++){
2387 f
= folder_entry(i
, FOLDERS(ctxt
));
2388 context_apply(mailbox_name
, ctxt
, f
->name
, MAILTMPLEN
);
2390 if((check_for_move_mbox(mailbox_name
, NULL
, 0, &target
)
2392 && (!strcmp(target
,strm
->mailbox
)
2393 || !strcmp(target
,strm
->original_mailbox
)))
2394 || same_stream_and_mailbox(mailbox_name
, strm
)
2395 || (!IS_REMOTE(mailbox_name
) && (cn
=mailboxfile(tmp
,mailbox_name
)) && (*cn
) && (!strcmp(cn
, strm
->mailbox
) || !strcmp(cn
, strm
->original_mailbox
)))){
2396 /* if we failed earlier on this one, give it another go */
2397 if(f
->last_unseen_update
== LUU_NOMORECHK
)
2398 init_incoming_unseen_data(ps_global
, f
);
2400 update_folder_unseen(f
, ctxt
, flags
, strm
);
2409 * Find the number of new, unseen, and the total number of
2410 * messages in mailbox_name.
2411 * If the corresponding arg is NULL it will skip the work
2412 * necessary for that flag.
2414 * Returns 1 if successful, 0 if not.
2417 get_recent_in_folder(char *mailbox_name
, long unsigned int *new,
2418 long unsigned int *unseen
, long unsigned int *total
,
2419 MAILSTREAM
*this_is_the_stream
)
2421 MAILSTREAM
*strm
= NIL
;
2422 unsigned long tot
, nw
, uns
;
2425 char *target
= NULL
;
2427 long excluded
, flags
;
2428 extern MAILSTATUS mm_status_result
;
2430 dprint((9, "get_recent_in_folder(%s)", mailbox_name
? mailbox_name
: "?"));
2432 if(check_for_move_mbox(mailbox_name
, NULL
, 0, &target
)){
2437 * If this maildrop is the currently open stream use that.
2440 && ((strm
=ps_global
->mail_stream
)
2442 && (!strcmp(target
,strm
->mailbox
)
2443 || !strcmp(target
,strm
->original_mailbox
)))){
2445 msgmap
= sp_msgmap(strm
);
2446 excluded
= any_lflagged(msgmap
, MN_EXLD
);
2448 tot
= strm
->nmsgs
- excluded
;
2451 if(sp_recent_since_visited(strm
) == 0)
2454 nw
= count_flagged(strm
, F_RECENT
| F_UNSEEN
| F_UNDEL
);
2458 uns
= count_flagged(strm
, F_UNSEEN
| F_UNDEL
);
2465 /* else fall through to just open it case */
2468 /* do we already have it selected? */
2470 && ((strm
= this_is_the_stream
)
2471 || (strm
= sp_stream_get(mailbox_name
, SP_MATCH
| SP_RO_OK
))
2472 || (!IS_REMOTE(mailbox_name
)
2473 && (strm
= already_open_stream(mailbox_name
, AOS_NONE
))))){
2477 * Unfortunately, we have to worry about excluded
2478 * messages. The user doesn't want to have
2479 * excluded messages count in the totals, especially
2480 * recent excluded messages.
2483 msgmap
= sp_msgmap(strm
);
2484 excluded
= any_lflagged(msgmap
, MN_EXLD
);
2486 tot
= strm
->nmsgs
- excluded
;
2489 if(sp_recent_since_visited(strm
) == 0)
2492 nw
= count_flagged(strm
, F_RECENT
| F_UNSEEN
| F_UNDEL
);
2496 uns
= count_flagged(strm
, F_UNSEEN
| F_UNDEL
);
2504 * No, but how about another stream to same server which
2505 * could be used for a STATUS command?
2507 else if(!gotit
&& (strm
= sp_stream_get(mailbox_name
, SP_SAME
))
2508 && modern_imap_stream(strm
)){
2512 flags
|= SA_MESSAGES
;
2520 mm_status_result
.flags
= 0L;
2522 pine_mail_status(strm
, mailbox_name
, flags
);
2524 if(mm_status_result
.flags
& SA_MESSAGES
){
2525 tot
= mm_status_result
.messages
;
2530 if(!(total
&& !gotit
)){
2532 if(mm_status_result
.flags
& SA_RECENT
){
2533 nw
= mm_status_result
.recent
;
2541 if(!((total
|| new) && !gotit
)){
2543 if(mm_status_result
.flags
& SA_UNSEEN
){
2544 uns
= mm_status_result
.unseen
;
2553 /* Let's just Select it. */
2559 * Traditional unix folders don't notice new mail if
2560 * they are opened readonly. So maildrops with unix folder
2561 * targets will snarf to the file but the stream that is
2562 * opened won't see the new mail. So make all maildrop
2563 * opens non-readonly here.
2565 openflags
= SP_USEPOOL
| SP_TEMPUSE
| (maildrop
? 0 : OP_READONLY
);
2567 saved_timeout
= (long) mail_parameters(NULL
, GET_OPENTIMEOUT
, NULL
);
2568 mail_parameters(NULL
, SET_OPENTIMEOUT
, (void *) (long) ps_global
->inc_check_timeout
);
2569 strm
= pine_mail_open(NULL
, mailbox_name
, openflags
, NULL
);
2570 mail_parameters(NULL
, SET_OPENTIMEOUT
, (void *) saved_timeout
);
2574 msgmap
= sp_msgmap(strm
);
2575 excluded
= any_lflagged(msgmap
, MN_EXLD
);
2577 tot
= strm
->nmsgs
- excluded
;
2580 if(sp_recent_since_visited(strm
) == 0)
2583 nw
= count_flagged(strm
, F_RECENT
| F_UNSEEN
| F_UNDEL
);
2587 uns
= count_flagged(strm
, F_UNSEEN
| F_UNDEL
);
2594 pine_mail_close(strm
);
2614 clear_incoming_valid_bits(void)
2620 if(F_ON(F_ENABLE_INCOMING_CHECKING
, ps_global
)
2621 && (ctxt
=ps_global
->context_list
) && ctxt
->use
& CNTXT_INCMNG
2622 && (ftotal
= folder_total(FOLDERS(ctxt
))))
2623 for(i
= 0; i
< ftotal
; i
++){
2624 f
= folder_entry(i
, FOLDERS(ctxt
));
2625 init_incoming_unseen_data(ps_global
, f
);
2631 selected_folders(CONTEXT_S
*context
)
2635 n
= folder_total(FOLDERS(context
));
2636 for(total
= i
= 0; i
< n
; i
++)
2637 if(folder_entry(i
, FOLDERS(context
))->selected
)
2649 selp
= (SELECTED_S
*)fs_get(sizeof(SELECTED_S
));
2651 selp
->reference
= NULL
;
2652 selp
->folders
= NULL
;
2660 * Free the current selected struct and all of the
2661 * following structs in the list
2664 free_selected(SELECTED_S
**selp
)
2666 if(!selp
|| !(*selp
))
2669 free_selected(&((*selp
)->sub
));
2671 free_strlist(&(*selp
)->folders
);
2672 if((*selp
)->reference
)
2673 fs_give((void **) &(*selp
)->reference
);
2675 fs_give((void **) selp
);
2680 folder_select_preserve(CONTEXT_S
*context
)
2683 && !(context
->use
& CNTXT_PARTFIND
)){
2686 SELECTED_S
*selp
= &context
->selected
;
2689 if(!context
->dir
->ref
){
2690 if(!context
->selected
.folders
)
2691 slpp
= &context
->selected
.folders
;
2696 if(!selected_folders(context
))
2701 if(!strcmp(selp
->reference
, context
->dir
->ref
))
2704 selp
->sub
= new_selected();
2706 slpp
= &(selp
->folders
);
2709 folder_n
= folder_total(FOLDERS(context
));
2711 for(i
= 0; i
< folder_n
; i
++)
2712 if((fp
= folder_entry(i
, FOLDERS(context
)))->selected
){
2713 *slpp
= new_strlist(fp
->name
);
2714 slpp
= &(*slpp
)->next
;
2717 /* Only remember "ref" if any folders were selected */
2718 if(selp
->folders
&& context
->dir
->ref
)
2719 selp
->reference
= cpystr(context
->dir
->ref
);
2721 selp
->zoomed
= (context
->use
& CNTXT_ZOOM
) != 0;
2727 folder_select_restore(CONTEXT_S
*context
)
2732 && !(context
->use
& CNTXT_PARTFIND
)){
2734 SELECTED_S
*selp
, *pselp
= NULL
;
2737 selp
= &(context
->selected
);
2739 if(context
->dir
->ref
){
2742 while(selp
&& strcmp(selp
->reference
, context
->dir
->ref
)){
2750 found
= selp
->folders
!= 0;
2752 for(slp
= selp
->folders
; slp
; slp
= slp
->next
)
2754 && (i
= folder_index(slp
->name
, context
, FI_FOLDER
)) >= 0){
2755 folder_entry(i
, FOLDERS(context
))->selected
= 1;
2759 /* Used, always clean them up */
2760 free_strlist(&selp
->folders
);
2762 fs_give((void **) &selp
->reference
);
2765 context
->use
|= CNTXT_ZOOM
;
2768 if(!(selp
== &context
->selected
)){
2770 pselp
->sub
= selp
->sub
;
2771 fs_give((void **) &selp
);