Commit 47546d96 47546d96e3c7659f831f96626dcb13662803f3ab by Alain Magloire

* imap4d/select.c(imap4_select_status): According to RFC2060

	There is no parenthesis when we send the UIDVALIDITY/UNSEEN.

	* mailbox/header.c (header_free_cache): New function.
	(fill_blurb): Call header_free_cache to release memory.
	(header_destroy): Move the code to header_free_cache().
	(header_set_fvalue): Accept a NULL value, it will permanent
	failure i.e. the header does not exist.
	(header_get_value): Check the cache provided by the header object
	then the cache from _get_fvalue() and fall back to downloading
	the entire header.
	* mailbox/mbx_imap.c (imap_hader_get_fvalue): Implement a cache
	in IMAP for the most use headers like "FROM, SUBJECT ....".
	* mailbox/include/imap0.h: New field in _m_imap to hold the
	header cache.
	* mailbox/mapfile_stream.c: signed and unsigned cast.
1 parent dcd0814e
2001-10-04 Alain Magloire
* imap4d/select.c(imap4_select_status): According to RFC2060
There is no parenthesis when we send the UIDVALIDITY/UNSEEN.
* mailbox/header.c (header_free_cache): New function.
(fill_blurb): Call header_free_cache to release memory.
(header_destroy): Move the code to header_free_cache().
(header_set_fvalue): Accept a NULL value, it will permanent
failure i.e. the header does not exist.
(header_get_value): Check the cache provided by the header object
then the cache from _get_fvalue() and fall back to downloading
the entire header.
* mailbox/mbx_imap.c (imap_hader_get_fvalue): Implement a cache
in IMAP for the most use headers like "FROM, SUBJECT ....".
* mailbox/include/imap0.h: New field in _m_imap to hold the
header cache.
* mailbox/mapfile_stream.c: signed and unsigned cast.
2001-10-03 Alain Magloire
* mailbox/mbx_imap.c (imap_attr_set_flags): Do not send
......
......@@ -111,11 +111,11 @@ imap4d_select_status()
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",
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_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()
......
......@@ -40,6 +40,7 @@ static int header_readline __P ((stream_t, char *, size_t, off_t, size_t *));
static int header_write __P ((stream_t, const char *, size_t, off_t,
size_t *));
static int fill_blurb __P ((header_t));
static void header_free_cache __P ((header_t));
int
header_create (header_t *ph, const char *blurb, size_t len, void *owner)
......@@ -59,6 +60,26 @@ header_create (header_t *ph, const char *blurb, size_t len, void *owner)
return status;
}
static void
header_free_cache (header_t header)
{
/* Clean up our fast header cache. */
if (header->fhdr)
{
size_t i;
for (i = 0; i < header->fhdr_count; i++)
{
if (header->fhdr[i].fn)
free (header->fhdr[i].fn);
if (header->fhdr[i].fv)
free (header->fhdr[i].fv);
}
free (header->fhdr);
header->fhdr = NULL;
header->fhdr_count = 0;
}
}
void
header_destroy (header_t *ph, void *owner)
{
......@@ -77,19 +98,7 @@ header_destroy (header_t *ph, void *owner)
if (header->blurb)
free (header->blurb);
/* Clean up the fast header cache. */
if (header->fhdr)
{
size_t i;
for (i = 0; i < header->fhdr_count; i++)
{
if (header->fhdr[i].fn)
free (header->fhdr[i].fn);
if (header->fhdr[i].fv)
free (header->fhdr[i].fv);
}
free (header->fhdr);
}
header_free_cache (header);
if (header->property)
property_destroy (&(header->property), header);
......@@ -343,6 +352,10 @@ header_set_value (header_t header, const char *fn, const char *fv, int replace)
return 0;
}
/* We try to cache the headers here to reduce networking access
especially for IMAP. When the buffer is NULL it means that
the field does not exist on the server and we should not
attempt to contact the server again for this field. */
static int
header_set_fvalue (header_t header, const char *name, char *buffer)
{
......@@ -359,6 +372,8 @@ header_set_fvalue (header_t header, const char *name, char *buffer)
thdr[header->fhdr_count].fn = field;
thdr[header->fhdr_count].fn_end = field + len;
if (buffer)
{
len = strlen (buffer);
field = malloc (len + 1);
if (field == NULL)
......@@ -367,6 +382,12 @@ header_set_fvalue (header_t header, const char *name, char *buffer)
field[len] = '\0';
thdr[header->fhdr_count].fv = field;
thdr[header->fhdr_count].fv_end = field + len;
}
else
{
thdr[header->fhdr_count].fv = NULL;
thdr[header->fhdr_count].fv_end = NULL;
}
header->fhdr_count++;
header->fhdr = thdr;
return 0;
......@@ -374,6 +395,9 @@ header_set_fvalue (header_t header, const char *name, char *buffer)
return ENOMEM;
}
/* For the cache header if the field exist but with no corresponding
value, it is a permanent failure i.e. the field does not exist
in the header return EINVAL to notify header_get_value(). */
static int
header_get_fvalue (header_t header, const char *name, char *buffer,
size_t buflen, size_t *pn)
......@@ -382,9 +406,6 @@ header_get_fvalue (header_t header, const char *name, char *buffer,
size_t name_len;
int err = ENOENT;
if (header->_get_fvalue)
return header->_get_fvalue (header, name, buffer, buflen, pn);
for (i = 0, name_len = strlen (name); i < header->fhdr_count; i++)
{
fn_len = header->fhdr[i].fn_end - header->fhdr[i].fn;
......@@ -392,6 +413,11 @@ header_get_fvalue (header_t header, const char *name, char *buffer,
&& strcasecmp (header->fhdr[i].fn, name) == 0)
{
fv_len = header->fhdr[i].fv_end - header->fhdr[i].fv;
/* Permanent failure. */
if (fv_len == 0)
return EINVAL;
if (buffer && buflen > 0)
{
buflen--;
......@@ -422,8 +448,21 @@ header_get_value (header_t header, const char *name, char *buffer,
if (header == NULL || name == NULL)
return EINVAL;
/* First Try the Fast header for hits. */
/* First scan our cache headers for hits. */
err = header_get_fvalue (header, name, buffer, buflen, pn);
switch (err)
{
case EINVAL: /* Permanent failure. */
return ENOENT;
case 0:
return 0;
case ENOMEM:
return err;
}
/* Try the provided cache. */
if (header->_get_fvalue)
err = header->_get_fvalue (header, name, buffer, buflen, pn);
if (err == 0)
return 0;
......@@ -448,6 +487,11 @@ header_get_value (header_t header, const char *name, char *buffer,
if (pn)
*pn = buflen;
}
else
{
/* Cache permanent failure also. */
header_set_fvalue (header, name, NULL);
}
return err;
}
......@@ -850,6 +894,7 @@ fill_blurb (header_t header)
/* The entire header is now ours(part of header_t), clear all the
overloading. */
header_free_cache (header);
header->_get_fvalue = NULL;
header->_get_value = NULL;
header->_set_value = NULL;
......@@ -943,7 +988,7 @@ header_read (stream_t is, char *buf, size_t buflen, off_t off, size_t *pnread)
len = header->blurb_len - off;
if (len > 0)
{
len = (buflen < (size_t)len) ? buflen : len;
len = (buflen < (size_t)len) ? buflen : (size_t)len;
memcpy (buf, header->blurb + off, len);
}
else
......@@ -986,7 +1031,7 @@ header_readline (stream_t is, char *buf, size_t buflen, off_t off, size_t *pn)
char *nl = memchr (header->blurb + off, '\n', len);
if (nl)
len = nl - (header->blurb + off) + 1;
len = (buflen < (size_t)len) ? buflen : len;
len = (buflen < (size_t)len) ? buflen : (size_t)len;
memcpy (buf, header->blurb + off, len);
}
else
......
......@@ -187,6 +187,8 @@ struct _msg_imap
int flags;
size_t uid;
header_t fheader;
size_t message_size;
size_t message_lines;
size_t body_size;
......
......@@ -97,7 +97,7 @@ _mapfile_readline (stream_t stream, char *optr, size_t osize,
/* Save space for the null byte. */
osize--;
nl = memchr (mfs->ptr + offset, '\n', mfs->size - offset);
n = (nl) ? nl - (mfs->ptr + offset) + 1 : mfs->size - offset;
n = (nl) ? (size_t)(nl - (mfs->ptr + offset) + 1) : mfs->size - offset;
n = (n > osize) ? osize : n;
memcpy (optr, mfs->ptr + offset, n);
optr[n] = '\0';
......
......@@ -77,6 +77,7 @@ static int imap_attr_unset_flags __P ((attribute_t, int));
/* Header. */
static int imap_header_read __P ((header_t, char*, size_t, off_t, size_t *));
static int imap_header_get_value __P ((header_t, const char*, char *, size_t, size_t *));
static int imap_header_get_fvalue __P ((header_t, const char*, char *, size_t, size_t *));
/* Body. */
static int imap_body_read __P ((stream_t, char *, size_t, off_t, size_t *));
......@@ -163,6 +164,8 @@ free_subparts (msg_imap_t msg_imap)
message_destroy (&(msg_imap->message), msg_imap);
if (msg_imap->parts)
free (msg_imap->parts);
if (msg_imap->fheader)
header_destroy (&msg_imap->fheader, NULL);
free(msg_imap);
}
......@@ -248,6 +251,9 @@ mailbox_imap_close (mailbox_t mailbox)
free (m_imap->imessages);
m_imap->imessages = NULL;
m_imap->imessages_count = 0;
m_imap->messages_count = 0;
m_imap->recent = 0;
m_imap->unseen = 0;
monitor_unlock (mailbox->monitor);
}
......@@ -293,7 +299,7 @@ imap_get_message (mailbox_t mailbox, size_t msgno, message_t *pmsg)
monitor_unlock (mailbox->monitor);
/* Allocate a concrete imap message. */
msg_imap = calloc (1, sizeof (*msg_imap));
msg_imap = calloc (1, sizeof *msg_imap);
if (msg_imap == NULL)
return ENOMEM;
/* Back pointer. */
......@@ -365,6 +371,7 @@ imap_get_message0 (msg_imap_t msg_imap, message_t *pmsg)
}
header_set_fill (header, imap_header_read, msg);
header_set_get_value (header, imap_header_get_value, msg);
header_set_get_fvalue (header, imap_header_get_fvalue, msg);
message_set_header (msg, header, msg_imap);
}
......@@ -1554,6 +1561,54 @@ imap_header_get_value (header_t header, const char *field, char * buffer,
}
static int
imap_header_get_fvalue (header_t header, const char *field, char * buffer,
size_t buflen, size_t *plen)
{
message_t msg = header_get_owner (header);
msg_imap_t msg_imap = message_get_owner (msg);
m_imap_t m_imap = msg_imap->m_imap;
f_imap_t f_imap = m_imap->f_imap;
int status;
size_t len = 0;
char *value;
/* Do we all ready have the headers. */
if (msg_imap->fheader)
return header_get_value (msg_imap->fheader, field, buffer, buflen, plen);
/* We are caching the must use headers. */
if (f_imap->state == IMAP_NO_STATE)
{
/* Select first. */
status = imap_messages_count (m_imap->mailbox, NULL);
if (status != 0)
return status;
#define CACHE_HEADERS "Bcc Cc Content-Language Content-Transfer-Encoding Content-Type Date From In-Reply-To Message-ID Reference Reply-To Sender Subject To X-UIDL"
status = imap_writeline (f_imap,
"g%d FETCH %d BODY.PEEK[HEADER.FIELDS (%s)]\r\n",
f_imap->seq++, msg_imap->num, CACHE_HEADERS);
CHECK_ERROR (f_imap, status);
MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer);
f_imap->state = IMAP_FETCH;
}
/* Should be enough for our needs. */
len = 2048;
value = calloc (len, sizeof *value);
status = message_operation (f_imap, msg_imap, value, len, &len);
if (status == 0)
{
status = header_create (&msg_imap->fheader, value, len, NULL);
if (status == 0)
status = header_get_value (msg_imap->fheader, field, buffer,
buflen, plen);
}
free (value);
return status;
}
static int
imap_header_read (header_t header, char *buffer, size_t buflen, off_t offset,
size_t *plen)
{
......
......@@ -94,7 +94,7 @@ const char *fhdr_table[] =
#define H_MESSAGE_ID 8
"Message-ID",
#define H_REFERENCE 9
"Reply-To",
"Reference",
#define H_REPLY_TO 10
"Reply-To",
#define H_SENDER 11
......@@ -1343,7 +1343,7 @@ mbox_readstream (mbox_message_t mum, char *buffer, size_t buflen,
if (ln > 0)
{
/* Position the file pointer and the buffer. */
nread = ((size_t)ln < buflen) ? ln : buflen;
nread = ((size_t)ln < buflen) ? (size_t)ln : buflen;
if (isreadline)
status = stream_readline (mum->mud->mailbox->stream, buffer, buflen,
start + off, &nread);
......
......@@ -70,7 +70,7 @@ _memory_readline (stream_t stream, char *optr, size_t osize,
/* Save space for the null byte. */
osize--;
nl = memchr (mfs->ptr + offset, '\n', mfs->size - offset);
n = (nl) ? nl - (mfs->ptr + offset) + 1 : mfs->size - offset;
n = (nl) ? (size_t)(nl - (mfs->ptr + offset) + 1) : mfs->size - offset;
n = (n > osize) ? osize : n;
memcpy (optr, mfs->ptr + offset, n);
optr[n] = '\0';
......