/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 2010-2019 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; }