2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include "../pith/headers.h"
16 #include "../c-client/utf8aux.h"
17 #include "../pith/folder.h"
18 #include "../pith/state.h"
19 #include "../pith/context.h"
20 #include "../pith/init.h"
21 #include "../pith/conf.h"
22 #include "../pith/stream.h"
23 #include "../pith/imap.h"
24 #include "../pith/util.h"
25 #include "../pith/flag.h"
26 #include "../pith/status.h"
27 #include "../pith/busy.h"
28 #include "../pith/mailindx.h"
31 typedef struct _build_folder_list_data
{
32 long mask
; /* bitmap of responses to ignore */
46 void mail_list_exists(MAILSTREAM
*, char *, int, long, void *, unsigned);
47 void init_incoming_folder_list(struct pine
*, CONTEXT_S
*);
48 void mail_list_filter(MAILSTREAM
*, char *, int, long, void *, unsigned);
49 void mail_lsub_filter(MAILSTREAM
*, char *, int, long, void *, unsigned);
50 int mail_list_in_collection(char **, char *, char *, char *);
51 char *folder_last_cmpnt(char *, int);
52 void free_folder_entries(FLIST
**);
53 int folder_insert_sorted(int, int, int, FOLDER_S
*, FLIST
*,
54 int (*)(FOLDER_S
*, FOLDER_S
*));
55 void folder_insert_index(FOLDER_S
*, int, FLIST
*);
56 void resort_folder_list(FLIST
*flist
);
57 int compare_folders_alpha_qsort(const qsort_t
*a1
, const qsort_t
*a2
);
58 int compare_folders_dir_alpha_qsort(const qsort_t
*a1
, const qsort_t
*a2
);
59 int compare_folders_alpha_dir_qsort(const qsort_t
*a1
, const qsort_t
*a2
);
60 int compare_names(const qsort_t
*, const qsort_t
*);
61 void init_incoming_unseen_data(struct pine
*, FOLDER_S
*f
);
65 folder_lister_desc(CONTEXT_S
*cntxt
, FDIR_S
*fdp
)
70 q
= ((p
= strstr(cntxt
->context
, "%s")) && !*(p
+2)
71 && !strncmp(fdp
->ref
, cntxt
->context
, p
- cntxt
->context
))
72 ? fdp
->ref
+ (p
- cntxt
->context
) : fdp
->ref
;
73 fname
= folder_name_decoded((unsigned char *) q
);
74 /* Provide context in new collection header */
75 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Dir: %s", fname
? (char *) fname
: q
);
76 if(fname
) fs_give((void **)&fname
);
78 return(cpystr(tmp_20k_buf
));
83 reset_context_folders(CONTEXT_S
*cntxt
)
87 for(tc
= cntxt
; tc
&& tc
->prev
; tc
= tc
->prev
)
88 ; /* start at beginning */
90 for( ; tc
; tc
= tc
->next
){
94 FDIR_S
*tp
= tc
->dir
->prev
;
95 free_fdir(&tc
->dir
, 0);
103 * next_folder_dir - return a directory structure with the folders it
107 next_folder_dir(CONTEXT_S
*context
, char *new_dir
, int build_list
, MAILSTREAM
**streamp
)
109 char tmp
[MAILTMPLEN
], dir
[3];
112 fp
= (FDIR_S
*) fs_get(sizeof(FDIR_S
));
113 memset(fp
, 0, sizeof(FDIR_S
));
114 (void) context_apply(tmp
, context
, new_dir
, MAILTMPLEN
);
115 dir
[0] = context
->dir
->delim
;
117 strncat(tmp
, dir
, sizeof(tmp
)-1-strlen(tmp
));
118 fp
->ref
= cpystr(tmp
);
119 fp
->delim
= context
->dir
->delim
;
120 fp
->view
.internal
= cpystr(NEWS_TEST(context
) ? "*" : "%");
121 fp
->folders
= init_folder_entries();
122 fp
->status
= CNTXT_NOFIND
;
123 tmp_fp
= context
->dir
; /* temporarily rebind */
127 build_folder_list(streamp
, context
, NULL
, NULL
,
128 NEWS_TEST(context
) ? BFL_LSUB
: BFL_NONE
);
130 context
->dir
= tmp_fp
;
136 * Return which pinerc incoming folder #index is in.
139 config_containing_inc_fldr(FOLDER_S
*folder
)
142 int i
, keep_going
= 1, inheriting
= 0;
143 struct variable
*v
= &ps_global
->vars
[V_INCOMING_FOLDERS
];
148 /* is it in exceptions config? */
149 if(v
->post_user_val
.l
){
150 for(i
= 0, t
=v
->post_user_val
.l
; t
[i
]; i
++){
151 if(expand_variables(tmp_20k_buf
, SIZEOF_20KBUF
, t
[i
], 0)){
154 if(!strcmp(tmp_20k_buf
, INHERIT
))
156 else if(folder
->varhash
== line_hash(tmp_20k_buf
))
165 /* is it in main config? */
166 if(keep_going
&& v
->main_user_val
.l
){
167 for(i
= 0, t
=v
->main_user_val
.l
; t
[i
]; i
++){
168 if(expand_variables(tmp_20k_buf
, SIZEOF_20KBUF
, t
[i
], 0) &&
169 folder
->varhash
== line_hash(tmp_20k_buf
))
178 /*----------------------------------------------------------------------
179 Format the given folder name for display for the user
181 Args: folder -- The folder name to fix up
183 Not sure this always makes it prettier. It could do nice truncation if we
184 passed in a length. Right now it adds the path name of the mail
185 subdirectory if appropriate.
189 pretty_fn(char *folder
)
191 if(!strucmp(folder
, ps_global
->inbox_name
))
192 return(ps_global
->inbox_name
);
198 /*----------------------------------------------------------------------
199 Return the path delimiter for the given folder on the given server
201 Args: folder -- folder type for delimiter
205 get_folder_delimiter(char *folder
)
211 memset(mm_list_info
= &ldata
, 0, sizeof(MM_LIST_S
));
212 ldata
.filter
= mail_list_response
;
213 memset(ldata
.data
= &response
, 0, sizeof(LISTRES_S
));
216 && !(ldata
.stream
= sp_stream_get(folder
, SP_MATCH
))
217 && !(ldata
.stream
= sp_stream_get(folder
, SP_SAME
))){
218 if((ldata
.stream
= pine_mail_open(NULL
,folder
,
219 OP_HALFOPEN
|OP_SILENT
|SP_USEPOOL
|SP_TEMPUSE
,
228 pine_mail_list(ldata
.stream
, folder
, "", NULL
);
231 pine_mail_close(ldata
.stream
);
233 return(response
.delim
);
237 /*----------------------------------------------------------------------
238 Check to see if folder exists in given context
240 Args: cntxt -- context inwhich to interpret "file" arg
241 file -- name of folder to check
243 Result: returns FEX_ISFILE if the folder exists and is a folder
244 FEX_ISDIR if the folder exists and is a directory
245 FEX_NOENT if it doesn't exist
248 The two existence return values above may be logically OR'd
250 Uses mail_list to sniff out the existence of the requested folder.
251 The context string is just here for convenience. Checking for
252 folder's existence within a given context is probably more efficiently
253 handled outside this function for now using build_folder_list().
257 folder_exists(CONTEXT_S
*cntxt
, char *file
)
259 return(folder_name_exists(cntxt
, file
, NULL
));
263 /*----------------------------------------------------------------------
264 Check to see if folder exists in given context
266 Args: cntxt -- context in which to interpret "file" arg
267 file -- name of folder to check
268 name -- name of folder folder with context applied
270 Result: returns FEX_ISFILE if the folder exists and is a folder
271 FEX_ISDIR if the folder exists and is a directory
272 FEX_NOENT if it doesn't exist
275 The two existence return values above may be logically OR'd
277 Uses mail_list to sniff out the existence of the requested folder.
278 The context string is just here for convenience. Checking for
279 folder's existence within a given context is probably more efficiently
280 handled outside this function for now using build_folder_list().
284 folder_name_exists(CONTEXT_S
*cntxt
, char *file
, char **fullpath
)
288 int we_cancel
= 0, res
;
289 char *p
, reference
[MAILTMPLEN
], tmp
[MAILTMPLEN
], *tfolder
= NULL
;
292 * No folder means "inbox".
294 if(*file
== '{' && (p
= strchr(file
, '}')) && (!*(p
+1))){
297 l
= strlen(file
)+strlen("inbox");
298 tfolder
= (char *) fs_get((l
+1) * sizeof(char));
299 snprintf(tfolder
, l
+1, "%s%s", file
, "inbox");
304 mm_list_info
= &ldata
; /* tie down global reference */
305 memset(&ldata
, 0, sizeof(ldata
));
306 ldata
.filter
= mail_list_exists
;
308 ldata
.stream
= sp_stream_get(context_apply(tmp
, cntxt
, file
, sizeof(tmp
)),
311 memset(ldata
.data
= &parms
, 0, sizeof(EXISTDATA_S
));
314 * If no preset reference string, must be at top of context
316 if(cntxt
&& context_isambig(file
)){
317 /* inbox in first context is the real inbox */
318 if(ps_global
->context_list
== cntxt
&& !strucmp(file
, ps_global
->inbox_name
)){
320 parms
.args
.reference
= reference
;
322 else if(!(parms
.args
.reference
= cntxt
->dir
->ref
)){
325 if((p
= strstr(cntxt
->context
, "%s")) != NULL
){
326 strncpy(parms
.args
.reference
= reference
,
328 MIN(p
- cntxt
->context
, sizeof(reference
)-1));
329 reference
[MIN(p
- cntxt
->context
, sizeof(reference
)-1)] = '\0';
334 parms
.args
.reference
= cntxt
->context
;
337 parms
.fullname
= fullpath
;
340 ps_global
->mm_log_error
= 0;
341 ps_global
->noshow_error
= 1;
343 we_cancel
= busy_cue(NULL
, NULL
, 1);
345 parms
.args
.name
= file
;
347 res
= pine_mail_list(ldata
.stream
, parms
.args
.reference
, parms
.args
.name
,
353 ps_global
->noshow_error
= 0;
355 if(cntxt
&& cntxt
->dir
&& parms
.response
.delim
)
356 cntxt
->dir
->delim
= parms
.response
.delim
;
359 fs_give((void **)&tfolder
);
360 return(((res
== FALSE
) || ps_global
->mm_log_error
)
362 : (((parms
.response
.isfile
)
364 | ((parms
.response
.isdir
)
366 | ((parms
.response
.ismarked
)
368 | ((parms
.response
.unmarked
)
369 ? FEX_UNMARKED
: 0)));
374 mail_list_exists(MAILSTREAM
*stream
, char *mailbox
, int delim
, long int attribs
,
375 void *data
, unsigned int options
)
378 ((EXISTDATA_S
*) data
)->response
.delim
= delim
;
380 if(mailbox
&& *mailbox
){
381 if(!(attribs
& LATT_NOSELECT
)){
382 ((EXISTDATA_S
*) data
)->response
.isfile
= 1;
383 ((EXISTDATA_S
*) data
)->response
.count
+= 1;
386 if(!(attribs
& LATT_NOINFERIORS
)){
387 ((EXISTDATA_S
*) data
)->response
.isdir
= 1;
388 ((EXISTDATA_S
*) data
)->response
.count
+= 1;
391 if(attribs
& LATT_MARKED
)
392 ((EXISTDATA_S
*) data
)->response
.ismarked
= 1;
394 /* don't mark #move folders unmarked */
395 if(attribs
& LATT_UNMARKED
&& !(options
& PML_IS_MOVE_MBOX
))
396 ((EXISTDATA_S
*) data
)->response
.unmarked
= 1;
398 if(attribs
& LATT_HASCHILDREN
)
399 ((EXISTDATA_S
*) data
)->response
.haschildren
= 1;
401 if(attribs
& LATT_HASNOCHILDREN
)
402 ((EXISTDATA_S
*) data
)->response
.hasnochildren
= 1;
404 if(((EXISTDATA_S
*) data
)->fullname
405 && ((((EXISTDATA_S
*) data
)->args
.reference
[0] != '\0')
406 ? struncmp(((EXISTDATA_S
*) data
)->args
.reference
, mailbox
,
407 strlen(((EXISTDATA_S
*)data
)->args
.reference
))
408 : struncmp(((EXISTDATA_S
*) data
)->args
.name
, mailbox
,
409 strlen(((EXISTDATA_S
*) data
)->args
.name
)))){
412 size_t len
= (((stream
&& stream
->mailbox
)
413 ? strlen(stream
->mailbox
) : 0)
414 + strlen(((EXISTDATA_S
*) data
)->args
.reference
)
415 + strlen(((EXISTDATA_S
*) data
)->args
.name
)
416 + strlen(mailbox
)) * sizeof(char);
419 * Fully qualify (in the c-client name structure sense)
420 * anything that's not in the context of the "reference"...
422 if(*((EXISTDATA_S
*) data
)->fullname
)
423 fs_give((void **) ((EXISTDATA_S
*) data
)->fullname
);
426 *((EXISTDATA_S
*) data
)->fullname
= (char *) fs_get(alloclen
);
428 && stream
&& stream
->mailbox
&& *stream
->mailbox
== '{'
429 && (p
= strindex(stream
->mailbox
, '}'))){
430 len
= (p
- stream
->mailbox
) + 1;
431 strncpy(*((EXISTDATA_S
*) data
)->fullname
,
432 stream
->mailbox
, MIN(len
,alloclen
));
433 p
= *((EXISTDATA_S
*) data
)->fullname
+ len
;
436 p
= *((EXISTDATA_S
*) data
)->fullname
;
438 strncpy(p
, mailbox
, alloclen
-(p
-(*((EXISTDATA_S
*) data
)->fullname
)));
439 (*((EXISTDATA_S
*) data
)->fullname
)[alloclen
-1] = '\0';;
446 mail_list_response(MAILSTREAM
*stream
, char *mailbox
, int delim
, long int attribs
,
447 void *data
, unsigned int options
)
452 ((LISTRES_S
*) data
)->delim
= delim
;
454 if(mailbox
&& *mailbox
){
455 if(!(attribs
& LATT_NOSELECT
)){
457 ((LISTRES_S
*) data
)->isfile
= 1;
458 ((LISTRES_S
*) data
)->count
+= 1;
461 if(!(attribs
& LATT_NOINFERIORS
)){
462 ((LISTRES_S
*) data
)->isdir
= 1;
465 ((LISTRES_S
*) data
)->count
+= 1;
468 if(attribs
& LATT_HASCHILDREN
)
469 ((LISTRES_S
*) data
)->haschildren
= 1;
471 if(attribs
& LATT_HASNOCHILDREN
)
472 ((LISTRES_S
*) data
)->hasnochildren
= 1;
478 folder_as_breakout(CONTEXT_S
*cntxt
, char *name
)
480 if(context_isambig(name
)){ /* if simple check doesn't pan out */
481 char tmp
[2*MAILTMPLEN
], *p
, *f
; /* look harder */
483 if(!cntxt
->dir
->delim
){
484 (void) context_apply(tmp
, cntxt
, "", sizeof(tmp
)/2);
485 cntxt
->dir
->delim
= get_folder_delimiter(tmp
);
488 if((p
= strindex(name
, cntxt
->dir
->delim
)) != NULL
){
489 if(p
== name
){ /* assumption 6,321: delim is root */
490 if(cntxt
->context
[0] == '{'
491 && (p
= strindex(cntxt
->context
, '}'))){
492 strncpy(tmp
, cntxt
->context
,
493 MIN((p
- cntxt
->context
) + 1, sizeof(tmp
)/2));
494 tmp
[MIN((p
- cntxt
->context
) + 1, sizeof(tmp
)/2)] = '\0';
495 strncpy(&tmp
[MIN((p
- cntxt
->context
) + 1, sizeof(tmp
)/2)],
496 name
, sizeof(tmp
)/2-strlen(tmp
));
497 tmp
[sizeof(tmp
)-1] = '\0';
501 return(cpystr(name
));
503 else{ /* assumption 6,322: no create ~foo */
504 strncpy(tmp
, name
, MIN(p
- name
, MAILTMPLEN
));
505 /* lop off trailingpath */
506 tmp
[MIN(p
- name
, sizeof(tmp
)/2)] = '\0';
508 (void)folder_name_exists(cntxt
, tmp
, &f
);
510 snprintf(tmp
, sizeof(tmp
), "%s%s",f
,p
);
511 tmp
[sizeof(tmp
)-1] = '\0';
512 fs_give((void **) &f
);
523 /*----------------------------------------------------------------------
524 Initialize global list of contexts for folder collections.
526 Interprets collections defined in the pinerc and orders them for
527 pine's use. Parses user-provided context labels and sets appropriate
528 use flags and the default prototype for that collection.
529 (See find_folders for how the actual folder list is found).
533 init_folders(struct pine
*ps
)
535 CONTEXT_S
*tc
, *top
= NULL
, **clist
;
541 * If no incoming folders are config'd, but the user asked for
542 * them via feature, make sure at least "inbox" ends up there...
544 if(F_ON(F_ENABLE_INCOMING
, ps
) && !ps
->VAR_INCOMING_FOLDERS
){
545 ps
->VAR_INCOMING_FOLDERS
= (char **)fs_get(2 * sizeof(char *));
546 ps
->VAR_INCOMING_FOLDERS
[0] = cpystr(ps
->inbox_name
);
547 ps
->VAR_INCOMING_FOLDERS
[1] = NULL
;
551 * Build context that's a list of folders the user's defined
552 * as receiving new messages. At some point, this should
553 * probably include adding a prefix with the new message count.
554 * fake new context...
556 if(ps
->VAR_INCOMING_FOLDERS
&& ps
->VAR_INCOMING_FOLDERS
[0]
557 && (tc
= new_context("Incoming-Folders []", NULL
))){
558 tc
->dir
->status
&= ~CNTXT_NOFIND
;
559 tc
->use
|= CNTXT_INCMNG
; /* mark this as incoming collection */
561 fs_give((void **) &tc
->label
);
563 /* TRANSLATORS: a label */
564 tc
->label
= cpystr(_("Incoming Message Folders"));
569 init_incoming_folder_list(ps
, tc
);
573 * Build list of folder collections. Because of the way init.c
574 * works, we're guaranteed at least a default. Also write any
575 * "bogus format" messages...
577 for(i
= 0; ps
->VAR_FOLDER_SPEC
&& ps
->VAR_FOLDER_SPEC
[i
] ; i
++)
578 if((tc
= new_context(ps
->VAR_FOLDER_SPEC
[i
], &prime
)) != NULL
){
579 *clist
= tc
; /* add it to list */
580 clist
= &tc
->next
; /* prepare for next */
581 tc
->var
.v
= &ps
->vars
[V_FOLDER_SPEC
];
587 * Whoah cowboy!!! Guess we couldn't find a valid folder
591 alpine_panic(_("No folder collections defined"));
594 * At this point, insert the INBOX mapping as the leading
595 * folder entry of the first collection...
597 init_inbox_mapping(ps
->VAR_INBOX_PATH
, top
);
599 set_news_spec_current_val(TRUE
, TRUE
);
602 * If news groups, loop thru list adding to collection list
604 for(i
= 0; ps
->VAR_NEWS_SPEC
&& ps
->VAR_NEWS_SPEC
[i
] ; i
++)
605 if(ps
->VAR_NEWS_SPEC
[i
][0]
606 && (tc
= new_context(ps
->VAR_NEWS_SPEC
[i
], NULL
))){
607 *clist
= tc
; /* add it to list */
608 clist
= &tc
->next
; /* prepare for next */
609 tc
->var
.v
= &ps
->vars
[V_NEWS_SPEC
];
613 ps
->context_list
= top
; /* init pointers */
614 ps
->context_current
= (top
->use
& CNTXT_INCMNG
) ? top
->next
: top
;
615 ps
->context_last
= NULL
;
616 /* Tie up all the previous pointers */
617 for(; top
; top
= top
->next
)
619 top
->next
->prev
= top
;
628 * Add incoming list of folders to context.
631 init_incoming_folder_list(struct pine
*ps
, CONTEXT_S
*cntxt
)
634 char *folder_string
, *nickname
;
637 for(i
= 0; ps
->VAR_INCOMING_FOLDERS
[i
] ; i
++){
639 * Parse folder line for nickname and folder name.
640 * No nickname on line is OK.
642 get_pair(ps
->VAR_INCOMING_FOLDERS
[i
], &nickname
, &folder_string
,0,0);
645 * Allow for inbox to be specified in the incoming list, but
646 * don't let it show up along side the one magically inserted
649 if(!folder_string
|| !strucmp(ps
->inbox_name
, folder_string
)){
651 fs_give((void **)&folder_string
);
654 fs_give((void **)&nickname
);
658 else if(update_bboard_spec(folder_string
, tmp_20k_buf
, SIZEOF_20KBUF
)){
659 fs_give((void **) &folder_string
);
660 folder_string
= cpystr(tmp_20k_buf
);
663 f
= new_folder(folder_string
,
664 line_hash(ps
->VAR_INCOMING_FOLDERS
[i
]));
666 fs_give((void **)&folder_string
);
669 if(strucmp(ps
->inbox_name
, nickname
)){
670 f
->nickname
= nickname
;
671 f
->name_len
= strlen(f
->nickname
);
674 fs_give((void **)&nickname
);
677 init_incoming_unseen_data(ps
, f
);
679 folder_insert(f
->nickname
680 && (strucmp(f
->nickname
, ps
->inbox_name
) == 0)
681 ? -1 : folder_total(FOLDERS(cntxt
)),
688 init_incoming_unseen_data(struct pine
*ps
, FOLDER_S
*f
)
690 int j
, check_this
= 0;
695 /* see if this folder is in the monitoring list */
696 if(F_ON(F_ENABLE_INCOMING_CHECKING
, ps
)){
697 if(!ps
->VAR_INCCHECKLIST
)
698 check_this
++; /* everything in by default */
700 for(j
= 0; !check_this
&& ps
->VAR_INCCHECKLIST
[j
]; j
++){
701 if((f
->nickname
&& !strucmp(ps
->VAR_INCCHECKLIST
[j
],f
->nickname
))
702 || (f
->name
&& !strucmp(ps
->VAR_INCCHECKLIST
[j
],f
->name
)))
709 f
->last_unseen_update
= LUU_INIT
;
711 f
->last_unseen_update
= LUU_NEVERCHK
;
716 reinit_incoming_folder_list(struct pine
*ps
, CONTEXT_S
*context
)
718 free_folder_entries(&(FOLDERS(context
)));
719 FOLDERS(context
) = init_folder_entries();
720 init_incoming_folder_list(ps_global
, context
);
721 init_inbox_mapping(ps_global
->VAR_INBOX_PATH
, context
);
729 init_inbox_mapping(char *path
, CONTEXT_S
*cntxt
)
735 * If mapping already exists, blast it and replace it below...
737 if((f
= folder_entry(0, FOLDERS(cntxt
)))
738 && f
->nickname
&& !strcmp(f
->nickname
, ps_global
->inbox_name
))
739 folder_delete(0, FOLDERS(cntxt
));
742 f
= new_folder(path
, 0);
743 f
->nickname
= cpystr(ps_global
->inbox_name
);
744 f
->name_len
= strlen(f
->nickname
);
747 f
= new_folder(ps_global
->inbox_name
, 0);
751 /* see if this folder is in the monitoring list */
753 if(F_ON(F_ENABLE_INCOMING_CHECKING
, ps_global
) && ps_global
->VAR_INCOMING_FOLDERS
&& ps_global
->VAR_INCOMING_FOLDERS
[0]){
754 if(!ps_global
->VAR_INCCHECKLIST
)
755 check_this
++; /* everything in by default */
757 for(j
= 0; !check_this
&& ps_global
->VAR_INCCHECKLIST
[j
]; j
++){
758 if((f
->nickname
&& !strucmp(ps_global
->VAR_INCCHECKLIST
[j
],f
->nickname
))
759 || (f
->name
&& !strucmp(ps_global
->VAR_INCCHECKLIST
[j
],f
->name
)))
766 f
->last_unseen_update
= LUU_INIT
;
768 f
->last_unseen_update
= LUU_NEVERCHK
;
770 folder_insert(0, f
, FOLDERS(cntxt
));
775 new_fdir(char *ref
, char *view
, int wildcard
)
777 FDIR_S
*rv
= (FDIR_S
*) fs_get(sizeof(FDIR_S
));
779 memset((void *) rv
, 0, sizeof(FDIR_S
));
781 /* Monkey with the view to make sure it has wildcard? */
783 rv
->view
.user
= cpystr(view
);
786 * This is sorta hairy since, for simplicity we allow
787 * users to use '*' in the view, but for mail
788 * we really mean '%' as def'd in 2060...
790 if(wildcard
== '*'){ /* must be #news. */
791 if(strindex(view
, wildcard
))
792 rv
->view
.internal
= cpystr(view
);
794 else{ /* must be mail */
797 if((p
= strpbrk(view
, "*%")) != NULL
){
798 rv
->view
.internal
= p
= cpystr(view
);
799 while((p
= strpbrk(p
, "*%")) != NULL
)
800 *p
++ = '%'; /* convert everything to '%' */
804 if(!rv
->view
.internal
){
808 rv
->view
.internal
= (char *) fs_get((l
+1) * sizeof(char));
809 snprintf(rv
->view
.internal
, l
+1, "%c%s%c", wildcard
, view
, wildcard
);
813 rv
->view
.internal
= (char *) fs_get(2 * sizeof(char));
814 snprintf(rv
->view
.internal
, 2, "%c", wildcard
);
820 rv
->folders
= init_folder_entries();
821 rv
->status
= CNTXT_NOFIND
;
827 free_fdir(FDIR_S
**f
, int recur
)
830 if((*f
)->prev
&& recur
)
831 free_fdir(&(*f
)->prev
, 1);
834 fs_give((void **)&(*f
)->ref
);
837 fs_give((void **)&(*f
)->view
.user
);
839 if((*f
)->view
.internal
)
840 fs_give((void **)&(*f
)->view
.internal
);
843 fs_give((void **)&(*f
)->desc
);
845 free_folder_entries(&(*f
)->folders
);
846 fs_give((void **) f
);
852 update_bboard_spec(char *bboard
, char *buf
, size_t buflen
)
854 char *p
= NULL
, *origbuf
;
855 int nntp
= 0, bracket
= 0;
860 || (*bboard
== '{' && (p
= strindex(bboard
, '}')) && *(p
+1) == '*')){
862 if(p
|| (*(bboard
+1) == '{' && (p
= strindex(++bboard
, '}'))))
863 while(bboard
<= p
&& (buf
-origbuf
< buflen
)) /* copy it */
864 if((*buf
++ = *bboard
++) == '/' && !strncmp(bboard
, "nntp", 4))
872 * See if path portion looks newsgroup-ish while being aware
873 * of the "view" portion of the spec...
875 for(p
= bboard
; *p
; p
++)
877 if(bracket
) /* only one set allowed! */
883 if(bracket
!= 1) /* must be closing bracket */
888 else if(!(isalnum((unsigned char) *p
) || strindex(".-", *p
)))
892 snprintf(buf
, buflen
-(buf
-origbuf
), "%s%s%s",
893 (!nntp
&& *p
) ? "#public" : "#news.",
894 (!nntp
&& *p
&& *bboard
!= '/') ? "/" : "",
905 * build_folder_list - call mail_list to fetch us a list of folders
906 * from the given context.
909 build_folder_list(MAILSTREAM
**stream
, CONTEXT_S
*context
, char *pat
, char *content
, int flags
)
913 int local_open
= 0, we_cancel
= 0, resort
= 0;
914 char reference
[2*MAILTMPLEN
], *p
;
916 if(!(context
->dir
->status
& CNTXT_NOFIND
)
917 || (context
->dir
->status
& CNTXT_PARTFIND
))
918 return; /* find already done! */
920 dprint((7, "build_folder_list: %s %s\n",
921 context
? context
->context
: "NULL",
922 pat
? pat
: "NULL"));
924 we_cancel
= busy_cue(NULL
, NULL
, 1);
927 * Set up the pattern of folder name's to match within the
930 if(!pat
|| ((*pat
== '*' || *pat
== '%') && *(pat
+1) == '\0')){
931 context
->dir
->status
&= ~CNTXT_NOFIND
; /* let'em know we tried */
932 pat
= context
->dir
->view
.internal
;
935 context
->use
|= CNTXT_PARTFIND
; /* or are in a partial find */
937 memset(mm_list_info
= &ldata
, 0, sizeof(MM_LIST_S
));
938 ldata
.filter
= (NEWS_TEST(context
)) ? mail_lsub_filter
: mail_list_filter
;
939 memset(ldata
.data
= &response
, 0, sizeof(BFL_DATA_S
));
940 response
.list
= FOLDERS(context
);
942 if(flags
& BFL_FLDRONLY
)
943 response
.mask
= LATT_NOSELECT
;
946 * if context is associated with a server, prepare a stream for
947 * sending our request.
949 if(*context
->context
== '{'){
951 * Try using a stream we've already got open...
954 && !(ldata
.stream
= same_stream(context
->context
, *stream
))){
955 pine_mail_close(*stream
);
960 ldata
.stream
= sp_stream_get(context
->context
, SP_MATCH
);
963 ldata
.stream
= sp_stream_get(context
->context
, SP_SAME
);
965 /* gotta open a new one? */
966 if((F_OFF(F_CMBND_FOLDER_DISP
, ps_global
)
967 || context
->update
== LUU_INIT
) && !ldata
.stream
){
968 ldata
.stream
= mail_cmd_stream(context
, &local_open
);
970 *stream
= ldata
.stream
;
973 dprint((ldata
.stream
? 7 : 1, "build_folder_list: mail_open(%s) %s.\n",
974 context
->server
? context
->server
: "?",
975 ldata
.stream
? "OK" : "FAILED"));
978 context
->use
&= ~CNTXT_PARTFIND
; /* unset partial find bit */
979 context
->update
= LUU_NOMORECHK
;
986 else if(stream
&& *stream
){ /* no server, simple case */
987 if(!sp_flagged(*stream
, SP_LOCKED
))
988 pine_mail_close(*stream
);
994 * If preset reference string, we're somewhere in the hierarchy.
995 * ELSE we must be at top of context...
997 response
.args
.name
= pat
;
998 if(!(response
.args
.reference
= context
->dir
->ref
)){
999 if((p
= strstr(context
->context
, "%s")) != NULL
){
1000 strncpy(response
.args
.reference
= reference
,
1002 MIN(p
- context
->context
, sizeof(reference
)/2));
1003 reference
[MIN(p
- context
->context
, sizeof(reference
)/2)] = '\0';
1005 response
.args
.tail
= p
;
1008 response
.args
.reference
= context
->context
;
1011 if(flags
& BFL_SCAN
)
1012 mail_scan(ldata
.stream
, response
.args
.reference
,
1013 response
.args
.name
, content
);
1014 else if(flags
& BFL_LSUB
)
1015 mail_lsub(ldata
.stream
, response
.args
.reference
, response
.args
.name
);
1017 set_read_predicted(1);
1018 pine_mail_list(ldata
.stream
, response
.args
.reference
, response
.args
.name
,
1020 set_read_predicted(0);
1023 context
->update
= LUU_INIT
;
1024 if(context
->dir
&& response
.response
.delim
)
1025 context
->dir
->delim
= response
.response
.delim
;
1027 if(!(flags
& (BFL_LSUB
|BFL_SCAN
)) && F_ON(F_QUELL_EMPTY_DIRS
, ps_global
)){
1032 for(i
= 0; i
< folder_total(response
.list
); i
++)
1033 if((f
= folder_entry(i
, FOLDERS(context
)))->isdir
){
1035 * we don't need to do a list if we know already
1036 * whether there are children or not.
1038 if(!f
->haschildren
&& !f
->hasnochildren
){
1039 memset(ldata
.data
= &listres
, 0, sizeof(LISTRES_S
));
1040 ldata
.filter
= mail_list_response
;
1042 if(context
->dir
->ref
)
1043 snprintf(reference
, sizeof(reference
), "%s%s", context
->dir
->ref
, f
->name
);
1045 context_apply(reference
, context
, f
->name
, sizeof(reference
)/2);
1047 /* append the delimiter to the reference */
1048 for(p
= reference
; *p
; p
++)
1051 *p
++ = context
->dir
->delim
;
1054 pine_mail_list(ldata
.stream
, reference
, "%", NULL
);
1056 /* anything interesting inside? */
1057 f
->hasnochildren
= (listres
.count
<= 1L);
1058 f
->haschildren
= !f
->hasnochildren
;;
1061 if(f
->hasnochildren
){
1064 if(ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_FIRST
1065 || ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_LAST
)
1069 * We don't want to hide directories
1070 * that are only directories even if they are
1071 * empty. We only want to hide the directory
1072 * piece of a dual-use folder when there are
1073 * no children in the directory (and the user
1074 * most likely thinks of it as a folder instead
1075 * of a folder and a directory).
1077 else if(f
->isdir
&& f
->isdual
){
1078 folder_delete(i
, FOLDERS(context
));
1086 resort_folder_list(response
.list
);
1088 if(local_open
&& !stream
)
1089 pine_mail_close(ldata
.stream
);
1091 if(context
->use
& CNTXT_PRESRV
)
1092 folder_select_restore(context
);
1094 context
->use
&= ~CNTXT_PARTFIND
; /* unset partial list bit */
1096 cancel_busy_cue(-1);
1101 * Validate LIST response to command issued from build_folder_list
1105 mail_list_filter(MAILSTREAM
*stream
, char *mailbox
, int delim
, long int attribs
, void *data
, unsigned int options
)
1107 BFL_DATA_S
*ld
= (BFL_DATA_S
*) data
;
1108 FOLDER_S
*new_f
= NULL
, *dual_f
= NULL
;
1109 int suppress_folder_add
= 0;
1115 ld
->response
.delim
= delim
;
1117 /* test against mask of DIS-allowed attributes */
1118 if((ld
->mask
& attribs
)){
1119 dprint((3, "mail_list_filter: failed attribute test"));
1124 * First, make sure response fits our "reference" arg
1125 * NOTE: build_folder_list can't supply breakout?
1127 if(!mail_list_in_collection(&mailbox
, ld
->args
.reference
,
1128 ld
->args
.name
, ld
->args
.tail
))
1131 /* ignore dotfolders unless told not to */
1132 if(F_OFF(F_ENABLE_DOT_FOLDERS
, ps_global
) && *mailbox
== '.'){
1133 dprint((3, "mail_list_filter: dotfolder disallowed"));
1138 * If this response is INBOX we have to handle it specially.
1139 * The special cases are:
1141 * Incoming folders are enabled and this INBOX is in the Incoming
1142 * folders list already. We don't want to list it here, as well,
1143 * unless it is also a directory.
1145 * Incoming folders are not enabled, but we inserted INBOX into
1146 * this primary collection (with init_inbox_mapping()) so it is
1147 * already accounted for unless it is also a directory.
1149 * Incoming folders are not enabled, but we inserted INBOX into
1150 * the primary collection (which we are not looking at here) so
1151 * it is already accounted for there. We'll add it as a directory
1152 * as well here if that is what is called for.
1154 if(!strucmp(mailbox
, ps_global
->inbox_name
)){
1157 char fullname
[1000], tmp1
[1000], tmp2
[1000], *l1
, *l2
;
1159 /* fullname is the name of the mailbox in the list response */
1160 if(ld
->args
.reference
){
1161 strncpy(fullname
, ld
->args
.reference
, sizeof(fullname
)-1);
1162 fullname
[sizeof(fullname
)-1] = '\0';
1167 strncat(fullname
, mailbox
, sizeof(fullname
)-strlen(fullname
)-1);
1168 fullname
[sizeof(fullname
)-1] = '\0';
1170 /* check if Incoming Folders are enabled */
1171 if(ps_global
->context_list
&& ps_global
->context_list
->use
& CNTXT_INCMNG
){
1172 int this_inbox_is_in_incoming
= 0;
1175 * Figure out if this INBOX is already in the Incoming list.
1178 /* compare fullname to each incoming folder */
1179 ftotal
= folder_total(FOLDERS(ps_global
->context_list
));
1180 for(i
= 0; i
< ftotal
&& !this_inbox_is_in_incoming
; i
++){
1181 f
= folder_entry(i
, FOLDERS(ps_global
->context_list
));
1183 if(same_remote_mailboxes(fullname
, f
->name
)
1184 || ((!IS_REMOTE(fullname
) && !IS_REMOTE(f
->name
))
1185 && (l1
=mailboxfile(tmp1
,fullname
))
1186 && (l2
=mailboxfile(tmp2
,f
->name
))
1188 this_inbox_is_in_incoming
++;
1192 if(this_inbox_is_in_incoming
){
1194 * Don't add a folder for this, only a directory if called for.
1195 * If it isn't a directory, skip it.
1197 if(!(delim
&& !(attribs
& LATT_NOINFERIORS
)))
1200 suppress_folder_add
++;
1204 int inbox_is_in_this_collection
= 0;
1206 /* is INBOX already in this folder list? */
1207 ftotal
= folder_total(ld
->list
);
1208 for(i
= 0; i
< ftotal
&& !inbox_is_in_this_collection
; i
++){
1209 f
= folder_entry(i
, ld
->list
);
1210 if(!strucmp(FLDR_NAME(f
), ps_global
->inbox_name
))
1211 inbox_is_in_this_collection
++;
1214 if(inbox_is_in_this_collection
){
1217 * Inbox is already inserted in this collection. Unless
1218 * it is also a directory, we are done.
1220 if(!(delim
&& !(attribs
& LATT_NOINFERIORS
)))
1224 * Since it is also a directory, what we do depends on
1225 * the F_SEP feature. If that feature is not set we just
1226 * want to mark the existing entry dual-use. If F_SEP is
1227 * set we want to add a new directory entry.
1229 if(F_ON(F_SEPARATE_FLDR_AS_DIR
, ps_global
)){
1232 f
->hasnochildren
= 0;
1233 /* fall through and add a new directory */
1236 /* mark existing entry dual-use and return */
1237 ld
->response
.count
++;
1238 ld
->response
.isdir
= 1;
1240 if(attribs
& LATT_HASCHILDREN
)
1243 if(attribs
& LATT_HASNOCHILDREN
)
1244 f
->hasnochildren
= 1;
1249 suppress_folder_add
++;
1253 int this_inbox_is_primary_inbox
= 0;
1256 * See if this INBOX is the same as the INBOX we inserted
1257 * in the primary collection.
1260 /* first find the existing INBOX entry */
1261 ftotal
= folder_total(FOLDERS(ps_global
->context_list
));
1262 for(i
= 0; i
< ftotal
&& !found_it
; i
++){
1263 f
= folder_entry(i
, FOLDERS(ps_global
->context_list
));
1264 if(!strucmp(FLDR_NAME(f
), ps_global
->inbox_name
))
1268 if(found_it
&& f
&& f
->name
){
1269 if(same_remote_mailboxes(fullname
, f
->name
)
1270 || ((!IS_REMOTE(fullname
) && !IS_REMOTE(f
->name
))
1271 && (l1
=mailboxfile(tmp1
,fullname
))
1272 && (l2
=mailboxfile(tmp2
,f
->name
))
1274 this_inbox_is_primary_inbox
++;
1277 if(this_inbox_is_primary_inbox
){
1279 * Don't add a folder for this, only a directory if called for.
1280 * If it isn't a directory, skip it.
1282 if(!(delim
&& !(attribs
& LATT_NOINFERIORS
)))
1285 suppress_folder_add
++;
1291 /* is it a mailbox? */
1292 if(!(attribs
& LATT_NOSELECT
) && !suppress_folder_add
){
1293 ld
->response
.count
++;
1294 ld
->response
.isfile
= 1;
1295 new_f
= new_folder(mailbox
, 0);
1296 new_f
->isfolder
= 1;
1298 if(F_ON(F_SEPARATE_FLDR_AS_DIR
, ps_global
)){
1299 folder_insert(-1, new_f
, ld
->list
);
1306 if(delim
&& !(attribs
& LATT_NOINFERIORS
)){
1307 ld
->response
.count
++;
1308 ld
->response
.isdir
= 1;
1311 new_f
= new_folder(mailbox
, 0);
1314 if(attribs
& LATT_HASCHILDREN
)
1315 new_f
->haschildren
= 1;
1316 if(attribs
& LATT_HASNOCHILDREN
)
1317 new_f
->hasnochildren
= 1;
1320 * When we have F_SEPARATE_FLDR_AS_DIR we still want to know
1321 * whether the name really represents both so that we don't
1322 * inadvertently delete both when the user meant one or the
1326 if(attribs
& LATT_HASCHILDREN
)
1327 dual_f
->haschildren
= 1;
1329 if(attribs
& LATT_HASNOCHILDREN
)
1330 dual_f
->hasnochildren
= 1;
1338 folder_insert(-1, new_f
, ld
->list
);
1340 if(attribs
& LATT_MARKED
)
1341 ld
->response
.ismarked
= 1;
1343 /* don't mark #move folders unmarked */
1344 if(attribs
& LATT_UNMARKED
&& !(options
& PML_IS_MOVE_MBOX
))
1345 ld
->response
.unmarked
= 1;
1347 if(attribs
& LATT_HASCHILDREN
)
1348 ld
->response
.haschildren
= 1;
1350 if(attribs
& LATT_HASNOCHILDREN
)
1351 ld
->response
.hasnochildren
= 1;
1356 * Validate LSUB response to command issued from build_folder_list
1360 mail_lsub_filter(MAILSTREAM
*stream
, char *mailbox
, int delim
, long int attribs
,
1361 void *data
, unsigned int options
)
1363 BFL_DATA_S
*ld
= (BFL_DATA_S
*) data
;
1364 FOLDER_S
*new_f
= NULL
;
1368 ld
->response
.delim
= delim
;
1370 /* test against mask of DIS-allowed attributes */
1371 if((ld
->mask
& attribs
)){
1372 dprint((3, "mail_lsub_filter: failed attribute test"));
1376 /* Normalize mailbox and reference strings re: namespace */
1377 if(!strncmp(mailbox
, "#news.", 6))
1380 if(!strncmp(ref
= ld
->args
.reference
, "#news.", 6))
1383 if(!mail_list_in_collection(&mailbox
, ref
, ld
->args
.name
, ld
->args
.tail
))
1386 if(!(attribs
& LATT_NOSELECT
)){
1387 ld
->response
.count
++;
1388 ld
->response
.isfile
= 1;
1389 new_f
= new_folder(mailbox
, 0);
1390 new_f
->isfolder
= 1;
1391 folder_insert(F_ON(F_READ_IN_NEWSRC_ORDER
, ps_global
)
1392 ? folder_total(ld
->list
) : -1,
1396 /* We don't support directories in #news */
1399 /* human readable name for a folder. memory freed by caller
1400 * This is a jacket to conversion from modified utf7 to utf8.
1402 unsigned char *folder_name_decoded(unsigned char *mailbox
)
1405 s
= (unsigned char *) utf8_from_mutf7((unsigned char *) mailbox
);
1406 if (s
== NULL
) s
= (unsigned char *) cpystr((char *)mailbox
);
1410 /* mutf7 encoded name of a folder, from its name in utf8.
1411 * memory freed by caller.
1413 unsigned char *folder_name_encoded(unsigned char *mailbox
)
1416 if ((s
= utf8_to_mutf7(mailbox
)) == NULL
)
1417 s
= (unsigned char *) cpystr((char *) mailbox
);
1422 mail_list_in_collection(char **mailbox
, char *ref
, char *name
, char *tail
)
1424 int boxlen
, reflen
, taillen
;
1427 boxlen
= strlen(*mailbox
);
1428 reflen
= ref
? strlen(ref
) : 0;
1429 taillen
= tail
? strlen(tail
) : 0;
1432 && (reflen
? !struncmp(*mailbox
, ref
, reflen
)
1433 : (p
= strpbrk(name
, "%*"))
1434 ? !struncmp(*mailbox
, name
, p
- name
)
1435 : !strucmp(*mailbox
,name
))
1437 || (taillen
< boxlen
- reflen
1438 && !strucmp(&(*mailbox
)[boxlen
- taillen
], tail
)))){
1440 (*mailbox
)[boxlen
- taillen
] = '\0';
1442 if(*(*mailbox
+= reflen
))
1446 * else don't worry about context "breakouts" since
1447 * build_folder_list doesn't let the user introduce
1456 * rebuild_folder_list -- free up old list and re-issue commands to build
1460 refresh_folder_list(CONTEXT_S
*context
, int nodirs
, int startover
, MAILSTREAM
**streamp
)
1463 free_folder_list(context
);
1465 build_folder_list(streamp
, context
, NULL
, NULL
,
1466 (NEWS_TEST(context
) ? BFL_LSUB
: BFL_NONE
)
1467 | ((nodirs
) ? BFL_FLDRONLY
: BFL_NONE
));
1472 * free_folder_list - loop thru the context's lists of folders
1473 * clearing all entries without nicknames
1474 * (as those were user provided) AND reset the
1475 * context's find flag.
1477 * NOTE: if fetched() information (e.g., like message counts come back
1478 * in bboard collections), we may want to have a check before
1479 * executing the loop and setting the FIND flag.
1482 free_folder_list(CONTEXT_S
*cntxt
)
1487 * In this case, don't blast the list as it was given to us by the
1488 * user and not the result of a mail_list call...
1490 if(cntxt
->use
& CNTXT_INCMNG
)
1493 if(cntxt
->use
& CNTXT_PRESRV
)
1494 folder_select_preserve(cntxt
);
1496 for(n
= folder_total(FOLDERS(cntxt
)), i
= 0; n
> 0; n
--)
1497 if(folder_entry(i
, FOLDERS(cntxt
))->nickname
)
1498 i
++; /* entry wasn't from LIST */
1500 folder_delete(i
, FOLDERS(cntxt
));
1502 cntxt
->dir
->status
|= CNTXT_NOFIND
; /* do find next time... */
1503 /* or add the fake entry */
1504 cntxt
->use
&= ~(CNTXT_PSEUDO
| CNTXT_PRESRV
| CNTXT_ZOOM
);
1509 * default_save_context - return the default context for saved messages
1512 default_save_context(CONTEXT_S
*cntxt
)
1515 if((cntxt
->use
) & CNTXT_SAVEDFLT
)
1518 cntxt
= cntxt
->next
;
1526 * folder_complete - foldername completion routine
1528 * Result: returns 0 if the folder doesn't have a any completion
1529 * 1 if the folder has a completion (*AND* "name" is
1530 * replaced with the completion)
1534 folder_complete(CONTEXT_S
*context
, char *name
, size_t namelen
, int *completions
)
1536 return(folder_complete_internal(context
, name
, namelen
, completions
, FC_NONE
));
1544 folder_complete_internal(CONTEXT_S
*context
, char *name
, size_t namelen
,
1545 int *completions
, int flags
)
1547 int i
, match
= -1, ftotal
;
1548 char tmp
[MAXFOLDER
+2], *a
, *b
, *fn
, *pat
= NULL
;
1554 if(*name
== '\0' || !context_isambig(name
))
1557 if(!((context
->use
& CNTXT_INCMNG
) || ALL_FOUND(context
))){
1559 * Build the folder list from scratch since we may need to
1560 * traverse hierarchy...
1563 free_folder_list(context
);
1564 snprintf(tmp
, sizeof(tmp
), "%s%c", name
, NEWS_TEST(context
) ? '*' : '%');
1565 build_folder_list(NULL
, context
, tmp
, NULL
,
1566 (NEWS_TEST(context
) & !(flags
& FC_FORCE_LIST
))
1567 ? BFL_LSUB
: BFL_NONE
);
1570 *tmp
= '\0'; /* find uniq substring */
1571 ftotal
= folder_total(FOLDERS(context
));
1572 for(i
= 0; i
< ftotal
; i
++){
1573 f
= folder_entry(i
, FOLDERS(context
));
1576 if(!(NEWS_TEST(context
) || (context
->use
& CNTXT_INCMNG
))){
1577 fn
= folder_last_cmpnt(fn
, context
->dir
->delim
);
1578 pat
= folder_last_cmpnt(pat
, context
->dir
->delim
);
1581 if(!strncmp(fn
, pat
, strlen(pat
))){
1582 if(match
!= -1){ /* oh well, do best we can... */
1585 f
= folder_entry(match
, FOLDERS(context
));
1587 if(!NEWS_TEST(context
))
1588 fn
= folder_last_cmpnt(fn
, context
->dir
->delim
);
1590 strncpy(tmp
, fn
, sizeof(tmp
)-1);
1591 tmp
[sizeof(tmp
)-1] = '\0';
1595 b
= tmp
; /* remember largest common text */
1596 while(*a
&& *b
&& *a
== *b
)
1602 match
= i
; /* bingo?? */
1606 if(match
>= 0){ /* found! */
1607 f
= folder_entry(match
, FOLDERS(context
));
1609 if(!(NEWS_TEST(context
) || (context
->use
& CNTXT_INCMNG
)))
1610 fn
= folder_last_cmpnt(fn
, context
->dir
->delim
);
1612 strncpy(pat
, fn
, namelen
-(pat
-name
));
1613 name
[namelen
-1] = '\0';
1614 if(f
->isdir
&& !f
->isfolder
){
1615 name
[i
= strlen(name
)] = context
->dir
->delim
;
1619 else if(match
== -2){ /* closest we could find */
1620 strncpy(pat
, tmp
, namelen
-(pat
-name
));
1621 name
[namelen
-1] = '\0';
1625 *completions
= ftotal
;
1627 if(!((context
->use
& CNTXT_INCMNG
) || ALL_FOUND(context
)))
1628 free_folder_list(context
);
1630 return((match
>= 0) ? ftotal
: 0);
1635 folder_last_cmpnt(char *s
, int d
)
1640 for(p
= s
; (p
= strindex(p
, d
)); s
= ++p
)
1648 * init_folder_entries - return a piece of memory suitable for attaching
1649 * a list of folders...
1653 init_folder_entries(void)
1655 FLIST
*flist
= (FLIST
*) fs_get(sizeof(FLIST
));
1656 flist
->folders
= (FOLDER_S
**) fs_get(FCHUNK
* sizeof(FOLDER_S
*));
1657 memset((void *)flist
->folders
, 0, (FCHUNK
* sizeof(FOLDER_S
*)));
1658 flist
->allocated
= FCHUNK
;
1665 * new_folder - return a brand new folder entry, with the given name
1668 * NOTE: THIS IS THE ONLY WAY TO PUT A NAME INTO A FOLDER ENTRY!!!
1669 * STRCPY WILL NOT WORK!!!
1672 new_folder(char *name
, long unsigned int hash
)
1675 size_t l
= strlen(name
);
1677 tmp
= (FOLDER_S
*)fs_get(sizeof(FOLDER_S
) + (l
* sizeof(char)));
1678 memset((void *)tmp
, 0, sizeof(FOLDER_S
));
1679 strncpy(tmp
->name
, name
, l
);
1680 tmp
->name
[l
] = '\0';
1681 tmp
->name_len
= (unsigned char) l
;
1682 tmp
->varhash
= hash
;
1688 * folder_entry - folder struct access routine. Permit reference to
1689 * folder structs via index number. Serves two purposes:
1690 * 1) easy way for callers to access folder data
1692 * 2) allows for a custom manager to limit memory use
1693 * under certain rather limited "operating systems"
1694 * who shall renameless, but whose initials are DOS
1699 folder_entry(int i
, FLIST
*flist
)
1701 return((i
>= flist
->used
) ? NULL
:flist
->folders
[i
]);
1706 * free_folder_entries - release all resources associated with the given
1707 * list of folder entries
1710 free_folder_entries(FLIST
**flist
)
1714 if(!(flist
&& *flist
))
1719 if((*flist
)->folders
[i
]->nickname
)
1720 fs_give((void **) &(*flist
)->folders
[i
]->nickname
);
1722 fs_give((void **) &((*flist
)->folders
[i
]));
1725 fs_give((void **) &((*flist
)->folders
));
1726 fs_give((void **) flist
);
1731 * return the number of folders associated with the given folder list
1734 folder_total(FLIST
*flist
)
1736 return((int) flist
->used
);
1741 * return the index number of the given name in the given folder list
1744 folder_index(char *name
, CONTEXT_S
*cntxt
, int flags
)
1750 for(i
= 0; (f
= folder_entry(i
, FOLDERS(cntxt
))); i
++)
1751 if(((flags
& FI_FOLDER
) && (f
->isfolder
|| (cntxt
->use
& CNTXT_INCMNG
)))
1752 || ((flags
& FI_DIR
) && f
->isdir
)){
1753 fname
= FLDR_NAME(f
);
1754 #if defined(DOS) || defined(OS2)
1755 if(flags
& FI_RENAME
){ /* case-dependent for rename */
1756 if(*name
== *fname
&& strcmp(name
, fname
) == 0)
1760 if(toupper((unsigned char)(*name
))
1761 == toupper((unsigned char)(*fname
)) && strucmp(name
, fname
) == 0)
1765 if(*name
== *fname
&& strcmp(name
, fname
) == 0)
1775 * folder_is_nick - check to see if the given name is a nickname
1776 * for some folder in the given context...
1778 * NOTE: no need to check if mm_list_names has been done as
1779 * nicknames can only be set by configuration...
1782 folder_is_nick(char *nickname
, FLIST
*flist
, int flags
)
1787 if(!(nickname
&& *nickname
&& flist
))
1790 while((f
= folder_entry(i
, flist
)) != NULL
){
1792 * The second part of the OR is checking in a case-indep
1793 * way for INBOX. It should be restricted to the context
1794 * to which we add the INBOX folder, which would be either
1795 * the Incoming Folders collection or the first collection
1796 * if there is no Incoming collection. We don't need to check
1797 * the collection because nickname assignment has already
1798 * done that for us. Most folders don't have nicknames, only
1799 * incoming folders and folders like inbox if not in incoming.
1802 && (!strcmp(nickname
, f
->nickname
)
1803 || (!strucmp(nickname
, f
->nickname
)
1804 && !strucmp(nickname
, ps_global
->inbox_name
)))){
1805 char source
[MAILTMPLEN
], *target
= NULL
;
1808 * If f is a maildrop, then we want to return the
1809 * destination folder, not the whole #move thing.
1811 if(!(flags
& FN_WHOLE_NAME
)
1812 && check_for_move_mbox(f
->name
, source
, sizeof(source
), &target
))
1826 folder_is_target_of_nick(char *longname
, CONTEXT_S
*cntxt
)
1830 FLIST
*flist
= NULL
;
1832 if(cntxt
&& cntxt
== ps_global
->context_list
)
1833 flist
= FOLDERS(cntxt
);
1835 if(!(longname
&& *longname
&& flist
))
1838 while((f
= folder_entry(i
, flist
)) != NULL
){
1839 if(f
->nickname
&& f
->name
&& !strcmp(longname
, f
->name
))
1840 return(f
->nickname
);
1849 /*----------------------------------------------------------------------
1850 Insert the given folder name into the sorted folder list
1851 associated with the given context. Only allow ambiguous folder
1852 names IF associated with a nickname.
1854 Args: index -- Index to insert at, OR insert in sorted order if -1
1855 folder -- folder structure to insert into list
1856 flist -- folder list to insert folder into
1859 DON'T count on the folder pointer being valid after this returns
1860 *** ALL FOLDER ELEMENT READS SHOULD BE THROUGH folder_entry() ***
1864 folder_insert(int index
, FOLDER_S
*folder
, FLIST
*flist
)
1866 /* requested index < 0 means add to sorted list */
1867 if(index
< 0 && (index
= folder_total(flist
)) > 0)
1868 index
= folder_insert_sorted(index
/ 2, 0, index
, folder
, flist
,
1869 (ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_FIRST
)
1870 ? compare_folders_dir_alpha
1871 : (ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_LAST
)
1872 ? compare_folders_alpha_dir
1873 : compare_folders_alpha
);
1875 folder_insert_index(folder
, index
, flist
);
1881 * folder_insert_sorted - Insert given folder struct into given list
1882 * observing sorting specified by given
1883 * comparison function
1886 folder_insert_sorted(int index
, int min_index
, int max_index
, FOLDER_S
*folder
,
1887 FLIST
*flist
, int (*compf
)(FOLDER_S
*, FOLDER_S
*))
1891 return(((i
= (*compf
)(folder_entry(index
, flist
), folder
)) == 0)
1894 ? ((++index
>= max_index
)
1896 : ((*compf
)(folder_entry(index
, flist
), folder
) > 0)
1898 : folder_insert_sorted((index
+ max_index
) / 2, index
,
1899 max_index
, folder
, flist
, compf
))
1900 : ((index
<= min_index
)
1902 : folder_insert_sorted((min_index
+ index
) / 2, min_index
, index
,
1903 folder
, flist
, compf
)));
1908 * folder_insert_index - Insert the given folder struct into the global list
1909 * at the given index.
1912 folder_insert_index(FOLDER_S
*folder
, int index
, FLIST
*flist
)
1914 register FOLDER_S
**flp
, **iflp
;
1916 /* if index is beyond size, place at end of list */
1917 index
= MIN(index
, flist
->used
);
1920 if(flist
->used
+ 1 > flist
->allocated
){
1921 flist
->allocated
+= FCHUNK
;
1922 fs_resize((void **)&(flist
->folders
),
1923 flist
->allocated
* sizeof(FOLDER_S
*));
1926 /* shift array left */
1927 iflp
= &((flist
->folders
)[index
]);
1928 for(flp
= &((flist
->folders
)[flist
->used
]);
1932 flist
->folders
[index
] = folder
;
1938 resort_folder_list(FLIST
*flist
)
1940 if(flist
&& folder_total(flist
) > 1 && flist
->folders
)
1941 qsort(flist
->folders
, folder_total(flist
), sizeof(flist
->folders
[0]),
1942 (ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_FIRST
)
1943 ? compare_folders_dir_alpha_qsort
1944 : (ps_global
->fld_sort_rule
== FLD_SORT_ALPHA_DIR_LAST
)
1945 ? compare_folders_alpha_dir_qsort
1946 : compare_folders_alpha_qsort
);
1950 /*----------------------------------------------------------------------
1951 Removes a folder at the given index in the given context's
1954 Args: index -- Index in folder list of folder to be removed
1955 flist -- folder list
1958 folder_delete(int index
, FLIST
*flist
)
1964 && (index
< 0 || index
>= flist
->used
))
1965 return; /* bogus call! */
1967 if((f
= folder_entry(index
, flist
))->nickname
)
1968 fs_give((void **)&(f
->nickname
));
1970 fs_give((void **) &(flist
->folders
[index
]));
1971 for(i
= index
; i
< flist
->used
- 1; i
++)
1972 flist
->folders
[i
] = flist
->folders
[i
+1];
1979 /*----------------------------------------------------------------------
1980 compare two names for qsort, case independent
1982 Args: pointers to strings to compare
1984 Result: integer result of strcmp of the names. Uses simple
1985 efficiency hack to speed the string comparisons up a bit.
1987 ----------------------------------------------------------------------*/
1989 compare_names(const qsort_t
*x
, const qsort_t
*y
)
1991 char *a
= *(char **)x
, *b
= *(char **)y
;
1993 #define CMPI(X,Y) ((X)[0] - (Y)[0])
1994 #define UCMPI(X,Y) ((isupper((unsigned char)((X)[0])) \
1995 ? (X)[0] - 'A' + 'a' : (X)[0]) \
1996 - (isupper((unsigned char)((Y)[0])) \
1997 ? (Y)[0] - 'A' + 'a' : (Y)[0]))
1999 /*---- Inbox always sorts to the top ----*/
2000 if(UCMPI(a
, ps_global
->inbox_name
) == 0
2001 && strucmp(a
, ps_global
->inbox_name
) == 0)
2002 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : -1);
2003 else if((UCMPI(b
, ps_global
->inbox_name
)) == 0
2004 && strucmp(b
, ps_global
->inbox_name
) == 0)
2005 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : 1);
2007 /*----- The sent-mail folder, is always next unless... ---*/
2008 else if(F_OFF(F_SORT_DEFAULT_FCC_ALPHA
, ps_global
)
2009 && CMPI(a
, ps_global
->VAR_DEFAULT_FCC
) == 0
2010 && strcmp(a
, ps_global
->VAR_DEFAULT_FCC
) == 0)
2011 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : -1);
2012 else if(F_OFF(F_SORT_DEFAULT_FCC_ALPHA
, ps_global
)
2013 && CMPI(b
, ps_global
->VAR_DEFAULT_FCC
) == 0
2014 && strcmp(b
, ps_global
->VAR_DEFAULT_FCC
) == 0)
2015 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : 1);
2017 /*----- The saved-messages folder, is always next unless... ---*/
2018 else if(F_OFF(F_SORT_DEFAULT_SAVE_ALPHA
, ps_global
)
2019 && CMPI(a
, ps_global
->VAR_DEFAULT_SAVE_FOLDER
) == 0
2020 && strcmp(a
, ps_global
->VAR_DEFAULT_SAVE_FOLDER
) == 0)
2021 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : -1);
2022 else if(F_OFF(F_SORT_DEFAULT_SAVE_ALPHA
, ps_global
)
2023 && CMPI(b
, ps_global
->VAR_DEFAULT_SAVE_FOLDER
) == 0
2024 && strcmp(b
, ps_global
->VAR_DEFAULT_SAVE_FOLDER
) == 0)
2025 return((CMPI(a
, b
) == 0 && strcmp(a
, b
) == 0) ? 0 : 1);
2028 return((r
= CMPI(a
, b
)) ? r
: strcmp(a
, b
));
2032 /*----------------------------------------------------------------------
2033 compare two folder structs for ordering alphabetically
2035 Args: pointers to folder structs to compare
2037 Result: integer result of dir-bit and strcmp of the folders.
2038 ----------------------------------------------------------------------*/
2040 compare_folders_alpha(FOLDER_S
*f1
, FOLDER_S
*f2
)
2043 char *f1name
= FLDR_NAME(f1
),
2044 *f2name
= FLDR_NAME(f2
);
2046 return(((i
= compare_names(&f1name
, &f2name
)) != 0)
2047 ? i
: (f2
->isdir
- f1
->isdir
));
2052 compare_folders_alpha_qsort(const qsort_t
*a1
, const qsort_t
*a2
)
2054 FOLDER_S
*f1
= *((FOLDER_S
**) a1
);
2055 FOLDER_S
*f2
= *((FOLDER_S
**) a2
);
2057 return(compare_folders_alpha(f1
, f2
));
2061 /*----------------------------------------------------------------------
2062 compare two folder structs alphabetically with dirs first
2064 Args: pointers to folder structs to compare
2066 Result: integer result of dir-bit and strcmp of the folders.
2067 ----------------------------------------------------------------------*/
2069 compare_folders_dir_alpha(FOLDER_S
*f1
, FOLDER_S
*f2
)
2073 if((i
= (f2
->isdir
- f1
->isdir
)) == 0){
2074 char *f1name
= FLDR_NAME(f1
),
2075 *f2name
= FLDR_NAME(f2
);
2077 return(compare_names(&f1name
, &f2name
));
2085 compare_folders_dir_alpha_qsort(const qsort_t
*a1
, const qsort_t
*a2
)
2087 FOLDER_S
*f1
= *((FOLDER_S
**) a1
);
2088 FOLDER_S
*f2
= *((FOLDER_S
**) a2
);
2090 return(compare_folders_dir_alpha(f1
, f2
));
2094 /*----------------------------------------------------------------------
2095 compare two folder structs alphabetically with dirs last
2097 Args: pointers to folder structs to compare
2099 Result: integer result of dir-bit and strcmp of the folders.
2100 ----------------------------------------------------------------------*/
2102 compare_folders_alpha_dir(FOLDER_S
*f1
, FOLDER_S
*f2
)
2106 if((i
= (f1
->isdir
- f2
->isdir
)) == 0){
2107 char *f1name
= FLDR_NAME(f1
),
2108 *f2name
= FLDR_NAME(f2
);
2110 return(compare_names(&f1name
, &f2name
));
2118 compare_folders_alpha_dir_qsort(const qsort_t
*a1
, const qsort_t
*a2
)
2120 FOLDER_S
*f1
= *((FOLDER_S
**) a1
);
2121 FOLDER_S
*f2
= *((FOLDER_S
**) a2
);
2123 return(compare_folders_alpha_dir(f1
, f2
));
2128 * Find incoming folders and update the unseen counts
2132 folder_unseen_count_updater(unsigned long flags
)
2135 time_t oldest
, started_checking
;
2136 int ftotal
, i
, first
= -1;
2140 * We would only do this if there is an incoming collection, the
2141 * user wants us to monitor, and we're in the folder screen.
2143 if(ps_global
->in_folder_screen
&& F_ON(F_ENABLE_INCOMING_CHECKING
, ps_global
)
2144 && (ctxt
=ps_global
->context_list
) && ctxt
->use
& CNTXT_INCMNG
2145 && (ftotal
= folder_total(FOLDERS(ctxt
)))){
2147 * Search through the last_unseen_update times to find
2148 * the one that was updated longest ago, and start with
2149 * that one. We don't want to delay long doing these
2150 * checks so may only check some of them each time we
2151 * get called. An update time equal to 1 means don't check
2152 * this folder at all.
2154 for(i
= 0; i
< ftotal
; i
++){
2155 f
= folder_entry(i
, FOLDERS(ctxt
));
2156 if(f
&& LUU_YES(f
->last_unseen_update
)){
2158 oldest
= f
->last_unseen_update
;
2164 * Now first is the first in the list that
2165 * should ever be checked. Next find the
2166 * one that should be checked next, the one
2167 * that was checked longest ago.
2170 for(i
= 1; i
< ftotal
; i
++){
2171 f
= folder_entry(i
, FOLDERS(ctxt
));
2172 if(f
&& LUU_YES(f
->last_unseen_update
) && f
->last_unseen_update
< oldest
){
2174 oldest
= f
->last_unseen_update
;
2179 /* now first is the next one to be checked */
2181 started_checking
= time(0);
2183 for(i
= first
; i
< ftotal
; i
++){
2184 /* update the next one */
2185 f
= folder_entry(i
, FOLDERS(ctxt
));
2186 if(f
&& LUU_YES(f
->last_unseen_update
)
2187 && (flags
& UFU_FORCE
2188 /* or it's been long enough and we've not been in this function too long */
2189 || (((time(0) - f
->last_unseen_update
) >= ps_global
->inc_check_interval
)
2190 && ((time(0) - started_checking
) < MIN(4,ps_global
->inc_check_timeout
)))))
2191 update_folder_unseen(f
, ctxt
, flags
, NULL
);
2194 for(i
= 0; i
< first
; i
++){
2195 f
= folder_entry(i
, FOLDERS(ctxt
));
2196 if(f
&& LUU_YES(f
->last_unseen_update
)
2197 && (flags
& UFU_FORCE
2198 || (((time(0) - f
->last_unseen_update
) >= ps_global
->inc_check_interval
)
2199 && ((time(0) - started_checking
) < MIN(4,ps_global
->inc_check_timeout
)))))
2200 update_folder_unseen(f
, ctxt
, flags
, NULL
);
2207 * Update the count of unseen in the FOLDER_S struct
2208 * for this folder. This will update if the time
2209 * interval has passed or if the FORCE flag is set.
2212 update_folder_unseen(FOLDER_S
*f
, CONTEXT_S
*ctxt
, unsigned long flags
,
2213 MAILSTREAM
*this_is_the_stream
)
2217 int use_imap_interval
= 0;
2218 int stream_is_open
= 0;
2219 unsigned long orig_unseen
, orig_new
, orig_tot
;
2220 char mailbox_name
[MAILTMPLEN
];
2221 char *target
= NULL
;
2224 if(!f
|| !LUU_YES(f
->last_unseen_update
))
2228 context_apply(mailbox_name
, ctxt
, f
->name
, MAILTMPLEN
);
2230 if(!mailbox_name
[0])
2233 if(check_for_move_mbox(mailbox_name
, NULL
, 0, &target
)){
2237 * If this maildrop is the currently open stream use that.
2238 * I'm not altogether sure that this is a good way to
2242 && ((strm
=ps_global
->mail_stream
)
2244 && (!strcmp(target
,strm
->mailbox
)
2245 || !strcmp(target
,strm
->original_mailbox
)))){
2250 MAILSTREAM
*m
= NULL
;
2252 stream_is_open
= (this_is_the_stream
2253 || (m
=sp_stream_get(mailbox_name
, SP_MATCH
| SP_RO_OK
))
2254 || ((m
=ps_global
->mail_stream
) && !sp_dead_stream(m
)
2255 && same_stream_and_mailbox(mailbox_name
,m
))
2256 || (!IS_REMOTE(mailbox_name
)
2257 && (m
=already_open_stream(mailbox_name
, AOS_NONE
)))) ? 1 : 0;
2260 if(!this_is_the_stream
)
2261 this_is_the_stream
= m
;
2265 * If it's IMAP or local we use a shorter interval.
2267 d
= mail_valid(NIL
, mailbox_name
, (char *) NIL
);
2268 if((d
&& !strcmp(d
->name
, "imap")) || !IS_REMOTE(mailbox_name
))
2269 use_imap_interval
++;
2274 * Update if forced, or if it's been a while, or if we have a
2275 * stream open to this mailbox already.
2277 if(flags
& UFU_FORCE
2279 || ((use_imap_interval
2280 && (now
- f
->last_unseen_update
) >= ps_global
->inc_check_interval
)
2281 || ((now
- f
->last_unseen_update
) >= ps_global
->inc_second_check_interval
))){
2282 unsigned long tot
, uns
, new;
2283 unsigned long *totp
= NULL
, *unsp
= NULL
, *newp
= NULL
;
2285 orig_valid
= f
->unseen_valid
;
2286 orig_unseen
= f
->unseen
;
2288 orig_tot
= f
->total
;
2290 if(F_ON(F_INCOMING_CHECKING_RECENT
, ps_global
))
2295 if(F_ON(F_INCOMING_CHECKING_TOTAL
, ps_global
))
2298 f
->unseen_valid
= 0;
2300 dprint((9, "update_folder_unseen(%s)", FLDR_NAME(f
)));
2301 if(get_recent_in_folder(mailbox_name
, newp
, unsp
, totp
, this_is_the_stream
)){
2302 f
->last_unseen_update
= time(0);
2303 f
->unseen_valid
= 1;
2314 dprint((9, "update_folder_unseen(%s): original: %s%s%s%s",
2316 F_ON(F_INCOMING_CHECKING_RECENT
,ps_global
) ? "new=" : "unseen=",
2317 F_ON(F_INCOMING_CHECKING_RECENT
,ps_global
) ? comatose(f
->new) : comatose(f
->unseen
),
2318 F_ON(F_INCOMING_CHECKING_TOTAL
,ps_global
) ? " tot=" : "",
2319 F_ON(F_INCOMING_CHECKING_TOTAL
,ps_global
) ? comatose(f
->total
) : ""));
2323 && ((F_ON(F_INCOMING_CHECKING_RECENT
, ps_global
)
2324 && orig_new
!= f
->new)
2326 (F_OFF(F_INCOMING_CHECKING_RECENT
, ps_global
)
2327 && orig_unseen
!= f
->unseen
)
2329 (F_ON(F_INCOMING_CHECKING_TOTAL
, ps_global
)
2330 && orig_tot
!= f
->total
))){
2332 if(ps_global
->in_folder_screen
)
2333 ps_global
->noticed_change_in_unseen
= 1;
2335 dprint((9, "update_folder_unseen(%s): changed: %s%s%s%s",
2337 F_ON(F_INCOMING_CHECKING_RECENT
,ps_global
) ? "new=" : "unseen=",
2338 F_ON(F_INCOMING_CHECKING_RECENT
,ps_global
) ? comatose(f
->new) : comatose(f
->unseen
),
2339 F_ON(F_INCOMING_CHECKING_TOTAL
,ps_global
) ? " tot=" : "",
2340 F_ON(F_INCOMING_CHECKING_TOTAL
,ps_global
) ? comatose(f
->total
) : ""));
2342 if(flags
& UFU_ANNOUNCE
2343 && ((F_ON(F_INCOMING_CHECKING_RECENT
, ps_global
)
2344 && orig_new
< f
->new)
2346 (F_OFF(F_INCOMING_CHECKING_RECENT
, ps_global
)
2347 && orig_unseen
< f
->unseen
))){
2348 if(F_ON(F_INCOMING_CHECKING_RECENT
, ps_global
))
2349 q_status_message3(SM_ASYNC
, 1, 3, "%s: %s %s",
2350 FLDR_NAME(f
), comatose(f
->new),
2353 q_status_message3(SM_ASYNC
, 1, 3, "%s: %s %s",
2354 FLDR_NAME(f
), comatose(f
->unseen
),
2360 f
->last_unseen_update
= LUU_NOMORECHK
; /* no further checking */
2366 update_folder_unseen_by_stream(MAILSTREAM
*strm
, unsigned long flags
)
2370 char mailbox_name
[MAILTMPLEN
], *target
;
2371 char *cn
, tmp
[MAILTMPLEN
];
2375 * Attempt to figure out which incoming folder this stream
2376 * is open to, if any, so we can update the unseen counters.
2379 && F_ON(F_ENABLE_INCOMING_CHECKING
, ps_global
)
2380 && (ctxt
=ps_global
->context_list
) && ctxt
->use
& CNTXT_INCMNG
2381 && (ftotal
= folder_total(FOLDERS(ctxt
)))){
2382 for(i
= 0; i
< ftotal
; i
++){
2383 f
= folder_entry(i
, FOLDERS(ctxt
));
2384 context_apply(mailbox_name
, ctxt
, f
->name
, MAILTMPLEN
);
2386 if((check_for_move_mbox(mailbox_name
, NULL
, 0, &target
)
2388 && (!strcmp(target
,strm
->mailbox
)
2389 || !strcmp(target
,strm
->original_mailbox
)))
2390 || same_stream_and_mailbox(mailbox_name
, strm
)
2391 || (!IS_REMOTE(mailbox_name
) && (cn
=mailboxfile(tmp
,mailbox_name
)) && (*cn
) && (!strcmp(cn
, strm
->mailbox
) || !strcmp(cn
, strm
->original_mailbox
)))){
2392 /* if we failed earlier on this one, give it another go */
2393 if(f
->last_unseen_update
== LUU_NOMORECHK
)
2394 init_incoming_unseen_data(ps_global
, f
);
2396 update_folder_unseen(f
, ctxt
, flags
, strm
);
2405 * Find the number of new, unseen, and the total number of
2406 * messages in mailbox_name.
2407 * If the corresponding arg is NULL it will skip the work
2408 * necessary for that flag.
2410 * Returns 1 if successful, 0 if not.
2413 get_recent_in_folder(char *mailbox_name
, long unsigned int *new,
2414 long unsigned int *unseen
, long unsigned int *total
,
2415 MAILSTREAM
*this_is_the_stream
)
2417 MAILSTREAM
*strm
= NIL
;
2418 unsigned long tot
= 0L, nw
= 0L, uns
= 0L;
2421 char *target
= NULL
;
2423 long excluded
, flags
;
2424 extern MAILSTATUS mm_status_result
;
2426 dprint((9, "get_recent_in_folder(%s)", mailbox_name
? mailbox_name
: "?"));
2428 if(check_for_move_mbox(mailbox_name
, NULL
, 0, &target
)){
2433 * If this maildrop is the currently open stream use that.
2436 && ((strm
=ps_global
->mail_stream
)
2438 && (!strcmp(target
,strm
->mailbox
)
2439 || !strcmp(target
,strm
->original_mailbox
)))){
2441 msgmap
= sp_msgmap(strm
);
2442 excluded
= any_lflagged(msgmap
, MN_EXLD
);
2444 tot
= strm
->nmsgs
- excluded
;
2447 if(sp_recent_since_visited(strm
) == 0)
2450 nw
= count_flagged(strm
, F_RECENT
| F_UNSEEN
| F_UNDEL
);
2454 uns
= count_flagged(strm
, F_UNSEEN
| F_UNDEL
);
2461 /* else fall through to just open it case */
2464 /* do we already have it selected? */
2466 && ((strm
= this_is_the_stream
)
2467 || (strm
= sp_stream_get(mailbox_name
, SP_MATCH
| SP_RO_OK
))
2468 || (!IS_REMOTE(mailbox_name
)
2469 && (strm
= already_open_stream(mailbox_name
, AOS_NONE
))))){
2473 * Unfortunately, we have to worry about excluded
2474 * messages. The user doesn't want to have
2475 * excluded messages count in the totals, especially
2476 * recent excluded messages.
2479 msgmap
= sp_msgmap(strm
);
2480 excluded
= any_lflagged(msgmap
, MN_EXLD
);
2482 tot
= strm
->nmsgs
- excluded
;
2485 if(sp_recent_since_visited(strm
) == 0)
2488 nw
= count_flagged(strm
, F_RECENT
| F_UNSEEN
| F_UNDEL
);
2492 uns
= count_flagged(strm
, F_UNSEEN
| F_UNDEL
);
2500 * No, but how about another stream to same server which
2501 * could be used for a STATUS command?
2503 else if(!gotit
&& (strm
= sp_stream_get(mailbox_name
, SP_SAME
))
2504 && modern_imap_stream(strm
)){
2508 flags
|= SA_MESSAGES
;
2516 mm_status_result
.flags
= 0L;
2518 pine_mail_status(strm
, mailbox_name
, flags
);
2520 if(mm_status_result
.flags
& SA_MESSAGES
){
2521 tot
= mm_status_result
.messages
;
2526 if(!(total
&& !gotit
)){
2528 if(mm_status_result
.flags
& SA_RECENT
){
2529 nw
= mm_status_result
.recent
;
2537 if(!((total
|| new) && !gotit
)){
2539 if(mm_status_result
.flags
& SA_UNSEEN
){
2540 uns
= mm_status_result
.unseen
;
2549 /* Let's just Select it. */
2555 * Traditional unix folders don't notice new mail if
2556 * they are opened readonly. So maildrops with unix folder
2557 * targets will snarf to the file but the stream that is
2558 * opened won't see the new mail. So make all maildrop
2559 * opens non-readonly here.
2561 openflags
= SP_USEPOOL
| SP_TEMPUSE
| (maildrop
? 0 : OP_READONLY
);
2563 saved_timeout
= (long) mail_parameters(NULL
, GET_OPENTIMEOUT
, NULL
);
2564 mail_parameters(NULL
, SET_OPENTIMEOUT
, (void *) (long) ps_global
->inc_check_timeout
);
2565 strm
= pine_mail_open(NULL
, mailbox_name
, openflags
, NULL
);
2566 mail_parameters(NULL
, SET_OPENTIMEOUT
, (void *) saved_timeout
);
2570 msgmap
= sp_msgmap(strm
);
2571 excluded
= any_lflagged(msgmap
, MN_EXLD
);
2573 tot
= strm
->nmsgs
- excluded
;
2576 if(sp_recent_since_visited(strm
) == 0)
2579 nw
= count_flagged(strm
, F_RECENT
| F_UNSEEN
| F_UNDEL
);
2583 uns
= count_flagged(strm
, F_UNSEEN
| F_UNDEL
);
2590 pine_mail_close(strm
);
2610 clear_incoming_valid_bits(void)
2616 if(F_ON(F_ENABLE_INCOMING_CHECKING
, ps_global
)
2617 && (ctxt
=ps_global
->context_list
) && ctxt
->use
& CNTXT_INCMNG
2618 && (ftotal
= folder_total(FOLDERS(ctxt
))))
2619 for(i
= 0; i
< ftotal
; i
++){
2620 f
= folder_entry(i
, FOLDERS(ctxt
));
2621 init_incoming_unseen_data(ps_global
, f
);
2627 selected_folders(CONTEXT_S
*context
)
2631 n
= folder_total(FOLDERS(context
));
2632 for(total
= i
= 0; i
< n
; i
++)
2633 if(folder_entry(i
, FOLDERS(context
))->selected
)
2645 selp
= (SELECTED_S
*)fs_get(sizeof(SELECTED_S
));
2647 selp
->reference
= NULL
;
2648 selp
->folders
= NULL
;
2656 * Free the current selected struct and all of the
2657 * following structs in the list
2660 free_selected(SELECTED_S
**selp
)
2662 if(!selp
|| !(*selp
))
2665 free_selected(&((*selp
)->sub
));
2667 free_strlist(&(*selp
)->folders
);
2668 if((*selp
)->reference
)
2669 fs_give((void **) &(*selp
)->reference
);
2671 fs_give((void **) selp
);
2676 folder_select_preserve(CONTEXT_S
*context
)
2679 && !(context
->use
& CNTXT_PARTFIND
)){
2682 SELECTED_S
*selp
= &context
->selected
;
2685 if(!context
->dir
->ref
){
2686 if(!context
->selected
.folders
)
2687 slpp
= &context
->selected
.folders
;
2692 if(!selected_folders(context
))
2697 if(!strcmp(selp
->reference
, context
->dir
->ref
))
2700 selp
->sub
= new_selected();
2702 slpp
= &(selp
->folders
);
2705 folder_n
= folder_total(FOLDERS(context
));
2707 for(i
= 0; i
< folder_n
; i
++)
2708 if((fp
= folder_entry(i
, FOLDERS(context
)))->selected
){
2709 *slpp
= new_strlist(fp
->name
);
2710 slpp
= &(*slpp
)->next
;
2713 /* Only remember "ref" if any folders were selected */
2714 if(selp
->folders
&& context
->dir
->ref
)
2715 selp
->reference
= cpystr(context
->dir
->ref
);
2717 selp
->zoomed
= (context
->use
& CNTXT_ZOOM
) != 0;
2723 folder_select_restore(CONTEXT_S
*context
)
2728 && !(context
->use
& CNTXT_PARTFIND
)){
2730 SELECTED_S
*selp
, *pselp
= NULL
;
2733 selp
= &(context
->selected
);
2735 if(context
->dir
->ref
){
2738 while(selp
&& strcmp(selp
->reference
, context
->dir
->ref
)){
2746 found
= selp
->folders
!= 0;
2748 for(slp
= selp
->folders
; slp
; slp
= slp
->next
)
2750 && (i
= folder_index(slp
->name
, context
, FI_FOLDER
)) >= 0){
2751 folder_entry(i
, FOLDERS(context
))->selected
= 1;
2755 /* Used, always clean them up */
2756 free_strlist(&selp
->folders
);
2758 fs_give((void **) &selp
->reference
);
2761 context
->use
|= CNTXT_ZOOM
;
2764 if(!(selp
== &context
->selected
)){
2766 pselp
->sub
= selp
->sub
;
2767 fs_give((void **) &selp
);