Commit 439f8c1a 439f8c1a9f99409087694f95c82f40c12e286371 by Sergey Poznyakoff

imap client: implement login and id commands.

* libproto/imap/id.c: New file.
* libproto/imap/login.c: New file.
* libproto/imap/Makefile.am: Add new files.
* libproto/imap/capability.c (mu_imap_capability): Clear
MU_IMAP_RESP before reading response.
Add MU_WRDSF_QUOTE to mu_wordsplit flags.
* libproto/imap/err.c (_mu_imap_seterrstr): Bugfix: initialize
imap->errstr.
* libproto/imap/logout.c (mu_imap_logout): Clear
MU_IMAP_RESP before reading response.
* libproto/imap/response.c (_mu_imap_response): Set error string.
* mu/imap.c (com_login, com_id): New functions.
(imap_comtab) <login, id>: New keywords.
1 parent 96cfdb5e
......@@ -49,8 +49,11 @@ int mu_imap_disconnect (mu_imap_t imap);
int mu_imap_capability (mu_imap_t imap, int reread, mu_iterator_t *piter);
int mu_imap_capability_test (mu_imap_t imap, const char *name,
const char **pret);
int mu_imap_login (mu_imap_t imap, const char *user, const char *pass);
int mu_imap_logout (mu_imap_t imap);
int mu_imap_id (mu_imap_t imap, char **idenv, mu_list_t *plist);
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);
......
......@@ -40,7 +40,9 @@ enum mu_imap_client_state
MU_IMAP_GREETINGS,
MU_IMAP_CONNECTED,
MU_IMAP_CAPABILITY_RX,
MU_IMAP_LOGIN_RX,
MU_IMAP_LOGOUT_RX,
MU_IMAP_ID_RX,
};
enum mu_imap_response
......
......@@ -33,6 +33,7 @@ struct mu_kwd bool_keytab[] = {
/*{ "reuse", MU_WRDSF_REUSE },*/
{ "undef", MU_WRDSF_UNDEF },
{ "novar", MU_WRDSF_NOVAR },
{ "nocmd", MU_WRDSF_NOCMD },
{ "ws", MU_WRDSF_WS },
{ "quote", MU_WRDSF_QUOTE },
{ "squeeze_delims", MU_WRDSF_SQUEEZE_DELIMS },
......@@ -205,7 +206,7 @@ main (int argc, char **argv)
plaintext_option = !negate;
continue;
}
if (mu_kwd_xlat_name (bool_keytab, opt, &flag) == 0)
{
if (negate)
......
......@@ -36,6 +36,8 @@ libmu_imap_la_SOURCES = \
destroy.c\
disconnect.c\
err.c\
id.c\
login.c\
logout.c\
response.c\
state.c\
......
......@@ -81,6 +81,7 @@ mu_imap_capability (mu_imap_t imap, int reread, mu_iterator_t *piter)
status = mu_stream_printf (imap->carrier, "%s CAPABILITY\r\n",
imap->tag_str);
MU_IMAP_CHECK_EAGAIN (imap, status);
MU_IMAP_FCLR (imap, MU_IMAP_RESP);
imap->state = MU_IMAP_CAPABILITY_RX;
case MU_IMAP_CAPABILITY_RX:
......@@ -103,7 +104,7 @@ mu_imap_capability (mu_imap_t imap, int reread, mu_iterator_t *piter)
if (mu_wordsplit (str, &ws,
MU_WRDSF_NOVAR | MU_WRDSF_NOCMD |
MU_WRDSF_SQUEEZE_DELIMS))
MU_WRDSF_QUOTE | MU_WRDSF_SQUEEZE_DELIMS))
{
int ec = errno;
mu_error ("mu_imap_capability: cannot split line: %s",
......
......@@ -32,6 +32,7 @@ _mu_imap_seterrstr (mu_imap_t imap, const char *str, size_t len)
if (!p)
return ENOMEM;
imap->errsize = len + 1;
imap->errstr = p;
}
memcpy (imap->errstr, str, len);
imap->errstr[len] = 0;
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2010 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
<http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <mailutils/errno.h>
#include <mailutils/cstr.h>
#include <mailutils/wordsplit.h>
#include <mailutils/stream.h>
#include <mailutils/sys/imap.h>
static int
id_comp (const void *item, const void *value)
{
const char *id = item;
const char *needle = value;
return mu_c_strcasecmp (id, needle);
}
static int
parse_id_reply (mu_imap_t imap, mu_list_t *plist)
{
mu_list_t list;
int rc;
const char *response;
struct mu_wordsplit ws;
size_t i;
rc = mu_list_create (&list);
if (rc)
return rc;
mu_list_set_comparator (list, id_comp);
mu_list_set_destroy_item (list, mu_list_free_item);
rc = mu_list_get (imap->untagged_resp, 0, (void*) &response);
if (rc == MU_ERR_NOENT)
{
*plist = list;
return 0;
}
else if (rc)
{
mu_list_destroy (&list);
return rc;
}
ws.ws_delim = "() \t";
if (mu_wordsplit (response, &ws,
MU_WRDSF_NOVAR | MU_WRDSF_NOCMD |
MU_WRDSF_QUOTE | MU_WRDSF_DELIM |
MU_WRDSF_SQUEEZE_DELIMS |
MU_WRDSF_WS))
{
int ec = errno;
mu_error ("mu_imap_id: cannot split line: %s",
mu_wordsplit_strerror (&ws));
mu_list_destroy (&list);
return ec;
}
for (i = 1; i < ws.ws_wordc; i += 2)
{
size_t len, l1, l2;
char *elt;
if (i + 1 == ws.ws_wordc)
break;
l1 = strlen (ws.ws_wordv[i]);
l2 = strlen (ws.ws_wordv[i+1]);
len = l1 + l2 + 1;
elt = malloc (len + 1);
if (!elt)
break;
memcpy (elt, ws.ws_wordv[i], l1);
elt[l1] = 0;
memcpy (elt + l1 + 1, ws.ws_wordv[i+1], l2);
elt[len] = 0;
mu_list_append (list, elt);
}
mu_wordsplit_free (&ws);
*plist = list;
return 0;
}
int
mu_imap_id (mu_imap_t imap, char **idenv, mu_list_t *plist)
{
int status;
if (imap == NULL)
return EINVAL;
if (!imap->carrier)
return MU_ERR_NO_TRANSPORT;
if (imap->state != MU_IMAP_CONNECTED)
return MU_ERR_SEQ;
switch (imap->state)
{
case MU_IMAP_CONNECTED:
status = _mu_imap_tag_next (imap);
MU_IMAP_CHECK_EAGAIN (imap, status);
status = mu_stream_printf (imap->carrier, "%s ID ",
imap->tag_str);
MU_IMAP_CHECK_ERROR (imap, status);
if (!idenv)
status = mu_stream_printf (imap->carrier, "NIL");
else
{
if (idenv[0])
{
int i;
char *delim = "(";
for (i = 0; idenv[i]; i++)
{
status = mu_stream_printf (imap->carrier, "%s\"%s\"",
delim, idenv[i]);
MU_IMAP_CHECK_ERROR (imap, status);
delim = " ";
if (status)
break;
}
status = mu_stream_printf (imap->carrier, ")");
}
else
status = mu_stream_printf (imap->carrier, "()");
}
MU_IMAP_CHECK_ERROR (imap, status);
status = mu_stream_printf (imap->carrier, "\r\n");
MU_IMAP_CHECK_ERROR (imap, status);
MU_IMAP_FCLR (imap, MU_IMAP_RESP);
imap->state = MU_IMAP_ID_RX;
case MU_IMAP_ID_RX:
status = _mu_imap_response (imap);
MU_IMAP_CHECK_EAGAIN (imap, status);
switch (imap->resp_code)
{
case MU_IMAP_OK:
imap->imap_state = MU_IMAP_STATE_AUTH;
if (plist)
status = parse_id_reply (imap, plist);
break;
case MU_IMAP_NO:
status = EACCES;
break;
case MU_IMAP_BAD:
status = MU_ERR_BADREPLY;
break;
}
imap->state = MU_IMAP_CONNECTED;
break;
default:
status = EINPROGRESS;
}
return status;
}
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2010 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
<http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <mailutils/errno.h>
#include <mailutils/stream.h>
#include <mailutils/sys/imap.h>
int
mu_imap_login (mu_imap_t imap, const char *user, const char *pass)
{
int status;
if (imap == NULL)
return EINVAL;
if (!imap->carrier)
return MU_ERR_NO_TRANSPORT;
if (imap->state != MU_IMAP_CONNECTED)
return MU_ERR_SEQ;
if (imap->imap_state != MU_IMAP_STATE_NONAUTH)
return MU_ERR_SEQ;
switch (imap->state)
{
case MU_IMAP_CONNECTED:
if (mu_imap_trace_mask (imap, MU_IMAP_TRACE_QRY, MU_XSCRIPT_SECURE))
_mu_imap_xscript_level (imap, MU_XSCRIPT_SECURE);
status = _mu_imap_tag_next (imap);
MU_IMAP_CHECK_EAGAIN (imap, status);
status = mu_stream_printf (imap->carrier, "%s LOGIN \"%s\" \"%s\"\r\n",
imap->tag_str, user, pass);
_mu_imap_xscript_level (imap, MU_XSCRIPT_NORMAL);
/* FIXME: how to obscure the passwd in the stream buffer? */
MU_IMAP_CHECK_EAGAIN (imap, status);
MU_IMAP_FCLR (imap, MU_IMAP_RESP);
imap->state = MU_IMAP_LOGIN_RX;
case MU_IMAP_LOGIN_RX:
status = _mu_imap_response (imap);
MU_IMAP_CHECK_EAGAIN (imap, status);
switch (imap->resp_code)
{
case MU_IMAP_OK:
imap->imap_state = MU_IMAP_STATE_AUTH;
break;
case MU_IMAP_NO:
status = EACCES;
break;
case MU_IMAP_BAD:
status = MU_ERR_BADREPLY;
break;
}
imap->state = MU_IMAP_CONNECTED;
break;
default:
status = EINPROGRESS;
}
return status;
}
......@@ -43,6 +43,7 @@ mu_imap_logout (mu_imap_t imap)
status = mu_stream_printf (imap->carrier, "%s LOGOUT\r\n",
imap->tag_str);
MU_IMAP_CHECK_EAGAIN (imap, status);
MU_IMAP_FCLR (imap, MU_IMAP_RESP);
imap->state = MU_IMAP_LOGOUT_RX;
case MU_IMAP_LOGOUT_RX:
......
......@@ -85,12 +85,24 @@ _mu_imap_response (mu_imap_t imap)
imap->tagbuf = np;
}
strcpy (imap->tagbuf, p);
if (IS_PREFIX(p, len, "OK"))
imap->resp_code = MU_IMAP_OK;
else if (IS_PREFIX(p, len, "NO"))
imap->resp_code = MU_IMAP_NO;
else if (IS_PREFIX(p, len, "BAD"))
imap->resp_code = MU_IMAP_BAD;
if (IS_PREFIX (p, len, "OK"))
{
imap->resp_code = MU_IMAP_OK;
p = mu_str_skip_cset (p + 2, " ");
_mu_imap_seterrstr (imap, p, strlen (p));
}
else if (IS_PREFIX (p, len, "NO"))
{
imap->resp_code = MU_IMAP_NO;
p = mu_str_skip_cset (p + 2, " ");
_mu_imap_seterrstr (imap, p, strlen (p));
}
else if (IS_PREFIX (p, len, "BAD"))
{
imap->resp_code = MU_IMAP_BAD;
p = mu_str_skip_cset (p + 2, " ");
_mu_imap_seterrstr (imap, p, strlen (p));
}
else
status = MU_ERR_BADREPLY;
MU_IMAP_FSET (imap, MU_IMAP_RESP);
......
......@@ -358,6 +358,102 @@ com_capability (int argc, char **argv)
return status;
}
static int
com_login (int argc, char **argv)
{
int status;
char *pwd, *passbuf = NULL;
if (argc == 2)
{
if (!mutool_shell_interactive)
{
mu_error (_("login: password required"));
return 1;
}
status = mu_getpass (mustrin, mustrout, "Password:", &passbuf);
if (status)
return status;
pwd = passbuf;
}
else
pwd = argv[2];
status = mu_imap_login (imap, argv[1], pwd);
memset (pwd, 0, strlen (pwd));
free (passbuf);
if (status == 0)
imap_prompt_env ();
else
{
const char *str;
mu_error ("authentication failed: %s", mu_strerror (status));
if (mu_imap_strerror (imap, &str) == 0)
mu_error ("server reply: %s", str);
}
return 0;
}
static int
_print_id (void *item, void *data)
{
const char *id = item;
mu_stream_printf (mustrout, "ID: %s %s\n", id, id + strlen (id) + 1);
return 0;
}
static int
com_id (int argc, char **argv)
{
mu_list_t list;
char *test = NULL;
int status;
argv++;
if (argv[0] && strcmp (argv[0], "-test") == 0)
{
argv++;
if (argv[0] == NULL)
{
mu_error ("id -test requires an argument");
return 0;
}
test = argv[0];
argv++;
}
status = mu_imap_id (imap, argv + 1, &list);
if (status == 0)
{
if (test)
{
const char *res;
int rc = mu_list_locate (list, test, (void*)&res);
switch (rc)
{
case 0:
mu_stream_printf (mustrout, "%s: %s\n", test,
res + strlen (res) + 1);
break;
case MU_ERR_NOENT:
mu_stream_printf (mustrout, "%s is not set\n", test);
break;
default:
return rc;
}
}
else
mu_list_do (list, _print_id, NULL);
mu_list_destroy (&list);
}
return status;
}
struct mutool_command imap_comtab[] = {
{ "capability", 1, -1, com_capability,
......@@ -375,9 +471,15 @@ struct mutool_command imap_comtab[] = {
com_disconnect,
NULL,
N_("close connection") },
{ "login", 2, 3, com_login,
N_("USER [PASS]"),
N_("login to the server") },
{ "logout", 1, 1, com_logout,
NULL,
N_("quit imap session") },
{ "id", 1, -1, com_id,
N_("[-test KW] [ARG [ARG...]]"),
N_("send ID command") },
{ "quit", 1, 1, com_logout,
NULL,
N_("same as `logout'") },
......