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-2014 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 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
)))
895 snprintf(buf
, buflen
-(buf
-origbuf
), "%s%s%s",
896 (!nntp
&& *p
) ? "#public" : "#news.",
897 (!nntp
&& *p
&& *bboard
!= '/') ? "/" : "",
908 * build_folder_list - call mail_list to fetch us a list of folders
909 * from the given context.
912 build_folder_list(MAILSTREAM
**stream
, CONTEXT_S
*context
, char *pat
, char *content
, int flags
)
916 int local_open
= 0, we_cancel
= 0, resort
= 0;
917 char reference
[2*MAILTMPLEN
], *p
;
919 if(!(context
->dir
->status
& CNTXT_NOFIND
)
920 || (context
->dir
->status
& CNTXT_PARTFIND
))
921 return; /* find already done! */
923 dprint((7, "build_folder_list: %s %s\n",
924 context
? context
->context
: "NULL",
925 pat
? pat
: "NULL"));
927 we_cancel
= busy_cue(NULL
, NULL
, 1);
930 * Set up the pattern of folder name's to match within the
933 if(!pat
|| ((*pat
== '*' || *pat
== '%') && *(pat
+1) == '\0')){
934 context
->dir
->status
&= ~CNTXT_NOFIND
; /* let'em know we tried */
935 pat
= context
->dir
->view
.internal
;
938 context
->use
|= CNTXT_PARTFIND
; /* or are in a partial find */
940 memset(mm_list_info
= &ldata
, 0, sizeof(MM_LIST_S
));
941 ldata
.filter
= (NEWS_TEST(context
)) ? mail_lsub_filter
: mail_list_filter
;
942 memset(ldata
.data
= &response
, 0, sizeof(BFL_DATA_S
));
943 response
.list
= FOLDERS(context
);
945 if(flags
& BFL_FLDRONLY
)
946 response
.mask
= LATT_NOSELECT
;
949 * if context is associated with a server, prepare a stream for
950 * sending our request.
952 if(*context
->context
== '{'){
954 * Try using a stream we've already got open...
957 && !(ldata
.stream
= same_stream(context
->context
, *stream
))){
958 pine_mail_close(*stream
);
963 ldata
.stream
= sp_stream_get(context
->context
, SP_MATCH
);
966 ldata
.stream
= sp_stream_get(context
->context
, SP_SAME
);
968 /* gotta open a new one? */
969 if((F_OFF(F_CMBND_FOLDER_DISP
, ps_global
)
970 || context
->update
== LUU_INIT
) && !ldata
.stream
){
971 ldata
.stream
= mail_cmd_stream(context
, &local_open
);
973 *stream
= ldata
.stream
;
976 dprint((ldata
.stream
? 7 : 1, "build_folder_list: mail_open(%s) %s.\n",
977 context
->server
? context
->server
: "?",
978 ldata
.stream
? "OK" : "FAILED"));
981 context
->use
&= ~CNTXT_PARTFIND
; /* unset partial find bit */
982 context
->update
= LUU_NOMORECHK
;
989 else if(stream
&& *stream
){ /* no server, simple case */
990 if(!sp_flagged(*stream
, SP_LOCKED
))
991 pine_mail_close(*stream
);
997 * If preset reference string, we're somewhere in the hierarchy.
998 * ELSE we must be at top of context...
1000 response
.args
.name
= pat
;
1001 if(!(response
.args
.reference
= context
->dir
->ref
)){
1002 if((p
= strstr(context
->context
, "%s")) != NULL
){
1003 strncpy(response
.args
.reference
= reference
,
1005 MIN(p
- context
->context
, sizeof(reference
)/2));
1006 reference
[MIN(p
- context
->context
, sizeof(reference
)/2)] = '\0';
1008 response
.args
.tail
= p
;
1011 response
.args
.reference
= context
->context
;
1014 if(flags
& BFL_SCAN
)
1015 mail_scan(ldata
.stream
, response
.args
.reference
,
1016 response
.args
.name
, content
);
1017 else if(flags
& BFL_LSUB
)
1018 mail_lsub(ldata
.stream
, response
.args
.reference
, response
.args
.name
);
1020 set_read_predicted(1);
1021 pine_mail_list(ldata
.stream
, response
.args
.reference
, response
.args
.name
,
1023 set_read_predicted(0);
1026 context
->update
= LUU_INIT
;
1027 if(context
->dir
&& response
.response
.delim
)
1028 context
->dir
->delim
= response
.response
.delim
;
1030 if(!(flags
& (BFL_LSUB
|BFL_SCAN
)) && F_ON(F_QUELL_EMPTY_DIRS
, ps_global
)){
1035 for(i
= 0; i
< folder_total(response
.list
); i
++)
1036 if((f
= folder_entry(i
, FOLDERS(context
)))->isdir
){
1038 * we don't need to do a list if we know already
1039 * whether there are children or not.
1041 if(!f
->haschildren
&& !f
->hasnochildren
){
1042 memset(ldata
.data
= &listres
, 0, sizeof(LISTRES_S
));
1043 ldata
.filter
= mail_list_response
;
1045 if(context
->dir
->ref
)
1046 snprintf(reference
, sizeof(reference
), "%s%s", context
->dir
->ref
, f
->name
);
1048 context_apply(reference
, context
, f
->name
, sizeof(reference
)/2);
1050 /* append the delimiter to the reference */
1051 for(p
= reference
; *p
; p
++)
1054 *p
++ = context
->dir
->delim
;
1057 pine_mail_list(ldata
.stream
, reference
, "%", NULL
);
1059 /* anything interesting inside? */
1060 f
->hasnochildren
= (listres
.count
<= 1L);
1061 f
->haschildren
= !f
->hasnochildren
;;
1064 if(f
->hasnochildren
){
1067 if(ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_FIRST
1068 || ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_LAST
)
1072 * We don't want to hide directories
1073 * that are only directories even if they are
1074 * empty. We only want to hide the directory
1075 * piece of a dual-use folder when there are
1076 * no children in the directory (and the user
1077 * most likely thinks of it as a folder instead
1078 * of a folder and a directory).
1080 else if(f
->isdir
&& f
->isdual
){
1081 folder_delete(i
, FOLDERS(context
));
1089 resort_folder_list(response
.list
);
1091 if(local_open
&& !stream
)
1092 pine_mail_close(ldata
.stream
);
1094 if(context
->use
& CNTXT_PRESRV
)
1095 folder_select_restore(context
);
1097 context
->use
&= ~CNTXT_PARTFIND
; /* unset partial list bit */
1099 cancel_busy_cue(-1);
1104 * Validate LIST response to command issued from build_folder_list
1108 mail_list_filter(MAILSTREAM
*stream
, char *mailbox
, int delim
, long int attribs
, void *data
, unsigned int options
)
1110 BFL_DATA_S
*ld
= (BFL_DATA_S
*) data
;
1111 FOLDER_S
*new_f
= NULL
, *dual_f
= NULL
;
1112 int suppress_folder_add
= 0;
1118 ld
->response
.delim
= delim
;
1120 /* test against mask of DIS-allowed attributes */
1121 if((ld
->mask
& attribs
)){
1122 dprint((3, "mail_list_filter: failed attribute test"));
1127 * First, make sure response fits our "reference" arg
1128 * NOTE: build_folder_list can't supply breakout?
1130 if(!mail_list_in_collection(&mailbox
, ld
->args
.reference
,
1131 ld
->args
.name
, ld
->args
.tail
))
1134 /* ignore dotfolders unless told not to */
1135 if(F_OFF(F_ENABLE_DOT_FOLDERS
, ps_global
) && *mailbox
== '.'){
1136 dprint((3, "mail_list_filter: dotfolder disallowed"));
1141 * If this response is INBOX we have to handle it specially.
1142 * The special cases are:
1144 * Incoming folders are enabled and this INBOX is in the Incoming
1145 * folders list already. We don't want to list it here, as well,
1146 * unless it is also a directory.
1148 * Incoming folders are not enabled, but we inserted INBOX into
1149 * this primary collection (with init_inbox_mapping()) so it is
1150 * already accounted for unless it is also a directory.
1152 * Incoming folders are not enabled, but we inserted INBOX into
1153 * the primary collection (which we are not looking at here) so
1154 * it is already accounted for there. We'll add it as a directory
1155 * as well here if that is what is called for.
1157 if(!strucmp(mailbox
, ps_global
->inbox_name
)){
1160 char fullname
[1000], tmp1
[1000], tmp2
[1000], *l1
, *l2
;
1162 /* fullname is the name of the mailbox in the list response */
1163 if(ld
->args
.reference
){
1164 strncpy(fullname
, ld
->args
.reference
, sizeof(fullname
)-1);
1165 fullname
[sizeof(fullname
)-1] = '\0';
1170 strncat(fullname
, mailbox
, sizeof(fullname
)-strlen(fullname
)-1);
1171 fullname
[sizeof(fullname
)-1] = '\0';
1173 /* check if Incoming Folders are enabled */
1174 if(ps_global
->context_list
&& ps_global
->context_list
->use
& CNTXT_INCMNG
){
1175 int this_inbox_is_in_incoming
= 0;
1178 * Figure out if this INBOX is already in the Incoming list.
1181 /* compare fullname to each incoming folder */
1182 ftotal
= folder_total(FOLDERS(ps_global
->context_list
));
1183 for(i
= 0; i
< ftotal
&& !this_inbox_is_in_incoming
; i
++){
1184 f
= folder_entry(i
, FOLDERS(ps_global
->context_list
));
1186 if(same_remote_mailboxes(fullname
, f
->name
)
1187 || ((!IS_REMOTE(fullname
) && !IS_REMOTE(f
->name
))
1188 && (l1
=mailboxfile(tmp1
,fullname
))
1189 && (l2
=mailboxfile(tmp2
,f
->name
))
1191 this_inbox_is_in_incoming
++;
1195 if(this_inbox_is_in_incoming
){
1197 * Don't add a folder for this, only a directory if called for.
1198 * If it isn't a directory, skip it.
1200 if(!(delim
&& !(attribs
& LATT_NOINFERIORS
)))
1203 suppress_folder_add
++;
1207 int inbox_is_in_this_collection
= 0;
1209 /* is INBOX already in this folder list? */
1210 ftotal
= folder_total(ld
->list
);
1211 for(i
= 0; i
< ftotal
&& !inbox_is_in_this_collection
; i
++){
1212 f
= folder_entry(i
, ld
->list
);
1213 if(!strucmp(FLDR_NAME(f
), ps_global
->inbox_name
))
1214 inbox_is_in_this_collection
++;
1217 if(inbox_is_in_this_collection
){
1220 * Inbox is already inserted in this collection. Unless
1221 * it is also a directory, we are done.
1223 if(!(delim
&& !(attribs
& LATT_NOINFERIORS
)))
1227 * Since it is also a directory, what we do depends on
1228 * the F_SEP feature. If that feature is not set we just
1229 * want to mark the existing entry dual-use. If F_SEP is
1230 * set we want to add a new directory entry.
1232 if(F_ON(F_SEPARATE_FLDR_AS_DIR
, ps_global
)){
1235 f
->hasnochildren
= 0;
1236 /* fall through and add a new directory */
1239 /* mark existing entry dual-use and return */
1240 ld
->response
.count
++;
1241 ld
->response
.isdir
= 1;
1243 if(attribs
& LATT_HASCHILDREN
)
1246 if(attribs
& LATT_HASNOCHILDREN
)
1247 f
->hasnochildren
= 1;
1252 suppress_folder_add
++;
1256 int this_inbox_is_primary_inbox
= 0;
1259 * See if this INBOX is the same as the INBOX we inserted
1260 * in the primary collection.
1263 /* first find the existing INBOX entry */
1264 ftotal
= folder_total(FOLDERS(ps_global
->context_list
));
1265 for(i
= 0; i
< ftotal
&& !found_it
; i
++){
1266 f
= folder_entry(i
, FOLDERS(ps_global
->context_list
));
1267 if(!strucmp(FLDR_NAME(f
), ps_global
->inbox_name
))
1271 if(found_it
&& f
&& f
->name
){
1272 if(same_remote_mailboxes(fullname
, f
->name
)
1273 || ((!IS_REMOTE(fullname
) && !IS_REMOTE(f
->name
))
1274 && (l1
=mailboxfile(tmp1
,fullname
))
1275 && (l2
=mailboxfile(tmp2
,f
->name
))
1277 this_inbox_is_primary_inbox
++;
1280 if(this_inbox_is_primary_inbox
){
1282 * Don't add a folder for this, only a directory if called for.
1283 * If it isn't a directory, skip it.
1285 if(!(delim
&& !(attribs
& LATT_NOINFERIORS
)))
1288 suppress_folder_add
++;
1294 /* is it a mailbox? */
1295 if(!(attribs
& LATT_NOSELECT
) && !suppress_folder_add
){
1296 ld
->response
.count
++;
1297 ld
->response
.isfile
= 1;
1298 new_f
= new_folder(mailbox
, 0);
1299 new_f
->isfolder
= 1;
1301 if(F_ON(F_SEPARATE_FLDR_AS_DIR
, ps_global
)){
1302 folder_insert(-1, new_f
, ld
->list
);
1309 if(delim
&& !(attribs
& LATT_NOINFERIORS
)){
1310 ld
->response
.count
++;
1311 ld
->response
.isdir
= 1;
1314 new_f
= new_folder(mailbox
, 0);
1317 if(attribs
& LATT_HASCHILDREN
)
1318 new_f
->haschildren
= 1;
1319 if(attribs
& LATT_HASNOCHILDREN
)
1320 new_f
->hasnochildren
= 1;
1323 * When we have F_SEPARATE_FLDR_AS_DIR we still want to know
1324 * whether the name really represents both so that we don't
1325 * inadvertently delete both when the user meant one or the
1329 if(attribs
& LATT_HASCHILDREN
)
1330 dual_f
->haschildren
= 1;
1332 if(attribs
& LATT_HASNOCHILDREN
)
1333 dual_f
->hasnochildren
= 1;
1341 folder_insert(-1, new_f
, ld
->list
);
1343 if(attribs
& LATT_MARKED
)
1344 ld
->response
.ismarked
= 1;
1346 /* don't mark #move folders unmarked */
1347 if(attribs
& LATT_UNMARKED
&& !(options
& PML_IS_MOVE_MBOX
))
1348 ld
->response
.unmarked
= 1;
1350 if(attribs
& LATT_HASCHILDREN
)
1351 ld
->response
.haschildren
= 1;
1353 if(attribs
& LATT_HASNOCHILDREN
)
1354 ld
->response
.hasnochildren
= 1;
1359 * Validate LSUB response to command issued from build_folder_list
1363 mail_lsub_filter(MAILSTREAM
*stream
, char *mailbox
, int delim
, long int attribs
,
1364 void *data
, unsigned int options
)
1366 BFL_DATA_S
*ld
= (BFL_DATA_S
*) data
;
1367 FOLDER_S
*new_f
= NULL
;
1371 ld
->response
.delim
= delim
;
1373 /* test against mask of DIS-allowed attributes */
1374 if((ld
->mask
& attribs
)){
1375 dprint((3, "mail_lsub_filter: failed attribute test"));
1379 /* Normalize mailbox and reference strings re: namespace */
1380 if(!strncmp(mailbox
, "#news.", 6))
1383 if(!strncmp(ref
= ld
->args
.reference
, "#news.", 6))
1386 if(!mail_list_in_collection(&mailbox
, ref
, ld
->args
.name
, ld
->args
.tail
))
1389 if(!(attribs
& LATT_NOSELECT
)){
1390 ld
->response
.count
++;
1391 ld
->response
.isfile
= 1;
1392 new_f
= new_folder(mailbox
, 0);
1393 new_f
->isfolder
= 1;
1394 folder_insert(F_ON(F_READ_IN_NEWSRC_ORDER
, ps_global
)
1395 ? folder_total(ld
->list
) : -1,
1399 /* We don't support directories in #news */
1402 /* human readable name for a folder. memory freed by caller
1403 * This is a jacket to conversion from modified utf7 to utf8.
1405 unsigned char *folder_name_decoded(unsigned char *mailbox
)
1408 s
= (unsigned char *) utf8_from_mutf7((unsigned char *) mailbox
);
1409 if (s
== NULL
) s
= (unsigned char *) cpystr(mailbox
);
1413 /* mutf7 encoded name of a folder, from its name in utf8.
1414 * memory freed by caller.
1416 unsigned char *folder_name_encoded(unsigned char *mailbox
)
1419 s
= (char *) utf8_to_mutf7(mailbox
);
1420 if (s
== NULL
) s
= cpystr(mailbox
);
1425 mail_list_in_collection(char **mailbox
, char *ref
, char *name
, char *tail
)
1427 int boxlen
, reflen
, taillen
;
1430 boxlen
= strlen(*mailbox
);
1431 reflen
= ref
? strlen(ref
) : 0;
1432 taillen
= tail
? strlen(tail
) : 0;
1435 && (reflen
? !struncmp(*mailbox
, ref
, reflen
)
1436 : (p
= strpbrk(name
, "%*"))
1437 ? !struncmp(*mailbox
, name
, p
- name
)
1438 : !strucmp(*mailbox
,name
))
1440 || (taillen
< boxlen
- reflen
1441 && !strucmp(&(*mailbox
)[boxlen
- taillen
], tail
)))){
1443 (*mailbox
)[boxlen
- taillen
] = '\0';
1445 if(*(*mailbox
+= reflen
))
1449 * else don't worry about context "breakouts" since
1450 * build_folder_list doesn't let the user introduce
1459 * rebuild_folder_list -- free up old list and re-issue commands to build
1463 refresh_folder_list(CONTEXT_S
*context
, int nodirs
, int startover
, MAILSTREAM
**streamp
)
1466 free_folder_list(context
);
1468 build_folder_list(streamp
, context
, NULL
, NULL
,
1469 (NEWS_TEST(context
) ? BFL_LSUB
: BFL_NONE
)
1470 | ((nodirs
) ? BFL_FLDRONLY
: BFL_NONE
));
1475 * free_folder_list - loop thru the context's lists of folders
1476 * clearing all entries without nicknames
1477 * (as those were user provided) AND reset the
1478 * context's find flag.
1480 * NOTE: if fetched() information (e.g., like message counts come back
1481 * in bboard collections), we may want to have a check before
1482 * executing the loop and setting the FIND flag.
1485 free_folder_list(CONTEXT_S
*cntxt
)
1490 * In this case, don't blast the list as it was given to us by the
1491 * user and not the result of a mail_list call...
1493 if(cntxt
->use
& CNTXT_INCMNG
)
1496 if(cntxt
->use
& CNTXT_PRESRV
)
1497 folder_select_preserve(cntxt
);
1499 for(n
= folder_total(FOLDERS(cntxt
)), i
= 0; n
> 0; n
--)
1500 if(folder_entry(i
, FOLDERS(cntxt
))->nickname
)
1501 i
++; /* entry wasn't from LIST */
1503 folder_delete(i
, FOLDERS(cntxt
));
1505 cntxt
->dir
->status
|= CNTXT_NOFIND
; /* do find next time... */
1506 /* or add the fake entry */
1507 cntxt
->use
&= ~(CNTXT_PSEUDO
| CNTXT_PRESRV
| CNTXT_ZOOM
);
1512 * default_save_context - return the default context for saved messages
1515 default_save_context(CONTEXT_S
*cntxt
)
1518 if((cntxt
->use
) & CNTXT_SAVEDFLT
)
1521 cntxt
= cntxt
->next
;
1529 * folder_complete - foldername completion routine
1531 * Result: returns 0 if the folder doesn't have a any completetion
1532 * 1 if the folder has a completion (*AND* "name" is
1533 * replaced with the completion)
1537 folder_complete(CONTEXT_S
*context
, char *name
, size_t namelen
, int *completions
)
1539 return(folder_complete_internal(context
, name
, namelen
, completions
, FC_NONE
));
1547 folder_complete_internal(CONTEXT_S
*context
, char *name
, size_t namelen
,
1548 int *completions
, int flags
)
1550 int i
, match
= -1, ftotal
;
1551 char tmp
[MAXFOLDER
+2], *a
, *b
, *fn
, *pat
;
1557 if(*name
== '\0' || !context_isambig(name
))
1560 if(!((context
->use
& CNTXT_INCMNG
) || ALL_FOUND(context
))){
1562 * Build the folder list from scratch since we may need to
1563 * traverse hierarchy...
1566 free_folder_list(context
);
1567 snprintf(tmp
, sizeof(tmp
), "%s%c", name
, NEWS_TEST(context
) ? '*' : '%');
1568 build_folder_list(NULL
, context
, tmp
, NULL
,
1569 (NEWS_TEST(context
) & !(flags
& FC_FORCE_LIST
))
1570 ? BFL_LSUB
: BFL_NONE
);
1573 *tmp
= '\0'; /* find uniq substring */
1574 ftotal
= folder_total(FOLDERS(context
));
1575 for(i
= 0; i
< ftotal
; i
++){
1576 f
= folder_entry(i
, FOLDERS(context
));
1579 if(!(NEWS_TEST(context
) || (context
->use
& CNTXT_INCMNG
))){
1580 fn
= folder_last_cmpnt(fn
, context
->dir
->delim
);
1581 pat
= folder_last_cmpnt(pat
, context
->dir
->delim
);
1584 if(!strncmp(fn
, pat
, strlen(pat
))){
1585 if(match
!= -1){ /* oh well, do best we can... */
1588 f
= folder_entry(match
, FOLDERS(context
));
1590 if(!NEWS_TEST(context
))
1591 fn
= folder_last_cmpnt(fn
, context
->dir
->delim
);
1593 strncpy(tmp
, fn
, sizeof(tmp
)-1);
1594 tmp
[sizeof(tmp
)-1] = '\0';
1598 b
= tmp
; /* remember largest common text */
1599 while(*a
&& *b
&& *a
== *b
)
1605 match
= i
; /* bingo?? */
1609 if(match
>= 0){ /* found! */
1610 f
= folder_entry(match
, FOLDERS(context
));
1612 if(!(NEWS_TEST(context
) || (context
->use
& CNTXT_INCMNG
)))
1613 fn
= folder_last_cmpnt(fn
, context
->dir
->delim
);
1615 strncpy(pat
, fn
, namelen
-(pat
-name
));
1616 name
[namelen
-1] = '\0';
1617 if(f
->isdir
&& !f
->isfolder
){
1618 name
[i
= strlen(name
)] = context
->dir
->delim
;
1622 else if(match
== -2){ /* closest we could find */
1623 strncpy(pat
, tmp
, namelen
-(pat
-name
));
1624 name
[namelen
-1] = '\0';
1628 *completions
= ftotal
;
1630 if(!((context
->use
& CNTXT_INCMNG
) || ALL_FOUND(context
)))
1631 free_folder_list(context
);
1633 return((match
>= 0) ? ftotal
: 0);
1638 folder_last_cmpnt(char *s
, int d
)
1643 for(p
= s
; (p
= strindex(p
, d
)); s
= ++p
)
1651 * init_folder_entries - return a piece of memory suitable for attaching
1652 * a list of folders...
1656 init_folder_entries(void)
1658 FLIST
*flist
= (FLIST
*) fs_get(sizeof(FLIST
));
1659 flist
->folders
= (FOLDER_S
**) fs_get(FCHUNK
* sizeof(FOLDER_S
*));
1660 memset((void *)flist
->folders
, 0, (FCHUNK
* sizeof(FOLDER_S
*)));
1661 flist
->allocated
= FCHUNK
;
1668 * new_folder - return a brand new folder entry, with the given name
1671 * NOTE: THIS IS THE ONLY WAY TO PUT A NAME INTO A FOLDER ENTRY!!!
1672 * STRCPY WILL NOT WORK!!!
1675 new_folder(char *name
, long unsigned int hash
)
1678 size_t l
= strlen(name
);
1680 tmp
= (FOLDER_S
*)fs_get(sizeof(FOLDER_S
) + (l
* sizeof(char)));
1681 memset((void *)tmp
, 0, sizeof(FOLDER_S
));
1682 strncpy(tmp
->name
, name
, l
);
1683 tmp
->name
[l
] = '\0';
1684 tmp
->name_len
= (unsigned char) l
;
1685 tmp
->varhash
= hash
;
1691 * folder_entry - folder struct access routine. Permit reference to
1692 * folder structs via index number. Serves two purposes:
1693 * 1) easy way for callers to access folder data
1695 * 2) allows for a custom manager to limit memory use
1696 * under certain rather limited "operating systems"
1697 * who shall renameless, but whose initials are DOS
1702 folder_entry(int i
, FLIST
*flist
)
1704 return((i
>= flist
->used
) ? NULL
:flist
->folders
[i
]);
1709 * free_folder_entries - release all resources associated with the given
1710 * list of folder entries
1713 free_folder_entries(FLIST
**flist
)
1717 if(!(flist
&& *flist
))
1722 if((*flist
)->folders
[i
]->nickname
)
1723 fs_give((void **) &(*flist
)->folders
[i
]->nickname
);
1725 fs_give((void **) &((*flist
)->folders
[i
]));
1728 fs_give((void **) &((*flist
)->folders
));
1729 fs_give((void **) flist
);
1734 * return the number of folders associated with the given folder list
1737 folder_total(FLIST
*flist
)
1739 return((int) flist
->used
);
1744 * return the index number of the given name in the given folder list
1747 folder_index(char *name
, CONTEXT_S
*cntxt
, int flags
)
1753 for(i
= 0; (f
= folder_entry(i
, FOLDERS(cntxt
))); i
++)
1754 if(((flags
& FI_FOLDER
) && (f
->isfolder
|| (cntxt
->use
& CNTXT_INCMNG
)))
1755 || ((flags
& FI_DIR
) && f
->isdir
)){
1756 fname
= FLDR_NAME(f
);
1757 #if defined(DOS) || defined(OS2)
1758 if(flags
& FI_RENAME
){ /* case-dependent for rename */
1759 if(*name
== *fname
&& strcmp(name
, fname
) == 0)
1763 if(toupper((unsigned char)(*name
))
1764 == toupper((unsigned char)(*fname
)) && strucmp(name
, fname
) == 0)
1768 if(*name
== *fname
&& strcmp(name
, fname
) == 0)
1778 * folder_is_nick - check to see if the given name is a nickname
1779 * for some folder in the given context...
1781 * NOTE: no need to check if mm_list_names has been done as
1782 * nicknames can only be set by configuration...
1785 folder_is_nick(char *nickname
, FLIST
*flist
, int flags
)
1790 if(!(nickname
&& *nickname
&& flist
))
1793 while((f
= folder_entry(i
, flist
)) != NULL
){
1795 * The second part of the OR is checking in a case-indep
1796 * way for INBOX. It should be restricted to the context
1797 * to which we add the INBOX folder, which would be either
1798 * the Incoming Folders collection or the first collection
1799 * if there is no Incoming collection. We don't need to check
1800 * the collection because nickname assignment has already
1801 * done that for us. Most folders don't have nicknames, only
1802 * incoming folders and folders like inbox if not in incoming.
1805 && (!strcmp(nickname
, f
->nickname
)
1806 || (!strucmp(nickname
, f
->nickname
)
1807 && !strucmp(nickname
, ps_global
->inbox_name
)))){
1808 char source
[MAILTMPLEN
], *target
= NULL
;
1811 * If f is a maildrop, then we want to return the
1812 * destination folder, not the whole #move thing.
1814 if(!(flags
& FN_WHOLE_NAME
)
1815 && check_for_move_mbox(f
->name
, source
, sizeof(source
), &target
))
1829 folder_is_target_of_nick(char *longname
, CONTEXT_S
*cntxt
)
1833 FLIST
*flist
= NULL
;
1835 if(cntxt
&& cntxt
== ps_global
->context_list
)
1836 flist
= FOLDERS(cntxt
);
1838 if(!(longname
&& *longname
&& flist
))
1841 while((f
= folder_entry(i
, flist
)) != NULL
){
1842 if(f
->nickname
&& f
->name
&& !strcmp(longname
, f
->name
))
1843 return(f
->nickname
);
1852 /*----------------------------------------------------------------------
1853 Insert the given folder name into the sorted folder list
1854 associated with the given context. Only allow ambiguous folder
1855 names IF associated with a nickname.
1857 Args: index -- Index to insert at, OR insert in sorted order if -1
1858 folder -- folder structure to insert into list
1859 flist -- folder list to insert folder into
1862 DON'T count on the folder pointer being valid after this returns
1863 *** ALL FOLDER ELEMENT READS SHOULD BE THRU folder_entry() ***
1867 folder_insert(int index
, FOLDER_S
*folder
, FLIST
*flist
)
1869 /* requested index < 0 means add to sorted list */
1870 if(index
< 0 && (index
= folder_total(flist
)) > 0)
1871 index
= folder_insert_sorted(index
/ 2, 0, index
, folder
, flist
,
1872 (ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_FIRST
)
1873 ? compare_folders_dir_alpha
1874 : (ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_LAST
)
1875 ? compare_folders_alpha_dir
1876 : compare_folders_alpha
);
1878 folder_insert_index(folder
, index
, flist
);
1884 * folder_insert_sorted - Insert given folder struct into given list
1885 * observing sorting specified by given
1886 * comparison function
1889 folder_insert_sorted(int index
, int min_index
, int max_index
, FOLDER_S
*folder
,
1890 FLIST
*flist
, int (*compf
)(FOLDER_S
*, FOLDER_S
*))
1894 return(((i
= (*compf
)(folder_entry(index
, flist
), folder
)) == 0)
1897 ? ((++index
>= max_index
)
1899 : ((*compf
)(folder_entry(index
, flist
), folder
) > 0)
1901 : folder_insert_sorted((index
+ max_index
) / 2, index
,
1902 max_index
, folder
, flist
, compf
))
1903 : ((index
<= min_index
)
1905 : folder_insert_sorted((min_index
+ index
) / 2, min_index
, index
,
1906 folder
, flist
, compf
)));
1911 * folder_insert_index - Insert the given folder struct into the global list
1912 * at the given index.
1915 folder_insert_index(FOLDER_S
*folder
, int index
, FLIST
*flist
)
1917 register FOLDER_S
**flp
, **iflp
;
1919 /* if index is beyond size, place at end of list */
1920 index
= MIN(index
, flist
->used
);
1923 if(flist
->used
+ 1 > flist
->allocated
){
1924 flist
->allocated
+= FCHUNK
;
1925 fs_resize((void **)&(flist
->folders
),
1926 flist
->allocated
* sizeof(FOLDER_S
*));
1929 /* shift array left */
1930 iflp
= &((flist
->folders
)[index
]);
1931 for(flp
= &((flist
->folders
)[flist
->used
]);
1935 flist
->folders
[index
] = folder
;
1941 resort_folder_list(FLIST
*flist
)
1943 if(flist
&& folder_total(flist
) > 1 && flist
->folders
)
1944 qsort(flist
->folders
, folder_total(flist
), sizeof(flist
->folders
[0]),
1945 (ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_FIRST
)
1946 ? compare_folders_dir_alpha_qsort
1947 : (ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_LAST
)
1948 ? compare_folders_alpha_dir_qsort
1949 : compare_folders_alpha_qsort
);
1953 /*----------------------------------------------------------------------
1954 Removes a folder at the given index in the given context's
1957 Args: index -- Index in folder list of folder to be removed
1958 flist -- folder list
1961 folder_delete(int index
, FLIST
*flist
)
1967 && (index
< 0 || index
>= flist
->used
))
1968 return; /* bogus call! */
1970 if((f
= folder_entry(index
, flist
))->nickname
)
1971 fs_give((void **)&(f
->nickname
));
1973 fs_give((void **) &(flist
->folders
[index
]));
1974 for(i
= index
; i
< flist
->used
- 1; i
++)
1975 flist
->folders
[i
] = flist
->folders
[i
+1];
1982 /*----------------------------------------------------------------------
1983 compare two names for qsort, case independent
1985 Args: pointers to strings to compare
1987 Result: integer result of strcmp of the names. Uses simple
1988 efficiency hack to speed the string comparisons up a bit.
1990 ----------------------------------------------------------------------*/
1992 compare_names(const qsort_t
*x
, const qsort_t
*y
)
1994 char *a
= *(char **)x
, *b
= *(char **)y
;
1996 #define CMPI(X,Y) ((X)[0] - (Y)[0])
1997 #define UCMPI(X,Y) ((isupper((unsigned char)((X)[0])) \
1998 ? (X)[0] - 'A' + 'a' : (X)[0]) \
1999 - (isupper((unsigned char)((Y)[0])) \
2000 ? (Y)[0] - 'A' + 'a' : (Y)[0]))
2002 /*---- Inbox always sorts to the top ----*/
2003 if(UCMPI(a
, ps_global
->inbox_name
) == 0
2004 && strucmp(a
, ps_global
->inbox_name
) == 0)
2005 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : -1);
2006 else if((UCMPI(b
, ps_global
->inbox_name
)) == 0
2007 && strucmp(b
, ps_global
->inbox_name
) == 0)
2008 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : 1);
2010 /*----- The sent-mail folder, is always next unless... ---*/
2011 else if(F_OFF(F_SORT_DEFAULT_FCC_ALPHA
, ps_global
)
2012 && CMPI(a
, ps_global
->VAR_DEFAULT_FCC
) == 0
2013 && strcmp(a
, ps_global
->VAR_DEFAULT_FCC
) == 0)
2014 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : -1);
2015 else if(F_OFF(F_SORT_DEFAULT_FCC_ALPHA
, ps_global
)
2016 && CMPI(b
, ps_global
->VAR_DEFAULT_FCC
) == 0
2017 && strcmp(b
, ps_global
->VAR_DEFAULT_FCC
) == 0)
2018 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : 1);
2020 /*----- The saved-messages folder, is always next unless... ---*/
2021 else if(F_OFF(F_SORT_DEFAULT_SAVE_ALPHA
, ps_global
)
2022 && CMPI(a
, ps_global
->VAR_DEFAULT_SAVE_FOLDER
) == 0
2023 && strcmp(a
, ps_global
->VAR_DEFAULT_SAVE_FOLDER
) == 0)
2024 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : -1);
2025 else if(F_OFF(F_SORT_DEFAULT_SAVE_ALPHA
, ps_global
)
2026 && CMPI(b
, ps_global
->VAR_DEFAULT_SAVE_FOLDER
) == 0
2027 && strcmp(b
, ps_global
->VAR_DEFAULT_SAVE_FOLDER
) == 0)
2028 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : 1);
2031 return((r
= CMPI(a
, b
)) ? r
: strcmp(a
, b
));
2035 /*----------------------------------------------------------------------
2036 compare two folder structs for ordering alphabetically
2038 Args: pointers to folder structs to compare
2040 Result: integer result of dir-bit and strcmp of the folders.
2041 ----------------------------------------------------------------------*/
2043 compare_folders_alpha(FOLDER_S
*f1
, FOLDER_S
*f2
)
2046 char *f1name
= FLDR_NAME(f1
),
2047 *f2name
= FLDR_NAME(f2
);
2049 return(((i
= compare_names(&f1name
, &f2name
)) != 0)
2050 ? i
: (f2
->isdir
- f1
->isdir
));
2055 compare_folders_alpha_qsort(const qsort_t
*a1
, const qsort_t
*a2
)
2057 FOLDER_S
*f1
= *((FOLDER_S
**) a1
);
2058 FOLDER_S
*f2
= *((FOLDER_S
**) a2
);
2060 return(compare_folders_alpha(f1
, f2
));
2064 /*----------------------------------------------------------------------
2065 compare two folder structs alphabetically with dirs first
2067 Args: pointers to folder structs to compare
2069 Result: integer result of dir-bit and strcmp of the folders.
2070 ----------------------------------------------------------------------*/
2072 compare_folders_dir_alpha(FOLDER_S
*f1
, FOLDER_S
*f2
)
2076 if((i
= (f2
->isdir
- f1
->isdir
)) == 0){
2077 char *f1name
= FLDR_NAME(f1
),
2078 *f2name
= FLDR_NAME(f2
);
2080 return(compare_names(&f1name
, &f2name
));
2088 compare_folders_dir_alpha_qsort(const qsort_t
*a1
, const qsort_t
*a2
)
2090 FOLDER_S
*f1
= *((FOLDER_S
**) a1
);
2091 FOLDER_S
*f2
= *((FOLDER_S
**) a2
);
2093 return(compare_folders_dir_alpha(f1
, f2
));
2097 /*----------------------------------------------------------------------
2098 compare two folder structs alphabetically with dirs last
2100 Args: pointers to folder structs to compare
2102 Result: integer result of dir-bit and strcmp of the folders.
2103 ----------------------------------------------------------------------*/
2105 compare_folders_alpha_dir(FOLDER_S
*f1
, FOLDER_S
*f2
)
2109 if((i
= (f1
->isdir
- f2
->isdir
)) == 0){
2110 char *f1name
= FLDR_NAME(f1
),
2111 *f2name
= FLDR_NAME(f2
);
2113 return(compare_names(&f1name
, &f2name
));
2121 compare_folders_alpha_dir_qsort(const qsort_t
*a1
, const qsort_t
*a2
)
2123 FOLDER_S
*f1
= *((FOLDER_S
**) a1
);
2124 FOLDER_S
*f2
= *((FOLDER_S
**) a2
);
2126 return(compare_folders_alpha_dir(f1
, f2
));
2131 * Find incoming folders and update the unseen counts
2135 folder_unseen_count_updater(unsigned long flags
)
2138 time_t oldest
, started_checking
;
2139 int ftotal
, i
, first
= -1;
2143 * We would only do this if there is an incoming collection, the
2144 * user wants us to monitor, and we're in the folder screen.
2146 if(ps_global
->in_folder_screen
&& F_ON(F_ENABLE_INCOMING_CHECKING
, ps_global
)
2147 && (ctxt
=ps_global
->context_list
) && ctxt
->use
& CNTXT_INCMNG
2148 && (ftotal
= folder_total(FOLDERS(ctxt
)))){
2150 * Search through the last_unseen_update times to find
2151 * the one that was updated longest ago, and start with
2152 * that one. We don't want to delay long doing these
2153 * checks so may only check some of them each time we
2154 * get called. An update time equal to 1 means don't check
2155 * this folder at all.
2157 for(i
= 0; i
< ftotal
; i
++){
2158 f
= folder_entry(i
, FOLDERS(ctxt
));
2159 if(f
&& LUU_YES(f
->last_unseen_update
)){
2161 oldest
= f
->last_unseen_update
;
2167 * Now first is the first in the list that
2168 * should ever be checked. Next find the
2169 * one that should be checked next, the one
2170 * that was checked longest ago.
2173 for(i
= 1; i
< ftotal
; i
++){
2174 f
= folder_entry(i
, FOLDERS(ctxt
));
2175 if(f
&& LUU_YES(f
->last_unseen_update
) && f
->last_unseen_update
< oldest
){
2177 oldest
= f
->last_unseen_update
;
2182 /* now first is the next one to be checked */
2184 started_checking
= time(0);
2186 for(i
= first
; i
< ftotal
; i
++){
2187 /* update the next one */
2188 f
= folder_entry(i
, FOLDERS(ctxt
));
2189 if(f
&& LUU_YES(f
->last_unseen_update
)
2190 && (flags
& UFU_FORCE
2191 /* or it's been long enough and we've not been in this function too long */
2192 || (((time(0) - f
->last_unseen_update
) >= ps_global
->inc_check_interval
)
2193 && ((time(0) - started_checking
) < MIN(4,ps_global
->inc_check_timeout
)))))
2194 update_folder_unseen(f
, ctxt
, flags
, NULL
);
2197 for(i
= 0; i
< first
; i
++){
2198 f
= folder_entry(i
, FOLDERS(ctxt
));
2199 if(f
&& LUU_YES(f
->last_unseen_update
)
2200 && (flags
& UFU_FORCE
2201 || (((time(0) - f
->last_unseen_update
) >= ps_global
->inc_check_interval
)
2202 && ((time(0) - started_checking
) < MIN(4,ps_global
->inc_check_timeout
)))))
2203 update_folder_unseen(f
, ctxt
, flags
, NULL
);
2210 * Update the count of unseen in the FOLDER_S struct
2211 * for this folder. This will update if the time
2212 * interval has passed or if the FORCE flag is set.
2215 update_folder_unseen(FOLDER_S
*f
, CONTEXT_S
*ctxt
, unsigned long flags
,
2216 MAILSTREAM
*this_is_the_stream
)
2220 int use_imap_interval
= 0;
2221 int stream_is_open
= 0;
2222 unsigned long orig_unseen
, orig_new
, orig_tot
;
2223 char mailbox_name
[MAILTMPLEN
];
2224 char *target
= NULL
;
2227 if(!f
|| !LUU_YES(f
->last_unseen_update
))
2231 context_apply(mailbox_name
, ctxt
, f
->name
, MAILTMPLEN
);
2233 if(!mailbox_name
[0])
2236 if(check_for_move_mbox(mailbox_name
, NULL
, 0, &target
)){
2240 * If this maildrop is the currently open stream use that.
2241 * I'm not altogether sure that this is a good way to
2245 && ((strm
=ps_global
->mail_stream
)
2247 && (!strcmp(target
,strm
->mailbox
)
2248 || !strcmp(target
,strm
->original_mailbox
)))){
2253 MAILSTREAM
*m
= NULL
;
2255 stream_is_open
= (this_is_the_stream
2256 || (m
=sp_stream_get(mailbox_name
, SP_MATCH
| SP_RO_OK
))
2257 || ((m
=ps_global
->mail_stream
) && !sp_dead_stream(m
)
2258 && same_stream_and_mailbox(mailbox_name
,m
))
2259 || (!IS_REMOTE(mailbox_name
)
2260 && (m
=already_open_stream(mailbox_name
, AOS_NONE
)))) ? 1 : 0;
2263 if(!this_is_the_stream
)
2264 this_is_the_stream
= m
;
2268 * If it's IMAP or local we use a shorter interval.
2270 d
= mail_valid(NIL
, mailbox_name
, (char *) NIL
);
2271 if((d
&& !strcmp(d
->name
, "imap")) || !IS_REMOTE(mailbox_name
))
2272 use_imap_interval
++;
2277 * Update if forced, or if it's been a while, or if we have a
2278 * stream open to this mailbox already.
2280 if(flags
& UFU_FORCE
2282 || ((use_imap_interval
2283 && (now
- f
->last_unseen_update
) >= ps_global
->inc_check_interval
)
2284 || ((now
- f
->last_unseen_update
) >= ps_global
->inc_second_check_interval
))){
2285 unsigned long tot
, uns
, new;
2286 unsigned long *totp
= NULL
, *unsp
= NULL
, *newp
= NULL
;
2288 orig_valid
= f
->unseen_valid
;
2289 orig_unseen
= f
->unseen
;
2291 orig_tot
= f
->total
;
2293 if(F_ON(F_INCOMING_CHECKING_RECENT
, ps_global
))
2298 if(F_ON(F_INCOMING_CHECKING_TOTAL
, ps_global
))
2301 f
->unseen_valid
= 0;
2303 dprint((9, "update_folder_unseen(%s)", FLDR_NAME(f
)));
2304 if(get_recent_in_folder(mailbox_name
, newp
, unsp
, totp
, this_is_the_stream
)){
2305 f
->last_unseen_update
= time(0);
2306 f
->unseen_valid
= 1;
2317 dprint((9, "update_folder_unseen(%s): original: %s%s%s%s",
2319 F_ON(F_INCOMING_CHECKING_RECENT
,ps_global
) ? "new=" : "unseen=",
2320 F_ON(F_INCOMING_CHECKING_RECENT
,ps_global
) ? comatose(f
->new) : comatose(f
->unseen
),
2321 F_ON(F_INCOMING_CHECKING_TOTAL
,ps_global
) ? " tot=" : "",
2322 F_ON(F_INCOMING_CHECKING_TOTAL
,ps_global
) ? comatose(f
->total
) : ""));
2326 && ((F_ON(F_INCOMING_CHECKING_RECENT
, ps_global
)
2327 && orig_new
!= f
->new)
2329 (F_OFF(F_INCOMING_CHECKING_RECENT
, ps_global
)
2330 && orig_unseen
!= f
->unseen
)
2332 (F_ON(F_INCOMING_CHECKING_TOTAL
, ps_global
)
2333 && orig_tot
!= f
->total
))){
2335 if(ps_global
->in_folder_screen
)
2336 ps_global
->noticed_change_in_unseen
= 1;
2338 dprint((9, "update_folder_unseen(%s): changed: %s%s%s%s",
2340 F_ON(F_INCOMING_CHECKING_RECENT
,ps_global
) ? "new=" : "unseen=",
2341 F_ON(F_INCOMING_CHECKING_RECENT
,ps_global
) ? comatose(f
->new) : comatose(f
->unseen
),
2342 F_ON(F_INCOMING_CHECKING_TOTAL
,ps_global
) ? " tot=" : "",
2343 F_ON(F_INCOMING_CHECKING_TOTAL
,ps_global
) ? comatose(f
->total
) : ""));
2345 if(flags
& UFU_ANNOUNCE
2346 && ((F_ON(F_INCOMING_CHECKING_RECENT
, ps_global
)
2347 && orig_new
< f
->new)
2349 (F_OFF(F_INCOMING_CHECKING_RECENT
, ps_global
)
2350 && orig_unseen
< f
->unseen
))){
2351 if(F_ON(F_INCOMING_CHECKING_RECENT
, ps_global
))
2352 q_status_message3(SM_ASYNC
, 1, 3, "%s: %s %s",
2353 FLDR_NAME(f
), comatose(f
->new),
2356 q_status_message3(SM_ASYNC
, 1, 3, "%s: %s %s",
2357 FLDR_NAME(f
), comatose(f
->unseen
),
2363 f
->last_unseen_update
= LUU_NOMORECHK
; /* no further checking */
2369 update_folder_unseen_by_stream(MAILSTREAM
*strm
, unsigned long flags
)
2373 char mailbox_name
[MAILTMPLEN
], *target
;
2374 char *cn
, tmp
[MAILTMPLEN
];
2378 * Attempt to figure out which incoming folder this stream
2379 * is open to, if any, so we can update the unseen counters.
2382 && F_ON(F_ENABLE_INCOMING_CHECKING
, ps_global
)
2383 && (ctxt
=ps_global
->context_list
) && ctxt
->use
& CNTXT_INCMNG
2384 && (ftotal
= folder_total(FOLDERS(ctxt
)))){
2385 for(i
= 0; i
< ftotal
; i
++){
2386 f
= folder_entry(i
, FOLDERS(ctxt
));
2387 context_apply(mailbox_name
, ctxt
, f
->name
, MAILTMPLEN
);
2389 if((check_for_move_mbox(mailbox_name
, NULL
, 0, &target
)
2391 && (!strcmp(target
,strm
->mailbox
)
2392 || !strcmp(target
,strm
->original_mailbox
)))
2393 || same_stream_and_mailbox(mailbox_name
, strm
)
2394 || (!IS_REMOTE(mailbox_name
) && (cn
=mailboxfile(tmp
,mailbox_name
)) && (*cn
) && (!strcmp(cn
, strm
->mailbox
) || !strcmp(cn
, strm
->original_mailbox
)))){
2395 /* if we failed earlier on this one, give it another go */
2396 if(f
->last_unseen_update
== LUU_NOMORECHK
)
2397 init_incoming_unseen_data(ps_global
, f
);
2399 update_folder_unseen(f
, ctxt
, flags
, strm
);
2408 * Find the number of new, unseen, and the total number of
2409 * messages in mailbox_name.
2410 * If the corresponding arg is NULL it will skip the work
2411 * necessary for that flag.
2413 * Returns 1 if successful, 0 if not.
2416 get_recent_in_folder(char *mailbox_name
, long unsigned int *new,
2417 long unsigned int *unseen
, long unsigned int *total
,
2418 MAILSTREAM
*this_is_the_stream
)
2420 MAILSTREAM
*strm
= NIL
;
2421 unsigned long tot
, nw
, uns
;
2424 char *target
= NULL
;
2426 long excluded
, flags
;
2427 extern MAILSTATUS mm_status_result
;
2429 dprint((9, "get_recent_in_folder(%s)", mailbox_name
? mailbox_name
: "?"));
2431 if(check_for_move_mbox(mailbox_name
, NULL
, 0, &target
)){
2436 * If this maildrop is the currently open stream use that.
2439 && ((strm
=ps_global
->mail_stream
)
2441 && (!strcmp(target
,strm
->mailbox
)
2442 || !strcmp(target
,strm
->original_mailbox
)))){
2444 msgmap
= sp_msgmap(strm
);
2445 excluded
= any_lflagged(msgmap
, MN_EXLD
);
2447 tot
= strm
->nmsgs
- excluded
;
2450 if(sp_recent_since_visited(strm
) == 0)
2453 nw
= count_flagged(strm
, F_RECENT
| F_UNSEEN
| F_UNDEL
);
2457 uns
= count_flagged(strm
, F_UNSEEN
| F_UNDEL
);
2464 /* else fall through to just open it case */
2467 /* do we already have it selected? */
2469 && ((strm
= this_is_the_stream
)
2470 || (strm
= sp_stream_get(mailbox_name
, SP_MATCH
| SP_RO_OK
))
2471 || (!IS_REMOTE(mailbox_name
)
2472 && (strm
= already_open_stream(mailbox_name
, AOS_NONE
))))){
2476 * Unfortunately, we have to worry about excluded
2477 * messages. The user doesn't want to have
2478 * excluded messages count in the totals, especially
2479 * recent excluded messages.
2482 msgmap
= sp_msgmap(strm
);
2483 excluded
= any_lflagged(msgmap
, MN_EXLD
);
2485 tot
= strm
->nmsgs
- excluded
;
2488 if(sp_recent_since_visited(strm
) == 0)
2491 nw
= count_flagged(strm
, F_RECENT
| F_UNSEEN
| F_UNDEL
);
2495 uns
= count_flagged(strm
, F_UNSEEN
| F_UNDEL
);
2503 * No, but how about another stream to same server which
2504 * could be used for a STATUS command?
2506 else if(!gotit
&& (strm
= sp_stream_get(mailbox_name
, SP_SAME
))
2507 && modern_imap_stream(strm
)){
2511 flags
|= SA_MESSAGES
;
2519 mm_status_result
.flags
= 0L;
2521 pine_mail_status(strm
, mailbox_name
, flags
);
2523 if(mm_status_result
.flags
& SA_MESSAGES
){
2524 tot
= mm_status_result
.messages
;
2529 if(!(total
&& !gotit
)){
2531 if(mm_status_result
.flags
& SA_RECENT
){
2532 nw
= mm_status_result
.recent
;
2540 if(!((total
|| new) && !gotit
)){
2542 if(mm_status_result
.flags
& SA_UNSEEN
){
2543 uns
= mm_status_result
.unseen
;
2552 /* Let's just Select it. */
2558 * Traditional unix folders don't notice new mail if
2559 * they are opened readonly. So maildrops with unix folder
2560 * targets will snarf to the file but the stream that is
2561 * opened won't see the new mail. So make all maildrop
2562 * opens non-readonly here.
2564 openflags
= SP_USEPOOL
| SP_TEMPUSE
| (maildrop
? 0 : OP_READONLY
);
2566 saved_timeout
= (long) mail_parameters(NULL
, GET_OPENTIMEOUT
, NULL
);
2567 mail_parameters(NULL
, SET_OPENTIMEOUT
, (void *) (long) ps_global
->inc_check_timeout
);
2568 strm
= pine_mail_open(NULL
, mailbox_name
, openflags
, NULL
);
2569 mail_parameters(NULL
, SET_OPENTIMEOUT
, (void *) saved_timeout
);
2573 msgmap
= sp_msgmap(strm
);
2574 excluded
= any_lflagged(msgmap
, MN_EXLD
);
2576 tot
= strm
->nmsgs
- excluded
;
2579 if(sp_recent_since_visited(strm
) == 0)
2582 nw
= count_flagged(strm
, F_RECENT
| F_UNSEEN
| F_UNDEL
);
2586 uns
= count_flagged(strm
, F_UNSEEN
| F_UNDEL
);
2593 pine_mail_close(strm
);
2613 clear_incoming_valid_bits(void)
2619 if(F_ON(F_ENABLE_INCOMING_CHECKING
, ps_global
)
2620 && (ctxt
=ps_global
->context_list
) && ctxt
->use
& CNTXT_INCMNG
2621 && (ftotal
= folder_total(FOLDERS(ctxt
))))
2622 for(i
= 0; i
< ftotal
; i
++){
2623 f
= folder_entry(i
, FOLDERS(ctxt
));
2624 init_incoming_unseen_data(ps_global
, f
);
2630 selected_folders(CONTEXT_S
*context
)
2634 n
= folder_total(FOLDERS(context
));
2635 for(total
= i
= 0; i
< n
; i
++)
2636 if(folder_entry(i
, FOLDERS(context
))->selected
)
2648 selp
= (SELECTED_S
*)fs_get(sizeof(SELECTED_S
));
2650 selp
->reference
= NULL
;
2651 selp
->folders
= NULL
;
2659 * Free the current selected struct and all of the
2660 * following structs in the list
2663 free_selected(SELECTED_S
**selp
)
2665 if(!selp
|| !(*selp
))
2668 free_selected(&((*selp
)->sub
));
2670 free_strlist(&(*selp
)->folders
);
2671 if((*selp
)->reference
)
2672 fs_give((void **) &(*selp
)->reference
);
2674 fs_give((void **) selp
);
2679 folder_select_preserve(CONTEXT_S
*context
)
2682 && !(context
->use
& CNTXT_PARTFIND
)){
2685 SELECTED_S
*selp
= &context
->selected
;
2688 if(!context
->dir
->ref
){
2689 if(!context
->selected
.folders
)
2690 slpp
= &context
->selected
.folders
;
2695 if(!selected_folders(context
))
2700 if(!strcmp(selp
->reference
, context
->dir
->ref
))
2703 selp
->sub
= new_selected();
2705 slpp
= &(selp
->folders
);
2708 folder_n
= folder_total(FOLDERS(context
));
2710 for(i
= 0; i
< folder_n
; i
++)
2711 if((fp
= folder_entry(i
, FOLDERS(context
)))->selected
){
2712 *slpp
= new_strlist(fp
->name
);
2713 slpp
= &(*slpp
)->next
;
2716 /* Only remember "ref" if any folders were selected */
2717 if(selp
->folders
&& context
->dir
->ref
)
2718 selp
->reference
= cpystr(context
->dir
->ref
);
2720 selp
->zoomed
= (context
->use
& CNTXT_ZOOM
) != 0;
2726 folder_select_restore(CONTEXT_S
*context
)
2731 && !(context
->use
& CNTXT_PARTFIND
)){
2733 SELECTED_S
*selp
, *pselp
= NULL
;
2736 selp
= &(context
->selected
);
2738 if(context
->dir
->ref
){
2741 while(selp
&& strcmp(selp
->reference
, context
->dir
->ref
)){
2749 found
= selp
->folders
!= 0;
2751 for(slp
= selp
->folders
; slp
; slp
= slp
->next
)
2753 && (i
= folder_index(slp
->name
, context
, FI_FOLDER
)) >= 0){
2754 folder_entry(i
, FOLDERS(context
))->selected
= 1;
2758 /* Used, always clean them up */
2759 free_strlist(&selp
->folders
);
2761 fs_give((void **) &selp
->reference
);
2764 context
->use
|= CNTXT_ZOOM
;
2767 if(!(selp
== &context
->selected
)){
2769 pselp
->sub
= selp
->sub
;
2770 fs_give((void **) &selp
);