diff options
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/Makefile.am | 4 | ||||
-rw-r--r-- | src/plugins/abrt-action-bugzilla.c | 340 | ||||
-rw-r--r-- | src/plugins/abrt-action-bugzilla.cpp | 958 | ||||
-rwxr-xr-x | src/plugins/abrt-action-install-debuginfo.py | 4 | ||||
-rw-r--r-- | src/plugins/abrt-action-mailx.txt | 2 | ||||
-rw-r--r-- | src/plugins/abrt-retrace-client.c | 4 | ||||
-rw-r--r-- | src/plugins/abrt_rh_support.c | 2 | ||||
-rw-r--r-- | src/plugins/ccpp_events.conf | 4 | ||||
-rw-r--r-- | src/plugins/rhbz.c | 482 | ||||
-rw-r--r-- | src/plugins/rhbz.h | 100 |
10 files changed, 932 insertions, 968 deletions
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index 1ef7fbc2..df0ad944 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -239,7 +239,7 @@ abrt_action_analyze_backtrace_LDADD = \ ../btparser/libbtparser.la abrt_action_bugzilla_SOURCES = \ - abrt-action-bugzilla.cpp + abrt-action-bugzilla.c rhbz.c rhbz.h abrt_action_bugzilla_CPPFLAGS = \ -I$(srcdir)/../include/report -I$(srcdir)/../include \ -I$(srcdir)/../lib \ @@ -253,7 +253,7 @@ abrt_action_bugzilla_CPPFLAGS = \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Wwrite-strings -Werror + -Wall -Wwrite-strings abrt_action_bugzilla_LDADD = \ $(GLIB_LIBS) \ ../lib/libabrt_web.la \ 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; +} diff --git a/src/plugins/abrt-action-bugzilla.cpp b/src/plugins/abrt-action-bugzilla.cpp deleted file mode 100644 index e8a605f1..00000000 --- a/src/plugins/abrt-action-bugzilla.cpp +++ /dev/null @@ -1,958 +0,0 @@ -/* - 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_xmlrpc.h" -#include "abrt_problem_data.h" -#include "parse_options.h" - -#define XML_RPC_SUFFIX "/xmlrpc.cgi" -#define MAX_HOPS 5 - -/* - * TODO: npajkovs: better deallocation of xmlrpc value - * npajkovs: better gathering function which collects all information from bugzilla - * npajkovs: figure out how to deal with cloning bugs - * npajkovs: check if attachment was uploaded successul an if not try it again(max 3 times) - * and if it still fails. retrun successful, but mention that attaching failed - * npajkovs: add option to set comment privat - */ - -struct bug_info { - const char* bug_status; - const char* bug_resolution; - const char* bug_reporter; - const char* bug_product; - xmlrpc_int32 bug_dup_id; - GList* bug_cc; -}; - -/* xzalloc */ -static void bug_info_init(struct bug_info* bz) -{ - bz->bug_status = NULL; - bz->bug_resolution = NULL; - bz->bug_reporter = NULL; - bz->bug_product = NULL; - bz->bug_dup_id = -1; - bz->bug_cc = NULL; -} - -static void bug_info_destroy(struct bug_info* bz) -{ - free((void*)bz->bug_status); - free((void*)bz->bug_resolution); - free((void*)bz->bug_reporter); - free((void*)bz->bug_product); - - list_free_with_free(bz->bug_cc); -} - -/* - * Static namespace for xmlrpc stuff. - * Used mainly to ensure we always destroy xmlrpc client and server_info. - */ - -namespace { - -struct ctx: public abrt_xmlrpc_conn { - xmlrpc_env env; - - ctx(const char* url, bool ssl_verify): abrt_xmlrpc_conn(url, ssl_verify) - { xmlrpc_env_init(&env); } - ~ctx() { xmlrpc_env_clean(&env); } - - void login(const char* login, const char* passwd); - void logout(); - - const char* get_bug_status(xmlrpc_value* result_xml); - const char* get_bug_resolution(xmlrpc_value* result_xml); - const char* get_bug_reporter(xmlrpc_value* result_xml); - const char* get_bug_product(xmlrpc_value* relult_xml); - - xmlrpc_value* call_quicksearch_duphash(const char* component, const char* release, const char* duphash); - xmlrpc_value* get_cc_member(xmlrpc_value* result_xml); - xmlrpc_value* get_member(const char* member, xmlrpc_value* result_xml); - - int get_array_size(xmlrpc_value* result_xml); - xmlrpc_int32 get_bug_id(xmlrpc_value* result_xml); - xmlrpc_int32 get_bug_dup_id(xmlrpc_value* result_xml); - void get_bug_cc(xmlrpc_value* result_xml, struct bug_info* bz); - int add_plus_one_cc(xmlrpc_int32 bug_id, const char* login); - xmlrpc_int32 new_bug(problem_data_t *problem_data, int depend_on_bugno); - int add_attachments(const char* bug_id_str, problem_data_t *problem_data); - int get_bug_info(struct bug_info* bz, xmlrpc_int32 bug_id); - int add_comment(xmlrpc_int32 bug_id, const char* comment, bool is_private); - - xmlrpc_value* call(const char* method, const char* format, ...); -}; - -xmlrpc_value* ctx::call(const char* method, const char* format, ...) -{ - xmlrpc_value* result = NULL; - - if (!env.fault_occurred) - { - xmlrpc_value* param = NULL; - va_list args; - const char* suffix; - - va_start(args, format); - xmlrpc_build_value_va(&env, format, args, ¶m, &suffix); - va_end(args); - - if (*suffix != '\0') - { - xmlrpc_env_set_fault_formatted( - &env, XMLRPC_INTERNAL_ERROR, "Junk after the argument " - "specifier: '%s'. There must be exactly one arument.", - suffix); - } - else - { - xmlrpc_client_call2(&env, m_pClient, m_pServer_info, method, param, &result); - } - xmlrpc_DECREF(param); - if (env.fault_occurred) - return NULL; - } - - return result; -} - -xmlrpc_value* ctx::get_member(const char* member, xmlrpc_value* result_xml) -{ - xmlrpc_value* cc_member = NULL; - xmlrpc_struct_find_value(&env, result_xml, member, &cc_member); - if (env.fault_occurred) - return NULL; - - return cc_member; -} - -int ctx::get_array_size(xmlrpc_value* result_xml) -{ - int size = xmlrpc_array_size(&env, result_xml); - if (env.fault_occurred) - return -1; - - return size; -} - -xmlrpc_int32 ctx::get_bug_dup_id(xmlrpc_value* result_xml) -{ - xmlrpc_value* dup_id = get_member("dup_id", result_xml); - if (!dup_id) - return -1; - - xmlrpc_int32 dup_id_int = -1; - xmlrpc_read_int(&env, dup_id, &dup_id_int); - xmlrpc_DECREF(dup_id); - if (env.fault_occurred) - return -1; - - VERB3 log("got dup_id: %i", dup_id_int); - return dup_id_int; -} - -const char* ctx::get_bug_product(xmlrpc_value* result_xml) -{ - xmlrpc_value* product_member = get_member("product", result_xml); - if (!product_member) //should never happend. Each bug has to set up product - return NULL; - - const char* product = NULL; - xmlrpc_read_string(&env, product_member, &product); - xmlrpc_DECREF(product_member); - if (env.fault_occurred) - return NULL; - - if (*product != '\0') - { - VERB3 log("got bug product: %s", product); - return product; - } - - free((void*)product); - return NULL; -} - -const char* ctx::get_bug_reporter(xmlrpc_value* result_xml) -{ - xmlrpc_value* reporter_member = get_member("reporter", result_xml); - if (!reporter_member) - return NULL; - - const char* reporter = NULL; - xmlrpc_read_string(&env, reporter_member, &reporter); - xmlrpc_DECREF(reporter_member); - if (env.fault_occurred) - return NULL; - - if (*reporter != '\0') - { - VERB3 log("got bug reporter: %s", reporter); - return reporter; - } - free((void*)reporter); - return NULL; -} - -const char* ctx::get_bug_resolution(xmlrpc_value* result_xml) -{ - xmlrpc_value* bug_resolution = get_member("resolution", result_xml); - if (!bug_resolution) - return NULL; - - const char* resolution_str = NULL; - xmlrpc_read_string(&env, bug_resolution, &resolution_str); - xmlrpc_DECREF(bug_resolution); - if (env.fault_occurred) - return NULL; - - if (*resolution_str != '\0') - { - VERB3 log("got resolution: %s", resolution_str); - return resolution_str; - } - free((void*)resolution_str); - return NULL; -} - -const char* ctx::get_bug_status(xmlrpc_value* result_xml) -{ - xmlrpc_value* bug_status = get_member("bug_status", result_xml); - if (!bug_status) - return NULL; - - const char* status_str = NULL; - xmlrpc_read_string(&env, bug_status, &status_str); - xmlrpc_DECREF(bug_status); - if (env.fault_occurred) - return NULL; - - if (*status_str != '\0') - { - VERB3 log("got bug_status: %s", status_str); - return status_str; - } - free((void*)status_str); - return NULL; -} - -void ctx::get_bug_cc(xmlrpc_value* result_xml, struct bug_info* bz) -{ - xmlrpc_value* cc_member = get_member("cc", result_xml); - if (!cc_member) - return; - - int array_size = xmlrpc_array_size(&env, cc_member); - if (array_size == -1) - return; - - VERB3 log("count members on cc %i", array_size); - - for (int i = 0; i < array_size; i++) - { - xmlrpc_value* item = NULL; - xmlrpc_array_read_item(&env, cc_member, i, &item); - if (env.fault_occurred) - return; - - if (item) - { - const char* cc = NULL; - xmlrpc_read_string(&env, item, &cc); - xmlrpc_DECREF(item); - if (env.fault_occurred) - { - xmlrpc_DECREF(cc_member); - return; - } - - if (*cc != '\0') - { - bz->bug_cc = g_list_append(bz->bug_cc, (char*)cc); - VERB3 log("member on cc is %s", cc); - continue; - } - free((char*)cc); - } - } - xmlrpc_DECREF(cc_member); - return; -} - -xmlrpc_value* ctx::call_quicksearch_duphash(const char* component, - const char* release, const char* duphash) -{ - char *query = NULL; - if (!release) - query = xasprintf("ALL component:\"%s\" whiteboard:\"%s\"", component, duphash); - else - { - char *product = NULL; - char *version = NULL; - parse_release_for_bz(release, &product, &version); - query = xasprintf("ALL component:\"%s\" whiteboard:\"%s\" product:\"%s\"", - component, duphash, product - ); - free(product); - free(version); - } - - VERB3 log("quicksearch for `%s'", query); - xmlrpc_value *ret = call("Bug.search", "({s:s})", "quicksearch", query); - free(query); - return ret; -} - -xmlrpc_int32 ctx::get_bug_id(xmlrpc_value* result_xml) -{ - xmlrpc_value* item = NULL; - xmlrpc_array_read_item(&env, result_xml, 0, &item); - if (env.fault_occurred) - return -1; - - xmlrpc_value* bug = get_member("bug_id", item); - xmlrpc_DECREF(item); - if (!bug) - return -1; - - xmlrpc_int32 bug_id = -1; - xmlrpc_read_int(&env, bug, &bug_id); - xmlrpc_DECREF(bug); - if (env.fault_occurred) - return -1; - - VERB3 log("got bug_id %d", (int)bug_id); - return bug_id; -} - -int ctx::add_plus_one_cc(xmlrpc_int32 bug_id, const char* login) -{ - xmlrpc_value* result = call("Bug.update", "({s:i,s:{s:(s)}})", "ids", (int)bug_id, "updates", "add_cc", login); - if (result) - xmlrpc_DECREF(result); - return result ? 0 : -1; -} - -int ctx::add_comment(xmlrpc_int32 bug_id, const char* comment, bool is_private) -{ - xmlrpc_value* result = call("Bug.add_comment", "({s:i,s:s,s:b})", "id", (int)bug_id, - "comment", comment, - "private", is_private); - if (result) - xmlrpc_DECREF(result); - return result ? 0 : -1; -} - -/* 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, -}; - -static const char *tainted_string(unsigned tainted) -{ - unsigned idx = 0; - while ((tainted >>= 1) != 0) - idx++; - - return taint_warnings[idx]; -} - -xmlrpc_int32 ctx::new_bug(problem_data_t *problem_data, int depend_on_bugno) -{ - const char *package = get_problem_item_content_or_NULL(problem_data, FILENAME_PACKAGE); - const char *component = get_problem_item_content_or_NULL(problem_data, FILENAME_COMPONENT); - 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 *duphash = get_problem_item_content_or_NULL(problem_data, FILENAME_DUPHASH); - const char *reason = get_problem_item_content_or_NULL(problem_data, FILENAME_REASON); - const char *function = get_problem_item_content_or_NULL(problem_data, FILENAME_CRASH_FUNCTION); - const char *analyzer = get_problem_item_content_or_NULL(problem_data, FILENAME_ANALYZER); - const char *tainted_str = get_problem_item_content_or_NULL(problem_data, FILENAME_TAINTED); - - struct strbuf *buf_summary = strbuf_new(); - strbuf_append_strf(buf_summary, "[abrt] %s", package); - - if (function != NULL && strlen(function) < 30) - strbuf_append_strf(buf_summary, ": %s", function); - - if (reason != NULL) - strbuf_append_strf(buf_summary, ": %s", reason); - - if (tainted_str && analyzer - && (strcmp(analyzer, "Kerneloops") == 0) - ) { - unsigned long tainted = xatoi_positive(tainted_str); - const char *tainted_warning = tainted_string(tainted); - if (tainted_warning) - strbuf_append_strf(buf_summary, ": TAINTED %s", tainted_warning); - } - - char *status_whiteboard = xasprintf("abrt_hash:%s", duphash); - - char *bz_dsc = make_description_bz(problem_data); - char *full_dsc = xasprintf("abrt version: "VERSION"\n%s", bz_dsc); - free(bz_dsc); - - char *product = NULL; - char *version = NULL; - parse_release_for_bz(release, &product, &version); - - xmlrpc_value* result = NULL; - char *summary = strbuf_free_nobuf(buf_summary); - if (depend_on_bugno > -1) - { - result = call("Bug.create", "({s:s,s:s,s:s,s:s,s:s,s:s,s:s,s:i})", - "product", product, - "component", component, - "version", version, - "summary", summary, - "description", full_dsc, - "status_whiteboard", status_whiteboard, - "platform", arch, - "dependson", depend_on_bugno - ); - } - else - { - result = call("Bug.create", "({s:s,s:s,s:s,s:s,s:s,s:s,s:s})", - "product", product, - "component", component, - "version", version, - "summary", summary, - "description", full_dsc, - "status_whiteboard", status_whiteboard, - "platform", arch - ); - } - free(status_whiteboard); - free(product); - free(version); - free(summary); - free(full_dsc); - - if (!result) - return -1; - - xmlrpc_value* id = get_member("id", result); - xmlrpc_DECREF(result); - if (!id) - return -1; - - xmlrpc_int32 bug_id = -1; - xmlrpc_read_int(&env, id, &bug_id); - xmlrpc_DECREF(id); - if (env.fault_occurred) - return -1; - - log(_("New bug id: %i"), (int)bug_id); - - return bug_id; -} - -int ctx::add_attachments(const char* bug_id_str, problem_data_t *problem_data) -{ - GHashTableIter iter; - char *name; - struct problem_item *value; - g_hash_table_iter_init(&iter, problem_data); - while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value)) - { - const char *content = value->content; - - // We were special-casing FILENAME_BACKTRACE here, but Karel says - // he can retrieve it in inlined form from comments too. - if ((value->flags & CD_FLAG_TXT) - && (strlen(content) > CD_TEXT_ATT_SIZE /*|| (strcmp(name, FILENAME_BACKTRACE) == 0)*/) - ) { - char *encoded64 = encode_base64(content, strlen(content)); - char *filename = xasprintf("File: %s", name); - xmlrpc_value* result = call("bugzilla.addAttachment", "(s{s:s,s:s,s:s,s:s})", bug_id_str, - "description", filename, - "filename", name, - "contenttype", "text/plain", - "data", encoded64 - ); - free(encoded64); - free(filename); - if (!result) - return -1; - - xmlrpc_DECREF(result); - } - } - return 0; -} - -int ctx::get_bug_info(struct bug_info* bz, xmlrpc_int32 bug_id) -{ - char bug_id_str[sizeof(long)*3 + 2]; - sprintf(bug_id_str, "%lu", (long)bug_id); - xmlrpc_value* result = call("bugzilla.getBug", "(s)", bug_id_str); - if (!result) - return -1; - - bz->bug_product = get_bug_product(result); - if (bz->bug_product == NULL) - return -1; - - bz->bug_status = get_bug_status(result); - if (bz->bug_status == NULL) - return -1; - - bz->bug_reporter = get_bug_reporter(result); - if (bz->bug_reporter == NULL) - return -1; - - // mandatory when bug status is CLOSED - if (strcmp(bz->bug_status, "CLOSED") == 0) - { - bz->bug_resolution = get_bug_resolution(result); - if ((env.fault_occurred) && (bz->bug_resolution == NULL)) - return -1; - } - - // mandatory when bug status is CLOSED and resolution is DUPLICATE - if ((strcmp(bz->bug_status, "CLOSED") == 0) - && (strcmp(bz->bug_resolution, "DUPLICATE") == 0) - ) { - bz->bug_dup_id = get_bug_dup_id(result); - if (env.fault_occurred) - return -1; - } - - get_bug_cc(result, bz); - if (env.fault_occurred) - return -1; - - xmlrpc_DECREF(result); - return 0; -} - -void ctx::login(const char* login, const char* passwd) -{ - xmlrpc_value* result = call("User.login", "({s:s,s:s})", "login", login, "password", passwd); -//TODO: with URL like http://bugzilla.redhat.com (that is, with http: instead of https:) -//we are getting this error: -//Logging into Bugzilla at http://bugzilla.redhat.com -//Can't login. Server said: HTTP response code is 301, not 200 -//But this is a 301 redirect! We _can_ follow it if we configure curl to understand that! - if (!result) - error_msg_and_die("Can't login. Server said: %s", env.fault_string); - xmlrpc_DECREF(result); -} - -void ctx::logout() -{ - xmlrpc_value* result = call("User.logout", "(s)", ""); - if (result) - xmlrpc_DECREF(result); - - throw_if_xml_fault_occurred(&env); -} - -} /* namespace */ - - -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"); - - ctx bz_server(bugzilla_xmlrpc, ssl_verify); - - log(_("Logging into Bugzilla at %s"), bugzilla_url); - bz_server.login(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 = bz_server.call_quicksearch_duphash(component, product, duphash); - else - result = bz_server.call_quicksearch_duphash(component, NULL, duphash); - - if (!result) - throw_if_xml_fault_occurred(&bz_server.env); - - xmlrpc_value *all_bugs = bz_server.get_member("bugs", result); - xmlrpc_DECREF(result); - - if (!all_bugs) - { - throw_if_xml_fault_occurred(&bz_server.env); - error_msg_and_die(_("Missing mandatory member 'bugs'")); - } - - xmlrpc_int32 bug_id = -1; - int all_bugs_size = bz_server.get_array_size(all_bugs); - struct bug_info bz; - int depend_on_bugno = -1; - if (all_bugs_size > 0) - { - bug_id = bz_server.get_bug_id(all_bugs); - xmlrpc_DECREF(all_bugs); - if (bug_id == -1) - throw_if_xml_fault_occurred(&bz_server.env); - - bug_info_init(&bz); - if (bz_server.get_bug_info(&bz, bug_id) == -1) - { - bug_info_destroy(&bz); - throw_if_xml_fault_occurred(&bz_server.env); - error_msg_and_die(_("get_bug_info() failed. Could not collect all mandatory information")); - } - - if (strcmp(bz.bug_product, product) != 0) - { - depend_on_bugno = bug_id; - bug_info_destroy(&bz); - result = bz_server.call_quicksearch_duphash(component, release, duphash); - if (!result) - throw_if_xml_fault_occurred(&bz_server.env); - - all_bugs = bz_server.get_member("bugs", result); - xmlrpc_DECREF(result); - - if (!all_bugs) - { - throw_if_xml_fault_occurred(&bz_server.env); - error_msg_and_die(_("Missing mandatory member 'bugs'")); - } - - all_bugs_size = bz_server.get_array_size(all_bugs); - if (all_bugs_size > 0) - { - bug_id = bz_server.get_bug_id(all_bugs); - xmlrpc_DECREF(all_bugs); - if (bug_id == -1) - throw_if_xml_fault_occurred(&bz_server.env); - - bug_info_init(&bz); - if (bz_server.get_bug_info(&bz, bug_id) == -1) - { - bug_info_destroy(&bz); - throw_if_xml_fault_occurred(&bz_server.env); - error_msg_and_die(_("get_bug_info() failed. Could not collect all mandatory information")); - } - } - else - xmlrpc_DECREF(all_bugs); - } - } - free(product); - - if (all_bugs_size < 0) - { - throw_if_xml_fault_occurred(&bz_server.env); - } - else if (all_bugs_size == 0) // Create new bug - { - log(_("Creating a new bug")); - bug_id = bz_server.new_bug(problem_data, depend_on_bugno); - if (bug_id < 0) - { - throw_if_xml_fault_occurred(&bz_server.env); - error_msg_and_die(_("Bugzilla entry creation failed")); - } - - log("Adding attachments to bug %ld", (long)bug_id); - char bug_id_str[sizeof(long)*3 + 2]; - sprintf(bug_id_str, "%ld", (long) bug_id); - int ret = bz_server.add_attachments(bug_id_str, problem_data); - if (ret == -1) - { - throw_if_xml_fault_occurred(&bz_server.env); - } - - log(_("Logging out")); - bz_server.logout(); - - log("Status: NEW %s/show_bug.cgi?id=%u", - bugzilla_url, - (int)bug_id - ); - return; - } - - if (all_bugs_size > 1) - { - // 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 %u reports with same duphash '%s'", all_bugs_size, duphash); - } - - // decision based on state - log(_("Bug is already reported: %i"), bug_id); - - xmlrpc_int32 original_bug_id = bug_id; - if ((strcmp(bz.bug_status, "CLOSED") == 0) && (strcmp(bz.bug_resolution, "DUPLICATE") == 0)) - { - for (int ii = 0; ii <= MAX_HOPS; ii++) - { - if (ii == MAX_HOPS) - { - VERB3 log("Bugzilla could not find a parent of bug %d", (int)original_bug_id); - bug_info_destroy(&bz); - error_msg_and_die(_("Bugzilla couldn't find parent of bug %d"), (int)original_bug_id); - } - - log("Bug %d is a duplicate, using parent bug %d", bug_id, (int)bz.bug_dup_id); - bug_id = bz.bug_dup_id; - bug_info_destroy(&bz); - bug_info_init(&bz); - - if (bz_server.get_bug_info(&bz, bug_id) == -1) - { - bug_info_destroy(&bz); - if (bz_server.env.fault_occurred) - { - throw_if_xml_fault_occurred(&bz_server.env); - } - error_msg_and_die(_("get_bug_info() failed. Could not collect all mandatory information")); - } - - // found a bug which is not CLOSED as DUPLICATE - if (bz.bug_dup_id == -1) - break; - } - } - - if (strcmp(bz.bug_status, "CLOSED") != 0) - { - int status = 0; - if ((strcmp(bz.bug_reporter, login) != 0) - && (g_list_find(bz.bug_cc, login))) - { - log(_("Add %s to CC list"), login); - status = bz_server.add_plus_one_cc(bug_id, login); - } - - if (status == -1) - { - bug_info_destroy(&bz); - throw_if_xml_fault_occurred(&bz_server.env); - } - - 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"), (int)bug_id); - - free(dsc); - - bool is_priv = is_private && string_to_bool(is_private); - if (bz_server.add_comment(bug_id, full_dsc, is_priv) == -1) - { - free(full_dsc); - bug_info_destroy(&bz); - throw_xml_fault(&bz_server.env); - } - free(full_dsc); - } - } - - log(_("Logging out")); - bz_server.logout(); - - log("Status: %s%s%s %s/show_bug.cgi?id=%u", - bz.bug_status, - bz.bug_resolution ? " " : "", - bz.bug_resolution ? bz.bug_resolution : "", - bugzilla_url, - (int)bug_id - ); - - dd = dd_opendir(dump_dir_name, /*flags:*/ 0); - if (dd) - { - char *msg = xasprintf("Bugzilla: URL=%s/show_bug.cgi?id=%u", bugzilla_url, (int)bug_id); - add_reported_to(dd, msg); - free(msg); - dd_close(dd); - } - - free_problem_data(problem_data); - bug_info_destroy(&bz); -} - -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; -} diff --git a/src/plugins/abrt-action-install-debuginfo.py b/src/plugins/abrt-action-install-debuginfo.py index a7968488..e15fc12b 100755 --- a/src/plugins/abrt-action-install-debuginfo.py +++ b/src/plugins/abrt-action-install-debuginfo.py @@ -96,7 +96,7 @@ def unpack_rpm(package_nevra, files, tmp_dir, destdir, keeprpm): package_name = package_nevra + ".rpm" package_full_path = tmp_dir + "/" + package_name log1("Extracting %s to %s", package_full_path, destdir) - log2(files) + log2("%s", files) print _("Extracting cpio from %s") % (package_full_path) unpacked_cpio_path = tmp_dir + "/unpacked.cpio" try: @@ -492,7 +492,7 @@ if __name__ == "__main__": missing = filter_installed_debuginfos(b_ids, cachedir) if missing: - log2(missing) + log2("%s", missing) print _("Coredump references %u debuginfo files, %u of them are not installed") % (len(b_ids), len(missing)) # TODO: should we pass keep_rpms=keeprpms to DebugInfoDownload here?? diff --git a/src/plugins/abrt-action-mailx.txt b/src/plugins/abrt-action-mailx.txt index 2fb11bef..a12c2bf1 100644 --- a/src/plugins/abrt-action-mailx.txt +++ b/src/plugins/abrt-action-mailx.txt @@ -53,7 +53,7 @@ problem happens. When this is desired, modify the event configuration file to run the tool on the 'post-create' event: ------------ -EVENT=post-create abrt-action-mailx +EVENT=post-create abrt-action-mailx ------------ OPTIONS diff --git a/src/plugins/abrt-retrace-client.c b/src/plugins/abrt-retrace-client.c index aa232475..524fa186 100644 --- a/src/plugins/abrt-retrace-client.c +++ b/src/plugins/abrt-retrace-client.c @@ -103,7 +103,7 @@ static int create_archive(bool unlink_temp) xmove_fd(tar_xz_pipe[0], STDIN_FILENO); xdup2(tempfd, STDOUT_FILENO); execvp(xz_args[0], (char * const*)xz_args); - perror_msg("Can't execute '%s'", xz_args[0]); + perror_msg("Can't execute '%s'", xz_args[0]); } close(tar_xz_pipe[0]); @@ -130,7 +130,7 @@ static int create_archive(bool unlink_temp) xmove_fd(xopen("/dev/null", O_RDWR), STDIN_FILENO); xmove_fd(tar_xz_pipe[1], STDOUT_FILENO); execvp(tar_args[0], (char * const*)tar_args); - perror_msg("Can't execute '%s'", tar_args[0]); + perror_msg("Can't execute '%s'", tar_args[0]); } close(tar_xz_pipe[1]); diff --git a/src/plugins/abrt_rh_support.c b/src/plugins/abrt_rh_support.c index 9a48485b..b83f041e 100644 --- a/src/plugins/abrt_rh_support.c +++ b/src/plugins/abrt_rh_support.c @@ -421,7 +421,7 @@ send_report_to_new_case(const char* baseURL, atch_state->http_resp_code, errmsg ? ": " : "", errmsg ? errmsg : "" - ); + ); break; case 200: diff --git a/src/plugins/ccpp_events.conf b/src/plugins/ccpp_events.conf index eb3384a6..b56601e9 100644 --- a/src/plugins/ccpp_events.conf +++ b/src/plugins/ccpp_events.conf @@ -9,7 +9,7 @@ EVENT=post-create analyzer=CCpp test -r /var/log/messages || exit 0 executable=`cat executable` && base_executable=${executable##*/} && - grep -e "$base_executable" /var/log/messages | tail -999 >var_log_messages && + grep -F -e "$base_executable" /var/log/messages | tail -999 >var_log_messages && echo "Element 'var_log_messages' saved" ) @@ -18,7 +18,7 @@ EVENT=analyze_xsession_errors analyzer=CCpp test -r ~/.xsession-errors || { echo "Can't read ~/.xsession-errors"; exit 1; } executable=`cat executable` && base_executable=${executable##*/} && - grep -e "$base_executable" ~/.xsession-errors | tail -999 >xsession_errors && + grep -F -e "$base_executable" ~/.xsession-errors | tail -999 >xsession_errors && echo "Element 'xsession_errors' saved" # TODO: can we still specify additional directories to search for debuginfos, diff --git a/src/plugins/rhbz.c b/src/plugins/rhbz.c new file mode 100644 index 00000000..90587e5e --- /dev/null +++ b/src/plugins/rhbz.c @@ -0,0 +1,482 @@ +/* + Copyright (C) 2011 ABRT team + Copyright (C) 2011 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 "rhbz.h" + +#define MAX_HOPS 5 + +struct bug_info *new_bug_info() +{ + struct bug_info *bi = xzalloc(sizeof(struct bug_info)); + bi->bi_dup_id = -1; + + return bi; +} + +void free_bug_info(struct bug_info *bi) +{ + if (!bi) + return; + + free((void*)bi->bi_status); + free((void*)bi->bi_resolution); + free((void*)bi->bi_reporter); + free((void*)bi->bi_product); + + list_free_with_free(bi->bi_cc_list); + + bi->bi_status = NULL; + bi->bi_resolution = NULL; + bi->bi_reporter = NULL; + bi->bi_product = NULL; + + bi->bi_cc_list = NULL; + + free(bi); +} + +void rhbz_login(struct abrt_xmlrpc *ax, const char* login, const char* passwd) +{ + xmlrpc_value* result = abrt_xmlrpc_call(ax, "User.login", "({s:s,s:s})", + "login", login, "password", passwd); + +//TODO: with URL like http://bugzilla.redhat.com (that is, with http: instead of https:) +//we are getting this error: +//Logging into Bugzilla at http://bugzilla.redhat.com +//Can't login. Server said: HTTP response code is 301, not 200 +//But this is a 301 redirect! We _can_ follow it if we configure curl to understand that! + xmlrpc_DECREF(result); +} + +xmlrpc_value *rhbz_search_duphash(struct abrt_xmlrpc *ax, const char *component, + const char *product, const char *duphash) +{ + char *query = NULL; + if (!product) + query = xasprintf("ALL component:\"%s\" whiteboard:\"%s\"", component, duphash); + else + query = xasprintf("ALL component:\"%s\" whiteboard:\"%s\" product:\"%s\"", + component, duphash, product); + + VERB3 log("search for '%s'", query); + xmlrpc_value *ret = abrt_xmlrpc_call(ax, "Bug.search", "({s:s})", + "quicksearch", query); + free(query); + return ret; +} + +xmlrpc_value *rhbz_get_member(const char *member, xmlrpc_value *xml) +{ + xmlrpc_env env; + xmlrpc_env_init(&env); + + xmlrpc_value *value = NULL; + /* The xmlrpc_struct_find_value functions consider "not found" to be + * a normal result. If a member of the structure with the specified key + * exists, it returns it as a handle to an xmlrpc_value. If not, it returns + * NULL in place of that handle. + */ + xmlrpc_struct_find_value(&env, xml, member, &value); + if (env.fault_occurred) + abrt_xmlrpc_error(&env); + + return value; +} + +/* The only way this can fail is if arrayP is not actually an array XML-RPC + * value. So it is usually not worth checking *envP. + * die or return size of array + */ +int rhbz_array_size(xmlrpc_value *xml) +{ + xmlrpc_env env; + xmlrpc_env_init(&env); + + int size = xmlrpc_array_size(&env, xml); + if (env.fault_occurred) + abrt_xmlrpc_die(&env); + + return size; +} + +/* die or return bug id; each bug must have bug id otherwise xml is corrupted */ +int rhbz_bug_id(xmlrpc_value* xml) +{ + xmlrpc_env env; + xmlrpc_env_init(&env); + + xmlrpc_value *item = NULL; + xmlrpc_value *bug = NULL; + int bug_id = -1;; + + xmlrpc_array_read_item(&env, xml, 0, &item); + if (env.fault_occurred) + abrt_xmlrpc_die(&env); + + bug = rhbz_get_member("bug_id", item); + xmlrpc_DECREF(item); + if (!bug) + abrt_xmlrpc_die(&env); + + xmlrpc_read_int(&env, bug, &bug_id); + xmlrpc_DECREF(bug); + if (env.fault_occurred) + abrt_xmlrpc_die(&env); + + VERB3 log("found bug_id %i", bug_id); + return bug_id; +} + +/* die when mandatory value is missing (set flag RHBZ_MANDATORY_MEMB) + * or return appropriate string or NULL when fail; + */ +// TODO: npajkovs: add flag to read xmlrpc_read_array_item first +void *rhbz_bug_read_item(const char *memb, xmlrpc_value *xml, int flags) +{ + xmlrpc_env env; + xmlrpc_env_init(&env); + + xmlrpc_value *member = rhbz_get_member(memb, xml); + + const char *string = NULL; + + if (!member) + goto die; + + if (IS_READ_STR(flags)) + { + xmlrpc_read_string(&env, member, &string); + xmlrpc_DECREF(member); + if (env.fault_occurred) + abrt_xmlrpc_die(&env); + + if (!*string) + goto die; + + VERB3 log("found %s: '%s'", memb, string); + return (void*)string; + } + + { + if (IS_READ_INT(flags)) + { + int *integer = xmalloc(sizeof(int)); + xmlrpc_read_int(&env, member, integer); + xmlrpc_DECREF(member); + if (env.fault_occurred) + abrt_xmlrpc_die(&env); + + VERB3 log("found %s: '%i'", memb, *integer); + return (void*)integer; + } + } +die: + free((void*)string); + if (IS_MANDATORY(flags)) + error_msg_and_die(_("Looks like corrupted xml response, because '%s'" + " member is missing."), memb); + + return NULL; +} + +GList *rhbz_bug_cc(xmlrpc_value* result_xml) +{ + xmlrpc_env env; + xmlrpc_env_init(&env); + + xmlrpc_value* cc_member = rhbz_get_member("cc", result_xml); + if (!cc_member) + return NULL; + + int array_size = rhbz_array_size(cc_member); + + VERB3 log("count members on cc %i", array_size); + GList *cc_list = NULL; + + for (int i = 0; i < array_size; ++i) + { + xmlrpc_value* item = NULL; + xmlrpc_array_read_item(&env, cc_member, i, &item); + if (env.fault_occurred) + abrt_xmlrpc_die(&env); + + if (!item) + continue; + + const char* cc = NULL; + xmlrpc_read_string(&env, item, &cc); + xmlrpc_DECREF(item); + if (env.fault_occurred) + abrt_xmlrpc_die(&env); + + if (*cc != '\0') + { + cc_list = g_list_append(cc_list, (char*)cc); + VERB3 log("member on cc is %s", cc); + continue; + } + free((char*)cc); + } + xmlrpc_DECREF(cc_member); + return cc_list; +} + +struct bug_info *rhbz_bug_info(struct abrt_xmlrpc *ax, int bug_id) +{ + struct bug_info *bz = new_bug_info(); + xmlrpc_value *xml_bug_response = abrt_xmlrpc_call(ax, "bugzilla.getBug", + "(i)", bug_id); + + int *ret = (int*)rhbz_bug_read_item("bug_id", xml_bug_response, + RHBZ_MANDATORY_MEMB | RHBZ_READ_INT); + bz->bi_id = *ret; + free(ret); + bz->bi_product = rhbz_bug_read_item("product", xml_bug_response, + RHBZ_MANDATORY_MEMB | RHBZ_READ_STR); + bz->bi_reporter = rhbz_bug_read_item("reporter", xml_bug_response, + RHBZ_MANDATORY_MEMB | RHBZ_READ_STR); + bz->bi_status = rhbz_bug_read_item("bug_status", xml_bug_response, + RHBZ_MANDATORY_MEMB | RHBZ_READ_STR); + bz->bi_resolution = rhbz_bug_read_item("resolution", xml_bug_response, + RHBZ_READ_STR); + + if (strcmp(bz->bi_status, "CLOSED") == 0 && !bz->bi_resolution) + error_msg_and_die(_("Bug %i is CLOSED, but it has no RESOLUTION"), bz->bi_id); + + ret = (int*)rhbz_bug_read_item("dup_id", xml_bug_response, + RHBZ_READ_INT); + if (strcmp(bz->bi_status, "CLOSED") == 0 + && strcmp(bz->bi_resolution, "DUPLICATE") == 0 + && !ret) + { + error_msg_and_die(_("Bug %i is CLOSED as DUPLICATE, but it has no DUP_ID"), + bz->bi_id); + } + + bz->bi_dup_id = (ret) ? *ret: -1; + free(ret); + + bz->bi_cc_list = rhbz_bug_cc(xml_bug_response); + + xmlrpc_DECREF(xml_bug_response); + + return bz; +} + +/* suppress mail notify by {s:i} (nomail:1) (driven by flag) */ +int rhbz_new_bug(struct abrt_xmlrpc *ax, problem_data_t *problem_data, + int depend_on_bug) +{ + const char *package = get_problem_item_content_or_NULL(problem_data, + FILENAME_PACKAGE); + const char *component = get_problem_item_content_or_NULL(problem_data, + FILENAME_COMPONENT); + 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 *duphash = get_problem_item_content_or_NULL(problem_data, + FILENAME_DUPHASH); + const char *reason = get_problem_item_content_or_NULL(problem_data, + FILENAME_REASON); + const char *function = get_problem_item_content_or_NULL(problem_data, + FILENAME_CRASH_FUNCTION); + const char *analyzer = get_problem_item_content_or_NULL(problem_data, + FILENAME_ANALYZER); + const char *tainted_str = get_problem_item_content_or_NULL(problem_data, + FILENAME_TAINTED); + + struct strbuf *buf_summary = strbuf_new(); + strbuf_append_strf(buf_summary, "[abrt] %s", package); + + if (function != NULL && strlen(function) < 30) + strbuf_append_strf(buf_summary, ": %s", function); + + if (reason != NULL) + strbuf_append_strf(buf_summary, ": %s", reason); + + if (tainted_str && analyzer + && (strcmp(analyzer, "Kerneloops") == 0) + ) { + //TODO: fix me; basically it doesn't work as it suppose to work + // I will fix it immediately when this patch land into abrt git + /* + unsigned long tainted = xatoi_positive(tainted_str); + const char *tainted_warning = tainted_string(tainted); + if (tainted_warning) + strbuf_append_strf(buf_summary, ": TAINTED %s", tainted_warning); + */ + } + + char *status_whiteboard = xasprintf("abrt_hash:%s", duphash); + + char *bz_dsc = make_description_bz(problem_data); + char *full_dsc = xasprintf("abrt version: "VERSION"\n%s", bz_dsc); + free(bz_dsc); + + char *product = NULL; + char *version = NULL; + parse_release_for_bz(release, &product, &version); + + xmlrpc_value* result = NULL; + char *summary = strbuf_free_nobuf(buf_summary); + if (depend_on_bug > -1) + { + result = abrt_xmlrpc_call(ax, "Bug.create", "({s:s,s:s,s:s,s:s,s:s,s:s,s:s,s:i})", + "product", product, + "component", component, + "version", version, + "summary", summary, + "description", full_dsc, + "status_whiteboard", status_whiteboard, + "platform", arch, + "dependson", depend_on_bug); + } + else + { + result = abrt_xmlrpc_call(ax, "Bug.create", "({s:s,s:s,s:s,s:s,s:s,s:s,s:s})", + "product", product, + "component", component, + "version", version, + "summary", summary, + "description", full_dsc, + "status_whiteboard", status_whiteboard, + "platform", arch); + } + free(status_whiteboard); + free(product); + free(version); + free(summary); + free(full_dsc); + + if (!result) + return -1; + + int *r = rhbz_bug_read_item("id", result, RHBZ_MANDATORY_MEMB | RHBZ_READ_INT); + xmlrpc_DECREF(result); + int new_bug_id = *r; + free(r); + + log(_("New bug id: %i"), new_bug_id); + return new_bug_id; +} + +/* suppress mail notify by {s:i} (nomail:1) (driven by flag) */ +int rhbz_attachment(struct abrt_xmlrpc *ax, const char *filename, + const char *bug_id, const char *data) +{ + char *encoded64 = encode_base64(data, strlen(data)); + char *fn = xasprintf("File: %s", filename); + xmlrpc_value* result; + result= abrt_xmlrpc_call(ax, "bugzilla.addAttachment", "(s{s:s,s:s,s:s,s:s})", + bug_id, + "description", fn, + "filename", filename, + "contenttype", "text/plain", + "data", encoded64); + free(encoded64); + free(fn); + if (!result) + return -1; + + xmlrpc_DECREF(result); + + return 0; +} + +/* suppress mail notify by {s:i} (nomail:1) (driven by flag) */ +int rhbz_attachments(struct abrt_xmlrpc *ax, const char *bug_id, + problem_data_t *problem_data) +{ + GHashTableIter iter; + char *name; + struct problem_item *value; + g_hash_table_iter_init(&iter, problem_data); + while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value)) + { + const char *content = value->content; + + // We were special-casing FILENAME_BACKTRACE here, but karel says + // he can retrieve it in inlined form from comments too. + if ((value->flags & CD_FLAG_TXT) + && (strlen(content) > CD_TEXT_ATT_SIZE /*|| (strcmp(name, FILENAME_BACKTRACE) == 0)*/) + ) { + /* check if the attachment failed and try it once more */ + rhbz_attachment(ax, name, bug_id, content); + } + } + + return 0; +} + +void rhbz_logout(struct abrt_xmlrpc *ax) +{ + xmlrpc_value* result = abrt_xmlrpc_call(ax, "User.logout", "(s)", ""); + if (result) + xmlrpc_DECREF(result); +} + +struct bug_info *rhbz_find_origin_bug_closed_duplicate(struct abrt_xmlrpc *ax, + struct bug_info *bi) +{ + struct bug_info *bi_tmp = new_bug_info(); + bi_tmp->bi_id = bi->bi_id; + bi_tmp->bi_dup_id = bi->bi_dup_id; + + for (int ii = 0; ii <= MAX_HOPS; ii++) + { + if (ii == MAX_HOPS) + error_msg_and_die(_("Bugzilla couldn't find parent of bug %d"), bi->bi_id); + + log("Bug %d is a duplicate, using parent bug %d", bi_tmp->bi_id, bi_tmp->bi_dup_id); + int bug_id = bi_tmp->bi_dup_id; + + free_bug_info(bi_tmp); + bi_tmp = rhbz_bug_info(ax, bug_id); + + // found a bug which is not CLOSED as DUPLICATE + if (bi_tmp->bi_dup_id == -1) + break; + } + + return bi_tmp; +} + +/* suppress mail notify by {s:i} (nomail:1) */ +void rhbz_mail_to_cc(struct abrt_xmlrpc *ax, int bug_id, const char *mail) +{ + xmlrpc_value *result = abrt_xmlrpc_call(ax, "Bug.update", "({s:i,s:{s:(s)}})", + "ids", bug_id, "updates", "add_cc", mail); + if (result) + xmlrpc_DECREF(result); +} + +void rhbz_add_comment(struct abrt_xmlrpc *ax, int bug_id, const char *comment, + int is_private) +{ + xmlrpc_value *result = abrt_xmlrpc_call(ax, "Bug.add_comment", "({s:i,s:s,s:b})", + "id", bug_id, + "comment", comment, + "private", is_private); + if (result) + xmlrpc_DECREF(result); +} diff --git a/src/plugins/rhbz.h b/src/plugins/rhbz.h new file mode 100644 index 00000000..73d76f0a --- /dev/null +++ b/src/plugins/rhbz.h @@ -0,0 +1,100 @@ +/* + Copyright (C) 2011 ABRT team + Copyright (C) 2011 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. +*/ + +#ifndef RHBZ_H +#define RHBZ_H + +/* include/stdint.h: typedef int int32_t; + * include/xmlrpc-c/base.h: typedef int32_t xmlrpc_int32; + */ + +#include "abrt_xmlrpc.h" +#include "abrt_problem_data.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + RHBZ_MANDATORY_MEMB = (1 << 0), + RHBZ_READ_STR = (1 << 1), + RHBZ_READ_INT = (1 << 2), +}; + +#define IS_MANDATORY(flags) ((flags) & RHBZ_MANDATORY_MEMB) +#define IS_READ_STR(flags) ((flags) & RHBZ_READ_STR) +#define IS_READ_INT(flags) ((flags) & RHBZ_READ_INT) + +struct bug_info { + int bi_id; + int bi_dup_id; + + const char *bi_status; + const char *bi_resolution; + const char *bi_reporter; + const char *bi_product; + + GList *bi_cc_list; +}; + +struct bug_info *new_bug_info(); +void free_bug_info(struct bug_info *bz); + +void rhbz_login(struct abrt_xmlrpc *ax, const char *login, const char *passwd); + +void rhbz_mail_to_cc(struct abrt_xmlrpc *ax, int bug_id, const char *mail); + +void rhbz_add_comment(struct abrt_xmlrpc *ax, int bug_id, const char *comment, + int is_private); + +void *rhbz_bug_read_item(const char *memb, xmlrpc_value *xml, int flags); + +void rhbz_logout(struct abrt_xmlrpc *ax); + +xmlrpc_value *rhbz_search_duphash(struct abrt_xmlrpc *ax, const char *component, + const char *release, const char *duphash); + +xmlrpc_value *rhbz_get_member(const char *member, xmlrpc_value *xml); + +int rhbz_array_size(xmlrpc_value *xml); + +int rhbz_bug_id(xmlrpc_value *xml); + +int rhbz_new_bug(struct abrt_xmlrpc *ax, problem_data_t *problem_data, + int depend_on_bug); + +int rhbz_attachments(struct abrt_xmlrpc *ax, const char *bug_id, + problem_data_t *problem_data); + +int rhbz_attachment(struct abrt_xmlrpc *ax, const char *filename, + const char *bug_id, const char *data); + +GList *rhbz_bug_cc(xmlrpc_value *result_xml); + +struct bug_info *rhbz_bug_info(struct abrt_xmlrpc *ax, int bug_id); + + +struct bug_info *rhbz_find_origin_bug_closed_duplicate(struct abrt_xmlrpc *ax, + struct bug_info *bi); + +#ifdef __cplusplus +} +#endif + +#endif |