Commit ebbbc397 ebbbc397fba86fd32fb2cccb377a59f8e38d03e9 by Sergey Poznyakoff

imap client: implement select/examine.

* include/mailutils/imap.h (mu_imap_stat): New structs.
(MU_IMAP_STAT_DEFINED_FLAGS,MU_IMAP_STAT_PERMANENT_FLAGS)
(MU_IMAP_STAT_MESSAGE_COUNT,MU_IMAP_STAT_RECENT_COUNT)
(MU_IMAP_STAT_FIRST_UNSEEN,MU_IMAP_STAT_UIDNEXT)
(MU_IMAP_STAT_UIDVALIDITY):  New flags.
(mu_imap_select): New proto.
* include/mailutils/imapio.h (mu_imap_flag_to_attribute)
(mu_imap_format_flags): New protos.
* include/mailutils/sys/imap.h (mu_imap_client_state)
<MU_IMAP_SELECT_RX>: New state.
(_mu_imap) <mbox_name,mbox_writable,mbox_stat>: New members.
* libmailutils/imapio/flags.c: New file.
* libmailutils/imapio/Makefile.am: Add flags.c
* libproto/imap/select.c: New file.
* libproto/imap/Makefile.am: Add select.c
* libproto/imap/err.c (_mu_imap_seterrstr)
(_mu_imap_clrerrstr, mu_imap_strerror): Allow for imap==NULL.
* libproto/imap/id.c (mu_imap_id): Set errstr on MU_ERR_BADREPLY
* libproto/imap/login.c (mu_imap_login): Likewise.
* libproto/imap/resplist.c (_mu_imap_list_element_is_string): New function.
* mu/imap.c: Implement examine and select.
1 parent a736b7a9
......@@ -37,7 +37,7 @@ enum mu_imap_state
MU_IMAP_STATE_NONAUTH, /* Non-Authenticated State */
MU_IMAP_STATE_AUTH, /* Authenticated State */
MU_IMAP_STATE_SELECTED, /* Selected State */
MU_IMAP_STATE_LOGOUT /* Logout State */
MU_IMAP_STATE_LOGOUT, /* Logout State */
};
int mu_imap_create (mu_imap_t *pimap);
......@@ -70,7 +70,31 @@ int mu_imap_state (mu_imap_t imap, int *pstate);
int mu_imap_state_str (int state, const char **pstr);
int mu_imap_tag (mu_imap_t imap, const char **pseq);
#define MU_IMAP_STAT_DEFINED_FLAGS 0x01
#define MU_IMAP_STAT_PERMANENT_FLAGS 0x02
#define MU_IMAP_STAT_MESSAGE_COUNT 0x04
#define MU_IMAP_STAT_RECENT_COUNT 0x08
#define MU_IMAP_STAT_FIRST_UNSEEN 0x10
#define MU_IMAP_STAT_UIDNEXT 0x20
#define MU_IMAP_STAT_UIDVALIDITY 0x40
struct mu_imap_stat
{
int flags; /* Bitmap of what fields are filled */
int defined_flags; /* Flags defined for this mailbox */
int permanent_flags; /* Flags that can be changed permanently */
size_t message_count; /* Number of messages */
size_t recent_count; /* Number of recent messages */
size_t first_unseen; /* Sequence number of the first unseen message */
size_t uidnext; /* The next unique identifier value. */
unsigned long uidvalidity; /* The unique identifier validity value. */
};
int mu_imap_select (mu_imap_t imap, const char *mbox, int writable,
struct mu_imap_stat *ps);
#ifdef __cplusplus
}
#endif
......
......@@ -45,6 +45,9 @@ int mu_imapio_get_trace (mu_imapio_t io);
int mu_imapio_getbuf (mu_imapio_t io, char **pptr, size_t *psize);
int mu_imapio_reply_string (struct _mu_imapio *io, size_t start, char **pbuf);
int mu_imap_flag_to_attribute (const char *item, int *attr);
int mu_imap_format_flags (mu_stream_t str, int flags);
#ifdef __cplusplus
}
......
......@@ -44,6 +44,7 @@ enum mu_imap_client_state
MU_IMAP_LOGIN_RX,
MU_IMAP_LOGOUT_RX,
MU_IMAP_ID_RX,
MU_IMAP_SELECT_RX
};
enum mu_imap_response
......@@ -77,6 +78,10 @@ struct _mu_imap
mu_list_t capa;
mu_imapio_t io;
char *mbox_name; /* Name of the currently opened mailbox */
int mbox_writable:1; /* Is it open read/write? */
struct mu_imap_stat mbox_stat; /* Stats obtained from it */
};
enum imap_eltype
......@@ -151,6 +156,9 @@ int _mu_imap_response (mu_imap_t imap);
int _mu_imap_untagged_response_clear (mu_imap_t imap);
int _mu_imap_untagged_response_add (mu_imap_t imap);
int _mu_imap_list_element_is_string (struct imap_list_element *elt,
const char *str);
# ifdef __cplusplus
}
......
......@@ -19,6 +19,7 @@ noinst_LTLIBRARIES = libimapio.la
libimapio_la_SOURCES = \
create.c\
flags.c\
getline.c\
literal.c\
printf.c\
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2011 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Mailutils 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include <mailutils/types.h>
#include <mailutils/imapio.h>
#include <mailutils/attribute.h>
#include <mailutils/stream.h>
#include <mailutils/errno.h>
#include <mailutils/cstr.h>
#include <mailutils/sys/imapio.h>
static struct
{
char *name;
int flag;
}
_imap4_attrlist[] =
{
{ "\\Answered", MU_ATTRIBUTE_ANSWERED },
{ "\\Flagged", MU_ATTRIBUTE_FLAGGED },
{ "\\Deleted", MU_ATTRIBUTE_DELETED },
{ "\\Draft", MU_ATTRIBUTE_DRAFT },
{ "\\Seen", MU_ATTRIBUTE_SEEN|MU_ATTRIBUTE_READ },
};
static int _imap4_nattr = MU_ARRAY_SIZE (_imap4_attrlist);
int
mu_imap_flag_to_attribute (const char *item, int *attr)
{
int i;
if (mu_c_strcasecmp (item, "\\Recent") == 0)
{
*attr |= MU_ATTRIBUTE_RECENT;
return 0;
}
for (i = 0; i < _imap4_nattr; i++)
if (mu_c_strcasecmp (item, _imap4_attrlist[i].name) == 0)
{
*attr |= _imap4_attrlist[i].flag;
return 0;
}
return MU_ERR_NOENT;
}
int
mu_imap_format_flags (mu_stream_t str, int flags)
{
int i;
int delim = 0;
for (i = 0; i < _imap4_nattr; i++)
if ((flags & _imap4_attrlist[i].flag) == _imap4_attrlist[i].flag)
{
if (delim)
mu_stream_printf (str, " ");
mu_stream_printf (str, "%s", _imap4_attrlist[i].name);
delim = 1;
}
if (MU_ATTRIBUTE_IS_UNSEEN (flags))
{
if (delim)
mu_stream_printf (str, " ");
mu_stream_printf (str, "\\Recent");
}
return 0;
}
......@@ -41,6 +41,7 @@ libmu_imap_la_SOURCES = \
logout.c\
resplist.c\
response.c\
select.c\
state.c\
tag.c\
trace.c
......
......@@ -26,6 +26,8 @@
int
_mu_imap_seterrstr (mu_imap_t imap, const char *str, size_t len)
{
if (!imap)
return EINVAL;
if (len + 1 > imap->errsize)
{
char *p = realloc (imap->errstr, len + 1);
......@@ -42,13 +44,18 @@ _mu_imap_seterrstr (mu_imap_t imap, const char *str, size_t len)
void
_mu_imap_clrerrstr (mu_imap_t imap)
{
if (imap->errstr)
if (imap && imap->errstr)
imap->errstr[0] = 0;
}
int
mu_imap_strerror (mu_imap_t imap, const char **pstr)
{
if (!imap)
{
*pstr = "(imap not initialized)";
return EINVAL;
}
if (imap->errstr)
{
*pstr = imap->errstr;
......
......@@ -105,11 +105,11 @@ parse_id_reply (mu_imap_t imap, mu_assoc_t *passoc)
return st.ret;
}
int
mu_imap_id (mu_imap_t imap, char **idenv, mu_assoc_t *passoc)
{
int status;
char *p;
if (imap == NULL)
return EINVAL;
......@@ -171,6 +171,11 @@ mu_imap_id (mu_imap_t imap, char **idenv, mu_assoc_t *passoc)
case MU_IMAP_BAD:
status = MU_ERR_BADREPLY;
if (mu_imapio_reply_string (imap->io, 2, &p) == 0)
{
_mu_imap_seterrstr (imap, p, strlen (p));
free (p);
}
break;
}
imap->state = MU_IMAP_CONNECTED;
......
......@@ -18,7 +18,8 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <mailutils/errno.h>
#include <mailutils/stream.h>
#include <mailutils/sys/imap.h>
......@@ -27,6 +28,7 @@ int
mu_imap_login (mu_imap_t imap, const char *user, const char *pass)
{
int status;
char *p;
if (imap == NULL)
return EINVAL;
......@@ -67,6 +69,11 @@ mu_imap_login (mu_imap_t imap, const char *user, const char *pass)
case MU_IMAP_BAD:
status = MU_ERR_BADREPLY;
if (mu_imapio_reply_string (imap->io, 2, &p) == 0)
{
_mu_imap_seterrstr (imap, p, strlen (p));
free (p);
}
break;
}
imap->state = MU_IMAP_CONNECTED;
......
......@@ -254,3 +254,13 @@ _mu_imap_untagged_response_add (mu_imap_t imap)
mu_list_append (imap->untagged_resp, elt);
return 0;
}
int
_mu_imap_list_element_is_string (struct imap_list_element *elt,
const char *str)
{
if (elt->type != imap_eltype_string)
return 0;
return strcmp (elt->v.string, str) == 0;
}
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2010, 2011 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/assoc.h>
#include <mailutils/stream.h>
#include <mailutils/imap.h>
#include <mailutils/sys/imap.h>
static int
_collect_flags (void *item, void *data)
{
struct imap_list_element *elt = item;
int *args = data;
if (elt->type == imap_eltype_string)
mu_imap_flag_to_attribute (elt->v.string, args);
return 0;
}
static int
_parse_stat (void *item, void *data)
{
struct imap_list_element *response = item;
mu_imap_t imap = data;
struct imap_list_element *elt;
size_t count;
int rc;
char *p;
if (response->type != imap_eltype_list)
return 0;
mu_list_count (response->v.list, &count);
rc = mu_list_get (response->v.list, 0, (void*) &elt);
if (rc)
return rc;
if (_mu_imap_list_element_is_string (elt, "OK"))
{
struct imap_list_element *arg;
if (count < 3)
return 0; /* ignore the line */
rc = mu_list_get (response->v.list, 1, (void*) &elt);
if (rc)
return rc;
rc = mu_list_get (response->v.list, 2, (void*) &arg);
if (rc)
return rc;
if (_mu_imap_list_element_is_string (elt, "[UIDVALIDITY"))
{
if (arg->type != imap_eltype_string)
return 0;
imap->mbox_stat.uidvalidity = strtoul (arg->v.string, &p, 10);
if (*p == ']')
imap->mbox_stat.flags |= MU_IMAP_STAT_UIDVALIDITY;
}
else if (_mu_imap_list_element_is_string (elt, "[UIDNEXT"))
{
if (arg->type != imap_eltype_string)
return 0;
imap->mbox_stat.uidnext = strtoul (arg->v.string, &p, 10);
if (*p == ']')
imap->mbox_stat.flags |= MU_IMAP_STAT_UIDNEXT;
}
else if (_mu_imap_list_element_is_string (elt, "[UNSEEN"))
{
if (arg->type != imap_eltype_string)
return 0;
imap->mbox_stat.first_unseen = strtoul (arg->v.string, &p, 10);
if (*p == ']')
imap->mbox_stat.flags |= MU_IMAP_STAT_FIRST_UNSEEN;
}
else if (_mu_imap_list_element_is_string (elt, "[PERMANENTFLAGS"))
{
if (arg->type != imap_eltype_list)
return 0;
mu_list_do (arg->v.list, _collect_flags,
&imap->mbox_stat.permanent_flags);
imap->mbox_stat.flags |= MU_IMAP_STAT_PERMANENT_FLAGS;
}
}
else if (_mu_imap_list_element_is_string (elt, "FLAGS"))
{
struct imap_list_element *arg;
rc = mu_list_get (response->v.list, 1, (void*) &arg);
if (rc)
return 0;
if (arg->type != imap_eltype_list)
return 0;
mu_list_do (arg->v.list, _collect_flags, &imap->mbox_stat.defined_flags);
imap->mbox_stat.flags |= MU_IMAP_STAT_DEFINED_FLAGS;
}
else if (count == 2)
{
struct imap_list_element *arg;
rc = mu_list_get (response->v.list, 1, (void*) &arg);
if (rc)
return rc;
if (_mu_imap_list_element_is_string (arg, "EXISTS"))
{
imap->mbox_stat.message_count = strtoul (elt->v.string, &p, 10);
if (*p == 0)
imap->mbox_stat.flags |= MU_IMAP_STAT_MESSAGE_COUNT;
}
else if (_mu_imap_list_element_is_string (arg, "RECENT"))
{
imap->mbox_stat.recent_count = strtoul (elt->v.string, &p, 10);
if (*p == 0)
imap->mbox_stat.flags |= MU_IMAP_STAT_RECENT_COUNT;
}
}
return 0;
}
int
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;
if (!imap->io)
return MU_ERR_NO_TRANSPORT;
if (imap->state != MU_IMAP_CONNECTED)
return MU_ERR_SEQ;
if (imap->imap_state != MU_IMAP_STATE_AUTH &&
imap->imap_state != MU_IMAP_STATE_SELECTED)
return MU_ERR_SEQ;
if (!mbox)
{
if (imap->imap_state == MU_IMAP_STATE_SELECTED)
{
if (ps)
*ps = imap->mbox_stat;
return 0;
}
return MU_ERR_INFO_UNAVAILABLE;
}
if (imap->mbox_name && strcmp (imap->mbox_name, mbox) == 0
&& writable == imap->mbox_writable)
{
if (ps)
*ps = imap->mbox_stat;
return 0;
}
switch (imap->state)
{
case MU_IMAP_CONNECTED:
status = _mu_imap_tag_next (imap);
MU_IMAP_CHECK_EAGAIN (imap, status);
status = mu_imapio_printf (imap->io, "%s %s %s\r\n",
imap->tag_str,
writable ? "SELECT" : "EXAMINE",
mbox);
MU_IMAP_CHECK_ERROR (imap, status);
MU_IMAP_FCLR (imap, MU_IMAP_RESP);
imap->state = MU_IMAP_SELECT_RX;
case MU_IMAP_SELECT_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_SELECTED;
free (imap->mbox_name);
imap->mbox_name = strdup (mbox);
if (!imap->mbox_name)
{
imap->state = MU_IMAP_ERROR;
return errno;
}
imap->mbox_writable = writable;
memset (&imap->mbox_stat, 0, sizeof (imap->mbox_stat));
mu_list_do (imap->untagged_resp, _parse_stat, imap);
if (ps)
*ps = imap->mbox_stat;
break;
case MU_IMAP_NO:
status = EACCES;
break;
case MU_IMAP_BAD:
status = MU_ERR_BADREPLY;
if (mu_imapio_reply_string (imap->io, 2, &p) == 0)
{
_mu_imap_seterrstr (imap, p, strlen (p));
free (p);
}
break;
}
imap->state = MU_IMAP_CONNECTED;
break;
default:
status = EINPROGRESS;
}
return status;
}
......@@ -25,6 +25,7 @@
#include <netinet/in.h>
#include <mailutils/mailutils.h>
#include <mailutils/imap.h>
#include <mailutils/imapio.h>
#include "mu.h"
#include "argp.h"
#include "xalloc.h"
......@@ -454,6 +455,71 @@ com_id (int argc, char **argv)
}
return status;
}
static void
print_imap_stats (struct mu_imap_stat *st)
{
if (st->flags & MU_IMAP_STAT_DEFINED_FLAGS)
{
mu_printf (_("Flags defined: "));
mu_imap_format_flags (mu_strout, st->defined_flags);
mu_printf ("\n");
}
if (st->flags & MU_IMAP_STAT_PERMANENT_FLAGS)
{
mu_printf (_("Flags permanent: "));
mu_imap_format_flags (mu_strout, st->permanent_flags);
mu_printf ("\n");
}
if (st->flags & MU_IMAP_STAT_MESSAGE_COUNT)
mu_printf (_("Total messages: %lu\n"), (unsigned long) st->message_count);
if (st->flags & MU_IMAP_STAT_RECENT_COUNT)
mu_printf (_("Recent messages: %lu\n"), (unsigned long) st->recent_count);
if (st->flags & MU_IMAP_STAT_FIRST_UNSEEN)
mu_printf (_("First unseen message: %lu\n"),
(unsigned long) st->first_unseen);
if (st->flags & MU_IMAP_STAT_UIDNEXT)
mu_printf (_("Next UID: %lu\n"), (unsigned long) st->uidnext);
if (st->flags & MU_IMAP_STAT_UIDVALIDITY)
mu_printf (_("UID validity: %lu\n"), st->uidvalidity);
}
static int
select_mbox (int argc, char **argv, int writable)
{
int status;
struct mu_imap_stat st;
status = mu_imap_select (imap, argv[1], writable, &st);
if (status == 0)
{
print_imap_stats (&st);
imap_prompt_env ();
}
else
{
const char *str;
mu_error ("select failed: %s", mu_strerror (status));
if (mu_imap_strerror (imap, &str) == 0)
mu_error ("server reply: %s", str);
}
return 0;
}
static int
com_select (int argc, char **argv)
{
return select_mbox (argc, argv, 1);
}
static int
com_examine (int argc, char **argv)
{
return select_mbox (argc, argv, 0);
}
struct mutool_command imap_comtab[] = {
......@@ -481,6 +547,12 @@ struct mutool_command imap_comtab[] = {
{ "id", 1, -1, com_id,
N_("[-test KW] [ARG [ARG...]]"),
N_("send ID command") },
{ "select", 1, 2, com_select,
N_("MBOX"),
N_("select a mailbox") },
{ "examine", 1, 2, com_examine,
N_("MBOX"),
N_("examine a mailbox") },
{ "quit", 1, 1, com_logout,
NULL,
N_("same as `logout'") },
......