/*
** Copyright 1998 - 2007 S. Varshavchik.
** 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
#include	<time.h>
#if HAVE_SYS_TIME_H
#include	<sys/time.h>
#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	<courier-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)
		{
			char *opt=auth_getoptionenv("sharedgroup");
			const char *q=opt;

			if (!q) q="";

			filenamep=malloc(strlen(p)+strlen(q)+1);

			if (!filenamep)
				write_error_exit(0);

			strcat(strcpy(filenamep, p), q);
			if (opt)
				free(opt);
		}
	}

	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 (strncasecmp(name, INBOX, sizeof(INBOX)-1) == 0)
		for (i=0; name[i] && name[i] != HIERCH; i++)
			name[i]=toupper( (int)(unsigned char)name[i] );

	/* ... except that "shared" should be lowercase ... */

	if (strncmp(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);
}
