1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: folder.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
3 #endif
4 
5 /*
6  * ========================================================================
7  * Copyright 2013-2021 Eduardo Chappa
8  * Copyright 2006-2008 University of Washington
9  *
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
13  *
14  *     http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * ========================================================================
17  */
18 
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"
33 
34 
35 typedef struct _build_folder_list_data {
36     long	 mask;			/* bitmap of responses to ignore    */
37     LISTARGS_S	 args;
38     LISTRES_S	 response;
39     int          is_move_folder;
40     FLIST	*list;
41 } BFL_DATA_S;
42 
43 
44 #define FCHUNK  64
45 
46 
47 /*
48  * Internal prototypes
49  */
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);
66 
67 
68 char *
folder_lister_desc(CONTEXT_S * cntxt,FDIR_S * fdp)69 folder_lister_desc(CONTEXT_S *cntxt, FDIR_S *fdp)
70 {
71     char *p, *q;
72     unsigned char *fname;
73 
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);
81 
82     return(cpystr(tmp_20k_buf));
83 }
84 
85 
86 void
reset_context_folders(CONTEXT_S * cntxt)87 reset_context_folders(CONTEXT_S *cntxt)
88 {
89     CONTEXT_S *tc;
90 
91     for(tc = cntxt; tc && tc->prev ; tc = tc->prev)
92       ;				/* start at beginning */
93 
94     for( ; tc ; tc = tc->next){
95 	free_folder_list(tc);
96 
97 	while(tc->dir->prev){
98 	    FDIR_S *tp = tc->dir->prev;
99 	    free_fdir(&tc->dir, 0);
100 	    tc->dir = tp;
101 	}
102     }
103 }
104 
105 
106 /*
107  * next_folder_dir - return a directory structure with the folders it
108  *		     contains
109  */
110 FDIR_S *
next_folder_dir(CONTEXT_S * context,char * new_dir,int build_list,MAILSTREAM ** streamp)111 next_folder_dir(CONTEXT_S *context, char *new_dir, int build_list, MAILSTREAM **streamp)
112 {
113     char      tmp[MAILTMPLEN], dir[3];
114     FDIR_S   *tmp_fp, *fp;
115 
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;
120     dir[1] = '\0';
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 */
128     context->dir      = fp;
129 
130     if(build_list)
131       build_folder_list(streamp, context, NULL, NULL,
132 			NEWS_TEST(context) ? BFL_LSUB : BFL_NONE);
133 
134     context->dir = tmp_fp;
135     return(fp);
136 }
137 
138 
139 /*
140  * Return which pinerc incoming folder #index is in.
141  */
142 EditWhich
config_containing_inc_fldr(FOLDER_S * folder)143 config_containing_inc_fldr(FOLDER_S *folder)
144 {
145     char    **t;
146     int       i, keep_going = 1, inheriting = 0;
147     struct variable *v = &ps_global->vars[V_INCOMING_FOLDERS];
148 
149     if(v->is_fixed)
150       return(None);
151 
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)){
156 		keep_going = 0;
157 
158 		if(!strcmp(tmp_20k_buf, INHERIT))
159 		  inheriting = 1;
160 		else if(folder->varhash == line_hash(tmp_20k_buf))
161 		  return(Post);
162 	    }
163 	}
164     }
165 
166     if(inheriting)
167       keep_going = 1;
168 
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))
174 	      return(Main);
175 	}
176     }
177 
178     return(None);
179 }
180 
181 
182 /*----------------------------------------------------------------------
183       Format the given folder name for display for the user
184 
185    Args: folder -- The folder name to fix up
186 
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.
190  ----*/
191 
192 char *
pretty_fn(char * folder)193 pretty_fn(char *folder)
194 {
195     if(!strucmp(folder, ps_global->inbox_name))
196       return(ps_global->inbox_name);
197     else
198       return(folder);
199 }
200 
201 
202 /*----------------------------------------------------------------------
203   Return the path delimiter for the given folder on the given server
204 
205   Args: folder -- folder type for delimiter
206 
207     ----*/
208 int
get_folder_delimiter(char * folder)209 get_folder_delimiter(char *folder)
210 {
211     MM_LIST_S    ldata;
212     LISTRES_S	 response;
213     int          ourstream = 0;
214 
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));
218 
219     if(*folder == '{'
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,
224 					 NULL)) != NULL){
225 	    ourstream++;
226 	}
227 	else{
228 	    return(FEX_ERROR);
229 	}
230     }
231 
232     pine_mail_list(ldata.stream, folder, "", NULL);
233 
234     if(ourstream)
235       pine_mail_close(ldata.stream);
236 
237     return(response.delim);
238 }
239 
240 
241 /*----------------------------------------------------------------------
242        Check to see if folder exists in given context
243 
244   Args: cntxt -- context inwhich to interpret "file" arg
245         file  -- name of folder to check
246 
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
250 		 FEX_ERROR  on error
251 
252   The two existence return values above may be logically OR'd
253 
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().
258 
259     ----*/
260 int
folder_exists(CONTEXT_S * cntxt,char * file)261 folder_exists(CONTEXT_S *cntxt, char *file)
262 {
263     return(folder_name_exists(cntxt, file, NULL));
264 }
265 
266 
267 /*----------------------------------------------------------------------
268        Check to see if folder exists in given context
269 
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
273 
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
277 		 FEX_ERROR  on error
278 
279   The two existence return values above may be logically OR'd
280 
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().
285 
286     ----*/
287 int
folder_name_exists(CONTEXT_S * cntxt,char * file,char ** fullpath)288 folder_name_exists(CONTEXT_S *cntxt, char *file, char **fullpath)
289 {
290     MM_LIST_S    ldata;
291     EXISTDATA_S	 parms;
292     int          we_cancel = 0, res;
293     char	*p, reference[MAILTMPLEN], tmp[MAILTMPLEN], *tfolder = NULL;
294 
295     /*
296      * No folder means "inbox".
297      */
298     if(*file == '{' && (p = strchr(file, '}')) && (!*(p+1))){
299 	size_t l;
300 
301 	l = strlen(file)+strlen("inbox");
302 	tfolder = (char *) fs_get((l+1) * sizeof(char));
303 	snprintf(tfolder, l+1, "%s%s", file, "inbox");
304 	file = tfolder;
305 	cntxt = NULL;
306     }
307 
308     mm_list_info = &ldata;		/* tie down global reference */
309     memset(&ldata, 0, sizeof(ldata));
310     ldata.filter = mail_list_exists;
311 
312     ldata.stream = sp_stream_get(context_apply(tmp, cntxt, file, sizeof(tmp)),
313 				 SP_SAME);
314 
315     memset(ldata.data = &parms, 0, sizeof(EXISTDATA_S));
316 
317     /*
318      * If no preset reference string, must be at top of context
319      */
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)){
323 	    reference[0] = '\0';
324 	    parms.args.reference = reference;
325 	}
326 	else if(!(parms.args.reference = cntxt->dir->ref)){
327 	    char *p;
328 
329 	    if((p = strstr(cntxt->context, "%s")) != NULL){
330 		strncpy(parms.args.reference = reference,
331 			cntxt->context,
332 			MIN(p - cntxt->context, sizeof(reference)-1));
333 		reference[MIN(p - cntxt->context, sizeof(reference)-1)] = '\0';
334 		if(*(p += 2))
335 		  parms.args.tail = p;
336 	    }
337 	    else
338 	      parms.args.reference = cntxt->context;
339 	}
340 
341 	parms.fullname = fullpath;
342     }
343 
344     ps_global->mm_log_error = 0;
345     ps_global->noshow_error = 1;
346 
347     we_cancel = busy_cue(NULL, NULL, 1);
348 
349     parms.args.name = file;
350 
351     res = pine_mail_list(ldata.stream, parms.args.reference, parms.args.name,
352 			 &ldata.options);
353 
354     if(we_cancel)
355       cancel_busy_cue(-1);
356 
357     ps_global->noshow_error = 0;
358 
359     if(cntxt && cntxt->dir && parms.response.delim)
360       cntxt->dir->delim = parms.response.delim;
361 
362     if(tfolder)
363       fs_give((void **)&tfolder);
364     return(((res == FALSE) || ps_global->mm_log_error)
365 	    ? FEX_ERROR
366 	    : (((parms.response.isfile)
367 		  ? FEX_ISFILE : 0 )
368 	       | ((parms.response.isdir)
369 		  ? FEX_ISDIR : 0)
370 	       | ((parms.response.ismarked)
371 		  ? FEX_ISMARKED : 0)
372 	       | ((parms.response.unmarked)
373 		  ? FEX_UNMARKED : 0)));
374 }
375 
376 
377 void
mail_list_exists(MAILSTREAM * stream,char * mailbox,int delim,long int attribs,void * data,unsigned int options)378 mail_list_exists(MAILSTREAM *stream, char *mailbox, int delim, long int attribs,
379 		 void *data, unsigned int options)
380 {
381     if(delim)
382       ((EXISTDATA_S *) data)->response.delim = delim;
383 
384     if(mailbox && *mailbox){
385 	if(!(attribs & LATT_NOSELECT)){
386 	    ((EXISTDATA_S *) data)->response.isfile  = 1;
387 	    ((EXISTDATA_S *) data)->response.count  += 1;
388 	}
389 
390 	if(!(attribs & LATT_NOINFERIORS)){
391 	    ((EXISTDATA_S *) data)->response.isdir  = 1;
392 	    ((EXISTDATA_S *) data)->response.count  += 1;
393 	}
394 
395 	if(attribs & LATT_MARKED)
396 	  ((EXISTDATA_S *) data)->response.ismarked = 1;
397 
398 	/* don't mark #move folders unmarked */
399 	if(attribs & LATT_UNMARKED && !(options & PML_IS_MOVE_MBOX))
400 	  ((EXISTDATA_S *) data)->response.unmarked = 1;
401 
402 	if(attribs & LATT_HASCHILDREN)
403 	  ((EXISTDATA_S *) data)->response.haschildren = 1;
404 
405 	if(attribs & LATT_HASNOCHILDREN)
406 	  ((EXISTDATA_S *) data)->response.hasnochildren = 1;
407 
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)))){
414 	    char   *p;
415 	    size_t  alloclen;
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);
421 
422 	    /*
423 	     * Fully qualify (in the c-client name structure sense)
424 	     * anything that's not in the context of the "reference"...
425 	     */
426 	    if(*((EXISTDATA_S *) data)->fullname)
427 	      fs_give((void **) ((EXISTDATA_S *) data)->fullname);
428 
429 	    alloclen = len;
430 	    *((EXISTDATA_S *) data)->fullname = (char *) fs_get(alloclen);
431 	    if(*mailbox != '{'
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;
438 	    }
439 	    else
440 	      p = *((EXISTDATA_S *) data)->fullname;
441 
442 	    strncpy(p, mailbox, alloclen-(p-(*((EXISTDATA_S *) data)->fullname)));
443 	    (*((EXISTDATA_S *) data)->fullname)[alloclen-1] = '\0';;
444 	}
445     }
446 }
447 
448 
449 void
mail_list_response(MAILSTREAM * stream,char * mailbox,int delim,long int attribs,void * data,unsigned int options)450 mail_list_response(MAILSTREAM *stream, char *mailbox, int delim, long int attribs,
451 		   void *data, unsigned int options)
452 {
453     int counted = 0;
454 
455     if(delim)
456       ((LISTRES_S *) data)->delim = delim;
457 
458     if(mailbox && *mailbox){
459 	if(!(attribs & LATT_NOSELECT)){
460 	    counted++;
461 	    ((LISTRES_S *) data)->isfile  = 1;
462 	    ((LISTRES_S *) data)->count	 += 1;
463 	}
464 
465 	if(!(attribs & LATT_NOINFERIORS)){
466 	    ((LISTRES_S *) data)->isdir  = 1;
467 
468 	    if(!counted)
469 	      ((LISTRES_S *) data)->count += 1;
470 	}
471 
472 	if(attribs & LATT_HASCHILDREN)
473 	  ((LISTRES_S *) data)->haschildren = 1;
474 
475 	if(attribs & LATT_HASNOCHILDREN)
476 	  ((LISTRES_S *) data)->hasnochildren = 1;
477     }
478 }
479 
480 
481 char *
folder_as_breakout(CONTEXT_S * cntxt,char * name)482 folder_as_breakout(CONTEXT_S *cntxt, char *name)
483 {
484     if(context_isambig(name)){		/* if simple check doesn't pan out */
485 	char tmp[2*MAILTMPLEN], *p, *f;	/* look harder */
486 
487 	if(!cntxt->dir->delim){
488 	    (void) context_apply(tmp, cntxt, "", sizeof(tmp)/2);
489 	    cntxt->dir->delim = get_folder_delimiter(tmp);
490 	}
491 
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';
502 		    return(cpystr(tmp));
503 		}
504 		else
505 		  return(cpystr(name));
506 	    }
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';
511 		f	      = NULL;
512 		(void)folder_name_exists(cntxt, tmp, &f);
513 		if(f){
514 		    snprintf(tmp, sizeof(tmp), "%s%s",f,p);
515 		    tmp[sizeof(tmp)-1] = '\0';
516 		    fs_give((void **) &f);
517 		    return(cpystr(tmp));
518 		}
519 	    }
520 	}
521     }
522 
523     return(NULL);
524 }
525 
526 
527 /*----------------------------------------------------------------------
528  Initialize global list of contexts for folder collections.
529 
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).
534 
535   ----*/
536 void
init_folders(struct pine * ps)537 init_folders(struct pine *ps)
538 {
539     CONTEXT_S  *tc, *top = NULL, **clist;
540     int         i, prime = 0;
541 
542     clist = ⊤
543 
544     /*
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...
547      */
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;
552     }
553 
554     /*
555      * Build context that's a list of folders the user's defined
556      * as receiving new messages.  At some point, this should
557      * probably include adding a prefix with the new message count.
558      * fake new context...
559      */
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 */
564 	if(tc->label)
565 	  fs_give((void **) &tc->label);
566 
567 	/* TRANSLATORS: a label */
568 	tc->label = cpystr(_("Incoming Message Folders"));
569 
570 	*clist = tc;
571 	clist  = &tc->next;
572 
573 	init_incoming_folder_list(ps, tc);
574     }
575 
576     /*
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...
580      */
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];
586 	  tc->var.i = i;
587       }
588 
589 
590     /*
591      * Whoah cowboy!!!  Guess we couldn't find a valid folder
592      * collection???
593      */
594     if(!prime)
595       alpine_panic(_("No folder collections defined"));
596 
597     /*
598      * At this point, insert the INBOX mapping as the leading
599      * folder entry of the first collection...
600      */
601     init_inbox_mapping(ps->VAR_INBOX_PATH, top);
602 
603     set_news_spec_current_val(TRUE, TRUE);
604 
605     /*
606      * If news groups, loop thru list adding to collection list
607      */
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];
614 	  tc->var.i   = i;
615       }
616 
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)
622       if(top->next)
623 	top->next->prev = top;
624 
625 #ifdef	DEBUG
626       dump_contexts();
627 #endif
628 }
629 
630 
631 /*
632  * Add incoming list of folders to context.
633  */
634 void
init_incoming_folder_list(struct pine * ps,CONTEXT_S * cntxt)635 init_incoming_folder_list(struct pine *ps, CONTEXT_S *cntxt)
636 {
637     int       i;
638     char     *folder_string, *nickname;
639     FOLDER_S *f;
640 
641     for(i = 0; ps->VAR_INCOMING_FOLDERS[i] ; i++){
642 	/*
643 	 * Parse folder line for nickname and folder name.
644 	 * No nickname on line is OK.
645 	 */
646 	get_pair(ps->VAR_INCOMING_FOLDERS[i], &nickname, &folder_string,0,0);
647 
648 	/*
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
651 	 * above!
652 	 */
653 	if(!folder_string || !strucmp(ps->inbox_name, folder_string)){
654 	    if(folder_string)
655 	      fs_give((void **)&folder_string);
656 
657 	    if(nickname)
658 	      fs_give((void **)&nickname);
659 
660 	    continue;
661 	}
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);
665 	}
666 
667 	f = new_folder(folder_string,
668 		       line_hash(ps->VAR_INCOMING_FOLDERS[i]));
669 	f->isfolder = 1;
670 	fs_give((void **)&folder_string);
671 
672 	if(nickname){
673 	    if(strucmp(ps->inbox_name, nickname)){
674 		f->nickname = nickname;
675 		f->name_len = strlen(f->nickname);
676 	    }
677 	    else
678 	      fs_give((void **)&nickname);
679 	}
680 
681 	init_incoming_unseen_data(ps, f);
682 
683 	folder_insert(f->nickname
684 		      && (strucmp(f->nickname, ps->inbox_name) == 0)
685 			    ? -1 : folder_total(FOLDERS(cntxt)),
686 		      f, FOLDERS(cntxt));
687     }
688 }
689 
690 
691 void
init_incoming_unseen_data(struct pine * ps,FOLDER_S * f)692 init_incoming_unseen_data(struct pine *ps, FOLDER_S *f)
693 {
694     int j, check_this = 0;
695 
696     if(!f)
697       return;
698 
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 */
703 	else{
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)))
707 		  check_this++;
708 	    }
709 	}
710     }
711 
712     if(check_this)
713       f->last_unseen_update = LUU_INIT;
714     else
715       f->last_unseen_update = LUU_NEVERCHK;
716 }
717 
718 
719 void
reinit_incoming_folder_list(struct pine * ps,CONTEXT_S * context)720 reinit_incoming_folder_list(struct pine *ps, CONTEXT_S *context)
721 {
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);
726 }
727 
728 
729 /*
730  *
731  */
732 void
init_inbox_mapping(char * path,CONTEXT_S * cntxt)733 init_inbox_mapping(char *path, CONTEXT_S *cntxt)
734 {
735     FOLDER_S *f;
736     int check_this, j;
737 
738     /*
739      * If mapping already exists, blast it and replace it below...
740      */
741     if((f = folder_entry(0, FOLDERS(cntxt)))
742        && f->nickname && !strcmp(f->nickname, ps_global->inbox_name))
743       folder_delete(0, FOLDERS(cntxt));
744 
745     if(path){
746 	f = new_folder(path, 0);
747 	f->nickname = cpystr(ps_global->inbox_name);
748 	f->name_len = strlen(f->nickname);
749     }
750     else
751       f = new_folder(ps_global->inbox_name, 0);
752 
753     f->isfolder = 1;
754 
755     /* see if this folder is in the monitoring list */
756     check_this = 0;
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 */
760 	else{
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)))
764 		  check_this++;
765 	    }
766 	}
767     }
768 
769     if(check_this)
770       f->last_unseen_update = LUU_INIT;
771     else
772       f->last_unseen_update = LUU_NEVERCHK;
773 
774     folder_insert(0, f, FOLDERS(cntxt));
775 }
776 
777 
778 FDIR_S *
new_fdir(char * ref,char * view,int wildcard)779 new_fdir(char *ref, char *view, int wildcard)
780 {
781     FDIR_S *rv = (FDIR_S *) fs_get(sizeof(FDIR_S));
782 
783     memset((void *) rv, 0, sizeof(FDIR_S));
784 
785     /* Monkey with the view to make sure it has wildcard? */
786     if(view && *view){
787 	rv->view.user = cpystr(view);
788 
789 	/*
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...
793 	 */
794 	if(wildcard == '*'){	/* must be #news. */
795 	    if(strindex(view, wildcard))
796 	      rv->view.internal = cpystr(view);
797 	}
798 	else{			/* must be mail */
799 	    char *p;
800 
801 	    if((p = strpbrk(view, "*%")) != NULL){
802 		rv->view.internal = p = cpystr(view);
803 		while((p = strpbrk(p, "*%")) != NULL)
804 		  *p++ = '%';	/* convert everything to '%' */
805 	    }
806 	}
807 
808 	if(!rv->view.internal){
809 	    size_t l;
810 
811 	    l = strlen(view)+2;
812 	    rv->view.internal = (char *) fs_get((l+1) * sizeof(char));
813 	    snprintf(rv->view.internal, l+1, "%c%s%c", wildcard, view, wildcard);
814 	}
815     }
816     else{
817 	rv->view.internal = (char *) fs_get(2 * sizeof(char));
818 	snprintf(rv->view.internal, 2, "%c", wildcard);
819     }
820 
821     if(ref)
822       rv->ref = ref;
823 
824     rv->folders = init_folder_entries();
825     rv->status = CNTXT_NOFIND;
826     return(rv);
827 }
828 
829 
830 void
free_fdir(FDIR_S ** f,int recur)831 free_fdir(FDIR_S **f, int recur)
832 {
833     if(f && *f){
834 	if((*f)->prev && recur)
835 	  free_fdir(&(*f)->prev, 1);
836 
837 	if((*f)->ref)
838 	  fs_give((void **)&(*f)->ref);
839 
840 	if((*f)->view.user)
841 	  fs_give((void **)&(*f)->view.user);
842 
843 	if((*f)->view.internal)
844 	  fs_give((void **)&(*f)->view.internal);
845 
846 	if((*f)->desc)
847 	  fs_give((void **)&(*f)->desc);
848 
849 	free_folder_entries(&(*f)->folders);
850 	fs_give((void **) f);
851     }
852 }
853 
854 
855 int
update_bboard_spec(char * bboard,char * buf,size_t buflen)856 update_bboard_spec(char *bboard, char *buf, size_t buflen)
857 {
858     char *p = NULL, *origbuf;
859     int	  nntp = 0, bracket = 0;
860 
861     origbuf = buf;
862 
863     if(*bboard == '*'
864        || (*bboard == '{' && (p = strindex(bboard, '}')) && *(p+1) == '*')){
865 	/* server name ? */
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))
869 	      nntp++;
870 
871 	if(*bboard == '*')
872 	  bboard++;
873 
874 	if(!nntp){
875 	  /*
876 	   * See if path portion looks newsgroup-ish while being aware
877 	   * of the "view" portion of the spec...
878 	   */
879 	  for(p = bboard; *p; p++)
880 	    if(*p == '['){
881 		if(bracket)		/* only one set allowed! */
882 		  break;
883 		else
884 		  bracket++;
885 	    }
886 	    else if(*p == ']'){
887 		if(bracket != 1)	/* must be closing bracket */
888 		  break;
889 		else
890 		  bracket++;
891 	    }
892 	    else if(!(isalnum((unsigned char) *p) || strindex(".-", *p)))
893 	      break;
894 	}
895 
896 	snprintf(buf, buflen-(buf-origbuf), "%s%s%s",
897 		(!nntp && *p) ? "#public" : "#news.",
898 		(!nntp && *p && *bboard != '/') ? "/" : "",
899 		bboard);
900 
901 	return(1);
902     }
903 
904     return(0);
905 }
906 
907 
908 /*
909  * build_folder_list - call mail_list to fetch us a list of folders
910  *		       from the given context.
911  */
912 void
build_folder_list(MAILSTREAM ** stream,CONTEXT_S * context,char * pat,char * content,int flags)913 build_folder_list(MAILSTREAM **stream, CONTEXT_S *context, char *pat, char *content, int flags)
914 {
915     MM_LIST_S  ldata;
916     BFL_DATA_S response;
917     int	       local_open = 0, we_cancel = 0, resort = 0;
918     char       reference[2*MAILTMPLEN], *p;
919 
920     if(!(context->dir->status & CNTXT_NOFIND)
921        || (context->dir->status & CNTXT_PARTFIND))
922       return;				/* find already done! */
923 
924     dprint((7, "build_folder_list: %s %s\n",
925 	       context ? context->context : "NULL",
926 	       pat ? pat : "NULL"));
927 
928     we_cancel = busy_cue(NULL, NULL, 1);
929 
930     /*
931      * Set up the pattern of folder name's to match within the
932      * given context.
933      */
934     if(!pat || ((*pat == '*' || *pat == '%') && *(pat+1) == '\0')){
935 	context->dir->status &= ~CNTXT_NOFIND;	/* let'em know we tried */
936 	pat = context->dir->view.internal;
937     }
938     else
939       context->use |= CNTXT_PARTFIND;	/* or are in a partial find */
940 
941     memset(mm_list_info = &ldata, 0, sizeof(MM_LIST_S));
942     ldata.filter = (NEWS_TEST(context)) ? mail_lsub_filter : mail_list_filter;
943     memset(ldata.data = &response, 0, sizeof(BFL_DATA_S));
944     response.list = FOLDERS(context);
945 
946     if(flags & BFL_FLDRONLY)
947       response.mask = LATT_NOSELECT;
948 
949     /*
950      * if context is associated with a server, prepare a stream for
951      * sending our request.
952      */
953     if(*context->context == '{'){
954 	/*
955 	 * Try using a stream we've already got open...
956 	 */
957 	if(stream && *stream
958 	   && !(ldata.stream = same_stream(context->context, *stream))){
959 	    pine_mail_close(*stream);
960 	    *stream = NULL;
961 	}
962 
963 	if(!ldata.stream)
964 	  ldata.stream = sp_stream_get(context->context, SP_MATCH);
965 
966 	if(!ldata.stream)
967 	  ldata.stream = sp_stream_get(context->context, SP_SAME);
968 
969 	/* gotta open a new one? */
970 	if((F_OFF(F_CMBND_FOLDER_DISP, ps_global)
971 		|| context->update == LUU_INIT) && !ldata.stream){
972 	    ldata.stream = mail_cmd_stream(context, &local_open);
973 	    if(stream)
974 	      *stream = ldata.stream;
975 	}
976 
977 	dprint((ldata.stream ? 7 : 1, "build_folder_list: mail_open(%s) %s.\n",
978 		context->server ? context->server : "?",
979 		ldata.stream ? "OK" : "FAILED"));
980 
981 	if(!ldata.stream){
982 	    context->use &= ~CNTXT_PARTFIND;	/* unset partial find bit */
983 	    context->update = LUU_NOMORECHK;
984 	    if(we_cancel)
985 	      cancel_busy_cue(-1);
986 
987 	    return;
988 	}
989     }
990     else if(stream && *stream){			/* no server, simple case */
991 	if(!sp_flagged(*stream, SP_LOCKED))
992 	  pine_mail_close(*stream);
993 
994 	*stream = NULL;
995     }
996 
997     /*
998      * If preset reference string, we're somewhere in the hierarchy.
999      * ELSE we must be at top of context...
1000      */
1001     response.args.name = pat;
1002     if(!(response.args.reference = context->dir->ref)){
1003 	if((p = strstr(context->context, "%s")) != NULL){
1004 	    strncpy(response.args.reference = reference,
1005 		    context->context,
1006 		    MIN(p - context->context, sizeof(reference)/2));
1007 	    reference[MIN(p - context->context, sizeof(reference)/2)] = '\0';
1008 	    if(*(p += 2))
1009 	      response.args.tail = p;
1010 	}
1011 	else
1012 	  response.args.reference = context->context;
1013     }
1014 
1015     if(flags & BFL_SCAN)
1016       mail_scan(ldata.stream, response.args.reference,
1017 		response.args.name, content);
1018     else if(flags & BFL_LSUB)
1019       mail_lsub(ldata.stream, response.args.reference, response.args.name);
1020     else{
1021       set_read_predicted(1);
1022       pine_mail_list(ldata.stream, response.args.reference, response.args.name,
1023 		     &ldata.options);
1024       set_read_predicted(0);
1025     }
1026 
1027     context->update = LUU_INIT;
1028     if(context->dir && response.response.delim)
1029       context->dir->delim = response.response.delim;
1030 
1031     if(!(flags & (BFL_LSUB|BFL_SCAN)) && F_ON(F_QUELL_EMPTY_DIRS, ps_global)){
1032 	LISTRES_S  listres;
1033 	FOLDER_S  *f;
1034 	int	   i;
1035 
1036 	for(i = 0; i < folder_total(response.list); i++)
1037 	  if((f = folder_entry(i, FOLDERS(context)))->isdir){
1038 	      /*
1039 	       * we don't need to do a list if we know already
1040 	       * whether there are children or not.
1041 	       */
1042 	      if(!f->haschildren && !f->hasnochildren){
1043 		  memset(ldata.data = &listres, 0, sizeof(LISTRES_S));
1044 		  ldata.filter = mail_list_response;
1045 
1046 		  if(context->dir->ref)
1047 		    snprintf(reference, sizeof(reference), "%s%s", context->dir->ref, f->name);
1048 		  else
1049 		    context_apply(reference, context, f->name, sizeof(reference)/2);
1050 
1051 		  /* append the delimiter to the reference */
1052 		  for(p = reference; *p; p++)
1053 		    ;
1054 
1055 		  *p++ = context->dir->delim;
1056 		  *p   = '\0';
1057 
1058 		  pine_mail_list(ldata.stream, reference, "%", NULL);
1059 
1060 		  /* anything interesting inside? */
1061 		  f->hasnochildren = (listres.count <= 1L);
1062 		  f->haschildren   = !f->hasnochildren;;
1063 	      }
1064 
1065 	      if(f->hasnochildren){
1066 		  if(f->isfolder){
1067 		      f->isdir = 0;
1068 		      if(ps_global->fld_sort_rule == FLD_SORT_ALPHA_DIR_FIRST
1069 			 || ps_global->fld_sort_rule == FLD_SORT_ALPHA_DIR_LAST)
1070 		        resort = 1;
1071 		  }
1072 		  /*
1073 		   * We don't want to hide directories
1074 		   * that are only directories even if they are
1075 		   * empty. We only want to hide the directory
1076 		   * piece of a dual-use folder when there are
1077 		   * no children in the directory (and the user
1078 		   * most likely thinks of it as a folder instead
1079 		   * of a folder and a directory).
1080 		   */
1081 		  else if(f->isdir && f->isdual){
1082 		      folder_delete(i, FOLDERS(context));
1083 		      i--;
1084 		  }
1085 	      }
1086 	  }
1087     }
1088 
1089     if(resort)
1090       resort_folder_list(response.list);
1091 
1092     if(local_open && !stream)
1093       pine_mail_close(ldata.stream);
1094 
1095     if(context->use & CNTXT_PRESRV)
1096       folder_select_restore(context);
1097 
1098     context->use &= ~CNTXT_PARTFIND;	/* unset partial list bit */
1099     if(we_cancel)
1100       cancel_busy_cue(-1);
1101 }
1102 
1103 
1104 /*
1105  * Validate LIST response to command issued from build_folder_list
1106  *
1107  */
1108 void
mail_list_filter(MAILSTREAM * stream,char * mailbox,int delim,long int attribs,void * data,unsigned int options)1109 mail_list_filter(MAILSTREAM *stream, char *mailbox, int delim, long int attribs, void *data, unsigned int options)
1110 {
1111     BFL_DATA_S *ld = (BFL_DATA_S *) data;
1112     FOLDER_S   *new_f = NULL, *dual_f = NULL;
1113     int         suppress_folder_add = 0;
1114 
1115     if(!ld)
1116       return;
1117 
1118     if(delim)
1119       ld->response.delim = delim;
1120 
1121     /* test against mask of DIS-allowed attributes */
1122     if((ld->mask & attribs)){
1123 	dprint((3, "mail_list_filter: failed attribute test"));
1124 	return;
1125     }
1126 
1127     /*
1128      * First, make sure response fits our "reference" arg
1129      * NOTE: build_folder_list can't supply breakout?
1130      */
1131     if(!mail_list_in_collection(&mailbox, ld->args.reference,
1132 				ld->args.name, ld->args.tail))
1133       return;
1134 
1135     /* ignore dotfolders unless told not to */
1136     if(F_OFF(F_ENABLE_DOT_FOLDERS, ps_global) && *mailbox == '.'){
1137 	dprint((3, "mail_list_filter: dotfolder disallowed"));
1138 	return;
1139     }
1140 
1141     /*
1142      * If this response is INBOX we have to handle it specially.
1143      * The special cases are:
1144      *
1145      * Incoming folders are enabled and this INBOX is in the Incoming
1146      * folders list already. We don't want to list it here, as well,
1147      * unless it is also a directory.
1148      *
1149      * Incoming folders are not enabled, but we inserted INBOX into
1150      * this primary collection (with init_inbox_mapping()) so it is
1151      * already accounted for unless it is also a directory.
1152      *
1153      * Incoming folders are not enabled, but we inserted INBOX into
1154      * the primary collection (which we are not looking at here) so
1155      * it is already accounted for there. We'll add it as a directory
1156      * as well here if that is what is called for.
1157      */
1158     if(!strucmp(mailbox, ps_global->inbox_name)){
1159 	int ftotal, i;
1160 	FOLDER_S *f = NULL;
1161 	char fullname[1000], tmp1[1000], tmp2[1000], *l1, *l2;
1162 
1163 	/* fullname is the name of the mailbox in the list response */
1164 	if(ld->args.reference){
1165 	    strncpy(fullname, ld->args.reference, sizeof(fullname)-1);
1166 	    fullname[sizeof(fullname)-1] = '\0';
1167 	}
1168 	else
1169 	  fullname[0] = '\0';
1170 
1171 	strncat(fullname, mailbox, sizeof(fullname)-strlen(fullname)-1);
1172 	fullname[sizeof(fullname)-1] = '\0';
1173 
1174 	/* check if Incoming Folders are enabled */
1175 	if(ps_global->context_list && ps_global->context_list->use & CNTXT_INCMNG){
1176 	    int this_inbox_is_in_incoming = 0;
1177 
1178 	    /*
1179 	     * Figure out if this INBOX is already in the Incoming list.
1180 	     */
1181 
1182 	    /* compare fullname to each incoming folder */
1183 	    ftotal = folder_total(FOLDERS(ps_global->context_list));
1184 	    for(i = 0; i < ftotal && !this_inbox_is_in_incoming; i++){
1185 		f = folder_entry(i, FOLDERS(ps_global->context_list));
1186 		if(f && f->name){
1187 		    if(same_remote_mailboxes(fullname, f->name)
1188 		       || ((!IS_REMOTE(fullname) && !IS_REMOTE(f->name))
1189 			   && (l1=mailboxfile(tmp1,fullname))
1190 			   && (l2=mailboxfile(tmp2,f->name))
1191 			   && !strcmp(l1,l2)))
1192 		      this_inbox_is_in_incoming++;
1193 		}
1194 	    }
1195 
1196 	    if(this_inbox_is_in_incoming){
1197 		/*
1198 		 * Don't add a folder for this, only a directory if called for.
1199 		 * If it isn't a directory, skip it.
1200 		 */
1201 		if(!(delim && !(attribs & LATT_NOINFERIORS)))
1202 		  return;
1203 
1204 		suppress_folder_add++;
1205 	    }
1206 	}
1207 	else{
1208 	    int inbox_is_in_this_collection = 0;
1209 
1210 	    /* is INBOX already in this folder list? */
1211 	    ftotal = folder_total(ld->list);
1212 	    for(i = 0; i < ftotal && !inbox_is_in_this_collection; i++){
1213 		f = folder_entry(i, ld->list);
1214 	        if(!strucmp(FLDR_NAME(f), ps_global->inbox_name))
1215 		  inbox_is_in_this_collection++;
1216 	    }
1217 
1218 	    if(inbox_is_in_this_collection){
1219 
1220 		/*
1221 		 * Inbox is already inserted in this collection. Unless
1222 		 * it is also a directory, we are done.
1223 		 */
1224 		if(!(delim && !(attribs & LATT_NOINFERIORS)))
1225 		  return;
1226 
1227 		/*
1228 		 * Since it is also a directory, what we do depends on
1229 		 * the F_SEP feature. If that feature is not set we just
1230 		 * want to mark the existing entry dual-use. If F_SEP is
1231 		 * set we want to add a new directory entry.
1232 		 */
1233 		if(F_ON(F_SEPARATE_FLDR_AS_DIR, ps_global)){
1234 		    f->isdir = 0;
1235 		    f->haschildren = 0;
1236 		    f->hasnochildren = 0;
1237 		    /* fall through and add a new directory */
1238 		}
1239 		else{
1240 		    /* mark existing entry dual-use and return */
1241 		    ld->response.count++;
1242 		    ld->response.isdir = 1;
1243 		    f->isdir = 1;
1244 		    if(attribs & LATT_HASCHILDREN)
1245 		      f->haschildren = 1;
1246 
1247 		    if(attribs & LATT_HASNOCHILDREN)
1248 		      f->hasnochildren = 1;
1249 
1250 		    return;
1251 		}
1252 
1253 		suppress_folder_add++;
1254 	    }
1255 	    else{
1256 		int found_it = 0;
1257 		int this_inbox_is_primary_inbox = 0;
1258 
1259 		/*
1260 		 * See if this INBOX is the same as the INBOX we inserted
1261 		 * in the primary collection.
1262 		 */
1263 
1264 		/* first find the existing INBOX entry */
1265 		ftotal = folder_total(FOLDERS(ps_global->context_list));
1266 		for(i = 0; i < ftotal && !found_it; i++){
1267 		    f = folder_entry(i, FOLDERS(ps_global->context_list));
1268 		    if(!strucmp(FLDR_NAME(f), ps_global->inbox_name))
1269 		      found_it++;
1270 		}
1271 
1272 		if(found_it && f && f->name){
1273 		    if(same_remote_mailboxes(fullname, f->name)
1274 		       || ((!IS_REMOTE(fullname) && !IS_REMOTE(f->name))
1275 			   && (l1=mailboxfile(tmp1,fullname))
1276 			   && (l2=mailboxfile(tmp2,f->name))
1277 			   && !strcmp(l1,l2)))
1278 		      this_inbox_is_primary_inbox++;
1279 		}
1280 
1281 		if(this_inbox_is_primary_inbox){
1282 		    /*
1283 		     * Don't add a folder for this, only a directory if called for.
1284 		     * If it isn't a directory, skip it.
1285 		     */
1286 		    if(!(delim && !(attribs & LATT_NOINFERIORS)))
1287 		      return;
1288 
1289 		    suppress_folder_add++;
1290 		}
1291 	    }
1292 	}
1293     }
1294 
1295     /* is it a mailbox? */
1296     if(!(attribs & LATT_NOSELECT) && !suppress_folder_add){
1297 	ld->response.count++;
1298 	ld->response.isfile = 1;
1299 	new_f = new_folder(mailbox, 0);
1300 	new_f->isfolder = 1;
1301 
1302 	if(F_ON(F_SEPARATE_FLDR_AS_DIR, ps_global)){
1303 	    folder_insert(-1, new_f, ld->list);
1304 	    dual_f = new_f;
1305 	    new_f = NULL;
1306 	}
1307     }
1308 
1309     /* directory? */
1310     if(delim && !(attribs & LATT_NOINFERIORS)){
1311 	ld->response.count++;
1312 	ld->response.isdir = 1;
1313 
1314 	if(!new_f)
1315 	  new_f = new_folder(mailbox, 0);
1316 
1317 	new_f->isdir = 1;
1318 	if(attribs & LATT_HASCHILDREN)
1319 	  new_f->haschildren = 1;
1320 	if(attribs & LATT_HASNOCHILDREN)
1321 	  new_f->hasnochildren = 1;
1322 
1323 	/*
1324 	 * When we have F_SEPARATE_FLDR_AS_DIR we still want to know
1325 	 * whether the name really represents both so that we don't
1326 	 * inadvertently delete both when the user meant one or the
1327 	 * other.
1328 	 */
1329 	if(dual_f){
1330 	    if(attribs & LATT_HASCHILDREN)
1331 	      dual_f->haschildren = 1;
1332 
1333 	    if(attribs & LATT_HASNOCHILDREN)
1334 	      dual_f->hasnochildren = 1;
1335 
1336 	    dual_f->isdual = 1;
1337 	    new_f->isdual = 1;
1338 	}
1339     }
1340 
1341     if(new_f)
1342       folder_insert(-1, new_f, ld->list);
1343 
1344     if(attribs & LATT_MARKED)
1345       ld->response.ismarked = 1;
1346 
1347     /* don't mark #move folders unmarked */
1348     if(attribs & LATT_UNMARKED && !(options & PML_IS_MOVE_MBOX))
1349       ld->response.unmarked = 1;
1350 
1351     if(attribs & LATT_HASCHILDREN)
1352       ld->response.haschildren = 1;
1353 
1354     if(attribs & LATT_HASNOCHILDREN)
1355       ld->response.hasnochildren = 1;
1356 }
1357 
1358 
1359 /*
1360  * Validate LSUB response to command issued from build_folder_list
1361  *
1362  */
1363 void
mail_lsub_filter(MAILSTREAM * stream,char * mailbox,int delim,long int attribs,void * data,unsigned int options)1364 mail_lsub_filter(MAILSTREAM *stream, char *mailbox, int delim, long int attribs,
1365 		 void *data, unsigned int options)
1366 {
1367     BFL_DATA_S *ld = (BFL_DATA_S *) data;
1368     FOLDER_S   *new_f = NULL;
1369     char       *ref;
1370 
1371     if(delim)
1372       ld->response.delim = delim;
1373 
1374     /* test against mask of DIS-allowed attributes */
1375     if((ld->mask & attribs)){
1376 	dprint((3, "mail_lsub_filter: failed attribute test"));
1377 	return;
1378     }
1379 
1380     /* Normalize mailbox and reference strings re: namespace */
1381     if(!strncmp(mailbox, "#news.", 6))
1382       mailbox += 6;
1383 
1384     if(!strncmp(ref = ld->args.reference, "#news.", 6))
1385       ref += 6;
1386 
1387     if(!mail_list_in_collection(&mailbox, ref, ld->args.name, ld->args.tail))
1388       return;
1389 
1390     if(!(attribs & LATT_NOSELECT)){
1391 	ld->response.count++;
1392 	ld->response.isfile = 1;
1393 	new_f = new_folder(mailbox, 0);
1394 	new_f->isfolder = 1;
1395 	folder_insert(F_ON(F_READ_IN_NEWSRC_ORDER, ps_global)
1396 			? folder_total(ld->list) : -1,
1397 		      new_f, ld->list);
1398     }
1399 
1400     /* We don't support directories in #news */
1401 }
1402 
1403 /* human readable name for a folder. memory freed by caller
1404  * This is a jacket to conversion from modified utf7 to utf8.
1405  */
folder_name_decoded(unsigned char * mailbox)1406 unsigned char *folder_name_decoded(unsigned char *mailbox)
1407 {
1408   unsigned char *s;
1409   s = (unsigned char *) utf8_from_mutf7((unsigned char *) mailbox);
1410   if (s == NULL) s = (unsigned char *) cpystr((char *)mailbox);
1411   return s;
1412 }
1413 
1414 /* mutf7 encoded name of a folder, from its name in utf8.
1415  * memory freed by caller.
1416  */
folder_name_encoded(unsigned char * mailbox)1417 unsigned char *folder_name_encoded(unsigned char *mailbox)
1418 {
1419   unsigned char *s;
1420   if ((s = utf8_to_mutf7(mailbox)) == NULL)
1421      s = (unsigned char *) cpystr((char *) mailbox);
1422   return s;
1423 }
1424 
1425 int
mail_list_in_collection(char ** mailbox,char * ref,char * name,char * tail)1426 mail_list_in_collection(char **mailbox, char *ref, char *name, char *tail)
1427 {
1428     int	  boxlen, reflen, taillen;
1429     char *p;
1430 
1431     boxlen  = strlen(*mailbox);
1432     reflen  = ref ? strlen(ref) : 0;
1433     taillen = tail ? strlen(tail) : 0;
1434 
1435     if(boxlen
1436        && (reflen ? !struncmp(*mailbox, ref, reflen)
1437 		  : (p = strpbrk(name, "%*"))
1438 		       ? !struncmp(*mailbox, name, p - name)
1439 		       : !strucmp(*mailbox,name))
1440        && (!taillen
1441 	   || (taillen < boxlen - reflen
1442 	       && !strucmp(&(*mailbox)[boxlen - taillen], tail)))){
1443 	if(taillen)
1444 	  (*mailbox)[boxlen - taillen] = '\0';
1445 
1446 	if(*(*mailbox += reflen))
1447 	  return(TRUE);
1448     }
1449     /*
1450      * else don't worry about context "breakouts" since
1451      * build_folder_list doesn't let the user introduce
1452      * one...
1453      */
1454 
1455     return(FALSE);
1456 }
1457 
1458 
1459 /*
1460  * rebuild_folder_list -- free up old list and re-issue commands to build
1461  *			  a new list.
1462  */
1463 void
refresh_folder_list(CONTEXT_S * context,int nodirs,int startover,MAILSTREAM ** streamp)1464 refresh_folder_list(CONTEXT_S *context, int nodirs, int startover, MAILSTREAM **streamp)
1465 {
1466     if(startover)
1467       free_folder_list(context);
1468 
1469     build_folder_list(streamp, context, NULL, NULL,
1470 		      (NEWS_TEST(context) ? BFL_LSUB : BFL_NONE)
1471 		        | ((nodirs) ? BFL_FLDRONLY : BFL_NONE));
1472 }
1473 
1474 
1475 /*
1476  * free_folder_list - loop thru the context's lists of folders
1477  *                     clearing all entries without nicknames
1478  *                     (as those were user provided) AND reset the
1479  *                     context's find flag.
1480  *
1481  * NOTE: if fetched() information (e.g., like message counts come back
1482  *       in bboard collections), we may want to have a check before
1483  *       executing the loop and setting the FIND flag.
1484  */
1485 void
free_folder_list(CONTEXT_S * cntxt)1486 free_folder_list(CONTEXT_S *cntxt)
1487 {
1488     int n, i;
1489 
1490     /*
1491      * In this case, don't blast the list as it was given to us by the
1492      * user and not the result of a mail_list call...
1493      */
1494     if(cntxt->use & CNTXT_INCMNG)
1495       return;
1496 
1497     if(cntxt->use & CNTXT_PRESRV)
1498       folder_select_preserve(cntxt);
1499 
1500     for(n = folder_total(FOLDERS(cntxt)), i = 0; n > 0; n--)
1501       if(folder_entry(i, FOLDERS(cntxt))->nickname)
1502 	i++;					/* entry wasn't from LIST */
1503       else
1504 	folder_delete(i, FOLDERS(cntxt));
1505 
1506     cntxt->dir->status |= CNTXT_NOFIND;		/* do find next time...  */
1507 						/* or add the fake entry */
1508     cntxt->use &= ~(CNTXT_PSEUDO | CNTXT_PRESRV | CNTXT_ZOOM);
1509 }
1510 
1511 
1512 /*
1513  * default_save_context - return the default context for saved messages
1514  */
1515 CONTEXT_S *
default_save_context(CONTEXT_S * cntxt)1516 default_save_context(CONTEXT_S *cntxt)
1517 {
1518     while(cntxt)
1519       if((cntxt->use) & CNTXT_SAVEDFLT)
1520 	return(cntxt);
1521       else
1522 	cntxt = cntxt->next;
1523 
1524     return(NULL);
1525 }
1526 
1527 
1528 
1529 /*
1530  * folder_complete - foldername completion routine
1531  *
1532  *   Result: returns 0 if the folder doesn't have a any completion
1533  *		     1 if the folder has a completion (*AND* "name" is
1534  *		       replaced with the completion)
1535  *
1536  */
1537 int
folder_complete(CONTEXT_S * context,char * name,size_t namelen,int * completions)1538 folder_complete(CONTEXT_S *context, char *name, size_t namelen, int *completions)
1539 {
1540     return(folder_complete_internal(context, name, namelen, completions, FC_NONE));
1541 }
1542 
1543 
1544 /*
1545  *
1546  */
1547 int
folder_complete_internal(CONTEXT_S * context,char * name,size_t namelen,int * completions,int flags)1548 folder_complete_internal(CONTEXT_S *context, char *name, size_t namelen,
1549 			 int *completions, int flags)
1550 {
1551     int	      i, match = -1, ftotal;
1552     char      tmp[MAXFOLDER+2], *a, *b, *fn, *pat = NULL;
1553     FOLDER_S *f;
1554 
1555     if(completions)
1556       *completions = 0;
1557 
1558     if(*name == '\0' || !context_isambig(name))
1559       return(0);
1560 
1561     if(!((context->use & CNTXT_INCMNG) || ALL_FOUND(context))){
1562 	/*
1563 	 * Build the folder list from scratch since we may need to
1564 	 * traverse hierarchy...
1565 	 */
1566 
1567 	free_folder_list(context);
1568 	snprintf(tmp, sizeof(tmp), "%s%c", name, NEWS_TEST(context) ? '*' : '%');
1569 	build_folder_list(NULL, context, tmp, NULL,
1570 			  (NEWS_TEST(context) & !(flags & FC_FORCE_LIST))
1571 			    ? BFL_LSUB : BFL_NONE);
1572     }
1573 
1574     *tmp = '\0';			/* find uniq substring */
1575     ftotal = folder_total(FOLDERS(context));
1576     for(i = 0; i < ftotal; i++){
1577 	f   = folder_entry(i, FOLDERS(context));
1578 	fn  = FLDR_NAME(f);
1579 	pat = name;
1580 	if(!(NEWS_TEST(context) || (context->use & CNTXT_INCMNG))){
1581 	    fn  = folder_last_cmpnt(fn, context->dir->delim);
1582 	    pat = folder_last_cmpnt(pat, context->dir->delim);
1583 	}
1584 
1585 	if(!strncmp(fn, pat, strlen(pat))){
1586 	    if(match != -1){		/* oh well, do best we can... */
1587 		a = fn;
1588 		if(match >= 0){
1589 		    f  = folder_entry(match, FOLDERS(context));
1590 		    fn = FLDR_NAME(f);
1591 		    if(!NEWS_TEST(context))
1592 		      fn = folder_last_cmpnt(fn, context->dir->delim);
1593 
1594 		    strncpy(tmp, fn, sizeof(tmp)-1);
1595 		    tmp[sizeof(tmp)-1] = '\0';
1596 		}
1597 
1598 		match = -2;
1599 		b     = tmp;		/* remember largest common text */
1600 		while(*a && *b && *a == *b)
1601 		  *b++ = *a++;
1602 
1603 		*b = '\0';
1604 	    }
1605 	    else
1606 	      match = i;		/* bingo?? */
1607 	}
1608     }
1609 
1610     if(match >= 0){			/* found! */
1611 	f  = folder_entry(match, FOLDERS(context));
1612 	fn = FLDR_NAME(f);
1613 	if(!(NEWS_TEST(context) || (context->use & CNTXT_INCMNG)))
1614 	  fn = folder_last_cmpnt(fn, context->dir->delim);
1615 
1616 	strncpy(pat, fn, namelen-(pat-name));
1617 	name[namelen-1] = '\0';
1618 	if(f->isdir && !f->isfolder){
1619 	    name[i = strlen(name)] = context->dir->delim;
1620 	    name[i+1] = '\0';
1621 	}
1622     }
1623     else if(match == -2){		/* closest we could find */
1624 	strncpy(pat, tmp, namelen-(pat-name));
1625 	name[namelen-1] = '\0';
1626     }
1627 
1628     if(completions)
1629       *completions = ftotal;
1630 
1631     if(!((context->use & CNTXT_INCMNG) || ALL_FOUND(context)))
1632       free_folder_list(context);
1633 
1634     return((match >= 0) ? ftotal : 0);
1635 }
1636 
1637 
1638 char *
folder_last_cmpnt(char * s,int d)1639 folder_last_cmpnt(char *s, int d)
1640 {
1641     register char *p;
1642 
1643     if(d)
1644       for(p = s; (p = strindex(p, d)); s = ++p)
1645 	;
1646 
1647     return(s);
1648 }
1649 
1650 
1651 /*
1652  * init_folder_entries - return a piece of memory suitable for attaching
1653  *                   a list of folders...
1654  *
1655  */
1656 FLIST *
init_folder_entries(void)1657 init_folder_entries(void)
1658 {
1659     FLIST *flist = (FLIST *) fs_get(sizeof(FLIST));
1660     flist->folders = (FOLDER_S **) fs_get(FCHUNK * sizeof(FOLDER_S *));
1661     memset((void *)flist->folders, 0, (FCHUNK * sizeof(FOLDER_S *)));
1662     flist->allocated = FCHUNK;
1663     flist->used      = 0;
1664     return(flist);
1665 }
1666 
1667 
1668 /*
1669  * new_folder - return a brand new folder entry, with the given name
1670  *              filled in.
1671  *
1672  * NOTE: THIS IS THE ONLY WAY TO PUT A NAME INTO A FOLDER ENTRY!!!
1673  * STRCPY WILL NOT WORK!!!
1674  */
1675 FOLDER_S *
new_folder(char * name,long unsigned int hash)1676 new_folder(char *name, long unsigned int hash)
1677 {
1678     FOLDER_S *tmp;
1679     size_t    l = strlen(name);
1680 
1681     tmp = (FOLDER_S *)fs_get(sizeof(FOLDER_S) + (l * sizeof(char)));
1682     memset((void *)tmp, 0, sizeof(FOLDER_S));
1683     strncpy(tmp->name, name, l);
1684     tmp->name[l] = '\0';
1685     tmp->name_len = (unsigned char) l;
1686     tmp->varhash = hash;
1687     return(tmp);
1688 }
1689 
1690 
1691 /*
1692  * folder_entry - folder struct access routine.  Permit reference to
1693  *                folder structs via index number. Serves two purposes:
1694  *            1) easy way for callers to access folder data
1695  *               conveniently
1696  *            2) allows for a custom manager to limit memory use
1697  *               under certain rather limited "operating systems"
1698  *               who shall renameless, but whose initials are DOS
1699  *
1700  *
1701  */
1702 FOLDER_S *
folder_entry(int i,FLIST * flist)1703 folder_entry(int i, FLIST *flist)
1704 {
1705     return((i >= flist->used) ? NULL:flist->folders[i]);
1706 }
1707 
1708 
1709 /*
1710  * free_folder_entries - release all resources associated with the given
1711  *			 list of folder entries
1712  */
1713 void
free_folder_entries(FLIST ** flist)1714 free_folder_entries(FLIST **flist)
1715 {
1716     register int i;
1717 
1718     if(!(flist && *flist))
1719       return;
1720 
1721     i = (*flist)->used;
1722     while(i--){
1723 	if((*flist)->folders[i]->nickname)
1724 	  fs_give((void **) &(*flist)->folders[i]->nickname);
1725 
1726 	fs_give((void **) &((*flist)->folders[i]));
1727     }
1728 
1729     fs_give((void **) &((*flist)->folders));
1730     fs_give((void **) flist);
1731 }
1732 
1733 
1734 /*
1735  * return the number of folders associated with the given folder list
1736  */
1737 int
folder_total(FLIST * flist)1738 folder_total(FLIST *flist)
1739 {
1740     return((int) flist->used);
1741 }
1742 
1743 
1744 /*
1745  * return the index number of the given name in the given folder list
1746  */
1747 int
folder_index(char * name,CONTEXT_S * cntxt,int flags)1748 folder_index(char *name, CONTEXT_S *cntxt, int flags)
1749 {
1750     register  int i = 0;
1751     FOLDER_S *f;
1752     char     *fname;
1753 
1754     for(i = 0; (f = folder_entry(i, FOLDERS(cntxt))); i++)
1755       if(((flags & FI_FOLDER) && (f->isfolder || (cntxt->use & CNTXT_INCMNG)))
1756 	 || ((flags & FI_DIR) && f->isdir)){
1757 	  fname = FLDR_NAME(f);
1758 #if defined(DOS) || defined(OS2)
1759 	  if(flags & FI_RENAME){	/* case-dependent for rename */
1760 	    if(*name == *fname && strcmp(name, fname) == 0)
1761 	      return(i);
1762 	  }
1763 	  else{
1764 	    if(toupper((unsigned char)(*name))
1765 	       == toupper((unsigned char)(*fname)) && strucmp(name, fname) == 0)
1766 	      return(i);
1767 	  }
1768 #else
1769 	  if(*name == *fname && strcmp(name, fname) == 0)
1770 	    return(i);
1771 #endif
1772       }
1773 
1774     return(-1);
1775 }
1776 
1777 
1778 /*
1779  * folder_is_nick - check to see if the given name is a nickname
1780  *                  for some folder in the given context...
1781  *
1782  *  NOTE: no need to check if mm_list_names has been done as
1783  *        nicknames can only be set by configuration...
1784  */
1785 char *
folder_is_nick(char * nickname,FLIST * flist,int flags)1786 folder_is_nick(char *nickname, FLIST *flist, int flags)
1787 {
1788     register  int  i = 0;
1789     FOLDER_S *f;
1790 
1791     if(!(nickname && *nickname && flist))
1792       return(NULL);
1793 
1794     while((f = folder_entry(i, flist)) != NULL){
1795 	/*
1796 	 * The second part of the OR is checking in a case-indep
1797 	 * way for INBOX. It should be restricted to the context
1798 	 * to which we add the INBOX folder, which would be either
1799 	 * the Incoming Folders collection or the first collection
1800 	 * if there is no Incoming collection. We don't need to check
1801 	 * the collection because nickname assignment has already
1802 	 * done that for us. Most folders don't have nicknames, only
1803 	 * incoming folders and folders like inbox if not in incoming.
1804 	 */
1805 	if(f->nickname
1806 	   && (!strcmp(nickname, f->nickname)
1807 	       || (!strucmp(nickname, f->nickname)
1808 		   && !strucmp(nickname, ps_global->inbox_name)))){
1809 	    char source[MAILTMPLEN], *target = NULL;
1810 
1811 	    /*
1812 	     * If f is a maildrop, then we want to return the
1813 	     * destination folder, not the whole #move thing.
1814 	     */
1815 	    if(!(flags & FN_WHOLE_NAME)
1816 	       && check_for_move_mbox(f->name, source, sizeof(source), &target))
1817 	      return(target);
1818 	    else
1819 	      return(f->name);
1820 	}
1821 	else
1822 	  i++;
1823     }
1824 
1825     return(NULL);
1826 }
1827 
1828 
1829 char *
folder_is_target_of_nick(char * longname,CONTEXT_S * cntxt)1830 folder_is_target_of_nick(char *longname, CONTEXT_S *cntxt)
1831 {
1832     register  int  i = 0;
1833     FOLDER_S *f;
1834     FLIST *flist = NULL;
1835 
1836     if(cntxt && cntxt == ps_global->context_list)
1837       flist = FOLDERS(cntxt);
1838 
1839     if(!(longname && *longname && flist))
1840       return(NULL);
1841 
1842     while((f = folder_entry(i, flist)) != NULL){
1843 	if(f->nickname && f->name && !strcmp(longname, f->name))
1844 	  return(f->nickname);
1845 	else
1846 	  i++;
1847     }
1848 
1849     return(NULL);
1850 }
1851 
1852 
1853 /*----------------------------------------------------------------------
1854   Insert the given folder name into the sorted folder list
1855   associated with the given context.  Only allow ambiguous folder
1856   names IF associated with a nickname.
1857 
1858    Args: index  -- Index to insert at, OR insert in sorted order if -1
1859          folder -- folder structure to insert into list
1860 	 flist  -- folder list to insert folder into
1861 
1862   **** WARNING ****
1863   DON'T count on the folder pointer being valid after this returns
1864   *** ALL FOLDER ELEMENT READS SHOULD BE THROUGH folder_entry() ***
1865 
1866   ----*/
1867 int
folder_insert(int index,FOLDER_S * folder,FLIST * flist)1868 folder_insert(int index, FOLDER_S *folder, FLIST *flist)
1869 {
1870     /* requested index < 0 means add to sorted list */
1871     if(index < 0 && (index = folder_total(flist)) > 0)
1872       index = folder_insert_sorted(index / 2, 0, index, folder, flist,
1873 		     (ps_global->fld_sort_rule == FLD_SORT_ALPHA_DIR_FIRST)
1874 			? compare_folders_dir_alpha
1875 			: (ps_global->fld_sort_rule == FLD_SORT_ALPHA_DIR_LAST)
1876 			     ? compare_folders_alpha_dir
1877 			     : compare_folders_alpha);
1878 
1879     folder_insert_index(folder, index, flist);
1880     return(index);
1881 }
1882 
1883 
1884 /*
1885  * folder_insert_sorted - Insert given folder struct into given list
1886  *			  observing sorting specified by given
1887  *			  comparison function
1888  */
1889 int
folder_insert_sorted(int index,int min_index,int max_index,FOLDER_S * folder,FLIST * flist,int (* compf)(FOLDER_S *,FOLDER_S *))1890 folder_insert_sorted(int index, int min_index, int max_index, FOLDER_S *folder,
1891 		     FLIST *flist, int (*compf)(FOLDER_S *, FOLDER_S *))
1892 {
1893     int	      i;
1894 
1895     return(((i = (*compf)(folder_entry(index, flist), folder)) == 0)
1896 	     ? index
1897 	     : (i < 0)
1898 		? ((++index >= max_index)
1899 		    ? max_index
1900 		    : ((*compf)(folder_entry(index, flist), folder) > 0)
1901 		       ? index
1902 		       : folder_insert_sorted((index + max_index) / 2, index,
1903 					      max_index, folder, flist, compf))
1904 		: ((index <= min_index)
1905 		    ? min_index
1906 		    : folder_insert_sorted((min_index + index) / 2, min_index, index,
1907 					   folder, flist, compf)));
1908 }
1909 
1910 
1911 /*
1912  * folder_insert_index - Insert the given folder struct into the global list
1913  *                 at the given index.
1914  */
1915 void
folder_insert_index(FOLDER_S * folder,int index,FLIST * flist)1916 folder_insert_index(FOLDER_S *folder, int index, FLIST *flist)
1917 {
1918     register FOLDER_S **flp, **iflp;
1919 
1920     /* if index is beyond size, place at end of list */
1921     index = MIN(index, flist->used);
1922 
1923     /* grow array ? */
1924     if(flist->used + 1 > flist->allocated){
1925 	flist->allocated += FCHUNK;
1926 	fs_resize((void **)&(flist->folders),
1927 		  flist->allocated * sizeof(FOLDER_S *));
1928     }
1929 
1930     /* shift array left */
1931     iflp = &((flist->folders)[index]);
1932     for(flp = &((flist->folders)[flist->used]);
1933 	flp > iflp; flp--)
1934       flp[0] = flp[-1];
1935 
1936     flist->folders[index] = folder;
1937     flist->used          += 1;
1938 }
1939 
1940 
1941 void
resort_folder_list(FLIST * flist)1942 resort_folder_list(FLIST *flist)
1943 {
1944     if(flist && folder_total(flist) > 1 && flist->folders)
1945       qsort(flist->folders, folder_total(flist), sizeof(flist->folders[0]),
1946 	    (ps_global->fld_sort_rule == FLD_SORT_ALPHA_DIR_FIRST)
1947 		? compare_folders_dir_alpha_qsort
1948 		: (ps_global->fld_sort_rule == FLD_SORT_ALPHA_DIR_LAST)
1949 		     ? compare_folders_alpha_dir_qsort
1950 		     : compare_folders_alpha_qsort);
1951 }
1952 
1953 
1954 /*----------------------------------------------------------------------
1955     Removes a folder at the given index in the given context's
1956     list.
1957 
1958 Args: index -- Index in folder list of folder to be removed
1959       flist -- folder list
1960  ----*/
1961 void
folder_delete(int index,FLIST * flist)1962 folder_delete(int index, FLIST *flist)
1963 {
1964     register int  i;
1965     FOLDER_S     *f;
1966 
1967     if(flist->used
1968        && (index < 0 || index >= flist->used))
1969       return;				/* bogus call! */
1970 
1971     if((f = folder_entry(index, flist))->nickname)
1972       fs_give((void **)&(f->nickname));
1973 
1974     fs_give((void **) &(flist->folders[index]));
1975     for(i = index; i < flist->used - 1; i++)
1976       flist->folders[i] = flist->folders[i+1];
1977 
1978 
1979     flist->used -= 1;
1980 }
1981 
1982 
1983 /*----------------------------------------------------------------------
1984       compare two names for qsort, case independent
1985 
1986    Args: pointers to strings to compare
1987 
1988  Result: integer result of strcmp of the names.  Uses simple
1989          efficiency hack to speed the string comparisons up a bit.
1990 
1991   ----------------------------------------------------------------------*/
1992 int
compare_names(const qsort_t * x,const qsort_t * y)1993 compare_names(const qsort_t *x, const qsort_t *y)
1994 {
1995     char *a = *(char **)x, *b = *(char **)y;
1996     int r;
1997 #define	CMPI(X,Y)	((X)[0] - (Y)[0])
1998 #define	UCMPI(X,Y)	((isupper((unsigned char)((X)[0]))	\
1999 				? (X)[0] - 'A' + 'a' : (X)[0])	\
2000 			  - (isupper((unsigned char)((Y)[0]))	\
2001 				? (Y)[0] - 'A' + 'a' : (Y)[0]))
2002 
2003     /*---- Inbox always sorts to the top ----*/
2004     if(UCMPI(a, ps_global->inbox_name) == 0
2005        && strucmp(a, ps_global->inbox_name) == 0)
2006       return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : -1);
2007     else if((UCMPI(b, ps_global->inbox_name)) == 0
2008        && strucmp(b, ps_global->inbox_name) == 0)
2009       return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : 1);
2010 
2011     /*----- The sent-mail folder, is always next unless... ---*/
2012     else if(F_OFF(F_SORT_DEFAULT_FCC_ALPHA, ps_global)
2013 	    && CMPI(a, ps_global->VAR_DEFAULT_FCC) == 0
2014 	    && strcmp(a, ps_global->VAR_DEFAULT_FCC) == 0)
2015       return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : -1);
2016     else if(F_OFF(F_SORT_DEFAULT_FCC_ALPHA, ps_global)
2017 	    && CMPI(b, ps_global->VAR_DEFAULT_FCC) == 0
2018 	    && strcmp(b, ps_global->VAR_DEFAULT_FCC) == 0)
2019       return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : 1);
2020 
2021     /*----- The saved-messages folder, is always next unless... ---*/
2022     else if(F_OFF(F_SORT_DEFAULT_SAVE_ALPHA, ps_global)
2023 	    && CMPI(a, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0
2024 	    && strcmp(a, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0)
2025       return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : -1);
2026     else if(F_OFF(F_SORT_DEFAULT_SAVE_ALPHA, ps_global)
2027 	    && CMPI(b, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0
2028 	    && strcmp(b, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0)
2029       return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : 1);
2030 
2031     else
2032       return((r = CMPI(a, b)) ? r : strcmp(a, b));
2033 }
2034 
2035 
2036 /*----------------------------------------------------------------------
2037       compare two folder structs for ordering alphabetically
2038 
2039    Args: pointers to folder structs to compare
2040 
2041  Result: integer result of dir-bit and strcmp of the folders.
2042   ----------------------------------------------------------------------*/
2043 int
compare_folders_alpha(FOLDER_S * f1,FOLDER_S * f2)2044 compare_folders_alpha(FOLDER_S *f1, FOLDER_S *f2)
2045 {
2046     int	  i;
2047     char *f1name = FLDR_NAME(f1),
2048 	 *f2name = FLDR_NAME(f2);
2049 
2050     return(((i = compare_names(&f1name, &f2name)) != 0)
2051 	       ? i : (f2->isdir - f1->isdir));
2052 }
2053 
2054 
2055 int
compare_folders_alpha_qsort(const qsort_t * a1,const qsort_t * a2)2056 compare_folders_alpha_qsort(const qsort_t *a1, const qsort_t *a2)
2057 {
2058     FOLDER_S *f1 = *((FOLDER_S **) a1);
2059     FOLDER_S *f2 = *((FOLDER_S **) a2);
2060 
2061     return(compare_folders_alpha(f1, f2));
2062 }
2063 
2064 
2065 /*----------------------------------------------------------------------
2066       compare two folder structs alphabetically with dirs first
2067 
2068    Args: pointers to folder structs to compare
2069 
2070  Result: integer result of dir-bit and strcmp of the folders.
2071   ----------------------------------------------------------------------*/
2072 int
compare_folders_dir_alpha(FOLDER_S * f1,FOLDER_S * f2)2073 compare_folders_dir_alpha(FOLDER_S *f1, FOLDER_S *f2)
2074 {
2075     int	  i;
2076 
2077     if((i = (f2->isdir - f1->isdir)) == 0){
2078 	char *f1name = FLDR_NAME(f1),
2079 	     *f2name = FLDR_NAME(f2);
2080 
2081 	return(compare_names(&f1name, &f2name));
2082     }
2083 
2084     return(i);
2085 }
2086 
2087 
2088 int
compare_folders_dir_alpha_qsort(const qsort_t * a1,const qsort_t * a2)2089 compare_folders_dir_alpha_qsort(const qsort_t *a1, const qsort_t *a2)
2090 {
2091     FOLDER_S *f1 = *((FOLDER_S **) a1);
2092     FOLDER_S *f2 = *((FOLDER_S **) a2);
2093 
2094     return(compare_folders_dir_alpha(f1, f2));
2095 }
2096 
2097 
2098 /*----------------------------------------------------------------------
2099       compare two folder structs alphabetically with dirs last
2100 
2101    Args: pointers to folder structs to compare
2102 
2103  Result: integer result of dir-bit and strcmp of the folders.
2104   ----------------------------------------------------------------------*/
2105 int
compare_folders_alpha_dir(FOLDER_S * f1,FOLDER_S * f2)2106 compare_folders_alpha_dir(FOLDER_S *f1, FOLDER_S *f2)
2107 {
2108     int	  i;
2109 
2110     if((i = (f1->isdir - f2->isdir)) == 0){
2111 	char *f1name = FLDR_NAME(f1),
2112 	     *f2name = FLDR_NAME(f2);
2113 
2114 	return(compare_names(&f1name, &f2name));
2115     }
2116 
2117     return(i);
2118 }
2119 
2120 
2121 int
compare_folders_alpha_dir_qsort(const qsort_t * a1,const qsort_t * a2)2122 compare_folders_alpha_dir_qsort(const qsort_t *a1, const qsort_t *a2)
2123 {
2124     FOLDER_S *f1 = *((FOLDER_S **) a1);
2125     FOLDER_S *f2 = *((FOLDER_S **) a2);
2126 
2127     return(compare_folders_alpha_dir(f1, f2));
2128 }
2129 
2130 
2131 /*
2132  * Find incoming folders and update the unseen counts
2133  * if necessary.
2134  */
2135 void
folder_unseen_count_updater(unsigned long flags)2136 folder_unseen_count_updater(unsigned long flags)
2137 {
2138   CONTEXT_S *ctxt;
2139   time_t oldest, started_checking;
2140   int ftotal, i, first = -1;
2141   FOLDER_S *f;
2142 
2143   /*
2144    * We would only do this if there is an incoming collection, the
2145    * user wants us to monitor, and we're in the folder screen.
2146    */
2147   if(ps_global->in_folder_screen && F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
2148      && (ctxt=ps_global->context_list) && ctxt->use & CNTXT_INCMNG
2149      && (ftotal = folder_total(FOLDERS(ctxt)))){
2150     /*
2151      * Search through the last_unseen_update times to find
2152      * the one that was updated longest ago, and start with
2153      * that one. We don't want to delay long doing these
2154      * checks so may only check some of them each time we
2155      * get called. An update time equal to 1 means don't check
2156      * this folder at all.
2157      */
2158     for(i = 0; i < ftotal; i++){
2159       f = folder_entry(i, FOLDERS(ctxt));
2160       if(f && LUU_YES(f->last_unseen_update)){
2161 	first = i;
2162 	oldest = f->last_unseen_update;
2163 	break;
2164       }
2165     }
2166 
2167     /*
2168      * Now first is the first in the list that
2169      * should ever be checked. Next find the
2170      * one that should be checked next, the one
2171      * that was checked longest ago.
2172      */
2173     if(first >= 0){
2174       for(i = 1; i < ftotal; i++){
2175         f = folder_entry(i, FOLDERS(ctxt));
2176         if(f && LUU_YES(f->last_unseen_update) && f->last_unseen_update < oldest){
2177 	  first = i;
2178 	  oldest = f->last_unseen_update;
2179 	}
2180       }
2181     }
2182 
2183     /* now first is the next one to be checked */
2184 
2185     started_checking = time(0);
2186 
2187     for(i = first; i < ftotal; i++){
2188       /* update the next one */
2189       f = folder_entry(i, FOLDERS(ctxt));
2190       if(f && LUU_YES(f->last_unseen_update)
2191          && (flags & UFU_FORCE
2192 	     /* or it's been long enough and we've not been in this function too long */
2193 	     || (((time(0) - f->last_unseen_update) >= ps_global->inc_check_interval)
2194 		 && ((time(0) - started_checking) < MIN(4,ps_global->inc_check_timeout)))))
2195 	update_folder_unseen(f, ctxt, flags, NULL);
2196     }
2197 
2198     for(i = 0; i < first; i++){
2199       f = folder_entry(i, FOLDERS(ctxt));
2200       if(f && LUU_YES(f->last_unseen_update)
2201          && (flags & UFU_FORCE
2202 	     || (((time(0) - f->last_unseen_update) >= ps_global->inc_check_interval)
2203 		 && ((time(0) - started_checking) < MIN(4,ps_global->inc_check_timeout)))))
2204 	update_folder_unseen(f, ctxt, flags, NULL);
2205     }
2206   }
2207 }
2208 
2209 
2210 /*
2211  * Update the count of unseen in the FOLDER_S struct
2212  * for this folder. This will update if the time
2213  * interval has passed or if the FORCE flag is set.
2214  */
2215 void
update_folder_unseen(FOLDER_S * f,CONTEXT_S * ctxt,unsigned long flags,MAILSTREAM * this_is_the_stream)2216 update_folder_unseen(FOLDER_S *f, CONTEXT_S *ctxt, unsigned long flags,
2217 		     MAILSTREAM *this_is_the_stream)
2218 {
2219     time_t now;
2220     int orig_valid;
2221     int use_imap_interval = 0;
2222     int stream_is_open = 0;
2223     unsigned long orig_unseen, orig_new, orig_tot;
2224     char mailbox_name[MAILTMPLEN];
2225     char *target = NULL;
2226     DRIVER *d;
2227 
2228     if(!f || !LUU_YES(f->last_unseen_update))
2229       return;
2230 
2231     now = time(0);
2232     context_apply(mailbox_name, ctxt, f->name, MAILTMPLEN);
2233 
2234     if(!mailbox_name[0])
2235       return;
2236 
2237     if(check_for_move_mbox(mailbox_name, NULL, 0, &target)){
2238 	MAILSTREAM *strm;
2239 
2240 	/*
2241 	 * If this maildrop is the currently open stream use that.
2242 	 * I'm not altogether sure that this is a good way to
2243 	 * check this.
2244 	 */
2245 	if(target
2246 	   && ((strm=ps_global->mail_stream)
2247 		&& strm->snarf.name
2248 		&& (!strcmp(target,strm->mailbox)
2249 		    || !strcmp(target,strm->original_mailbox)))){
2250 	    stream_is_open++;
2251 	}
2252     }
2253     else{
2254 	MAILSTREAM *m = NULL;
2255 
2256 	stream_is_open = (this_is_the_stream
2257 			  || (m=sp_stream_get(mailbox_name, SP_MATCH | SP_RO_OK))
2258 			  || ((m=ps_global->mail_stream) && !sp_dead_stream(m)
2259 			      && same_stream_and_mailbox(mailbox_name,m))
2260 	                  || (!IS_REMOTE(mailbox_name)
2261 			      && (m=already_open_stream(mailbox_name, AOS_NONE)))) ? 1 : 0;
2262 
2263 	if(stream_is_open){
2264 	    if(!this_is_the_stream)
2265 	      this_is_the_stream = m;
2266 	}
2267 	else{
2268 	    /*
2269 	     * If it's IMAP or local we use a shorter interval.
2270 	     */
2271 	    d = mail_valid(NIL, mailbox_name, (char *) NIL);
2272 	    if((d && !strcmp(d->name, "imap")) || !IS_REMOTE(mailbox_name))
2273 	      use_imap_interval++;
2274 	}
2275     }
2276 
2277     /*
2278      * Update if forced, or if it's been a while, or if we have a
2279      * stream open to this mailbox already.
2280      */
2281     if(flags & UFU_FORCE
2282        || stream_is_open
2283        || ((use_imap_interval
2284 	    && (now - f->last_unseen_update) >= ps_global->inc_check_interval)
2285 	   || ((now - f->last_unseen_update) >= ps_global->inc_second_check_interval))){
2286 	unsigned long tot, uns, new;
2287 	unsigned long *totp = NULL, *unsp = NULL, *newp = NULL;
2288 
2289 	orig_valid  = f->unseen_valid;
2290 	orig_unseen = f->unseen;
2291 	orig_new    = f->new;
2292 	orig_tot    = f->total;
2293 
2294 	if(F_ON(F_INCOMING_CHECKING_RECENT, ps_global))
2295 	  newp = &new;
2296 	else
2297 	  unsp = &uns;
2298 
2299 	if(F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))
2300 	  totp = &tot;
2301 
2302 	f->unseen_valid = 0;
2303 
2304 	dprint((9, "update_folder_unseen(%s)", FLDR_NAME(f)));
2305 	if(get_recent_in_folder(mailbox_name, newp, unsp, totp, this_is_the_stream)){
2306 	    f->last_unseen_update = time(0);
2307 	    f->unseen_valid = 1;
2308 	    if(unsp)
2309 	      f->unseen = uns;
2310 
2311 	    if(newp)
2312 	      f->new = new;
2313 
2314 	    if(totp)
2315 	      f->total = tot;
2316 
2317 	    if(!orig_valid){
2318 		dprint((9, "update_folder_unseen(%s): original: %s%s%s%s",
2319 		    FLDR_NAME(f),
2320 		    F_ON(F_INCOMING_CHECKING_RECENT,ps_global) ? "new=" : "unseen=",
2321 		    F_ON(F_INCOMING_CHECKING_RECENT,ps_global) ? comatose(f->new) : comatose(f->unseen),
2322 		    F_ON(F_INCOMING_CHECKING_TOTAL,ps_global) ? " tot=" : "",
2323 		    F_ON(F_INCOMING_CHECKING_TOTAL,ps_global) ? comatose(f->total) : ""));
2324 	    }
2325 
2326 	    if(orig_valid
2327 	       && ((F_ON(F_INCOMING_CHECKING_RECENT, ps_global)
2328 		    && orig_new != f->new)
2329 		   ||
2330 	           (F_OFF(F_INCOMING_CHECKING_RECENT, ps_global)
2331 		    && orig_unseen != f->unseen)
2332 		   ||
2333 	           (F_ON(F_INCOMING_CHECKING_TOTAL, ps_global)
2334 		    && orig_tot != f->total))){
2335 
2336 		if(ps_global->in_folder_screen)
2337 		  ps_global->noticed_change_in_unseen = 1;
2338 
2339 		dprint((9, "update_folder_unseen(%s): changed: %s%s%s%s",
2340 		    FLDR_NAME(f),
2341 		    F_ON(F_INCOMING_CHECKING_RECENT,ps_global) ? "new=" : "unseen=",
2342 		    F_ON(F_INCOMING_CHECKING_RECENT,ps_global) ? comatose(f->new) : comatose(f->unseen),
2343 		    F_ON(F_INCOMING_CHECKING_TOTAL,ps_global) ? " tot=" : "",
2344 		    F_ON(F_INCOMING_CHECKING_TOTAL,ps_global) ? comatose(f->total) : ""));
2345 
2346 		if(flags & UFU_ANNOUNCE
2347 		   && ((F_ON(F_INCOMING_CHECKING_RECENT, ps_global)
2348 			&& orig_new < f->new)
2349 		       ||
2350 		       (F_OFF(F_INCOMING_CHECKING_RECENT, ps_global)
2351 			&& orig_unseen < f->unseen))){
2352 		    if(F_ON(F_INCOMING_CHECKING_RECENT, ps_global))
2353 		      q_status_message3(SM_ASYNC, 1, 3, "%s: %s %s",
2354 					FLDR_NAME(f), comatose(f->new),
2355 					_("new"));
2356 		    else
2357 		      q_status_message3(SM_ASYNC, 1, 3, "%s: %s %s",
2358 					FLDR_NAME(f), comatose(f->unseen),
2359 					_("unseen"));
2360 		}
2361 	    }
2362 	}
2363 	else
2364 	  f->last_unseen_update = LUU_NOMORECHK;	/* no further checking */
2365     }
2366 }
2367 
2368 
2369 void
update_folder_unseen_by_stream(MAILSTREAM * strm,unsigned long flags)2370 update_folder_unseen_by_stream(MAILSTREAM *strm, unsigned long flags)
2371 {
2372   CONTEXT_S *ctxt;
2373   int ftotal, i;
2374   char mailbox_name[MAILTMPLEN], *target;
2375   char *cn, tmp[MAILTMPLEN];
2376   FOLDER_S *f;
2377 
2378   /*
2379    * Attempt to figure out which incoming folder this stream
2380    * is open to, if any, so we can update the unseen counters.
2381    */
2382   if(strm
2383      && F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
2384      && (ctxt=ps_global->context_list) && ctxt->use & CNTXT_INCMNG
2385      && (ftotal = folder_total(FOLDERS(ctxt)))){
2386     for(i = 0; i < ftotal; i++){
2387       f = folder_entry(i, FOLDERS(ctxt));
2388       context_apply(mailbox_name, ctxt, f->name, MAILTMPLEN);
2389 
2390       if((check_for_move_mbox(mailbox_name, NULL, 0, &target)
2391 		&& strm->snarf.name
2392 		&& (!strcmp(target,strm->mailbox)
2393 		    || !strcmp(target,strm->original_mailbox)))
2394 	 || same_stream_and_mailbox(mailbox_name, strm)
2395          || (!IS_REMOTE(mailbox_name) && (cn=mailboxfile(tmp,mailbox_name)) && (*cn) && (!strcmp(cn, strm->mailbox) || !strcmp(cn, strm->original_mailbox)))){
2396 	/* if we failed earlier on this one, give it another go */
2397 	if(f->last_unseen_update == LUU_NOMORECHK)
2398 	  init_incoming_unseen_data(ps_global, f);
2399 
2400 	update_folder_unseen(f, ctxt, flags, strm);
2401 	return;
2402       }
2403     }
2404   }
2405 }
2406 
2407 
2408 /*
2409  * Find the number of new, unseen, and the total number of
2410  * messages in mailbox_name.
2411  * If the corresponding arg is NULL it will skip the work
2412  * necessary for that flag.
2413  *
2414  * Returns 1 if successful, 0 if not.
2415  */
2416 int
get_recent_in_folder(char * mailbox_name,long unsigned int * new,long unsigned int * unseen,long unsigned int * total,MAILSTREAM * this_is_the_stream)2417 get_recent_in_folder(char *mailbox_name, long unsigned int *new,
2418 		     long unsigned int *unseen, long unsigned int *total,
2419 		     MAILSTREAM *this_is_the_stream)
2420 {
2421     MAILSTREAM   *strm = NIL;
2422     unsigned long tot = 0L, nw = 0L, uns = 0L;
2423     int           gotit = 0;
2424     int           maildrop = 0;
2425     char         *target = NULL;
2426     MSGNO_S      *msgmap;
2427     long          excluded, flags;
2428     extern MAILSTATUS mm_status_result;
2429 
2430     dprint((9, "get_recent_in_folder(%s)", mailbox_name ? mailbox_name : "?"));
2431 
2432     if(check_for_move_mbox(mailbox_name, NULL, 0, &target)){
2433 
2434 	maildrop++;
2435 
2436 	/*
2437 	 * If this maildrop is the currently open stream use that.
2438 	 */
2439 	if(target
2440 	   && ((strm=ps_global->mail_stream)
2441 		&& strm->snarf.name
2442 		&& (!strcmp(target,strm->mailbox)
2443 		    || !strcmp(target,strm->original_mailbox)))){
2444 	    gotit++;
2445 	    msgmap = sp_msgmap(strm);
2446 	    excluded = any_lflagged(msgmap, MN_EXLD);
2447 
2448 	    tot = strm->nmsgs - excluded;
2449 	    if(tot){
2450 	      if(new){
2451 		  if(sp_recent_since_visited(strm) == 0)
2452 		    nw = 0;
2453 		  else
2454 		    nw = count_flagged(strm, F_RECENT | F_UNSEEN | F_UNDEL);
2455 	      }
2456 
2457 	      if(unseen)
2458 		uns = count_flagged(strm, F_UNSEEN | F_UNDEL);
2459 	    }
2460 	    else{
2461 	      nw = 0;
2462 	      uns = 0;
2463 	    }
2464 	}
2465 	/* else fall through to just open it case */
2466     }
2467 
2468     /* do we already have it selected? */
2469     if(!gotit
2470        && ((strm = this_is_the_stream)
2471            || (strm = sp_stream_get(mailbox_name, SP_MATCH | SP_RO_OK))
2472            || (!IS_REMOTE(mailbox_name)
2473 	       && (strm = already_open_stream(mailbox_name, AOS_NONE))))){
2474 	gotit++;
2475 
2476 	/*
2477 	 * Unfortunately, we have to worry about excluded
2478 	 * messages. The user doesn't want to have
2479 	 * excluded messages count in the totals, especially
2480 	 * recent excluded messages.
2481 	 */
2482 
2483 	msgmap = sp_msgmap(strm);
2484 	excluded = any_lflagged(msgmap, MN_EXLD);
2485 
2486 	tot = strm->nmsgs - excluded;
2487 	if(tot){
2488 	  if(new){
2489 	      if(sp_recent_since_visited(strm) == 0)
2490 		nw = 0;
2491 	      else
2492 		nw = count_flagged(strm, F_RECENT | F_UNSEEN | F_UNDEL);
2493 	  }
2494 
2495 	  if(unseen)
2496 	    uns = count_flagged(strm, F_UNSEEN | F_UNDEL);
2497 	}
2498 	else{
2499 	  nw = 0;
2500 	  uns = 0;
2501 	}
2502     }
2503     /*
2504      * No, but how about another stream to same server which
2505      * could be used for a STATUS command?
2506      */
2507     else if(!gotit && (strm = sp_stream_get(mailbox_name, SP_SAME))
2508 	    && modern_imap_stream(strm)){
2509 
2510 	flags = 0L;
2511 	if(total)
2512 	  flags |= SA_MESSAGES;
2513 
2514 	if(new)
2515 	  flags |= SA_RECENT;
2516 
2517 	if(unseen)
2518 	  flags |= SA_UNSEEN;
2519 
2520 	mm_status_result.flags = 0L;
2521 
2522 	pine_mail_status(strm, mailbox_name, flags);
2523 	if(total){
2524 	    if(mm_status_result.flags & SA_MESSAGES){
2525 		tot = mm_status_result.messages;
2526 		gotit++;
2527 	    }
2528 	}
2529 
2530 	if(!(total && !gotit)){
2531 	    if(new){
2532 		if(mm_status_result.flags & SA_RECENT){
2533 		    nw = mm_status_result.recent;
2534 		    gotit++;
2535 		}
2536 		else
2537 		  gotit = 0;
2538 	    }
2539 	}
2540 
2541 	if(!((total || new) && !gotit)){
2542 	    if(unseen){
2543 		if(mm_status_result.flags & SA_UNSEEN){
2544 		    uns = mm_status_result.unseen;
2545 		    gotit++;
2546 		}
2547 		else
2548 		  gotit = 0;
2549 	    }
2550 	}
2551     }
2552 
2553     /* Let's just Select it. */
2554     if(!gotit){
2555 	long saved_timeout;
2556 	long openflags;
2557 
2558 	/*
2559 	 * Traditional unix folders don't notice new mail if
2560 	 * they are opened readonly. So maildrops with unix folder
2561 	 * targets will snarf to the file but the stream that is
2562 	 * opened won't see the new mail. So make all maildrop
2563 	 * opens non-readonly here.
2564 	 */
2565 	openflags = SP_USEPOOL | SP_TEMPUSE | (maildrop ? 0 : OP_READONLY);
2566 
2567 	saved_timeout = (long) mail_parameters(NULL, GET_OPENTIMEOUT, NULL);
2568 	mail_parameters(NULL, SET_OPENTIMEOUT, (void *) (long) ps_global->inc_check_timeout);
2569 	strm = pine_mail_open(NULL, mailbox_name, openflags, NULL);
2570 	mail_parameters(NULL, SET_OPENTIMEOUT, (void *) saved_timeout);
2571 
2572 	if(strm){
2573 	    gotit++;
2574 	    msgmap = sp_msgmap(strm);
2575 	    excluded = any_lflagged(msgmap, MN_EXLD);
2576 
2577 	    tot = strm->nmsgs - excluded;
2578 	    if(tot){
2579 	      if(new){
2580 		  if(sp_recent_since_visited(strm) == 0)
2581 		    nw = 0;
2582 		  else
2583 		    nw = count_flagged(strm, F_RECENT | F_UNSEEN | F_UNDEL);
2584 	      }
2585 
2586 	      if(unseen)
2587 		uns = count_flagged(strm, F_UNSEEN | F_UNDEL);
2588 	    }
2589 	    else{
2590 	      nw = 0;
2591 	      uns = 0;
2592 	    }
2593 
2594 	    pine_mail_close(strm);
2595 	}
2596     }
2597 
2598     if(gotit){
2599 	if(new)
2600 	  *new = nw;
2601 
2602 	if(unseen)
2603 	  *unseen = uns;
2604 
2605 	if(total)
2606 	  *total = tot;
2607     }
2608 
2609     return(gotit);
2610 }
2611 
2612 
2613 void
clear_incoming_valid_bits(void)2614 clear_incoming_valid_bits(void)
2615 {
2616     CONTEXT_S *ctxt;
2617     int ftotal, i;
2618     FOLDER_S *f;
2619 
2620     if(F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
2621        && (ctxt=ps_global->context_list) && ctxt->use & CNTXT_INCMNG
2622        && (ftotal = folder_total(FOLDERS(ctxt))))
2623       for(i = 0; i < ftotal; i++){
2624 	  f = folder_entry(i, FOLDERS(ctxt));
2625 	  init_incoming_unseen_data(ps_global, f);
2626       }
2627 }
2628 
2629 
2630 int
selected_folders(CONTEXT_S * context)2631 selected_folders(CONTEXT_S *context)
2632 {
2633     int	      i, n, total;
2634 
2635     n = folder_total(FOLDERS(context));
2636     for(total = i = 0; i < n; i++)
2637       if(folder_entry(i, FOLDERS(context))->selected)
2638 	total++;
2639 
2640     return(total);
2641 }
2642 
2643 
2644 SELECTED_S *
new_selected(void)2645 new_selected(void)
2646 {
2647   SELECTED_S *selp;
2648 
2649   selp = (SELECTED_S *)fs_get(sizeof(SELECTED_S));
2650   selp->sub = NULL;
2651   selp->reference = NULL;
2652   selp->folders = NULL;
2653   selp->zoomed = 0;
2654 
2655   return selp;
2656 }
2657 
2658 
2659 /*
2660  *  Free the current selected struct and all of the
2661  *  following structs in the list
2662  */
2663 void
free_selected(SELECTED_S ** selp)2664 free_selected(SELECTED_S **selp)
2665 {
2666     if(!selp || !(*selp))
2667       return;
2668     if((*selp)->sub)
2669       free_selected(&((*selp)->sub));
2670 
2671     free_strlist(&(*selp)->folders);
2672     if((*selp)->reference)
2673       fs_give((void **) &(*selp)->reference);
2674 
2675     fs_give((void **) selp);
2676 }
2677 
2678 
2679 void
folder_select_preserve(CONTEXT_S * context)2680 folder_select_preserve(CONTEXT_S *context)
2681 {
2682     if(context
2683        && !(context->use & CNTXT_PARTFIND)){
2684 	FOLDER_S   *fp;
2685 	STRLIST_S **slpp;
2686 	SELECTED_S *selp = &context->selected;
2687 	int	    i, folder_n;
2688 
2689 	if(!context->dir->ref){
2690 	  if(!context->selected.folders)
2691 	    slpp     = &context->selected.folders;
2692 	  else
2693 	    return;
2694 	}
2695 	else{
2696 	  if(!selected_folders(context))
2697 	    return;
2698 	  else{
2699 	    while(selp->sub){
2700 	      selp = selp->sub;
2701 	      if(!strcmp(selp->reference, context->dir->ref))
2702 		return;
2703 	    }
2704 	    selp->sub = new_selected();
2705 	    selp = selp->sub;
2706 	    slpp = &(selp->folders);
2707 	  }
2708 	}
2709 	folder_n = folder_total(FOLDERS(context));
2710 
2711 	for(i = 0; i < folder_n; i++)
2712 	  if((fp = folder_entry(i, FOLDERS(context)))->selected){
2713 	      *slpp = new_strlist(fp->name);
2714 	      slpp = &(*slpp)->next;
2715 	  }
2716 
2717 	/* Only remember "ref" if any folders were selected */
2718 	if(selp->folders && context->dir->ref)
2719 	  selp->reference = cpystr(context->dir->ref);
2720 
2721 	selp->zoomed = (context->use & CNTXT_ZOOM) != 0;
2722     }
2723 }
2724 
2725 
2726 int
folder_select_restore(CONTEXT_S * context)2727 folder_select_restore(CONTEXT_S *context)
2728 {
2729     int rv = 0;
2730 
2731     if(context
2732        && !(context->use & CNTXT_PARTFIND)){
2733 	STRLIST_S *slp;
2734 	SELECTED_S *selp, *pselp = NULL;
2735 	int	   i, found = 0;
2736 
2737 	selp = &(context->selected);
2738 
2739 	if(context->dir->ref){
2740 	  pselp = selp;
2741 	  selp = selp->sub;
2742 	  while(selp && strcmp(selp->reference, context->dir->ref)){
2743 	    pselp = selp;
2744 	    selp = selp->sub;
2745 	  }
2746 	  if (selp)
2747 	    found = 1;
2748 	}
2749 	else
2750 	  found = selp->folders != 0;
2751 	if(found){
2752 	  for(slp = selp->folders; slp; slp = slp->next)
2753 	    if(slp->name
2754 	       && (i = folder_index(slp->name, context, FI_FOLDER)) >= 0){
2755 		folder_entry(i, FOLDERS(context))->selected = 1;
2756 		rv++;
2757 	    }
2758 
2759 	  /* Used, always clean them up */
2760 	  free_strlist(&selp->folders);
2761 	  if(selp->reference)
2762 	    fs_give((void **) &selp->reference);
2763 
2764 	  if(selp->zoomed){
2765 	    context->use |= CNTXT_ZOOM;
2766 	    selp->zoomed = 0;
2767 	  }
2768 	  if(!(selp == &context->selected)){
2769 	    if(pselp){
2770 	      pselp->sub = selp->sub;
2771 	      fs_give((void **) &selp);
2772 	    }
2773 	  }
2774 	}
2775     }
2776 
2777     return(rv);
2778 }
2779 
2780