summaryrefslogtreecommitdiffstats
path: root/imap/mailboxlist.c
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /imap/mailboxlist.c
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2
Initial checkin
Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'imap/mailboxlist.c')
-rw-r--r--imap/mailboxlist.c1064
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);
+}