diff --git a/imap/imapd.c b/imap/imapd.c
index fa6402b..6de4cde 100644
--- a/imap/imapd.c
+++ b/imap/imapd.c
@@ -171,6 +171,8 @@ static int imapd_starttls_done = 0; /* have we done a successful starttls? */
 static void *imapd_tls_comp = NULL; /* TLS compression method, if any */
 static int imapd_compress_done = 0; /* have we done a successful compress? */
 const char *plaintextloginalert = NULL;
+struct keyvalue **xlist_attrs = NULL; /* per user xlist special use conf */
+unsigned long num_xlist_attrs = 0;
 
 #ifdef HAVE_SSL
 /* our tls connection, if any */
@@ -738,6 +740,16 @@ static void imapd_reset(void)
     imapd_starttls_done = 0;
     plaintextloginalert = NULL;
 
+    if (xlist_attrs) {
+        /* Cleanup the xlist attr(s) */
+        while (num_xlist_attrs) {
+            struct keyvalue *cur_xlistattr = xlist_attrs[--num_xlist_attrs];
+	    free(cur_xlistattr);
+        }
+        free(xlist_attrs);
+	xlist_attrs = NULL;
+    }
+
     if(saslprops.iplocalport) {
 	free(saslprops.iplocalport);
 	saslprops.iplocalport = NULL;
@@ -1095,6 +1107,15 @@ void fatal(const char *s, int code)
 	free(stage);
     }
 
+    if (xlist_attrs) {
+        /* Cleanup the xlist attr(s) */
+        while (num_xlist_attrs) {
+            struct keyvalue *cur_xlistattr = xlist_attrs[--num_xlist_attrs];
+	    free(cur_xlistattr);
+        }
+        free(xlist_attrs);
+    }
+
     syslog(LOG_ERR, "Fatal error: %s", s);
     shut_down(code);
 }
@@ -9817,6 +9838,103 @@ static void xlist_check(const char *key, const char *val, void *rock)
     r->sep = " ";
 }
 
+static void xlist_read_config()
+{
+    unsigned GROWSIZE = 4096;
+    char *filename = user_hash_xlist(imapd_userid);
+    FILE *infile = NULL;
+    int lineno = 0;
+    char *buf = NULL;
+    char *p, *q, *key;
+    char internal_mailboxname[MAX_MAILBOX_BUFFER];
+    unsigned bufsize = GROWSIZE, len = 0;
+    unsigned long numalloc = 1;
+    xlist_attrs = xmalloc(numalloc * sizeof(struct keyvalue *));
+    num_xlist_attrs = 0;
+    struct keyvalue *curr_attr = NULL;
+
+    infile = fopen(filename, "r");
+    free(filename);
+    if (!infile) {
+	/* can't read file, then xlist_attrs is ready to go */
+	return;
+    }
+    buf = xmalloc(bufsize);
+
+    /* read lines of the config file */
+    while (fgets(buf+len, bufsize-len, infile)) {
+	if (buf[len]) {
+	    len = strlen(buf);
+	    if (buf[len-1] == '\n') {
+		/* end of line */
+		buf[--len] = '\0';
+	    } else if (!feof(infile) && len == bufsize-1) {
+		/* line is longer than the buffer */
+		bufsize += GROWSIZE;
+		buf = xrealloc(buf, bufsize);
+		continue;
+	    }
+	}
+	len = 0;
+
+	/* remove leading whitespace */
+	for (p = buf; *p && Uisspace(*p); p++);
+
+	/* skip comments */
+	if (!*p || *p == '#') continue;
+
+	/* go over the key */
+	key = p;
+	while (*p && (Uisalnum(*p) || *p == '-' || *p == '_')) {
+	    if (Uisupper(*p)) *p = tolower((unsigned char) *p);
+	    p++;
+	}
+	if (*p != ':') {
+	    /* keys must end with :, ignoring wrong line */
+	    continue;
+	}
+	*p++ = '\0';
+
+	/* remove leading whitespace for the value */
+	while (*p && Uisspace(*p)) p++;
+
+	/* remove trailing whitespace in the value */
+	for (q = p + strlen(p) - 1; q > p && Uisspace(*q); q--) {
+	    *q = '\0';
+	}
+
+	if (!*p) {
+	    /* empty key,  ignoring wrong line */
+	    continue;
+	}
+
+	/* set curr_attr */
+	if (!curr_attr) {
+	    curr_attr = *xlist_attrs = xzmalloc(sizeof(struct keyvalue));
+	    num_xlist_attrs++;
+	} else {
+	    /* allocate more memory if needed */
+	    if (num_xlist_attrs == numalloc) {
+		numalloc *= 2;
+		xlist_attrs = xrealloc(xlist_attrs,
+				       numalloc * sizeof(struct keyvalue *));
+	    }
+	    curr_attr = xlist_attrs[num_xlist_attrs] = xzmalloc(sizeof(struct keyvalue));
+	    num_xlist_attrs++;
+	}
+
+	/* we need to convert the key (mboxname) to internal, which
+	 * is the way it will be compared */
+	(*imapd_namespace.mboxname_tointernal)(&imapd_namespace, p,
+					       imapd_userid, internal_mailboxname);
+	curr_attr->key = xstrdup(key);
+	curr_attr->value = xstrdup(internal_mailboxname);
+    }
+
+    fclose(infile);
+    free(buf);
+}
+
 static void xlist_flags(const char *mboxname, char *sep)
 {
     char inboxname[MAX_MAILBOX_PATH+1];
@@ -9826,23 +9944,34 @@ static void xlist_flags(const char *mboxname, char *sep)
 					   imapd_userid, inboxname);
     inboxlen = strlen(inboxname);
 
-    /* doesn't match inbox, not xlistable */
-    if (strncmp(mboxname, inboxname, inboxlen))
-	return;
-
     /* inbox */
-    if (mboxname[inboxlen] == '\0') {
+    if (!strncmp(mboxname, inboxname, inboxlen) && mboxname[inboxlen] == '\0') {
 	prot_printf(imapd_out, "%s\\Inbox", sep);
+	return;
+    }
+
+    struct xlist_rock rock;
+    rock.sep = sep;
+
+    /* Go over the global xlist special-use attrs */
+    if (!strncmp(mboxname, inboxname, inboxlen) && mboxname[inboxlen] == '.') {
+      rock.mboxname = mboxname + inboxlen + 1;
+      config_foreachoverflowstring(xlist_check, &rock);
     }
-    /* subdir */
-    else if (mboxname[inboxlen] == '.') {
-	struct xlist_rock rock;
-	rock.sep = sep;
-	rock.mboxname = mboxname + inboxlen + 1;
-	config_foreachoverflowstring(xlist_check, &rock);
+
+    rock.mboxname = mboxname;
+
+    /* check per user xlist special-use attributes, reading it from file
+      * the first time (lazy loading) */
+    if (!xlist_attrs) {
+	xlist_read_config();
+    }
+
+    /* go over the per user xlist special-use attrs */
+    unsigned long i = 0;
+    for(i = 0; i < num_xlist_attrs; i++) {
+	xlist_check(xlist_attrs[i]->key, xlist_attrs[i]->value, &rock);
     }
-    /* otherwise it's actually another user who matches for
-     * the substr.  Ok to just print nothing */
 }
 
 /* Print LIST or LSUB untagged response */
diff --git a/imap/user.c b/imap/user.c
index c811cf9..f3157f8 100644
--- a/imap/user.c
+++ b/imap/user.c
@@ -84,6 +84,7 @@
 #include "sync_log.h"
 
 #define FNAME_SUBSSUFFIX ".sub"
+#define FNAME_XLISTSUFFIX ".xlist"
 
 #if 0
 static int user_deleteacl(char *name, int matchlen, int maycreate, void* rock)
@@ -510,4 +511,29 @@ char *user_hash_subs(const char *userid)
     return fname;
 }
 
+/* hash the userid to a file containing the subscriptions for that user */
+char *user_hash_xlist(const char *userid)
+{
+    char *fname = xmalloc(strlen(config_dir) + sizeof(FNAME_DOMAINDIR) +
+			  sizeof(FNAME_USERDIR) + strlen(userid) +
+			  sizeof(FNAME_XLISTSUFFIX) + 10);
+    char c, *domain;
+
+    if (config_virtdomains && (domain = strchr(userid, '@'))) {
+	char d = (char) dir_hash_c(domain+1, config_fulldirhash);
+	*domain = '\0';  /* split user@domain */
+	c = (char) dir_hash_c(userid, config_fulldirhash);
+	sprintf(fname, "%s%s%c/%s%s%c/%s%s", config_dir, FNAME_DOMAINDIR, d,
+		domain+1, FNAME_USERDIR, c, userid, FNAME_XLISTSUFFIX);
+	*domain = '@';  /* replace '@' */
+    }
+    else {
+	c = (char) dir_hash_c(userid, config_fulldirhash);
+	sprintf(fname, "%s%s%c/%s%s", config_dir, FNAME_USERDIR, c, userid,
+		FNAME_XLISTSUFFIX);
+    }
+
+    return fname;
+}
+
 
diff --git a/imap/user.h b/imap/user.h
index 3733659..1cfc29e 100644
--- a/imap/user.h
+++ b/imap/user.h
@@ -76,4 +76,7 @@ int user_deletequotaroots(const char *user);
 /* find the subscriptions file for user */
 char *user_hash_subs(const char *user);
 
+/* find the xlist file for user */
+char *user_hash_xlist(const char *user);
+
 #endif
