static xmlrpc_value *rhbz_search_duphash(struct abrt_xmlrpc *ax, const char *product, const char *version, const char *component, const char *duphash) { struct strbuf *query = strbuf_new(); strbuf_append_strf(query, "ALL whiteboard:\"%s\"", duphash); if (product) strbuf_append_strf(query, " product:\"%s\"", product); if (version) strbuf_append_strf(query, " version:\"%s\"", version); if (component) strbuf_append_strf(query, " component:\"%s\"", component); char *s = strbuf_free_nobuf(query); VERB3 log("search for '%s'", s); xmlrpc_value *search = abrt_xmlrpc_call(ax, "Bug.search", "({s:s})", "quicksearch", s); free(s); xmlrpc_value *bugs = rhbz_get_member("bugs", search); xmlrpc_DECREF(search); if (!bugs) error_msg_and_die(_("Bug.search(quicksearch) return value did not contain member 'bugs'")); return bugs; }
/* die or return bug id; each bug must have bug id otherwise xml is corrupted */ int rhbz_get_bug_id_from_array0(xmlrpc_value* xml, unsigned ver) { func_entry(); xmlrpc_env env; xmlrpc_env_init(&env); xmlrpc_value *item = NULL; xmlrpc_array_read_item(&env, xml, 0, &item); if (env.fault_occurred) abrt_xmlrpc_die(&env); const char *id; if (ver >= BUGZILLA_VERSION(4,2,0)) id = "id"; else id = "bug_id"; xmlrpc_value *bug; bug = rhbz_get_member(id, item); xmlrpc_DECREF(item); if (!bug) error_msg_and_die("Can't get member '%s' from bug data", id); int bug_id = -1; 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; }
// TODO: npajkovs: add flag to read xmlrpc_read_array_item first void *rhbz_bug_read_item(const char *memb, xmlrpc_value *xml, int flags) { func_entry(); 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) { func_entry(); xmlrpc_env env; xmlrpc_env_init(&env); xmlrpc_value* cc_member = rhbz_get_member("cc", result_xml); if (!cc_member) return NULL; unsigned array_size = rhbz_array_size(cc_member); VERB3 log("count members on cc %i", array_size); GList *cc_list = NULL; for (unsigned 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; }
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); }
static GList *rhbz_comments(struct abrt_xmlrpc *ax, int bug_id) { func_entry(); /* http://www.bugzilla.org/docs/4.2/en/html/api/Bugzilla/WebService/Bug.html#comments */ /* * <methodResponse> * <params><param> * <value><struct> * <member><name>bugs</name> * <value><struct> * <member><name>BUG_ID</name> * <value><struct> * <member><name>comments</name> * <value><array> * ... */ xmlrpc_value *xml_response = abrt_xmlrpc_call(ax, "Bug.comments", "({s:(i)})", "ids", bug_id); /* bugs * This is used for bugs specified in ids. This is a hash, where the * keys are the numeric ids of the bugs, and the value is a hash with a * single key, comments, which is an array of comments. */ xmlrpc_value *bugs_memb = rhbz_get_member("bugs", xml_response); /* Get hash value assigned to bug_id key */ char *item = xasprintf("%d", bug_id); xmlrpc_value *item_memb = rhbz_get_member(item, bugs_memb); free(item); /* Get array of comments */ xmlrpc_value *comments_memb = rhbz_get_member("comments", item_memb); xmlrpc_env env; xmlrpc_env_init(&env); int comments_memb_size = rhbz_array_size(comments_memb); GList *comments = NULL; for (int i = 0; i < comments_memb_size; ++i) { xmlrpc_value* item = NULL; xmlrpc_array_read_item(&env, comments_memb, i, &item); if (env.fault_occurred) abrt_xmlrpc_die(&env); char *comment_body = rhbz_bug_read_item("text", item, RHBZ_READ_STR); /* attachments are sometimes without comments -- skip them */ if (comment_body) comments = g_list_prepend(comments, comment_body); xmlrpc_DECREF(item); } xmlrpc_env_clean(&env); xmlrpc_DECREF(comments_memb); xmlrpc_DECREF(item_memb); xmlrpc_DECREF(bugs_memb); xmlrpc_DECREF(xml_response); return g_list_reverse(comments); }
struct bug_info *rhbz_bug_info(struct abrt_xmlrpc *ax, int bug_id) { func_entry(); struct bug_info *bz = new_bug_info(); /* http://www.bugzilla.org/docs/4.2/en/html/api/Bugzilla/WebService/Bug.html#get * * <methodResponse> * <params> * <param><value><struct> * <member><name>faults</name><value><array><data/></array></value></member> * <member><name>bugs</name> * <value><array><data> * ... */ xmlrpc_value *xml_bug_response = abrt_xmlrpc_call(ax, "Bug.get", "({s:(i)})", "ids", bug_id); xmlrpc_value *bugs_memb = rhbz_get_member("bugs", xml_bug_response); xmlrpc_value *bug_item = rhbz_array_item_at(bugs_memb, 0); int *ret = (int*)rhbz_bug_read_item("id", bug_item, RHBZ_MANDATORY_MEMB | RHBZ_READ_INT); bz->bi_id = *ret; free(ret); bz->bi_product = rhbz_bug_read_item("product", bug_item, RHBZ_MANDATORY_MEMB | RHBZ_READ_STR); bz->bi_reporter = rhbz_bug_read_item("creator", bug_item, RHBZ_MANDATORY_MEMB | RHBZ_READ_STR); bz->bi_status = rhbz_bug_read_item("status", bug_item, RHBZ_MANDATORY_MEMB | RHBZ_READ_STR); bz->bi_resolution = rhbz_bug_read_item("resolution", bug_item, RHBZ_READ_STR); bz->bi_platform = rhbz_bug_read_item("platform", bug_item, 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("dupe_of", bug_item, 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(bug_item); bz->bi_comments = rhbz_comments(ax, bug_id); bz->bi_best_bt_rating = find_best_bt_rating_in_comments(bz->bi_comments); xmlrpc_DECREF(bugs_memb); xmlrpc_DECREF(bug_item); xmlrpc_DECREF(xml_bug_response); return bz; }