//#include #include "abrtlib.h" #include "Bugzilla.h" #include "CrashTypes.h" #include "DebugDump.h" #include "ABRTException.h" #include "CommLayerInner.h" #ifdef HAVE_CONFIG_H #include #endif #define XML_RPC_SUFFIX "/xmlrpc.cgi" static xmlrpc_env env; static xmlrpc_client* client = NULL; static struct xmlrpc_clientparms clientParms; static struct xmlrpc_curl_xportparms curlParms; static xmlrpc_server_info* server_info = NULL; static void new_xmlrpc_client(const char* url, bool no_ssl_verify); static void destroy_xmlrpc_client(); static int32_t check_uuid_in_bugzilla(const char* component, const char* UUID); static bool check_cc_and_reporter(const uint32_t bug_id, const char* login); static void add_plus_one_cc(const uint32_t bug_id, const char* login); #define throw_if_fault_occurred(env) \ do \ { \ xmlrpc_env* e = (env); \ if (e->fault_occurred) \ { \ char buffer[2048]; \ snprintf(buffer, 2047, "XML-RPC Fault: %s(%d)", e->fault_string, e->fault_code); \ throw CABRTException(EXCEP_PLUGIN, std::string(buffer)); \ } \ }while(0) static void new_xmlrpc_client(const char* url, bool no_ssl_verify) { xmlrpc_env_init(&env); xmlrpc_client_setup_global_const(&env); curlParms.network_interface = NULL; curlParms.no_ssl_verifypeer = no_ssl_verify; curlParms.no_ssl_verifyhost = no_ssl_verify; #ifdef VERSION curlParms.user_agent = PACKAGE_NAME"/"VERSION; #else curlParms.user_agent = "abrt"; #endif 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), &client); throw_if_fault_occurred(&env); server_info = xmlrpc_server_info_new(&env, url); throw_if_fault_occurred(&env); } static void destroy_xmlrpc_client() { xmlrpc_server_info_free(server_info); xmlrpc_env_clean(&env); xmlrpc_client_destroy(client); xmlrpc_client_teardown_global_const(); } CReporterBugzilla::CReporterBugzilla() : m_sBugzillaURL("https://bugzilla.redhat.com"), m_sBugzillaXMLRPC("https://bugzilla.redhat.com" + std::string(XML_RPC_SUFFIX)), m_bNoSSLVerify(false), m_bLoggedIn(false) {} CReporterBugzilla::~CReporterBugzilla() {} PRInt32 CReporterBugzilla::Base64Encode_cb(void *arg, const char *obuf, PRInt32 size) { /* CReporterBugzilla* bz = static_cast(arg); int ii; for (ii = 0; ii < size; ii++) { if (isprint(obuf[ii])) { bz->m_sAttchmentInBase64 += obuf[ii]; } } */ return 1; } static void login(const char* login, const char* passwd) { xmlrpc_value* result; xmlrpc_value* param = xmlrpc_build_value(&env, "({s:s,s:s})", "login", login, "password", passwd); throw_if_fault_occurred(&env); xmlrpc_client_call2(&env, client, server_info, "User.login", param, &result); throw_if_fault_occurred(&env); } void CReporterBugzilla::Logout() { /* xmlrpc_c::paramList paramList; paramList.add(xmlrpc_c::value_string("")); xmlrpc_c::rpcPtr rpc(new xmlrpc_c::rpc("User.logout", paramList)); try { rpc->call(m_pXmlrpcClient, m_pCarriageParm); } catch (std::exception& e) { throw CABRTException(EXCEP_PLUGIN, std::string("CReporterBugzilla::Logout(): ") + e.what()); } */ } static bool check_cc_and_reporter(const uint32_t bug_id, const char* login) { xmlrpc_value* param = NULL; xmlrpc_value* result = NULL; xmlrpc_value* reporter_member = NULL; xmlrpc_value* cc_member = NULL; const char* bug = to_string(bug_id).c_str(); param = xmlrpc_build_value(&env, "(s)", bug); throw_if_fault_occurred(&env); xmlrpc_client_call2(&env, client, server_info, "bugzilla.getBug", param, &result); throw_if_fault_occurred(&env); xmlrpc_struct_find_value(&env, result, "reporter", &reporter_member); throw_if_fault_occurred(&env); if (reporter_member) { const char* reporter = NULL; xmlrpc_read_string(&env, reporter_member, &reporter); throw_if_fault_occurred(&env); if (strcmp(reporter, login) == 0 ) { std::cout << "reporter=login" << std::endl; return true; } } xmlrpc_struct_find_value(&env, result, "cc", &cc_member); throw_if_fault_occurred(&env); if (cc_member) { xmlrpc_value* item = NULL; uint32_t array_size = xmlrpc_array_size(&env, cc_member); for (uint32_t i = 0; i < array_size; i++) { xmlrpc_array_read_item(&env, cc_member, i, &item); // Correct throw_if_fault_occurred(&env); const char* cc = NULL; xmlrpc_read_string(&env, item, &cc); throw_if_fault_occurred(&env); if (strcmp(cc, login) == 0) { std::cout << "cc=login" << std::endl; return true; } } } xmlrpc_DECREF(result); return false; } static void add_plus_one_cc(const uint32_t bug_id, const char* login) { xmlrpc_value* param = NULL; xmlrpc_value* result = NULL; param = xmlrpc_build_value(&env, "({s:i,s:{s:(s)}})", "ids", bug_id, "updates", "add_cc", login); throw_if_fault_occurred(&env); xmlrpc_client_call2(&env, client, server_info, "Bug.update", param, &result); throw_if_fault_occurred(&env); xmlrpc_DECREF(result); } static int32_t check_uuid_in_bugzilla(const char* component, const char* UUID) { xmlrpc_value* param = NULL; xmlrpc_value* result = NULL; xmlrpc_value* bugs_member = NULL; xmlrpc_int bug_id; char query[1024]; snprintf(query, 1023, "ALL component:\"%s\" statuswhiteboard:\"%s\"", component, UUID); param = xmlrpc_build_value(&env, "({s:s})", "quicksearch", query); throw_if_fault_occurred(&env); xmlrpc_client_call2(&env, client, server_info, "Bug.search", param, &result); throw_if_fault_occurred(&env); xmlrpc_struct_find_value(&env, result, "bugs", &bugs_member); throw_if_fault_occurred(&env); if (bugs_member) { // when array size is equal 0 that means no bug reportet uint32_t array_size = xmlrpc_array_size(&env, bugs_member); throw_if_fault_occurred(&env); if( array_size == 0 ) return -1; xmlrpc_value* item = NULL; xmlrpc_array_read_item(&env, bugs_member, 0, &item); // Correct throw_if_fault_occurred(&env); xmlrpc_value* bug = NULL; xmlrpc_struct_find_value(&env, item,"bug_id", &bug); throw_if_fault_occurred(&env); if (bug) { xmlrpc_read_int(&env, bug, &bug_id); log("Bug is already reported: %i", bug_id); update_client(_("Bug is already reported: ") + to_string(bug_id)); xmlrpc_DECREF(result); xmlrpc_DECREF(bug); xmlrpc_DECREF(item); xmlrpc_DECREF(bugs_member); return bug_id; } } xmlrpc_DECREF(result); xmlrpc_DECREF(bugs_member); return -1; } void CReporterBugzilla::CreateNewBugDescription(const map_crash_report_t& pCrashReport, std::string& pDescription) { /* std::string howToReproduce; std::string comment; if (pCrashReport.find(CD_REPRODUCE) != pCrashReport.end()) { howToReproduce = "\n\nHow to reproduce\n" "-----\n" + pCrashReport.find(CD_REPRODUCE)->second[CD_CONTENT]; } if (pCrashReport.find(CD_COMMENT) != pCrashReport.end()) { comment = "\n\nComment\n" "-----\n" + pCrashReport.find(CD_COMMENT)->second[CD_CONTENT]; } pDescription = "\nabrt detected a crash.\n" + howToReproduce + comment + "\n\nAdditional information\n" "======\n"; map_crash_report_t::const_iterator it; for (it = pCrashReport.begin(); it != pCrashReport.end(); it++) { if (it->second[CD_TYPE] == CD_TXT) { if (it->first != CD_UUID && it->first != FILENAME_ARCHITECTURE && it->first != FILENAME_RELEASE && it->first != CD_REPRODUCE && it->first != CD_COMMENT) { pDescription += "\n" + it->first + "\n"; pDescription += "-----\n"; pDescription += it->second[CD_CONTENT] + "\n\n"; } } else if (it->second[CD_TYPE] == CD_ATT) { pDescription += "\n\nAttached files\n" "----\n"; pDescription += it->first + "\n"; } else if (it->second[CD_TYPE] == CD_BIN) { char buffer[1024]; snprintf(buffer, 1024, _("Binary file %s will not be reported."), it->first.c_str()); warn_client(std::string(buffer)); //update_client(_("Binary file ")+it->first+_(" will not be reported.")); } } */ } void CReporterBugzilla::GetProductAndVersion(const std::string& pRelease, std::string& pProduct, std::string& pVersion) { /* if (pRelease.find("Rawhide") != std::string::npos) { pProduct = "Fedora"; pVersion = "rawhide"; return; } if (pRelease.find("Fedora") != std::string::npos) { pProduct = "Fedora"; } else if (pRelease.find("Red Hat Enterprise Linux") != std::string::npos) { pProduct = "Red Hat Enterprise Linux "; } std::string::size_type pos = pRelease.find("release"); pos = pRelease.find(" ", pos) + 1; while (pRelease[pos] != ' ') { pVersion += pRelease[pos]; if (pProduct == "Red Hat Enterprise Linux ") { pProduct += pRelease[pos]; } pos++; } */ } std::string CReporterBugzilla::NewBug(const map_crash_report_t& pCrashReport) { /* xmlrpc_c::paramList paramList; map_xmlrpc_params_t bugParams; map_xmlrpc_params_t ret; std::string package = pCrashReport.find(FILENAME_PACKAGE)->second[CD_CONTENT]; std::string component = pCrashReport.find(FILENAME_COMPONENT)->second[CD_CONTENT]; std::string description; std::string release = pCrashReport.find(FILENAME_RELEASE)->second[CD_CONTENT];; std::string product; std::string version; std::stringstream bugId; CreateNewBugDescription(pCrashReport, description); GetProductAndVersion(release, product, version); bugParams["product"] = xmlrpc_c::value_string(product); bugParams["component"] = xmlrpc_c::value_string(component); bugParams["version"] = xmlrpc_c::value_string(version); //bugParams["op_sys"] = xmlrpc_c::value_string("Linux"); bugParams["summary"] = xmlrpc_c::value_string("[abrt] crash detected in " + package); bugParams["description"] = xmlrpc_c::value_string(description); bugParams["status_whiteboard"] = xmlrpc_c::value_string("abrt_hash:" + pCrashReport.find(CD_UUID)->second[CD_CONTENT]); bugParams["platform"] = xmlrpc_c::value_string(pCrashReport.find(FILENAME_ARCHITECTURE)->second[CD_CONTENT]); paramList.add(xmlrpc_c::value_struct(bugParams)); xmlrpc_c::rpcPtr rpc(new xmlrpc_c::rpc("Bug.create", paramList)); try { rpc->call(m_pXmlrpcClient, m_pCarriageParm); ret = xmlrpc_c::value_struct(rpc->getResult()); bugId << xmlrpc_c::value_int(ret["id"]); log("New bug id: %s", bugId.str().c_str()); update_client(_("New bug id: ") + bugId.str()); } catch (std::exception& e) { throw CABRTException(EXCEP_PLUGIN, std::string("CReporterBugzilla::NewBug(): ") + e.what()); } return bugId.str(); */ } void CReporterBugzilla::AddAttachments(const std::string& pBugId, const map_crash_report_t& pCrashReport) { /* xmlrpc_c::paramList paramList; map_xmlrpc_params_t attachmentParams; std::vector ret; NSSBase64Encoder* base64; map_crash_report_t::const_iterator it; for (it = pCrashReport.begin(); it != pCrashReport.end(); it++) { if (it->second[CD_TYPE] == CD_ATT) { m_sAttchmentInBase64 = ""; base64 = NSSBase64Encoder_Create(Base64Encode_cb, this); if (!base64) { throw CABRTException(EXCEP_PLUGIN, "CReporterBugzilla::AddAttachemnt(): cannot initialize base64."); } NSSBase64Encoder_Update(base64, reinterpret_cast(it->second[CD_CONTENT].c_str()), it->second[CD_CONTENT].length()); NSSBase64Encoder_Destroy(base64, PR_FALSE); paramList.add(xmlrpc_c::value_string(pBugId)); attachmentParams["description"] = xmlrpc_c::value_string("File: " + it->first); attachmentParams["filename"] = xmlrpc_c::value_string(it->first); attachmentParams["contenttype"] = xmlrpc_c::value_string("text/plain"); attachmentParams["data"] = xmlrpc_c::value_string(m_sAttchmentInBase64); paramList.add(xmlrpc_c::value_struct(attachmentParams)); xmlrpc_c::rpcPtr rpc(new xmlrpc_c::rpc("bugzilla.addAttachment", paramList)); try { rpc->call(m_pXmlrpcClient, m_pCarriageParm); ret = xmlrpc_c::value_array(rpc->getResult()).vectorValueValue(); std::stringstream ss; ss << xmlrpc_c::value_int(ret[0]); log("New attachment id: %s", ss.str().c_str()); } catch (std::exception& e) { throw CABRTException(EXCEP_PLUGIN, std::string("CReporterBugzilla::AddAttachemnt(): ") + e.what()); } } } */ } std::string CReporterBugzilla::Report(const map_crash_report_t& pCrashReport, const std::string& pArgs) { int32_t bug_id = -1; std::string component = pCrashReport.find(FILENAME_COMPONENT)->second[CD_CONTENT]; std::string uuid = pCrashReport.find(CD_UUID)->second[CD_CONTENT]; try { new_xmlrpc_client(m_sBugzillaXMLRPC.c_str(), m_bNoSSLVerify); update_client(_("Checking for duplicates...")); bug_id = check_uuid_in_bugzilla(component.c_str(), uuid.c_str()); if (bug_id > 0) { update_client(_("Logging into bugzilla...")); if ((m_sLogin == "") && (m_sPassword=="")) { VERB3 log("Empty login and password"); throw CABRTException(EXCEP_PLUGIN, std::string(_("Empty login and password. Please check Bugzilla.conf"))); } login(m_sLogin.c_str(), m_sPassword.c_str()); update_client(_("Checking CC...")); if (!check_cc_and_reporter(bug_id, m_sLogin.c_str())) { add_plus_one_cc(bug_id, m_sLogin.c_str()); } destroy_xmlrpc_client(); return m_sBugzillaURL + "/show_bug.cgi?id="+to_string(bug_id); } } catch (CABRTException& e) { destroy_xmlrpc_client(); throw CABRTException(EXCEP_PLUGIN, std::string("CReporterBugzilla::Report(): ") + e.what()); return ""; } destroy_xmlrpc_client(); if (bug_id > 0) { return m_sBugzillaURL + "/show_bug.cgi?id="+to_string(bug_id); } return m_sBugzillaURL + "/show_bug.cgi?id="; } void CReporterBugzilla::SetSettings(const map_plugin_settings_t& pSettings) { if (pSettings.find("BugzillaURL") != pSettings.end()) { m_sBugzillaURL = pSettings.find("BugzillaURL")->second; //remove the /xmlrpc.cgi part from old settings //FIXME: can be removed after users are informed about new config format std::string::size_type pos = m_sBugzillaURL.find(XML_RPC_SUFFIX); if(pos != std::string::npos) { m_sBugzillaURL.erase(pos); } //remove the trailing '/' while (m_sBugzillaURL[m_sBugzillaURL.length() - 1] == '/') { m_sBugzillaURL.erase(m_sBugzillaURL.length() - 1); } /* if(*(--m_sBugzillaURL.end()) == '/') { m_sBugzillaURL.erase(--m_sBugzillaURL.end()); } */ m_sBugzillaXMLRPC = m_sBugzillaURL + std::string(XML_RPC_SUFFIX); } if (pSettings.find("Login") != pSettings.end()) { m_sLogin = pSettings.find("Login")->second; } if (pSettings.find("Password") != pSettings.end()) { m_sPassword = pSettings.find("Password")->second; } if (pSettings.find("NoSSLVerify") != pSettings.end()) { m_bNoSSLVerify = pSettings.find("NoSSLVerify")->second == "yes"; } } map_plugin_settings_t CReporterBugzilla::GetSettings() { map_plugin_settings_t ret; ret["BugzillaURL"] = m_sBugzillaURL; ret["Login"] = m_sLogin; ret["Password"] = m_sPassword; ret["NoSSLVerify"] = m_bNoSSLVerify ? "yes" : "no"; return ret; } PLUGIN_INFO(REPORTER, CReporterBugzilla, "Bugzilla", "0.0.3", "Check if a bug isn't already reported in a bugzilla " "and if not, report it.", "zprikryl@redhat.com", "https://fedorahosted.org/abrt/wiki", PLUGINS_LIB_DIR"/Bugzilla.GTKBuilder");