Commit 80b012f7 80b012f7f89ca6acbf1191ef1b8073239509b941 by Sergey Poznyakoff

Change callback handling in imap client.

* include/mailutils/imap.h (MU_IMAP_CB_NO)
(MU_IMAP_CB_BAD,MU_IMAP_CB_BYE)
(MU_IMAP_CB_PREAUTH): New callbacks.
(mu_imap_callback_t): Change signature.
(mu_imap_callback): Change signature.
* include/mailutils/sys/imap.h (mu_imap_client_state)
<MU_IMAP_CLOSING>: New state.
* libproto/imap/callback.c (mu_imap_callback): Change signature.
Fix boundary check.
(mu_imap_register_callback_function): Fix boundary check.
* libproto/imap/connect.c (mu_imap_connect): Use mu_imap_foreach_response
to parse response.
* libproto/imap/response.c (_mu_imap_response): Check number of words
before dereferencing them.
* libproto/imap/resproc.c: Handle all server responses.
* mu/imap.c (com_connect): Set POPAUTH callback.
Set verbosity flags after I/O structure has been created.
1 parent bbc46f0c
......@@ -108,19 +108,30 @@ int mu_imap_foreach_response (mu_imap_t imap, mu_imap_response_action_t fun,
void *data);
/* 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. */
#define MU_IMAP_CB_PERMANENT_FLAGS 0
#define MU_IMAP_CB_MESSAGE_COUNT 1
#define MU_IMAP_CB_RECENT_COUNT 2
#define MU_IMAP_CB_FIRST_UNSEEN 3
#define MU_IMAP_CB_UIDNEXT 4
#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. */
#define MU_IMAP_CB_OK 6
#define _MU_IMAP_CB_MAX 7
#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
typedef void (*mu_imap_callback_t) (void *, int code, mu_list_t resp,
va_list ap);
typedef void (*mu_imap_callback_t) (void *, int code, va_list ap);
void mu_imap_callback (mu_imap_t imap, int code, mu_list_t resp, ...);
void mu_imap_callback (mu_imap_t imap, int code, ...);
void mu_imap_register_callback_function (mu_imap_t imap, int code,
mu_imap_callback_t callback,
......
......@@ -45,7 +45,8 @@ enum mu_imap_client_state
MU_IMAP_LOGOUT_RX,
MU_IMAP_ID_RX,
MU_IMAP_SELECT_RX,
MU_IMAP_STATUS_RX
MU_IMAP_STATUS_RX,
MU_IMAP_CLOSING
};
enum mu_imap_response
......
......@@ -23,15 +23,15 @@
#include <mailutils/sys/imap.h>
void
mu_imap_callback (mu_imap_t imap, int code, mu_list_t resp, ...)
mu_imap_callback (mu_imap_t imap, int code, ...)
{
va_list ap;
if (code < 0 || code > _MU_IMAP_CB_MAX || !imap->callback[code].action)
if (code < 0 || code >= _MU_IMAP_CB_MAX || !imap->callback[code].action)
return;
va_start (ap, resp);
imap->callback[code].action (imap->callback[code].data, code, resp, ap);
va_start (ap, code);
imap->callback[code].action (imap->callback[code].data, code, ap);
va_end (ap);
}
......@@ -40,7 +40,7 @@ mu_imap_register_callback_function (mu_imap_t imap, int code,
mu_imap_callback_t callback,
void *data)
{
if (code < 0 || code > _MU_IMAP_CB_MAX)
if (code < 0 || code >= _MU_IMAP_CB_MAX)
{
mu_error ("%s:%d: ignoring unsupported callback code %d",
__FILE__, __LINE__, code);
......
......@@ -37,11 +37,17 @@ mu_imap_connect (mu_imap_t imap)
char **wv;
char *bufptr;
size_t bufsize;
if (imap == NULL)
return EINVAL;
if (imap->io == NULL)
return EINVAL;
_mu_imap_clrerrstr (imap);
status = _mu_imap_untagged_response_clear (imap);
if (status)
return status;
switch (imap->state)
{
default:
......@@ -79,29 +85,26 @@ mu_imap_connect (mu_imap_t imap)
imap->state = MU_IMAP_ERROR;
return MU_ERR_BADREPLY;
}
else if (strcmp (wv[1], "BYE") == 0)
{
status = EACCES;
mu_imapio_getbuf (imap->io, &bufptr, &bufsize);
_mu_imap_seterrstr (imap, bufptr + 2, bufsize - 2);
}
else if (strcmp (wv[1], "PREAUTH") == 0)
{
status = 0;
imap->state = MU_IMAP_CONNECTED;
imap->imap_state = MU_IMAP_STATE_AUTH;
}
else if (strcmp (wv[1], "OK") == 0)
{
status = 0;
imap->state = MU_IMAP_CONNECTED;
imap->imap_state = MU_IMAP_STATE_NONAUTH;
}
else
{
status = MU_ERR_BADREPLY;
imap->state = MU_IMAP_ERROR;
_mu_imap_untagged_response_add (imap);
mu_imap_foreach_response (imap, NULL, NULL);
switch (imap->state)
{
case MU_IMAP_CONNECTED:
status = 0;
break;
case MU_IMAP_CLOSING:
status = EACCES;
break;
default:
imap->state = MU_IMAP_ERROR;
status = MU_ERR_BADREPLY;
}
}
break;
}
return status;
......
......@@ -85,7 +85,6 @@ int
mu_imap_id (mu_imap_t imap, char **idenv, mu_assoc_t *passoc)
{
int status;
char *p;
if (imap == NULL)
return EINVAL;
......
......@@ -28,7 +28,6 @@ int
mu_imap_login (mu_imap_t imap, const char *user, const char *pass)
{
int status;
char *p;
if (imap == NULL)
return EINVAL;
......
......@@ -59,6 +59,13 @@ _mu_imap_response (mu_imap_t imap)
size_t wc;
mu_imapio_get_words (imap->io, &wc, &wv);
if (wc == 0)
{
imap->state = MU_IMAP_ERROR;
status = MU_ERR_BADREPLY;/* FIXME: ECONNRESET ? */
break;
}
if (strcmp (wv[0], "*") == 0)
{
_mu_imap_untagged_response_add (imap);/* FIXME: error checking */
......
......@@ -53,89 +53,157 @@ struct mu_kwd mu_imap_response_codes[] = {
{ NULL }
};
static void
ok_response (mu_imap_t imap, mu_list_t resp, void *data)
static int
parse_response_code (mu_imap_t imap, mu_list_t resp)
{
struct imap_list_element *arg;
int rcode = -1;
size_t n = 0;
arg = _mu_imap_list_at (resp, 1);
if (!arg)
return;
return -1;
if (_mu_imap_list_element_is_string (arg, "["))
{
char *p;
arg = _mu_imap_list_at (resp, 2);
if (!arg || arg->type != imap_eltype_string)
return;
return -1;
if (mu_kwd_xlat_name (mu_imap_response_codes, arg->v.string, &rcode))
rcode = -1;
return -1;
arg = _mu_imap_list_at (resp, 4);
if (!arg || !_mu_imap_list_element_is_string (arg, "]"))
return;
switch (rcode)
{
case MU_IMAP_RESPONSE_PERMANENTFLAGS:
arg = _mu_imap_list_at (resp, 3);
if (!arg ||
_mu_imap_collect_flags (arg, &imap->mbox_stat.permanent_flags))
break;
imap->mbox_stat.flags |= MU_IMAP_STAT_PERMANENT_FLAGS;
mu_imap_callback (imap, MU_IMAP_CB_PERMANENT_FLAGS, resp,
&imap->mbox_stat);
return;
return -1;
}
return rcode;
}
static void
ok_response (mu_imap_t imap, mu_list_t resp, void *data)
{
struct imap_list_element *arg;
int rcode;
size_t n = 0;
char *p;
rcode = parse_response_code (imap, resp);
switch (rcode)
{
case MU_IMAP_RESPONSE_PERMANENTFLAGS:
arg = _mu_imap_list_at (resp, 3);
if (!arg ||
_mu_imap_collect_flags (arg, &imap->mbox_stat.permanent_flags))
break;
imap->mbox_stat.flags |= MU_IMAP_STAT_PERMANENT_FLAGS;
mu_imap_callback (imap, MU_IMAP_CB_PERMANENT_FLAGS, &imap->mbox_stat);
return;
case MU_IMAP_RESPONSE_UIDNEXT:
arg = _mu_imap_list_at (resp, 3);
if (!arg || arg->type != imap_eltype_string)
break;
n = strtoul (arg->v.string, &p, 10);
if (*p == 0)
{
imap->mbox_stat.uidnext = n;
imap->mbox_stat.flags |= MU_IMAP_STAT_UIDNEXT;
mu_imap_callback (imap, MU_IMAP_CB_UIDNEXT, resp,
&imap->mbox_stat);
}
return;
case MU_IMAP_RESPONSE_UIDVALIDITY:
arg = _mu_imap_list_at (resp, 3);
if (!arg || arg->type != imap_eltype_string)
break;
n = strtoul (arg->v.string, &p, 10);
if (*p == 0)
{
imap->mbox_stat.uidvalidity = n;
imap->mbox_stat.flags |= MU_IMAP_STAT_UIDVALIDITY;
mu_imap_callback (imap, MU_IMAP_CB_UIDVALIDITY, resp,
&imap->mbox_stat);
}
return;
case MU_IMAP_RESPONSE_UIDNEXT:
arg = _mu_imap_list_at (resp, 3);
if (!arg || arg->type != imap_eltype_string)
break;
n = strtoul (arg->v.string, &p, 10);
if (*p == 0)
{
imap->mbox_stat.uidnext = n;
imap->mbox_stat.flags |= MU_IMAP_STAT_UIDNEXT;
mu_imap_callback (imap, MU_IMAP_CB_UIDNEXT, &imap->mbox_stat);
}
return;
case MU_IMAP_RESPONSE_UNSEEN:
arg = _mu_imap_list_at (resp, 3);
if (!arg || arg->type != imap_eltype_string)
break;
n = strtoul (arg->v.string, &p, 10);
if (*p == 0)
{
imap->mbox_stat.first_unseen = n;
imap->mbox_stat.flags |= MU_IMAP_STAT_FIRST_UNSEEN;
mu_imap_callback (imap, MU_IMAP_CB_FIRST_UNSEEN, resp,
&imap->mbox_stat);
}
return;
case MU_IMAP_RESPONSE_UIDVALIDITY:
arg = _mu_imap_list_at (resp, 3);
if (!arg || arg->type != imap_eltype_string)
break;
n = strtoul (arg->v.string, &p, 10);
if (*p == 0)
{
imap->mbox_stat.uidvalidity = n;
imap->mbox_stat.flags |= MU_IMAP_STAT_UIDVALIDITY;
mu_imap_callback (imap, MU_IMAP_CB_UIDVALIDITY, &imap->mbox_stat);
}
return;
case MU_IMAP_RESPONSE_UNSEEN:
arg = _mu_imap_list_at (resp, 3);
if (!arg || arg->type != imap_eltype_string)
break;
n = strtoul (arg->v.string, &p, 10);
if (*p == 0)
{
imap->mbox_stat.first_unseen = n;
imap->mbox_stat.flags |= MU_IMAP_STAT_FIRST_UNSEEN;
mu_imap_callback (imap, MU_IMAP_CB_FIRST_UNSEEN, &imap->mbox_stat);
}
return;
}
if (mu_list_tail (resp, (void*) &arg) || arg->type != imap_eltype_string)
arg = NULL;
mu_imap_callback (imap, MU_IMAP_CB_OK, rcode, arg ? arg->v.string : NULL);
if (imap->state == MU_IMAP_GREETINGS)
{
imap->state = MU_IMAP_CONNECTED;
imap->imap_state = MU_IMAP_STATE_NONAUTH;
}
}
static void
default_response (mu_imap_t imap, int code, mu_list_t resp, void *data)
{
struct imap_list_element *arg;
if (mu_list_tail (resp, (void*) &arg) || arg->type != imap_eltype_string)
arg = NULL;
mu_imap_callback (imap, code, parse_response_code (imap, resp),
arg ? arg->v.string : NULL);
}
static void
no_response (mu_imap_t imap, mu_list_t resp, void *data)
{
default_response (imap, MU_IMAP_CB_NO, resp, data);
if (imap->state == MU_IMAP_GREETINGS)
imap->state = MU_IMAP_ERROR;
}
static void
bad_response (mu_imap_t imap, mu_list_t resp, void *data)
{
default_response (imap, MU_IMAP_CB_BAD, resp, data);
if (imap->state == MU_IMAP_GREETINGS)
imap->state = MU_IMAP_ERROR;
}
static void
bye_response (mu_imap_t imap, mu_list_t resp, void *data)
{
default_response (imap, MU_IMAP_CB_BYE, resp, data);
imap->state = MU_IMAP_CLOSING;
}
static void
preauth_response (mu_imap_t imap, mu_list_t resp, void *data)
{
if (imap->state == MU_IMAP_GREETINGS)
{
struct imap_list_element *arg;
if (mu_list_tail (resp, (void*) &arg) || arg->type != imap_eltype_string)
arg = NULL;
mu_imap_callback (imap, MU_IMAP_CB_PREAUTH,
parse_response_code (imap, resp),
arg ? arg->v.string : NULL);
imap->state = MU_IMAP_CONNECTED;
imap->imap_state = MU_IMAP_STATE_AUTH;
}
mu_imap_callback (imap, MU_IMAP_CB_OK, resp, rcode);
else
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
("ignoring unexpected PREAUTH response"));
}
struct response_closure
{
......@@ -152,10 +220,10 @@ struct resptab
static struct resptab resptab[] = {
{ "OK", ok_response },
{ "NO", },
{ "BAD", },
{ "PREAUTH", },
{ "BYE", },
{ "NO", no_response },
{ "BAD", bad_response },
{ "PREAUTH", preauth_response },
{ "BYE", bye_response },
{ NULL }
};
......@@ -216,7 +284,8 @@ _process_unsolicited_response (mu_imap_t imap, mu_list_t resp)
return 1;
imap->mbox_stat.message_count = n;
imap->mbox_stat.flags |= MU_IMAP_STAT_MESSAGE_COUNT;
mu_imap_callback (imap, MU_IMAP_CB_MESSAGE_COUNT, resp, n);
mu_imap_callback (imap, MU_IMAP_CB_MESSAGE_COUNT, resp,
&imap->mbox_stat);
return 0;
}
else if (_mu_imap_list_element_is_string (arg, "RECENT"))
......@@ -229,7 +298,8 @@ _process_unsolicited_response (mu_imap_t imap, mu_list_t resp)
return 1;
imap->mbox_stat.recent_count = n;
imap->mbox_stat.flags |= MU_IMAP_STAT_RECENT_COUNT;
mu_imap_callback (imap, MU_IMAP_CB_RECENT_COUNT, resp, n);
mu_imap_callback (imap, MU_IMAP_CB_RECENT_COUNT, resp,
&imap->mbox_stat);
return 0;
}
}
......@@ -248,7 +318,13 @@ _process_response (void *item, void *data)
("ignoring string response \"%s\"", elt->v.string));
}
else if (_process_unsolicited_response (clos->imap, elt->v.list))
clos->fun (clos->imap, elt->v.list, clos->data);
{
if (clos->fun)
clos->fun (clos->imap, elt->v.list, clos->data);
else
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
("ignoring unexpected response"));
}
return 0;
}
......
......@@ -67,7 +67,6 @@ mu_imap_select (mu_imap_t imap, const char *mbox, int writable,
struct mu_imap_stat *ps)
{
int status;
char *p;
if (imap == NULL)
return EINVAL;
......
......@@ -119,7 +119,6 @@ int
mu_imap_status (mu_imap_t imap, const char *mboxname, struct mu_imap_stat *ps)
{
int status;
char *p;
int delim = 0;
int i;
......
......@@ -150,7 +150,18 @@ imap_prompt_env ()
mutool_prompt_env[14] = NULL;
}
/* Callbacks */
static void
imap_popauth_callback (void *data, int code, va_list ap)
{
int rcode = va_arg (ap, int);
const char *text = va_arg (ap, const char *);
if (text)
mu_diag_output (MU_DIAG_INFO, _("session authenticated: %s"), text);
else
mu_diag_output (MU_DIAG_INFO, _("session authenticated"));
}
static int
com_disconnect (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
......@@ -206,12 +217,6 @@ com_connect (int argc, char **argv)
struct mu_sockaddr *sa;
struct mu_sockaddr_hints hints;
if (QRY_VERBOSE ())
{
imap_set_verbose ();
imap_set_verbose_mask ();
}
memset (&hints, 0, sizeof (hints));
hints.flags = MU_AH_DETECT_FAMILY;
hints.port = tls ? MU_IMAP_DEFAULT_SSL_PORT : MU_IMAP_DEFAULT_PORT;
......@@ -243,6 +248,19 @@ com_connect (int argc, char **argv)
}
#endif
mu_imap_set_carrier (imap, tcp);
if (QRY_VERBOSE ())
{
imap_set_verbose ();
imap_set_verbose_mask ();
}
/* Set callbacks */
mu_imap_register_callback_function (imap, MU_IMAP_CB_PREAUTH,
imap_popauth_callback,
NULL);
status = mu_imap_connect (imap);
if (status)
{
......@@ -554,7 +572,6 @@ com_status (int argc, char **argv)
}
return 0;
}
struct mutool_command imap_comtab[] = {
{ "capability", 1, -1, com_capability,
......