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