diff options
Diffstat (limited to 'src/plugins/rhbz.c')
-rw-r--r-- | src/plugins/rhbz.c | 482 |
1 files changed, 482 insertions, 0 deletions
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); +} |