summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2017-01-24 22:22:09 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2017-01-24 22:22:09 +0200
commit014c99be6f811a0e099f3c0013a0a0bcc6316a1c (patch)
tree70344c65b4ef9ab593ff58edeb5ec2fada3472b9
parent8282a0f66a27de33971a2b5c42470548090bf4dc (diff)
downloadmailutils-014c99be6f811a0e099f3c0013a0a0bcc6316a1c.tar.gz
mailutils-014c99be6f811a0e099f3c0013a0a0bcc6316a1c.tar.bz2
Further improvements of IMAP LIST functionality
* imap4d/list.c (list_fun): Ignore names that contain delimiter as part of their name (untranslatable names); Don't insert spurious separators. (list_ref): Make sure a pathnames are properly separated from namespace prefixes. (imap4d_list): Fix eventual use of uninitialized pfx. * imap4d/namespace.c (prefix_translate_name): Allow for NS_OTHER prefixes ending with a delimiter. (extract_username): Return NULL if prefix without trailing delimiter was used. * imap4d/tests/atlocal.in (make_config): Add more namespace prefixes. * imap4d/tests/list.at: Add more tests. * libmailutils/string/expvar.c (exp_getvar): Return MU_WRDSE_UNDEF if the variable is defined, but has NULL value.
-rw-r--r--imap4d/list.c97
-rw-r--r--imap4d/namespace.c15
-rw-r--r--imap4d/tests/atlocal.in18
-rw-r--r--imap4d/tests/list.at282
-rw-r--r--libmailutils/string/expvar.c3
5 files changed, 324 insertions, 91 deletions
diff --git a/imap4d/list.c b/imap4d/list.c
index 54a905977..2a4e0722f 100644
--- a/imap4d/list.c
+++ b/imap4d/list.c
@@ -42,12 +42,17 @@ list_fun (mu_folder_t folder, struct mu_list_response *resp, void *data)
&& refinfo->pfx->record != resp->format)
return 0;
- name = resp->name;
- size = strlen (name);
- if (size == refinfo->reflen + 6
- && memcmp (name + refinfo->reflen + 1, "INBOX", 5) == 0)
+ name = resp->name + refinfo->dirlen;
+
+ /* There can be only one INBOX */
+ if (refinfo->reflen == 0 && strcmp (name, "INBOX") == 0)
+ return 0;
+
+ /* Ignore mailboxes that contain delimiter as part of their name */
+ if (refinfo->pfx->delim != resp->separator
+ && strchr (name, refinfo->pfx->delim))
return 0;
-
+
io_sendf ("* %s", "LIST (");
if ((resp->type & (MU_FOLDER_ATTRIBUTE_FILE|MU_FOLDER_ATTRIBUTE_DIRECTORY))
== (MU_FOLDER_ATTRIBUTE_FILE|MU_FOLDER_ATTRIBUTE_DIRECTORY))
@@ -59,7 +64,6 @@ list_fun (mu_folder_t folder, struct mu_list_response *resp, void *data)
io_sendf (") \"%c\" ", refinfo->pfx->delim);
- name = resp->name + refinfo->dirlen + 1;
size = strlen (name) + refinfo->reflen + 2;
if (size > refinfo->bufsize)
{
@@ -77,9 +81,8 @@ list_fun (mu_folder_t folder, struct mu_list_response *resp, void *data)
if (refinfo->refptr[0])
{
- p = mu_stpcpy (refinfo->buf, refinfo->refptr);
- if (refinfo->reflen == strlen (refinfo->pfx->prefix) - 1)
- *p++ = refinfo->pfx->delim;
+ memcpy (refinfo->buf, refinfo->refptr, refinfo->reflen);
+ p = refinfo->buf + refinfo->reflen;
}
else
p = refinfo->buf;
@@ -101,6 +104,21 @@ list_fun (mu_folder_t folder, struct mu_list_response *resp, void *data)
}
static int
+match_pfx (struct namespace_prefix const *pfx, char const *ref)
+{
+ char const *p = pfx->prefix, *q = ref;
+
+ for (; *q; p++, q++)
+ {
+ if (*p == 0 || *p != *q)
+ return 0;
+ }
+ if (*p == pfx->delim)
+ p++;
+ return *p == 0;
+}
+
+static int
list_ref (char const *ref, char const *wcard, char const *cwd,
struct namespace_prefix const *pfx)
{
@@ -110,8 +128,7 @@ list_ref (char const *ref, char const *wcard, char const *cwd,
char const *dir;
mu_url_t url;
- if (pfx->ns == NS_OTHER && strcmp (ref, pfx->prefix) == 0
- && strpbrk (wcard, "*%"))
+ if (pfx->ns == NS_OTHER && match_pfx (pfx, ref) && strpbrk (wcard, "*%"))
{
/* [A] server MAY return NO to such a LIST command, requiring that a
user name be included with the Other Users' Namespace prefix
@@ -128,18 +145,23 @@ list_ref (char const *ref, char const *wcard, char const *cwd,
memset (&refinfo, 0, sizeof refinfo);
+ refinfo.pfx = pfx;
/* Note: original reference would always coincide with the pfx->prefix,
if it weren't for the special handling of NS_OTHER namespace, where
the part between the prefix and the first delimiter is considered to
be a user name and is handled as part of the actual prefix. */
refinfo.refptr = ref;
refinfo.reflen = strlen (ref);
- refinfo.pfx = pfx;
-
+
mu_folder_get_url (folder, &url);
mu_url_sget_path (url, &dir);
refinfo.dirlen = strlen (dir);
-
+
+ if (refinfo.refptr[refinfo.reflen-1] == pfx->delim)
+ refinfo.reflen--;
+ else if (strcmp (ref, pfx->prefix) == 0)
+ refinfo.dirlen++;
+
/* The special name INBOX is included in the output from LIST, if
INBOX is supported by this server for this user and if the
uppercase string "INBOX" matches the interpreted reference and
@@ -236,8 +258,7 @@ imap4d_list (struct imap4d_session *session,
else
{
char *cwd = NULL;
- size_t i;
- struct namespace_prefix const *pfx;
+ struct namespace_prefix const *pfx = NULL;
if (ref[0] == 0)
{
@@ -254,27 +275,37 @@ imap4d_list (struct imap4d_session *session,
}
else
ref = mu_strdup (ref);
-
- /* Find the longest directory prefix */
- i = strcspn (wcard, "%*");
- if (wcard[i])
+
+ if (!pfx)
+ {
+ cwd = namespace_translate_name (ref, &pfx);
+ if (cwd)
+ free (cwd);
+ }
+
+ if (pfx)
{
- while (i > 0 && wcard[i - 1] != pfx->delim)
- i--;
- /* Append it to the reference */
- if (i)
+ /* Find the longest directory prefix */
+ size_t i = strcspn (wcard, "%*");
+ if (wcard[i])
{
- size_t reflen = strlen (ref);
- size_t len = i + reflen;
-
- ref = mu_realloc (ref, len);
- memcpy (ref + reflen, wcard, i - 1); /* omit the trailing / */
- ref[len-1] = 0;
-
- wcard += i;
+ while (i > 0 && wcard[i - 1] != pfx->delim)
+ i--;
+ /* Append it to the reference */
+ if (i)
+ {
+ size_t reflen = strlen (ref);
+ size_t len = i + reflen;
+
+ ref = mu_realloc (ref, len);
+ memcpy (ref + reflen, wcard, i - 1); /* omit the trailing / */
+ ref[len-1] = 0;
+
+ wcard += i;
+ }
}
}
-
+
cwd = namespace_translate_name (ref, &pfx);
if (cwd)
status = list_ref (ref, wcard, cwd, pfx);
diff --git a/imap4d/namespace.c b/imap4d/namespace.c
index 8f1e93204..f391691a9 100644
--- a/imap4d/namespace.c
+++ b/imap4d/namespace.c
@@ -188,9 +188,11 @@ prefix_translate_name (struct namespace_prefix const *pfx, char const *name,
p = mu_stpcpy (p, pfx->dir);
if (*name)
{
- if (pfx->ns == NS_OTHER
- && pfx->prefix[strlen(pfx->prefix) - 1] != pfx->delim)
+ if (pfx->ns == NS_OTHER)
{
+ if (pfx->prefix[strlen (pfx->prefix) - 1] == pfx->delim)
+ ++name;
+
while (*name && *name != pfx->delim)
name++;
}
@@ -246,10 +248,15 @@ translate_name (char const *name, struct namespace_prefix const **return_pfx)
static char *
extract_username (char const *name, struct namespace_prefix const *pfx)
{
- char const *p = name + strlen (pfx->prefix);
- char *end = strchr (p, pfx->delim);
+ char const *p;
+ char *end;
char *user;
size_t len;
+
+ if (strlen (name) < strlen (pfx->prefix))
+ return NULL;
+ p = name + strlen (pfx->prefix);
+ end = strchr (p, pfx->delim);
if (end)
len = end - p;
diff --git a/imap4d/tests/atlocal.in b/imap4d/tests/atlocal.in
index 6ccfc26cb..f44952580 100644
--- a/imap4d/tests/atlocal.in
+++ b/imap4d/tests/atlocal.in
@@ -22,8 +22,22 @@ namespace personal {
prefix "" {
directory "$HOMEDIR";
}
- prefix "~/" {
- directory "$HOMEDIR";
+ prefix "#archive:" {
+ directory "$CWD/archive";
+ delimiter ".";
+ }
+ prefix "archive." {
+ directory "$CWD/archive";
+ delimiter ".";
+ }
+}
+
+namespace other {
+ prefix "~" {
+ directory "$CWD/home/\$user";
+ }
+ prefix "other/" {
+ directory "$CWD/home/\$user";
}
}
diff --git a/imap4d/tests/list.at b/imap4d/tests/list.at
index f49eb59ff..bb8e4ee9f 100644
--- a/imap4d/tests/list.at
+++ b/imap4d/tests/list.at
@@ -18,15 +18,15 @@ m4_pushdef([IMAP4D_HOMEDIR],[spool])
dnl LIST_CHECK([NAME],[KW],[ARG],[OUTPUT],[EXTRA-CODE],[filter,][expand]
m4_define([LIST_CHECK],[
-AT_SETUP([$1])
+AT_SETUP([m4_if([$1],,[list $3],[$1])])
AT_KEYWORDS([list $2])
IMAP4D_CHECK([
mkdir IMAP4D_HOMEDIR
-m4_foreach([MAILBOX],[bigto.mbox,mbox1,mbox,relational.mbox,relational.mbox,
- search.mbox,sieve.mbox,teaparty.mbox],[
- MUT_MBCOPY($abs_top_srcdir/testsuite/spool/MAILBOX,IMAP4D_HOMEDIR)
-])
+for name in bigto relational search sieve teaparty
+do
+ cp $abs_top_srcdir/testsuite/spool/$name.mbox IMAP4D_HOMEDIR/$name
+done
$5
],
[1 LIST $3
@@ -39,54 +39,246 @@ AT_CLEANUP
])
dnl ----------------------------------------------------------------------
-LIST_CHECK([asterisk],[list00],
-["~" "*"],
+LIST_CHECK([],[list02],
+["" "*"],
[dnl
-* LIST (\NoInferiors) "/" ~/bigto.mbox
-* LIST (\NoInferiors) "/" ~/mbox
-* LIST (\NoInferiors) "/" ~/mbox1
-* LIST (\NoInferiors) "/" ~/relational.mbox
-* LIST (\NoInferiors) "/" ~/search.mbox
-* LIST (\NoInferiors) "/" ~/sieve.mbox
-* LIST (\NoInferiors) "/" ~/teaparty.mbox
+* LIST (\NoInferiors) "/" bigto
+* LIST (\NoInferiors) "/" relational
+* LIST (\NoInferiors) "/" search
+* LIST (\NoInferiors) "/" sieve
+* LIST (\NoInferiors) "/" teaparty
+* LIST (\NoInferiors) NIL INBOX
])
-LIST_CHECK([percent],[list01],
-["~" "%"],
+LIST_CHECK([],[list02],
+["" "%"],
[dnl
-* LIST (\NoInferiors) "/" ~/bigto.mbox
-* LIST (\NoInferiors) "/" ~/mbox
-* LIST (\NoInferiors) "/" ~/mbox1
-* LIST (\NoInferiors) "/" ~/relational.mbox
-* LIST (\NoInferiors) "/" ~/search.mbox
-* LIST (\NoInferiors) "/" ~/sieve.mbox
-* LIST (\NoInferiors) "/" ~/teaparty.mbox
+* LIST (\NoInferiors) "/" bigto
+* LIST (\NoInferiors) "/" relational
+* LIST (\NoInferiors) "/" search
+* LIST (\NoInferiors) "/" sieve
+* LIST (\NoInferiors) "/" teaparty
+* LIST (\NoInferiors) NIL INBOX
])
-LIST_CHECK([empty ref + asterisk],[list02],
-["" "*"],
+LIST_CHECK([duplicate INBOX],[list02],
+["" "%"],
+[dnl
+* LIST (\NoInferiors) "/" bigto
+* LIST (\NoInferiors) "/" relational
+* LIST (\NoInferiors) "/" search
+* LIST (\NoInferiors) "/" sieve
+* LIST (\NoInferiors) "/" teaparty
+* LIST (\NoInferiors) NIL INBOX
+],
+[cp $abs_top_srcdir/testsuite/spool/mbox IMAP4D_HOMEDIR/INBOX
+])
+
+LIST_CHECK([],[list07],
+["" INBOX],
[dnl
-* LIST (\NoInferiors) "/" bigto.mbox
-* LIST (\NoInferiors) "/" mbox
-* LIST (\NoInferiors) "/" mbox1
-* LIST (\NoInferiors) "/" relational.mbox
-* LIST (\NoInferiors) "/" search.mbox
-* LIST (\NoInferiors) "/" sieve.mbox
-* LIST (\NoInferiors) "/" teaparty.mbox
* LIST (\NoInferiors) NIL INBOX
])
+LIST_CHECK([],[list08],
+["" "search"],
+[dnl
+* LIST (\NoInferiors) "/" search
+])
+
+LIST_CHECK([],[],
+["" "#archive:*"],
+[* LIST (\NoInferiors) "." #archive:mbox
+* LIST (\NoInferiors) "." #archive:old.mbox1
+* LIST (\NoInferiors) "." #archive:old.very.saved
+* LIST (\NoSelect) "." #archive:old
+* LIST (\NoSelect) "." #archive:old.very
+],
+[mkdir archive archive/old archive/old/very
+cp $abs_top_srcdir/testsuite/spool/mbox archive
+cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old
+cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old/very/saved
+])
+
+LIST_CHECK([],[],
+["" "#archive:%"],
+[* LIST (\NoInferiors) "." #archive:mbox
+* LIST (\NoSelect) "." #archive:old
+],
+[mkdir archive archive/old archive/old/very
+cp $abs_top_srcdir/testsuite/spool/mbox archive
+cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old
+cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old/very/saved
+])
+
+LIST_CHECK([],[],
+["" "archive.*"],
+[* LIST (\NoInferiors) "." archive.mbox
+* LIST (\NoInferiors) "." archive.old.mbox1
+* LIST (\NoInferiors) "." archive.old.very.saved
+* LIST (\NoSelect) "." archive.old
+* LIST (\NoSelect) "." archive.old.very
+],
+[mkdir archive archive/old archive/old/very
+cp $abs_top_srcdir/testsuite/spool/mbox archive
+cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old
+cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old/very/saved
+])
+
+LIST_CHECK([],[],
+["archive" "*"],
+[* LIST (\NoInferiors) "." archive.mbox
+* LIST (\NoInferiors) "." archive.old.mbox1
+* LIST (\NoInferiors) "." archive.old.very.saved
+* LIST (\NoSelect) "." archive.old
+* LIST (\NoSelect) "." archive.old.very
+],
+[mkdir archive archive/old archive/old/very
+cp $abs_top_srcdir/testsuite/spool/mbox archive
+cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old
+cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old/very/saved
+])
+
+LIST_CHECK([],[],
+["archive." "*"],
+[* LIST (\NoInferiors) "." archive.mbox
+* LIST (\NoInferiors) "." archive.old.mbox1
+* LIST (\NoInferiors) "." archive.old.very.saved
+* LIST (\NoSelect) "." archive.old
+* LIST (\NoSelect) "." archive.old.very
+],
+[mkdir archive archive/old archive/old/very
+cp $abs_top_srcdir/testsuite/spool/mbox archive
+cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old
+cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old/very/saved
+])
+
+LIST_CHECK([],[],
+["archive." "*"],
+[* LIST (\NoInferiors) "." archive.mbox
+* LIST (\NoInferiors) "." archive.old.mbox1
+* LIST (\NoInferiors) "." archive.old.very.saved
+* LIST (\NoSelect) "." archive.old
+* LIST (\NoSelect) "." archive.old.very
+],
+[mkdir archive archive/old archive/old/very
+cp $abs_top_srcdir/testsuite/spool/mbox archive
+cp $abs_top_srcdir/testsuite/spool/mbox archive/mbox.1
+cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old
+cp $abs_top_srcdir/testsuite/spool/mbox1 archive/old/very/saved
+])
+
+LIST_CHECK([],[],
+["~" "*"],
+[1 NO LIST The requested item could not be found
+])
+
+LIST_CHECK([],[],
+["~foo" "%"],
+[* LIST (\NoInferiors) "/" ~foo/mbox
+* LIST (\NoSelect) "/" ~foo/dir
+],
+[mkdir home home/foo home/foo/dir home/bar
+cp $abs_top_srcdir/testsuite/spool/mbox home/foo
+cp $abs_top_srcdir/testsuite/spool/mbox1 home/foo/dir/saved
+cp $abs_top_srcdir/testsuite/spool/mbox1 home/bar
+])
+
+LIST_CHECK([],[],
+["~foo" "*"],
+[* LIST (\NoInferiors) "/" ~foo/dir/saved
+* LIST (\NoInferiors) "/" ~foo/mbox
+* LIST (\NoSelect) "/" ~foo/dir
+],
+[mkdir home home/foo home/foo/dir home/bar
+cp $abs_top_srcdir/testsuite/spool/mbox home/foo
+cp $abs_top_srcdir/testsuite/spool/mbox1 home/foo/dir/saved
+cp $abs_top_srcdir/testsuite/spool/mbox1 home/bar
+])
+
+LIST_CHECK([],[],
+["" "~foo/*"],
+[* LIST (\NoInferiors) "/" ~foo/dir/saved
+* LIST (\NoInferiors) "/" ~foo/mbox
+* LIST (\NoSelect) "/" ~foo/dir
+],
+[mkdir home home/foo home/foo/dir home/bar
+cp $abs_top_srcdir/testsuite/spool/mbox home/foo
+cp $abs_top_srcdir/testsuite/spool/mbox1 home/foo/dir/saved
+cp $abs_top_srcdir/testsuite/spool/mbox1 home/bar
+])
+
+LIST_CHECK([],[],
+["~foo" "%/*"],
+[* LIST (\NoInferiors) "/" ~foo/dir/saved
+],
+[mkdir home home/foo home/foo/dir home/bar
+cp $abs_top_srcdir/testsuite/spool/mbox home/foo
+cp $abs_top_srcdir/testsuite/spool/mbox1 home/foo/dir/saved
+cp $abs_top_srcdir/testsuite/spool/mbox1 home/bar
+])
+
+# ###
+
+LIST_CHECK([],[],
+["other" "*"],
+[1 NO LIST The requested item could not be found
+])
+
+LIST_CHECK([],[],
+["other/foo" "%"],
+[* LIST (\NoInferiors) "/" other/foo/mbox
+* LIST (\NoSelect) "/" other/foo/dir
+],
+[mkdir home home/foo home/foo/dir home/bar
+cp $abs_top_srcdir/testsuite/spool/mbox home/foo
+cp $abs_top_srcdir/testsuite/spool/mbox1 home/foo/dir/saved
+cp $abs_top_srcdir/testsuite/spool/mbox1 home/bar
+])
+
+LIST_CHECK([],[],
+["other/foo" "*"],
+[* LIST (\NoInferiors) "/" other/foo/dir/saved
+* LIST (\NoInferiors) "/" other/foo/mbox
+* LIST (\NoSelect) "/" other/foo/dir
+],
+[mkdir home home/foo home/foo/dir home/bar
+cp $abs_top_srcdir/testsuite/spool/mbox home/foo
+cp $abs_top_srcdir/testsuite/spool/mbox1 home/foo/dir/saved
+cp $abs_top_srcdir/testsuite/spool/mbox1 home/bar
+])
+
+LIST_CHECK([],[],
+["" "other/foo/*"],
+[* LIST (\NoInferiors) "/" other/foo/dir/saved
+* LIST (\NoInferiors) "/" other/foo/mbox
+* LIST (\NoSelect) "/" other/foo/dir
+],
+[mkdir home home/foo home/foo/dir home/bar
+cp $abs_top_srcdir/testsuite/spool/mbox home/foo
+cp $abs_top_srcdir/testsuite/spool/mbox1 home/foo/dir/saved
+cp $abs_top_srcdir/testsuite/spool/mbox1 home/bar
+])
+
+LIST_CHECK([],[],
+["other/foo" "%/*"],
+[* LIST (\NoInferiors) "/" other/foo/dir/saved
+],
+[mkdir home home/foo home/foo/dir home/bar
+cp $abs_top_srcdir/testsuite/spool/mbox home/foo
+cp $abs_top_srcdir/testsuite/spool/mbox1 home/foo/dir/saved
+cp $abs_top_srcdir/testsuite/spool/mbox1 home/bar
+])
+
# FIXME: I'm not sure whether it should include / in the reply.
LIST_CHECK([root ref + asterisk],[list03],
["/" "*"],
[dnl
-* LIST (\NoInferiors) "/" /bigto.mbox
-* LIST (\NoInferiors) "/" /mbox
-* LIST (\NoInferiors) "/" /mbox1
-* LIST (\NoInferiors) "/" /relational.mbox
-* LIST (\NoInferiors) "/" /search.mbox
-* LIST (\NoInferiors) "/" /sieve.mbox
-* LIST (\NoInferiors) "/" /teaparty.mbox
+* LIST (\NoInferiors) "/" /bigto
+* LIST (\NoInferiors) "/" /relational
+* LIST (\NoInferiors) "/" /search
+* LIST (\NoInferiors) "/" /sieve
+* LIST (\NoInferiors) "/" /teaparty
])
LIST_CHECK([absolute reference + asterisk],[list04],
@@ -110,18 +302,6 @@ LIST_CHECK([absolute reference + mailbox],[list06],
MUT_MBCOPY($abs_top_srcdir/testsuite/folder/one,IMAP4D_HOMEDIR/folder)
MUT_MBCOPY($abs_top_srcdir/testsuite/folder/two,IMAP4D_HOMEDIR/folder)])
-LIST_CHECK([empty reference + INBOX],[list07],
-["" INBOX],
-[dnl
-* LIST (\NoInferiors) NIL INBOX
-])
-
-LIST_CHECK([empty reference + mailbox],[list08],
-["" "search.mbox"],
-[dnl
-* LIST (\NoInferiors) "/" search.mbox
-])
-
dnl ----------------------------------------------------------------------
m4_popdef([IMAP4D_HOMEDIR])
diff --git a/libmailutils/string/expvar.c b/libmailutils/string/expvar.c
index 04e45421a..032ca3d0e 100644
--- a/libmailutils/string/expvar.c
+++ b/libmailutils/string/expvar.c
@@ -77,7 +77,8 @@ exp_getvar (char **ret, const char *vptr, size_t vlen, void *data)
return MU_WRDSE_NOSPACE;
*ret = s;
}
-
+ else
+ rc = MU_WRDSE_UNDEF;
return rc;
}

Return to:

Send suggestions and report system problems to the System administrator.