Commit 9b610735 9b610735d22a860127a43321864274e2d74c605a by Sergey Poznyakoff

see ChangeLog

1 parent 83f6a439
......@@ -79,7 +79,13 @@ imap4d_copy0 (char *arg, int isuid, char *resp, size_t resplen)
mailbox_name = strdup ((pw) ? pw->pw_name : "");
}
else
mailbox_name = util_getfullpath (name, delim);
mailbox_name = namespace_getfullpath (name, delim);
if (!mailbox_name)
{
snprintf (resp, resplen, "NO Create failed.");
return RESP_NO;
}
/* If the destination mailbox does not exist, a server should return
an error. */
......
......@@ -52,7 +52,10 @@ imap4d_create (struct imap4d_command *command, char *arg)
return util_finish (command, RESP_BAD, "Already exist");
/* Allocates memory. */
name = util_getfullpath (name, delim);
name = namespace_getfullpath (name, delim);
if (!name)
return util_finish (command, RESP_NO, "Can not create mailbox");
/* It will fail if the mailbox already exists. */
if (access (name, F_OK) != 0)
......
......@@ -44,7 +44,9 @@ imap4d_delete (struct imap4d_command *command, char *arg)
return util_finish (command, RESP_NO, "Already exist");
/* Allocates memory. */
name = util_getfullpath (name, delim);
name = namespace_getfullpath (name, delim);
if (!name)
return util_finish (command, RESP_NO, "Can not remove");
if (remove (name) != 0)
{
......
......@@ -32,12 +32,14 @@ static struct option long_options[] =
{"help", no_argument, 0, 'h'},
{"inetd", no_argument, 0, 'i'},
{"port", required_argument, 0, 'p'},
{"other-namespace", required_argument, 0, 'O'},
{"shared-namespace", required_argument, 0, 'S'},
{"timeout", required_argument, 0, 't'},
{"version", no_argument, 0, 'v'},
{0, 0, 0, 0}
};
const char *short_options ="d::hip:t:v";
const char *short_options ="d::hip:t:vO:P:S:";
static int syslog_error_printer __P ((const char *fmt, va_list ap));
static int imap4d_mainloop __P ((int, int));
......@@ -90,6 +92,14 @@ main (int argc, char **argv)
port = strtoul (optarg, NULL, 10);
break;
case 'O':
set_namespace (NS_OTHER, optarg);
break;
case 'S':
set_namespace (NS_SHARED, optarg);
break;
case 't':
timeout = strtoul (optarg, NULL, 10);
break;
......
......@@ -132,11 +132,18 @@ struct imap4d_command
#define ERR_TIMEOUT 3
#define ERR_SIGNAL 4
/* Namespace numbers */
#define NS_PRIVATE 0
#define NS_OTHER 1
#define NS_SHARED 2
#define NS_MAX 3
extern struct imap4d_command imap4d_command_table[];
extern FILE *ofile;
extern unsigned int timeout;
extern mailbox_t mbox;
extern char *homedir;
extern char *rootdir;
extern int state;
extern volatile size_t children;
......@@ -169,7 +176,8 @@ extern int imap4d_store0 __P ((char *, int, char *, size_t));
extern int imap4d_subscribe __P ((struct imap4d_command *, char *));
extern int imap4d_uid __P ((struct imap4d_command *, char *));
extern int imap4d_unsubscribe __P ((struct imap4d_command *, char *));
extern int imap4d_namespace __P ((struct imap4d_command *, char *));
/* Synchronisation on simultenous access. */
extern int imap4d_sync __P ((void));
extern int imap4d_sync_flags __P ((size_t));
......
......@@ -141,13 +141,19 @@ imap4d_list (struct imap4d_command *command, char *arg)
}
/* Allocates. */
cwd = util_getfullpath (ref, delim);
cwd = namespace_checkfullpath (ref, wcard, delim);
if (!cwd)
{
free (ref);
return util_finish (command, RESP_NO,
"The requested item could not be found.");
}
/* If wcard match inbox return it too, part of the list. */
if (!*ref && (match ("INBOX", wcard, delim)
|| match ("inbox", wcard, delim)))
util_out (RESP_NONE, "LIST (\\NoInferiors) NIL INBOX");
if (chdir (cwd) == 0)
{
list_file (cwd, ref, (dir) ? dir : "", delim);
......@@ -156,6 +162,7 @@ imap4d_list (struct imap4d_command *command, char *arg)
free (cwd);
free (ref);
}
return util_finish (command, RESP_OK, "Completed");
}
......
......@@ -136,9 +136,10 @@ imap4d_login (struct imap4d_command *command, char *arg)
if (pw->pw_uid > 1)
setuid (pw->pw_uid);
homedir = strdup (pw->pw_dir);
homedir = util_normalize_path (strdup (pw->pw_dir), "/");
/* FIXME: Check for errors. */
chdir (homedir);
namespace_init(pw->pw_dir);
syslog (LOG_INFO, "User '%s' logged in", username);
return util_finish (command, RESP_OK, "Completed");
}
......
......@@ -26,5 +26,6 @@ imap4d_noop (struct imap4d_command *command, char *arg)
return util_finish (command, RESP_BAD, "Wrong state");
if (util_getword (arg, &sp))
return util_finish (command, RESP_BAD, "Too many args");
imap4d_select_status();
return util_finish (command, RESP_OK, "Completed");
}
......
......@@ -50,7 +50,9 @@ imap4d_rename (struct imap4d_command *command, char *arg)
return util_finish (command, RESP_NO, "Name Inbox is reservered");
/* Allocates memory. */
newname = util_getfullpath (newname, delim);
newname = namespace_getfullpath (newname, delim);
if (!newname)
return util_finish (command, RESP_NO, "Permission denied");
/* It is an error to attempt to rename from a mailbox name that already
exist. */
......@@ -118,15 +120,16 @@ imap4d_rename (struct imap4d_command *command, char *arg)
return util_finish (command, RESP_OK, "Already exist");
}
oldname = util_getfullpath (oldname, delim);
oldname = namespace_getfullpath (oldname, delim);
/* It must exist. */
if (rename (oldname, newname) != 0)
if (!oldname || rename (oldname, newname) != 0)
{
rc = RESP_NO;
msg = "Failed";
}
free (oldname);
if (oldname)
free (oldname);
free (newname);
return util_finish (command, rc, msg);
}
......
......@@ -17,6 +17,8 @@
#include "imap4d.h"
static int select_flags;
/* select ::= "SELECT" SPACE mailbox */
int
......@@ -67,40 +69,19 @@ imap4d_select0 (struct imap4d_command *command, char *arg, int flags)
mailbox_name = strdup ("/dev/null");
}
else
mailbox_name = util_getfullpath (mailbox_name, "/");
mailbox_name = namespace_getfullpath (mailbox_name, "/");
if (!mailbox_name)
return util_finish (command, RESP_NO, "Couldn't open mailbox");
if (mailbox_create (&mbox, mailbox_name) == 0
&& mailbox_open (mbox, flags) == 0)
{
const char *mflags = "\\Answered \\Flagged \\Deleted \\Seen \\Draft";
const char *pflags = "\\Answered \\Deleted \\Seen";
unsigned long uidvalidity = 0;
size_t count = 0, recent = 0, unseen = 0, uidnext = 0;
free (mailbox_name);
mailbox_uidvalidity (mbox, &uidvalidity);
mailbox_uidnext (mbox, &uidnext);
mailbox_messages_count (mbox, &count);
mailbox_messages_recent (mbox, &recent);
mailbox_message_unseen (mbox, &unseen);
util_out (RESP_NONE, "%d EXISTS", count);
util_out (RESP_NONE, "%d RECENT", recent);
util_out (RESP_OK, "[UIDVALIDITY (%d)] UID valididy status",
uidvalidity);
util_out (RESP_OK, "[UIDNEXT %d] Predicted next uid", uidnext);
if (unseen)
util_out (RESP_OK, "[UNSEEN (%d)] first unseen messsage ", unseen);
util_out (RESP_NONE, "FLAGS (%s)", mflags);
/* FIXME:
- '\*' can be supported if we use the attribute_set userflag()
- Answered is still not set in the mailbox code. */
if (flags == MU_STREAM_READ)
util_out (RESP_OK, "[PERMANENTFLAGS ()] No Permanent flags");
else
util_out (RESP_OK, "[PERMANENTFLAGS (%s)] Permanent flags", pflags);
/* Need to set the state explicitely for select. */
select_flags = flags;
state = STATE_SEL;
imap4d_select_status();
/* Need to set the state explicitely for select. */
return util_send ("%s OK [%s] %s Completed\r\n", command->tag,
(MU_STREAM_READ == flags) ?
"READ-ONLY" : "READ-WRITE", command->name);
......@@ -109,3 +90,37 @@ imap4d_select0 (struct imap4d_command *command, char *arg, int flags)
free (mailbox_name);
return status;
}
/* The code is shared between select and noop */
void
imap4d_select_status()
{
const char *mflags = "\\Answered \\Flagged \\Deleted \\Seen \\Draft";
const char *pflags = "\\Answered \\Deleted \\Seen";
unsigned long uidvalidity = 0;
size_t count = 0, recent = 0, unseen = 0, uidnext = 0;
if (state != STATE_SEL)
return;
mailbox_uidvalidity (mbox, &uidvalidity);
mailbox_uidnext (mbox, &uidnext);
mailbox_messages_count (mbox, &count);
mailbox_messages_recent (mbox, &recent);
mailbox_message_unseen (mbox, &unseen);
util_out (RESP_NONE, "%d EXISTS", count);
util_out (RESP_NONE, "%d RECENT", recent);
util_out (RESP_OK, "[UIDVALIDITY (%d)] UID valididy status",
uidvalidity);
util_out (RESP_OK, "[UIDNEXT %d] Predicted next uid", uidnext);
if (unseen)
util_out (RESP_OK, "[UNSEEN (%d)] first unseen messsage ", unseen);
util_out (RESP_NONE, "FLAGS (%s)", mflags);
/* FIXME:
- '\*' can be supported if we use the attribute_set userflag()
- Answered is still not set in the mailbox code. */
if (select_flags & MU_STREAM_READ)
util_out (RESP_OK, "[PERMANENTFLAGS ()] No Permanent flags");
else
util_out (RESP_OK, "[PERMANENTFLAGS (%s)] Permanent flags", pflags);
}
......
......@@ -51,7 +51,10 @@ imap4d_status (struct imap4d_command *command, char *arg)
mailbox_name = strdup ((pw) ? pw->pw_name : "");
}
else
mailbox_name = util_getfullpath (name, delim);
mailbox_name = namespace_getfullpath (name, delim);
if (!mailbox_name)
return util_finish (command, RESP_NO, "Error opening mailbox");
status = mailbox_create_default (&smbox, mailbox_name);
if (status == 0)
......
......@@ -138,6 +138,7 @@ imap4d_store0 (char *arg, int isuid, char *resp, size_t resplen)
}
attribute_set_flags (attr, type);
}
attribute_set_flags (attr, MU_ATTRIBUTE_MODIFIED);
flags = realloc (flags, strlen (flags) + strlen (item) + 2);
if (*flags)
strcat (flags, " ");
......
......@@ -152,6 +152,71 @@ util_tilde_expansion (const char *ref, const char *delim)
return p;
}
/* util_normalize_path: convert pathname containig relative paths specs (../)
into an equivalent absolute path. Strip trailing delimiter if present,
unless it is the only character left. E.g.:
/home/user/../smith --> /home/smith
/home/user/../.. --> /
FIXME: delim is superfluous. The function deals with unix filesystem
paths, so delim should be always "/" */
char *
util_normalize_path (char *path, const char *delim)
{
int len;
char *p;
if (!path)
return path;
len = strlen (path);
/* Empty string is returned as is */
if (len == 0)
return path;
/* delete trailing delimiter if any */
if (len && path[len-1] == delim[0])
path[len-1] = 0;
/* Eliminate any /../ */
for (p = strchr (path, '.'); p; p = strchr (p, '.'))
{
if (p > path && p[-1] == delim[0])
{
if (p[1] == '.' && (p[2] == 0 || p[2] == delim[0]))
/* found */
{
char *q, *s;
/* Find previous delimiter */
for (q = p-2; *q != delim[0] && q >= path; q--)
;
if (q < path)
break;
/* Copy stuff */
s = p + 2;
p = q;
while (*q++ = *s++)
;
continue;
}
}
p++;
}
if (path[0] == 0)
{
path[0] = delim[0];
path[1] = 0;
}
return path;
}
/* Get the absolute path. */
/* NOTE: Path is allocated and must be free()d by the caller. */
char *
......@@ -165,7 +230,7 @@ util_getfullpath (char *name, const char *delim)
free (p);
p = s;
}
return p;
return util_normalize_path (p, delim);
}
/* Return in set an allocated array contain (n) numbers, for imap messsage set
......@@ -226,7 +291,7 @@ util_msgset (char *s, size_t **set, int *n, int isuid)
{
long tmp = low;
tmp -= 2;
if (tmp <= 0 || val == 0)
if (tmp < 0 || val == 0)
{
free (*set);
*n = 0;
......@@ -245,7 +310,7 @@ util_msgset (char *s, size_t **set, int *n, int isuid)
}
else
{
status = add2set(set, n, val);
status = add2set (set, n, val);
if (status != 0)
return status;
}
......@@ -397,7 +462,7 @@ util_finish (struct imap4d_command *command, int rc, const char *format, ...)
va_start (ap, format);
status = vfprintf (ofile, buf, ap);
va_end(ap);
va_end (ap);
free (buf);
/* Reset the state. */
new_state = (rc == RESP_OK) ? command->success : command->failure;
......@@ -610,3 +675,189 @@ add2set (size_t **set, int *n, unsigned long val)
(*n)++;
return 0;
}
static const char *months[] =
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
#define c2d(c) (c-'0')
int
util_parse_internal_date (char *date, time_t *timep)
{
struct tm tm;
char *save;
int n, i;
int year, day, hour, min, sec;
char mon[4];
char sign[2];
char tzs[6];
time_t time;
memset (&tm, 0, sizeof (tm));
n = sscanf (date, "%2d-%3s-%4d %2d:%2d:%2d %5s\n",
&day, mon, &year,
&hour, &min, &sec, &tzs);
switch (n)
{
case 3:
case 6:
case 7: break;
default: return 1;
}
tm.tm_mday = day;
for (i = 0; i < 11; i++)
if (strncmp (months[i], mon, 3) == 0)
break;
if (i == 12)
return 1;
tm.tm_mon = i;
tm.tm_year = (year < 1900) ? year : year - 1900;
if (n >= 6)
{
tm.tm_hour = hour;
tm.tm_min = min;
tm.tm_sec = sec;
}
tm.tm_isdst = -1; /* unknown. */
time = mktime (&tm);
if (time == (time_t) -1)
return 2;
if (n == 7)
{
int sign;
int tz;
if (strlen (tzs) != 5)
return 3;
for (i = 1; i <= 4; i++)
if (!isdigit (tzs[i]))
return 3;
tz = (c2d (tzs[1])*10 + c2d (tzs[2]))*60 +
c2d (tzs[3])*10 + c2d (tzs[4]);
if (tzs[0] == '-')
tz = -tz;
else if (tzs[0] != '+')
return 4;
time -= tz*60;
}
*timep = time;
return 0;
}
int
util_parse_header_date (char *date, time_t *timep)
{
struct tm tm;
char *save;
int n, i;
int year, day, hour, min, sec;
char wday[5];
char mon[4];
char sign[2];
char tzs[6];
time_t time;
memset (&tm, 0, sizeof (tm));
n = sscanf (date, "%3s, %2d %3s %4d %2d:%2d:%2d %5s",
wday, &day, mon, &year,
&hour, &min, &sec, &tzs);
if (n < 7)
return 1;
tm.tm_mday = day;
for (i = 0; i < 11; i++)
if (strncmp (months[i], mon, 3) == 0)
break;
if (i == 12)
return 1;
tm.tm_mon = i;
tm.tm_year = (year < 1900) ? year : year - 1900;
if (n >= 6)
{
tm.tm_hour = hour;
tm.tm_min = min;
tm.tm_sec = sec;
}
tm.tm_isdst = -1; /* unknown. */
time = mktime (&tm);
if (time == (time_t) -1)
return 2;
if (n == 8)
{
int sign;
int tz;
if (strlen (tzs) != 5)
return 3;
for (i = 1; i <= 4; i++)
if (!isdigit (tzs[i]))
return 3;
tz = (c2d (tzs[1])*10 + c2d (tzs[2]))*60 +
c2d (tzs[3])*10 + c2d (tzs[4]);
if (tzs[0] == '-')
tz = -tz;
else if (tzs[0] != '+')
return 4;
time -= tz*60;
}
*timep = time;
return 0;
}
int
util_parse_rfc822_date (char *date, time_t *timep)
{
int year, mon, day, hour, min, sec;
int offt;
int i;
struct tm tm;
char month[5];
char wday[5];
month[0] = '\0';
wday[0] = '\0';
day = mon = year = hour = min = sec = offt = 0;
/* RFC822 Date: format. */
if (sscanf (date, "%3s %3s %2d %2d:%2d:%2d %d\n", wday, month, &day,
&hour, &min, &sec, &year) != 7)
return 1;
tm.tm_sec = sec;
tm.tm_min = min;
tm.tm_hour = hour;
for (i = 0; i < 12; i++)
{
if (strncasecmp (month, months[i], 3) == 0)
{
mon = i;
break;
}
}
tm.tm_mday = day;
tm.tm_mon = mon;
tm.tm_year = (year > 1900) ? year - 1900 : year;
tm.tm_yday = 0; /* unknown. */
tm.tm_wday = 0; /* unknown. */
tm.tm_isdst = -1; /* unknown. */
/* What to do the timezone? */
*timep = mktime (&tm);
return 0;
}
......