This patch splits off Bugzilla plugin's bugzilla submission
generation into a separate tool:
# abrt-action-bugzilla -z
abrt-action-bugzilla: invalid option -- 'z'
Usage: abrt-action-bugzilla -c CONFFILE -d DIR [-vs]
Report a crash to Bugzilla
Options:
-c FILE Configuration file (may be given many times)
-d DIR Crash dump directory
-v Verbose
-s Log to syslog
This change is bigger than previous similar patches:
I needed to find a way to pass settings to the child
and get back reporting result - all without breaking
current interface.
I decided to pass configuration via -c CONFFILE:
(1) it is trivial to pass /etc/abrt/bugzilla.conf to it,
and
(2) I can use "-c -" to parse config from stdin.
abrtd simply write per-reporting settings to child's stdin.
The log and reporting result (success/failure) is
taken from child's stdout.
Lightly run tested. I plan to do more run-testing now,
For now, please review the code. Don't run-test yet.
After a few more such patches, we can remove XMLRPC
and HTTP code and library dependencies from abrtd.
--
vda
diff -x '*.po' -d -urpN abrt.0/abrt.spec abrt.1/abrt.spec
--- abrt.0/abrt.spec 2010-09-23 16:50:00.000000000 +0200
+++ abrt.1/abrt.spec 2010-10-07 14:04:49.317132230 +0200
@@ -351,6 +351,7 @@ fi
%{_sbindir}/abrt-server
%{_sbindir}/abrt-action-generate-backtrace
%{_sbindir}/abrt-action-save-package-data
+%{_bindir}/abrt-action-bugzilla
%{_bindir}/abrt-action-install-debuginfo
%{_bindir}/%{name}-handle-upload
%{_bindir}/%{name}-backtrace
diff -x '*.po' -d -urpN abrt.0/inc/crash_types.h abrt.1/inc/crash_types.h
--- abrt.0/inc/crash_types.h 2010-09-23 16:48:44.000000000 +0200
+++ abrt.1/inc/crash_types.h 2010-10-07 01:54:44.263155279 +0200
@@ -115,6 +115,8 @@ void add_to_crash_data(map_crash_data_t&
const char *pItem,
const char *pContent);
+void load_crash_data_from_debug_dump(struct dump_dir *dd, map_crash_data_t& data);
+
const char *get_crash_data_item_content_or_NULL(const map_crash_data_t& crash_data,
const char *key);
// Aborts if key is not found:
const std::string& get_crash_data_item_content(const map_crash_data_t&
crash_data, const char *key);
diff -x '*.po' -d -urpN abrt.0/lib/plugins/Bugzilla.cpp
abrt.1/lib/plugins/Bugzilla.cpp
--- abrt.0/lib/plugins/Bugzilla.cpp 2010-09-23 16:48:44.000000000 +0200
+++ abrt.1/lib/plugins/Bugzilla.cpp 2010-10-07 16:05:02.144132594 +0200
@@ -18,965 +18,128 @@
*/
#include "abrtlib.h"
-#include "abrt_xmlrpc.h"
#include "Bugzilla.h"
#include "crash_types.h"
#include "abrt_exception.h"
#include "comm_layer_inner.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;
- std::vector<const char*> bug_cc;
-};
-
-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;
-}
-
-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);
-
- if (!bz->bug_cc.empty())
- {
- for (int ii = 0; ii < bz->bug_cc.size(); ii++)
- free((void*)bz->bug_cc[ii]);
-
- bz->bug_cc.clear();
- }
-}
-
-static int am_i_in_cc(const struct bug_info* bz, const char* login)
-{
- if (bz->bug_cc.empty())
- return -1;
-
- int size = bz->bug_cc.size();
- for (int ii = 0; ii < size; ii++)
- {
- if (strcmp(login, bz->bug_cc[ii]) == 0)
- return 0;
- }
- return -1;
-}
-
-/*
- * 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(const map_crash_data_t& pCrashData, int depend_on_bugno);
- int add_attachments(const char* bug_id_str, const map_crash_data_t&
pCrashData);
- 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, ...)
-{
- va_list args;
- xmlrpc_value* param = NULL;
- xmlrpc_value* result = NULL;
- const char* suffix;
-
- va_start(args, format);
- xmlrpc_build_value_va(&env, format, args, ¶m, &suffix);
- va_end(args);
-
- if (!env.fault_occurred)
- {
- 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);
-
- xmlrpc_DECREF(param);
- return NULL;
- }
-
- 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.push_back(cc);
- VERB3 log("member on cc is %s", cc);
- continue;
- }
- free((void*)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(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;
-}
-
-xmlrpc_int32 ctx::new_bug(const map_crash_data_t& pCrashData, int depend_on_bugno)
-{
- const char *package = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_PACKAGE);
- const char *component = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_COMPONENT);
- const char *release = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_RELEASE);
- const char *arch = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_ARCHITECTURE);
- const char *duphash = get_crash_data_item_content_or_NULL(pCrashData,
CD_DUPHASH);
- const char *reason = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_REASON);
- const char *function = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_CRASH_FUNCTION);
-
- 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);
-
- char *status_whiteboard = xasprintf("abrt_hash:%s", duphash);
-
- char *bz_dsc = make_description_bz(pCrashData);
- char *full_dsc = xasprintf("abrt version: "VERSION"\n%s",
bz_dsc);
- free(bz_dsc);
-
- char *product = NULL;
- char *version = NULL;
- parse_release(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);
- update_client(_("New bug id: %i"), (int)bug_id);
-
- return bug_id;
-}
-
-int ctx::add_attachments(const char* bug_id_str, const map_crash_data_t& pCrashData)
-{
- map_crash_data_t::const_iterator it = pCrashData.begin();
- for (; it != pCrashData.end(); it++)
- {
- const char *itemname = it->first.c_str();
- const char *type = it->second[CD_TYPE].c_str();
- const char *content = it->second[CD_CONTENT].c_str();
-
- if ((strcmp(type, CD_TXT) == 0)
- && (strlen(content) > CD_TEXT_ATT_SIZE || (strcmp(itemname,
FILENAME_BACKTRACE) == 0))
- ) {
- char *encoded64 = encode_base64(content, strlen(content));
- char *filename = xasprintf("File: %s", itemname);
- xmlrpc_value* result = call("bugzilla.addAttachment",
"(s{s:s,s:s,s:s,s:s})", bug_id_str,
- "description", filename,
- "filename", itemname,
- "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)
-{
- xmlrpc_value* result = call("bugzilla.getBug", "(s)",
to_string(bug_id).c_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;
-}
-
-//-------------------------------------------------------------------
-// ^
-// | nice
-// -------------------------------------------------------------------
-// | BAD
-// v
-//-------------------------------------------------------------------
-//TODO: need to rewrite
-void ctx::login(const char* login, const char* passwd)
-{
- xmlrpc_value* result = call("User.login", "({s:s,s:s})",
"login", login, "password", passwd);
-
- if (!result)
- {
- char *errmsg = xasprintf("Can't login. Check
Edit->Plugins->Bugzilla and /etc/abrt/plugins/Bugzilla.conf. Server said: %s",
env.fault_string);
- error_msg("%s", errmsg); // show error in daemon log
- CABRTException e(EXCEP_PLUGIN, errmsg);
- free(errmsg);
- throw e;
- }
- 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 */
-
-
-/*
- * CReporterBugzilla
- */
-
-static map_plugin_settings_t parse_settings(const map_plugin_settings_t& pSettings)
-{
- map_plugin_settings_t plugin_settings;
-
- map_plugin_settings_t::const_iterator end = pSettings.end();
- map_plugin_settings_t::const_iterator it;
- it = pSettings.find("BugzillaURL");
- if (it != end)
- {
- std::string BugzillaURL = it->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 = BugzillaURL.find(XML_RPC_SUFFIX);
- if (pos != std::string::npos)
- {
- BugzillaURL.erase(pos);
- }
- //remove the trailing '/'
- while (BugzillaURL[BugzillaURL.length() - 1] == '/')
- {
- BugzillaURL.erase(BugzillaURL.length() - 1);
- }
- plugin_settings["BugzillaXMLRPC"] = BugzillaURL + XML_RPC_SUFFIX;
- plugin_settings["BugzillaURL"] = BugzillaURL;
- }
-
- it = pSettings.find("Login");
- if (it == end)
- {
- /* if any of the option is not set we use the defaults for everything */
- plugin_settings.clear();
- return plugin_settings;
- }
- plugin_settings["Login"] = it->second;
-
- it = pSettings.find("Password");
- if (it == end)
- {
- plugin_settings.clear();
- return plugin_settings;
- }
- plugin_settings["Password"] = it->second;
-
- it = pSettings.find("SSLVerify");
- if (it == end)
- {
- plugin_settings.clear();
- return plugin_settings;
- }
- plugin_settings["SSLVerify"] = it->second;
-
- VERB1 log("User settings ok, using them instead of defaults");
- return plugin_settings;
-}
-
CReporterBugzilla::CReporterBugzilla()
{
- m_ssl_verify = true;
- m_rating_required = true;
- m_login = NULL;
- m_password = NULL;
- m_bugzilla_url = xstrdup("https://bugzilla.redhat.com");
- m_bugzilla_xmlrpc = xstrdup("https://bugzilla.redhat.com"XML_RPC_SUFFIX);
+ m_pSettings["BugzillaURL"] = "https://bugzilla.redhat.com";
+ m_pSettings["Login"] = "";
+ m_pSettings["Password"] = "";
+ m_pSettings["SSLVerify"] = "yes";
+ m_pSettings["RatingRequired"] = "yes";
}
CReporterBugzilla::~CReporterBugzilla()
{
- free(m_login);
- free(m_password);
- free(m_bugzilla_url);
- free(m_bugzilla_xmlrpc);
}
-std::string CReporterBugzilla::Report(const map_crash_data_t& pCrashData,
- const map_plugin_settings_t& pSettings,
- const char *pArgs)
+void CReporterBugzilla::SetSettings(const map_plugin_settings_t& pSettings)
{
- xmlrpc_int32 bug_id = -1;
- const char *login = NULL;
- const char *password = NULL;
- const char *bugzilla_xmlrpc = NULL;
- const char *bugzilla_url = NULL;
- bool ssl_verify;
-
- map_plugin_settings_t settings = parse_settings(pSettings);
- /* if parse_settings fails it returns an empty map so we need to use defaults */
- if (!settings.empty())
- {
- login = settings["Login"].c_str();
- password = settings["Password"].c_str();
- bugzilla_xmlrpc = settings["BugzillaXMLRPC"].c_str();
- bugzilla_url = settings["BugzillaURL"].c_str();
- ssl_verify = string_to_bool(settings["SSLVerify"].c_str());
- }
- else
- {
- login = m_login;
- password = m_password;
- bugzilla_xmlrpc = m_bugzilla_xmlrpc;
- bugzilla_url = m_bugzilla_url;
- ssl_verify = m_ssl_verify;
- }
-
- if (!login[0] || !password[0])
- {
- VERB3 log("Empty login and password");
- throw CABRTException(EXCEP_PLUGIN, _("Empty login or password.\nPlease check
"PLUGINS_CONF_DIR"/Bugzilla.conf."));
- }
-
- const char *component = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_COMPONENT);
- const char *duphash = get_crash_data_item_content_or_NULL(pCrashData, CD_DUPHASH);
- const char *release = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_RELEASE);
-
- ctx bz_server(bugzilla_xmlrpc, ssl_verify);
-
- update_client(_("Logging into bugzilla..."));
- bz_server.login(login, password);
-
- update_client(_("Checking for duplicates..."));
-
- char *product = NULL;
- char *version = NULL;
- parse_release(release, &product, &version);
-
- xmlrpc_value *result;
- if (strcmp(product, "Fedora") == 0)
- result = bz_server.call_quicksearch_duphash(component, product, duphash);
- else
- result = bz_server.call_quicksearch_duphash(component, NULL, duphash);
+//BUG! This gets called when user's keyring contains login data,
+//then it takes precedence over /etc/abrt/plugins/Bugzilla.conf.
+//I got a case when keyring had a STALE password, and there was no way
+//for me to know that it is being used. Moreover, when I discovered it
+//(by hacking abrt source!), I don't know how to purge it from the keyring.
+//At the very least, log("SOMETHING") here.
- if (!result)
- throw_if_xml_fault_occurred(&bz_server.env);
+ /* Can't simply do this:
- xmlrpc_value *all_bugs = bz_server.get_member("bugs", result);
- xmlrpc_DECREF(result);
+ m_pSettings = pSettings;
- if (!all_bugs)
- {
- throw_if_xml_fault_occurred(&bz_server.env);
- throw CABRTException(EXCEP_PLUGIN, _("Missing mandatory member
'bugs'"));
- }
+ * - it will erase keys which aren't present in pSettings.
+ * Example: if Bugzilla.conf doesn't have "Login = foo",
+ * then there's no pSettings["Login"] and m_pSettings = pSettings
+ * will nuke default m_pSettings["Login"] = "",
+ * making GUI think that we have no "Login" key at all
+ * and thus never overriding it - even if it *has* an override!
+ */
- int all_bugs_size = bz_server.get_array_size(all_bugs);
- struct bug_info bz;
- int depend_on_bugno = -1;
- if (all_bugs_size > 0)
+ map_plugin_settings_t::iterator it = m_pSettings.begin();
+ while (it != m_pSettings.end())
{
- 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);
- throw CABRTException(EXCEP_PLUGIN, _("get_bug_info() failed. Could not
collect all mandatory information"));
- }
-
- if (strcmp(bz.bug_product, product) != 0)
+ map_plugin_settings_t::const_iterator override = pSettings.find(it->first);
+ if (override != pSettings.end())
{
- depend_on_bugno = bug_id;
- bug_info_destroy(&bz);
- result = bz_server.call_quicksearch_duphash(component, release, duphash);
- if (!result)
- throw_if_xml_fault_occurred(&bz_server.env);
-
- all_bugs = bz_server.get_member("bugs", result);
- xmlrpc_DECREF(result);
-
- if (!all_bugs)
- {
- throw_if_xml_fault_occurred(&bz_server.env);
- throw CABRTException(EXCEP_PLUGIN, _("Missing mandatory member
'bugs'"));
- }
-
- all_bugs_size = bz_server.get_array_size(all_bugs);
- if (all_bugs_size > 0)
- {
- bug_id = bz_server.get_bug_id(all_bugs);
- xmlrpc_DECREF(all_bugs);
- if (bug_id == -1)
- throw_if_xml_fault_occurred(&bz_server.env);
-
- bug_info_init(&bz);
- if (bz_server.get_bug_info(&bz, bug_id) == -1)
- {
- bug_info_destroy(&bz);
- throw_if_xml_fault_occurred(&bz_server.env);
- throw CABRTException(EXCEP_PLUGIN, _("get_bug_info() failed.
Could not collect all mandatory information"));
- }
- }
- else
- xmlrpc_DECREF(all_bugs);
+ VERB3 log(" 3 settings[%s]='%s'", it->first.c_str(),
it->second.c_str());
+ it->second = override->second;
}
+ it++;
}
- free(product);
- free(version);
+}
- if (all_bugs_size < 0)
- {
- throw_if_xml_fault_occurred(&bz_server.env);
- }
- else if (all_bugs_size == 0) // Create new bug
- {
- update_client(_("Creating a new bug..."));
- bug_id = bz_server.new_bug(pCrashData, depend_on_bugno);
- if (bug_id < 0)
- {
- throw_if_xml_fault_occurred(&bz_server.env);
- throw CABRTException(EXCEP_PLUGIN, _("Bugzilla entry creation
failed"));
- }
+std::string CReporterBugzilla::Report(const map_crash_data_t& crash_data,
+ const map_plugin_settings_t& settings,
+ const char *args)
+{
+ /* abrt-action-bugzilla [-s] -c /etc/arbt/Bugzilla.conf -c - -d pCrashData.dir NULL
*/
+ char *argv[9];
+ char **pp = argv;
+ *pp++ = (char*)"abrt-action-bugzilla";
- log("Adding attachments to bug %d...", bug_id);
- int ret = bz_server.add_attachments(to_string(bug_id).c_str(), pCrashData);
- if (ret == -1)
- {
- throw_if_xml_fault_occurred(&bz_server.env);
- }
+//We want to consume output, so don't redirect to syslog.
+// if (logmode & LOGMODE_SYSLOG)
+// *pp++ = (char*)"-s";
+//TODO: the actions<->daemon interaction will be changed anyway...
- update_client(_("Logging out..."));
- bz_server.logout();
+ *pp++ = (char*)"-c";
+ *pp++ = (char*)(PLUGINS_CONF_DIR"/Bugzilla."PLUGINS_CONF_EXTENSION);
+ *pp++ = (char*)"-c";
+ *pp++ = (char*)"-";
+ *pp++ = (char*)"-d";
+ *pp++ = (char*)get_crash_data_item_content_or_NULL(crash_data, CD_DUMPDIR);
+ *pp = NULL;
+ int pipefds[2];
+ pid_t pid = fork_execv_on_steroids(EXECFLG_INPUT + EXECFLG_OUTPUT + EXECFLG_ERR2OUT,
+ argv,
+ pipefds,
+ /* unsetenv_vec: */ NULL,
+ /* dir: */ NULL,
+ /* uid(unused): */ 0
+ );
- std::string bug_status = ssprintf(
- "Status: NEW\n"
- "%s/show_bug.cgi?id=%u",
- bugzilla_url,
- (int)bug_id
- );
- return bug_status;
- }
- else if (all_bugs_size > 1)
+ /* Write the configuration to stdin */
+ map_plugin_settings_t::const_iterator it = settings.begin();
+ while (it != settings.end())
{
- // 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);
+ full_write_str(pipefds[1], it->first.c_str());
+ full_write_str(pipefds[1], "=");
+ full_write_str(pipefds[1], it->second.c_str());
+ full_write_str(pipefds[1], "\n");
+ it++;
}
+ close(pipefds[1]);
- // decision based on state
- update_client(_("Bug is already reported: %i"), bug_id);
-
- xmlrpc_int32 original_bug_id = bug_id;
- if ((strcmp(bz.bug_status, "CLOSED") == 0) &&
(strcmp(bz.bug_resolution, "DUPLICATE") == 0))
- {
- for (int ii = 0; ii <= MAX_HOPS; ii++)
- {
- if (ii == MAX_HOPS)
- {
- VERB3 log("Bugzilla could not find a parent of bug %d",
(int)original_bug_id);
- bug_info_destroy(&bz);
- throw CABRTException(EXCEP_PLUGIN, _("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);
- }
- throw CABRTException(EXCEP_PLUGIN, _("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;
- }
- }
+ FILE *fp = fdopen(pipefds[0], "r");
+ if (!fp)
+ die_out_of_memory();
- if (strcmp(bz.bug_status, "CLOSED") != 0)
+ /* Consume log from stdout */
+ std::string bug_status;
+ char buf[512];
+ while (fgets(buf, sizeof(buf), fp))
{
- int status = 0;
- if ((strcmp(bz.bug_reporter, login) != 0) && (am_i_in_cc(&bz,
login)))
- {
- VERB2 log(_("Add %s to CC list"), login);
- update_client(_("Add %s to CC list"), login);
- status = bz_server.add_plus_one_cc(bug_id, login);
- }
-
- if (status == -1)
- {
- bug_info_destroy(&bz);
- throw_if_xml_fault_occurred(&bz_server.env);
- }
-
- char *dsc = make_description_reproduce_comment(pCrashData);
- if (dsc)
+ strchrnul(buf, '\n')[0] = '\0';
+ if (strncmp(buf, "STATUS:", 7) == 0)
+ bug_status = buf + 7;
+ else
+ if (strncmp(buf, "EXCEPT:", 7) == 0)
{
- const char* package = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_PACKAGE);
- const char* release = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_RELEASE);
- const char* arch = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_ARCHITECTURE);
- const char* is_private = get_crash_data_item_content_or_NULL(pCrashData,
"is_private");
-
- char *full_dsc = xasprintf("Package: %s\n"
- "Architecture: %s\n"
- "OS Release: %s\n"
- "%s", package, arch, release, dsc
- );
-
- update_client(_("Adding new comment to bug %d"), (int)bug_id);
-
- free(dsc);
-
- bool is_priv = is_private && (is_private[0] == '1');
- if (bz_server.add_comment(bug_id, full_dsc, is_priv) == -1)
- {
- free(full_dsc);
- bug_info_destroy(&bz);
- throw_xml_fault(&bz_server.env);
- }
- free(full_dsc);
+ fclose(fp);
+ waitpid(pid, NULL, 0);
+ throw CABRTException(EXCEP_PLUGIN, "%s", buf + 7);
}
- }
-
- update_client(_("Logging out..."));
- bz_server.logout();
-
- std::string bug_status = ssprintf(
- "Status: %s%s%s\n"
- "%s/show_bug.cgi?id=%u",
- bz.bug_status,
- bz.bug_resolution ? " " : "",
- bz.bug_resolution ? bz.bug_resolution : "",
- bugzilla_url,
- (int)bug_id
- );
-
- bug_info_destroy(&bz);
-
- return bug_status;
-}
-
-void CReporterBugzilla::SetSettings(const map_plugin_settings_t& pSettings)
-{
- m_pSettings = pSettings;
-
-//BUG! This gets called when user's keyring contains login data,
-//then it takes precedence over /etc/abrt/plugins/Bugzilla.conf.
-//I got a case when keyring had a STALE password, and there was no way
-//for me to know that it is being used. Moreover, when I discovered it
-//(by hacking abrt source!), I don't know how to purge it from the keyring.
-//At the very least, log("SOMETHING") here.
-
- map_plugin_settings_t::const_iterator end = pSettings.end();
- map_plugin_settings_t::const_iterator it;
- it = pSettings.find("BugzillaURL");
- if (it != end)
- {
- free(m_bugzilla_url);
- free(m_bugzilla_xmlrpc);
-
- m_bugzilla_url = xstrdup(it->second.c_str());
-
- int cnt = strlen(m_bugzilla_url);
- while (m_bugzilla_url[cnt--] == '/')
- m_bugzilla_url[cnt] = '\0';
-
- int ret = suffixcmp(m_bugzilla_url, XML_RPC_SUFFIX);
- if (ret != 0)
- m_bugzilla_xmlrpc = xasprintf("%s%s", m_bugzilla_url,
XML_RPC_SUFFIX);
else
- m_bugzilla_xmlrpc = xstrdup(m_bugzilla_url);
- }
- it = pSettings.find("Login");
- if (it != end)
- {
- free(m_login);
- m_login = xstrdup(it->second.c_str());
- }
- it = pSettings.find("Password");
- if (it != end)
- {
- free(m_password);
- m_password = xstrdup(it->second.c_str());
- }
- it = pSettings.find("SSLVerify");
- if (it != end)
- {
- m_ssl_verify = string_to_bool(it->second.c_str());
+ update_client("%s", buf);
}
-}
-/* Should not be deleted (why?) */
-const map_plugin_settings_t& CReporterBugzilla::GetSettings()
-{
- m_pSettings["BugzillaURL"] = (m_bugzilla_url)? m_bugzilla_url:
"";
- m_pSettings["Login"] = (m_login)? m_login: "";
- m_pSettings["Password"] = (m_password)? m_password: "";
- m_pSettings["SSLVerify"] = m_ssl_verify ? "yes" :
"no";
- m_pSettings["RatingRequired"] = m_rating_required ? "yes" :
"no";
+ fclose(fp); /* this also closes pipefds[0] */
+ /* wait for child to actually exit, and prevent leaving a zombie behind */
+ waitpid(pid, NULL, 0);
- return m_pSettings;
+ return bug_status;
}
PLUGIN_INFO(REPORTER,
diff -x '*.po' -d -urpN abrt.0/lib/plugins/Bugzilla.h
abrt.1/lib/plugins/Bugzilla.h
--- abrt.0/lib/plugins/Bugzilla.h 2010-09-23 16:48:44.000000000 +0200
+++ abrt.1/lib/plugins/Bugzilla.h 2010-10-07 15:47:48.902132205 +0200
@@ -24,14 +24,6 @@
class CReporterBugzilla : public CReporter
{
- private:
- bool m_ssl_verify;
- char *m_bugzilla_url;
- char *m_bugzilla_xmlrpc;
- char *m_login;
- char *m_password;
- bool m_rating_required;
-
public:
CReporterBugzilla();
virtual ~CReporterBugzilla();
@@ -39,9 +31,7 @@ class CReporterBugzilla : public CReport
virtual std::string Report(const map_crash_data_t& pCrashData,
const map_plugin_settings_t& pSettings,
const char *pArgs);
-
virtual void SetSettings(const map_plugin_settings_t& pSettings);
- virtual const map_plugin_settings_t& GetSettings();
};
#endif /* BUGZILLA_H_ */
diff -x '*.po' -d -urpN abrt.0/lib/plugins/Logger.cpp
abrt.1/lib/plugins/Logger.cpp
--- abrt.0/lib/plugins/Logger.cpp 2010-09-23 16:48:44.000000000 +0200
+++ abrt.1/lib/plugins/Logger.cpp 2010-10-07 01:14:22.110212508 +0200
@@ -66,7 +66,7 @@ std::string CLogger::Report(const map_cr
throw CABRTException(EXCEP_PLUGIN, "Can't open '%s'",
fname);
update_client(_("Writing report to '%s'"), fname);
- full_write(fd, full_dsc, strlen(full_dsc));
+ full_write_str(fd, full_dsc);
free(full_dsc);
close(fd);
diff -x '*.po' -d -urpN abrt.0/lib/plugins/Mailx.cpp abrt.1/lib/plugins/Mailx.cpp
--- abrt.0/lib/plugins/Mailx.cpp 2010-09-23 16:48:44.000000000 +0200
+++ abrt.1/lib/plugins/Mailx.cpp 2010-10-07 01:14:11.844962539 +0200
@@ -52,7 +52,7 @@ static void exec_and_feed_input(uid_t ui
/*dir:*/ NULL,
uid);
- full_write(pipein[1], text, strlen(text));
+ full_write_str(pipein[1], text);
close(pipein[1]);
waitpid(child, NULL, 0); /* wait for command completion */
diff -x '*.po' -d -urpN abrt.0/lib/plugins/Makefile.am
abrt.1/lib/plugins/Makefile.am
--- abrt.0/lib/plugins/Makefile.am 2010-09-23 16:48:44.000000000 +0200
+++ abrt.1/lib/plugins/Makefile.am 2010-10-07 01:59:32.532152619 +0200
@@ -116,9 +116,9 @@ libSOSreport_la_LDFLAGS = -avoid-version
# Bugzilla
libBugzilla_la_SOURCES = Bugzilla.h Bugzilla.cpp
-libBugzilla_la_LIBADD = $(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS)
+libBugzilla_la_LIBADD =
libBugzilla_la_LDFLAGS = -avoid-version
-libBugzilla_la_CPPFLAGS = $(XMLRPC_CFLAGS) $(XMLRPC_CLIENT_CFLAGS) \
+libBugzilla_la_CPPFLAGS = \
-I$(INC_PATH) -I$(UTILS_PATH) \
-DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
-DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\"
diff -x '*.po' -d -urpN abrt.0/lib/utils/CrashTypes.cpp
abrt.1/lib/utils/CrashTypes.cpp
--- abrt.0/lib/utils/CrashTypes.cpp 2010-09-23 16:48:44.000000000 +0200
+++ abrt.1/lib/utils/CrashTypes.cpp 2010-10-07 01:54:17.900406279 +0200
@@ -82,6 +82,129 @@ void add_to_crash_data(map_crash_data_t&
add_to_crash_data_ext(pCrashData, pItem, CD_TXT, CD_ISNOTEDITABLE, pContent);
}
+static char* is_text_file(const char *name, ssize_t *sz)
+{
+ /* We were using magic.h API to check for file being text, but it thinks
+ * that file containing just "0" is not text (!!)
+ * So, we do it ourself.
+ */
+
+ int fd = open(name, O_RDONLY);
+ if (fd < 0)
+ return NULL; /* it's not text (because it does not exist! :) */
+
+ /* Maybe 64k limit is small. But _some_ limit is necessary:
+ * fields declared "text" may end up in editing fields and such.
+ * We don't want to accidentally end up with 100meg text in a textbox!
+ * So, don't remove this. If you really need to, raise the limit.
+ */
+ off_t size = lseek(fd, 0, SEEK_END);
+ if (size < 0 || size > 64*1024)
+ {
+ close(fd);
+ return NULL; /* it's not a SMALL text */
+ }
+ lseek(fd, 0, SEEK_SET);
+
+ char *buf = (char*)xmalloc(*sz);
+ ssize_t r = *sz = full_read(fd, buf, *sz);
+ close(fd);
+ if (r < 0)
+ {
+ free(buf);
+ return NULL; /* it's not text (because we can't read it) */
+ }
+
+ /* Some files in our dump directories are known to always be textual */
+ const char *base = strrchr(name, '/');
+ if (base)
+ {
+ base++;
+ if (strcmp(base, FILENAME_BACKTRACE) == 0
+ || strcmp(base, FILENAME_CMDLINE) == 0
+ ) {
+ return buf;
+ }
+ }
+
+ /* Every once in a while, even a text file contains a few garbled
+ * or unexpected non-ASCII chars. We should not declare it "binary".
+ */
+ const unsigned RATIO = 50;
+ unsigned total_chars = r + RATIO;
+ unsigned bad_chars = 1; /* 1 prevents division by 0 later */
+ while (--r >= 0)
+ {
+ if (buf[r] >= 0x7f
+ /* among control chars, only '\t','\n' etc are allowed */
+ || (buf[r] < ' ' && !isspace(buf[r]))
+ ) {
+ if (buf[r] == '\0')
+ {
+ /* We don't like NULs very much. Not text for sure! */
+ free(buf);
+ return NULL;
+ }
+ bad_chars++;
+ }
+ }
+
+ if ((total_chars / bad_chars) >= RATIO)
+ return buf; /* looks like text to me */
+
+ free(buf);
+ return NULL; /* it's binary */
+}
+
+void load_crash_data_from_debug_dump(struct dump_dir *dd, map_crash_data_t& data)
+{
+ char *short_name;
+ char *full_name;
+
+ dd_init_next_file(dd);
+ while (dd_get_next_file(dd, &short_name, &full_name))
+ {
+ ssize_t sz = 4*1024;
+ char *text = NULL;
+ bool editable = is_editable_file(short_name);
+
+ if (!editable)
+ {
+ text = is_text_file(full_name, &sz);
+ if (!text)
+ {
+ add_to_crash_data_ext(data,
+ short_name,
+ CD_BIN,
+ CD_ISNOTEDITABLE,
+ full_name
+ );
+
+ free(short_name);
+ free(full_name);
+ continue;
+ }
+ }
+
+ char *content;
+ if (sz < 4*1024) /* is_text_file did read entire file */
+ content = xstrndup(text, sz); //TODO: can avoid this copying if
is_text_file() adds NUL
+ else /* no, need to read it all */
+ content = dd_load_text(dd, short_name);
+ free(text);
+
+ add_to_crash_data_ext(data,
+ short_name,
+ CD_TXT,
+ editable ? CD_ISEDITABLE : CD_ISNOTEDITABLE,
+ content
+ );
+ free(short_name);
+ free(full_name);
+ free(content);
+ }
+}
+
static const std::string* helper_get_crash_data_item_content(const map_crash_data_t&
crash_data, const char *key)
{
map_crash_data_t::const_iterator it = crash_data.find(key);
diff -x '*.po' -d -urpN abrt.0/src/daemon/abrt-action-bugzilla.cpp
abrt.1/src/daemon/abrt-action-bugzilla.cpp
--- abrt.0/src/daemon/abrt-action-bugzilla.cpp 1970-01-01 01:00:00.000000000 +0100
+++ abrt.1/src/daemon/abrt-action-bugzilla.cpp 2010-10-07 16:12:02.473132479 +0200
@@ -0,0 +1,906 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 RedHat Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "abrtlib.h"
+#include "abrt_xmlrpc.h"
+#include "crash_types.h"
+#include "abrt_exception.h"
+#include "comm_layer_inner.h"
+
+#include "plugin.h" /* make_description_bz */
+
+#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;
+ std::vector<char*> bug_cc;
+};
+
+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;
+}
+
+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);
+
+ if (!bz->bug_cc.empty())
+ {
+ for (unsigned ii = 0; ii < bz->bug_cc.size(); ii++)
+ free(bz->bug_cc[ii]);
+
+ bz->bug_cc.clear();
+ }
+}
+
+static int am_i_in_cc(const struct bug_info* bz, const char* login)
+{
+ if (bz->bug_cc.empty())
+ return -1;
+
+ int size = bz->bug_cc.size();
+ for (int ii = 0; ii < size; ii++)
+ {
+ if (strcmp(login, bz->bug_cc[ii]) == 0)
+ return 0;
+ }
+ return -1;
+}
+
+/*
+ * 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(const map_crash_data_t& pCrashData, int depend_on_bugno);
+ int add_attachments(const char* bug_id_str, const map_crash_data_t&
pCrashData);
+ 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, ...)
+{
+ va_list args;
+ xmlrpc_value* param = NULL;
+ xmlrpc_value* result = NULL;
+ const char* suffix;
+
+ va_start(args, format);
+ xmlrpc_build_value_va(&env, format, args, ¶m, &suffix);
+ va_end(args);
+
+ if (!env.fault_occurred)
+ {
+ 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);
+
+ xmlrpc_DECREF(param);
+ return NULL;
+ }
+
+ 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.push_back((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(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;
+}
+
+xmlrpc_int32 ctx::new_bug(const map_crash_data_t& pCrashData, int depend_on_bugno)
+{
+ const char *package = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_PACKAGE);
+ const char *component = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_COMPONENT);
+ const char *release = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_RELEASE);
+ const char *arch = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_ARCHITECTURE);
+ const char *duphash = get_crash_data_item_content_or_NULL(pCrashData,
CD_DUPHASH);
+ const char *reason = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_REASON);
+ const char *function = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_CRASH_FUNCTION);
+
+ 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);
+
+ char *status_whiteboard = xasprintf("abrt_hash:%s", duphash);
+
+ char *bz_dsc = make_description_bz(pCrashData);
+ char *full_dsc = xasprintf("abrt version: "VERSION"\n%s",
bz_dsc);
+ free(bz_dsc);
+
+ char *product = NULL;
+ char *version = NULL;
+ parse_release(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, const map_crash_data_t& pCrashData)
+{
+ map_crash_data_t::const_iterator it = pCrashData.begin();
+ for (; it != pCrashData.end(); it++)
+ {
+ const char *itemname = it->first.c_str();
+ const char *type = it->second[CD_TYPE].c_str();
+ const char *content = it->second[CD_CONTENT].c_str();
+
+ if ((strcmp(type, CD_TXT) == 0)
+ && (strlen(content) > CD_TEXT_ATT_SIZE || (strcmp(itemname,
FILENAME_BACKTRACE) == 0))
+ ) {
+ char *encoded64 = encode_base64(content, strlen(content));
+ char *filename = xasprintf("File: %s", itemname);
+ xmlrpc_value* result = call("bugzilla.addAttachment",
"(s{s:s,s:s,s:s,s:s})", bug_id_str,
+ "description", filename,
+ "filename", itemname,
+ "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)
+{
+ xmlrpc_value* result = call("bugzilla.getBug", "(s)",
to_string(bug_id).c_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);
+
+ if (!result)
+ {
+ char *errmsg = xasprintf("Can't login. Check
Edit->Plugins->Bugzilla and /etc/abrt/plugins/Bugzilla.conf. Server said: %s",
env.fault_string);
+ error_msg("%s", errmsg); // show error in daemon log
+ CABRTException e(EXCEP_PLUGIN, errmsg);
+ free(errmsg);
+ throw e;
+ }
+ 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,
+ /*const*/ map_plugin_settings_t& settings)
+{
+ struct dump_dir *dd = dd_init();
+ if (!dd_opendir(dd, dump_dir_name, DD_CLOSE_ON_OPEN_ERR))
+ {
+ throw CABRTException(EXCEP_PLUGIN, _("Can't open '%s'"),
dump_dir_name);
+ }
+ map_crash_data_t pCrashData;
+ load_crash_data_from_debug_dump(dd, pCrashData);
+ dd_close(dd);
+
+ const char *login;
+ const char *password;
+ const char *bugzilla_xmlrpc;
+ const char *bugzilla_url;
+ bool ssl_verify;
+
+ login = settings["Login"].c_str();
+ password = settings["Password"].c_str();
+ bugzilla_url = settings["BugzillaURL"].c_str();
+ if (!bugzilla_url[0])
+ bugzilla_url = "https://bugzilla.redhat.com";
+ bugzilla_xmlrpc = settings["BugzillaXMLRPC"].c_str();
+ if (!bugzilla_xmlrpc[0])
+ bugzilla_xmlrpc = xasprintf("%s"XML_RPC_SUFFIX, bugzilla_url);
+ ssl_verify = string_to_bool(settings["SSLVerify"].c_str());
+
+ if (!login[0] || !password[0])
+ {
+ VERB3 log("Empty login and password");
+ throw CABRTException(EXCEP_PLUGIN, _("Empty login or password, please check
%s"), PLUGINS_CONF_DIR"/Bugzilla.conf");
+ }
+
+ const char *component = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_COMPONENT);
+ const char *duphash = get_crash_data_item_content_or_NULL(pCrashData, CD_DUPHASH);
+ const char *release = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_RELEASE);
+
+ ctx bz_server(bugzilla_xmlrpc, ssl_verify);
+
+ log(_("Logging into bugzilla..."));
+ bz_server.login(login, password);
+
+ log(_("Checking for duplicates..."));
+
+ char *product = NULL;
+ char *version = NULL;
+ parse_release(release, &product, &version);
+
+ xmlrpc_value *result;
+ if (strcmp(product, "Fedora") == 0)
+ result = bz_server.call_quicksearch_duphash(component, product, duphash);
+ else
+ result = bz_server.call_quicksearch_duphash(component, NULL, duphash);
+
+ if (!result)
+ throw_if_xml_fault_occurred(&bz_server.env);
+
+ xmlrpc_value *all_bugs = bz_server.get_member("bugs", result);
+ xmlrpc_DECREF(result);
+
+ if (!all_bugs)
+ {
+ throw_if_xml_fault_occurred(&bz_server.env);
+ throw CABRTException(EXCEP_PLUGIN, _("Missing mandatory member
'bugs'"));
+ }
+
+ xmlrpc_int32 bug_id = -1;
+ int all_bugs_size = bz_server.get_array_size(all_bugs);
+ struct bug_info bz;
+ int depend_on_bugno = -1;
+ if (all_bugs_size > 0)
+ {
+ bug_id = bz_server.get_bug_id(all_bugs);
+ xmlrpc_DECREF(all_bugs);
+ if (bug_id == -1)
+ throw_if_xml_fault_occurred(&bz_server.env);
+
+ bug_info_init(&bz);
+ if (bz_server.get_bug_info(&bz, bug_id) == -1)
+ {
+ bug_info_destroy(&bz);
+ throw_if_xml_fault_occurred(&bz_server.env);
+ throw CABRTException(EXCEP_PLUGIN, _("get_bug_info() failed. Could not
collect all mandatory information"));
+ }
+
+ if (strcmp(bz.bug_product, product) != 0)
+ {
+ depend_on_bugno = bug_id;
+ bug_info_destroy(&bz);
+ result = bz_server.call_quicksearch_duphash(component, release, duphash);
+ if (!result)
+ throw_if_xml_fault_occurred(&bz_server.env);
+
+ all_bugs = bz_server.get_member("bugs", result);
+ xmlrpc_DECREF(result);
+
+ if (!all_bugs)
+ {
+ throw_if_xml_fault_occurred(&bz_server.env);
+ throw CABRTException(EXCEP_PLUGIN, _("Missing mandatory member
'bugs'"));
+ }
+
+ all_bugs_size = bz_server.get_array_size(all_bugs);
+ if (all_bugs_size > 0)
+ {
+ bug_id = bz_server.get_bug_id(all_bugs);
+ xmlrpc_DECREF(all_bugs);
+ if (bug_id == -1)
+ throw_if_xml_fault_occurred(&bz_server.env);
+
+ bug_info_init(&bz);
+ if (bz_server.get_bug_info(&bz, bug_id) == -1)
+ {
+ bug_info_destroy(&bz);
+ throw_if_xml_fault_occurred(&bz_server.env);
+ throw CABRTException(EXCEP_PLUGIN, _("get_bug_info() failed.
Could not collect all mandatory information"));
+ }
+ }
+ else
+ xmlrpc_DECREF(all_bugs);
+ }
+ }
+ free(product);
+ free(version);
+
+ if (all_bugs_size < 0)
+ {
+ throw_if_xml_fault_occurred(&bz_server.env);
+ }
+ else if (all_bugs_size == 0) // Create new bug
+ {
+ log(_("Creating a new bug..."));
+ bug_id = bz_server.new_bug(pCrashData, depend_on_bugno);
+ if (bug_id < 0)
+ {
+ throw_if_xml_fault_occurred(&bz_server.env);
+ throw CABRTException(EXCEP_PLUGIN, _("Bugzilla entry creation
failed"));
+ }
+
+ log("Adding attachments to bug %d...", bug_id);
+ int ret = bz_server.add_attachments(to_string(bug_id).c_str(), pCrashData);
+ if (ret == -1)
+ {
+ throw_if_xml_fault_occurred(&bz_server.env);
+ }
+
+ log(_("Logging out..."));
+ bz_server.logout();
+
+ printf("STATUS:Status: NEW %s/show_bug.cgi?id=%u\n",
+ bugzilla_url,
+ (int)bug_id
+ );
+ return;
+ }
+
+ if (all_bugs_size > 1)
+ {
+ // When someone clones bug it has same duphash, so we can find more than 1.
+ // Need to be checked if component is same.
+ VERB3 log("Bugzilla has %u reports with same duphash '%s'",
all_bugs_size, duphash);
+ }
+
+ // decision based on state
+ log(_("Bug is already reported: %i"), bug_id);
+
+ xmlrpc_int32 original_bug_id = bug_id;
+ if ((strcmp(bz.bug_status, "CLOSED") == 0) &&
(strcmp(bz.bug_resolution, "DUPLICATE") == 0))
+ {
+ for (int ii = 0; ii <= MAX_HOPS; ii++)
+ {
+ if (ii == MAX_HOPS)
+ {
+ VERB3 log("Bugzilla could not find a parent of bug %d",
(int)original_bug_id);
+ bug_info_destroy(&bz);
+ throw CABRTException(EXCEP_PLUGIN, _("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);
+ }
+ throw CABRTException(EXCEP_PLUGIN, _("get_bug_info() failed. Could
not collect all mandatory information"));
+ }
+
+ // found a bug which is not CLOSED as DUPLICATE
+ if (bz.bug_dup_id == -1)
+ break;
+ }
+ }
+
+ if (strcmp(bz.bug_status, "CLOSED") != 0)
+ {
+ int status = 0;
+ if ((strcmp(bz.bug_reporter, login) != 0) && (am_i_in_cc(&bz,
login)))
+ {
+ log(_("Add %s to CC list"), login);
+ status = bz_server.add_plus_one_cc(bug_id, login);
+ }
+
+ if (status == -1)
+ {
+ bug_info_destroy(&bz);
+ throw_if_xml_fault_occurred(&bz_server.env);
+ }
+
+ char *dsc = make_description_reproduce_comment(pCrashData);
+ if (dsc)
+ {
+ const char* package = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_PACKAGE);
+ const char* release = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_RELEASE);
+ const char* arch = get_crash_data_item_content_or_NULL(pCrashData,
FILENAME_ARCHITECTURE);
+ const char* is_private = get_crash_data_item_content_or_NULL(pCrashData,
"is_private");
+
+ char *full_dsc = xasprintf("Package: %s\n"
+ "Architecture: %s\n"
+ "OS Release: %s\n"
+ "%s", package, arch, release, dsc
+ );
+
+ log(_("Adding new comment to bug %d"), (int)bug_id);
+
+ free(dsc);
+
+ bool is_priv = is_private && (is_private[0] == '1');
+ if (bz_server.add_comment(bug_id, full_dsc, is_priv) == -1)
+ {
+ free(full_dsc);
+ bug_info_destroy(&bz);
+ throw_xml_fault(&bz_server.env);
+ }
+ free(full_dsc);
+ }
+ }
+
+ log(_("Logging out..."));
+ bz_server.logout();
+
+ printf("STATUS:Status: %s%s%s %s/show_bug.cgi?id=%u\n",
+ bz.bug_status,
+ bz.bug_resolution ? " " : "",
+ bz.bug_resolution ? bz.bug_resolution : "",
+ bugzilla_url,
+ (int)bug_id
+ );
+
+ bug_info_destroy(&bz);
+}
+
+int main(int argc, char **argv)
+{
+ char *env_verbose = getenv("ABRT_VERBOSE");
+ if (env_verbose)
+ g_verbose = atoi(env_verbose);
+
+ map_plugin_settings_t settings;
+
+ const char *dump_dir_name = ".";
+ enum {
+ OPT_s = (1 << 0),
+ };
+ int optflags = 0;
+ int opt;
+ while ((opt = getopt(argc, argv, "c:d:vs")) != -1)
+ {
+ switch (opt)
+ {
+ case 'c':
+ dump_dir_name = optarg;
+ VERB1 log("Loading settings from '%s'", optarg);
+ LoadPluginSettings(optarg, settings);
+ VERB3 log("Loaded '%s'", optarg);
+ break;
+ case 'd':
+ dump_dir_name = optarg;
+ break;
+ case 'v':
+ g_verbose++;
+ break;
+ case 's':
+ optflags |= OPT_s;
+ break;
+ default:
+ /* Careful: the string below contains tabs, dont replace with spaces */
+ error_msg_and_die(
+ "Usage: abrt-action-bugzilla -c CONFFILE -d DIR [-vs]"
+ "\n"
+ "\nReport a crash to Bugzilla"
+ "\n"
+ "\nOptions:"
+ "\n -c FILE Configuration file (may be given many times)"
+ "\n -d DIR Crash dump directory"
+ "\n -v Verbose"
+ "\n -s Log to syslog"
+ );
+ }
+ }
+
+ putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
+
+//DONT! our stdout/stderr goes directly to daemon, don't want to have prefix there.
+// msg_prefix = xasprintf("abrt-action-bugzilla[%u]", getpid());
+
+ if (optflags & OPT_s)
+ {
+ openlog(msg_prefix, 0, LOG_DAEMON);
+ logmode = LOGMODE_SYSLOG;
+ }
+
+ VERB1 log("Initializing XML-RPC library");
+ xmlrpc_env env;
+ xmlrpc_env_init(&env);
+ xmlrpc_client_setup_global_const(&env);
+ if (env.fault_occurred)
+ error_msg_and_die("XML-RPC Fault: %s(%d)", env.fault_string,
env.fault_code);
+ xmlrpc_env_clean(&env);
+
+ try
+ {
+ report_to_bugzilla(dump_dir_name, settings);
+ }
+ catch (CABRTException& e)
+ {
+ printf("EXCEPT:%s\n", e.what());
+ return 1;
+ }
+
+ return 0;
+}
diff -x '*.po' -d -urpN abrt.0/src/daemon/Makefile.am
abrt.1/src/daemon/Makefile.am
--- abrt.0/src/daemon/Makefile.am 2010-09-23 16:50:00.000000000 +0200
+++ abrt.1/src/daemon/Makefile.am 2010-10-07 14:30:08.810132557 +0200
@@ -7,6 +7,9 @@ sbin_PROGRAMS = abrtd \
abrt-action-generate-backtrace \
abrt-action-save-package-data
+bin_PROGRAMS = \
+ abrt-action-bugzilla
+
abrtd_SOURCES = \
PluginManager.h PluginManager.cpp \
MiddleWare.h MiddleWare.cpp \
@@ -97,6 +100,26 @@ abrt_action_save_package_data_LDADD = \
../../lib/utils/libABRTdUtils.la \
../../lib/utils/libABRTUtils.la
+abrt_action_bugzilla_SOURCES = \
+ abrt-action-bugzilla.cpp
+abrt_action_bugzilla_CPPFLAGS = \
+ -I$(srcdir)/../../inc \
+ -I$(srcdir)/../../lib/utils \
+ -DBIN_DIR=\"$(bindir)\" \
+ -DVAR_RUN=\"$(VAR_RUN)\" \
+ -DCONF_DIR=\"$(CONF_DIR)\" \
+ -DLOCALSTATEDIR='"$(localstatedir)"' \
+ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
+ -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \
+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
+ $(GLIB_CFLAGS) \
+ -D_GNU_SOURCE \
+ -Wall -Werror
+abrt_action_bugzilla_LDADD = \
+ ../../lib/utils/libABRTdUtils.la \
+ ../../lib/utils/libABRTUtils.la
+
dbusabrtconfdir = ${sysconfdir}/dbus-1/system.d/
dist_dbusabrtconf_DATA = dbus-abrt.conf
diff -x '*.po' -d -urpN abrt.0/src/daemon/MiddleWare.cpp
abrt.1/src/daemon/MiddleWare.cpp
--- abrt.0/src/daemon/MiddleWare.cpp 2010-09-23 16:48:44.000000000 +0200
+++ abrt.1/src/daemon/MiddleWare.cpp 2010-10-07 01:53:59.980405862 +0200
@@ -54,129 +54,6 @@ static vector_pair_string_string_t s_vec
static void RunAnalyzerActions(const char *pAnalyzer, const char* pPackageName, const
char *pDebugDumpDir, int force);
-static char* is_text_file(const char *name, ssize_t *sz)
-{
- /* We were using magic.h API to check for file being text, but it thinks
- * that file containing just "0" is not text (!!)
- * So, we do it ourself.
- */
-
- int fd = open(name, O_RDONLY);
- if (fd < 0)
- return NULL; /* it's not text (because it does not exist! :) */
-
- /* Maybe 64k limit is small. But _some_ limit is necessary:
- * fields declared "text" may end up in editing fields and such.
- * We don't want to accidentally end up with 100meg text in a textbox!
- * So, don't remove this. If you really need to, raise the limit.
- */
- off_t size = lseek(fd, 0, SEEK_END);
- if (size < 0 || size > 64*1024)
- {
- close(fd);
- return NULL; /* it's not a SMALL text */
- }
- lseek(fd, 0, SEEK_SET);
-
- char *buf = (char*)xmalloc(*sz);
- ssize_t r = *sz = full_read(fd, buf, *sz);
- close(fd);
- if (r < 0)
- {
- free(buf);
- return NULL; /* it's not text (because we can't read it) */
- }
-
- /* Some files in our dump directories are known to always be textual */
- const char *base = strrchr(name, '/');
- if (base)
- {
- base++;
- if (strcmp(base, FILENAME_BACKTRACE) == 0
- || strcmp(base, FILENAME_CMDLINE) == 0
- ) {
- return buf;
- }
- }
-
- /* Every once in a while, even a text file contains a few garbled
- * or unexpected non-ASCII chars. We should not declare it "binary".
- */
- const unsigned RATIO = 50;
- unsigned total_chars = r + RATIO;
- unsigned bad_chars = 1; /* 1 prevents division by 0 later */
- while (--r >= 0)
- {
- if (buf[r] >= 0x7f
- /* among control chars, only '\t','\n' etc are allowed */
- || (buf[r] < ' ' && !isspace(buf[r]))
- ) {
- if (buf[r] == '\0')
- {
- /* We don't like NULs very much. Not text for sure! */
- free(buf);
- return NULL;
- }
- bad_chars++;
- }
- }
-
- if ((total_chars / bad_chars) >= RATIO)
- return buf; /* looks like text to me */
-
- free(buf);
- return NULL; /* it's binary */
-}
-
-static void load_crash_data_from_debug_dump(struct dump_dir *dd, map_crash_data_t&
data)
-{
- char *short_name;
- char *full_name;
-
- dd_init_next_file(dd);
- while (dd_get_next_file(dd, &short_name, &full_name))
- {
- ssize_t sz = 4*1024;
- char *text = NULL;
- bool editable = is_editable_file(short_name);
-
- if (!editable)
- {
- text = is_text_file(full_name, &sz);
- if (!text)
- {
- add_to_crash_data_ext(data,
- short_name,
- CD_BIN,
- CD_ISNOTEDITABLE,
- full_name
- );
-
- free(short_name);
- free(full_name);
- continue;
- }
- }
-
- char *content;
- if (sz < 4*1024) /* is_text_file did read entire file */
- content = xstrndup(text, sz); //TODO: can avoid this copying if
is_text_file() adds NUL
- else /* no, need to read it all */
- content = dd_load_text(dd, short_name);
- free(text);
-
- add_to_crash_data_ext(data,
- short_name,
- CD_TXT,
- editable ? CD_ISEDITABLE : CD_ISNOTEDITABLE,
- content
- );
- free(short_name);
- free(full_name);
- free(content);
- }
-}
-
/**
* Transforms a debugdump directory to inner crash
* report form. This form is used for later reporting.