/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 2019-2021 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, 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 GNU Mailutils. If not, see . */ #include #include "tesh.h" unsigned long get_num (char const *s) { unsigned long n; char *p; errno = 0; n = strtoul (s, &p, 10); if (errno || *p) { mu_error ("not a number: %s", s); abort (); } return n; } struct interp_env { char const *mbxname; mu_mailbox_t mbx; mu_message_t msg; size_t msgno; }; int mbop_env_date (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; mu_envelope_t msg_env; char *str; MU_ASSERT (mu_message_get_envelope (ienv->msg, &msg_env)); MU_ASSERT (mu_envelope_aget_date (msg_env, &str)); mu_printf ("%s", str); free (str); return 0; } int mbop_env_sender (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; mu_envelope_t msg_env; char *str; MU_ASSERT (mu_message_get_envelope (ienv->msg, &msg_env)); MU_ASSERT (mu_envelope_aget_sender (msg_env, &str)); mu_printf ("%s", str); free (str); return 0; } int mbop_header_lines (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; mu_header_t hdr; size_t lines; MU_ASSERT (mu_message_get_header (ienv->msg, &hdr)); MU_ASSERT (mu_header_lines (hdr, &lines)); mu_printf ("%lu", (unsigned long) lines); return 0; } int mbop_header_count (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; mu_header_t hdr; size_t n; MU_ASSERT (mu_message_get_header (ienv->msg, &hdr)); MU_ASSERT (mu_header_get_field_count (hdr, &n)); mu_printf ("%lu", (unsigned long) n); return 0; } int mbop_header_size (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; mu_header_t hdr; size_t s; MU_ASSERT (mu_message_get_header (ienv->msg, &hdr)); MU_ASSERT (mu_header_size (hdr, &s)); mu_printf ("%lu", (unsigned long) s); return 0; } int mbop_header_field (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; mu_header_t hdr; size_t n = get_num (argv[1]); char const *s; MU_ASSERT (mu_message_get_header (ienv->msg, &hdr)); MU_ASSERT (mu_header_sget_field_name (hdr, n, &s)); mu_printf ("%s", s); return 0; } int mbop_header_value (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; mu_header_t hdr; size_t n = get_num (argv[1]); char const *s; MU_ASSERT (mu_message_get_header (ienv->msg, &hdr)); MU_ASSERT (mu_header_sget_field_value (hdr, n, &s)); mu_printf ("%s", s); return 0; } int mbop_headers (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; mu_header_t hdr; char const *name; char *val; size_t i, n; MU_ASSERT (mu_message_get_header (ienv->msg, &hdr)); MU_ASSERT (mu_header_get_field_count (hdr, &n)); for (i = 1; i <= n; i++) { MU_ASSERT (mu_header_sget_field_name (hdr, i, &name)); mu_printf ("%s:", name); MU_ASSERT (mu_header_aget_field_value_unfold (hdr, i, &val)); mu_printf ("%s\n", val); } return 0; } int mbop_body_lines (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; mu_body_t body; size_t lines; MU_ASSERT (mu_message_get_body (ienv->msg, &body)); MU_ASSERT (mu_body_lines (body, &lines)); mu_printf ("%lu", (unsigned long) lines); return 0; } int mbop_body_size (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; mu_body_t body; size_t s; MU_ASSERT (mu_message_get_body (ienv->msg, &body)); MU_ASSERT (mu_body_size (body, &s)); mu_printf ("%lu", (unsigned long) s); return 0; } int mbop_body_text (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; mu_body_t body; mu_stream_t str; MU_ASSERT (mu_message_get_body (ienv->msg, &body)); MU_ASSERT (mu_body_get_streamref (body, &str)); MU_ASSERT (mu_stream_copy (mu_strout, str, 0, NULL)); mu_stream_unref (str); return 0; } int mbop_attr (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; mu_attribute_t attr; char abuf[MU_STATUS_BUF_SIZE]; MU_ASSERT (mu_message_get_attribute (ienv->msg, &attr)); MU_ASSERT (mu_attribute_to_string (attr, abuf, sizeof (abuf), NULL)); mu_printf ("%s", abuf[0] ? abuf : "-"); return 0; } int mbop_uid (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; size_t uid; MU_ASSERT (mu_message_get_uid (ienv->msg, &uid)); mu_printf ("%lu", (unsigned long) uid); return 0; } int mbop_message_lines (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; size_t lines; MU_ASSERT (mu_message_lines (ienv->msg, &lines)); mu_printf ("%lu", (unsigned long) lines); return 0; } int mbop_message_size (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; size_t size; MU_ASSERT (mu_message_size (ienv->msg, &size)); mu_printf ("%lu", (unsigned long) size); return 0; } #define __cat2__(a,b) a ## b #define __cat3__(a,b,c) a ## b ## c #define __cat4__(a,b,c,d) a ## b ## c ## d #define ATTR_FUN(op,attr) \ static int \ __cat4__(mbop_,op,_,attr) (int argc, char **argv, mu_assoc_t options, void *env) \ { \ struct interp_env *ienv = env; \ mu_attribute_t a; \ MU_ASSERT (mu_message_get_attribute (ienv->msg, &a)); \ MU_ASSERT (__cat4__(mu_attribute_,op,_,attr)(a)); \ mu_printf ("OK"); \ return 0; \ } ATTR_FUN(set,seen) ATTR_FUN(set,answered) ATTR_FUN(set,flagged) ATTR_FUN(set,deleted) ATTR_FUN(set,draft) ATTR_FUN(set,recent) ATTR_FUN(set,read) ATTR_FUN(unset,seen) ATTR_FUN(unset,answered) ATTR_FUN(unset,flagged) ATTR_FUN(unset,deleted) ATTR_FUN(unset,draft) ATTR_FUN(unset,recent) ATTR_FUN(unset,read) int mbop_append (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; mu_stream_t str; mu_message_t newmsg; MU_ASSERT (mu_file_stream_create (&str, argv[1], MU_STREAM_READ)); MU_ASSERT (mu_stream_to_message (str, &newmsg)); MU_ASSERT (mu_mailbox_append_message (ienv->mbx, newmsg)); mu_stream_destroy (&str); mu_printf ("OK"); return 0; } int mbop_expunge (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; MU_ASSERT (mu_mailbox_expunge (ienv->mbx)); mu_printf ("OK"); return 0; } int mbop_sync (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; MU_ASSERT (mu_mailbox_sync (ienv->mbx)); mu_printf ("OK"); return 0; } int mbop_count (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; size_t n; MU_ASSERT (mu_mailbox_messages_count (ienv->mbx, &n)); mu_printf ("%lu", (unsigned long) n); return 0; } int mbop_uidvalidity (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; unsigned long v; MU_ASSERT (mu_mailbox_uidvalidity (ienv->mbx, &v)); mu_printf ("%lu", v); return 0; } int mbop_uidnext (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; size_t n; MU_ASSERT (mu_mailbox_uidnext (ienv->mbx, &n)); mu_printf ("%lu", (unsigned long) n); return 0; } int mbop_recent (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; size_t n; MU_ASSERT (mu_mailbox_messages_recent (ienv->mbx, &n)); mu_printf ("%lu", (unsigned long) n); return 0; } int mbop_unseen (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; size_t n; MU_ASSERT (mu_mailbox_message_unseen (ienv->mbx, &n)); mu_printf ("%lu", (unsigned long) n); return 0; } static char const *mbox_actions[] = { "expunge", "sync", "append", "uidvalidity", "uidvalidity_reset", "uidnext", "count", "recent", "unseen", "qget", NULL }; int needs_message (char const *name) { int i; for (i = 0; mbox_actions[i]; i++) if (strcmp (mbox_actions[i], name) == 0) return 0; return 1; } int mbop_envinit (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; if (needs_message (argv[0])) { if (!ienv->msg) { mu_error ("no message selected"); exit (1); } mu_printf ("%lu ", (unsigned long) ienv->msgno); } mu_printf ("%s: ", argv[0]); return 0; } int mbop_envfini (int argc, char **argv, mu_assoc_t options, void *env) { mu_printf ("\n"); return 0; } int mbop_nocmd (int argc, char **argv, mu_assoc_t options, void *env) { struct interp_env *ienv = env; if (mu_isdigit (*argv[0])) { ienv->msgno = get_num (argv[0]); MU_ASSERT (mu_mailbox_get_message (ienv->mbx, ienv->msgno, &ienv->msg)); mu_printf ("%lu current message\n", (unsigned long) ienv->msgno); return 0; } return MU_ERR_PARSE; } int mbop_qget (int argc, char **argv, mu_assoc_t options, void *data) { struct interp_env *env = data; mu_mailbox_t mbx; mu_message_qid_t qid; mu_message_t msg; mu_stream_t str; MU_ASSERT (mu_mailbox_create_default (&mbx, env->mbxname)); MU_ASSERT (mu_mailbox_open (mbx, MU_STREAM_READ|MU_STREAM_QACCESS)); qid = argv[1]; MU_ASSERT (mu_mailbox_quick_get_message (mbx, qid, &msg)); MU_ASSERT (mu_message_get_streamref (msg, &str)); MU_ASSERT (mu_stream_copy (mu_strout, str, 0, NULL)); mu_stream_destroy (&str); mu_mailbox_destroy (&mbx); return 0; } int mbop_uidvalidity_reset (int argc, char **argv, mu_assoc_t options, void *data) { struct interp_env *ienv = data; MU_ASSERT (mu_mailbox_uidvalidity_reset (ienv->mbx)); mu_printf ("OK"); return 0; } struct mu_tesh_command commands[] = { { "__ENVINIT__", "", mbop_envinit }, { "__ENVFINI__", "", mbop_envfini }, { "__NOCMD__", "", mbop_nocmd }, { "env_date", "", mbop_env_date }, { "env_sender", "", mbop_env_sender }, { "header_lines", "", mbop_header_lines }, { "header_size", "", mbop_header_size }, { "header_count", "", mbop_header_count }, { "header_field", "NUMBER", mbop_header_field }, { "header_value", "NAME", mbop_header_value }, { "headers", "", mbop_headers }, { "body_lines", "", mbop_body_lines }, { "body_size", "", mbop_body_size }, { "body_text", "", mbop_body_text }, { "attr", "", mbop_attr }, { "uid", "", mbop_uid }, { "set_seen", "", mbop_set_seen }, { "set_answered", "", mbop_set_answered }, { "set_flagged", "", mbop_set_flagged }, { "set_deleted", "", mbop_set_deleted }, { "set_draft", "", mbop_set_draft }, { "set_recent", "", mbop_set_recent }, { "set_read", "", mbop_set_read }, { "unset_seen", "", mbop_unset_seen }, { "unset_answered", "", mbop_unset_answered }, { "unset_flagged", "", mbop_unset_flagged }, { "unset_deleted", "", mbop_unset_deleted }, { "unset_draft", "", mbop_unset_draft }, { "unset_recent", "", mbop_unset_recent }, { "unset_read", "", mbop_unset_read }, { "expunge", "", mbop_expunge }, { "sync", "", mbop_sync }, { "append", "FILE", mbop_append }, { "uidvalidity", "", mbop_uidvalidity }, { "uidnext", "", mbop_uidnext }, { "uidvalidity_reset", "", mbop_uidvalidity_reset }, { "count", "", mbop_count }, { "recent", "", mbop_recent }, { "unseen", "", mbop_unseen }, { "qget", "QID", mbop_qget }, { "message_lines", "", mbop_message_lines }, { "message_size", "", mbop_message_size }, { NULL } }; static int test_notify (mu_observer_t obs, size_t type, void *data, void *action_data) { struct interp_env *env = mu_observer_get_owner (obs); mu_mailbox_t mbx; mu_message_qid_t qid = data; int rc; mu_message_t msg; mu_header_t hdr; char const *from = "(NONE)", *subj = "(NONE)"; MU_ASSERT (mu_mailbox_create_default (&mbx, env->mbxname)); MU_ASSERT (mu_mailbox_open (mbx, MU_STREAM_READ|MU_STREAM_QACCESS)); rc = mu_mailbox_quick_get_message (mbx, qid, &msg); if (rc) { mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_quick_get_message", NULL, rc); goto err; } rc = mu_message_get_header (msg, &hdr); if (rc) { mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_header", NULL, rc); goto err; } mu_header_sget_value (hdr, MU_HEADER_FROM, &from); mu_header_sget_value (hdr, MU_HEADER_SUBJECT, &subj); mu_stream_printf (mu_strerr, "new message: %s %s\n", from, subj); err: mu_mailbox_close (mbx); mu_mailbox_destroy (&mbx); return 0; } int main (int argc, char **argv) { struct interp_env env = { NULL, NULL, NULL, 0 }; int debug_option = 0; int detect_option = 0; int notify_option = 0; int append_option = 0; int ro_option = 0; int mbox_flags; struct mu_option options[] = { { "debug", 'd', NULL, MU_OPTION_DEFAULT, "enable debugging", mu_c_incr, &debug_option }, { "mailbox", 'm', "FILE", MU_OPTION_DEFAULT, "use this mailbox", mu_c_string, &env.mbxname }, { "detect", 'D', NULL, MU_OPTION_DEFAULT, "detect mailbox format", mu_c_incr, &detect_option }, { "notify", 'N', NULL, MU_OPTION_DEFAULT, "test notification code", mu_c_incr, ¬ify_option }, { "append", 'a', NULL, MU_OPTION_DEFAULT, "open mailbox in append mode", mu_c_incr, &append_option }, { "read-only", 'r', NULL, MU_OPTION_DEFAULT, "open mailbox in read-only mode", mu_c_incr, &ro_option }, MU_OPTION_END }; env.mbxname = getenv ("MAIL"); mu_tesh_init (argv[0]); mu_registrar_record (MBOP_RECORD); mu_registrar_set_default_scheme (MBOP_SCHEME); mu_cli_simple (argc, argv, MU_CLI_OPTION_OPTIONS, options, MU_CLI_OPTION_PROG_DOC, "test tool for " MBOP_SCHEME " mailboxes", MU_CLI_OPTION_PROG_ARGS, "CMD [; CMD ;...]", MU_CLI_OPTION_EX_USAGE, 2, MU_CLI_OPTION_RETURN_ARGC, &argc, MU_CLI_OPTION_RETURN_ARGV, &argv, MU_CLI_OPTION_END); if (debug_option) mu_debug_enable_category ("mailbox", 7, MU_DEBUG_LEVEL_UPTO (MU_DEBUG_PROT)); if (detect_option) { mu_url_t url; mu_record_t rec; int n; MU_ASSERT (mu_registrar_lookup_scheme (MBOP_SCHEME, &rec)); MU_ASSERT (mu_url_create_hint (&url, env.mbxname, MU_URL_PARSE_SLASH | MU_URL_PARSE_LOCAL, NULL)); n = mu_record_is_scheme (rec, url, MU_FOLDER_ATTRIBUTE_FILE); mu_printf ("%s: %d\n", env.mbxname, n); mu_url_destroy (&url); exit (n & MU_FOLDER_ATTRIBUTE_FILE ? 0 : 1); } if (append_option && ro_option) { mu_error ("conflicting options"); exit (2); } if (append_option) mbox_flags = MU_STREAM_APPEND; else if (ro_option) mbox_flags = MU_STREAM_READ; else mbox_flags = MU_STREAM_RDWR; #ifdef MBOP_PRE_OPEN_HOOK MBOP_PRE_OPEN_HOOK (); #endif MU_ASSERT (mu_mailbox_create_default (&env.mbx, env.mbxname)); MU_ASSERT (mu_mailbox_open (env.mbx, mbox_flags)); if (notify_option) { mu_observer_t observer; mu_observable_t observable; mu_observer_create (&observer, &env); mu_observer_set_action (observer, test_notify, &env); mu_mailbox_get_observable (env.mbx, &observable); mu_observable_attach (observable, MU_EVT_MAILBOX_MESSAGE_APPEND, observer); } mu_tesh_read_and_eval (argc, argv, commands, &env); mu_mailbox_close (env.mbx); mu_mailbox_destroy (&env.mbx); return 0; }