Commit b9ef5553 b9ef5553104e1125d305c828017394c40941b19a by Sergey Poznyakoff

* examples/lsf.c: Use mu_folder_enumerate + callback function, for

speed up.
* imap4d/imap4d.h (WCARD_NOMATCH,WCARD_MATCH)
(WCARD_RECURSE_MATCH): Remove.
* imap4d/list.c: Rewrite using mu_folder_enumerate.
* imap4d/lsub.c (imap4d_lsub): Fix call to util_wcard_match.
* imap4d/util.c (util_wcard_match): Return 0 for match, 1
otherwise.
* imap4d/testsuite/imap4d/list.exp: Fix two testcases to match
the new (stricter RFC-compliant) behavior.
* include/mailutils/folder.h (mu_folder_match_fp): New typedef.
(mu_folder_enumerate_fp): New typedef.
(mu_folder_enumerate): New function.
(mu_folder_set_match, mu_folder_get_match): New functions.

* libproto/imap/folder.c, libproto/include/imap0.h,
libproto/nntp/folder.c : Use new folder list framework.
* libproto/include/folder0.h (struct _mu_folder._list): Change
signature.
(_match): New member.
* libproto/mbox/folder.c (_path_is_scheme): Improve automatic
mailbox	format detection.
(folder_mbox_list): Do not use glob, recursively scan
subdirectories instead.
* mailbox/folder.c (mu_folder_match): New function.
(mu_folder_create_from_record): Set mu_folder_match as the default
matcher.
(mu_folder_set_match, mu_folder_get_match): New functions.
(mu_folder_enumerate): New function.
(mu_folder_list): Rewrite using mu_folder_enumerate.
1 parent 6d490ce1
2007-12-21 Sergey Poznyakoff <gray@gnu.org.ua>
* examples/lsf.c: Use mu_folder_enumerate + callback function, for
speed up.
* imap4d/imap4d.h (WCARD_NOMATCH,WCARD_MATCH)
(WCARD_RECURSE_MATCH): Remove.
* imap4d/list.c: Rewrite using mu_folder_enumerate.
* imap4d/lsub.c (imap4d_lsub): Fix call to util_wcard_match.
* imap4d/util.c (util_wcard_match): Return 0 for match, 1
otherwise.
* imap4d/testsuite/imap4d/list.exp: Fix two testcases to match
the new (stricter RFC-compliant) behavior.
* include/mailutils/folder.h (mu_folder_match_fp): New typedef.
(mu_folder_enumerate_fp): New typedef.
(mu_folder_enumerate): New function.
(mu_folder_set_match, mu_folder_get_match): New functions.
* libproto/imap/folder.c, libproto/include/imap0.h,
libproto/nntp/folder.c : Use new folder list framework.
* libproto/include/folder0.h (struct _mu_folder._list): Change
signature.
(_match): New member.
* libproto/mbox/folder.c (_path_is_scheme): Improve automatic
mailbox format detection.
(folder_mbox_list): Do not use glob, recursively scan
subdirectories instead.
* mailbox/folder.c (mu_folder_match): New function.
(mu_folder_create_from_record): Set mu_folder_match as the default
matcher.
(mu_folder_set_match, mu_folder_get_match): New functions.
(mu_folder_enumerate): New function.
(mu_folder_list): Rewrite using mu_folder_enumerate.
2007-12-19 Sergey Poznyakoff <gray@gnu.org.ua>
* NEWS: Update.
......
......@@ -25,10 +25,8 @@
#include <mailutils/mailutils.h>
static int
ls_printer (void *item, void *data)
enumfun (mu_folder_t folder, struct mu_list_response *resp, void *data)
{
struct mu_list_response *resp = item;
printf ("%c%c %c %4d %s\n",
(resp->type & MU_FOLDER_ATTRIBUTE_DIRECTORY) ? 'd' : '-',
(resp->type & MU_FOLDER_ATTRIBUTE_FILE) ? 'f' : '-',
......@@ -60,13 +58,14 @@ ls_folders (char *fname, char *ref, char *pattern, int level)
return 1;
}
status = mu_folder_list (folder, ref, pattern, level, &flist);
status = mu_folder_enumerate (folder, ref, pattern, 0, level, &flist,
enumfun, NULL);
switch (status)
{
case 0:
mu_list_count (flist, &count);
printf ("Number of folders: %lu\n", (unsigned long) count);
mu_list_do (flist, ls_printer, NULL);
mu_list_destroy (&flist);
break;
case MU_ERR_NOENT:
......@@ -105,7 +104,5 @@ main (int argc, char *argv[])
mu_register_all_mbox_formats ();
if (!ref)
ref = folder;
return ls_folders (folder, ref, pattern, level);
}
......
......@@ -148,11 +148,6 @@ struct imap4d_command
#define NS_SHARED 2
#define NS_MAX 3
/* Wildcard return codes */
#define WCARD_NOMATCH 0
#define WCARD_MATCH 1
#define WCARD_RECURSE_MATCH 2
/* IMAP4D capability names */
#define IMAP_CAPA_STARTTLS "STARTTLS"
#define IMAP_CAPA_LOGINDISABLED "LOGINDISABLED"
......
......@@ -28,13 +28,90 @@
#define NOINFERIORS (1 << 3)
#define NOSELECT_RECURSE (1 << 4)
struct inode_list
static int
imap4d_match (const char *name, void *pat, int flags)
{
return util_wcard_match (name, pat, "/");
}
struct refinfo
{
struct inode_list *next;
ino_t inode;
dev_t dev;
char *refptr; /* Original reference */
size_t reflen; /* Length of the original reference */
size_t pfxlen; /* Length of the current prefix */
size_t homelen; /* Length of homedir */
char *buf;
size_t bufsize;
};
static int
list_fun (mu_folder_t folder, struct mu_list_response *resp, void *data)
{
char *name;
struct refinfo *refinfo = data;
size_t size;
name = resp->name;
size = strlen (name);
if (size == refinfo->homelen + 6
&& memcmp (name, homedir, refinfo->homelen) == 0
&& memcmp (name + refinfo->homelen + 1, "INBOX", 5) == 0)
return 0;
util_send ("* %s", "LIST (");
if ((resp->type & (MU_FOLDER_ATTRIBUTE_FILE|MU_FOLDER_ATTRIBUTE_DIRECTORY))
== (MU_FOLDER_ATTRIBUTE_FILE|MU_FOLDER_ATTRIBUTE_DIRECTORY))
/* nothing */;
else if (resp->type & MU_FOLDER_ATTRIBUTE_FILE)
util_send ("\\NoInferiors");
else if (resp->type & MU_FOLDER_ATTRIBUTE_DIRECTORY)
util_send ("\\NoSelect");
util_send (") \"%c\" ", resp->separator);
name = resp->name + refinfo->pfxlen;
size = strlen (name) + refinfo->reflen + 1;
if (size > refinfo->bufsize)
{
if (refinfo->buf == NULL)
{
refinfo->bufsize = size;
refinfo->buf = malloc (refinfo->bufsize);
if (!refinfo->buf)
{
mu_error ("%s", mu_strerror (errno));
return 1;
}
memcpy (refinfo->buf, refinfo->refptr, refinfo->reflen);
}
else
{
char *p = realloc (refinfo->buf, size);
if (!p)
{
mu_error ("%s", mu_strerror (errno));
return 1;
}
refinfo->buf = p;
refinfo->bufsize = size;
}
}
if ((refinfo->reflen == 0 || refinfo->refptr[refinfo->reflen - 1] == '/')
&& name[0] == '/')
name++;
strcpy (refinfo->buf + refinfo->reflen, name);
name = refinfo->buf;
if (strpbrk (name, "\"{}"))
util_send ("{%d}\r\n%s\r\n", strlen (name), name);
else if (is_atom (name))
util_send ("%s\r\n", name);
else
util_send ("\"%s\"\r\n", name);
return 0;
}
/*
1- IMAP4 insists: the reference argument present in the
interpreted form SHOULD prefix the interpreted form. It SHOULD
......@@ -42,6 +119,7 @@ struct inode_list
rule permits the client to determine if the returned mailbox name
is in the context of the reference argument, or if something about
the mailbox argument overrode the reference argument.
ex:
Reference Mailbox --> Interpretation
~smith/Mail foo.* --> ~smith/Mail/foo.*
......@@ -51,13 +129,8 @@ struct inode_list
archive ~fred/Mail --> ~fred/Mail/ *
2- The character "*" is a wildcard, and matches zero or more characters
at this position. The charcater "%" is similar to "*",
but it does not match a hierarchy delimiter. */
static int match (const char *, const char *, const char *);
static void list_file (const char *, const char *, const char *, const char *, struct inode_list *);
static void print_file (const char *, const char *, const char *);
static void print_dir (const char *, const char *, const char *);
at this position. The character "%" is similar to "*",
but it does not match the hierarchy delimiter. */
int
imap4d_list (struct imap4d_command *command, char *arg)
......@@ -90,8 +163,12 @@ imap4d_list (struct imap4d_command *command, char *arg)
}
else
{
int status;
mu_folder_t folder;
char *cwd;
char *dir;
char *p, *q;
struct refinfo refinfo;
switch (*wcard)
{
/* Absolute Path in wcard, dump the old ref. */
......@@ -130,20 +207,22 @@ imap4d_list (struct imap4d_command *command, char *arg)
/* Move any directory not containing a wildcard into the reference
So (ref = ~guest, wcard = Mail/folder1/%.vf) -->
(ref = ~guest/Mail/folder1, wcard = %.vf). */
for (; (dir = strpbrk (wcard, "/%*")); wcard = dir)
{
if (*dir == '/')
for (p = wcard; (q = strpbrk (p, "/%*")) && *q == '/'; p = q + 1)
;
if (p > wcard)
{
*dir = '\0';
ref = realloc (ref, strlen (ref) + 1 + (dir - wcard) + 1);
if (*ref && ref[strlen (ref) - 1] != '/')
strcat (ref, "/");
strcat (ref, wcard);
dir++;
}
else
dir = wcard;
break;
size_t seglen = p - wcard;
size_t reflen = strlen (ref);
int addslash = !!(reflen == 0 || ref[reflen-1] != '/');
size_t len = seglen + reflen + addslash + 1;
ref = realloc (ref, len);
if (addslash)
ref[reflen++] = '/';
memcpy (ref + reflen, wcard, seglen);
ref[reflen + seglen] = 0;
wcard += seglen;
}
/* Allocates. */
......@@ -154,6 +233,22 @@ imap4d_list (struct imap4d_command *command, char *arg)
return util_finish (command, RESP_NO,
"The requested item could not be found.");
}
status = mu_folder_create (&folder, cwd);
if (status)
{
free (ref);
free (cwd);
return util_finish (command, RESP_NO,
"The requested item could not be found.");
}
mu_folder_set_match (folder, imap4d_match);
memset (&refinfo, 0, sizeof refinfo);
refinfo.refptr = ref;
refinfo.reflen = strlen (ref);
refinfo.pfxlen = strlen (cwd);
refinfo.homelen = strlen (homedir);
/* The special name INBOX is included in the output from LIST, if
INBOX is supported by this server for this user and if the
......@@ -163,22 +258,14 @@ imap4d_list (struct imap4d_command *command, char *arg)
failure; it is not relevant whether the user's real INBOX resides
on this or some other server. */
if (!*ref && (match ("INBOX", wcard, delim)
|| match ("inbox", wcard, delim)))
if (!*ref && (imap4d_match ("INBOX", wcard, 0) == 0
|| imap4d_match ("inbox", wcard, 0) == 0))
util_out (RESP_NONE, "LIST (\\NoInferiors) NIL INBOX");
if (chdir (cwd) == 0)
{
struct stat st;
struct inode_list inode_rec;
stat (cwd, &st);
inode_rec.next = NULL;
inode_rec.inode = st.st_ino;
inode_rec.dev = st.st_dev;
list_file (cwd, ref, (dir) ? dir : wcard, delim, &inode_rec);
chdir (homedir);
}
mu_folder_enumerate (folder, NULL, wcard, 0, 0, NULL,
list_fun, &refinfo);
mu_folder_destroy (&folder);
free (refinfo.buf);
free (cwd);
free (ref);
}
......@@ -186,188 +273,3 @@ imap4d_list (struct imap4d_command *command, char *arg)
return util_finish (command, RESP_OK, "Completed");
}
static int
inode_list_lookup (struct inode_list *list, struct stat *st)
{
for (; list; list = list->next)
if (list->inode == st->st_ino && list->dev == st->st_dev)
return 1;
return 0;
}
static char *
mkfullname (const char *dir, const char *name, const char *delim)
{
char *p;
int dlen = strlen (dir);
if (dlen == 0)
return strdup (name);
if (dir[dlen-1] == delim[0])
dlen--;
p = malloc (dlen + 1 + strlen (name) + 1);
if (p)
{
memcpy (p, dir, dlen);
p[dlen] = '/';
strcpy (p + dlen + 1, name);
}
return p;
}
/* Recusively calling the files. */
static void
list_file (const char *cwd, const char *ref, const char *pattern,
const char *delim, struct inode_list *inode_list)
{
DIR *dirp;
struct dirent *dp;
char *next;
if (!cwd || !ref)
return;
/* Shortcut no wildcards. */
if (*pattern == '\0' || !strpbrk (pattern, "%*"))
{
/* Equivalent to stat(). */
int status;
if (*pattern == '\0')
status = match (cwd, cwd, delim);
else
status = match (pattern, pattern, delim);
if (status & NOSELECT)
print_dir (ref, pattern, delim);
else if (status & NOINFERIORS)
print_file (ref, pattern, delim);
return ;
}
dirp = opendir (".");
if (dirp == NULL)
return;
next = strchr (pattern, delim[0]);
if (next)
*next++ = '\0';
while ((dp = readdir (dirp)) != NULL)
{
/* Skip "", ".", and "..". "" is returned by at least one buggy
implementation: Solaris 2.4 readdir on NFS filesystems. */
char const *entry = dp->d_name;
if (entry[entry[0] != '.' ? 0 : entry[1] != '.' ? 1 : 2] != '\0' &&
!(!strcmp (entry, "INBOX") && !strcmp(cwd, homedir)))
{
int status = match (entry, pattern, delim);
if (status)
{
if (status & NOSELECT)
{
struct stat st;
if (stat (entry, &st))
{
mu_error (_("Cannot stat %s: %s"),
entry, strerror (errno));
continue;
}
if (next || status & RECURSE_MATCH)
{
if (!next)
print_dir (ref, entry, delim);
if (S_ISDIR (st.st_mode)
&& inode_list_lookup (inode_list, &st) == 0)
{
if (chdir (entry) == 0)
{
char *rf;
char *cd;
struct inode_list inode_rec;
inode_rec.inode = st.st_ino;
inode_rec.dev = st.st_dev;
inode_rec.next = inode_list;
rf = mkfullname (ref, entry, delim);
cd = mkfullname (cwd, entry, delim);
list_file (cd, rf, (next) ? next : pattern,
delim, &inode_rec);
free (rf);
free (cd);
chdir (cwd);
}
}
}
else
print_dir (ref, entry, delim);
}
else if (status & NOINFERIORS)
{
print_file (ref, entry, delim);
}
}
}
}
closedir (dirp);
}
static void
print_name (const char *ref, const char *file, const char *delim,
const char *attr)
{
char *name = mkfullname (ref, file, delim);
if (strpbrk (name, "\"{}"))
{
util_out (RESP_NONE, "LIST (%s) \"%s\" {%d}",
attr, delim, strlen (name));
util_send ("%s\r\n", name);
}
else if (is_atom (name))
util_out (RESP_NONE, "LIST (%s) \"%s\" %s", attr, delim, name);
else
util_out (RESP_NONE, "LIST (%s) \"%s\" \"%s\"", attr, delim, name);
free (name);
}
static void
print_file (const char *ref, const char *file, const char *delim)
{
print_name (ref, file, delim, "\\NoInferiors");
}
static void
print_dir (const char *ref, const char *file, const char *delim)
{
print_name (ref, file, delim, "\\NoSelect");
}
/* Calls the imap_matcher if a match found out the attribute. */
static int
match (const char *entry, const char *pattern, const char *delim)
{
struct stat stats;
int status = util_wcard_match (entry, pattern, delim);
switch (status)
{
case WCARD_RECURSE_MATCH:
status = RECURSE_MATCH;
break;
case WCARD_MATCH:
status = MATCH;
break;
case WCARD_NOMATCH:
status = NOMATCH;
}
if (status)
{
if (stat (entry, &stats) == 0)
status |= (S_ISREG (stats.st_mode)) ? NOINFERIORS : NOSELECT;
}
return status;
}
......
......@@ -60,12 +60,12 @@ imap4d_lsub (struct imap4d_command *command, char *arg)
char *buf = NULL;
size_t n = 0;
while (getline(&buf, &n, fp) > 0)
while (getline (&buf, &n, fp) > 0)
{
int len = strlen (buf);
if (buf[len - 1] == '\n')
buf[len - 1] = '\0';
if (util_wcard_match (buf, pattern, delim) != WCARD_NOMATCH)
if (util_wcard_match (buf, pattern, delim) == 0)
util_out (RESP_NONE, "LIST () \"%s\" %s", delim, buf);
}
fclose (fp);
......
......@@ -53,8 +53,6 @@ imap4d_test "LIST \"/\" \"*\""\
imap4d_test -sort "LIST \"$MU_DATA_DIR\" \"*\""\
"LIST (\\NoSelect) \"/\" $MU_DATA_DIR/etc"\
"LIST (\\NoInferiors) \"/\" $MU_DATA_DIR/etc/mail.rc"\
"LIST (\\NoInferiors) \"/\" $MU_DATA_DIR/etc/passwd"\
"LIST (\\NoSelect) \"/\" $MU_DATA_DIR/spool"\
"LIST (\\NoInferiors) \"/\" $MU_DATA_DIR/spool/bigto.mbox"\
"LIST (\\NoInferiors) \"/\" $MU_DATA_DIR/spool/relational.mbox" \
......@@ -77,13 +75,8 @@ imap4d_test "LIST \"$MU_DATA_DIR/folder\" \"one\""\
"LIST (\\NoInferiors) \"/\" $MU_DATA_DIR/folder/one"\
"OK LIST Completed"
# Well, I doubt if this is quite OK with the RFC, but at least it does
# not contradict it. The first INBOX refers to the reserved word meaning
# "the primary mailbox for this user on this server", the second one is
# the actual filename.
imap4d_test -sort "LIST \"\" INBOX"\
"LIST (\\NoInferiors) NIL INBOX"\
"LIST (\\NoInferiors) \"/\" INBOX"\
"OK LIST Completed"
imap4d_stop
......
......@@ -1028,7 +1028,7 @@ util_localname ()
return localname;
}
/* Match STRING against the IMAP4 wildard pattern PATTERN */
/* Match STRING against the IMAP4 wildcard pattern PATTERN. */
int
util_wcard_match (const char *string, const char *pattern, const char *delim)
......@@ -1041,40 +1041,39 @@ util_wcard_match (const char *string, const char *pattern, const char *delim)
switch (c)
{
case '%':
/* Matches everything except '/' */
if (*p == '\0')
{
/* Matches everything except '/' */
for (; *n && *n != delim[0]; n++)
;
return (*n == delim[0]) ? WCARD_RECURSE_MATCH : WCARD_MATCH;
for (; *n; ++n)
if (*n == *delim)
return 1;
return 0;
}
else
for (; *n != '\0'; ++n)
if (util_wcard_match (n, p, delim) == WCARD_MATCH)
return WCARD_MATCH;
if (util_wcard_match (n, p, delim) == 0)
return 0;
break;
case '*':
if (*p == '\0')
return WCARD_RECURSE_MATCH;
return 0;
else
for (; *n != '\0'; ++n)
{
int status = util_wcard_match (n, p, delim);
if (status == WCARD_MATCH || status == WCARD_RECURSE_MATCH)
return status;
}
if (util_wcard_match (n, p, delim) == 0)
return 0;
break;
default:
if (c != *n)
return WCARD_NOMATCH;
return 1;
}
}
if (!c && !*n)
return WCARD_MATCH;
return 0;
return WCARD_NOMATCH;
return 1;
}
/* Return the uindvalidity of a mailbox.
......
......@@ -33,6 +33,10 @@ struct mu_list_response
char *name;
};
typedef int (*mu_folder_match_fp) (const char *, void *, int);
typedef int (*mu_folder_enumerate_fp) (mu_folder_t, struct mu_list_response *,
void *data);
/* Constructor/destructor and possible types. */
extern int mu_folder_create (mu_folder_t *, const char *);
extern int mu_folder_create_from_record (mu_folder_t *, const char *,
......@@ -47,8 +51,12 @@ extern int mu_folder_delete (mu_folder_t, const char *);
extern int mu_folder_rename (mu_folder_t, const char *, const char *);
extern int mu_folder_subscribe (mu_folder_t, const char *);
extern int mu_folder_unsubscribe (mu_folder_t, const char *);
extern int mu_folder_list (mu_folder_t, const char *, const char *,
extern int mu_folder_list (mu_folder_t, const char *, void *,
size_t, mu_list_t *);
extern int mu_folder_enumerate (mu_folder_t, const char *,
void *, int,
size_t, mu_list_t *,
mu_folder_enumerate_fp, void *);
extern int mu_folder_lsub (mu_folder_t, const char *, const char *,
mu_list_t *);
......@@ -56,6 +64,11 @@ extern int mu_folder_lsub (mu_folder_t, const char *, const char *,
extern int mu_folder_get_stream (mu_folder_t, mu_stream_t *);
extern int mu_folder_set_stream (mu_folder_t, mu_stream_t);
/* Match function */
extern int mu_folder_set_match (mu_folder_t folder, mu_folder_match_fp pmatch);
extern int mu_folder_get_match (mu_folder_t folder,
mu_folder_match_fp *pmatch);
/* Notifications. */
extern int mu_folder_get_observable (mu_folder_t, mu_observable_t *);
......
......@@ -111,6 +111,7 @@ typedef struct _mu_tcp_server *mu_tcp_server_t;
#define MU_FOLDER_ATTRIBUTE_DIRECTORY 0x001
#define MU_FOLDER_ATTRIBUTE_FILE 0x002
#define MU_FOLDER_ATTRIBUTE_ALL \
(MU_FOLDER_ATTRIBUTE_DIRECTORY|MU_FOLDER_ATTRIBUTE_FILE)
......
......@@ -111,9 +111,10 @@ static int folder_imap_open (mu_folder_t, int);
static int folder_imap_close (mu_folder_t);
static void folder_imap_destroy (mu_folder_t);
static int folder_imap_delete (mu_folder_t, const char *);
static int folder_imap_list (mu_folder_t, const char *, const char *,
static int folder_imap_list (mu_folder_t, const char *, void *,
size_t,
mu_list_t);
mu_list_t,
mu_folder_enumerate_fp efp, void *edp);
static int folder_imap_lsub (mu_folder_t, const char *, const char *,
mu_list_t);
static int folder_imap_rename (mu_folder_t, const char *,
......@@ -975,9 +976,10 @@ glob_to_imap (const char *pat, int recursive)
}
static int
folder_imap_list (mu_folder_t folder, const char *ref, const char *name,
folder_imap_list (mu_folder_t folder, const char *ref, void *name,
size_t max_level,
mu_list_t flist)
mu_list_t flist,
mu_folder_enumerate_fp efp, void *edp)
{
f_imap_t f_imap = folder->data;
int status = 0;
......@@ -992,6 +994,11 @@ folder_imap_list (mu_folder_t folder, const char *ref, const char *name,
if (name == NULL)
name = "";
f_imap->folder = folder;
f_imap->enum_fun = efp;
f_imap->enum_stop = 0;
f_imap->enum_data = edp;
switch (f_imap->state)
{
case IMAP_NO_STATE:
......@@ -1017,6 +1024,11 @@ folder_imap_list (mu_folder_t folder, const char *ref, const char *name,
break;
}
f_imap->folder = NULL;
f_imap->enum_fun = NULL;
f_imap->enum_stop = 0;
f_imap->enum_data = NULL;
list_copy (flist, f_imap->flist, strlen (ref),
imap_mailbox_name_match, name, max_level);
......@@ -1041,6 +1053,10 @@ folder_imap_lsub (mu_folder_t folder, const char *ref, const char *name,
if (name == NULL)
name = "";
f_imap->enum_fun = NULL;
f_imap->enum_stop = 0;
f_imap->enum_data = NULL;
switch (f_imap->state)
{
case IMAP_NO_STATE:
......@@ -1385,6 +1401,9 @@ imap_list (f_imap_t f_imap)
int argc;
char **argv;
if (f_imap->enum_stop)
return 0;
buffer = malloc (len);
if (!buffer)
return ENOMEM;
......@@ -1400,7 +1419,6 @@ imap_list (f_imap_t f_imap)
mu_list_create (&f_imap->flist);
mu_list_set_destroy_item (f_imap->flist, mu_list_response_free);
}
mu_list_append (f_imap->flist, lr);
/* Glob untag. */
tok = strtok_r (buffer, " ", &sp);
......@@ -1475,6 +1493,12 @@ imap_list (f_imap_t f_imap)
}
mu_argcv_free (argc, argv);
free (buffer);
if (f_imap->enum_fun)
f_imap->enum_stop = f_imap->enum_fun (f_imap->folder, lr,
f_imap->enum_data);
mu_list_append (f_imap->flist, lr);
return status;
}
......
......@@ -33,6 +33,9 @@
extern "C" {
#endif
#define MU_FOLDER_LIST 0
#define MU_FOLDER_ENUM 1
struct _mu_folder
{
/* Data */
......@@ -55,11 +58,11 @@ struct _mu_folder
int (*_open) (mu_folder_t, int flag);
int (*_close) (mu_folder_t);
int (*_list) (mu_folder_t, const char *, const char *,
size_t,
mu_list_t);
int (*_list) (mu_folder_t, const char *, void *, int, size_t,
mu_list_t, mu_folder_enumerate_fp, void *);
int (*_lsub) (mu_folder_t, const char *, const char *,
mu_list_t);
mu_folder_match_fp _match;
int (*_delete) (mu_folder_t, const char *);
int (*_rename) (mu_folder_t, const char *, const char *);
int (*_subscribe) (mu_folder_t, const char *);
......
......@@ -161,6 +161,9 @@ struct _f_imap
/* Use for LIST and LSUB. */
mu_list_t flist;
mu_folder_enumerate_fp enum_fun;
void *enum_data;
int enum_stop;
int isopen;
......@@ -170,8 +173,8 @@ struct _f_imap
char *ptr;
char *nl;
mu_off_t offset; /* Dummy, this is used because of the stream buffering.
The mu_stream_t maintains and offset and the offset we use must
be in sync. */
The mu_stream_t maintains and offset and the offset we
use must be in sync. */
/* Login */
char *user;
......
......@@ -41,6 +41,7 @@
#include <mailutils/stream.h>
#include <mailutils/mutil.h>
#include <mailutils/errno.h>
#include <mailutils/debug.h>
/* We export url parsing and the initialisation of
the mailbox, via the register entry/record. */
......@@ -77,11 +78,32 @@ _path_is_scheme (mu_record_t record, const char *url, int flags)
struct stat st;
if (stat (path, &st) < 0)
return MU_FOLDER_ATTRIBUTE_ALL; /* mu_mailbox_open will complain */
{
if (errno == ENOENT)
rc |= MU_FOLDER_ATTRIBUTE_FILE;
return rc;
}
if ((flags & MU_FOLDER_ATTRIBUTE_FILE)
&& (S_ISREG (st.st_mode) || S_ISCHR (st.st_mode)))
if (S_ISREG (st.st_mode) || S_ISCHR (st.st_mode))
{
if (st.st_size == 0)
{
rc |= MU_FOLDER_ATTRIBUTE_FILE;
}
else if (flags & MU_FOLDER_ATTRIBUTE_FILE)
{
int fd = open (path, O_RDONLY);
if (fd != -1)
{
char buf[5];
if (read (fd, buf, 5) == 5)
if (memcmp (buf, "From ", 5) == 0)
rc |= MU_FOLDER_ATTRIBUTE_FILE;
close (fd);
}
}
}
if ((flags & MU_FOLDER_ATTRIBUTE_DIRECTORY)
&& S_ISDIR (st.st_mode))
rc |= MU_FOLDER_ATTRIBUTE_DIRECTORY;
......@@ -113,8 +135,9 @@ static int folder_mbox_open (mu_folder_t, int);
static int folder_mbox_close (mu_folder_t);
static int folder_mbox_delete (mu_folder_t, const char *);
static int folder_mbox_rename (mu_folder_t , const char *, const char *);
static int folder_mbox_list (mu_folder_t, const char *, const char *,
size_t, mu_list_t);
static int folder_mbox_list (mu_folder_t, const char *, void *, int,
size_t, mu_list_t, mu_folder_enumerate_fp,
void *);
static int folder_mbox_subscribe (mu_folder_t, const char *);
static int folder_mbox_unsubscribe (mu_folder_t, const char *);
static int folder_mbox_lsub (mu_folder_t, const char *, const char *,
......@@ -228,7 +251,8 @@ folder_mbox_delete (mu_folder_t folder, const char *filename)
}
static int
folder_mbox_rename (mu_folder_t folder, const char *oldpath, const char *newpath)
folder_mbox_rename (mu_folder_t folder, const char *oldpath,
const char *newpath)
{
fmbox_t fmbox = folder->data;
if (oldpath && newpath)
......@@ -266,8 +290,15 @@ struct inode_list /* Inode/dev number list used to cut off
struct search_data
{
mu_list_t result;
const char *pattern;
mu_folder_enumerate_fp enumfun;
void *enumdata;
char *dirname;
size_t dirlen;
void *pattern;
int flags;
size_t max_level;
size_t errcnt;
mu_folder_t folder;
};
static int
......@@ -279,126 +310,146 @@ inode_list_lookup (struct inode_list *list, struct stat *st)
return 0;
}
static int
list_helper (struct search_data *data,
const char *dirname, size_t level, struct inode_list *ilist)
list_helper (struct search_data *data, const char *dirname, size_t level,
struct inode_list *ilist)
{
int status;
glob_t gl;
char *pathname;
DIR *dirp;
struct dirent *dp;
int stop = 0;
++level;
if (data->max_level && level > data->max_level)
return 0;
pathname = get_pathname (dirname, data->pattern);
if (!pathname)
return ENOMEM;
memset(&gl, 0, sizeof(gl));
status = glob (pathname, 0, NULL, &gl);
free (pathname);
if (status == 0)
dirp = opendir (dirname);
if (dirp == NULL)
{
size_t i;
struct mu_list_response *resp;
MU_DEBUG2 (data->folder->debug, MU_DEBUG_ERROR,
"list_helper cannot open directory %s: %s",
dirname, mu_strerror (errno));
data->errcnt++;
return 1;
}
for (i = 0; i < gl.gl_pathc; i++)
while ((dp = readdir (dirp)))
{
char const *ename = dp->d_name;
char *fname;
if (ename[ename[0] != '.' ? 0 : ename[1] != '.' ? 1 : 2] == 0)
continue;
fname = get_pathname (dirname, ename);
if (data->folder->_match == NULL
|| data->folder->_match (fname + data->dirlen +
((data->dirlen > 1
&& data->dirname[data->dirlen-1] != '/') ?
1 : 0),
data->pattern,
data->flags) == 0)
{
struct stat st;
if (stat (fname, &st) == 0)
{
char *refname = fname;
int type = 0;
struct mu_list_response *resp;
resp = malloc (sizeof (*resp));
if (resp == NULL)
{
status = ENOMEM;
break;
MU_DEBUG1 (data->folder->debug, MU_DEBUG_ERROR,
"list_helper: %s", mu_strerror (ENOMEM));
data->errcnt++;
free (fname);
continue;
}
else if ((resp->name = strdup (gl.gl_pathv[i])) == NULL)
mu_registrar_lookup (refname, MU_FOLDER_ATTRIBUTE_ALL, NULL,
&type);
resp->name = fname;
resp->level = level;
resp->separator = '/';
resp->type = type;
if (resp->type == 0)
{
free (resp->name);
free (resp);
status = ENOMEM;
break;
continue;
}
resp->level = level;
resp->separator = '/';
resp->type = 0;
if (data->enumfun)
{
if (data->enumfun (data->folder, resp, data->enumdata))
{
free (resp->name);
free (resp);
stop = 1;
break;
}
}
if (data->result)
{
fname = NULL;
mu_list_append (data->result, resp);
}
else
free (resp);
if (stat (gl.gl_pathv[i], &st) == 0)
{
resp->type = 0;
mu_registrar_lookup (gl.gl_pathv[i], MU_FOLDER_ATTRIBUTE_ALL,
NULL, &resp->type);
if ((resp->type & MU_FOLDER_ATTRIBUTE_DIRECTORY)
if ((type & MU_FOLDER_ATTRIBUTE_DIRECTORY)
&& !inode_list_lookup (ilist, &st))
{
struct inode_list idata;
idata.inode = st.st_ino;
idata.dev = st.st_dev;
idata.next = ilist;
status = list_helper (data, gl.gl_pathv[i], level, &idata);
if (status)
break;
}
stop = list_helper (data, refname, level + 1, &idata);
}
}
globfree (&gl);
}
else
{
switch (status)
{
case GLOB_NOSPACE:
status = ENOMEM;
break;
case GLOB_ABORTED:
status = MU_ERR_READ;
break;
case GLOB_NOMATCH:
if (mu_list_is_empty (data->result))
status = MU_ERR_NOENT;
else
status = 0;
break;
case GLOB_NOSYS:
status = ENOSYS;
break;
default:
status = MU_ERR_FAILURE;
break;
MU_DEBUG2 (data->folder->debug, MU_DEBUG_ERROR,
"list_helper cannot stat %s: %s",
fname, mu_strerror (errno));
}
}
return status;
free (fname);
}
closedir (dirp);
return stop;
}
/* The listing is not recursive and we use glob() some expansion for us.
Unfortunately glob() does not expand the '~'. We also return
the full pathname so it can be use to create other folders. */
static int
folder_mbox_list (mu_folder_t folder, const char *dirname, const char *pattern,
folder_mbox_list (mu_folder_t folder, const char *ref,
void *pattern,
int flags,
size_t max_level,
mu_list_t flist)
mu_list_t flist,
mu_folder_enumerate_fp enumfun, void *enumdata)
{
fmbox_t fmbox = folder->data;
struct inode_list iroot;
struct search_data sdata;
memset (&iroot, 0, sizeof iroot);
if (dirname == NULL || dirname[0] == '\0')
dirname = (const char *)fmbox->dirname;
sdata.dirname = get_pathname (fmbox->dirname, ref);
sdata.dirlen = strlen (sdata.dirname);
sdata.result = flist;
sdata.enumfun = enumfun;
sdata.enumdata = enumdata;
sdata.pattern = pattern;
sdata.flags = flags;
sdata.max_level = max_level;
return list_helper (&sdata, dirname, 0, &iroot);
sdata.folder = folder;
sdata.errcnt = 0;
list_helper (&sdata, sdata.dirname, 0, &iroot);
free (sdata.dirname);
/* FIXME: error code */
return 0;
}
static int
......
......@@ -63,9 +63,10 @@ static int nntp_folder_open (mu_folder_t, int);
static int nntp_folder_close (mu_folder_t);
static void nntp_folder_destroy (mu_folder_t folder);
static int nntp_folder_list (mu_folder_t folder, const char *ref,
const char *name,
void *name,
size_t max,
mu_list_t flist);
mu_list_t flist,
mu_folder_enumerate_fp efp, void *edp);
int
_nntp_folder_init (mu_folder_t folder)
......@@ -185,8 +186,9 @@ nntp_folder_destroy (mu_folder_t folder)
static int
nntp_folder_list (mu_folder_t folder, const char *ref, const char *name,
size_t max_level, mu_list_t flist)
nntp_folder_list (mu_folder_t folder, const char *ref, void *pat,
size_t max_level, mu_list_t flist,
mu_folder_enumerate_fp efp, void *edp)
{
return ENOTSUP;
}
......
......@@ -24,6 +24,7 @@
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fnmatch.h>
#include <mailutils/auth.h>
#include <mailutils/debug.h>
......@@ -45,13 +46,19 @@ static int is_known_folder (mu_url_t, mu_folder_t *);
/* Static folder lock. */
static struct mu_monitor folder_lock = MU_MONITOR_INITIALIZER;
int
mu_folder_match (const char *name, void *pattern, int flags)
{
return fnmatch (pattern, name[0] == '/' ? name + 1 : name, flags);
}
/* A folder could be remote (IMAP), or local(a spool directory) like $HOME/Mail
etc .. We maintain a known list of folder to not generate multiple folder
of the same URL. Meaning when mu_folder_create () is call we'll check if we
already have a folder for that URL and return the same, if not we create a
new one. The downside, the scheme to detect the same URL is very weak, and
they maybe cases where you want a different folder for the same URL, there
is not easy way to do this. */
etc .. We maintain a list of known folders to avoid creating multiple
folders for the same URL. So, when mu_folder_create is called we check if
we already have a folder for that URL and return it, otherwise we create a
new one. Downsides: the scheme to detect the same URL is very weak, and
there could be cases where you'll want a different folder for the same URL,
there is not easy way to do this. */
int
mu_folder_create_from_record (mu_folder_t *pfolder, const char *name,
mu_record_t record)
......@@ -112,6 +119,8 @@ mu_folder_create_from_record (mu_folder_t *pfolder, const char *name,
status = f_init (folder);
if (status == 0)
{
if (!folder->_match)
folder->_match = mu_folder_match;
*pfolder = folder;
folder->ref++;
/* Put on the internal list of known folders. */
......@@ -280,6 +289,26 @@ mu_folder_get_observable (mu_folder_t folder, mu_observable_t *pobservable)
}
int
mu_folder_set_match (mu_folder_t folder, mu_folder_match_fp pmatch)
{
if (folder == NULL)
return EINVAL;
folder->_match = pmatch;
return 0;
}
int
mu_folder_get_match (mu_folder_t folder, mu_folder_match_fp *pmatch)
{
if (folder == NULL)
return EINVAL;
if (pmatch == NULL)
return MU_ERR_OUT_PTR_NULL;
*pmatch = folder->_match;
return 0;
}
int
mu_folder_has_debug (mu_folder_t folder)
{
if (folder == NULL)
......@@ -325,20 +354,41 @@ mu_list_response_free (void *data)
}
int
mu_folder_list (mu_folder_t folder, const char *dirname, const char *basename,
mu_folder_list (mu_folder_t folder, const char *dirname, void *pattern,
size_t max_level,
mu_list_t *pflist)
{
return mu_folder_enumerate (folder, dirname, pattern, 0, max_level,
pflist, NULL, NULL);
}
int
mu_folder_enumerate (mu_folder_t folder, const char *name,
void *pattern, int flags,
size_t max_level,
mu_list_t *pflist,
mu_folder_enumerate_fp enumfun, void *enumdata)
{
int status;
if (folder == NULL || folder->_list == NULL)
return EINVAL;
else
{
status = mu_list_create (pflist);
mu_list_t list = NULL;
if (pflist)
{
status = mu_list_create (&list);
if (status)
return status;
mu_list_set_destroy_item (*pflist, mu_list_response_free);
status = folder->_list (folder, dirname, basename, max_level, *pflist);
*pflist = list;
mu_list_set_destroy_item (list, mu_list_response_free);
}
else if (!enumfun)
return EINVAL;
status = folder->_list (folder, name, pattern, flags, max_level,
list, enumfun, enumdata);
if (status)
mu_list_destroy (pflist);
}
......