diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2017-01-24 22:22:09 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2017-01-24 22:22:09 +0200 |
commit | 014c99be6f811a0e099f3c0013a0a0bcc6316a1c (patch) | |
tree | 70344c65b4ef9ab593ff58edeb5ec2fada3472b9 | |
parent | 8282a0f66a27de33971a2b5c42470548090bf4dc (diff) | |
download | mailutils-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.c | 97 | ||||
-rw-r--r-- | imap4d/namespace.c | 15 | ||||
-rw-r--r-- | imap4d/tests/atlocal.in | 18 | ||||
-rw-r--r-- | imap4d/tests/list.at | 282 | ||||
-rw-r--r-- | libmailutils/string/expvar.c | 3 |
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; } |