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)
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 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/address.h>
#include <mailutils/cstr.h>
#include <mailutils/cctype.h>
#include <mailutils/imap.h>
#include <mailutils/sys/imap.h>
int
mu_imap_fetch (mu_imap_t imap, const char *msgset, const char *items)
{
int status;
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_SELECTED)
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_imapio_printf (imap->io, "%s FETCH %s %s\r\n",
imap->tag_str, msgset, items);
MU_IMAP_CHECK_ERROR (imap, status);
MU_IMAP_FCLR (imap, MU_IMAP_RESP);
imap->state = MU_IMAP_FETCH_RX;
case MU_IMAP_FETCH_RX:
status = _mu_imap_response (imap, NULL, NULL);
MU_IMAP_CHECK_EAGAIN (imap, status);
switch (imap->resp_code)
{
case MU_IMAP_OK:
status = 0;
break;
case MU_IMAP_NO:
status = MU_ERR_FAILURE;
break;
case MU_IMAP_BAD:
status = MU_ERR_BADREPLY;
break;
}
imap->state = MU_IMAP_CONNECTED;
break;
default:
status = EINPROGRESS;
}
return status;
}
static void
_free_fetch_response (void *ptr)
{
union mu_imap_fetch_response *resp = ptr;
switch (resp->type)
{
case MU_IMAP_FETCH_BODY:
free (resp->body.partv);
free (resp->body.key);
free (resp->body.text);
break;
case MU_IMAP_FETCH_BODYSTRUCTURE:
/* FIXME */
break;
case MU_IMAP_FETCH_ENVELOPE:
free (resp->envelope.subject);
mu_address_destroy (&resp->envelope.from);
mu_address_destroy (&resp->envelope.sender);
mu_address_destroy (&resp->envelope.reply_to);
mu_address_destroy (&resp->envelope.to);
mu_address_destroy (&resp->envelope.cc);
mu_address_destroy (&resp->envelope.bcc);
free (resp->envelope.in_reply_to);
free (resp->envelope.message_id);
break;
case MU_IMAP_FETCH_FLAGS:
case MU_IMAP_FETCH_INTERNALDATE:
case MU_IMAP_FETCH_RFC822_SIZE:
case MU_IMAP_FETCH_UID:
break;
}
free (resp);
}
static int
alloc_response (union mu_imap_fetch_response **resp, int type)
{
static size_t sizetab[] = {
sizeof (struct mu_imap_fetch_body),
sizeof (struct mu_imap_fetch_bodystructure),
sizeof (struct mu_imap_fetch_envelope),
sizeof (struct mu_imap_fetch_flags),
sizeof (struct mu_imap_fetch_internaldate),
sizeof (struct mu_imap_fetch_rfc822_size),
sizeof (struct mu_imap_fetch_uid)
};
union mu_imap_fetch_response *p;
if (type < 0 || type >= MU_ARRAY_SIZE (sizetab))
return EINVAL;
p = calloc (1, sizetab[type]);
if (!p)
return ENOMEM;
p->type = type;
*resp = p;
return 0;
}
static int
_uid_mapper (struct imap_list_element **elt,
union mu_imap_fetch_response **return_resp)
{
union mu_imap_fetch_response *resp;
int rc;
char *p;
size_t uid;
if (elt[1]->type != imap_eltype_string)
return MU_ERR_FAILURE;
uid = strtoul (elt[1]->v.string, &p, 0);
if (*p)
return MU_ERR_FAILURE;
rc = alloc_response (&resp, MU_IMAP_FETCH_UID);
if (rc)
return rc;
resp->uid.uid = uid;
*return_resp = resp;
return 0;
}
static int
_size_mapper (struct imap_list_element **elt,
union mu_imap_fetch_response **return_resp)
{
union mu_imap_fetch_response *resp;
int rc;
char *p;
size_t size;
if (elt[1]->type != imap_eltype_string)
return MU_ERR_FAILURE;
size = strtoul (elt[1]->v.string, &p, 0);
if (*p)
return MU_ERR_FAILURE;
rc = alloc_response (&resp, MU_IMAP_FETCH_RFC822_SIZE);
if (rc)
return rc;
resp->rfc822_size.size = size;
*return_resp = resp;
return 0;
}
static int
_body_mapper (struct imap_list_element **elt,
union mu_imap_fetch_response **return_resp)
{
union mu_imap_fetch_response *resp;
int rc;
char *section, *p;
size_t partc = 0;
size_t *partv = NULL;
if (elt[1]->type != imap_eltype_string)
return MU_ERR_FAILURE;
rc = alloc_response (&resp, MU_IMAP_FETCH_BODY);
if (rc)
return rc;
section = strchr (elt[0]->v.string, '[');
if (section)
{
p = section;
while (1)
{
p = strchr (p, '.');
if (*p)
{
p++;
if (mu_isdigit (p[1]))
{
partc++;
continue;
}
}
break;
}
}
if (partc)
{
size_t i;
partv = calloc (partc, sizeof (partv[0]));
for (i = 0, p = section; i < partc; i++)
{
partv[i] = strtoul (p, &p, 10);
p++;
}
}
resp->body.partc = partc;
resp->body.partv = partv;
if (p)
{
size_t len = strlen (p);
resp->body.key = malloc (len);
if (!resp->body.key)
{
free (resp->body.partv);
free (resp);
return ENOMEM;
}
len--;
memcpy (resp->body.key, p, len);
resp->body.key[len] = 0;
}
resp->body.text = strdup (elt[1]->v.string);
if (!resp->body.text)
{
free (resp->body.key);
free (resp->body.partv);
free (resp);
return ENOMEM;
}
*return_resp = resp;
return 0;
}
static int
_rfc822_mapper (const char *key, struct imap_list_element *elt,
union mu_imap_fetch_response **return_resp)
{
union mu_imap_fetch_response *resp;
int rc;
if (elt->type != imap_eltype_string)
return MU_ERR_FAILURE;
rc = alloc_response (&resp, MU_IMAP_FETCH_BODY);
if (rc)
return rc;
resp->body.partc = 0;
resp->body.partv = NULL;
resp->body.key = strdup (key);
if (!resp->body.key)
{
free (resp);
return ENOMEM;
}
resp->body.text = strdup (elt->v.string);
if (!resp->body.text)
{
free (resp->body.key);
free (resp->body.partv);
free (resp);
return ENOMEM;
}
*return_resp = resp;
return 0;
}
static int
_rfc822_header_mapper (struct imap_list_element **elt,
union mu_imap_fetch_response **return_resp)
{
return _rfc822_mapper ("HEADER", elt[1], return_resp);
}
static int
_rfc822_text_mapper (struct imap_list_element **elt,
union mu_imap_fetch_response **return_resp)
{
return _rfc822_mapper ("TEXT", elt[1], return_resp);
}
static int
_flags_mapper (struct imap_list_element **elt,
union mu_imap_fetch_response **return_resp)
{
union mu_imap_fetch_response *resp;
int rc;
int flags;
if (elt[1]->type != imap_eltype_list)
return MU_ERR_FAILURE;
if (_mu_imap_collect_flags (elt[1], &flags))
return MU_ERR_FAILURE;
rc = alloc_response (&resp, MU_IMAP_FETCH_FLAGS);
if (rc)
return rc;
resp->flags.flags = flags;
*return_resp = resp;
return 0;
}
static int
_date_mapper (struct imap_list_element **elt,
union mu_imap_fetch_response **return_resp)
{
struct mu_imap_fetch_internaldate idate;
union mu_imap_fetch_response *resp;
int rc;
const char *p;
if (elt[1]->type != imap_eltype_string)
return MU_ERR_FAILURE;
p = elt[1]->v.string;
if (mu_parse_imap_date_time (&p, &idate.tm, &idate.tz))
return MU_ERR_FAILURE;
rc = alloc_response (&resp, MU_IMAP_FETCH_INTERNALDATE);
if (rc)
return rc;
resp->internaldate = idate;
*return_resp = resp;
return 0;
}
struct fill_env
{
struct mu_imap_fetch_envelope *envelope;
size_t n;
};
enum env_index
{
env_date,
env_subject,
env_from,
env_sender,
env_reply_to,
env_to,
env_cc,
env_bcc,
env_in_reply_to,
env_message_id
};
static int
elt_to_string (struct imap_list_element *elt, char **pstr)
{
char *p;
if (elt->type != imap_eltype_string)
return EINVAL;
if (mu_c_strcasecmp (elt->v.string, "NIL") == 0)
p = NULL;
else
{
p = strdup (elt->v.string);
if (!p)
return ENOMEM;
}
*pstr = p;
return 0;
}
struct addr_env
{
mu_address_t addr;
size_t n;
};
static int
_fill_subaddr (void *item, void *data)
{
struct addr_env *addr_env = data;
struct imap_list_element *elt = item, *arg;
const char *domain = NULL, *local = NULL, *personal = NULL;
if (elt->type != imap_eltype_list)
return 0;
arg = _mu_imap_list_at (elt->v.list, 0);
if (arg && arg->type == imap_eltype_string && strcmp (arg->v.string, "NIL"))
personal = arg->v.string;
arg = _mu_imap_list_at (elt->v.list, 2);
if (arg && arg->type == imap_eltype_string && strcmp (arg->v.string, "NIL"))
local = arg->v.string;
arg = _mu_imap_list_at (elt->v.list, 3);
if (arg && arg->type == imap_eltype_string && strcmp (arg->v.string, "NIL"))
domain = arg->v.string;
if (domain && local)
{
if (!addr_env->addr)
{
int rc = mu_address_create_null (&addr_env->addr);
if (rc)
return rc;
}
mu_address_set_local_part (addr_env->addr, addr_env->n, local);
mu_address_set_domain (addr_env->addr, addr_env->n, domain);
mu_address_set_personal (addr_env->addr, addr_env->n, personal);
addr_env->n++;
}
return 0;
}
static int
elt_to_address (struct imap_list_element *elt, mu_address_t *paddr)
{
if (elt->type != imap_eltype_list)
{
if (mu_c_strcasecmp (elt->v.string, "NIL") == 0)
*paddr = NULL;
else
return EINVAL;
}
else
{
struct addr_env addr_env;
addr_env.addr = NULL;
addr_env.n = 1;
mu_list_foreach (elt->v.list, _fill_subaddr, &addr_env);
*paddr = addr_env.addr;
}
return 0;
}
static int
_fill_response (void *item, void *data)
{
int rc;
struct imap_list_element *elt = item;
struct fill_env *env = data;
switch (env->n++)
{
case env_date:
if (elt->type != imap_eltype_string)
rc = MU_ERR_FAILURE;
else
{
char const *p = elt->v.string;
if (mu_parse_imap_date_time (&p,
&env->envelope->date,
&env->envelope->tz))
rc = MU_ERR_FAILURE;
else
rc = 0;
}
break;
case env_subject:
rc = elt_to_string (elt, &env->envelope->subject);
break;
case env_from:
rc = elt_to_address (elt, &env->envelope->from);
break;
case env_sender:
rc = elt_to_address (elt, &env->envelope->sender);
break;
case env_reply_to:
rc = elt_to_address (elt, &env->envelope->reply_to);
break;
case env_to:
rc = elt_to_address (elt, &env->envelope->to);
break;
case env_cc:
rc = elt_to_address (elt, &env->envelope->cc);
break;
case env_bcc:
rc = elt_to_address (elt, &env->envelope->bcc);
break;
case env_in_reply_to:
rc = elt_to_string (elt, &env->envelope->in_reply_to);
break;
case env_message_id:
rc = elt_to_string (elt, &env->envelope->message_id);
break;
}
return rc;
}
static int
_envelope_mapper (struct imap_list_element **elt,
union mu_imap_fetch_response **return_resp)
{
union mu_imap_fetch_response *resp;
int rc;
struct fill_env env;
if (elt[1]->type != imap_eltype_list)
return MU_ERR_FAILURE;
rc = alloc_response (&resp, MU_IMAP_FETCH_ENVELOPE);
if (rc)
return rc;
env.envelope = &resp->envelope;
env.n = 0;
mu_list_foreach (elt[1]->v.list, _fill_response, &env);
*return_resp = resp;
return 0;
}
struct mapper_tab
{
char *name;
size_t size;
int prefix;
int (*mapper) (struct imap_list_element **, union mu_imap_fetch_response **);
};
static struct mapper_tab mapper_tab[] = {
#define S(s) s, (sizeof (s) - 1)
{ S("BODYSTRUCTURE"), 0, },
{ S("BODY["), 1, _body_mapper },
{ S("BODY"), 0, _body_mapper },
{ S("ENVELOPE"), 0, _envelope_mapper },
{ S("FLAGS"), 0, _flags_mapper },
{ S("INTERNALDATE"), 0, _date_mapper },
{ S("RFC822"), 0, _body_mapper},
{ S("RFC822.HEADER"), 0, _rfc822_header_mapper },
{ S("RFC822.SIZE"), 0, _size_mapper },
{ S("RFC822.TEXT"), 0, _rfc822_text_mapper },
{ S("UID"), 0, _uid_mapper },
#undef S
{ NULL }
};
static int
_fetch_mapper (void **itmv, size_t itmc, void *call_data)
{
int *status = call_data;
struct mapper_tab *mt;
struct imap_list_element *elt[2];
char *kw;
size_t kwlen;
union mu_imap_fetch_response *resp;
elt[0] = itmv[0];
elt[1] = itmv[1];
if (elt[0]->type != imap_eltype_string)
{
*status = MU_ERR_FAILURE;
return MU_LIST_MAP_STOP|MU_LIST_MAP_SKIP;
}
kw = elt[0]->v.string;
kwlen = strlen (kw);
for (mt = mapper_tab; mt->name; mt++)
{
if (mt->prefix)
{
if (mt->size >= kwlen && memcmp (mt->name, kw, kwlen) == 0)
break;
}
else if (mt->size == kwlen && memcmp (mt->name, kw, kwlen) == 0)
break;
}
if (!mt->name)
{
mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_TRACE9,
("ignoring unknown FETCH item '%s'", kw));
return MU_LIST_MAP_SKIP;
}
if (mt->mapper)
{
int rc = mt->mapper (elt, &resp);
if (rc == 0)
{
itmv[0] = resp;
return MU_LIST_MAP_OK;
}
else
{
*status = rc;
return MU_LIST_MAP_STOP|MU_LIST_MAP_SKIP;
}
}
return MU_LIST_MAP_SKIP;
}
int
_mu_imap_parse_fetch_response (mu_list_t input, mu_list_t *result_list)
{
mu_list_t result;
int status;
status = mu_list_create (&result);
if (status)
{
mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR,
("mu_list_create: %s", mu_strerror (status)));
return 1;
}
mu_list_set_destroy_item (result, _free_fetch_response);
mu_list_map (input, _fetch_mapper, &status, 2, &result);
if (status)
mu_list_destroy (&result);
else
*result_list = result;
return status;
}
......@@ -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;
......