diff options
-rw-r--r-- | po/POTFILES.in | 1 | ||||
-rw-r--r-- | src/lib/Makefile.am | 2 | ||||
-rw-r--r-- | src/lib/abrt_xmlrpc.c | 137 | ||||
-rw-r--r-- | src/lib/abrt_xmlrpc.cpp | 102 | ||||
-rw-r--r-- | src/lib/abrt_xmlrpc.h | 39 | ||||
-rw-r--r-- | src/plugins/Makefile.am | 4 | ||||
-rw-r--r-- | src/plugins/abrt-action-bugzilla.cpp | 768 | ||||
-rw-r--r-- | src/plugins/rhbz.c | 482 | ||||
-rw-r--r-- | src/plugins/rhbz.h | 100 |
9 files changed, 814 insertions, 821 deletions
diff --git a/po/POTFILES.in b/po/POTFILES.in index 7a2ecacb..a31b13a1 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -42,3 +42,4 @@ src/plugins/report_Bugzilla.xml.in src/plugins/report_Kerneloops.xml.in src/plugins/report_Mailx.xml.in src/plugins/report_RHTSupport.xml.in +src/plugins/rhbz.c diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 88671f54..8fb147ac 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -87,7 +87,7 @@ libabrt_dbus_la_LIBADD = \ libabrt_web_la_SOURCES = \ abrt_curl.h abrt_curl.c \ - abrt_xmlrpc.h abrt_xmlrpc.cpp + abrt_xmlrpc.h abrt_xmlrpc.c libabrt_web_la_CPPFLAGS = \ -Wall -Wwrite-strings -Werror \ -I$(srcdir)/../include/report -I$(srcdir)/../include \ diff --git a/src/lib/abrt_xmlrpc.c b/src/lib/abrt_xmlrpc.c new file mode 100644 index 00000000..28d42325 --- /dev/null +++ b/src/lib/abrt_xmlrpc.c @@ -0,0 +1,137 @@ +/* + 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" + +void abrt_xmlrpc_die(xmlrpc_env *env) +{ + error_msg_and_die("fatal: XML-RPC(%d): %s", env->fault_code, env->fault_string); +} + +void abrt_xmlrpc_error(xmlrpc_env *env) +{ + error_msg("error: XML-RPC (%d): %s", env->fault_code, env->fault_string); +} + +struct abrt_xmlrpc *abrt_xmlrpc_new_client(const char *url, int ssl_verify) +{ + xmlrpc_env env; + xmlrpc_env_init(&env); + + struct abrt_xmlrpc *ax = xzalloc(sizeof(struct abrt_xmlrpc)); + + /* This should be done at program startup, once. We do it in main */ + /* xmlrpc_client_setup_global_const(&env); */ + + /* URL - bugzilla.redhat.com/show_bug.cgi?id=666893 Unable to make sense of + * XML-RPC response from server + * + * By default, XML data from the network may be no larger than 512K. + * XMLRPC_XML_SIZE_LIMIT_DEFAULT is #defined to (512*1024) in xmlrpc-c/base.h + * + * Users reported trouble with 733402 byte long responses, hope raising the + * limit to 2*512k is enough + */ + xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, 2 * XMLRPC_XML_SIZE_LIMIT_DEFAULT); + + struct xmlrpc_curl_xportparms curl_parms; + memset(&curl_parms, 0, sizeof(curl_parms)); + /* curlParms.network_interface = NULL; - done by memset */ + curl_parms.no_ssl_verifypeer = !ssl_verify; + curl_parms.no_ssl_verifyhost = !ssl_verify; +#ifdef VERSION + curl_parms.user_agent = PACKAGE_NAME"/"VERSION; +#else + curl_parms.user_agent = "abrt"; +#endif + + struct xmlrpc_clientparms client_parms; + memset(&client_parms, 0, sizeof(client_parms)); + client_parms.transport = "curl"; + client_parms.transportparmsP = &curl_parms; + client_parms.transportparm_size = XMLRPC_CXPSIZE(user_agent); + + xmlrpc_client_create(&env, XMLRPC_CLIENT_NO_FLAGS, + PACKAGE_NAME, VERSION, + &client_parms, XMLRPC_CPSIZE(transportparm_size), + &ax->ax_client); + + if (env.fault_occurred) + abrt_xmlrpc_die(&env); + + ax->ax_server_info = xmlrpc_server_info_new(&env, url); + if (env.fault_occurred) + { + xmlrpc_client_destroy(ax->ax_client); + abrt_xmlrpc_die(&env); + } + + return ax; +} + +void abrt_xmlrpc_free_client(struct abrt_xmlrpc *ax) +{ + if (!ax) + return; + + if (ax->ax_server_info) + xmlrpc_server_info_free(ax->ax_server_info); + + if (ax->ax_client) + xmlrpc_client_destroy(ax->ax_client); + + free(ax); +} + +/* die or return expected results */ +xmlrpc_value *abrt_xmlrpc_call(struct abrt_xmlrpc *ax, + const char* method, const char* format, ...) +{ + xmlrpc_env env; + xmlrpc_env_init(&env); + + xmlrpc_value* param = NULL; + const char* suffix; + va_list args; + + va_start(args, format); + xmlrpc_build_value_va(&env, format, args, ¶m, &suffix); + va_end(args); + if (env.fault_occurred) + abrt_xmlrpc_die(&env); + + xmlrpc_value* result = NULL; + if (*suffix != '\0') + { + xmlrpc_env_set_fault_formatted( + &env, XMLRPC_INTERNAL_ERROR, "Junk after the argument " + "specifier: '%s'. There must be exactly one argument.", + suffix); + } + else + { + xmlrpc_client_call2(&env, ax->ax_client, ax->ax_server_info, method, + param, &result); + } + xmlrpc_DECREF(param); + if (env.fault_occurred) + abrt_xmlrpc_die(&env); + + return result; +} diff --git a/src/lib/abrt_xmlrpc.cpp b/src/lib/abrt_xmlrpc.cpp deleted file mode 100644 index ae75a47f..00000000 --- a/src/lib/abrt_xmlrpc.cpp +++ /dev/null @@ -1,102 +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" - -void throw_xml_fault(xmlrpc_env *env) -{ - error_msg_and_die("XML-RPC Fault(%d): %s", env->fault_code, env->fault_string); -} - -void throw_if_xml_fault_occurred(xmlrpc_env *env) -{ - if (env->fault_occurred) - { - throw_xml_fault(env); - } -} - -void abrt_xmlrpc_conn::new_xmlrpc_client(const char* url, bool ssl_verify) -{ - m_pClient = NULL; - m_pServer_info = NULL; - - xmlrpc_env env; - xmlrpc_env_init(&env); - - /* This should be done at program startup, once. We do it in main */ - /* xmlrpc_client_setup_global_const(&env); */ - - /* URL - bugzilla.redhat.com/show_bug.cgi?id=666893 Unable to make sense of - * XML-RPC response from server - * - * By default, XML data from the network may be no larger than 512K. - * XMLRPC_XML_SIZE_LIMIT_DEFAULT is #defined to (512*1024) in xmlrpc-c/base.h - * - * Users reported trouble with 733402 byte long responses, hope raising the - * limit to 2*512k is enough - */ - xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, 2 * XMLRPC_XML_SIZE_LIMIT_DEFAULT); - - struct xmlrpc_curl_xportparms curlParms; - memset(&curlParms, 0, sizeof(curlParms)); - /* curlParms.network_interface = NULL; - done by memset */ - curlParms.no_ssl_verifypeer = !ssl_verify; - curlParms.no_ssl_verifyhost = !ssl_verify; -#ifdef VERSION - curlParms.user_agent = PACKAGE_NAME"/"VERSION; -#else - curlParms.user_agent = "abrt"; -#endif - - struct xmlrpc_clientparms clientParms; - memset(&clientParms, 0, sizeof(clientParms)); - clientParms.transport = "curl"; - clientParms.transportparmsP = &curlParms; - clientParms.transportparm_size = XMLRPC_CXPSIZE(user_agent); - - xmlrpc_client_create(&env, XMLRPC_CLIENT_NO_FLAGS, - PACKAGE_NAME, VERSION, - &clientParms, XMLRPC_CPSIZE(transportparm_size), - &m_pClient); - if (env.fault_occurred) - throw_xml_fault(&env); - - m_pServer_info = xmlrpc_server_info_new(&env, url); - if (env.fault_occurred) - { - xmlrpc_client_destroy(m_pClient); - m_pClient = NULL; - throw_xml_fault(&env); - } -} - -void abrt_xmlrpc_conn::destroy_xmlrpc_client() -{ - if (m_pServer_info) - { - xmlrpc_server_info_free(m_pServer_info); - m_pServer_info = NULL; - } - if (m_pClient) - { - xmlrpc_client_destroy(m_pClient); - m_pClient = NULL; - } -} diff --git a/src/lib/abrt_xmlrpc.h b/src/lib/abrt_xmlrpc.h index 93c5a9d6..5c94360f 100644 --- a/src/lib/abrt_xmlrpc.h +++ b/src/lib/abrt_xmlrpc.h @@ -19,37 +19,30 @@ #ifndef ABRT_XMLRPC_H_ #define ABRT_XMLRPC_H_ 1 -#include <curl/curl.h> +/* include/stdint.h: typedef int int32_t; + * include/xmlrpc-c/base.h: typedef int32_t xmlrpc_int32; + */ + #include <xmlrpc-c/base.h> #include <xmlrpc-c/client.h> #ifdef __cplusplus -/* - * Simple class holding XMLRPC connection data. - * Used mainly to ensure we always destroy xmlrpc client and server_info - * on return or throw. - */ -struct abrt_xmlrpc_conn { - xmlrpc_client* m_pClient; - xmlrpc_server_info* m_pServer_info; - - abrt_xmlrpc_conn(const char* url, bool ssl_verify) { new_xmlrpc_client(url, ssl_verify); } - /* this never throws exceptions - calls C functions only */ - ~abrt_xmlrpc_conn() { destroy_xmlrpc_client(); } - - void new_xmlrpc_client(const char* url, bool ssl_verify); - void destroy_xmlrpc_client(); -}; +extern "C" { #endif +struct abrt_xmlrpc { + xmlrpc_client *ax_client; + xmlrpc_server_info *ax_server_info; +}; -#ifdef __cplusplus -extern "C" { -#endif +struct abrt_xmlrpc *abrt_xmlrpc_new_client(const char *url, int ssl_verify); +void abrt_xmlrpc_free_client(struct abrt_xmlrpc *ax); +void abrt_xmlrpc_die(xmlrpc_env *env) __attribute__((noreturn)); +void abrt_xmlrpc_error(xmlrpc_env *env); -/* Utility functions */ -void throw_xml_fault(xmlrpc_env *env); -void throw_if_xml_fault_occurred(xmlrpc_env *env); +/* die or return expected results */ +xmlrpc_value *abrt_xmlrpc_call(struct abrt_xmlrpc *ax, + const char *method, const char *format, ...); #ifdef __cplusplus } diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index 1ef7fbc2..22c1b85a 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.cpp 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.cpp b/src/plugins/abrt-action-bugzilla.cpp index e8a605f1..91bc26f8 100644 --- a/src/plugins/abrt-action-bugzilla.cpp +++ b/src/plugins/abrt-action-bugzilla.cpp @@ -17,350 +17,12 @@ 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" +#include "abrt_xmlrpc.h" +#include "rhbz.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' }, @@ -415,6 +77,7 @@ static const char * const taint_warnings[] = { NULL, }; +/* TODO: npajkovs: fix tainted string */ static const char *tainted_string(unsigned tainted) { unsigned idx = 0; @@ -424,207 +87,7 @@ static const char *tainted_string(unsigned tainted) 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) +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) @@ -669,13 +132,12 @@ static void report_to_bugzilla( 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); + struct abrt_xmlrpc *client = abrt_xmlrpc_new_client(bugzilla_xmlrpc, ssl_verify); log(_("Logging into Bugzilla at %s"), bugzilla_url); - bz_server.login(login, password); + rhbz_login(client, login, password); log(_("Checking for duplicates")); - char *product = NULL; char *version = NULL; parse_release_for_bz(release, &product, &version); @@ -683,225 +145,145 @@ static void report_to_bugzilla( xmlrpc_value *result; if (strcmp(product, "Fedora") == 0) - result = bz_server.call_quicksearch_duphash(component, product, duphash); + result = rhbz_search_duphash(client, component, product, duphash); else - result = bz_server.call_quicksearch_duphash(component, NULL, duphash); + result = rhbz_search_duphash(client, component, NULL, duphash); - if (!result) - throw_if_xml_fault_occurred(&bz_server.env); - - xmlrpc_value *all_bugs = bz_server.get_member("bugs", result); + xmlrpc_value *all_bugs = rhbz_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; + 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 = bz_server.get_bug_id(all_bugs); + bug_id = rhbz_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")); - } + bz = rhbz_bug_info(client, bug_id); - if (strcmp(bz.bug_product, product) != 0) + if (strcmp(bz->bi_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); + dependent_bug = bug_id; + /* found something, but its a different product */ + free_bug_info(bz); - all_bugs = bz_server.get_member("bugs", result); + xmlrpc_value *result = rhbz_search_duphash(client, component, + product, duphash); + xmlrpc_value *all_bugs = rhbz_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); + all_bugs_size = rhbz_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")); - } + bug_id = rhbz_bug_id(all_bugs); + bz = rhbz_bug_info(client, bug_id); } - else - xmlrpc_DECREF(all_bugs); + 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 + 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")); - } + bug_id = rhbz_new_bug(client, problem_data, bug_id); - 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("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")); - bz_server.logout(); + rhbz_logout(client); - log("Status: NEW %s/show_bug.cgi?id=%u", - bugzilla_url, - (int)bug_id - ); + log("Status: NEW %s/show_bug.cgi?id=%u", bugzilla_url, bug_id); + abrt_xmlrpc_free_client(client); 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)) + log(_("Bug is already reported: %i"), bz->bi_id); + if ((strcmp(bz->bi_status, "CLOSED") == 0) + && (strcmp(bz->bi_resolution, "DUPLICATE") == 0)) { - for (int ii = 0; ii <= MAX_HOPS; ii++) + struct bug_info *origin; + origin = rhbz_find_origin_bug_closed_duplicate(client, bz); + if (origin) { - 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; + free_bug_info(bz); + bz = origin; } } - if (strcmp(bz.bug_status, "CLOSED") != 0) + if (strcmp(bz->bi_status, "CLOSED") != 0) { - int status = 0; - if ((strcmp(bz.bug_reporter, login) != 0) - && (g_list_find(bz.bug_cc, login))) + 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); - 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); + 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); + 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"); + 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); + "Architecture: %s\n" + "OS Release: %s\n" + "%s", package, arch, release, dsc); + log(_("Adding new comment to bug %d"), bz->bi_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); - } + 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")); - bz_server.logout(); + rhbz_logout(client); log("Status: %s%s%s %s/show_bug.cgi?id=%u", - bz.bug_status, - bz.bug_resolution ? " " : "", - bz.bug_resolution ? bz.bug_resolution : "", + bz->bi_status, + bz->bi_resolution ? " " : "", + bz->bi_resolution ? bz->bi_resolution : "", bugzilla_url, - (int)bug_id - ); + 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, (int)bug_id); + 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); - bug_info_destroy(&bz); + free_bug_info(bz); + abrt_xmlrpc_free_client(client); } int main(int argc, char **argv) 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 |