Commit e9b90de7 e9b90de7b7807f0828243e04785b03d9de0055e3 by Sergey Poznyakoff

Provide a function for quick retrieval of message lines.

* include/mailutils/message.h (mu_message_quick_lines): New proto.
(mu_message_set_lines): Change signature of the 2nd argument.
* include/mailutils/sys/message.h (_mu_message) <lines>: Change
signature.
* libmailutils/diag/errors (MU_ERR_INFO_UNAVAILABLE): New error code.
* libmailutils/mailbox/message.c (mu_message_set_lines): Change
signature of the 2nd argument.
(mu_message_lines): Update call to the _lines method.
(mu_message_quick_lines): New function.
* libproto/pop/mbox.c (_POP3_MSG_LINES): New flag.
(_pop3_message)<message_lines>: New member.
(pop_create_pop3_message): New function.
(pop_scan): Use LIST to obtain scan listing.
(pop_message_lines): New function.
(pop_create_message): Set _lines method.
(pop_get_message): Use pop_create_pop3_message.

* mail/from.c (hdr_quick_lines): New function.
(compile_headline): New escape %L

* pop3d/capa.c (pop3d_capa): Show XLINES capability if
configured.
* pop3d/list.c (pop3d_list): Optionally output the number of
lines in the message.
* pop3d/pop3d.c (pop3d_xlines): New global.
(pop3d_cfg_param): New statement "scan-lines".
* pop3d/pop3d.h (pop3d_xlines): New extern.
1 parent b6a16292
......@@ -97,8 +97,9 @@ extern int mu_message_set_size (mu_message_t,
void *owner);
extern int mu_message_lines (mu_message_t, size_t *);
extern int mu_message_quick_lines (mu_message_t, size_t *);
extern int mu_message_set_lines (mu_message_t,
int (*_lines) (mu_message_t, size_t *),
int (*_lines) (mu_message_t, size_t *, int),
void *owner);
extern int mu_message_get_num_parts (mu_message_t, size_t *nparts);
......
......@@ -58,7 +58,7 @@ struct _mu_message
int (*_get_num_parts) (mu_message_t, size_t *);
int (*_get_part) (mu_message_t, size_t, mu_message_t *);
int (*_is_multipart) (mu_message_t, int *);
int (*_lines) (mu_message_t, size_t *);
int (*_lines) (mu_message_t, size_t *, int);
int (*_size) (mu_message_t, size_t *);
};
......
......@@ -95,3 +95,5 @@ MU_ERR_AUTH_NO_CRED _("No credentials supplied")
MU_ERR_URL_MISS_PARTS _("URL missing required parts")
MU_ERR_URL_EXTRA_PARTS _("URL has parts not allowed by its scheme")
MU_ERR_INFO_UNAVAILABLE _("Information is not yet available")
......
......@@ -882,7 +882,7 @@ mu_message_set_get_stream (mu_message_t msg,
int
mu_message_set_lines (mu_message_t msg, int (*_lines)
(mu_message_t, size_t *), void *owner)
(mu_message_t, size_t *, int), void *owner)
{
if (msg == NULL)
return EINVAL;
......@@ -902,7 +902,7 @@ mu_message_lines (mu_message_t msg, size_t *plines)
return EINVAL;
/* Overload. */
if (msg->_lines)
return msg->_lines (msg, plines);
return msg->_lines (msg, plines, 0);
if (plines)
{
hlines = blines = 0;
......@@ -913,6 +913,36 @@ mu_message_lines (mu_message_t msg, size_t *plines)
return ret;
}
/* Return the number of lines in the message, without going into
excess trouble for calculating it. If obtaining the result
means downloading the entire message (as is the case for POP3,
for example), return MU_ERR_INFO_UNAVAILABLE. */
int
mu_message_quick_lines (mu_message_t msg, size_t *plines)
{
size_t hlines, blines;
int rc;
if (msg == NULL)
return EINVAL;
/* Overload. */
if (msg->_lines)
{
int rc = msg->_lines (msg, plines, 1);
if (rc != ENOSYS)
return rc;
}
if (plines)
{
hlines = blines = 0;
if ((rc = mu_header_lines (msg->header, &hlines)) == 0)
rc = mu_body_lines (msg->body, &blines);
if (rc == 0)
*plines = hlines + blines;
}
return rc;
}
int
mu_message_set_size (mu_message_t msg, int (*_size)
(mu_message_t, size_t *), void *owner)
......
......@@ -60,6 +60,7 @@
#define _POP3_MSG_SIZE 0x02 /* Message size obtained */
#define _POP3_MSG_SCANNED 0x04 /* Message has been scanned */
#define _POP3_MSG_ATTRSET 0x08 /* Attributes has been set */
#define _POP3_MSG_LINES 0x10 /* Number of lines was obtained */
struct _pop3_message
{
......@@ -71,6 +72,7 @@ struct _pop3_message
size_t body_lines; /* Number of lines in the body */
int attr_flags; /* Message attributes */
size_t message_size; /* Message size */
size_t message_lines; /* Number of lines in the message */
size_t num; /* Message number */
char *uidl; /* Cached uidl string. */
mu_message_t message; /* Pointer to the message structure */
......@@ -95,6 +97,9 @@ struct _pop3_mailbox
mu_secret_t secret;
};
static int pop_create_pop3_message (struct _pop3_mailbox *mpd, size_t msgno,
struct _pop3_message **mptr);
/* ------------------------------------------------------------------------- */
/* Basic operations */
......@@ -277,27 +282,82 @@ pop_scan (mu_mailbox_t mbox, size_t msgno, size_t *pcount)
int status;
size_t i;
size_t count = 0;
struct _pop3_mailbox *mpd = mbox->data;
int flags;
mu_iterator_t itr;
status = pop_messages_count (mbox, &count);
if (status != 0)
return status;
if (pcount)
*pcount = count;
if (mbox->observable == NULL)
return 0;
for (i = msgno; i <= count; i++)
flags = _POP3_MSG_SIZE;
if (!mu_pop3_capa_test (mpd->pop3, "XLINES", NULL))
flags |= _POP3_MSG_LINES;
status = mu_pop3_list_all (mpd->pop3, &itr);
if (status)
return status;
for (i = 0, mu_iterator_first (itr);
i <= count && !mu_iterator_is_done (itr);
i++, mu_iterator_next (itr))
{
const char *str;
char *p;
size_t num;
mu_iterator_current (itr, (void**) &str);
num = strtoul (str, &p, 10);
if (*p != ' ')
{
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
("invalid reply to LIST command: %s", str));
status = MU_ERR_BADREPLY;
break;
}
if (num >= msgno)
{
size_t size, lines;
struct _pop3_message *mpm;
size = strtoul (p + 1, &p, 10);
if (flags & _POP3_MSG_LINES)
{
if (*p != ' ')
{
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
("invalid reply to LIST command: %s", str));
status = MU_ERR_BADREPLY;
break;
}
lines = strtoul (p + 1, &p, 10);
}
status = pop_create_pop3_message (mpd, num, &mpm);
if (status)
break;
mpm->message_size = size;
if (flags & _POP3_MSG_LINES)
mpm->message_lines = lines;
mpm->flags |= flags;
if (mbox->observable)
{
size_t tmp = i;
if (mu_observable_notify (mbox->observable, MU_EVT_MESSAGE_ADD,
&tmp) != 0)
&num) != 0)
break;
if (((i + 1) % 10) == 0)
{
mu_observable_notify (mbox->observable, MU_EVT_MAILBOX_PROGRESS,
mu_observable_notify (mbox->observable,
MU_EVT_MAILBOX_PROGRESS,
NULL);
}
}
return 0;
}
mu_iterator_destroy (&itr);
return status;
}
/* There's no way to retrieve this info via POP3 */
......@@ -475,6 +535,29 @@ pop_message_size (mu_message_t msg, size_t *psize)
}
static int
pop_message_lines (mu_message_t msg, size_t *plines, int quick)
{
int rc;
struct _pop3_message *mpm = mu_message_get_owner (msg);
if (!(mpm->flags & _POP3_MSG_LINES))
{
if (quick && !(mpm->flags & _POP3_MSG_CACHED))
return MU_ERR_INFO_UNAVAILABLE;
if (!pop_is_updated (mpm->mpd->mbox))
pop_scan (mpm->mpd->mbox, 1, NULL);
rc = pop_scan_message (mpm);
if (rc)
return rc;
mpm->message_lines = mpm->header_lines + mpm->body_lines + 1;
mpm->flags |= _POP3_MSG_LINES;
}
*plines = mpm->message_lines;
return 0;
}
static int
pop_create_message (struct _pop3_message *mpm, struct _pop3_mailbox *mpd)
{
int status;
......@@ -486,10 +569,47 @@ pop_create_message (struct _pop3_message *mpm, struct _pop3_mailbox *mpd)
mu_message_set_get_stream (msg, pop_message_get_stream, mpm);
mu_message_set_size (msg, pop_message_size, mpm);
mu_message_set_lines (msg, pop_message_lines, mpm);
mpm->message = msg;
return 0;
}
static int
pop_create_pop3_message (struct _pop3_mailbox *mpd, size_t msgno,
struct _pop3_message **mptr)
{
int status;
struct _pop3_message *mpm;
if (msgno > mpd->msg_count)
return EINVAL;
if (!mpd->msg)
{
mpd->msg = calloc (mpd->msg_count, sizeof (mpd->msg[0]));
if (!mpd->msg)
return ENOMEM;
}
if (mpd->msg[msgno - 1])
{
*mptr = mpd->msg[msgno - 1];
return 0;
}
mpm = calloc (1, sizeof (*mpm));
if (mpm == NULL)
return ENOMEM;
/* Back pointer. */
mpm->mpd = mpd;
mpm->num = msgno;
mpd->msg[msgno - 1] = mpm;
*mptr = mpm;
return status;
}
/* ------------------------------------------------------------------------- */
/* Header */
......@@ -798,35 +918,18 @@ pop_get_message (mu_mailbox_t mbox, size_t msgno, mu_message_t *pmsg)
if (!pop_is_updated (mbox))
pop_scan (mbox, 1, NULL);
if (msgno > mpd->msg_count)
return EINVAL;
if (!mpd->msg)
{
mpd->msg = calloc (mpd->msg_count, sizeof (mpd->msg[0]));
if (!mpd->msg)
return ENOMEM;
}
if (mpd->msg[msgno - 1])
status = pop_create_pop3_message (mpd, msgno, &mpm);
if (status)
return status;
if (mpm->message)
{
*pmsg = mpd->msg[msgno - 1]->message;
*pmsg = mpm->message;
return 0;
}
mpm = calloc (1, sizeof (*mpm));
if (mpm == NULL)
return ENOMEM;
/* Back pointer. */
mpm->mpd = mpd;
mpm->num = msgno;
status = pop_create_message (mpm, mpd);
if (status)
{
free (mpm);
return status;
}
do
{
......@@ -852,7 +955,6 @@ pop_get_message (mu_mailbox_t mbox, size_t msgno, mu_message_t *pmsg)
mu_message_set_uid (mpm->message, pop_uid, mpm);
mpd->msg[msgno - 1] = mpm;
mu_message_set_mailbox (mpm->message, mbox, mpm);
*pmsg = mpm->message;
return 0;
......
......@@ -294,11 +294,29 @@ hdr_lines (struct header_call_args *args, void *data)
{
size_t m_lines;
char buf[UINTMAX_STRSIZE_BOUND];
mu_message_lines (args->msg, &m_lines);
return header_buf_string (args, umaxtostr (m_lines, buf));
}
/* %L */
static char *
hdr_quick_lines (struct header_call_args *args, void *data)
{
size_t m_lines;
char buf[UINTMAX_STRSIZE_BOUND];
int rc;
const char *p;
rc = mu_message_quick_lines (args->msg, &m_lines);
if (rc == 0)
p = umaxtostr (m_lines, buf);
else
p = "NA";
return header_buf_string (args, p);
}
/* %m */
static char *
hdr_number (struct header_call_args *args, void *data)
......@@ -467,6 +485,11 @@ compile_headline (const char *str)
seg = new_header_segment (ALIGN_NUMBER, width, NULL, hdr_lines);
break;
case 'L': /* Same, but in quick mode */
seg = new_header_segment (ALIGN_NUMBER, width, NULL,
hdr_quick_lines);
break;
case 'm': /* Message number */
seg = new_header_segment (ALIGN_NUMBER, width, NULL, hdr_number);
break;
......
......@@ -38,6 +38,8 @@ pop3d_capa (char *arg)
pop3d_outf ("UIDL\n");
pop3d_outf ("RESP-CODES\n");
pop3d_outf ("PIPELINING\n");
if (pop3d_xlines)
pop3d_outf ("XLINES\n");
#ifdef WITH_TLS
if (tls_available && tls_done == 0)
......
......@@ -17,8 +17,27 @@
#include "pop3d.h"
/* Displays the size of message number arg or all messages (if no arg) */
/* From RFC 1939:
In order to simplify parsing, all POP3 servers are
required to use a certain format for scan listings. A
scan listing consists of the message-number of the
message, followed by a single space and the exact size of
the message in octets. Methods for calculating the exact
size of the message are described in the "Message Format"
section below. This memo makes no requirement on what
follows the message size in the scan listing. Minimal
implementations should just end that line of the response
with a CRLF pair. More advanced implementations may
include other information, as parsed from the message.
<end of quote>
GNU pop3d uses this allowance and includes in the scan
listing the number of lines in message. This optional
feature is enabled by setting "scan-lines yes" in the
configuration file. When on, it is indicated by the
XLINES capability.
*/
int
pop3d_list (char *arg)
{
......@@ -47,9 +66,12 @@ pop3d_list (char *arg)
{
mu_message_size (msg, &size);
mu_message_lines (msg, &lines);
pop3d_outf ("%s %s\n",
pop3d_outf ("%s %s",
mu_umaxtostr (0, mesgno),
mu_umaxtostr (1, size + lines));
if (pop3d_xlines)
pop3d_outf (" %s", mu_umaxtostr (2, lines));
pop3d_outf ("\n");
}
}
pop3d_outf (".\n");
......@@ -64,10 +86,14 @@ pop3d_list (char *arg)
return ERR_MESG_DELE;
mu_message_size (msg, &size);
mu_message_lines (msg, &lines);
pop3d_outf ("+OK %s %s\n",
pop3d_outf ("+OK %s %s",
mu_umaxtostr (0, mesgno),
mu_umaxtostr (1, size + lines));
if (pop3d_xlines)
pop3d_outf (" %s", mu_umaxtostr (2, lines));
pop3d_outf ("\n");
}
return OK;
}
......
......@@ -30,6 +30,7 @@ unsigned int idle_timeout;
int pop3d_transcript;
int debug_mode;
int tls_required;
int pop3d_xlines;
#ifdef WITH_TLS
int tls_available;
......@@ -95,6 +96,8 @@ static struct mu_cfg_param pop3d_cfg_param[] = {
N_("days") },
{ "delete-expired", mu_cfg_bool, &expire_on_exit, 0, NULL,
N_("Delete expired messages upon closing the mailbox.") },
{ "scan-lines", mu_cfg_bool, &pop3d_xlines, 0, NULL,
N_("Output the number of lines in the message in its scan listing.") },
#ifdef WITH_TLS
{ "tls-required", mu_cfg_bool, &tls_required, 0, NULL,
N_("Always require STLS before entering authentication phase.") },
......
......@@ -196,6 +196,7 @@ extern struct mu_auth_data *auth_data;
extern unsigned int idle_timeout;
extern int pop3d_transcript;
extern size_t pop3d_output_bufsize;
extern int pop3d_xlines;
extern pop3d_command_handler_t pop3d_find_command (const char *name);
......
......@@ -59,3 +59,4 @@ pop3d_stat (char *arg)
return OK;
}
......