Commit 5ec8063e 5ec8063eccd7552626991c977bcaa8574604009e by Alain Magloire

* mailbox/imap4d/fetch.c : First draft implementation, very yucky.

* mailbox/imap4d/select.c : First draft implementation.
* mailbox/imap4d/util.c : Add util_msgset() and util_send().
Reuse of util_getcommand() for subcommands.
1 parent 0412541c
......@@ -17,22 +17,412 @@
#include "imap4d.h"
/*
* This will suck, too
*/
/* This will suck, too.
Alain: Yest it does. */
/* Taken from RFC2060
fetch ::= "FETCH" SPACE set SPACE ("ALL" / "FULL" /
"FAST" / fetch_att / "(" 1#fetch_att ")")
fetch_att ::= "ENVELOPE" / "FLAGS" / "INTERNALDATE" /
"RFC822" [".HEADER" / ".SIZE" / ".TEXT"] /
"BODY" ["STRUCTURE"] / "UID" /
"BODY" [".PEEK"] section
["<" number "." nz_number ">"]
*/
static int fetch_all (struct imap4d_command *, char*);
static int fetch_full (struct imap4d_command *, char*);
static int fetch_fast (struct imap4d_command *, char*);
static int fetch_envelope (struct imap4d_command *, char*);
static int fetch_flags (struct imap4d_command *, char*);
static int fetch_internaldate (struct imap4d_command *, char*);
static int fetch_rfc822_header (struct imap4d_command *, char*);
static int fetch_rfc822_size (struct imap4d_command *, char*);
static int fetch_rfc822_text (struct imap4d_command *, char*);
static int fetch_rfc822 (struct imap4d_command *, char*);
static int fetch_bodystructure (struct imap4d_command *, char*);
static int fetch_body_peek (struct imap4d_command *, char*);
static int fetch_body (struct imap4d_command *, char*);
static int fetch_uid (struct imap4d_command *, char*);
struct imap4d_command fetch_command_table [] =
{
#define F_ALL 0
{"ALL", fetch_all},
#define F_FULL 1
{"FULL", fetch_full},
#define F_FAST 2
{"FAST", fetch_fast},
#define F_ENVELOPE 3
{"ENVELOPE", fetch_envelope},
#define F_FLAGS 4
{"FLAGS", fetch_flags},
#define F_INTERNALDATE 5
{"INTERNALDATE", fetch_internaldate},
#define F_RFC822_HEADER 6
{"RFC822.HEADER", fetch_rfc822_header},
#define F_RFC822_SIZE 7
{"RFC822.SIZE", fetch_rfc822_size},
#define F_RFC822_TEXT 8
{"RFC822.TEXT", fetch_rfc822_text},
#define F_RFC822 9
{"RFC822", fetch_rfc822},
#define F_BODYSTRUCTURE 10
{"BODYSTRUCTURE", fetch_bodystructure},
#define F_BODY_PEEK 12
{"BODY.PEEK", fetch_body_peek},
#define F_BODY 13
{"BODY", fetch_body},
#define F_UID 14
{"UID", fetch_uid},
{ 0, 0},
};
int
imap4d_fetch (struct imap4d_command *command, char *arg)
{
char *sp = NULL;
char *message_set;
char *msgset;
int *set = NULL;
char *item;
int i, n = 0;
int rc = RESP_OK;
int status;
const char *errmsg = "Completed";
struct imap4d_command *fcmd;
message_set = util_getword (arg, &sp);
item = util_getword (NULL, &sp);
if (!message_set || !item)
msgset = util_getword (arg, &sp);
if (!msgset)
return util_finish (command, RESP_BAD, "Too few args");
/* check for paren list */
return util_finish (command, RESP_NO, "Not supported");
item = strchr (sp, '[');
if (item)
{
char *s = alloca (item - sp + 1);
memcpy (s, sp, item - sp);
s[item - sp] = '\0';
arg = item;
item = s;
}
else
arg = item = sp;
fcmd = util_getcommand (item, fetch_command_table);
if (!fcmd)
return util_finish (command, RESP_BAD, "Command unknown");
status = util_msgset (msgset, &set, &n, 0);
if (status != 0)
return util_finish (command, RESP_BAD, "Bogus number set");
/* We use the states to hold the msgno/uid, the success to say if we're
The first. */
for (i = 0; i < n; i++)
{
fcmd->states = set[i];
fcmd->tag = command->tag;
fcmd->success = 1;
status = fcmd->func (fcmd, sp);
if (status != 0)
{
rc = RESP_BAD;
errmsg = "Bogus attribute";
break;
}
else
util_send (")\r\n");
}
free (set);
return util_finish (command, rc, errmsg);
}
/* --------------- Fetch commands definition ----- */
#define EPILOGUE(command) \
do { \
if (command->success) \
util_send ("* FETCH %d (%s", command->states, command->name); \
else \
util_send (" %s", command->name); \
} while (0)
static int
fetch_all (struct imap4d_command *command, char *arg)
{
struct imap4d_command c_env = fetch_command_table[F_ENVELOPE];
fetch_fast (command, arg);
c_env.states = command->states;
c_env.success = 0;
fetch_envelope (&c_env, arg);
return 0;
}
static int
fetch_full (struct imap4d_command *command, char *arg)
{
struct imap4d_command c_body = fetch_command_table[F_BODY];
fetch_all (command, arg);
c_body.states = command->states;
c_body.success = 0;
fetch_body (&c_body, arg);
return 0;
}
static int
fetch_fast (struct imap4d_command *command, char *arg)
{
struct imap4d_command c_idate = fetch_command_table[F_INTERNALDATE];
struct imap4d_command c_rfc = fetch_command_table[F_RFC822_SIZE];
struct imap4d_command c_flags = fetch_command_table[F_FLAGS];
c_flags.states = command->states;
c_flags.success = command->success;
fetch_flags (&c_flags, arg);
command->success = 0;
c_idate.states = command->states;
c_idate.success = 0;
fetch_internaldate (&c_idate, arg);
c_rfc.states = command->states;
c_rfc.success = 0;
fetch_rfc822_size (&c_rfc, arg);
return 0;
}
/* FIXME, FIXME:
- incorrect DATE
- address not the correct format
- strings change to literals when detecting '"'
*/
static int
fetch_envelope (struct imap4d_command *command, char *arg)
{
char buffer[512];
header_t header = NULL;
message_t msg = NULL;
int status;
mailbox_get_message (mbox, command->states, &msg);
message_get_header (msg, &header);
EPILOGUE(command);
status = header_get_value (header, "Date", buffer, sizeof (buffer), NULL);
util_send (" \"%s\"", buffer);
status = header_get_value (header, "Subject", buffer, sizeof (buffer), NULL);
util_send (" \"%s\"", buffer);
status = header_get_value (header, "From", buffer, sizeof (buffer), NULL);
util_send (" ((NIL NIL NIL \"%s\"))", buffer);
status = header_get_value (header, "Sender", buffer, sizeof (buffer), NULL);
util_send (" ((NIL NIL NIL \"%s\"))", buffer);
status = header_get_value (header, "Reply-to", buffer, sizeof (buffer), NULL);
util_send (" ((NIL NIL NIL \"%s\"))", buffer);
status = header_get_value (header, "To", buffer, sizeof (buffer), NULL);
util_send (" ((NIL NIL NIL \"%s\"))", buffer);
status = header_get_value (header, "Cc", buffer, sizeof (buffer), NULL);
util_send (" ((NIL NIL NIL \"%s\"))", buffer);
status = header_get_value (header, "Bcc", buffer, sizeof (buffer), NULL);
util_send (" ((NIL NIL NIL \"%s\"))", buffer);
status = header_get_value (header, "In-Reply-To", buffer, sizeof (buffer), NULL);
util_send (" \"%s\"", buffer);
status = header_get_value (header, "Message-ID", buffer, sizeof (buffer), NULL);
util_send (" \"%s\"", buffer);
return 0;
}
static int
fetch_flags (struct imap4d_command *command, char *arg)
{
attribute_t attr = NULL;
message_t msg = NULL;
mailbox_get_message (mbox, command->states, &msg);
message_get_attribute (msg, &attr);
EPILOGUE(command);
util_send (" (");
if (attribute_is_deleted (attr))
util_send (" \\Deleted");
if (attribute_is_read (attr))
util_send (" \\Read");
if (attribute_is_seen (attr))
util_send (" \\Seen");
if (attribute_is_draft (attr))
util_send (" \\Draft");
util_send (" )");
return 0;
}
static int
fetch_internaldate (struct imap4d_command *command, char *arg)
{
char date[512];
envelope_t env = NULL;
message_t msg = NULL;
mailbox_get_message (mbox, command->states, &msg);
message_get_envelope (msg, &env);
date[0] = '\0';
envelope_date (env, date, sizeof (date), NULL);
EPILOGUE(command);
if (date[strlen (date) - 1] == '\n')
date[strlen (date) - 1] = '\0';
util_send (" \"%s\"", date);
return 0;
}
static int
fetch_rfc822_header (struct imap4d_command *command, char *arg)
{
char buffer[64];
struct imap4d_command c_body_p = fetch_command_table[F_BODY_PEEK];
c_body_p.states = command->states;
c_body_p.success = command->success;
strcpy (buffer, "[HEADER]");
fetch_body_peek (&c_body_p, buffer);
return 0;
}
static int
fetch_rfc822_size (struct imap4d_command *command, char *arg)
{
size_t size = 0;
size_t lines = 0;
message_t msg = NULL;
mailbox_get_message (mbox, command->states, &msg);
message_size (msg, &size);
message_lines (msg, &lines);
EPILOGUE(command);
util_send (" %u", size + lines);
return 0;
}
static int
fetch_rfc822_text (struct imap4d_command *command, char *arg)
{
char buffer[64];
struct imap4d_command c_body = fetch_command_table[F_BODY];
c_body.states = command->states;
c_body.success = command->success;
strcpy (buffer, "[TEXT]");
fetch_body (&c_body, buffer);
return 0;
}
static int
fetch_rfc822 (struct imap4d_command *command, char *arg)
{
char buffer[64];
struct imap4d_command c_body = fetch_command_table[F_BODY];
c_body.states = command->states;
c_body.success = command->success;
strcpy (buffer, "[]");
fetch_body (&c_body, buffer);
return 0;
}
/* FIXME: not implemeted. */
static int
fetch_bodystructure (struct imap4d_command *command, char *arg)
{
return 0;
}
static int
fetch_body (struct imap4d_command *command, char *arg)
{
attribute_t attr = NULL;
message_t msg = NULL;
struct imap4d_command c_body_p = fetch_command_table[F_BODY_PEEK];
mailbox_get_message (mbox, command->states, &msg);
message_get_attribute (msg, &attr);
c_body_p.states = command->states;
c_body_p.success = command->success;
fetch_body_peek (&c_body_p, arg);
attribute_set_seen (attr);
return 0;
}
static int
fetch_uid (struct imap4d_command *command, char *arg)
{
size_t uid = 0;
message_t msg = NULL;
mailbox_get_message (mbox, command->states, &msg);
message_get_uid (msg, &uid);
EPILOGUE (command);
util_send (" %d", uid);
return 0;
}
static int
fetch_body_peek (struct imap4d_command *command, char *arg)
{
message_t msg = NULL;
char *partial = strchr (arg, '<');
mailbox_get_message (mbox, command->states, &msg);
EPILOGUE(command);
if (strncasecmp (arg, "[]", 2) == 0)
{
stream_t stream = NULL;
char buffer[512];
size_t size = 0, lines = 0, n = 0;
off_t off = 0;
message_get_stream (msg, &stream);
message_size (msg, &size);
message_size (msg, &lines);
util_send (" BODY[] {%u}\r\n", size + lines);
while (stream_readline (stream, buffer, sizeof (buffer), off, &n) == 0
&& n > 0)
{
/* Nuke the trainline newline. */
if (buffer[n - 1] == '\n')
buffer[n - 1] = '\0';
util_send ("%s\r\n", buffer);
off += n;
}
}
else if (strncasecmp (arg, "[HEADER]", 8) == 0)
{
header_t header = NULL;
stream_t stream = NULL;
char buffer[512];
size_t size = 0, lines = 0, n = 0;
off_t off = 0;
message_get_header (msg, &header);
header_size (header, &size);
header_lines (header, &lines);
util_send (" BODY[HEADER] {%u}\r\n", size + lines);
header_get_stream (msg, &stream);
while (stream_readline (stream, buffer, sizeof (buffer), off, &n) == 0
&& n > 0)
{
/* Nuke the trainline newline. */
if (buffer[n - 1] == '\n')
buffer[n - 1] = '\0';
util_send ("%s\r\n", buffer);
off += n;
}
}
else if (strncasecmp (arg, "[TEXT]", 6) == 0)
{
body_t body = NULL;
stream_t stream = NULL;
char buffer[512];
size_t size = 0, lines = 0, n = 0;
off_t off = 0;
message_get_body (msg, &body);
body_size (body, &size);
body_lines (body, &lines);
util_send (" BODY[TEXT] {%u}\r\n", size + lines);
body_get_stream (msg, &stream);
while (stream_readline (stream, buffer, sizeof (buffer), off, &n) == 0
&& n > 0)
{
/* Nuke the trainline newline. */
if (buffer[n - 1] == '\n')
buffer[n - 1] = '\0';
util_send ("%s\r\n", buffer);
off += n;
}
}
else
util_send (" Not supported");
return 0;
}
......
......@@ -29,6 +29,7 @@
#endif
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
......@@ -121,14 +122,17 @@ int imap4d_uid __P ((struct imap4d_command *, char *));
/* Helper functions. */
int util_out __P ((int rc, const char *f, ...));
int util_send __P ((const char *f, ...));
int util_start __P ((char *tag));
int util_finish __P ((struct imap4d_command *, int rc, const char *f, ...));
int util_finish __P ((struct imap4d_command *, int sc, const char *f, ...));
int util_getstate __P ((void));
int util_do_command __P ((char *prompt));
char *imap4d_readline __P ((int fd));
void util_quit __P ((int));
char *util_getword __P ((char *s, char **save_ptr));
struct imap4d_command *util_getcommand __P ((char *cmd));
int util_msgset __P ((char *s, int **set, int *n, int isuid));
struct imap4d_command *util_getcommand __P ((char *cmd,
struct imap4d_command []));
#ifdef __cplusplus
}
......
......@@ -17,9 +17,7 @@
#include "imap4d.h"
/*
* argv[2] == mailbox
*/
/* select ::= "SELECT" SPACE mailbox */
int
imap4d_select (struct imap4d_command *command, char *arg)
......@@ -58,18 +56,32 @@ imap4d_select0 (struct imap4d_command *command, char *arg, int flags)
if (mailbox_create_default (&mbox, mailbox_name) == 0
&& mailbox_open (mbox, flags) == 0)
{
const char *sflags = "\\Answered \\Flagged \\Deleted \\Seen \\Draft";
int num = 0, recent = 0, uid = 0;
const char *mflags = "\\Answered \\Flagged \\Deleted \\Seen \\Draft";
const char *pflags = "\\Answered \\Flagged \\Deleted \\Seen \\Draft";
unsigned long uidvalidity = 0;
size_t count = 0, recent = 0, unseen = 0, uidnext = 0;
mailbox_messages_count (mbox, &num);
mailbox_recent_count (mbox, &recent);
util_out (RESP_NONE, "%d EXISTS", num);
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_NONE, "FLAGS (%s)", sflags);
util_out (RESP_OK, "[UIDNEXT %d]", num + 1);
/*util_out (RESP_OK, "[UIDVALIDITY (%d)]", uid);*/
/*util_out (RESP_OK, "[PERMANENTFLAGS (%s)]", flags);*/
return util_finish (command, RESP_OK, "Complete");
util_out (RESP_NONE, "FLAGS (%s)", mflags);
util_out (RESP_OK, "[UIDNEXT %d] Predicted next uid", uidnext);
util_out (RESP_OK, "[UIDVALIDITY (%d)] UID valididy status",
uidvalidity);
if (unseen)
util_out (RESP_OK, "[UNSEEN (%d)] %d is first unseen messsage ",
unseen, unseen);
/* FIXME:
- '\*' can be supported if we use the attribute_set userflag()
- Answered is still not set in the mailbox code. */
util_out (RESP_OK, "[PERMANENTFLAGS (%s)]", pflags);
return util_send ("%s OK [%s] %s Complete\r\n", command->tag,
(MU_STREAM_READ == flags) ?
"READ-ONLY" : "READ-WRITE", command->name);
}
return util_finish (command, RESP_NO, "Couldn't open %s", mailbox_name);
}
......
......@@ -17,34 +17,144 @@
#include "imap4d.h"
static const char *
rc2string (int rc)
static int add2set (int **set, int *n, unsigned long val, size_t max);
static const char * sc2string (int rc);
/* FIXME: Some words are:
between double quotes, between parenthesis. */
char *
util_getword (char *s, char **save)
{
switch (rc)
return strtok_r (s, " \r\n", save);
}
/* Return in set an allocated array contain (n) numbers, for imap messsage set
set ::= sequence_num / (sequence_num ":" sequence_num) / (set "," set)
sequence_num ::= nz_number / "*"
;; * is the largest number in use. For message
;; sequence numbers, it is the number of messages
;; in the mailbox. For unique identifiers, it is
;; the unique identifier of the last message in
;; the mailbox.
nz_number ::= digit_nz *digit
FIXME: The algo below is to relaxe, things like <,,,> or <:12> or <20:10>
will not generate an error. */
int
util_msgset (char *s, int **set, int *n, int isuid)
{
unsigned long val = 0;
unsigned long low = 0;
int done = 0;
int status = 0;
size_t max = 0;
status = mailbox_messages_count (mbox, &max);
if (status != 0)
return status;
if (isuid)
{
case RESP_OK:
return "OK ";
message_t msg = NULL;
mailbox_get_message (mbox, max, &msg);
message_get_uid (msg, &max);
}
case RESP_BAD:
return "BAD ";
*n = 0;
*set = NULL;
while (*s)
{
switch (*s)
{
/* isdigit */
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
errno = 0;
val = strtoul (s, &s, 10);
if (val == ULONG_MAX && errno == ERANGE)
{
if (*set)
free (*set);
*n = 0;
return EINVAL;
}
if (low)
{
for (;low && low <= val; low++)
{
status = add2set (set, n, low, max);
if (status != 0)
return status;
}
low = 0;
}
else
{
status = add2set(set, n, val, max);
if (status != 0)
return status;
}
break;
}
case RESP_NO:
return "NO ";
case ':':
low = val + 1;
s++;
break;
case RESP_BYE:
return "BYE ";
case '*':
{
if (status != 0)
{
if (*set)
free (*set);
*n = 0;
return status;
}
return "";
val = max;
s++;
break;
}
case ',':
s++;
break;
default:
done = 1;
if (*set)
free (*set);
*n = 0;
return EINVAL;
} /* switch */
if (done)
break;
} /* while */
if (low)
{
for (;low && low <= val; low++)
{
status = add2set (set, n, low, max);
if (status != 0)
return status;
}
}
return 0;
}
/* FIXME: Some words are:
between double quotes, consider like one word.
between parenthesis, consider line one word. */
char *
util_getword (char *s, char **save)
int
util_send (const char *format, ...)
{
static char *sp;
return strtok_r (s, " \r\n", ((save) ? save : &sp));
int status;
va_list ap;
va_start (ap, format);
status = vfprintf (ofile, format, ap);
va_end (ap);
return status;
}
int
......@@ -57,7 +167,7 @@ util_out (int rc, const char *format, ...)
vasprintf (&buf, format, ap);
va_end (ap);
fprintf (ofile, "* %s%s\r\n", rc2string (rc), buf);
fprintf (ofile, "* %s%s\r\n", sc2string (rc), buf);
free (buf);
return 0;
}
......@@ -73,7 +183,7 @@ util_finish (struct imap4d_command *command, int rc, const char *format, ...)
vasprintf (&buf, format, ap);
va_end(ap);
resp = rc2string (rc);
resp = sc2string (rc);
fprintf (ofile, "%s %s%s %s\r\n", command->tag, resp, command->name, buf);
free (buf);
return 0;
......@@ -86,6 +196,7 @@ imap4d_readline (int fd)
struct timeval tv;
char buf[512], *ret = NULL;
int nread;
int total = 0;
int available;
FD_ZERO (&rfds);
......@@ -99,35 +210,35 @@ imap4d_readline (int fd)
{
available = select (fd + 1, &rfds, NULL, NULL, &tv);
if (!available)
util_quit (1); /* FIXME: Timeout. */
util_quit (1); /* FIXME: Timeout, send a "* BYE". */
}
nread = read (fd, buf, sizeof (buf) - 1);
if (nread < 1)
util_quit (1); /* FIXME: dead socket */
util_quit (1); /* FIXME: dead socket, need to do something? */
buf[nread] = '\0';
ret = realloc (ret, (total + nread + 1) * sizeof (char));
if (ret == NULL)
{
ret = malloc ((nread + 1) * sizeof (char));
strcpy (ret, buf);
}
else
{
ret = realloc (ret, (strlen (ret) + nread + 1) * sizeof (char));
strcat (ret, buf);
}
util_quit (1); /* FIXME: ENOMEM, send a "* BYE" to the client. */
memcpy (ret + total, buf, nread + 1);
total += nread;
/* FIXME: handle literal strings here. */
}
while (strchr (buf, '\n') == NULL);
while (memchr (buf, '\n', nread) == NULL);
for (nread = total; nread > 0; nread--)
if (ret[nread] == '\r' || ret[nread] == '\n')
ret[nread] = '\0';
return ret;
}
int
util_do_command (char *prompt)
{
char *sp = NULL, *tag, *cmd, *arg;
char *sp = NULL, *tag, *cmd;
struct imap4d_command *command;
static struct imap4d_command nullcommand;
......@@ -148,7 +259,7 @@ util_do_command (char *prompt)
util_start (tag);
command = util_getcommand (cmd);
command = util_getcommand (cmd, imap4d_command_table);
if (command == NULL)
{
nullcommand.name = "";
......@@ -183,15 +294,54 @@ util_getstate (void)
}
struct imap4d_command *
util_getcommand (char *cmd)
util_getcommand (char *cmd, struct imap4d_command command_table[])
{
size_t i, len = strlen (cmd);
for (i = 0; imap4d_command_table[i].name != 0; i++)
for (i = 0; command_table[i].name != 0; i++)
{
if (strlen (imap4d_command_table[i].name) == len &&
!strcasecmp (imap4d_command_table[i].name, cmd))
return &imap4d_command_table[i];
if (strlen (command_table[i].name) == len &&
!strcasecmp (command_table[i].name, cmd))
return &command_table[i];
}
return NULL;
}
/* Status Code to String. */
static const char *
sc2string (int rc)
{
switch (rc)
{
case RESP_OK:
return "OK ";
case RESP_BAD:
return "BAD ";
case RESP_NO:
return "NO ";
case RESP_BYE:
return "BYE ";
}
return "";
}
static int
add2set (int **set, int *n, unsigned long val, size_t max)
{
int *tmp;
if (val == 0 || val > max
|| (tmp = realloc (*set, (*n + 1) * sizeof (**set))) == NULL)
{
if (*set)
free (*set);
*n = 0;
return ENOMEM;
}
*set = tmp;
(*set)[*n] = val;
(*n)++;
return 0;
}
......