Optimisez votre comptabilité avec les logiciels Ciel 2011
by CIEL comptabilité
CIEL !
Et tout deviant facile
Suivez quotidiennement votre activit? !
Enregistrez rapidement vos ?critures
Anticipez vos besoins de tr?sorerie
Suivez pr?cis?ment votre activit?
Simplifiez vos t?ches administratives
Facilitez vos ?changes avec votre expert-comptable
Plus d'informations:
http://track.effiliation.com/servlet/effi.redir?id_compteur=11366933&url=...
********************************************************************
Conform?ment ? la loi informatique & libert?s du 6 janvier 1978, je
dispose d'un droit d'acc?s, de rectification et d'opposition aux
donn?es personnelles me concernant.
Ce message commercial vous est envoy? par "Team Leaders".. Vous
recevez ce message parce que vous vous ?tes inscrit sur l'un des sites
partenaires de "Team Leaders". Vos donn?es nominatives n'ont pas ?t?
transmises ? l'annonceur. Si vous ne souhaitez plus recevoir notre
lettre d'information Remplissez ce formulaire:
http://87.255.69.213/unsubscribe/index.php?q=crash-catcher@lists.fedoraho...
13 years, 7 months
[PATCH] move bugzilla reporting to a separate program (abrt-action-bugzilla)
by Denys Vlasenko
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.
13 years, 7 months
[PATCH] add --help option in parse_opts(), not at every call site
by Denys Vlasenko
This patch removes the need to add --help option to every program.
It is added (and handled) by parse_opts().
Run tested. Please review.
--
vda
diff -x '*.po' -d -urpN abrt.0/lib/utils/parse_options.c abrt.1/lib/utils/parse_options.c
--- abrt.0/lib/utils/parse_options.c 2010-10-05 17:23:41.000000000 +0200
+++ abrt.1/lib/utils/parse_options.c 2010-10-08 13:28:34.655230855 +0200
@@ -67,13 +67,18 @@ void parse_usage_and_die(const char * co
void parse_opts(int argc, char **argv, const struct options *opt,
const char * const usage[])
{
+ int help = 0;
int size = parse_opt_size(opt);
struct strbuf *shortopts = strbuf_new();
- struct option *longopts = xcalloc(sizeof(struct options), size);
+ struct option *longopts = xzalloc(sizeof(longopts[0]) * (size+1));
+ longopts[0].name = "help";
+ /*longopts[0].has_arg = 0; - xzalloc did it */
+ longopts[0].flag = &help;
+ longopts[0].val = 1;
int ii;
- for (ii = 0; ii < size - 1; ++ii)
+ for (ii = 1; ii < size; ++ii)
{
longopts[ii].name = opt[ii].long_name;
@@ -97,21 +102,21 @@ void parse_opts(int argc, char **argv, c
longopts[ii].flag = 0;
longopts[ii].val = opt[ii].short_name;
}
-
- longopts[ii].name = 0;
+ /* xzalloc did it already:
+ longopts[ii].name = NULL;
longopts[ii].has_arg = 0;
- longopts[ii].flag = 0;
+ longopts[ii].flag = NULL;
longopts[ii].val = 0;
+ */
- int option_index = 0;
while (1)
{
- int c = getopt_long(argc, argv, shortopts->buf, longopts, &option_index);
+ int c = getopt_long(argc, argv, shortopts->buf, longopts, /*longindex:*/ NULL);
if (c == -1)
break;
- if (c == '?')
+ if (c == '?' || (c == 0 && help))
{
free(longopts);
strbuf_free(shortopts);
diff -x '*.po' -d -urpN abrt.0/lib/utils/parse_options.h abrt.1/lib/utils/parse_options.h
--- abrt.0/lib/utils/parse_options.h 2010-10-05 17:23:41.000000000 +0200
+++ abrt.1/lib/utils/parse_options.h 2010-10-08 13:30:26.604480641 +0200
@@ -38,7 +38,6 @@ struct options {
#define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) }
#define OPT__VERBOSE(v) OPT_BOOL('v', "verbose", (v), "be verbose")
-#define OPT__HELP(v) OPT_BOOL('h', "help", (v), "show this help message")
void parse_opts(int argc, char **argv, const struct options *opt,
const char * const usage[]);
diff -x '*.po' -d -urpN abrt.0/lib/utils/xfuncs.c abrt.1/lib/utils/xfuncs.c
--- abrt.0/lib/utils/xfuncs.c 2010-10-05 17:23:41.000000000 +0200
+++ abrt.1/lib/utils/xfuncs.c 2010-10-08 13:31:24.878480382 +0200
@@ -39,6 +39,7 @@ int close_on_exec_on(int fd)
return fcntl(fd, F_SETFD, FD_CLOEXEC);
}
+#if 0 /* unused */
void *xcalloc(size_t nmemb, size_t size)
{
void *ptr = calloc(nmemb, size);
@@ -46,6 +47,7 @@ void *xcalloc(size_t nmemb, size_t size)
die_out_of_memory();
return ptr;
}
+#endif
// Die if we can't allocate size bytes of memory.
void* xmalloc(size_t size)
diff -x '*.po' -d -urpN abrt.0/src/daemon/abrt-action-generate-backtrace.c abrt.1/src/daemon/abrt-action-generate-backtrace.c
--- abrt.0/src/daemon/abrt-action-generate-backtrace.c 2010-10-08 13:06:56.000000000 +0200
+++ abrt.1/src/daemon/abrt-action-generate-backtrace.c 2010-10-08 13:30:50.959230457 +0200
@@ -23,11 +23,6 @@
#define DEBUGINFO_CACHE_DIR LOCALSTATEDIR"/cache/abrt-di"
-static const char * const abrt_action_generage_backtrace_usage[] = {
- "abrt-action-generate-backtrace [options] -d DIR",
- NULL
-};
-
static const char *dump_dir_name = ".";
static char *debuginfo_dirs;
static int exec_timeout_sec = 60;
@@ -246,10 +241,14 @@ static char *get_backtrace(struct dump_d
}
static char *d_opt, *i_opt;
-static int s_opt, help_opt;
+static int s_opt;
+
+static const char * const abrt_action_generage_backtrace_usage[] = {
+ "abrt-action-generate-backtrace [options] -d DIR",
+ NULL
+};
static struct options abrt_action_generate_backtrace_options[] = {
- OPT__HELP(&help_opt),
OPT__VERBOSE(&g_verbose),
OPT_GROUP(""),
OPT_STRING( 'd' , 0, &d_opt, "dir", "Crash dump directory"),
@@ -268,7 +267,7 @@ int main(int argc, char **argv)
parse_opts(argc, argv, abrt_action_generate_backtrace_options,
abrt_action_generage_backtrace_usage);
- if (help_opt || !d_opt)
+ if (!d_opt)
parse_usage_and_die(abrt_action_generage_backtrace_usage,
abrt_action_generate_backtrace_options);
diff -x '*.po' -d -urpN abrt.0/src/daemon/abrt-action-save-package-data.cpp abrt.1/src/daemon/abrt-action-save-package-data.cpp
--- abrt.0/src/daemon/abrt-action-save-package-data.cpp 2010-10-08 13:06:56.000000000 +0200
+++ abrt.1/src/daemon/abrt-action-save-package-data.cpp 2010-10-08 13:30:34.221480398 +0200
@@ -23,11 +23,6 @@
#include "rpm.h"
#include "parse_options.h"
-static const char * const abrt_action_save_package_data_usage[] = {
- "abrt-action-save-package-data [options] -d DIR",
- NULL
-};
-
/**
* Returns the first full path argument in the command line or NULL.
* Skips options are in form "-XXX".
@@ -273,10 +268,14 @@ static int SavePackageDescriptionToDebug
}
static char *d_opt;
-static int s_opt, help_opt;
+static int s_opt;
+
+static const char * const abrt_action_save_package_data_usage[] = {
+ "abrt-action-save-package-data [options] -d DIR",
+ NULL
+};
static struct options abrt_action_save_package_data_options[] = {
- OPT__HELP(&help_opt),
OPT__VERBOSE(&g_verbose),
OPT_GROUP(""),
OPT_STRING( 'd' , 0, &d_opt, "dir", "Crash dump directory"),
@@ -295,7 +294,7 @@ int main(int argc, char **argv)
parse_opts(argc, argv, abrt_action_save_package_data_options,
abrt_action_save_package_data_usage);
- if (help_opt || !d_opt)
+ if (!d_opt)
parse_usage_and_die(abrt_action_save_package_data_usage,
abrt_action_save_package_data_options);
diff -x '*.po' -d -urpN abrt.0/src/daemon/abrt-server.c abrt.1/src/daemon/abrt-server.c
--- abrt.0/src/daemon/abrt-server.c 2010-10-08 13:06:56.000000000 +0200
+++ abrt.1/src/daemon/abrt-server.c 2010-10-08 13:30:20.911480328 +0200
@@ -76,11 +76,6 @@ Finalizing dump creation:
\0
*/
-static const char * const abrt_server_usage[] = {
- "abrt-server [options]",
- NULL
-};
-
/* Buffer for incomplete incoming messages. */
static char *messagebuf_data = NULL;
static unsigned messagebuf_len = 0;
@@ -282,10 +277,14 @@ static void process_message(const char *
static void dummy_handler(int sig_unused) {}
-static int s_opt, help_opt;
+static int s_opt;
+
+static const char * const abrt_server_usage[] = {
+ "abrt-server [options]",
+ NULL
+};
static struct options abrt_server_options[] = {
- OPT__HELP(&help_opt),
OPT__VERBOSE(&g_verbose),
OPT_GROUP(""),
OPT_INTEGER( 'u' , 0, &client_uid, "Use UID as client uid"),
@@ -299,14 +298,9 @@ int main(int argc, char **argv)
if (env_verbose)
g_verbose = atoi(env_verbose);
-
parse_opts(argc, argv, abrt_server_options,
abrt_server_usage);
- if (help_opt)
- parse_usage_and_die(abrt_server_usage,
- abrt_server_options);
-
putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
msg_prefix = xasprintf("abrt-server[%u]", getpid());
if (s_opt)
diff -x '*.po' -d -urpN abrt.0/src/daemon/Daemon.cpp abrt.1/src/daemon/Daemon.cpp
--- abrt.0/src/daemon/Daemon.cpp 2010-10-08 13:06:56.000000000 +0200
+++ abrt.1/src/daemon/Daemon.cpp 2010-10-08 13:30:08.044480622 +0200
@@ -85,11 +85,6 @@ using namespace std;
* Both are sent as unicast to last client set by set_client_name(name).
* If set_client_name(NULL) was done, they are not sent.
*/
-static const char * const abrtd_usage[] = {
- _("abrtd [options]"),
- NULL
-};
-
CCommLayerServer* g_pCommLayer;
static bool daemonize = true;
@@ -865,11 +860,15 @@ static void sanitize_dump_dir_rights()
ensure_writable_dir(VAR_RUN"/abrt", 0755, "root");
}
-static int daemonize_opt, syslog_opt, help_opt;
+static int daemonize_opt, syslog_opt;
static char *timeout_opt;
+static const char * const abrtd_usage[] = {
+ _("abrtd [options]"),
+ NULL
+};
+
static struct options abrtd_options[] = {
- OPT__HELP(&help_opt),
OPT__VERBOSE(&g_verbose),
OPT_GROUP(""),
OPT_BOOL( 'd' , 0, &daemonize_opt, _("Do not daemonize")),
@@ -898,9 +897,6 @@ int main(int argc, char** argv)
parse_opts(argc, argv, abrtd_options, abrtd_usage);
- if (help_opt)
- parse_usage_and_die(abrtd_usage, abrtd_options);
-
if (daemonize_opt)
daemonize = false;
13 years, 7 months