Commit 435f1918 435f1918df505d58b8cd3fea9b3ab6bba296faa9 by Sergey Poznyakoff

imap4d: various fixes.

* imap4d/lsub.c (imap4d_lsub): Fix untagged responses.
* imap4d/imap4d.h (make_interdir): New proto.
* imap4d/create.c (mkdir_p): Remove function.
(MKDIR_PERMISSIONS): Move macro definition to imap4d.h
(imap4d_create): Use make_interdir instead of mkdir_p.
* imap4d/rename.c (make_interdir): New function.
(imap4d_rename): Create intermediate directories as required
by RFC 3501.
Issue additional diagnostics if rename fails.
Fix OK response text.
* imap4d/imap4d.c (imap4d_mainloop): Implement idle timeout.
* imap4d/bye.c (imap4d_bye0): Reset the SIGPIPE before sending
the OK response in order to avoid recursion.

Unrelated changes:

* maidag/tests/forward.at: Fix the expected reply (see f5374bc2).
1 parent 6c615a02
......@@ -23,6 +23,14 @@ imap4d_bye (int reason)
return imap4d_bye0 (reason, NULL);
}
static jmp_buf pipejmp;
static RETSIGTYPE
sigpipe (int sig)
{
longjmp (pipejmp, 1);
}
int
imap4d_bye0 (int reason, struct imap4d_command *command)
{
......@@ -97,7 +105,14 @@ imap4d_bye0 (int reason, struct imap4d_command *command)
}
if (status == EX_OK && command)
{
/* Some clients may close the connection immediately after sending
LOGOUT. Do not treat this as error (RFC 2683). */
static int sigtab[] = { SIGPIPE };
mu_set_signals (sigpipe, sigtab, MU_ARRAY_SIZE (sigtab));
if (setjmp (pipejmp) == 0)
io_completion_response (command, RESP_OK, "Completed");
}
util_bye ();
......
......@@ -18,51 +18,6 @@
#include "imap4d.h"
#include <unistd.h>
#define MKDIR_PERMISSIONS 0700
/* FIXME: take permissions as argument */
static int
mkdir_p (char *name, int delim)
{
char *p, *dir;
int rc = 0;
dir = name;
if (dir[0] == delim)
dir++;
for (; rc == 0 && (p = strchr (dir, delim)); dir = p)
{
struct stat st;
*p++ = 0;
if (stat (name, &st) == 0)
{
if (!S_ISDIR (st.st_mode))
{
mu_diag_output (MU_DIAG_ERR,
_("component %s is not a directory"), name);
rc = 1;
}
}
else if (errno != ENOENT)
{
mu_diag_output (MU_DIAG_ERR,
_("cannot stat file %s: %s"),
name, mu_strerror (errno));
rc = 1;
}
else if (mkdir (name, MKDIR_PERMISSIONS))
{
mu_diag_output (MU_DIAG_ERR,
_("cannot create directory %s: %s"), name,
mu_strerror (errno));
rc = 1;
}
p[-1] = delim;
}
return 0;
}
/*
6.3.3. CREATE Command
......@@ -120,7 +75,7 @@ imap4d_create (struct imap4d_command *command, imap4d_tokbuf_t tok)
/* It will fail if the mailbox already exists. */
if (access (name, F_OK) != 0)
{
if (mkdir_p (name, delim[0]))
if (make_interdir (name, delim[0], MKDIR_PERMISSIONS))
{
rc = RESP_NO;
msg = "Cannot create mailbox";
......
......@@ -64,7 +64,7 @@ static struct argp_option options[] = {
{ "daemon", 'd', N_("NUMBER"), OPTION_ARG_OPTIONAL,
N_("runs in daemon mode with a maximum of NUMBER children"), 0 },
{"preauth", OPT_PREAUTH, NULL, 0,
{ "preauth", OPT_PREAUTH, NULL, 0,
N_("start in preauth mode") },
{NULL, 0, NULL, 0, NULL, 0}
......@@ -436,6 +436,8 @@ imap4d_mainloop (int ifd, int ofd)
tokp = imap4d_tokbuf_init ();
while (1)
{
if (idle_timeout && io_wait_input (idle_timeout) != 1)
imap4d_bye (ERR_TIMEOUT);
imap4d_readline (tokp);
/* check for updates */
imap4d_sync ();
......
......@@ -319,6 +319,11 @@ extern int imap4d_check_home_dir (const char *dir, uid_t uid, gid_t gid);
/* Shared between fetch and store */
extern void fetch_flags0 (const char *prefix, mu_message_t msg, int isuid);
/* Permissions for creating intermediate directories.
FIXME: These should better be configurable. */
#define MKDIR_PERMISSIONS 0700
int make_interdir (const char *name, int delim, int perms);
/* Synchronisation on simultaneous access. */
extern int imap4d_sync (void);
extern int imap4d_sync_flags (size_t);
......
......@@ -65,7 +65,8 @@ imap4d_lsub (struct imap4d_command *command, imap4d_tokbuf_t tok)
mu_iterator_current_kv (itr, (const void **)&name, (void**)&val);
if (util_wcard_match (name, pattern, delim) == 0)
io_untagged_response (RESP_NONE, "LIST () \"%s\" %s", delim, name);
io_untagged_response (RESP_NONE, "LSUB () \"%s\" \"%s\"",
delim, name);
}
}
else
......
......@@ -17,6 +17,74 @@
#include "imap4d.h"
int
make_interdir (const char *name, int delim, int perms)
{
int rc;
size_t i;
struct mu_wordsplit ws;
char *namebuf;
size_t namelen = 0;
char delimbuf[2];
namebuf = malloc (strlen (name) + 1);
if (!namebuf)
imap4d_bye (ERR_NO_MEM);
if (name[0] == '/')
namebuf[namelen++] = name[0];
delimbuf[0] = delim;
delimbuf[1] = 0;
ws.ws_delim = delimbuf;
if (mu_wordsplit (name, &ws,
MU_WRDSF_DELIM|MU_WRDSF_SQUEEZE_DELIMS|
MU_WRDSF_NOVAR|MU_WRDSF_NOCMD))
{
mu_error (_("cannot split line `%s': %s"), name,
mu_wordsplit_strerror (&ws));
free (namebuf);
return 1;
}
rc = 0;
for (i = 0; rc == 0 && i < ws.ws_wordc - 1; i++)
{
struct stat st;
strcpy (namebuf + namelen, ws.ws_wordv[i]);
namelen += strlen (ws.ws_wordv[i]);
if (stat (namebuf, &st))
{
if (errno == ENOENT)
{
if (mkdir (namebuf, perms))
{
mu_error (_("cannot create directory %s: %s"), namebuf,
mu_strerror (errno));
rc = 1;
}
}
else
{
mu_error (_("cannot stat file %s: %s"),
namebuf, mu_strerror (errno));
rc = 1;
}
}
else if (!S_ISDIR (st.st_mode))
{
mu_error (_("component %s is not a directory"), namebuf);
rc = 1;
}
namebuf[namelen++] = '/';
}
mu_wordsplit_free (&ws);
free (namebuf);
return rc;
}
/*
6.3.5. RENAME Command
......@@ -71,6 +139,12 @@ imap4d_rename (struct imap4d_command *command, imap4d_tokbuf_t tok)
}
}
if (make_interdir (newname, delim[0], MKDIR_PERMISSIONS))
{
free (newname);
return io_completion_response (command, RESP_NO, "Cannot rename");
}
/* Renaming INBOX is permitted, and has special behavior. It moves
all messages in INBOX to a new mailbox with the given name,
leaving INBOX empty. */
......@@ -119,7 +193,7 @@ imap4d_rename (struct imap4d_command *command, imap4d_tokbuf_t tok)
}
mu_mailbox_close (newmbox);
mu_mailbox_destroy (&newmbox);
return io_completion_response (command, RESP_OK, "Already exist");
return io_completion_response (command, RESP_OK, "Rename successful");
}
oldname = namespace_getfullpath (oldname, delim, NULL);
......@@ -127,14 +201,25 @@ imap4d_rename (struct imap4d_command *command, imap4d_tokbuf_t tok)
/* It must exist. */
/* FIXME: 1. What if odlname or newname is a remote mailbox?
2. If newname is local and is in another namespace, its
permissions must be fixed */
if (!oldname || rename (oldname, newname) != 0)
permissions must be fixed.
3. All in all, it would perhaps be better to use the same
algorithm as for INBOX, and delete source mailbox afterwards.
*/
if (!oldname)
{
rc = RESP_NO;
msg = "Failed";
}
else
{
if (rename (oldname, newname) != 0)
{
mu_diag_funcall (MU_DIAG_ERROR, "rename", oldname, errno);
rc = RESP_NO;
msg = "Failed";
}
if (oldname)
free (oldname);
}
free (newname);
return io_completion_response (command, rc, msg);
}
......
......@@ -44,7 +44,7 @@ echo "" >> expout.0
echo "ENVELOPE FROM: gulliver@example.net" > expout.1
echo "ENVELOPE TO: <foo@bar.baz>" >> expout.1
awk '{printf("% 4d: %s\n", NR-1,$0)}' $abs_top_srcdir/maidag/tests/input.msg >> expout.1
awk '{printf("% 4d:",NR-1); if (NF!=0) printf(" %s",$0); print ""}' $abs_top_srcdir/maidag/tests/input.msg >> expout.1
echo "END OF MESSAGE" >> expout.1
MTA_DIAG=mta.diag
......