Commit b848706b b848706b89934be5a4419ab2ec382157d2f30aba by Sergey Poznyakoff

imap client: initial implementation of fetch.

* include/mailutils/imap.h (mu_imap_fetch): New proto.
(MU_IMAP_CB_FETCH): New callback code.
(MU_IMAP_FETCH_BODY,MU_IMAP_FETCH_BODYSTRUCTURE)
(MU_IMAP_FETCH_ENVELOPE,MU_IMAP_FETCH_FLAGS)
(MU_IMAP_FETCH_INTERNALDATE,MU_IMAP_FETCH_RFC822_SIZE)
(MU_IMAP_FETCH_UID): New constants.
(mu_imap_fetch_body,mu_imap_fetch_bodystructure)
(mu_imap_fetch_envelope,mu_imap_fetch_flags)
(mu_imap_fetch_internaldate,mu_imap_fetch_rfc822_size)
(mu_imap_fetch_uid,mu_imap_fetch_response): New structures.
* include/mailutils/sys/imap.h (MU_IMAP_SET_XSCRIPT_MASK)
(MU_IMAP_CLR_XSCRIPT_MASK): New macros.
(mu_imap_client_state)<MU_IMAP_FETCH_RX>: New state.
(_mu_imap_list_nth_element_is_string): New proto.
* libproto/imap/fetch.c: New file.
* libproto/imap/Makefile.am (libmu_imap_la_SOURCES): Add fetch.c
* libproto/imap/carrier.c (mu_imap_set_carrier): Update call to
mu_imapio_create.
* libproto/imap/resplist.c (_mu_imap_list_nth_element_is_string): New
function.
* libproto/imap/resproc.c (_process_unsolicited_response): Handle FETCH
response.
* libproto/imap/trace.c (mu_imap_trace_mask): Call mu_imapio_trace_payload
if needed.
* mu/imap.c (imap_bad_callback): New function.
(com_disconnect): Register imap_bad_callback.
(com_fetch): New function.
(imap_comtab): Add the "fetch" command.
* mu/mu.h (mutool_command) <flags>: New member.
* mu/pop.c (pop_comtab): Update initializers.
* mu/shell.c (default_comtab): Update initializers.
(execute_line): Rewrite using incremental wordsplit.  This allows for
optionally coalescing arguments with indices >= argmax into one argument.
This is useful for such commands as "prompt" and "fetch".
1 parent 29021e95
......@@ -560,7 +560,6 @@ fetch_bodystructure0 (mu_message_t message, int extension)
mu_message_get_header (message, &header);
/* The subtype. */
if (mu_header_aget_value (header, MU_HEADER_CONTENT_TYPE, &buffer) == 0)
{
......
......@@ -21,6 +21,7 @@
#include <mailutils/iterator.h>
#include <mailutils/debug.h>
#include <mailutils/stream.h>
#include <mailutils/util.h>
#include <mailutils/kwd.h>
#ifdef __cplusplus
......@@ -58,6 +59,8 @@ int mu_imap_id (mu_imap_t imap, char **idenv, mu_assoc_t *passoc);
int mu_imap_noop (mu_imap_t imap);
int mu_imap_fetch (mu_imap_t imap, const char *msgset, const char *items);
int mu_imap_set_carrier (mu_imap_t imap, mu_stream_t carrier);
int mu_imap_get_carrier (mu_imap_t imap, mu_stream_t *pcarrier);
......@@ -104,7 +107,7 @@ extern struct mu_kwd _mu_imap_status_name_table[];
/* The following five callbacks correspond to members of struct
mu_imap_stat and take a pointer to struct mu_imap_stat as their
only argument. */
PDAT argument. SDAT is always 0. */
#define MU_IMAP_CB_PERMANENT_FLAGS 0
#define MU_IMAP_CB_MESSAGE_COUNT 1
#define MU_IMAP_CB_RECENT_COUNT 2
......@@ -113,15 +116,20 @@ extern struct mu_kwd _mu_imap_status_name_table[];
#define MU_IMAP_CB_UIDVALIDITY 5
/* The following callbacks correspond to server responses and take two
argument: a response code (see MU_IMAP_RESPONSE, below), and
human-readable text string as returned by the server. The latter can
be NULL. */
arguments: a response code (see MU_IMAP_RESPONSE, below) in SDAT, and
human-readable text string as returned by the server in PDAT. The
latter can be NULL. */
#define MU_IMAP_CB_OK 6
#define MU_IMAP_CB_NO 7
#define MU_IMAP_CB_BAD 8
#define MU_IMAP_CB_BYE 9
#define MU_IMAP_CB_PREAUTH 10
#define _MU_IMAP_CB_MAX 11
/* FETCH callback. Arguments: SDAT - message sequence number, PDAT - a
list (mu_list_t) of union mu_imap_fetch_response (see below). */
#define MU_IMAP_CB_FETCH 11
#define _MU_IMAP_CB_MAX 12
typedef void (*mu_imap_callback_t) (void *, int code, size_t sdat, void *pdat);
......@@ -144,7 +152,92 @@ void mu_imap_register_callback_function (mu_imap_t imap, int code,
#define MU_IMAP_RESPONSE_UNSEEN 10
extern struct mu_kwd mu_imap_response_codes[];
/* FETCH Response Codes */
/* BODY[<section>]<<origin octet>> */
#define MU_IMAP_FETCH_BODY 0
/* BODY & BODYSTRUCTURE */
#define MU_IMAP_FETCH_BODYSTRUCTURE 1
/* ENVELOPE */
#define MU_IMAP_FETCH_ENVELOPE 2
/* FLAGS */
#define MU_IMAP_FETCH_FLAGS 3
/* INTERNALDATE */
#define MU_IMAP_FETCH_INTERNALDATE 4
/* RFC822.SIZE */
#define MU_IMAP_FETCH_RFC822_SIZE 5
/* UID */
#define MU_IMAP_FETCH_UID 6
struct mu_imap_fetch_body
{
int type;
size_t *partv;
size_t partc;
char *key;
char *text;
};
struct mu_imap_fetch_bodystructure
{
int type;
//FIXME?
};
struct mu_imap_fetch_envelope
{
int type;
struct tm date;
struct mu_timezone tz;
char *subject;
mu_address_t from;
mu_address_t sender;
mu_address_t reply_to;
mu_address_t to;
mu_address_t cc;
mu_address_t bcc;
char *in_reply_to;
char *message_id;
};
struct mu_imap_fetch_flags
{
int type;
int flags;
};
struct mu_imap_fetch_internaldate
{
int type;
struct tm tm;
struct mu_timezone tz;
};
struct mu_imap_fetch_rfc822_size
{
int type;
size_t size;
};
struct mu_imap_fetch_uid
{
int type;
size_t uid;
};
union mu_imap_fetch_response
{
int type;
struct mu_imap_fetch_body body;
struct mu_imap_fetch_bodystructure bodystructure;
struct mu_imap_fetch_envelope envelope;
struct mu_imap_fetch_flags flags;
struct mu_imap_fetch_internaldate internaldate;
struct mu_imap_fetch_rfc822_size rfc822_size;
struct mu_imap_fetch_uid uid;
};
#ifdef __cplusplus
}
#endif
......
......@@ -32,7 +32,17 @@ extern "C" {
#define MU_IMAP_RESP 0x01
#define MU_IMAP_TRACE 0x02
#define MU_IMAP_XSCRIPT_MASK(n) (1<<((n)+1))
#define MU_IMAP_SET_XSCRIPT_MASK(imap,n) \
do \
(imap)->flags |= MU_IMAP_XSCRIPT_MASK (n); \
while (0)
#define MU_IMAP_CLR_XSCRIPT_MASK(imap,n) \
do \
(imap)->flags &= ~MU_IMAP_XSCRIPT_MASK (n); \
while (0)
#define MU_IMAP_IS_XSCRIPT_MASK(imap,n) \
((imap)->flags & MU_IMAP_XSCRIPT_MASK (n))
enum mu_imap_client_state
{
MU_IMAP_NO_STATE,
......@@ -47,6 +57,7 @@ enum mu_imap_client_state
MU_IMAP_SELECT_RX,
MU_IMAP_STATUS_RX,
MU_IMAP_NOOP_RX,
MU_IMAP_FETCH_RX,
MU_IMAP_CLOSING
};
......@@ -174,10 +185,14 @@ int _mu_imap_response (mu_imap_t imap, mu_imap_response_action_t fun,
int _mu_imap_list_element_is_string (struct imap_list_element *elt,
const char *str);
int _mu_imap_list_nth_element_is_string (mu_list_t list, size_t n,
const char *str);
int _mu_imap_collect_flags (struct imap_list_element *arg, int *res);
struct imap_list_element *_mu_imap_list_at (mu_list_t list, int idx);
int _mu_imap_parse_fetch_response (mu_list_t resp, mu_list_t *result_list);
# ifdef __cplusplus
}
......
......@@ -28,6 +28,7 @@ libmu_imap_la_LIBADD = ${MU_LIB_AUTH} ${MU_LIB_MAILUTILS} @INTLLIBS@
# url.c
libmu_imap_la_SOURCES = \
fake-folder.c\
fetch.c\
callback.c\
capability.c\
capatst.c\
......
......@@ -37,7 +37,7 @@ mu_imap_set_carrier (mu_imap_t imap, mu_stream_t carrier)
if (imap == NULL)
return EINVAL;
rc = mu_imapio_create (&io, carrier);
rc = mu_imapio_create (&io, carrier, MU_IMAPIO_CLIENT);
if (rc)
return rc;
if (imap->io)
......
......@@ -270,4 +270,13 @@ _mu_imap_list_at (mu_list_t list, int idx)
return arg;
}
int
_mu_imap_list_nth_element_is_string (mu_list_t list, size_t n,
const char *str)
{
struct imap_list_element *elt = _mu_imap_list_at (list, n);
return elt && elt->type == imap_eltype_string &&
strcmp (elt->v.string, str) == 0;
}
......
......@@ -304,6 +304,34 @@ _process_unsolicited_response (mu_imap_t imap, mu_list_t resp)
return 0;
}
}
else if (count == 3 &&
_mu_imap_list_nth_element_is_string (resp, 1, "FETCH"))
{
size_t msgno;
arg = _mu_imap_list_at (resp, 0);
if (arg && arg->type == imap_eltype_string)
{
char *p;
msgno = strtoul (arg->v.string, &p, 10);
if (*p)
return 1;
arg = _mu_imap_list_at (resp, 2);
if (arg->type == imap_eltype_list)
{
mu_list_t list;
if (_mu_imap_parse_fetch_response (arg->v.list, &list) == 0)
{
mu_imap_callback (imap, MU_IMAP_CB_FETCH, msgno, list);
mu_list_destroy (&list);
}
return 0;
}
}
}
return 1;
}
......@@ -317,8 +345,8 @@ _mu_imap_process_untagged_response (mu_imap_t imap, mu_list_t list,
if (fun)
fun (imap, list, data);
else
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
("ignoring unexpected response"));
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE9,
("ignoring unrecognized response"));
}
return 0;
}
......
......@@ -89,21 +89,26 @@ mu_imap_trace_mask (mu_imap_t imap, int op, int lev)
switch (op)
{
case MU_IMAP_TRACE_SET:
imap->flags |= MU_IMAP_XSCRIPT_MASK (lev);
MU_IMAP_SET_XSCRIPT_MASK (imap, lev);
if (lev & MU_XSCRIPT_PAYLOAD)
mu_imapio_trace_payload (imap->io, 1);
break;
case MU_IMAP_TRACE_CLR:
imap->flags &= ~MU_IMAP_XSCRIPT_MASK (lev);
MU_IMAP_CLR_XSCRIPT_MASK (imap, lev);
if (lev & MU_XSCRIPT_PAYLOAD)
mu_imapio_trace_payload (imap->io, 0);
break;
case MU_IMAP_TRACE_QRY:
if (imap->flags & MU_IMAP_XSCRIPT_MASK (lev))
if (MU_IMAP_IS_XSCRIPT_MASK (imap, lev))
break;
return MU_ERR_NOENT;
default:
return EINVAL;
}
return 0;
}
......
......@@ -182,6 +182,13 @@ imap_bye_callback (void *data, int code, size_t sdat, void *pdat)
mu_diag_output (MU_DIAG_INFO, _("server is closing connection"));
}
static void
imap_bad_callback (void *data, int code, size_t sdat, void *pdat)
{
const char *text = pdat;
mu_diag_output (MU_DIAG_CRIT, "SERVER ALERT: %s", text);
}
static int
com_disconnect (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
......@@ -282,6 +289,9 @@ com_connect (int argc, char **argv)
mu_imap_register_callback_function (imap, MU_IMAP_CB_BYE,
imap_bye_callback,
NULL);
mu_imap_register_callback_function (imap, MU_IMAP_CB_BAD,
imap_bad_callback,
NULL);
status = mu_imap_connect (imap);
......@@ -587,44 +597,68 @@ com_noop (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
return 0;
}
static int
com_fetch (int argc, char **argv)
{
int status = mu_imap_fetch (imap, argv[1], argv[2]);
if (status)
report_failure ("fetch", status);
return 0;
}
struct mutool_command imap_comtab[] = {
{ "capability", 1, -1, com_capability,
{ "capability", 1, -1, 0,
com_capability,
/* TRANSLATORS: -reread is a keyword; do not translate. */
N_("[-reread] [NAME...]"),
N_("list server capabilities") },
{ "verbose", 1, 4, com_verbose,
{ "verbose", 1, 4, 0,
com_verbose,
"[on|off|mask|unmask] [secure [payload]]",
N_("control the protocol tracing") },
{ "connect", 1, 4, com_connect,
{ "connect", 1, 4, 0,
com_connect,
/* TRANSLATORS: --tls is a keyword. */
N_("[-tls] HOSTNAME [PORT]"),
N_("open connection") },
{ "disconnect", 1, 1,
{ "disconnect", 1, 1, 0,
com_disconnect,
NULL,
N_("close connection") },
{ "login", 2, 3, com_login,
{ "login", 2, 3, 0,
com_login,
N_("USER [PASS]"),
N_("login to the server") },
{ "logout", 1, 1, com_logout,
{ "logout", 1, 1, 0,
com_logout,
NULL,
N_("quit imap session") },
{ "id", 1, -1, com_id,
{ "id", 1, -1, 0,
com_id,
N_("[-test KW] [ARG [ARG...]]"),
N_("send ID command") },
{ "noop", 1, 1, com_noop,
{ "noop", 1, 1, 0,
com_noop,
NULL,
N_("no operation (keepalive)") },
{ "select", 1, 2, com_select,
{ "select", 1, 2, 0,
com_select,
N_("[MBOX]"),
N_("select a mailbox") },
{ "examine", 1, 2, com_examine,
{ "examine", 1, 2, 0,
com_examine,
N_("[MBOX]"),
N_("examine a mailbox") },
{ "status", 3, -1, com_status,
{ "status", 3, -1, 0,
com_status,
N_("MBOX KW [KW...]"),
N_("get mailbox status") },
{ "quit", 1, 1, com_logout,
{ "fetch", 3, 3, CMD_COALESCE_EXTRA_ARGS,
com_fetch,
N_("MSGSET ITEMS"),
N_("fetch message data") },
{ "quit", 1, 1, 0,
com_logout,
NULL,
N_("same as `logout'") },
{ NULL }
......
......@@ -18,13 +18,15 @@
typedef int (*mutool_action_t) (int argc, char **argv);
#define CMD_COALESCE_EXTRA_ARGS 0x01
struct mutool_command
{
const char *name; /* User printable name of the function. */
int argmin; /* Min. acceptable number of arguments (-1 means
pass all arguments as a single string */
int argmin; /* Min. acceptable number of arguments (> 1) */
int argmax; /* Max. allowed number of arguments (-1 means not
limited */
int flags;
mutool_action_t func; /* Function to call to do the job. */
const char *argdoc; /* Documentation for the arguments */
const char *docstring;/* Documentation for this function. */
......
......@@ -611,59 +611,59 @@ com_quit (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
}
struct mutool_command pop_comtab[] = {
{ "apop", 2, 3, com_apop,
{ "apop", 2, 3, 0, com_apop,
N_("USER [PASS]"),
N_("authenticate with APOP") },
{ "capa", 1, -1, com_capa,
{ "capa", 1, -1, 0, com_capa,
/* TRANSLATORS: -reread is a keyword; do not translate. */
N_("[-reread] [NAME...]"),
N_("list server capabilities") },
{ "disconnect", 1, 1,
{ "disconnect", 1, 1, 0,
com_disconnect,
NULL,
N_("close connection") },
{ "dele", 2, 2, com_dele,
{ "dele", 2, 2, 0, com_dele,
N_("NUMBER"),
N_("mark message for deletion") },
{ "list", 1, 2, com_list,
{ "list", 1, 2, 0, com_list,
N_("[NUMBER]"),
N_("list messages") },
{ "noop", 1, 1, com_noop,
{ "noop", 1, 1, 0, com_noop,
NULL,
N_("send a \"no operation\"") },
{ "pass", 1, 2, com_pass,
{ "pass", 1, 2, 0, com_pass,
N_("[PASSWORD]"),
N_("send password") },
{ "connect", 1, 4, com_connect,
{ "connect", 1, 4, 0, com_connect,
/* TRANSLATORS: --tls is a keyword. */
N_("[-tls] HOSTNAME [PORT]"),
N_("open connection") },
{ "quit", 1, 1, com_quit,
{ "quit", 1, 1, 0, com_quit,
NULL,
N_("quit pop3 session") },
{ "retr", 2, 2, com_retr,
{ "retr", 2, 2, 0, com_retr,
"NUMBER",
N_("retrieve a message") },
{ "rset", 1, 1, com_rset,
{ "rset", 1, 1, 0, com_rset,
NULL,
N_("remove deletion marks") },
{ "stat", 1, 1, com_stat,
{ "stat", 1, 1, 0, com_stat,
NULL,
N_("get the mailbox size and number of messages in it") },
{ "stls", 1, 1, com_stls,
{ "stls", 1, 1, 0, com_stls,
NULL,
N_("start TLS negotiation") },
{ "top", 2, 3, com_top,
{ "top", 2, 3, 0, com_top,
"MSGNO [NUMBER]",
N_("display message headers and first NUMBER (default 5) lines of"
" its body") },
{ "uidl", 1, 2, com_uidl,
{ "uidl", 1, 2, 0, com_uidl,
N_("[NUMBER]"),
N_("show unique message identifiers") },
{ "user", 2, 2, com_user,
{ "user", 2, 2, 0, com_user,
N_("NAME"),
N_("send login") },
{ "verbose", 1, 4, com_verbose,
{ "verbose", 1, 4, 0, com_verbose,
"[on|off|mask|unmask] [secure [payload]]",
N_("control the protocol tracing") },
{ NULL }
......
......@@ -71,18 +71,18 @@ static int shell_history (int, char **);
#endif
struct mutool_command default_comtab[] = {
{ "prompt", -1, -1, shell_prompt,
{ "prompt", 1, 2, CMD_COALESCE_EXTRA_ARGS, shell_prompt,
N_("STRING"),
N_("set command prompt") },
{ "exit", 1, 1, shell_exit, NULL, N_("exit program") },
{ "help", 1, 2, shell_help,
{ "exit", 1, 1, 0, shell_exit, NULL, N_("exit program") },
{ "help", 1, 2, 0, shell_help,
N_("[COMMAND]"),
N_("display this text") },
{ "?", 1, 1, shell_help,
{ "?", 1, 1, 0, shell_help,
N_("[COMMAND]"),
N_("synonym for `help'") },
#ifdef WITH_READLINE
{ "history", 1, 1, shell_history,
{ "history", 1, 1, 0, shell_history,
NULL,
N_("show command history") },
#endif
......@@ -492,59 +492,101 @@ add_history (const char *s MU_ARG_UNUSED)
}
#endif
static int
next_arg (struct mu_wordsplit *ws)
{
int rc = mu_wordsplit (NULL, ws, MU_WRDSF_INCREMENTAL);
if (rc == MU_WRDSE_NOINPUT)
{
mu_error ("%s: too few arguments", ws->ws_wordv[0]);
mu_wordsplit_free (ws);
return -1;
}
else if (rc)
{
mu_error ("cannot parse input line: %s",
mu_wordsplit_strerror (ws));
return 1;
}
return 0;
}
/* Parse and execute a command line. */
int
execute_line (char *line)
{
int rc;
struct mu_wordsplit ws;
int argc;
char **argv;
struct mutool_command *cmd;
int status = 0;
ws.ws_comment = "#";
ws.ws_offs = 1; /* Keep extra slot for expansion in case when argmin == -1 */
if (mu_wordsplit (line, &ws,
MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT|MU_WRDSF_DOOFFS))
rc = mu_wordsplit (line, &ws,
MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT|
MU_WRDSF_INCREMENTAL|MU_WRDSF_APPEND);
if (rc == MU_WRDSE_NOINPUT)
{
mu_wordsplit_free (&ws);
return 0;
}
else if (rc)
{
mu_error("cannot parse input line: %s", mu_wordsplit_strerror (&ws));
mu_error ("cannot parse input line: %s", mu_wordsplit_strerror (&ws));
return 0;
}
argc = ws.ws_wordc;
argv = ws.ws_wordv + 1;
if (argc >= 0)
if (ws.ws_wordc)
{
struct mutool_command *cmd = find_command (argv[0]);
int argmin;
cmd = find_command (ws.ws_wordv[0]);
if (!cmd)
mu_error ("%s: no such command.", argv[0]);
else if (cmd->argmin > 0 && argc < cmd->argmin)
mu_error ("%s: too few arguments", argv[0]);
else if (cmd->argmax > 0 && argc > cmd->argmax)
mu_error ("%s: too many arguments", argv[0]);
{
mu_error ("%s: no such command.", ws.ws_wordv[0]);
mu_wordsplit_free (&ws);
return 0;
}
argmin = cmd->argmin;
if (cmd->flags & CMD_COALESCE_EXTRA_ARGS)
--argmin;
while (ws.ws_wordc < argmin)
{
if (next_arg (&ws))
return 0;
}
if (cmd->flags & CMD_COALESCE_EXTRA_ARGS)
{
ws.ws_flags |= MU_WRDSF_NOSPLIT;
if (next_arg (&ws))
return 0;
}
else
{
if (cmd->argmin <= 0 && argc != 2)
for (;;)
{
size_t i;
char *word = mu_str_skip_class (line, MU_CTYPE_SPACE);
char *arg = mu_str_skip_class_comp (word, MU_CTYPE_SPACE);
if (*arg)
if (cmd->argmax > 0 && ws.ws_wordc > cmd->argmax)
{
mu_error ("%s: too many arguments", ws.ws_wordv[0]);
mu_wordsplit_free (&ws);
return 0;
}
rc = mu_wordsplit (NULL, &ws, MU_WRDSF_INCREMENTAL);
if (rc == 0)
continue;
else if (rc == MU_WRDSE_NOINPUT)
break;
else
{
*arg++ = 0;
arg = mu_str_skip_class (arg, MU_CTYPE_SPACE);
mu_error ("cannot parse input line: %s",
mu_wordsplit_strerror (&ws));
return 0;
}
for (i = 0; i < ws.ws_wordc; i++)
free (ws.ws_wordv[i + 1]);
ws.ws_wordv[0] = xstrdup (word);
ws.ws_wordv[1] = xstrdup (arg);
ws.ws_wordv[2] = NULL;
ws.ws_wordc = 2;
argc = ws.ws_wordc;
argv = ws.ws_wordv;
}
status = cmd->func (argc, argv);
}
status = cmd->func (ws.ws_wordc, ws.ws_wordv);
}
mu_wordsplit_free (&ws);
return status;
......