summaryrefslogtreecommitdiffstats
path: root/src/plugins/abrt-action-bugzilla.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/abrt-action-bugzilla.c')
-rw-r--r--src/plugins/abrt-action-bugzilla.c340
1 files changed, 340 insertions, 0 deletions
diff --git a/src/plugins/abrt-action-bugzilla.c b/src/plugins/abrt-action-bugzilla.c
new file mode 100644
index 00000000..91bc26f8
--- /dev/null
+++ b/src/plugins/abrt-action-bugzilla.c
@@ -0,0 +1,340 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 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 "abrtlib.h"
+#include "abrt_problem_data.h"
+#include "parse_options.h"
+#include "abrt_xmlrpc.h"
+#include "rhbz.h"
+
+#define XML_RPC_SUFFIX "/xmlrpc.cgi"
+
+/* From RHEL6 kernel/panic.c:
+ * { TAINT_PROPRIETARY_MODULE, 'P', 'G' },
+ * { TAINT_FORCED_MODULE, 'F', ' ' },
+ * { TAINT_UNSAFE_SMP, 'S', ' ' },
+ * { TAINT_FORCED_RMMOD, 'R', ' ' },
+ * { TAINT_MACHINE_CHECK, 'M', ' ' },
+ * { TAINT_BAD_PAGE, 'B', ' ' },
+ * { TAINT_USER, 'U', ' ' },
+ * { TAINT_DIE, 'D', ' ' },
+ * { TAINT_OVERRIDDEN_ACPI_TABLE, 'A', ' ' },
+ * { TAINT_WARN, 'W', ' ' },
+ * { TAINT_CRAP, 'C', ' ' },
+ * { TAINT_FIRMWARE_WORKAROUND, 'I', ' ' },
+ * entries 12 - 27 are unused
+ * { TAINT_HARDWARE_UNSUPPORTED, 'H', ' ' },
+ * entries 29 - 31 are unused
+ */
+
+static const char * const taint_warnings[] = {
+ "Proprietary Module",
+ "Forced Module",
+ "Unsafe SMP",
+ "Forced rmmod",
+ "Machine Check",
+ "Bad Page",
+ "User",
+ "Die",
+ "Overriden ACPI Table",
+ "Warning Issued",
+ "Experimental Module Loaded",
+ "Firmware Workaround",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Hardware Unsupported",
+ NULL,
+ NULL,
+};
+
+/* TODO: npajkovs: fix tainted string */
+static const char *tainted_string(unsigned tainted)
+{
+ unsigned idx = 0;
+ while ((tainted >>= 1) != 0)
+ idx++;
+
+ return taint_warnings[idx];
+}
+
+static void report_to_bugzilla(const char *dump_dir_name, map_string_h *settings)
+{
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
+ xfunc_die(); /* dd_opendir already emitted error msg */
+ problem_data_t *problem_data = create_problem_data_from_dump_dir(dd);
+ dd_close(dd);
+
+ const char *env;
+ const char *login;
+ const char *password;
+ const char *bugzilla_xmlrpc;
+ const char *bugzilla_url;
+ bool ssl_verify;
+
+ env = getenv("Bugzilla_Login");
+ login = env ? env : get_map_string_item_or_empty(settings, "Login");
+ env = getenv("Bugzilla_Password");
+ password = env ? env : get_map_string_item_or_empty(settings, "Password");
+ if (!login[0] || !password[0])
+ error_msg_and_die(_("Empty login or password, please check your configuration"));
+
+ env = getenv("Bugzilla_BugzillaURL");
+ bugzilla_url = env ? env : get_map_string_item_or_empty(settings, "BugzillaURL");
+ if (!bugzilla_url[0])
+ bugzilla_url = "https://bugzilla.redhat.com";
+ bugzilla_xmlrpc = xasprintf("%s"XML_RPC_SUFFIX, bugzilla_url);
+
+ env = getenv("Bugzilla_SSLVerify");
+ ssl_verify = string_to_bool(env ? env : get_map_string_item_or_empty(settings, "SSLVerify"));
+
+ const char *component = get_problem_item_content_or_NULL(problem_data, FILENAME_COMPONENT);
+ const char *duphash = get_problem_item_content_or_NULL(problem_data, FILENAME_DUPHASH);
+ if (!duphash)
+ error_msg_and_die(_("Essential file '%s' is missing, can't continue.."),
+ FILENAME_DUPHASH);
+
+ if (!*duphash)
+ error_msg_and_die(_("Essential file '%s' is empty, can't continue.."),
+ FILENAME_DUPHASH);
+
+ const char *release = get_problem_item_content_or_NULL(problem_data, FILENAME_OS_RELEASE);
+ if (!release) /* Old dump dir format compat. Remove in abrt-2.1 */
+ release = get_problem_item_content_or_NULL(problem_data, "release");
+
+ struct abrt_xmlrpc *client = abrt_xmlrpc_new_client(bugzilla_xmlrpc, ssl_verify);
+
+ log(_("Logging into Bugzilla at %s"), bugzilla_url);
+ rhbz_login(client, login, password);
+
+ log(_("Checking for duplicates"));
+ char *product = NULL;
+ char *version = NULL;
+ parse_release_for_bz(release, &product, &version);
+ free(version);
+
+ xmlrpc_value *result;
+ if (strcmp(product, "Fedora") == 0)
+ result = rhbz_search_duphash(client, component, product, duphash);
+ else
+ result = rhbz_search_duphash(client, component, NULL, duphash);
+
+ xmlrpc_value *all_bugs = rhbz_get_member("bugs", result);
+ xmlrpc_DECREF(result);
+
+ if (!all_bugs)
+ error_msg_and_die(_("Missing mandatory member 'bugs'"));
+
+ int all_bugs_size = rhbz_array_size(all_bugs);
+ // When someone clones bug it has same duphash, so we can find more than 1.
+ // Need to be checked if component is same.
+ VERB3 log("Bugzilla has %i reports with same duphash '%s'",
+ all_bugs_size, duphash);
+
+ int bug_id = -1, dependent_bug = -1;
+ struct bug_info *bz = NULL;
+ if (all_bugs_size > 0)
+ {
+ bug_id = rhbz_bug_id(all_bugs);
+ xmlrpc_DECREF(all_bugs);
+ bz = rhbz_bug_info(client, bug_id);
+
+ if (strcmp(bz->bi_product, product) != 0)
+ {
+ dependent_bug = bug_id;
+ /* found something, but its a different product */
+ free_bug_info(bz);
+
+ xmlrpc_value *result = rhbz_search_duphash(client, component,
+ product, duphash);
+ xmlrpc_value *all_bugs = rhbz_get_member("bugs", result);
+ xmlrpc_DECREF(result);
+
+ all_bugs_size = rhbz_array_size(all_bugs);
+ if (all_bugs_size > 0)
+ {
+ bug_id = rhbz_bug_id(all_bugs);
+ bz = rhbz_bug_info(client, bug_id);
+ }
+ xmlrpc_DECREF(all_bugs);
+ }
+
+ }
+ free(product);
+
+ if (all_bugs_size == 0) // Create new bug
+ {
+ log(_("Creating a new bug"));
+ bug_id = rhbz_new_bug(client, problem_data, bug_id);
+
+ log("Adding attachments to bug %i", bug_id);
+ char bug_id_str[sizeof(int)*3 + 2];
+ sprintf(bug_id_str, "%i", bug_id);
+
+ rhbz_attachments(client, bug_id_str, problem_data);
+
+ log(_("Logging out"));
+ rhbz_logout(client);
+
+ log("Status: NEW %s/show_bug.cgi?id=%u", bugzilla_url, bug_id);
+ abrt_xmlrpc_free_client(client);
+ return;
+ }
+
+ // decision based on state
+ log(_("Bug is already reported: %i"), bz->bi_id);
+ if ((strcmp(bz->bi_status, "CLOSED") == 0)
+ && (strcmp(bz->bi_resolution, "DUPLICATE") == 0))
+ {
+ struct bug_info *origin;
+ origin = rhbz_find_origin_bug_closed_duplicate(client, bz);
+ if (origin)
+ {
+ free_bug_info(bz);
+ bz = origin;
+ }
+ }
+
+ if (strcmp(bz->bi_status, "CLOSED") != 0)
+ {
+ if ((strcmp(bz->bi_reporter, login) != 0)
+ && (!g_list_find_custom(bz->bi_cc_list, login, (GCompareFunc)g_strcmp0)))
+ {
+ log(_("Add %s to CC list"), login);
+ rhbz_mail_to_cc(client, bz->bi_id, login);
+ }
+
+ char *dsc = make_description_comment(problem_data);
+ if (dsc)
+ {
+ const char *package = get_problem_item_content_or_NULL(problem_data,
+ FILENAME_PACKAGE);
+ const char *release = get_problem_item_content_or_NULL(problem_data,
+ FILENAME_OS_RELEASE);
+ if (!release) /* Old dump dir format compat. Remove in abrt-2.1 */
+ release = get_problem_item_content_or_NULL(problem_data, "release");
+ const char *arch = get_problem_item_content_or_NULL(problem_data,
+ FILENAME_ARCHITECTURE);
+ const char *is_private = get_problem_item_content_or_NULL(problem_data,
+ "is_private");
+
+ char *full_dsc = xasprintf("Package: %s\n"
+ "Architecture: %s\n"
+ "OS Release: %s\n"
+ "%s", package, arch, release, dsc);
+
+ log(_("Adding new comment to bug %d"), bz->bi_id);
+ free(dsc);
+
+ int is_priv = is_private && string_to_bool(is_private);
+ rhbz_add_comment(client, bz->bi_id, full_dsc, is_priv);
+ free(full_dsc);
+ }
+ }
+
+ log(_("Logging out"));
+ rhbz_logout(client);
+
+ log("Status: %s%s%s %s/show_bug.cgi?id=%u",
+ bz->bi_status,
+ bz->bi_resolution ? " " : "",
+ bz->bi_resolution ? bz->bi_resolution : "",
+ bugzilla_url,
+ bz->bi_id);
+
+ dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (dd)
+ {
+ char *msg = xasprintf("Bugzilla: URL=%s/show_bug.cgi?id=%u", bugzilla_url, bz->bi_id);
+ add_reported_to(dd, msg);
+ free(msg);
+ dd_close(dd);
+ }
+
+ free_problem_data(problem_data);
+ free_bug_info(bz);
+ abrt_xmlrpc_free_client(client);
+}
+
+int main(int argc, char **argv)
+{
+ abrt_init(argv);
+
+ map_string_h *settings = new_map_string();
+ const char *dump_dir_name = ".";
+ GList *conf_file = NULL;
+
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ "\b [-v] -c CONFFILE -d DIR\n"
+ "\n"
+ "Reports problem to Bugzilla"
+ );
+ enum {
+ OPT_v = 1 << 0,
+ OPT_d = 1 << 1,
+ OPT_c = 1 << 2,
+ };
+ /* 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" , _("Dump directory")),
+ OPT_LIST( 'c', NULL, &conf_file , "FILE", _("Configuration file (may be given many times)")),
+ OPT_END()
+ };
+ /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
+
+ export_abrt_envvars(0);
+
+ while (conf_file)
+ {
+ char *fn = (char *)conf_file->data;
+ VERB1 log("Loading settings from '%s'", fn);
+ load_conf_file(fn, settings, /*skip key w/o values:*/ true);
+ VERB3 log("Loaded '%s'", fn);
+ conf_file = g_list_remove(conf_file, fn);
+ }
+
+ VERB1 log("Initializing XML-RPC library");
+ xmlrpc_env env;
+ xmlrpc_env_init(&env);
+ xmlrpc_client_setup_global_const(&env);
+ if (env.fault_occurred)
+ error_msg_and_die("XML-RPC Fault: %s(%d)", env.fault_string, env.fault_code);
+ xmlrpc_env_clean(&env);
+
+ report_to_bugzilla(dump_dir_name, settings);
+
+ free_map_string(settings);
+ return 0;
+}