summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorNikola Pajkovsky <npajkovs@redhat.com>2011-05-12 15:12:08 +0200
committerNikola Pajkovsky <npajkovs@redhat.com>2011-05-12 15:12:08 +0200
commite9f36a1f33bfa043048578e8f49cade74c9f182d (patch)
treee22b03c3ebcff0f13629220506d4db6287403784 /src/plugins
parent31bd75150e6eb74daff7dc0b9ca3fb7e1155fec6 (diff)
parente71c1268337cdad09a999886b956c753530b49ef (diff)
downloadabrt-e9f36a1f33bfa043048578e8f49cade74c9f182d.tar.gz
abrt-e9f36a1f33bfa043048578e8f49cade74c9f182d.tar.xz
abrt-e9f36a1f33bfa043048578e8f49cade74c9f182d.zip
Merge branch 'bz/xmlrpc-c'
* bz/xmlrpc-c: remove c++ism from configure abrt-action-bugzilla.cpp -> src/plugins/abrt-action-bugzilla.c xmlrpc and bugzilla in a new C coat
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/Makefile.am4
-rw-r--r--src/plugins/abrt-action-bugzilla.c340
-rw-r--r--src/plugins/abrt-action-bugzilla.cpp958
-rw-r--r--src/plugins/rhbz.c482
-rw-r--r--src/plugins/rhbz.h100
5 files changed, 924 insertions, 960 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, &param, &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/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