diff options
Diffstat (limited to 'imap/mailboxlist.c')
| -rw-r--r-- | imap/mailboxlist.c | 1064 | 
1 files changed, 1064 insertions, 0 deletions
| diff --git a/imap/mailboxlist.c b/imap/mailboxlist.c new file mode 100644 index 0000000..a204ebf --- /dev/null +++ b/imap/mailboxlist.c @@ -0,0 +1,1064 @@ +/* +** Copyright 1998 - 2007 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if	HAVE_CONFIG_H +#include	"config.h" +#endif +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	<errno.h> +#include	<ctype.h> +#include	<fcntl.h> +#if	HAVE_UNISTD_H +#include	<unistd.h> +#endif +#if HAVE_DIRENT_H +#include <dirent.h> +#define NAMLEN(dirent) strlen((dirent)->d_name) +#else +#define dirent direct +#define NAMLEN(dirent) (dirent)->d_namlen +#if HAVE_SYS_NDIR_H +#include <sys/ndir.h> +#endif +#if HAVE_SYS_DIR_H +#include <sys/dir.h> +#endif +#if HAVE_NDIR_H +#include <ndir.h> +#endif +#endif +#if	HAVE_UTIME_H +#include	<utime.h> +#endif +#if TIME_WITH_SYS_TIME +#include	<sys/time.h> +#include	<time.h> +#else +#if HAVE_SYS_TIME_H +#include	<sys/time.h> +#else +#include	<time.h> +#endif +#endif + +#include	<sys/types.h> +#include	<sys/stat.h> + +#include	"imaptoken.h" +#include	"imapwrite.h" +#include	"imapscanclient.h" + +#include	"mysignal.h" +#include	"imapd.h" +#include	"fetchinfo.h" +#include	"searchinfo.h" +#include	"storeinfo.h" +#include	"mailboxlist.h" + +#include	"maildir/config.h" +#include	"maildir/maildirmisc.h" +#include	"maildir/maildiraclt.h" +#include	"maildir/maildirnewshared.h" +#include	"maildir/maildirinfo.h" +#include	"unicode/unicode.h" +#include	"courierauth.h" + + +static const char hierchs[]={HIERCH, 0}; + +extern char *decode_valid_mailbox(const char *, int); +extern dev_t homedir_dev; +extern ino_t homedir_ino; +/* +		LIST MAILBOXES +*/ + +static int do_mailbox_list(int do_lsub, char *qq, int isnullname, +			   int (*callback_func)(const char *hiersep, +						const char *mailbox, +						int flags, +						void *void_arg), +			   void *void_arg); + +static int shared_index_err_reported=0; + +const char *maildir_shared_index_file() +{ +	static char *filenamep=NULL; + +	if (filenamep == NULL) +	{ +		const char *p=getenv("IMAP_SHAREDINDEXFILE"); + +		if (p && *p) +		{ +			const char *q=auth_getoptionenv("sharedgroup"); + +			if (!q) q=""; + +			filenamep=malloc(strlen(p)+strlen(q)+1); + +			if (!filenamep) +				write_error_exit(0); + +			strcat(strcpy(filenamep, p), q); +		} +	} + +	if (filenamep && !shared_index_err_reported) /* Bitch just once */ +	{ +		struct stat stat_buf; + +		shared_index_err_reported=1; +		if (stat(filenamep, &stat_buf)) +		{ +			fprintf(stderr, "ERR: "); +			perror(filenamep); +		} +	} + +	return filenamep; +} + +/* +**	IMAP sucks.  Here's why. +*/ + + +int mailbox_scan(const char *reference, const char *name, +		 int list_options, +		 int (*callback_func)(const char *hiersep, +				      const char *mailbox, +				      int flags, +				      void *void_arg), +		 void *void_arg) +{ +	char	*pattern, *p; +	int	nullname= *name == 0; +	int	rc; + +	pattern=malloc(strlen(reference)+strlen(name)+2); + +	strcpy(pattern, reference); + +	p=strrchr(pattern, HIERCH); +	if (p && p[1] == 0)	*p=0; /* Strip trailing . for now */ +	if (*pattern) +	{ +		struct maildir_info mi; + +		if (maildir_info_imap_find(&mi, pattern, +					   getenv("AUTHENTICATED"))) +		{ +			free(pattern); +			return (0); /* Invalid reference */ +		} +		maildir_info_destroy(&mi); +	} + +	/* Combine reference and name. */ +	if (*pattern && *name) +		strcat(pattern, hierchs); +	strcat(pattern, name); + +	if (name && *name) +	{ +	char *s=strrchr(pattern, HIERCH); + +		if (s && s[1] == 0)	*s=0;	/* strip trailing . */ + +	} + +	/* Now, do the list */ + +	rc=do_mailbox_list(list_options, pattern, nullname, +			   callback_func, void_arg); +	free(pattern); +	return (rc); +} + +static int match_mailbox(char *, char *, int flags); +static void match_mailbox_prep(char *); + +/* Check if a folder has any new messages */ + +static int hasnewmsgs2(const char *dir) +{ +DIR	*dirp=opendir(dir); +struct	dirent *de; + +	while (dirp && (de=readdir(dirp)) != 0) +	{ +	char	*p; + +		if (de->d_name[0] == '.')	continue; +		p=strrchr(de->d_name, MDIRSEP[0]); +		if (p == 0 || strncmp(p, MDIRSEP "2,", 3) || +			strchr(p, 'S') == 0) +		{ +			closedir(dirp); +			return (1); +		} +	} +	if (dirp)	closedir(dirp); +	return (0); +} + +static int hasnewmsgs(const char *folder) +{ +char *dir=decode_valid_mailbox(folder, 0); +char *subdir; + +	if (!dir)	return (0); + +	if (is_sharedsubdir(dir)) +		maildir_shared_sync(dir); + +	subdir=malloc(strlen(dir)+sizeof("/cur")); +	if (!subdir)	write_error_exit(0); +	strcat(strcpy(subdir, dir), "/new"); +	if (hasnewmsgs2(subdir)) +	{ +		free(subdir); +		free(dir); +		return (1); +	} + +	strcat(strcpy(subdir, dir), "/cur"); +	if (hasnewmsgs2(subdir)) +	{ +		free(subdir); +		free(dir); +		return (1); +	} + +	free(subdir); +	free(dir); +	return (0); +} + +/* Each folder is listed with the \Noinferiors tag.  Then, for every subfolder +** we've seen, we need to output a listing for all the higher-level hierarchies +** with a \Noselect tag.  Therefore, we need to keep track of all the +** hierarchies we've seen so far. +*/ + +struct hierlist { +	struct hierlist *next; +	int flag; +	char *hier; +	} ; + +static int add_hier(struct hierlist **h, const char *s) +{ +struct hierlist *p; + +	for (p= *h; p; p=p->next) +		if (strcmp(p->hier, s) == 0)	return (1); +			/* Seen this one already */ + +	if ((p=(struct hierlist *) +		malloc( sizeof(struct hierlist)+1+strlen(s))) == 0) +			/* HACK!!!! */ +		write_error_exit(0); +	p->flag=0; +	p->hier=(char *)(p+1); +	strcpy(p->hier, s); +	p->next= *h; +	*h=p; +	return (0); +} + +static struct hierlist *search_hier(struct hierlist *h, const char *s) +{ +struct hierlist *p; + +	for (p= h; p; p=p->next) +		if (strcmp(p->hier, s) == 0)	return (p); +	return (0); +} + +static void hier_entry(char *folder, +		       struct hierlist **hierarchies); + +static int has_hier_entry(char *folder, +			  struct hierlist **hierarchies); + +static void folder_entry(char *folder, char *pattern, +			 int list_options, +			 struct hierlist **folders, +			 struct hierlist **hierarchies) +{ +	size_t i; +	size_t folder_l=strlen(folder); + +	int need_add_hier; +	int need_add_folders; + +	match_mailbox_prep(folder); + +	/* Optimize away folders we don't care about */ + +	for (i=0; pattern[i]; i++) +	{ +		if ((!(list_options & LIST_CHECK1FOLDER)) && +		    (pattern[i] == '%' || pattern[i] == '*')) +		{ +			while (i) +			{ +				if (pattern[i] == HIERCH) +					break; +				--i; +			} +			break; +		} +	} + +	if (folder_l <= i) +	{ +		if (memcmp(folder, pattern, folder_l)) +			return; + +		if (folder_l != i && pattern[folder_l] != HIERCH) +			return; +	} +	else if (i) +	{ +		if (memcmp(folder, pattern, i)) +			return; +		if (folder[i] != HIERCH) +			return; +	} + +	need_add_folders=0; + +	if (match_mailbox(folder, pattern, list_options) == 0) +		need_add_folders=1; + +	need_add_hier=0; +	if (!has_hier_entry(folder, hierarchies)) +		need_add_hier=1; + +	if (!need_add_folders && !need_add_hier) +		return; /* Nothing to do */ + +	{ +		CHECK_RIGHTSM(folder, have_rights, ACL_LOOKUP); + +		if (!have_rights[0]) +			return; +	} + +	if (need_add_folders) +		(void) add_hier(folders, folder); + +	if (need_add_hier) +		hier_entry(folder, hierarchies); +} + +static void hier_entry(char *folder, +		       struct hierlist **hierarchies) +{ +	unsigned i; + +	for (i=0; folder[i]; i++) +	{ +		if (folder[i] != HIERCH)	continue; +		folder[i]=0; +		(void)add_hier(hierarchies, folder); +		folder[i]=HIERCH; +	} +} + +static int has_hier_entry(char *folder, +			  struct hierlist **hierarchies) +{ +	unsigned i; + +	for (i=0; folder[i]; i++) +	{ +		if (folder[i] != HIERCH)	continue; +		folder[i]=0; +		if (!search_hier(*hierarchies, folder)) +		{ +			folder[i]=HIERCH; +			return (0); +		} +		folder[i]=HIERCH; +	} +	return (1); +} + +struct list_sharable_info { +	char *pattern; +	struct hierlist **folders, **hierarchies; +	int flags; +	int (*callback_func)(const char *hiersep, +			     const char *mailbox, +			     int flags, +			     void *void_arg); +	void *cb_arg; +	} ; + +static void list_sharable(const char *n, +	void *voidp) +{ +struct list_sharable_info *ip=(struct list_sharable_info *)voidp; +char	*p=malloc(strlen(n)+sizeof("shared.")); + +	if (!p)	write_error_exit(0); + +	strcat(strcpy(p, "shared."), n); + +	folder_entry(p, ip->pattern, ip->flags, +		     ip->folders, ip->hierarchies); + +	free(p); +} + +static void list_subscribed(char *hier, +			    int flags, +			    struct hierlist **folders, +			    struct hierlist **hierarchies) +{ +char	buf[BUFSIZ]; +FILE	*fp; + +	fp=fopen(SUBSCRIBEFILE, "r"); +	if (fp) +	{ +		while (fgets(buf, sizeof(buf), fp) != 0) +		{ +			char *q=strchr(buf, '\n'); + +			if (q)	*q=0; + +			if (*hier == '#') +			{ +				if (*buf != '#') +					continue; +			} +			else +			{ +				if (*buf == '#') +					continue; +			} + +			folder_entry(buf, hier, flags, +				     folders, hierarchies); +		} +		fclose(fp); +	} +} + +static void maildir_scan(const char *inbox_dir, +			 const char *inbox_name, +			 struct list_sharable_info *shared_info) +{ +	DIR	*dirp; +	struct	dirent *de; + +	/* Scan maildir, looking for .subdirectories */ + +	dirp=opendir(inbox_dir && inbox_dir ? inbox_dir:"."); +	while (dirp && (de=readdir(dirp)) != 0) +	{ +	char	*p; + +		if (de->d_name[0] != '.' || +		    strcmp(de->d_name, "..") == 0) +			continue; + +		if ((p=malloc(strlen(de->d_name)+strlen(inbox_name)+10)) == 0) +					/* A bit too much, that's OK */ +			write_error_exit(0); + +		strcpy(p, inbox_name); + +		if (strcmp(de->d_name, ".")) +			strcat(p, de->d_name); + +		folder_entry(p, shared_info->pattern, shared_info->flags, +			     shared_info->folders, +			     shared_info->hierarchies); +		free(p); +	} + +	if (dirp)	closedir(dirp); +} + +/* List the #shared hierarchy */ + +struct list_newshared_info { +	const char *acc_pfix; +	const char *skipped_pattern; +	struct list_sharable_info *shared_info; +	struct maildir_shindex_cache *parentCache; +	int dorecurse; +}; + +static int list_newshared_cb(struct maildir_newshared_enum_cb *cb); +static int list_newshared_skipcb(struct maildir_newshared_enum_cb *cb); +static int list_newshared_skiplevel(struct maildir_newshared_enum_cb *cb); + +static int list_newshared_shortcut(const char *skipped_pattern, +				   struct list_sharable_info *shared_info, +				   const char *current_namespace, +				   struct maildir_shindex_cache *parentCache, +				   const char *indexfile, +				   const char *subhierarchy); + +static int list_newshared(const char *skipped_pattern, +			  struct list_sharable_info *shared_info) +{ +	return list_newshared_shortcut(skipped_pattern, shared_info, +				       NEWSHARED, +				       NULL, NULL, NULL); +} + +static int list_newshared_shortcut(const char *skipped_pattern, +				   struct list_sharable_info *shared_info, +				   const char *acc_pfix, +				   struct maildir_shindex_cache *parentCache, +				   const char *indexfile, +				   const char *subhierarchy) +{ +	struct list_newshared_info lni; +	int rc; +	struct maildir_shindex_cache *curcache=NULL; + +	lni.acc_pfix=acc_pfix; +	lni.skipped_pattern=skipped_pattern; +	lni.shared_info=shared_info; +	lni.dorecurse=1; + +	/* Try for some common optimization, to avoid expanding the +	** entire #shared hierarchy, taking advantage of the cache list. +	*/ + +	for (;;) +	{ +		const char *p; +		size_t i; +		char *q; +		int eof; + +		if (strcmp(skipped_pattern, "%") == 0) +		{ +			lni.dorecurse=0; +			break; +		} + +		if (strncmp(skipped_pattern, "%" HIERCHS, +			    sizeof("%" HIERCHS)-1) == 0) +		{ +			curcache=maildir_shared_cache_read(parentCache, +							   indexfile, +							   subhierarchy); +			if (!curcache) +				return 0; + +			lni.acc_pfix=acc_pfix; +			lni.skipped_pattern=skipped_pattern +				+ sizeof("%" HIERCHS)-1; +			lni.parentCache=curcache; + +			for (i=0; i<curcache->nrecords; i++) +			{ +				if (i == 0) +				{ +					curcache->indexfile.startingpos=0; +					rc=maildir_newshared_nextAt(&curcache->indexfile, +								    &eof, +								    list_newshared_skiplevel, +								    &lni); +				} +				else +					rc=maildir_newshared_next(&curcache->indexfile, +								  &eof, +								  list_newshared_skiplevel, +								  &lni); + +				if (rc || eof) +				{ +					fprintf(stderr, "ERR:maildir_newshared_next failed: %s\n", +						strerror(errno)); +					break; +				} +			} +			return 0; +		} + +		for (p=skipped_pattern; *p; p++) +			if (*p == HIERCH || +			    ((lni.shared_info->flags & LIST_CHECK1FOLDER) == 0 +			     && (*p == '*' || *p == '%'))) +				break; + +		if (*p && *p != HIERCH) +			break; + +		curcache=maildir_shared_cache_read(parentCache, indexfile, +						   subhierarchy); +		if (!curcache) +			return 0; + +		for (i=0; i < curcache->nrecords; i++) +		{ +			char *n=maildir_info_imapmunge(curcache->records[i] +						       .name); + +			if (!n) +				write_error_exit(0); + +			if (strlen(n) == p-skipped_pattern && +			    strncmp(n, skipped_pattern, p-skipped_pattern) == 0) +			{ +				free(n); +				break; +			} +			free(n); +		} + +		if (i >= curcache->nrecords) /* not found */ +			return 0; + +		if (*p) +			++p; + + +		q=malloc(strlen(acc_pfix)+(p-skipped_pattern)+1); +		if (!q) +		{ +			write_error_exit(0); +		} +		strcpy(q, acc_pfix); +		strncat(q, skipped_pattern, p-skipped_pattern); + +		lni.acc_pfix=q; +		lni.skipped_pattern=p; +		lni.parentCache=curcache; + +		curcache->indexfile.startingpos=curcache->records[i].offset; + +		rc=maildir_newshared_nextAt(&curcache->indexfile, &eof, +					    list_newshared_skipcb, &lni); +		free(q); +		return rc; + +	} + +	if (!indexfile) +		indexfile=maildir_shared_index_file(); + +	rc=maildir_newshared_enum(indexfile, list_newshared_cb, &lni); + +	return rc; +} + +static int list_newshared_cb(struct maildir_newshared_enum_cb *cb) +{ +	const char *name=cb->name; +	const char *homedir=cb->homedir; +	const char *maildir=cb->maildir; +	struct list_newshared_info *lni= +		(struct list_newshared_info *)cb->cb_arg; +	char *n=maildir_info_imapmunge(name); +	int rc; + +	if (!n) +		write_error_exit(0); + +	if (homedir == NULL) +	{ +		struct list_newshared_info new_lni= *lni; +		char *new_pfix=malloc(strlen(lni->acc_pfix)+ +				      strlen(n)+2); +		if (!new_pfix) +			write_error_exit(0); + +		strcat(strcpy(new_pfix, lni->acc_pfix), n); + +		free(n); +		n=new_pfix; +		new_lni.acc_pfix=n; +		add_hier(lni->shared_info->hierarchies, n); +		hier_entry(n, lni->shared_info->hierarchies); +		strcat(n, hierchs); +		rc=lni->dorecurse ? +			maildir_newshared_enum(maildir, list_newshared_cb, +					       &new_lni):0; +	} +	else +	{ +		char *new_pfix; +		struct stat stat_buf; + +		new_pfix=maildir_location(homedir, maildir); + +		if (stat(new_pfix, &stat_buf) < 0 || +		    /* maildir inaccessible, perhaps another server? */ + +		    (stat_buf.st_dev == homedir_dev && +		     stat_buf.st_ino == homedir_ino)) +		    /* Exclude ourselves from the shared list */ +		{ +			free(new_pfix); +			free(n); +			return 0; +		} +		free(new_pfix); + +		new_pfix=malloc(strlen(lni->acc_pfix)+ +				      strlen(n)+1); +		if (!new_pfix) +			write_error_exit(0); + +		strcat(strcpy(new_pfix, lni->acc_pfix), n); + +		free(n); +		n=new_pfix; + +		new_pfix=malloc(strlen(homedir)+strlen(maildir)+2); + +		if (!new_pfix) +			write_error_exit(0); + +		if (*maildir == '/') +			strcpy(new_pfix, maildir); +		else +			strcat(strcat(strcpy(new_pfix, homedir), "/"), +			       maildir); + +		/*		if (lni->dorecurse) */ + +		maildir_scan(new_pfix, n, lni->shared_info); +#if 0 +		else +		{ +			folder_entry(n, lni->shared_info->pattern, +				     lni->shared_info->flags, +				     lni->shared_info->folders, +				     lni->shared_info->hierarchies); +		} +#endif + +		free(new_pfix); +		rc=0; +	} +	free(n); +	return rc; +} + +static int list_newshared_skiplevel(struct maildir_newshared_enum_cb *cb) +{ +	struct list_newshared_info *lni= +		(struct list_newshared_info *)cb->cb_arg; +	char *n=maildir_info_imapmunge(cb->name); + +	char *p=malloc(strlen(lni->acc_pfix)+strlen(n)+sizeof(HIERCHS)); +	int rc; +	const char *save_skip; + +	if (!n || !p) +		write_error_exit(0); + +	strcat(strcat(strcpy(p, lni->acc_pfix), n), HIERCHS); +	free(n); + +	save_skip=lni->acc_pfix; +	lni->acc_pfix=p; + +	rc=list_newshared_skipcb(cb); +	lni->acc_pfix=save_skip; +	free(p); +	return rc; +} + +static int list_newshared_skipcb(struct maildir_newshared_enum_cb *cb) +{ +	struct list_newshared_info *lni= +		(struct list_newshared_info *)cb->cb_arg; +	char *dir; +	char *inbox_name; + +	if (cb->homedir == NULL) +		return list_newshared_shortcut(lni->skipped_pattern, +					       lni->shared_info, +					       lni->acc_pfix, +					       lni->parentCache, +					       cb->maildir, +					       cb->name); + +	inbox_name=my_strdup(lni->acc_pfix); + +	dir=strrchr(inbox_name, HIERCH); +	if (dir && dir[1] == 0) +		*dir=0; /* Strip trailing hier separator */ + +	dir=malloc(strlen(cb->homedir)+strlen(cb->maildir)+2); + +	if (!dir) +	{ +		free(inbox_name); +		write_error_exit(0); +	} + +	if (*cb->maildir == '/') +		strcpy(dir, cb->maildir); +	else +		strcat(strcat(strcpy(dir, cb->homedir), "/"), cb->maildir); + +	maildir_scan(dir, inbox_name, lni->shared_info); +	free(dir); +	free(inbox_name); +	return 0; +} + +static int do_mailbox_list(int list_options, char *pattern, int isnullname, +			   int (*callback_func)(const char *hiersep, +						const char *mailbox, +						int flags, +						void *void_arg), +			   void *void_arg) +{ +int	found_hier=MAILBOX_NOSELECT; +int	is_interesting; +int	i,j,bad_pattern; +struct	hierlist *hierarchies, *folders, *hp; +struct list_sharable_info shared_info; + +const char *obsolete; +int check_all_folders=0; +char hiersepbuf[8]; +int callback_rc=0; + +	obsolete=getenv("IMAP_CHECK_ALL_FOLDERS"); +	if (obsolete && atoi(obsolete)) +		check_all_folders=1; + +	obsolete=getenv("IMAP_OBSOLETE_CLIENT"); + +	if (obsolete && atoi(obsolete) == 0) +		obsolete=0; + +	/* Allow up to ten wildcards */ + +	for (i=j=0; pattern[i]; i++) +		if (pattern[i] == '*' || pattern[i] == '%')	++j; +	bad_pattern= j > 10; + +	if (list_options & LIST_CHECK1FOLDER) +		bad_pattern=0; + +	if (bad_pattern) +	{ +		errno=EINVAL; +		return -1; +	} + +	hierarchies=0; +	folders=0; + +	match_mailbox_prep(pattern); + +	shared_info.pattern=pattern; +	shared_info.folders= &folders; +	shared_info.hierarchies= &hierarchies; +	shared_info.flags=list_options; +	shared_info.callback_func=callback_func; +	shared_info.cb_arg=void_arg; + +	if (!(list_options & LIST_SUBSCRIBED)) +	{ +		if (strncmp(pattern, NEWSHARED, +			    sizeof(NEWSHARED)-1) == 0) +		{ +			list_newshared(pattern + +				       sizeof(NEWSHARED)-1, +				       &shared_info); +		} +		else +		{ +			maildir_scan(".", INBOX, &shared_info); + +			/* List sharable maildirs */ + +			maildir_list_sharable( ".", &list_sharable, +					       &shared_info ); +		} +	} +	else +	{ +		list_subscribed(pattern, list_options, &folders, &hierarchies); + +		/* List shared folders */ + +		maildir_list_shared( ".", &list_sharable, +				     &shared_info ); +	} + +	while ((hp=folders) != 0) +	{ +		struct hierlist *d; +		int mb_flags; + +		folders=hp->next; + +		is_interesting= -1; + +		if (strcmp(hp->hier, INBOX) == 0 || check_all_folders) +			is_interesting=hasnewmsgs(hp->hier); + +		strcat(strcat(strcpy(hiersepbuf, "\""), hierchs), "\""); + +		mb_flags=0; + +		if (is_interesting == 0) +		{ +			mb_flags|=MAILBOX_UNMARKED; +		} +		if (is_interesting > 0) +		{ +			mb_flags|=MAILBOX_MARKED; +		} + +		if ((d=search_hier(hierarchies, hp->hier)) == 0) +		{ +			mb_flags |= +				obsolete ? MAILBOX_NOINFERIORS:MAILBOX_NOCHILDREN; +		} +		else +		{ +			d->flag=1; +			if (!obsolete) +				mb_flags |= MAILBOX_CHILDREN; +		} + +		if (isnullname) +			found_hier=mb_flags; +		else +			if (callback_rc == 0) +				callback_rc=(*callback_func) +					(hiersepbuf, hp->hier, +					 mb_flags | list_options, void_arg); +		free(hp); +	} + +	while ((hp=hierarchies) != 0) +	{ +		hierarchies=hp->next; + +		match_mailbox_prep(hp->hier); + +		if (match_mailbox(hp->hier, pattern, list_options) == 0 +		    && hp->flag == 0) +		{ +			int mb_flags=MAILBOX_NOSELECT; + +			if (!obsolete) +				mb_flags |= MAILBOX_CHILDREN; + +			if (isnullname) +				found_hier=mb_flags; +			else  +			{ +				strcat(strcat(strcpy(hiersepbuf, "\""), +					      hierchs), "\""); + +				if (callback_rc == 0) +					callback_rc=(*callback_func) +						(hiersepbuf, +						 hp->hier, +						 mb_flags | list_options, +						 void_arg); +			} +		} +		free(hp); +	} + +	if (isnullname) +	{ +		const char *namesp=""; + +		if (strncmp(pattern, NEWSHARED, sizeof(NEWSHARED)-1) == 0) +			namesp=NEWSHARED; + +		strcat(strcat(strcpy(hiersepbuf, "\""), hierchs), "\""); + +		if (callback_rc == 0) +			callback_rc=(*callback_func) +				(hiersepbuf, namesp, found_hier | list_options, +				 void_arg); +	} +	return callback_rc; +} + +static int match_recursive(char *, char *, int); + +static void match_mailbox_prep(char *name) +{ +	size_t	i; + +	/* First component, INBOX, is case insensitive */ + +	if ( +#if	HAVE_STRNCASECMP +	    strncasecmp(name, INBOX, sizeof(INBOX)-1) == 0 +#else +	    strnicmp(name, INBOX, sizeof(INBOX)-1) == 0 +#endif +	    ) +		for (i=0; name[i] && name[i] != HIERCH; i++) +			name[i]=toupper( (int)(unsigned char)name[i] ); + +	/* ... except that "shared" should be lowercase ... */ + +	if (memcmp(name, "SHARED", 6) == 0) +		memcpy(name, "shared", 6); +} + +static int match_mailbox(char *name, char *pattern, int list_options) +{ +        if (list_options & LIST_CHECK1FOLDER) +                return strcmp(name, pattern); + +	return (match_recursive(name, pattern, HIERCH)); +} + +static int match_recursive(char *name, char *pattern, int hierch) +{ +	for (;;) +	{ +		if (*pattern == '*') +		{ +			do +			{ +				if (match_recursive(name, pattern+1, +					hierch) == 0) +					return (0); +			} while (*name++); +			return (-1); +		} +		if (*pattern == '%') +		{ +			do +			{ +				if (match_recursive(name, pattern+1, hierch) +					== 0) return (0); +				if (*name == hierch)	break; +			} while (*name++); +			return (-1); +		} +		if (*name == 0 && *pattern == 0)	break; +		if (*name == 0 || *pattern == 0)	return (-1); +		if (*name != *pattern)	return (-1); +		++name; +		++pattern; +	} +	return (0); +} | 
