Blame src/plugins/reporter-mailx.c

Packit 4f15d5
/*
Packit 4f15d5
    Copyright (C) 2009  Zdenek Prikryl (zprikryl@redhat.com)
Packit 4f15d5
    Copyright (C) 2009  RedHat inc.
Packit 4f15d5
Packit 4f15d5
    This program is free software; you can redistribute it and/or modify
Packit 4f15d5
    it under the terms of the GNU General Public License as published by
Packit 4f15d5
    the Free Software Foundation; either version 2 of the License, or
Packit 4f15d5
    (at your option) any later version.
Packit 4f15d5
Packit 4f15d5
    This program is distributed in the hope that it will be useful,
Packit 4f15d5
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 4f15d5
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 4f15d5
    GNU General Public License for more details.
Packit 4f15d5
Packit 4f15d5
    You should have received a copy of the GNU General Public License along
Packit 4f15d5
    with this program; if not, write to the Free Software Foundation, Inc.,
Packit 4f15d5
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Packit 4f15d5
*/
Packit 4f15d5
#include "internal_libreport.h"
Packit 4f15d5
#include "client.h"
Packit 4f15d5
#include "problem_report.h"
Packit 4f15d5
Packit 4f15d5
#define PR_DEFAULT_SUBJECT \
Packit 4f15d5
    "[abrt] %pkg_name%[[: %crash_function%()]][[: %reason%]][[: TAINTED %tainted_short%]]"
Packit 4f15d5
Packit 4f15d5
#define PR_MAILX_TEMPLATE \
Packit 4f15d5
    "%%summary:: %s\n" \
Packit 4f15d5
    "\n" \
Packit 4f15d5
    "::" \
Packit 4f15d5
    FILENAME_REASON","FILENAME_CRASH_FUNCTION"," \
Packit 4f15d5
    FILENAME_CMDLINE","FILENAME_EXECUTABLE"," \
Packit 4f15d5
    FILENAME_PACKAGE","FILENAME_COMPONENT","FILENAME_PID","FILENAME_PWD"," \
Packit 4f15d5
    FILENAME_HOSTNAME","FILENAME_COUNT", %%oneline\n" \
Packit 4f15d5
    "\n" \
Packit 4f15d5
    "::" \
Packit 4f15d5
    FILENAME_COMMENT","FILENAME_REPORTED_TO","FILENAME_BACKTRACE"," \
Packit 4f15d5
    FILENAME_CORE_BACKTRACE", %%multiline"
Packit 4f15d5
Packit 4f15d5
#define PR_ATTACH_BINARY "\n%attach:: %binary"
Packit 4f15d5
Packit 4f15d5
enum {
Packit 4f15d5
    RM_FLAG_NOTIFY = (1 << 0),
Packit 4f15d5
    RM_FLAG_DEBUG  = (1 << 1)
Packit 4f15d5
};
Packit 4f15d5
Packit 4f15d5
static void exec_and_feed_input(const char* text, char **args)
Packit 4f15d5
{
Packit 4f15d5
    int pipein[2];
Packit 4f15d5
Packit 4f15d5
    pid_t child = fork_execv_on_steroids(
Packit 4f15d5
                EXECFLG_INPUT | EXECFLG_QUIET,
Packit 4f15d5
                args,
Packit 4f15d5
                pipein,
Packit 4f15d5
                /*env_vec:*/ NULL,
Packit 4f15d5
                /*dir:*/ NULL,
Packit 4f15d5
                /*uid (ignored):*/ 0
Packit 4f15d5
    );
Packit 4f15d5
Packit 4f15d5
    full_write_str(pipein[1], text);
Packit 4f15d5
    close(pipein[1]);
Packit 4f15d5
Packit 4f15d5
    int status;
Packit 4f15d5
    safe_waitpid(child, &status, 0); /* wait for command completion */
Packit 4f15d5
    if (status != 0)
Packit 4f15d5
        error_msg_and_die("Error running '%s'", args[0]);
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
static char** append_str_to_vector(char **vec, unsigned *size_p, const char *str)
Packit 4f15d5
{
Packit 4f15d5
    //log_warning("old vec: %p", vec);
Packit 4f15d5
    unsigned size = *size_p;
Packit 4f15d5
    vec = (char**) xrealloc(vec, (size+2) * sizeof(vec[0]));
Packit 4f15d5
    vec[size] = xstrdup(str);
Packit 4f15d5
    //log_warning("new vec: %p, added [%d] %p", vec, size, vec[size]);
Packit 4f15d5
    size++;
Packit 4f15d5
    vec[size] = NULL;
Packit 4f15d5
    *size_p = size;
Packit 4f15d5
    return vec;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
static char *ask_email_address(const char *type, const char *def_address)
Packit 4f15d5
{
Packit 4f15d5
    char *ask_text = xasprintf(_("Email address of %s was not specified. Would you like to do so now? If not, '%s' is to be used"), type, def_address);
Packit 4f15d5
    const int ret = ask_yes_no(ask_text);
Packit 4f15d5
    free(ask_text);
Packit 4f15d5
Packit 4f15d5
    if (!ret)
Packit 4f15d5
        return xstrdup(def_address);
Packit 4f15d5
Packit 4f15d5
    ask_text = xasprintf(_("Please, type email address of %s:"), type);
Packit 4f15d5
    char *address = ask(ask_text);
Packit 4f15d5
    free(ask_text);
Packit 4f15d5
Packit 4f15d5
    if (address == NULL || address[0] == '\0')
Packit 4f15d5
    {
Packit 4f15d5
        set_xfunc_error_retval(EXIT_CANCEL_BY_USER);
Packit 4f15d5
        error_msg_and_die(_("Can't continue without email address of %s"), type);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    return address;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
static void create_and_send_email(
Packit 4f15d5
                const char *dump_dir_name,
Packit 4f15d5
                map_string_t *settings,
Packit 4f15d5
                const char *fmt_file,
Packit 4f15d5
                int flag)
Packit 4f15d5
{
Packit 4f15d5
    problem_data_t *problem_data = create_problem_data_for_reporting(dump_dir_name);
Packit 4f15d5
    if (!problem_data)
Packit 4f15d5
        xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */
Packit 4f15d5
Packit 4f15d5
    char* env;
Packit 4f15d5
    env = getenv("Mailx_EmailFrom");
Packit 4f15d5
    char *email_from = (env ? xstrdup(env) : xstrdup(get_map_string_item_or_NULL(settings, "EmailFrom")) ? : ask_email_address("sender", "ABRT Daemon <DoNotReply>"));
Packit 4f15d5
    env = getenv("Mailx_EmailTo");
Packit 4f15d5
    char *email_to = (env ? xstrdup(env) : xstrdup(get_map_string_item_or_NULL(settings, "EmailTo")) ? : ask_email_address("receiver", "root@localhost"));
Packit 4f15d5
    env = getenv("Mailx_SendBinaryData");
Packit 4f15d5
    bool send_binary_data = string_to_bool(env ? env : get_map_string_item_or_empty(settings, "SendBinaryData"));
Packit 4f15d5
Packit 4f15d5
    problem_formatter_t *pf = problem_formatter_new();
Packit 4f15d5
    /* formatting file is not set */
Packit 4f15d5
    if (fmt_file == NULL)
Packit 4f15d5
    {
Packit 4f15d5
        env = getenv("Mailx_Subject");
Packit 4f15d5
        const char *subject = (env ? env : get_map_string_item_or_NULL(settings, "Subject") ? : PR_DEFAULT_SUBJECT);
Packit 4f15d5
Packit 4f15d5
        char *format_string = xasprintf(PR_MAILX_TEMPLATE, subject);
Packit 4f15d5
Packit 4f15d5
        /* attaching binary file to the email */
Packit 4f15d5
        if (send_binary_data)
Packit 4f15d5
            format_string = append_to_malloced_string(format_string, PR_ATTACH_BINARY);
Packit 4f15d5
Packit 4f15d5
        if (problem_formatter_load_string(pf, format_string))
Packit 4f15d5
            error_msg_and_die("BUG: Invalid default problem report format string");
Packit 4f15d5
Packit 4f15d5
        free(format_string);
Packit 4f15d5
    }
Packit 4f15d5
    else
Packit 4f15d5
    {
Packit 4f15d5
        if (problem_formatter_load_file(pf, fmt_file))
Packit 4f15d5
            error_msg_and_die("Invalid format file: %s", fmt_file);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    problem_report_t *pr = NULL;
Packit 4f15d5
    if (problem_formatter_generate_report(pf, problem_data, &pr))
Packit 4f15d5
        error_msg_and_die("Failed to format bug report from problem data");
Packit 4f15d5
Packit 4f15d5
    const char *subject = problem_report_get_summary(pr);
Packit 4f15d5
    const char *dsc = problem_report_get_description(pr);
Packit 4f15d5
Packit 4f15d5
    if (flag & RM_FLAG_DEBUG)
Packit 4f15d5
    {
Packit 4f15d5
        printf("subject: %s\n"
Packit 4f15d5
                  "\n"
Packit 4f15d5
                  "%s"
Packit 4f15d5
                  "\n"
Packit 4f15d5
                  , subject
Packit 4f15d5
                  , dsc);
Packit 4f15d5
Packit 4f15d5
        puts("attachments:");
Packit 4f15d5
        for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a))
Packit 4f15d5
            printf(" %s\n", (const char *)a->data);
Packit 4f15d5
Packit 4f15d5
        problem_report_free(pr);
Packit 4f15d5
        problem_formatter_free(pf);
Packit 4f15d5
        free(email_from);
Packit 4f15d5
        free(email_to);
Packit 4f15d5
        exit(0);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    char **args = NULL;
Packit 4f15d5
    unsigned arg_size = 0;
Packit 4f15d5
    args = append_str_to_vector(args, &arg_size, "/bin/mailx");
Packit 4f15d5
Packit 4f15d5
    /* attaching files to the email */
Packit 4f15d5
    for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a))
Packit 4f15d5
    {
Packit 4f15d5
        args = append_str_to_vector(args, &arg_size, "-a");
Packit 4f15d5
        char *full_name = concat_path_file(realpath(dump_dir_name, NULL), a->data);
Packit 4f15d5
        args = append_str_to_vector(args, &arg_size, full_name);
Packit 4f15d5
        free(full_name);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    args = append_str_to_vector(args, &arg_size, "-s");
Packit 4f15d5
    args = append_str_to_vector(args, &arg_size, subject);
Packit 4f15d5
    args = append_str_to_vector(args, &arg_size, "-r");
Packit 4f15d5
    args = append_str_to_vector(args, &arg_size, email_from);
Packit 4f15d5
    args = append_str_to_vector(args, &arg_size, email_to);
Packit 4f15d5
Packit 4f15d5
    free(email_from);
Packit 4f15d5
Packit 4f15d5
    /* This makes (some versions of) mailx to wait for child process to finish,
Packit 4f15d5
     * and to report its exit code, not useless "always 0" exit code.
Packit 4f15d5
     * Sadly, usually this still doesn't help. See:
Packit 4f15d5
     * https://bugzilla.redhat.com/show_bug.cgi?id=740895
Packit 4f15d5
     */
Packit 4f15d5
    putenv((char*)"sendwait=1");
Packit 4f15d5
Packit 4f15d5
    /* Prevent mailx from creating dead.letter if sending fails. The file is
Packit 4f15d5
     * useless in our case and if the reporter is called from abrtd, SELinux
Packit 4f15d5
     * complains a lot about mailx touching ABRT data.
Packit 4f15d5
     */
Packit 4f15d5
    putenv((char*)"DEAD=/dev/null");
Packit 4f15d5
Packit 4f15d5
    if (flag & RM_FLAG_NOTIFY)
Packit 4f15d5
        log_warning(_("Sending a notification email to: %s"), email_to);
Packit 4f15d5
    else
Packit 4f15d5
        log_warning(_("Sending an email..."));
Packit 4f15d5
Packit 4f15d5
    exec_and_feed_input(dsc, args);
Packit 4f15d5
Packit 4f15d5
    problem_report_free(pr);
Packit 4f15d5
    problem_formatter_free(pf);
Packit 4f15d5
Packit 4f15d5
    while (*args)
Packit 4f15d5
        free(*args++);
Packit 4f15d5
    args -= arg_size;
Packit 4f15d5
    free(args);
Packit 4f15d5
Packit 4f15d5
    problem_data_free(problem_data);
Packit 4f15d5
Packit 4f15d5
    if (!(flag & RM_FLAG_NOTIFY))
Packit 4f15d5
    {
Packit 4f15d5
        struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
Packit 4f15d5
        if (dd)
Packit 4f15d5
        {
Packit 4f15d5
            report_result_t rr = { .label = (char *)"email" };
Packit 4f15d5
            rr.url = xasprintf("mailto:%s", email_to);
Packit 4f15d5
            add_reported_to_entry(dd, &rr);
Packit 4f15d5
            free(rr.url);
Packit 4f15d5
            dd_close(dd);
Packit 4f15d5
        }
Packit 4f15d5
        log_warning(_("Email was sent to: %s"), email_to);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    free(email_to);
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
int main(int argc, char **argv)
Packit 4f15d5
{
Packit 4f15d5
    abrt_init(argv);
Packit 4f15d5
Packit 4f15d5
    /* I18n */
Packit 4f15d5
    setlocale(LC_ALL, "");
Packit 4f15d5
#if ENABLE_NLS
Packit 4f15d5
    bindtextdomain(PACKAGE, LOCALEDIR);
Packit 4f15d5
    textdomain(PACKAGE);
Packit 4f15d5
#endif
Packit 4f15d5
Packit 4f15d5
    const char *dump_dir_name = ".";
Packit 4f15d5
    const char *conf_file = CONF_DIR"/plugins/mailx.conf";
Packit 4f15d5
    const char *fmt_file = NULL;
Packit 4f15d5
Packit 4f15d5
    /* Can't keep these strings/structs static: _() doesn't support that */
Packit 4f15d5
    const char *program_usage_string = _(
Packit 4f15d5
        "& [-v] -d DIR [-c CONFFILE] [-F FMTFILE]"
Packit 4f15d5
        "\n"
Packit 4f15d5
        "\n""Sends contents of a problem directory DIR via email"
Packit 4f15d5
        "\n"
Packit 4f15d5
        "\n""If not specified, CONFFILE defaults to "CONF_DIR"/plugins/mailx.conf"
Packit 4f15d5
        "\n""Its lines should have 'PARAM = VALUE' format."
Packit 4f15d5
        "\n""Recognized string parameters: Subject, EmailFrom, EmailTo."
Packit 4f15d5
        "\n""Recognized boolean parameter (VALUE should be 1/0, yes/no): SendBinaryData."
Packit 4f15d5
        "\n""Parameters can be overridden via $Mailx_PARAM environment variables."
Packit 4f15d5
    );
Packit 4f15d5
Packit 4f15d5
    enum {
Packit 4f15d5
        OPT_v = 1 << 0,
Packit 4f15d5
        OPT_d = 1 << 1,
Packit 4f15d5
        OPT_c = 1 << 2,
Packit 4f15d5
        OPT_F = 1 << 3,
Packit 4f15d5
        OPT_n = 1 << 4,
Packit 4f15d5
        OPT_D = 1 << 5,
Packit 4f15d5
    };
Packit 4f15d5
    /* Keep enum above and order of options below in sync! */
Packit 4f15d5
    struct options program_options[] = {
Packit 4f15d5
        OPT__VERBOSE(&g_verbose),
Packit 4f15d5
        OPT_STRING('d', NULL, &dump_dir_name, "DIR"     , _("Problem directory")),
Packit 4f15d5
        OPT_STRING('c', NULL, &conf_file    , "CONFFILE", _("Config file")),
Packit 4f15d5
        OPT_STRING('F', NULL, &fmt_file     , "FILE"    , _("Formatting file for an email")),
Packit 4f15d5
        OPT_BOOL('n', "notify-only", NULL  , _("Notify only (Do not mark the report as sent)")),
Packit 4f15d5
        OPT_BOOL(  'D', NULL, NULL          ,         _("Debug")),
Packit 4f15d5
        OPT_END()
Packit 4f15d5
    };
Packit 4f15d5
    unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
Packit 4f15d5
Packit 4f15d5
    export_abrt_envvars(0);
Packit 4f15d5
Packit 4f15d5
    map_string_t *settings = new_map_string();
Packit 4f15d5
    load_conf_file(conf_file, settings, /*skip key w/o values:*/ false);
Packit 4f15d5
Packit 4f15d5
    int flag = 0;
Packit 4f15d5
    if (opts & OPT_n)
Packit 4f15d5
        flag |= RM_FLAG_NOTIFY;
Packit 4f15d5
Packit 4f15d5
    if (opts & OPT_D)
Packit 4f15d5
        flag |= RM_FLAG_DEBUG;
Packit 4f15d5
Packit 4f15d5
    create_and_send_email(dump_dir_name, settings, fmt_file, flag);
Packit 4f15d5
Packit 4f15d5
    free_map_string(settings);
Packit 4f15d5
    return 0;
Packit 4f15d5
}