/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2010-2021 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 . */
#if defined(HAVE_CONFIG_H)
# include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "mu.h"
char imap_docstring[] = N_("IMAP4 client shell");
static mu_imap_t imap;
static int uid_mode;
static enum mu_imap_session_state
current_imap_state ()
{
int state;
if (imap == NULL)
state = MU_IMAP_SESSION_INIT;
else
state = mu_imap_session_state (imap);
return state;
}
static void
imap_set_verbose ()
{
if (imap)
{
if (QRY_VERBOSE ())
mu_imap_trace (imap, MU_IMAP_TRACE_SET);
else
mu_imap_trace (imap, MU_IMAP_TRACE_CLR);
}
}
void
imap_set_verbose_mask ()
{
if (imap)
{
mu_imap_trace_mask (imap, QRY_VERBOSE_MASK (MU_XSCRIPT_SECURE)
? MU_IMAP_TRACE_SET : MU_IMAP_TRACE_CLR,
MU_XSCRIPT_SECURE);
mu_imap_trace_mask (imap, QRY_VERBOSE_MASK (MU_XSCRIPT_PAYLOAD)
? MU_IMAP_TRACE_SET : MU_IMAP_TRACE_CLR,
MU_XSCRIPT_PAYLOAD);
}
}
static int
com_verbose (int argc, char **argv)
{
return shell_verbose (argc, argv,
imap_set_verbose, imap_set_verbose_mask);
}
static mu_msgset_t
parse_msgset (const char *arg)
{
int status;
mu_msgset_t msgset;
char *p;
status = mu_msgset_create (&msgset, NULL, MU_MSGSET_NUM);
if (status)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_msgset_create", NULL, status);
return NULL;
}
status = mu_msgset_parse_imap (msgset, MU_MSGSET_NUM, arg, &p);
if (status)
{
mu_error (_("failed to parse message set near \"%s\": %s"),
p, mu_strerror (status));
mu_msgset_destroy (&msgset);
}
return msgset;
}
static void
report_failure (const char *what, int status)
{
const char *str;
mu_error (_("%s failed: %s"), what, mu_strerror (status));
if (mu_imap_strerror (imap, &str) == 0)
mu_error (_("server reply: %s"), str);
}
static int connect_argc;
static char **connect_argv;
#define host connect_argv[0]
static char *username;
static void
imap_prompt_env (void)
{
mu_assoc_t assoc = mutool_shell_prompt_assoc ();
enum mu_imap_session_state state = current_imap_state ();
const char *p;
if (state >= MU_IMAP_SESSION_AUTH && username)
mu_assoc_install (assoc, "user", username);
if (connect_argv)
mu_assoc_install (assoc, "host", host);
if (mu_imap_session_state_str (state, &p) == 0)
mu_assoc_install (assoc, "status", (void*) p);
}
/* Callbacks */
static void
imap_preauth_callback (void *data, int code, size_t sdat, void *pdat)
{
const char *text = pdat;
if (text)
mu_diag_output (MU_DIAG_INFO, _("session authenticated: %s"), text);
else
mu_diag_output (MU_DIAG_INFO, _("session authenticated"));
}
static void
imap_bye_callback (void *data, int code, size_t sdat, void *pdat)
{
const char *text = pdat;
if (text)
mu_diag_output (MU_DIAG_INFO, _("server is closing connection: %s"), text);
else
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);
}
/* Fetch callback */
static void
format_email (mu_stream_t str, const char *name, mu_address_t addr)
{
mu_stream_printf (str, " %s = ", name);
if (!addr)
mu_stream_printf (str, "NIL");
else
mu_stream_format_address (str, addr);
mu_stream_printf (str, "\n");
}
static void
format_date (mu_stream_t str, char *name,
struct tm *tm, struct mu_timezone *tz)
{
char date[128];
strftime (date, sizeof (date), "%a %b %e %H:%M:%S", tm);
mu_stream_printf (str, " %s = %s", name, date);
if (tz->tz_name)
mu_stream_printf (str, " %s", tz->tz_name);
else
{
int off = tz->utc_offset;
if (off < 0)
{
mu_stream_printf (str, " -");
off = - off;
}
else
mu_stream_printf (str, " +");
off /= 60;
mu_stream_printf (str, "%02d%02d", off / 60, off % 60);
}
mu_stream_printf (str, "\n");
}
static void
print_param (mu_stream_t ostr, const char *prefix, mu_assoc_t assoc,
int indent)
{
mu_iterator_t itr;
int i;
mu_stream_printf (ostr, "%*s%s:\n", indent, "", prefix);
indent += 4;
if (mu_assoc_get_iterator (assoc, &itr))
return;
for (i = 0, mu_iterator_first (itr);
!mu_iterator_is_done (itr);
i++, mu_iterator_next (itr))
{
const char *name;
struct mu_mime_param *p;
mu_iterator_current_kv (itr, (const void **)&name, (void**)&p);
mu_stream_printf (ostr, "%*s%d: %s=%s\n", indent, "", i, name, p->value);
}
mu_iterator_destroy (&itr);
}
struct print_data
{
mu_stream_t ostr;
int num;
int level;
};
static void print_bs (mu_stream_t ostr,
struct mu_bodystructure *bs, int level);
static int
print_item (void *item, void *data)
{
struct mu_bodystructure *bs = item;
struct print_data *pd = data;
mu_stream_printf (pd->ostr, "%*sPart #%d\n", (pd->level-1) << 2, "",
pd->num);
print_bs (pd->ostr, bs, pd->level);
++pd->num;
return 0;
}
static void
print_address (mu_stream_t ostr, const char *title, mu_address_t addr,
int indent)
{
mu_stream_printf (ostr, "%*s%s: ", indent, "", title);
mu_stream_format_address (mu_strout, addr);
mu_stream_printf (ostr, "\n");
}
static void
print_imapenvelope (mu_stream_t ostr, struct mu_imapenvelope *env, int level)
{
int indent = (level << 2);
mu_stream_printf (ostr, "%*sEnvelope:\n", indent, "");
indent += 4;
mu_stream_printf (ostr, "%*sTime: ", indent, "");
mu_c_streamftime (mu_strout, "%c%n", &env->date, &env->tz);
mu_stream_printf (ostr, "%*sSubject: %s\n", indent, "",
mu_prstr (env->subject));
print_address (ostr, "From", env->from, indent);
print_address (ostr, "Sender", env->sender, indent);
print_address (ostr, "Reply-to", env->reply_to, indent);
print_address (ostr, "To", env->to, indent);
print_address (ostr, "Cc", env->cc, indent);
print_address (ostr, "Bcc", env->bcc, indent);
mu_stream_printf (ostr, "%*sIn-Reply-To: %s\n", indent, "",
mu_prstr (env->in_reply_to));
mu_stream_printf (ostr, "%*sMessage-ID: %s\n", indent, "",
mu_prstr (env->message_id));
}
static void
print_bs (mu_stream_t ostr, struct mu_bodystructure *bs, int level)
{
int indent = level << 2;
mu_stream_printf (ostr, "%*sbody_type=%s\n", indent, "",
mu_prstr (bs->body_type));
mu_stream_printf (ostr, "%*sbody_subtype=%s\n", indent, "",
mu_prstr (bs->body_subtype));
print_param (ostr, "Parameters", bs->body_param, indent);
mu_stream_printf (ostr, "%*sbody_id=%s\n", indent, "",
mu_prstr (bs->body_id));
mu_stream_printf (ostr, "%*sbody_descr=%s\n", indent, "",
mu_prstr (bs->body_descr));
mu_stream_printf (ostr, "%*sbody_encoding=%s\n", indent, "",
mu_prstr (bs->body_encoding));
mu_stream_printf (ostr, "%*sbody_size=%lu\n", indent, "",
(unsigned long) bs->body_size);
/* Optional */
mu_stream_printf (ostr, "%*sbody_md5=%s\n", indent, "",
mu_prstr (bs->body_md5));
mu_stream_printf (ostr, "%*sbody_disposition=%s\n", indent, "",
mu_prstr (bs->body_disposition));
print_param (ostr, "Disposition Parameters", bs->body_disp_param, indent);
mu_stream_printf (ostr, "%*sbody_language=%s\n", indent, "",
mu_prstr (bs->body_language));
mu_stream_printf (ostr, "%*sbody_location=%s\n", indent, "",
mu_prstr (bs->body_location));
mu_stream_printf (ostr, "%*sType ", indent, "");
switch (bs->body_message_type)
{
case mu_message_other:
mu_stream_printf (ostr, "mu_message_other\n");
break;
case mu_message_text:
mu_stream_printf (ostr, "mu_message_text:\n%*sbody_lines=%lu\n",
indent + 4, "",
(unsigned long) bs->v.text.body_lines);
break;
case mu_message_rfc822:
mu_stream_printf (ostr, "mu_message_rfc822:\n%*sbody_lines=%lu\n",
indent + 4, "",
(unsigned long) bs->v.rfc822.body_lines);
print_imapenvelope (ostr, bs->v.rfc822.body_env, level + 1);
print_bs (ostr, bs->v.rfc822.body_struct, level + 1);
break;
case mu_message_multipart:
{
struct print_data pd;
pd.ostr = ostr;
pd.num = 0;
pd.level = level + 1;
mu_stream_printf (ostr, "mu_message_multipart:\n");
mu_list_foreach (bs->v.multipart.body_parts, print_item, &pd);
}
}
}
static int
fetch_response_printer (void *item, void *data)
{
union mu_imap_fetch_response *resp = item;
mu_stream_t str = data;
switch (resp->type)
{
case MU_IMAP_FETCH_BODY:
mu_stream_printf (str, "BODY [");
if (resp->body.partv)
{
size_t i;
for (i = 0; i < resp->body.partc; i++)
mu_stream_printf (str, "%lu.",
(unsigned long) resp->body.partv[i]);
}
if (resp->body.section)
mu_stream_printf (str, "%s", resp->body.section);
mu_stream_printf (str, "]");
mu_stream_printf (str, "\nBEGIN\n%s\nEND\n", resp->body.text);
break;
case MU_IMAP_FETCH_BODYSTRUCTURE:
/* FIXME */
mu_stream_printf (str, "BODYSTRUCTURE:\nBEGIN\n");
print_bs (str, resp->bodystructure.bs, 0);
mu_stream_printf (str, "END\n");
break;
case MU_IMAP_FETCH_ENVELOPE:
{
struct mu_imapenvelope *env = resp->envelope.imapenvelope;
mu_stream_printf (str, "ENVELOPE:\n");
format_date (str, "date", &env->date, &env->tz);
mu_stream_printf (str, " subject = %s\n",
env->subject ?
env->subject : "NIL");
format_email (str, "from", env->from);
format_email (str, "sender", env->sender);
format_email (str, "reply-to", env->reply_to);
format_email (str, "to", env->to);
format_email (str, "cc", env->cc);
format_email (str, "bcc", env->bcc);
mu_stream_printf (str, " in-reply-to = %s\n",
env->in_reply_to ?
env->in_reply_to : "NIL");
mu_stream_printf (str, " message-id = %s\n",
env->message_id ?
env->message_id : "NIL");
}
break;
case MU_IMAP_FETCH_FLAGS:
mu_stream_printf (str, " flags = ");
mu_imap_format_flags (str, resp->flags.flags, 1);
mu_stream_printf (str, "\n");
break;
case MU_IMAP_FETCH_INTERNALDATE:
format_date (str, "internaldate", &resp->internaldate.tm,
&resp->internaldate.tz);
break;
case MU_IMAP_FETCH_RFC822_SIZE:
mu_stream_printf (str, " size = %lu\n",
(unsigned long) resp->rfc822_size.size);
break;
case MU_IMAP_FETCH_UID:
mu_stream_printf (str, " UID = %lu\n",
(unsigned long) resp->uid.uid);
}
return 0;
}
static void
imap_fetch_callback (void *data, int code, size_t sdat, void *pdat)
{
mu_stream_t str = data;
mu_list_t list = pdat;
mu_stream_printf (str, "Message #%lu:\n", (unsigned long) sdat);
mu_list_foreach (list, fetch_response_printer, str);
mu_stream_printf (str, "\n\n");
}
static int
com_uid (int argc, char **argv)
{
int bv;
if (argc == 1)
mu_printf ("%s\n", uid_mode ? _("UID is on") : _("UID is off"));
else if (get_bool (argv[1], &bv) == 0)
uid_mode = bv;
else
mu_error (_("invalid boolean value"));
return 0;
}
static int
com_disconnect (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
{
if (imap)
{
mu_imap_disconnect (imap);
mu_imap_destroy (&imap);
mu_argcv_free (connect_argc, connect_argv);
connect_argc = 0;
connect_argv = NULL;
imap_prompt_env ();
}
return 0;
}
static int
com_connect (int argc, char **argv)
{
int status;
int tls = 0;
int i = 1;
enum mu_imap_session_state state;
for (i = 1; i < argc; i++)
{
if (strcmp (argv[i], "-tls") == 0)
{
#ifdef WITH_TLS
tls = 1;
#else
mu_error (_("TLS not supported"));
return 0;
#endif
}
else
break;
}
argc -= i;
argv += i;
state = current_imap_state ();
if (state != MU_IMAP_SESSION_INIT)
com_disconnect (0, NULL);
status = mu_imap_create (&imap);
if (status == 0)
{
mu_stream_t tcp;
struct mu_sockaddr *sa;
struct mu_sockaddr_hints hints;
memset (&hints, 0, sizeof (hints));
hints.flags = MU_AH_DETECT_FAMILY;
hints.port = tls ? MU_IMAP_DEFAULT_SSL_PORT : MU_IMAP_DEFAULT_PORT;
hints.protocol = IPPROTO_TCP;
hints.socktype = SOCK_STREAM;
status = mu_sockaddr_from_node (&sa, argv[0], argv[1], &hints);
if (status == 0)
{
status = mu_tcp_stream_create_from_sa (&tcp, sa, NULL, 0);
if (status)
mu_sockaddr_free (sa);
}
if (status == 0)
{
#ifdef WITH_TLS
if (tls)
{
mu_stream_t tlsstream;
status = mu_tls_client_stream_create (&tlsstream, tcp, tcp, 0);
mu_stream_unref (tcp);
if (status)
{
mu_error (_("cannot create TLS stream: %s"),
mu_strerror (status));
return 0;
}
tcp = tlsstream;
}
#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_preauth_callback,
NULL);
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);
mu_imap_register_callback_function (imap, MU_IMAP_CB_FETCH,
imap_fetch_callback,
mu_strout);
status = mu_imap_connect (imap);
if (status)
{
const char *err;
mu_error ("Failed to connect: %s", mu_strerror (status));
if (mu_imap_strerror (imap, &err))
mu_error ("server response: %s", err);
mu_imap_destroy (&imap);
}
}
else
mu_imap_destroy (&imap);
}
else
mu_error (_("Failed to create imap connection: %s"), mu_strerror (status));
if (!status)
{
connect_argc = argc;
connect_argv = mu_calloc (argc + 1, sizeof (*connect_argv));
for (i = 0; i < argc; i++)
connect_argv[i] = mu_strdup (argv[i]);
connect_argv[i] = NULL;
imap_prompt_env ();
}
return status;
}
static int
com_starttls (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
{
int status = mu_imap_starttls (imap);
if (status)
report_failure ("starttls", status);
return 0;
}
static int
com_logout (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
{
int status = 0;
if (imap)
{
if (mu_imap_logout (imap) == 0)
{
status = com_disconnect (0, NULL);
}
else
{
mu_printf ("Try 'exit' to leave %s\n", mu_program_name);
}
}
else
mu_printf (_("Try 'exit' to leave %s\n"), mu_program_name);
return status;
}
static int
com_capability (int argc, char **argv)
{
mu_iterator_t iterator = NULL;
int status = 0;
int reread = 0;
int i = 1;
for (i = 1; i < argc; i++)
{
if (strcmp (argv[i], "-reread") == 0)
reread = 1;
else
break;
}
if (i < argc)
{
if (reread)
{
status = mu_imap_capability (imap, 1, NULL);
if (status)
return status;
}
for (; i < argc; i++)
{
const char *elt;
int rc = mu_imap_capability_test (imap, argv[i], &elt);
switch (rc)
{
case 0:
if (*elt)
mu_printf ("%s: %s\n", argv[i], elt);
else
mu_printf (_("%s is set\n"), argv[i]);
break;
case MU_ERR_NOENT:
mu_printf (_("%s is not set\n"), argv[i]);
break;
default:
return rc;
}
}
}
else
{
status = mu_imap_capability (imap, reread, &iterator);
if (status == 0)
{
for (mu_iterator_first (iterator);
!mu_iterator_is_done (iterator); mu_iterator_next (iterator))
{
char *capa = NULL;
mu_iterator_current (iterator, (void **) &capa);
mu_printf ("CAPA: %s\n", capa ? capa : "");
}
mu_iterator_destroy (&iterator);
}
}
return status;
}
static int
com_login (int argc, char **argv)
{
int status;
char *pwd, *passbuf = NULL;
if (!imap)
{
mu_error (_("you need to connect first"));
return 0;
}
if (argc == 2)
{
if (!mutool_shell_interactive)
{
mu_error (_("login: password required"));
return 1;
}
status = mu_getpass (mu_strin, mu_strout, _("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)
{
free (username);
username = mu_strdup (argv[1]);
imap_prompt_env ();
}
else
report_failure ("login", status);
return 0;
}
static int
com_id (int argc, char **argv)
{
mu_assoc_t assoc;
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, &assoc);
if (status == 0)
{
if (test)
{
char *val = mu_assoc_get (assoc, test);
if (val)
{
mu_printf ("%s: %s\n", test, val);
}
else
mu_printf (_("%s is not set\n"), test);
}
else
{
mu_iterator_t itr;
mu_assoc_get_iterator (assoc, &itr);
for (mu_iterator_first (itr);
!mu_iterator_is_done (itr); mu_iterator_next (itr))
{
char *key;
char *val;
mu_iterator_current_kv (itr, (const void**)&key, (void**)&val);
mu_printf ("ID: %s %s\n", key, val);
}
mu_iterator_destroy (&itr);
}
mu_assoc_destroy (&assoc);
}
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, 0);
mu_printf ("\n");
}
if (st->flags & MU_IMAP_STAT_PERMANENT_FLAGS)
{
mu_printf (_("Flags permanent: "));
mu_imap_format_flags (mu_strout, st->permanent_flags, 0);
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
report_failure ("select", status);
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);
}
static int
com_status (int argc, char **argv)
{
struct mu_imap_stat st;
int i, flag;
int status;
st.flags = 0;
for (i = 2; i < argc; i++)
{
if (mu_kwd_xlat_name_ci (_mu_imap_status_name_table, argv[i], &flag))
{
mu_error (_("unknown data item: %s"), argv[i]);
return 0;
}
st.flags |= flag;
}
status = mu_imap_status (imap, argv[1], &st);
if (status == 0)
{
print_imap_stats (&st);
}
else
report_failure ("status", status);
return 0;
}
static int
com_noop (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
{
int status = mu_imap_noop (imap);
if (status)
report_failure ("noop", status);
return 0;
}
static int
com_check (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
{
int status = mu_imap_check (imap);
if (status)
report_failure ("check", status);
return 0;
}
static int
com_fetch (int argc, char **argv)
{
mu_stream_t out = mutool_open_pager ();
mu_msgset_t msgset = parse_msgset (argv[1]);
if (msgset)
{
int status;
mu_imap_register_callback_function (imap, MU_IMAP_CB_FETCH,
imap_fetch_callback,
out);
status = mu_imap_fetch (imap, uid_mode, msgset, argv[2]);
mu_stream_destroy (&out);
mu_imap_register_callback_function (imap, MU_IMAP_CB_FETCH,
imap_fetch_callback,
mu_strout);
mu_msgset_free (msgset);
if (status)
report_failure ("fetch", status);
}
return 0;
}
static int
com_store (int argc, char **argv)
{
mu_msgset_t msgset = parse_msgset (argv[1]);
if (msgset)
{
int status = mu_imap_store (imap, uid_mode, msgset, argv[2]);
if (status)
report_failure ("store", status);
}
return 0;
}
static int
com_close (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
{
int status = mu_imap_close (imap);
if (status)
report_failure ("close", status);
return 0;
}
static int
com_unselect (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
{
int status = mu_imap_noop (imap);
if (status)
report_failure ("unselect", status);
return 0;
}
static int
com_delete (int argc, char **argv)
{
int status = mu_imap_delete (imap, argv[1]);
if (status)
report_failure ("delete", status);
return 0;
}
static int
com_rename (int argc, char **argv)
{
int status = mu_imap_rename (imap, argv[1], argv[2]);
if (status)
report_failure ("rename", status);
return 0;
}
static int
com_expunge (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
{
int status = mu_imap_expunge (imap);
if (status)
report_failure ("expunge", status);
return 0;
}
static int
com_create (int argc, char **argv)
{
int status = mu_imap_mailbox_create (imap, argv[1]);
if (status)
report_failure ("create", status);
return 0;
}
static int
com_subscribe (int argc, char **argv)
{
int status = mu_imap_subscribe (imap, argv[1]);
if (status)
report_failure ("subscribe", status);
return 0;
}
static int
com_unsubscribe (int argc, char **argv)
{
int status = mu_imap_unsubscribe (imap, argv[1]);
if (status)
report_failure ("unsubscribe", status);
return 0;
}
static int
com_append (int argc, char **argv)
{
struct tm tmbuf, *tm = NULL;
struct mu_timezone tzbuf, *tz = NULL;
int flags = 0;
mu_stream_t stream;
int rc, i;
for (i = 1; i < argc; i++)
{
if (strcmp (argv[i], "-time") == 0)
{
char *p;
if (++i == argc)
{
mu_error (_("-time requires argument"));
return 0;
}
rc = mu_scan_datetime (argv[i], MU_DATETIME_INTERNALDATE,
&tmbuf, &tzbuf, &p);
if (rc || *p)
{
mu_error (_("cannot parse time"));
return 0;
}
tm = &tmbuf;
tz = &tzbuf;
}
else if (strcmp (argv[i], "-flag") == 0)
{
if (++i == argc)
{
mu_error (_("-flag requires argument"));
return 0;
}
if (mu_imap_flag_to_attribute (argv[i], &flags))
{
mu_error (_("unrecognized flag: %s"), argv[i]);
return 0;
}
}
else if (strcmp (argv[i], "--") == 0)
{
i++;
break;
}
else if (argv[i][0] == '-')
{
mu_error (_("unrecognized option: %s"), argv[i]);
return 0;
}
else
break;
}
if (argc - i != 2)
{
mu_error (_("wrong number of arguments"));
return 0;
}
rc = mu_file_stream_create (&stream, argv[i + 1],
MU_STREAM_READ|MU_STREAM_SEEK);
if (rc)
{
mu_error (_("cannot open file %s: %s"), argv[i + 1], mu_strerror (rc));
return 0;
}
rc = mu_imap_append_stream (imap, argv[i], flags, tm, tz, stream);
mu_stream_unref (stream);
if (rc)
report_failure ("append", rc);
return 0;
}
static int
com_copy (int argc, char **argv)
{
mu_msgset_t msgset = parse_msgset (argv[1]);
if (msgset)
{
int status = mu_imap_copy (imap, uid_mode, msgset, argv[2]);
mu_msgset_free (msgset);
if (status)
report_failure ("copy", status);
}
return 0;
}
static int
com_search (int argc, char **argv)
{
mu_msgset_t mset;
size_t count;
int rc;
rc = mu_imap_search (imap, uid_mode, argv[1], &mset);
if (rc)
{
report_failure ("search", rc);
return 0;
}
rc = mu_msgset_count (mset, &count);
if (rc == EINVAL || count == 0)
{
mu_printf ("%s\n", _("no matches"));
return 0;
}
mu_printf ("%lu matches:", (unsigned long) count);
mu_msgset_imap_print (mu_strout, mset);
mu_printf ("\n");
mu_msgset_free (mset);
return 0;
}
static int
print_list_item (void *item, void *data)
{
struct mu_list_response *resp = item;
mu_stream_t out = data;
mu_stream_printf (out,
"%c%c %c %4d %s\n",
(resp->type & MU_FOLDER_ATTRIBUTE_DIRECTORY) ? 'd' : '-',
(resp->type & MU_FOLDER_ATTRIBUTE_FILE) ? 'f' : '-',
resp->separator ?
resp->separator : MU_HIERARCHY_DELIMITER,
resp->depth,
resp->name);
return 0;
}
static int
com_list (int argc, char **argv)
{
mu_list_t list;
int rc;
mu_stream_t out;
rc = mu_imap_list_new (imap, argv[1], argv[2], &list);
if (rc)
{
report_failure ("list", rc);
return 0;
}
out = mutool_open_pager ();
mu_list_foreach (list, print_list_item, out);
mu_stream_unref (out);
return 0;
}
static int
com_lsub (int argc, char **argv)
{
mu_list_t list;
int rc;
mu_stream_t out;
rc = mu_imap_lsub_new (imap, argv[1], argv[2], &list);
if (rc)
{
report_failure ("lsub", rc);
return 0;
}
out = mutool_open_pager ();
mu_list_foreach (list, print_list_item, out);
mu_stream_unref (out);
return 0;
}
struct mutool_command imap_comtab[] = {
{ "capability", 1, -1, 0,
com_capability,
/* TRANSLATORS: -reread is a keyword; do not translate. */
N_("[-reread] [NAME...]"),
N_("list server capabilities") },
{ "verbose", 1, 4, 0,
com_verbose,
"[on|off|mask|unmask] [secure [payload]]",
N_("control the protocol tracing") },
{ "connect", 1, 4, 0,
com_connect,
/* TRANSLATORS: -tls is a keyword. */
N_("[-tls] HOSTNAME [PORT]"),
N_("open connection") },
{ "disconnect", 1, 1, 0,
com_disconnect,
NULL,
N_("close connection") },
{ "starttls", 1, 1, 0,
com_starttls,
NULL,
N_("Establish TLS encrypted channel") },
{ "login", 2, 3, 0,
com_login,
N_("USER [PASS]"),
N_("login to the server") },
{ "logout", 1, 1, 0,
com_logout,
NULL,
N_("quit imap session") },
{ "id", 1, -1, 0,
com_id,
N_("[-test KW] [ARG [ARG...]]"),
N_("send ID command") },
{ "noop", 1, 1, 0,
com_noop,
NULL,
N_("no operation (keepalive)") },
{ "check", 1, 1, 0,
com_check,
NULL,
N_("request a server checkpoint") },
{ "select", 1, 2, 0,
com_select,
N_("[MBOX]"),
N_("select a mailbox") },
{ "examine", 1, 2, 0,
com_examine,
N_("[MBOX]"),
N_("examine a mailbox") },
{ "status", 3, -1, 0,
com_status,
N_("MBOX KW [KW...]"),
N_("get mailbox status") },
{ "fetch", 3, 3, CMD_COALESCE_EXTRA_ARGS,
com_fetch,
N_("MSGSET ITEMS"),
N_("fetch message data") },
{ "store", 3, 3, CMD_COALESCE_EXTRA_ARGS,
com_store,
N_("MSGSET ITEMS"),
N_("alter mailbox data") },
{ "close", 1, 1, 0,
com_close,
NULL,
N_("close the mailbox (with expunge)") },
{ "unselect", 1, 1, 0,
com_unselect,
NULL,
N_("close the mailbox (without expunge)") },
{ "delete", 2, 2, 0,
com_delete,
N_("MAILBOX"),
N_("delete the mailbox") },
{ "rename", 3, 3, 0,
com_rename,
N_("OLD-NAME NEW-NAME"),
N_("rename existing mailbox") },
{ "expunge", 1, 1, 0,
com_expunge,
NULL,
N_("permanently remove messages marked for deletion") },
{ "create", 2, 2, 0,
com_create,
N_("MAILBOX"),
N_("create new mailbox") },
{ "append", 3, -1, 0,
com_append,
N_("[-time DATETIME] [-flag FLAG] MAILBOX FILE"),
N_("append message text from FILE to MAILBOX") },
{ "copy", 3, 3, 0,
com_copy,
N_("MSGSET MAILBOX"),
N_("Copy messages from MSGSET to MAILBOX") },
{ "list", 3, 3, 0,
com_list,
N_("REF MBOX"),
N_("List matching mailboxes") },
{ "lsub", 3, 3, 0,
com_lsub,
N_("REF MBOX"),
N_("List subscribed mailboxes") },
{ "subscribe", 2, 2, 0,
com_subscribe,
N_("MBOX"),
N_("Subscribe to a mailbox") },
{ "unsubscribe", 2, 2, 0,
com_unsubscribe,
N_("MBOX"),
N_("Remove mailbox from subscription list") },
{ "uid", 1, 2, 0,
com_uid,
N_("[on|off]"),
N_("control UID mode") },
{ "search", 2, 2, CMD_COALESCE_EXTRA_ARGS,
com_search,
N_("args..."),
N_("search the mailbox") },
{ "quit", 1, 1, 0,
com_logout,
NULL,
N_("same as `logout'") },
{ NULL }
};
int
main (int argc, char **argv)
{
mu_action_getopt (&argc, &argv, NULL, imap_docstring, NULL);
if (argc)
{
mu_error (_("too many arguments"));
return 1;
}
/* Command line prompt */
mutool_shell_prompt = mu_strdup ("imap> ");
imap_prompt_env ();
mutool_shell ("imap", imap_comtab);
return 0;
}