From 611ca48b8d81e5edc0907885ece48cb0dce986fa Mon Sep 17 00:00:00 2001
From: Matej Habrnal <mhabrnal@redhat.com>
Date: Tue, 13 Jan 2015 19:30:27 -0500
Subject: [PATCH] reporter-mantisbt: first version of the reporter-mantisbt
Related to #272
Signed-off-by: Matej Habrnal <mhabrnal@redhat.com>
---
configure.ac | 33 +
po/POTFILES.in | 11 +
src/plugins/Makefile.am | 43 +-
src/plugins/centos_report_event.conf | 37 +
src/plugins/mantisbt.c | 1111 ++++++++++++++++++++++++
src/plugins/mantisbt.conf | 8 +
src/plugins/mantisbt.h | 140 +++
src/plugins/mantisbt_format.conf | 59 ++
src/plugins/mantisbt_formatdup.conf | 65 ++
src/plugins/report_CentOSBugTracker.conf | 4 +
src/plugins/report_CentOSBugTracker.xml.in | 65 ++
src/plugins/reporter-mantisbt.c | 696 +++++++++++++++
src/workflows/Makefile.am | 15 +-
src/workflows/report_centos.conf | 31 +
src/workflows/workflow_CentOSCCpp.xml.in | 12 +
src/workflows/workflow_CentOSJava.xml.in | 11 +
src/workflows/workflow_CentOSKerneloops.xml.in | 11 +
src/workflows/workflow_CentOSLibreport.xml.in | 9 +
src/workflows/workflow_CentOSPython.xml.in | 11 +
src/workflows/workflow_CentOSPython3.xml.in | 11 +
src/workflows/workflow_CentOSVmcore.xml.in | 12 +
src/workflows/workflow_CentOSXorg.xml.in | 9 +
22 files changed, 2402 insertions(+), 2 deletions(-)
create mode 100644 src/plugins/centos_report_event.conf
create mode 100644 src/plugins/mantisbt.c
create mode 100644 src/plugins/mantisbt.conf
create mode 100644 src/plugins/mantisbt.h
create mode 100644 src/plugins/mantisbt_format.conf
create mode 100644 src/plugins/mantisbt_formatdup.conf
create mode 100644 src/plugins/report_CentOSBugTracker.conf
create mode 100644 src/plugins/report_CentOSBugTracker.xml.in
create mode 100644 src/plugins/reporter-mantisbt.c
create mode 100644 src/workflows/report_centos.conf
create mode 100644 src/workflows/workflow_CentOSCCpp.xml.in
create mode 100644 src/workflows/workflow_CentOSJava.xml.in
create mode 100644 src/workflows/workflow_CentOSKerneloops.xml.in
create mode 100644 src/workflows/workflow_CentOSLibreport.xml.in
create mode 100644 src/workflows/workflow_CentOSPython.xml.in
create mode 100644 src/workflows/workflow_CentOSPython3.xml.in
create mode 100644 src/workflows/workflow_CentOSVmcore.xml.in
create mode 100644 src/workflows/workflow_CentOSXorg.xml.in
diff --git a/configure.ac b/configure.ac
index 8aea410..66508c0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -131,6 +131,39 @@ do
done
fi dnl end NO_BUGZILLA
+AC_ARG_WITH(mantisbt,
+AS_HELP_STRING([--with-mantisbt],[use MantisBT plugin (default is YES)]),
+LIBREPORT_PARSE_WITH([mantisbt]))
+
+if test -z "$NO_MANTISBT"; then
+AM_CONDITIONAL(BUILD_MANTISBT, true)
+
+# enable mantisbt & deps translations
+for FILE in `grep -e "#.*antisbt.*" -e "#.*naconda.*" po/POTFILES.in`
+do
+ sed -ie "s,$FILE,${FILE:1}," po/POTFILES.in
+ sed -ie "\,^${FILE:1}$,d" po/POTFILES.skip
+done
+else
+AM_CONDITIONAL(BUILD_MANTISBT, false)
+
+# disablie mantisbt & deps translations
+for FILE in `grep -e "antisbt" -e "naconda" po/POTFILES.in`
+do
+ if test "${FILE:0:1}" = "#"
+ then
+ continue
+ fi
+
+ sed -ie "s,$FILE,#$FILE," po/POTFILES.in
+ grep "$FILE" po/POTFILES.skip > /dev/null 2>&1
+ if test $?
+ then
+ echo "$FILE" >> po/POTFILES.skip
+ fi
+done
+fi dnl end NO_MANTISBT
+
AC_PATH_PROG([PYTHON_CONFIG], [python-config], [no])
[if test "$PYTHON_CONFIG" = "no"]
[then]
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 5588540..3415b03 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -36,6 +36,8 @@ src/plugins/reporter-print.c
src/plugins/reporter-rhtsupport.c
src/plugins/reporter-rhtsupport-parse.c
src/plugins/reporter-upload.c
+src/plugins/reporter-mantisbt.c
+src/plugins/report_CentOSBugTracker.xml.in
src/plugins/report_Kerneloops.xml.in
src/plugins/report_Logger.xml.in
src/plugins/report_Mailx.xml.in
@@ -44,12 +46,21 @@ src/plugins/report_Uploader.xml.in
src/plugins/report_uReport.xml.in
src/plugins/report_EmergencyAnalysis.xml.in
src/plugins/rhbz.c
+src/plugins/mantisbt.c
src/plugins/reporter-ureport.c
src/report-newt/report-newt.c
src/workflows/workflow_AnacondaFedora.xml.in
src/workflows/workflow_AnacondaRHEL.xml.in
src/workflows/workflow_AnacondaRHELBugzilla.xml.in
src/workflows/workflow_AnacondaUpload.xml.in
+src/workflows/workflow_CentOSCCpp.xml.in
+src/workflows/workflow_CentOSJava.xml.in
+src/workflows/workflow_CentOSKerneloops.xml.in
+src/workflows/workflow_CentOSLibreport.xml.in
+src/workflows/workflow_CentOSPython.xml.in
+src/workflows/workflow_CentOSPython3.xml.in
+src/workflows/workflow_CentOSVmcore.xml.in
+src/workflows/workflow_CentOSXorg.xml.in
src/workflows/workflow_FedoraCCpp.xml.in
src/workflows/workflow_FedoraKerneloops.xml.in
src/workflows/workflow_FedoraPython.xml.in
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index 7ec08d7..d70b4db 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -7,6 +7,11 @@ reporters_bin += \
report
endif
+if BUILD_MANTISBT
+reporters_bin += \
+ reporter-mantisbt
+endif
+
if BUILD_UREPORT
reporters_bin += reporter-ureport
endif
@@ -34,6 +39,12 @@ reporters_plugin_format_conf += bugzilla_format.conf \
bugzilla_formatdup_anaconda.conf
endif
+if BUILD_MANTISBT
+reporters_plugin_conf += mantisbt.conf
+reporters_plugin_format_conf += mantisbt_format.conf \
+ mantisbt_formatdup.conf
+endif
+
defaultreportpluginsconfdir = $(DEFAULT_REPORT_PLUGINS_CONF_DIR)
dist_defaultreportpluginsconf_DATA = $(reporters_plugin_conf) \
rhtsupport.conf \
@@ -53,6 +64,12 @@ reporters_events += report_Bugzilla.xml
reporters_events_conf += report_Bugzilla.conf
endif
+if BUILD_MANTISBT
+reporters_events += report_CentOSBugTracker.xml
+
+reporters_events_conf += report_CentOSBugTracker.conf
+endif
+
if BUILD_UREPORT
reporters_events += report_uReport.xml
endif
@@ -77,7 +94,8 @@ dist_eventsdef_DATA = \
print_event.conf \
rhtsupport_event.conf \
uploader_event.conf \
- emergencyanalysis_event.conf
+ emergencyanalysis_event.conf \
+ centos_report_event.conf
reporters_extra_dist =
if BUILD_BUGZILLA
@@ -125,6 +143,29 @@ reporter_bugzilla_LDADD = \
../lib/libreport.la
endif
+if BUILD_MANTISBT
+reporter_mantisbt_SOURCES = \
+ reporter-mantisbt.c mantisbt.c mantisbt.h
+reporter_mantisbt_CPPFLAGS = \
+ -I$(srcdir)/../include \
+ -I$(srcdir)/../lib \
+ -DBIN_DIR=\"$(bindir)\" \
+ -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=\"$(REPORT_PLUGINS_CONF_DIR)\" \
+ $(GLIB_CFLAGS) \
+ $(LIBREPORT_CFLAGS) \
+ $(LIBXML_CFLAGS) \
+ -D_GNU_SOURCE
+reporter_mantisbt_LDADD = \
+ $(GLIB_LIBS) \
+ ../lib/libreport-web.la \
+ ../lib/libreport.la
+endif
+
reporter_rhtsupport_SOURCES = \
abrt_rh_support.h abrt_rh_support.c \
reporter-rhtsupport.h \
diff --git a/src/plugins/centos_report_event.conf b/src/plugins/centos_report_event.conf
new file mode 100644
index 0000000..53f12d8
--- /dev/null
+++ b/src/plugins/centos_report_event.conf
@@ -0,0 +1,37 @@
+EVENT=report_CentOSBugTracker analyzer=xorg
+ reporter-mantisbt
+
+EVENT=report_CentOSBugTracker analyzer=Kerneloops
+ reporter-mantisbt
+
+EVENT=report_CentOSBugTracker analyzer=vmcore
+ reporter-mantisbt
+
+EVENT=report_CentOSBugTracker analyzer=Python component!=anaconda
+ test -f component || abrt-action-save-package-data
+ reporter-mantisbt \
+ -c /etc/libreport/plugins/mantisbt.conf \
+ -F /etc/libreport/plugins/mantisbt_format.conf \
+ -A /etc/libreport/plugins/mantisbt_formatdup.conf
+
+EVENT=report_CentOSBugTracker analyzer=Python3 component!=anaconda
+ test -f component || abrt-action-save-package-data
+ reporter-mantisbt \
+ -c /etc/libreport/plugins/mantisbt.conf \
+ -F /etc/libreport/plugins/mantisbt_format.conf \
+ -A /etc/libreport/plugins/mantisbt_formatdup.conf
+
+EVENT=report_CentOSBugTracker analyzer=CCpp duphash!=
+ test -f component || abrt-action-save-package-data
+ component="`cat component`"
+ format="mantisbt_format.conf"
+ test -f "/etc/libreport/plugins/mantisbt_format_$component.conf" \
+ && format="mantisbt_format_$component.conf"
+ formatdup="mantisbt_formatdup.conf"
+ test -f "/etc/libreport/plugins/mantisbt_formatdup_$component.conf" \
+ && formatdup="mantisbt_formatdup_$component.conf"
+ reporter-mantisbt \
+ -c /etc/libreport/plugins/mantisbt.conf \
+ -F "/etc/libreport/plugins/$format" \
+ -A "/etc/libreport/plugins/$formatdup"
+
diff --git a/src/plugins/mantisbt.c b/src/plugins/mantisbt.c
new file mode 100644
index 0000000..1c496b4
--- /dev/null
+++ b/src/plugins/mantisbt.c
@@ -0,0 +1,1111 @@
+/*
+ Copyright (C) 2014 ABRT team
+ Copyright (C) 2014 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 <curl/curl.h>
+
+#include <libxml/xmlreader.h>
+
+#include "internal_libreport.h"
+#include "libreport_curl.h"
+#include "mantisbt.h"
+#include "client.h"
+
+/*
+ * SOAP
+*/
+
+#define XML_VERSION "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+
+/* fprint string */
+#define SOAP_TEMPLATE \
+ "<SOAP-ENV:Envelope xmlns:ns3=\"http://schemas.xmlsoap.org/soap/encoding/\" " \
+ "xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\" " \
+ "xmlns:ns0=\"http://schemas.xmlsoap.org/soap/encoding/\" " \
+ "xmlns:ns1=\"http://schemas.xmlsoap.org/soap/envelope/\" " \
+ "xmlns:ns2=\"http://www.w3.org/2001/XMLSchema\" " \
+ "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
+ "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" " \
+ "SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" \
+ "<SOAP-ENV:Header/>" \
+ "<ns1:Body>" \
+ "<ns3:%s>" \
+ "</ns3:%s>" \
+ "</ns1:Body>" \
+ "</SOAP-ENV:Envelope>"
+
+#define MAX_SUMMARY_LENGTH 128
+#define CUSTOMFIELD_DUPHASH "abrt_hash"
+#define CUSTOMFIELD_URL "URL"
+#define MAX_HOPS 5
+
+/* MantisBT limit is 2MB by default
+ */
+#define MANTISBT_MAX_FILE_UPLOAD_SIZE (2 * 1024 * 1024)
+
+typedef struct mantisbt_custom_fields
+{
+ char *cf_abrt_hash_id;
+ char *cf_url_id;
+} mantisbt_custom_fields_t;
+
+/*
+ * MantisBT settings issue info
+ */
+void
+mantisbt_settings_free(mantisbt_settings_t *s)
+{
+ if (s == NULL)
+ return;
+
+ free(s->m_login);
+ free(s->m_password);
+ free(s->m_project);
+ free(s->m_project_id);
+ free(s->m_project_version);
+}
+
+/*
+ * MantisBT issue info
+ */
+mantisbt_issue_info_t *
+mantisbt_issue_info_new()
+{
+ mantisbt_issue_info_t *info = xzalloc(sizeof(mantisbt_issue_info_t));
+ info->mii_id = -1;
+ info->mii_dup_id = -1;
+
+ return info;
+}
+
+void
+mantisbt_issue_info_free(mantisbt_issue_info_t *info)
+{
+ if (info == NULL)
+ return;
+
+ free(info->mii_status);
+ free(info->mii_resolution);
+ free(info->mii_reporter);
+ free(info->mii_project);
+
+ list_free_with_free(info->mii_notes);
+ list_free_with_free(info->mii_attachments);
+
+ free(info);
+}
+
+mantisbt_issue_info_t *
+mantisbt_find_origin_bug_closed_duplicate(mantisbt_settings_t *settings, mantisbt_issue_info_t *info)
+{
+ mantisbt_issue_info_t *info_tmp = mantisbt_issue_info_new();
+ info_tmp->mii_id = info->mii_id;
+ info_tmp->mii_dup_id = info->mii_dup_id;
+
+ for (int ii = 0; ii <= MAX_HOPS; ii++)
+ {
+ if (ii == MAX_HOPS)
+ error_msg_and_die(_("MantisBT couldn't find parent of issue %d"), info->mii_id);
+
+ log("Issue %d is a duplicate, using parent issue %d", info_tmp->mii_id, info_tmp->mii_dup_id);
+ int issue_id = info_tmp->mii_dup_id;
+
+ mantisbt_issue_info_free(info_tmp);
+ info_tmp = mantisbt_get_issue_info(settings, issue_id);
+
+ // found a issue which is not CLOSED as DUPLICATE
+ if (info_tmp->mii_dup_id == -1)
+ break;
+ }
+
+ return info_tmp;
+}
+
+/*
+ * SOAP request
+ */
+static soap_request_t *
+soap_request_new()
+{
+ soap_request_t *req = xzalloc(sizeof(*req));
+
+ return req;
+}
+
+void
+soap_request_free(soap_request_t *req)
+{
+ if (req == NULL)
+ return;
+
+ if (req->sr_root != NULL)
+ xmlFreeDoc(req->sr_root->doc);
+
+ free(req);
+
+ return;
+}
+
+static xmlNodePtr
+soap_node_get_next_element_node(xmlNodePtr node)
+{
+ for (; node != NULL; node = node->next)
+ if (node->type == XML_ELEMENT_NODE)
+ break;
+
+ return node;
+}
+
+static xmlNodePtr
+soap_node_get_child_element(xmlNodePtr node)
+{
+ if (node == NULL)
+ error_msg_and_die(_("SOAP: Failed to get child element because of no parent."));
+
+ return soap_node_get_next_element_node(node->xmlChildrenNode);
+}
+
+static xmlNodePtr
+soap_node_get_next_sibling(xmlNodePtr node)
+{
+ if (node == NULL)
+ error_msg_and_die(_("SOAP: Failed to get next element because of no node."));
+
+ return soap_node_get_next_element_node(node->next);
+}
+
+static xmlNodePtr
+soap_node_get_child_node(xmlNodePtr parent, const char *name)
+{
+ if (parent == NULL)
+ error_msg_and_die(_("SOAP: Failed to get child node because of no parent."));
+
+ xmlNodePtr node;
+ for (node = soap_node_get_child_element(parent); node != NULL; node = soap_node_get_next_sibling(node))
+ {
+ if (xmlStrcmp(node->name, BAD_CAST name) == 0)
+ return node;
+ }
+
+ return NULL;
+}
+
+soap_request_t *
+soap_request_new_for_method(const char *method)
+{
+ char *xml_str = xasprintf(SOAP_TEMPLATE, method, method);
+
+ xmlDocPtr doc = xmlParseDoc(BAD_CAST xml_str);
+ free(xml_str);
+
+ if (doc == NULL)
+ error_msg_and_die(_("SOAP: Failed to parse xml during creating request."));
+
+ soap_request_t *req = soap_request_new();
+
+ req->sr_root = xmlDocGetRootElement(doc);
+ if (req->sr_root == NULL)
+ {
+ soap_request_free(req);
+ error_msg_and_die(_("SOAP: Failed to get xml root element."));
+ }
+
+ req->sr_body = soap_node_get_child_node(req->sr_root, "Body");
+ req->sr_method = soap_node_get_child_node(req->sr_body, method);
+
+ return req;
+}
+
+static xmlNodePtr
+soap_node_add_child_node(xmlNodePtr node, const char *name, const char *type, const char *value)
+{
+ if (node == NULL || name == NULL)
+ error_msg_and_die(_("SOAP: Failed to add a new child node because of no node or no child name."));
+
+ xmlNodePtr new_node = xmlNewTextChild(node, /* namespace */ NULL, BAD_CAST name, BAD_CAST value);
+
+ if (new_node == NULL)
+ error_msg_and_die(_("SOAP: Failed to create a new xml child item."));
+
+ if (type != NULL)
+ {
+ if (xmlNewProp(new_node, BAD_CAST "xsi:type", BAD_CAST type) == NULL)
+ error_msg_and_die(_("SOAP: Failed to create a new property."));
+ }
+
+ return new_node;
+}
+
+void
+soap_request_add_method_parameter(soap_request_t *req, const char *name, const char *type, const char *value)
+{
+ if (req == NULL || req->sr_method == NULL)
+ error_msg_and_die(_("SOAP: Failed to add method parametr."));
+
+ soap_node_add_child_node(req->sr_method, name, type, value);
+ return;
+}
+
+void
+soap_request_add_credentials_parameter(soap_request_t *req, const mantisbt_settings_t *settings)
+{
+ soap_request_add_method_parameter(req, "username", SOAP_STRING, settings->m_login);
+ soap_request_add_method_parameter(req, "password", SOAP_STRING, settings->m_password);
+
+ return;
+}
+
+static void
+soap_add_new_issue_parameters(soap_request_t *req,
+ const char *project,
+ const char *version,
+ const char *category,
+ const char *summary,
+ const char *description,
+ const char *additional_information,
+ bool private,
+ mantisbt_custom_fields_t *fields,
+ const char *duphash,
+ const char *tracker_url)
+{
+ if (req == NULL || req->sr_method == NULL)
+ error_msg_and_die(_("SOAP: Failed to add new issue parametrs."));
+
+ if (project == NULL || category == NULL || summary == NULL || description == NULL)
+ error_msg_and_die(_("SOAP: Failed to add new issue parameters because the required items are missing."));
+
+ xmlNodePtr issue_node = soap_node_add_child_node(req->sr_method, "issue", SOAP_ISSUEDATA, /* content */ NULL);
+
+ // project
+ xmlNodePtr project_node = soap_node_add_child_node(issue_node, "project", SOAP_OBJECTREF, /* content */ NULL);
+ soap_node_add_child_node(project_node, "name", SOAP_STRING, project);
+
+ // view status
+ xmlNodePtr view_node = soap_node_add_child_node(issue_node, "view_state", SOAP_OBJECTREF, /* content */ NULL);
+ soap_node_add_child_node(view_node, "name", SOAP_STRING, (private) ? "private" : "public");
+
+ /* if any custom field exists */
+ int custom_fields_count = 0;
+ xmlNodePtr duphash_node;
+ if (fields->cf_abrt_hash_id != NULL || fields->cf_url_id != NULL)
+ duphash_node = soap_node_add_child_node(issue_node, "custom_fields", SOAP_CUSTOMFIELD_ARRAY, /* content */ NULL);
+
+ // custom fields (duphash and URL to tracker)
+ xmlNodePtr item_node, field_node;
+ if (fields->cf_abrt_hash_id != NULL)
+ {
+ item_node = soap_node_add_child_node(duphash_node, "item", SOAP_CUSTOMFIELD, /* content */ NULL);
+ field_node = soap_node_add_child_node(item_node, "field", SOAP_OBJECTREF, /* content */ NULL);
+ soap_node_add_child_node(field_node, "id", SOAP_INTEGER, /* custom_field id */ fields->cf_abrt_hash_id);
+ soap_node_add_child_node(item_node, "value", SOAP_STRING, duphash);
+ ++custom_fields_count;
+ }
+
+ // if tracker url exists, attach it to the issue
+ if (tracker_url != NULL && fields->cf_url_id != NULL)
+ {
+ item_node = soap_node_add_child_node(duphash_node, "item", SOAP_CUSTOMFIELD, /* content */ NULL);
+ field_node = soap_node_add_child_node(item_node, "field", SOAP_OBJECTREF, /* content */ NULL);
+ soap_node_add_child_node(field_node, "id", SOAP_INTEGER, /* custom_field */ fields->cf_url_id);
+ soap_node_add_child_node(item_node, "value", SOAP_STRING, tracker_url);
+ ++custom_fields_count;
+ }
+
+ if (custom_fields_count > 0)
+ {
+ char *type = xasprintf("%s[%i]", SOAP_CUSTOMFIELD, custom_fields_count);
+
+ if (xmlNewProp(duphash_node, BAD_CAST "ns3:arrayType", BAD_CAST type) == NULL)
+ error_msg_and_die(_("SOAP: Failed to create a new property in custom fields."));
+
+ free(type);
+ }
+
+ soap_node_add_child_node(issue_node, "os_build", SOAP_STRING, version);
+ soap_node_add_child_node(issue_node, "category", SOAP_STRING, category);
+ soap_node_add_child_node(issue_node, "summary", SOAP_STRING, summary);
+ soap_node_add_child_node(issue_node, "description", SOAP_STRING, description);
+ soap_node_add_child_node(issue_node, "additional_information", SOAP_STRING, additional_information);
+
+ return;
+}
+
+char *
+soap_request_to_str(const soap_request_t *req)
+{
+ if (req == NULL || req->sr_root == NULL || req->sr_root->doc == NULL)
+ error_msg_and_die(_("SOAP: Failed to create SOAP string because of invalid function arguments."));
+
+ xmlBufferPtr buffer = xmlBufferCreate();
+ int err = xmlNodeDump(buffer, req->sr_root->doc, req->sr_root, 1, /* formatting */ 0);
+ if (err == -1)
+ {
+ xmlBufferFree(buffer);
+ error_msg_and_die(_("SOAP: Failed to dump xml node."));
+ }
+
+ char *ret = xasprintf("%s%s", XML_VERSION, (const char *) xmlBufferContent(buffer));
+ xmlBufferFree(buffer);
+
+ return ret;
+}
+
+#if 0
+void
+soap_request_print(soap_request_t *req)
+{
+ if (req == NULL || req->sr_root == NULL || req->sr_root->doc == NULL)
+ error_msg_and_die(_("SOAP: Failed to print SOAP string."));
+
+ xmlBufferPtr buffer = xmlBufferCreate();
+ int err = xmlNodeDump(buffer, req->sr_root->doc, req->sr_root, 1, /* formatting */ 0);
+ if (err == -1)
+ {
+ xmlBufferFree(buffer);
+ error_msg_and_die(_("Failed to dump xml node."));
+ }
+
+ puts((const char *) xmlBufferContent(buffer));
+
+ xmlBufferFree(buffer);
+ return;
+}
+#endif
+
+static bool
+reader_move_reader_if_node_type_is_element_with_name_and_verify_its_value(xmlTextReaderPtr reader, const char *name)
+{
+ /* is not element node */
+ if (xmlTextReaderNodeType(reader) != XML_ELEMENT_NODE)
+ return false;
+
+ /* is not required name */
+ if (xmlStrcmp(xmlTextReaderConstName(reader), BAD_CAST name) != 0)
+ return false;
+
+ /* read next node */
+ if (xmlTextReaderRead(reader) != 1)
+ return false;
+
+ /* no value node */
+ if (xmlTextReaderHasValue(reader) == 0)
+ return false;
+
+ /* no text node */
+ if (xmlTextReaderNodeType(reader) != XML_TEXT_NODE)
+ return false;
+
+ return true;
+}
+
+static void
+reader_find_element_by_name(xmlTextReaderPtr reader, const char *name)
+{
+ while (xmlTextReaderRead(reader) == 1)
+ {
+ /* is not element node */
+ if (xmlTextReaderNodeType(reader) != XML_ELEMENT_NODE)
+ continue;
+
+ /* is not required name */
+ if (xmlStrcmp(xmlTextReaderConstName(reader), BAD_CAST name) != 0)
+ continue;
+
+ break;
+ }
+
+ return;
+}
+
+/* It is not possible to search only by name because the response contains
+ * different node with the same name. (e.g. id - user id, project id, issue id etc.)
+ * We are interested in only about issues id which is located at a different depth than others.
+ * ...
+ * <item xsi:type="ns1:IssueData">
+ * <id xsi:type="xsd:integer">10</id> <-- This is issue ID (required)
+ * <view_state xsi:type="ns1:ObjectRef">
+ * <id xsi:type="xsd:integer">10</id> <-- This is view_state ID (not required)
+ * <name xsi:type="xsd:string">public</name>
+ * </view_state>
+ * <project xsi:type="ns1:ObjectRef">
+ * <id xsi:type="xsd:integer">1</id> <-- This is project ID (not required)
+ * <name xsi:type="xsd:string">test</name>
+ * </project>
+ * ...
+ */
+static GList *
+response_values_at_depth_by_name(const char *xml, const char *name, int depth)
+{
+ xmlDocPtr doc = xmlParseDoc(BAD_CAST xml);
+ if (doc == NULL)
+ error_msg_and_die(_("SOAP: Failed to parse xml (searching value at depth by name)."));
+
+ xmlTextReaderPtr reader = xmlReaderWalker(doc);
+ if (reader == NULL)
+ error_msg_and_die(_("SOAP: Failed to create xml text reader."));
+
+ GList *result = NULL;
+
+ const xmlChar *value;
+ while (xmlTextReaderRead(reader) == 1)
+ {
+ /* is not right depth */
+ if (depth != -1 && xmlTextReaderDepth(reader) != depth)
+ continue;
+
+ if (reader_move_reader_if_node_type_is_element_with_name_and_verify_its_value(reader, name) == false)
+ continue;
+
+ if ((value = xmlTextReaderConstValue(reader)) != NULL)
+ result = g_list_append(result, xstrdup((const char *) value));
+ }
+ xmlFreeTextReader(reader);
+
+ return result;
+}
+
+/*
+ * Finds an element named 'elem' and returns a text of a child named 'name'
+ *
+ * Example:
+ * For
+ * <elem>
+ * <id>1</id>
+ * <name>foo</name>
+ * </elem>
+ *
+ * returns "foo"
+ */
+static char *
+response_get_name_value_of_element(const char *xml, const char *element)
+{
+ xmlDocPtr doc = xmlParseDoc(BAD_CAST xml);
+ if (doc == NULL)
+ error_msg_and_die(_("SOAP: Failed to parse xml."));
+
+ xmlTextReaderPtr reader = xmlReaderWalker(doc);
+ if (reader == NULL)
+ error_msg_and_die(_("SOAP: Failed to create xml text reader."));
+
+ const xmlChar *value = NULL;
+
+ reader_find_element_by_name(reader, element);
+
+ /* find 'name' element and return its text */
+ while (xmlTextReaderRead(reader) == 1)
+ {
+ if (reader_move_reader_if_node_type_is_element_with_name_and_verify_its_value(reader, "name") == false)
+ continue;
+
+ if ((value = xmlTextReaderConstValue(reader)) != NULL)
+ break;
+ }
+ xmlFreeTextReader(reader);
+
+ return (char *) value;
+}
+
+static int
+response_get_id_of_relatedto_issue(const char *xml)
+{
+ xmlDocPtr doc = xmlParseDoc(BAD_CAST xml);
+ if (doc == NULL)
+ error_msg_and_die(_("SOAP: Failed to parse xml (get related to issue)."));
+
+ xmlTextReaderPtr reader = xmlReaderWalker(doc);
+ if (reader == NULL)
+ error_msg_and_die(_("SOAP: Failed to create xml text reader."));
+
+ const xmlChar *value = NULL;
+ const xmlChar *id = NULL;
+
+ /* find relationships section */
+ reader_find_element_by_name(reader, "relationships");
+
+ /* find "name" value of 'name' element */
+ while (xmlTextReaderRead(reader) == 1)
+ {
+ /* find type of relattionship */
+ if (reader_move_reader_if_node_type_is_element_with_name_and_verify_its_value(reader, "name") == false)
+ continue;
+
+ if ((value = xmlTextReaderConstValue(reader)) == NULL)
+ continue;
+
+ /* we need 'duplicate of' realtionship type */
+ if (xmlStrcmp(value, BAD_CAST "duplicate of") != 0)
+ continue;
+
+ /* find id of duplicate issues */
+ reader_find_element_by_name(reader, "target_id");
+
+ /* verify target_id node */
+ if (reader_move_reader_if_node_type_is_element_with_name_and_verify_its_value(reader, "target_id") == false)
+ continue;
+
+ /* get its value */
+ if ((id = xmlTextReaderConstValue(reader)) != NULL)
+ break;
+ }
+ xmlFreeTextReader(reader);
+
+ return (id == NULL) ? -1 : atoi((const char *) id);
+}
+
+GList *
+response_get_main_ids_list(const char *xml)
+{
+ return response_values_at_depth_by_name(xml, "id", 5);
+}
+
+int
+response_get_main_id(const char *xml)
+{
+ GList *l = response_values_at_depth_by_name(xml, "id", 5);
+ return (l != NULL) ? atoi(l->data) : -1;
+}
+
+static int
+response_get_return_value(const char *xml)
+{
+ GList *l = response_values_at_depth_by_name(xml, "return", 3);
+ return (l != NULL) ? atoi(l->data) : -1;
+}
+
+static char*
+response_get_return_value_as_string(const char *xml)
+{
+ GList *l = response_values_at_depth_by_name(xml, "return", 3);
+ return (l != NULL) ? l->data : NULL;
+}
+
+static char *
+response_get_error_msg(const char *xml)
+{
+ GList *l = response_values_at_depth_by_name(xml, "faultstring", 3);
+ return (l != NULL) ? xstrdup(l->data) : NULL;
+}
+
+static char *
+response_get_additioanl_information(const char *xml)
+{
+ GList *l = response_values_at_depth_by_name(xml, "additional_information", -1);
+ return (l != NULL) ? xstrdup(l->data) : NULL;
+}
+
+void
+response_values_free(GList *values)
+{
+ g_list_free_full(values, free);
+}
+
+/*
+ * POST
+ */
+
+void
+mantisbt_result_free(mantisbt_result_t *result)
+{
+ if (result == NULL)
+ return;
+
+ free(result->mr_url);
+ free(result->mr_msg);
+ free(result->mr_body);
+ free(result);
+}
+
+mantisbt_result_t *
+mantisbt_soap_call(const mantisbt_settings_t *settings, const soap_request_t *req)
+{
+ char *request = soap_request_to_str(req);
+
+ const char *url = settings->m_mantisbt_soap_url;
+
+ mantisbt_result_t *result = xzalloc(sizeof(*result));
+
+ if (url == NULL || request == NULL)
+ {
+ result->mr_error = -2;
+ result->mr_msg = xasprintf(_("Url or request isn't specified."));
+ free(request);
+
+ return result;
+ }
+
+ char *url_copy = NULL;
+
+ int redirect_count = 0;
+ char *errmsg;
+ post_state_t *post_state;
+
+redirect:
+ post_state = new_post_state(0
+ + POST_WANT_HEADERS
+ + POST_WANT_BODY
+ + POST_WANT_ERROR_MSG
+ + (settings->m_ssl_verify ? POST_WANT_SSL_VERIFY : 0)
+ );
+
+ post_string(post_state, settings->m_mantisbt_soap_url, "text/xml", NULL, request);
+
+ char *location = find_header_in_post_state(post_state, "Location:");
+
+ switch (post_state->http_resp_code)
+ {
+ case 404:
+ result->mr_error = -1;
+ result->mr_msg = xasprintf(_("Error in HTTP POST, "
+ "HTTP code: 404 (Not found), URL:'%s'"), url);
+ break;
+ case 500:
+ result->mr_error = -1;
+ result->mr_msg = response_get_error_msg(post_state->body);
+
+ break;
+ case 301: /* "301 Moved Permanently" (for example, used to move http:// to https://) */
+ case 302: /* "302 Found" (just in case) */
+ case 305: /* "305 Use Proxy" */
+ if (++redirect_count < 10 && location)
+ {
+ free(url_copy);
+ url = url_copy = xstrdup(location);
+ free_post_state(post_state);
+ goto redirect;
+ }
+ /* fall through */
+
+ default:
+ result->mr_error = -1;
+ errmsg = post_state->curl_error_msg;
+ if (errmsg && errmsg[0])
+ result->mr_msg = xasprintf(_("Error in MantisBT request at '%s': %s"), url, errmsg);
+ else
+ result->mr_msg = xasprintf(_("Error in MantisBT request at '%s'"), url);
+ break;
+
+ case 200:
+ case 201:
+ /* sent successfully */
+ result->mr_url = xstrdup(location); /* note: xstrdup(NULL) returns NULL */
+ } /* switch (HTTP code) */
+
+ result->mr_http_resp_code = post_state->http_resp_code;
+ result->mr_body = post_state->body;
+ post_state->body = NULL;
+
+ free_post_state(post_state);
+ free(url_copy);
+ free(request);
+
+ return result;
+}
+
+int
+mantisbt_attach_data(const mantisbt_settings_t *settings, const char *bug_id,
+ const char *att_name, const char *data, int size)
+{
+ soap_request_t *req = soap_request_new_for_method("mc_issue_attachment_add");
+ soap_request_add_credentials_parameter(req, settings);
+
+ soap_request_add_method_parameter(req, "issue_id", SOAP_INTEGER, bug_id);
+ soap_request_add_method_parameter(req, "name", SOAP_STRING, att_name);
+
+ soap_request_add_method_parameter(req, "file_type", SOAP_STRING, "text");
+ soap_request_add_method_parameter(req, "content", SOAP_BASE64, encode_base64(data, size));
+
+ mantisbt_result_t *result = mantisbt_soap_call(settings, req);
+ soap_request_free(req);
+
+ if (result->mr_http_resp_code != 200)
+ {
+ int ret = -1;
+ if (strcmp(result->mr_msg, "Duplicate filename.") == 0)
+ ret = -2;
+
+ error_msg(_("Failed to attach file: '%s'"), result->mr_msg);
+ mantisbt_result_free(result);
+ return ret;
+ }
+
+ int id = response_get_return_value(result->mr_body);
+
+ mantisbt_result_free(result);
+
+ return id;
+}
+
+static int
+mantisbt_attach_fd(const mantisbt_settings_t *settings, const char *bug_id,
+ const char *att_name, int fd)
+{
+ off_t size = lseek(fd, 0, SEEK_END);
+ if (size < 0)
+ {
+ perror_msg(_("Can't lseek '%s'"), att_name);
+ return -1;
+ }
+
+ if (size >= MANTISBT_MAX_FILE_UPLOAD_SIZE)
+ {
+ error_msg(_("Can't upload '%s', it's too large (%llu bytes)"), att_name, (long long)size);
+ return -1;
+ }
+ lseek(fd, 0, SEEK_SET);
+
+ char *data = xmalloc(size + 1);
+ ssize_t r = full_read(fd, data, size);
+ if (r < 0)
+ {
+ free(data);
+ perror_msg(_("Can't read '%s'"), att_name);
+ return -1;
+ }
+
+ int res = mantisbt_attach_data(settings, bug_id, att_name, data, size);
+ free(data);
+ return res;
+}
+
+int
+mantisbt_attach_file(const mantisbt_settings_t *settings, const char *bug_id,
+ const char *att_name, const char *path)
+{
+ int fd = open(path, O_RDONLY);
+ if (fd < 0)
+ {
+ perror_msg(_("Can't open '%s'"), path);
+ return 0;
+ }
+ errno = 0;
+ struct stat st;
+ if (fstat(fd, &st) != 0 || !S_ISREG(st.st_mode))
+ {
+ perror_msg("'%s': not a regular file", path);
+ close(fd);
+ return 0;
+ }
+ log_debug("attaching '%s' as file", att_name);
+ int ret = mantisbt_attach_fd(settings, bug_id, att_name, fd);
+ close(fd);
+ return ret;
+}
+
+static void
+soap_filter_add_new_array_parameter(xmlNodePtr filter_node, const char *name, const char *type, const char *value)
+{
+ const char *array_type = NULL;
+ if( strcmp(type, SOAP_INTEGER) == 0 )
+ array_type = SOAP_INTEGERARRAY;
+ else
+ array_type = SOAP_STRINGARRAY;
+
+ xmlNodePtr filter_item = soap_node_add_child_node(filter_node, name, array_type, /* content */ NULL);
+ soap_node_add_child_node(filter_item, "item", type, value);
+}
+
+static void
+soap_filter_custom_fields_add_new_item(xmlNodePtr filter_node, const char *custom_field_name, const char *value)
+{
+ xmlNodePtr item_node = soap_node_add_child_node(filter_node, "item", SOAP_FILTER_CUSTOMFIELD, /* content */ NULL);
+
+ xmlNodePtr field_node = soap_node_add_child_node(item_node, "field", SOAP_OBJECTREF, /* content */ NULL);
+ soap_node_add_child_node(field_node, "name", SOAP_STRING, custom_field_name);
+
+ xmlNodePtr value_node = soap_node_add_child_node(item_node, "value", SOAP_STRINGARRAY, /* content */ NULL);
+ soap_node_add_child_node(value_node, "item", SOAP_STRING, value);
+}
+
+GList *
+mantisbt_search_by_abrt_hash(mantisbt_settings_t *settings, const char *abrt_hash)
+{
+ soap_request_t *req = soap_request_new_for_method("mc_filter_search_issues");
+ soap_request_add_credentials_parameter(req, settings);
+
+ xmlNodePtr filter_node = soap_node_add_child_node(req->sr_method, "filter", SOAP_FILTER_SEARCH_DATA, /* content */ NULL);
+
+ /* 'hide_status_is : -2' means, searching within all status */
+ soap_filter_add_new_array_parameter(filter_node, "hide_status_id", SOAP_INTEGERARRAY, "-2");
+
+ // custom fields
+ xmlNodePtr custom_fields_node = soap_node_add_child_node(filter_node, "custom_fields", SOAP_FILTER_CUSTOMFIELD_ARRAY, /* content */ NULL);
+
+ // custom field 'abrt_hash'
+ soap_filter_custom_fields_add_new_item(custom_fields_node, "abrt_hash", abrt_hash);
+
+ soap_request_add_method_parameter(req, "page_number", SOAP_INTEGER, "1");
+ soap_request_add_method_parameter(req, "per_page", SOAP_INTEGER, /* -1 means get all issues */ "-1");
+
+ mantisbt_result_t *result = mantisbt_soap_call(settings, req);
+ soap_request_free(req);
+
+ if (result->mr_error == -1)
+ {
+ error_msg(_("Failed to search MantisBT issue by duphash: '%s'"), result->mr_msg);
+ mantisbt_result_free(result);
+ return NULL;
+ }
+
+ GList *ids = response_get_main_ids_list(result->mr_body);
+
+ return ids;
+}
+
+GList *
+mantisbt_search_duplicate_issues(mantisbt_settings_t *settings, const char *category,
+ const char *version, const char *abrt_hash)
+{
+ soap_request_t *req = soap_request_new_for_method("mc_filter_search_issues");
+ soap_request_add_credentials_parameter(req, settings);
+
+ xmlNodePtr filter_node = soap_node_add_child_node(req->sr_method, "filter", SOAP_FILTER_SEARCH_DATA, /* content */ NULL);
+
+ soap_filter_add_new_array_parameter(filter_node, "project_id", SOAP_INTEGERARRAY, settings->m_project_id);
+
+ /* 'hide_status_is : -2' means, searching within all status */
+ soap_filter_add_new_array_parameter(filter_node, "hide_status_id", SOAP_INTEGERARRAY, "-2");
+
+ soap_filter_add_new_array_parameter(filter_node, "category", SOAP_STRINGARRAY, category);
+
+ // custom fields
+ xmlNodePtr custom_fields_node = soap_node_add_child_node(filter_node, "custom_fields", SOAP_FILTER_CUSTOMFIELD_ARRAY, /* content */ NULL);
+
+ // custom field 'abrt_hash'
+ soap_filter_custom_fields_add_new_item(custom_fields_node, "abrt_hash", abrt_hash);
+
+ // version
+ if (version != NULL)
+ soap_filter_add_new_array_parameter(filter_node, "os_build", SOAP_STRINGARRAY, version);
+
+ soap_request_add_method_parameter(req, "page_number", SOAP_INTEGER, "1");
+ soap_request_add_method_parameter(req, "per_page", SOAP_INTEGER, /* -1 means get all issues */ "-1");
+
+ mantisbt_result_t *result = mantisbt_soap_call(settings, req);
+ soap_request_free(req);
+
+ if (result->mr_error == -1)
+ {
+ error_msg(_("Failed to search MantisBT duplicate issue: '%s'"), result->mr_msg);
+ mantisbt_result_free(result);
+ return NULL;
+ }
+
+ GList *ids = response_get_main_ids_list(result->mr_body);
+
+ return ids;
+}
+
+static char *
+custom_field_get_id_from_name(GList *ids, GList *names, const char *name)
+{
+ GList *i = ids;
+ GList *n = names;
+ for (; i != NULL; i = i->next, n = n->next)
+ {
+ if (strcmp(n->data, name) == 0)
+ return i->data;
+ }
+
+ return NULL;
+}
+
+static void
+custom_field_ask(const char *name)
+{
+ char *msg = xasprintf(_("CentOS Bug Tracker doesn't contain custom field '%s', which is required for full functionality of the reporter. Do you still want to create a new issue?"), name);
+ int yes = ask_yes_no(msg);
+ free(msg);
+
+ if (!yes)
+ {
+ set_xfunc_error_retval(EXIT_CANCEL_BY_USER);
+ xfunc_die();
+ }
+
+ return;
+}
+
+static void
+mantisbt_get_custom_fields(const mantisbt_settings_t *settings, mantisbt_custom_fields_t *fields, const char *project_id)
+{
+ soap_request_t *req = soap_request_new_for_method("mc_project_get_custom_fields");
+ soap_request_add_credentials_parameter(req, settings);
+ soap_request_add_method_parameter(req, "project_id", SOAP_INTEGER, project_id);
+
+ mantisbt_result_t *result = mantisbt_soap_call(settings, req);
+ soap_request_free(req);
+
+ if (result->mr_http_resp_code != 200)
+ error_msg_and_die(_("Failed to get custom fields for '%s' project"), settings->m_project);
+
+ GList *ids = response_values_at_depth_by_name(result->mr_body, "id", -1);
+ GList *names = response_values_at_depth_by_name(result->mr_body, "name", -1);
+
+ mantisbt_result_free(result);
+
+ if ((fields->cf_abrt_hash_id = custom_field_get_id_from_name(ids, names, CUSTOMFIELD_DUPHASH)) == NULL)
+ custom_field_ask(CUSTOMFIELD_DUPHASH);
+
+ if ((fields->cf_url_id = custom_field_get_id_from_name(ids, names, CUSTOMFIELD_URL)) == NULL)
+ custom_field_ask(CUSTOMFIELD_URL);
+
+ return;
+}
+
+int
+mantisbt_create_new_issue(const mantisbt_settings_t *settings,
+ problem_data_t *problem_data,
+ const problem_report_t *pr,
+ const char *tracker_url)
+{
+
+ const char *category = problem_data_get_content_or_NULL(problem_data, FILENAME_COMPONENT);
+ const char *duphash = problem_data_get_content_or_NULL(problem_data, FILENAME_DUPHASH);
+
+ char *summary = shorten_string_to_length(problem_report_get_summary(pr), MAX_SUMMARY_LENGTH);
+
+ const char *description = problem_report_get_description(pr);
+ const char *additional_information = problem_report_get_section(pr, PR_SEC_ADDITIONAL_INFO);
+
+ mantisbt_custom_fields_t fields;
+ mantisbt_get_custom_fields(settings, &fields, settings->m_project_id);
+
+ soap_request_t *req = soap_request_new_for_method("mc_issue_add");
+ soap_request_add_credentials_parameter(req, settings);
+ soap_add_new_issue_parameters(req, settings->m_project, settings->m_project_version, category, summary, description, additional_information, settings->m_create_private, &fields, duphash, tracker_url);
+
+ mantisbt_result_t *result = mantisbt_soap_call(settings, req);
+ soap_request_free(req);
+ free(summary);
+
+ if (result->mr_error == -1)
+ {
+ error_msg(_("Failed to create a new issue: '%s'"), result->mr_msg);
+ mantisbt_result_free(result);
+ return -1;
+ }
+
+ int id = response_get_return_value(result->mr_body);
+
+ mantisbt_result_free(result);
+ return id;
+}
+
+mantisbt_issue_info_t *
+mantisbt_get_issue_info(const mantisbt_settings_t *settings, int issue_id)
+{
+ soap_request_t *req = soap_request_new_for_method("mc_issue_get");
+ soap_request_add_credentials_parameter(req, settings);
+
+ char *issue_id_str = xasprintf("%d", issue_id);
+ soap_request_add_method_parameter(req, "issue_id", SOAP_INTEGER, issue_id_str);
+ free(issue_id_str);
+
+ mantisbt_result_t *result = mantisbt_soap_call(settings, req);
+ soap_request_free(req);
+
+ if (result->mr_error == -1)
+ {
+ error_msg(_("Failed to get MantisBT issue: '%s'"), result->mr_msg);
+ mantisbt_result_free(result);
+ return NULL;
+ }
+
+ mantisbt_issue_info_t *issue_info = mantisbt_issue_info_new();
+
+ issue_info->mii_id = issue_id;
+ issue_info->mii_status = response_get_name_value_of_element(result->mr_body, "status");
+ issue_info->mii_resolution = response_get_name_value_of_element(result->mr_body, "resolution");
+ issue_info->mii_reporter = response_get_name_value_of_element(result->mr_body, "reporter");
+ issue_info->mii_project = response_get_name_value_of_element(result->mr_body, "project");
+
+ if (strcmp(issue_info->mii_status, "closed") == 0 && !issue_info->mii_resolution)
+ error_msg(_("Issue %i is CLOSED, but it has no RESOLUTION"), issue_info->mii_id);
+
+ issue_info->mii_dup_id = response_get_id_of_relatedto_issue(result->mr_body);
+
+ if (strcmp(issue_info->mii_status, "closed") == 0
+ && strcmp(issue_info->mii_resolution, "duplicate") == 0
+ && issue_info->mii_dup_id == -1 )
+ {
+ error_msg(_("Issue %i is CLOSED as DUPLICATE, but it has no DUPLICATE_ID"),
+ issue_info->mii_id);
+ }
+
+ /* notes are stored in <text> element */
+ issue_info->mii_notes = response_values_at_depth_by_name(result->mr_body, "text", -1);
+
+ /* looking for bt rating in additional information too */
+ char *add_info = response_get_additioanl_information(result->mr_body);
+ if (add_info != NULL)
+ issue_info->mii_notes = g_list_append (issue_info->mii_notes, add_info);
+ issue_info->mii_attachments = response_values_at_depth_by_name(result->mr_body, "filename", -1);
+ issue_info->mii_best_bt_rating = comments_find_best_bt_rating(issue_info->mii_notes);
+
+ mantisbt_result_free(result);
+ return issue_info;
+}
+
+int
+mantisbt_add_issue_note(const mantisbt_settings_t *settings, int issue_id, const char *note)
+{
+ soap_request_t *req = soap_request_new_for_method("mc_issue_note_add");
+ soap_request_add_credentials_parameter(req, settings);
+
+ char *issue_id_str = xasprintf("%i", issue_id);
+ soap_node_add_child_node(req->sr_method, "issue_id", SOAP_INTEGER, issue_id_str);
+
+ xmlNodePtr note_node = soap_node_add_child_node(req->sr_method, "note", SOAP_ISSUENOTE, /* content */ NULL);
+ soap_node_add_child_node(note_node, "text", SOAP_STRING, note);
+
+ mantisbt_result_t *result = mantisbt_soap_call(settings, req);
+
+ free(issue_id_str);
+ soap_request_free(req);
+
+ if (result->mr_error == -1)
+ {
+ error_msg(_("Failed to add MantisBT issue note: '%s'"), result->mr_msg);
+ mantisbt_result_free(result);
+ return -1;
+ }
+ int id = response_get_return_value(result->mr_body);
+
+ mantisbt_result_free(result);
+ return id;
+}
+
+void
+mantisbt_get_project_id_from_name(mantisbt_settings_t *settings)
+{
+ if (settings->m_project == NULL)
+ error_msg_and_die(_("The MantisBT project has not been deretmined."));
+
+ soap_request_t *req = soap_request_new_for_method("mc_project_get_id_from_name");
+ soap_request_add_credentials_parameter(req, settings);
+ soap_node_add_child_node(req->sr_method, "project_name", SOAP_STRING, settings->m_project);
+
+ mantisbt_result_t *result = mantisbt_soap_call(settings, req);
+
+ if (result->mr_http_resp_code != 200)
+ error_msg_and_die(_("Failed to get project id from name"));
+
+ settings->m_project_id = response_get_return_value_as_string(result->mr_body);
+
+ return;
+}
diff --git a/src/plugins/mantisbt.conf b/src/plugins/mantisbt.conf
new file mode 100644
index 0000000..15a1065
--- /dev/null
+++ b/src/plugins/mantisbt.conf
@@ -0,0 +1,8 @@
+# MantisBT URL
+MantisbtURL = https://bugs.centos.org/
+# yes means that ssl certificates will be checked
+SSLVerify = yes
+# your login has to exist, if you don have any, please create one
+Login =
+# your password
+Password =
diff --git a/src/plugins/mantisbt.h b/src/plugins/mantisbt.h
new file mode 100644
index 0000000..c31c174
--- /dev/null
+++ b/src/plugins/mantisbt.h
@@ -0,0 +1,140 @@
+/*
+ Copyright (C) 2014 ABRT team
+ Copyright (C) 2014 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.
+*/
+
+#ifndef MANTISBT_H
+#define MANTISBT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libxml/encoding.h>
+#include "problem_report.h"
+
+#define SOAP_STRING "ns2:string"
+#define SOAP_INTEGER "ns2:integer"
+#define SOAP_INTEGERARRAY "ns2:IntegerArray"
+#define SOAP_STRINGARRAY "ns2:StringArray"
+#define SOAP_ISSUEDATA "ns3:IssueData"
+#define SOAP_OBJECTREF "ns3:ObjectRef"
+#define SOAP_CUSTOMFIELD_ARRAY "ns2:CustomFieldValueForIssueDataArray"
+#define SOAP_FILTER_CUSTOMFIELD "ns2:FilterCustomField"
+#define SOAP_FILTER_CUSTOMFIELD_ARRAY "ns2:FilterCustomFieldArray"
+#define SOAP_FILTER_SEARCH_DATA "ns2:FilterSearchData"
+#define SOAP_CUSTOMFIELD "ns2:CustomFieldValueForIssueData"
+#define SOAP_BASE64 "SOAP-ENC:base64"
+#define SOAP_ISSUENOTE "ns3:IssueNoteData"
+
+#define PR_SEC_ADDITIONAL_INFO "Additional info"
+
+typedef struct soap_request
+{
+ xmlNodePtr sr_root;
+ xmlNodePtr sr_body;
+ xmlNodePtr sr_method;
+} soap_request_t;
+
+typedef struct mantisbt_settings
+{
+ char *m_login;
+ char *m_password;
+ const char *m_mantisbt_url;
+ const char *m_mantisbt_soap_url;
+ char *m_project;
+ char *m_project_id;
+ char *m_project_version;
+ const char *m_DontMatchComponents;
+ int m_ssl_verify;
+ int m_create_private;
+} mantisbt_settings_t;
+
+typedef struct mantisbt_result
+{
+ int mr_http_resp_code;
+ int mr_error;
+ char *mr_msg;
+ char *mr_url;
+ char *mr_body;
+} mantisbt_result_t;
+
+typedef struct mantisbt_issue_info
+{
+ int mii_id;
+ int mii_dup_id;
+ unsigned mii_best_bt_rating;
+
+ char *mii_status;
+ char *mii_resolution;
+ char *mii_reporter;
+ char *mii_project;
+
+ GList *mii_notes;
+ GList *mii_attachments;
+} mantisbt_issue_info_t;
+
+void mantisbt_settings_free(mantisbt_settings_t *settings);
+
+mantisbt_issue_info_t * mantisbt_issue_info_new();
+void mantisbt_issue_info_free(mantisbt_issue_info_t *info);
+mantisbt_issue_info_t * mantisbt_find_origin_bug_closed_duplicate(mantisbt_settings_t *settings, mantisbt_issue_info_t *info);
+
+void soap_request_free(soap_request_t *req);
+
+soap_request_t *soap_request_new_for_method(const char *method);
+
+void soap_request_add_method_parameter(soap_request_t *req, const char *name, const char *type, const char *value);
+void soap_request_add_credentials_parameter(soap_request_t *req, const mantisbt_settings_t *settings);
+
+char *soap_request_to_str(const soap_request_t *req);
+
+#if 0
+void soap_request_print(soap_request_t *req);
+#endif
+
+GList * response_get_main_ids_list(const char *xml);
+int response_get_main_id(const char *xml);
+void response_values_free(GList *values);
+
+void mantisbt_result_free(mantisbt_result_t *result);
+mantisbt_result_t *mantisbt_soap_call(const mantisbt_settings_t *settings, const soap_request_t *req);
+
+int mantisbt_attach_data(const mantisbt_settings_t *settings, const char *bug_id,
+ const char *att_name, const char *data, int size);
+
+int mantisbt_attach_file(const mantisbt_settings_t *settings, const char *bug_id,
+ const char *att_name, const char *data);
+
+GList * mantisbt_search_by_abrt_hash(mantisbt_settings_t *settings, const char *abrt_hash);
+GList * mantisbt_search_duplicate_issues(mantisbt_settings_t *settings, const char *category,
+ const char *version, const char *abrt_hash);
+
+int mantisbt_create_new_issue(const mantisbt_settings_t *settings, problem_data_t *problem_data,
+ const problem_report_t *pr, const char *tracker_url);
+
+mantisbt_issue_info_t * mantisbt_get_issue_info(const mantisbt_settings_t *settings, int issue_id);
+int mantisbt_add_issue_note(const mantisbt_settings_t *settings, int issue_id, const char *note);
+
+void mantisbt_get_project_id_from_name(mantisbt_settings_t *settings);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/src/plugins/mantisbt_format.conf b/src/plugins/mantisbt_format.conf
new file mode 100644
index 0000000..da08aa6
--- /dev/null
+++ b/src/plugins/mantisbt_format.conf
@@ -0,0 +1,59 @@
+# Lines starting with # are ignored.
+# Lines can be continued on the next line using trailing backslash.
+#
+# Format:
+# %summary:: summary format
+# section:: element1[,element2]...
+# The literal text line to be added to Bugzilla comment. Can be empty.
+# (IOW: empty lines are NOT ignored!)
+#
+# Summary format is a line of text, where %element% is replaced by
+# text element's content, and [[...%element%...]] block is used only if
+# %element% exists. [[...]] blocks can nest.
+#
+# Sections can be:
+# - %summary: bug summary format string.
+# - %attach: a list of elements to attach.
+# - text, double colon (::) and the list of comma-separated elements.
+# Text can be empty (":: elem1, elem2, elem3" works),
+# in this case "Text:" header line will be omitted.
+#
+# Elements can be:
+# - problem directory element names, which get formatted as
+# <element_name>: <contents>
+# or
+# <element_name>:
+# :<contents>
+# :<contents>
+# :<contents>
+# - problem directory element names prefixed by "%bare_",
+# which is formatted as-is, without "<element_name>:" and colons
+# - %oneline, %multiline, %text wildcards, which select all corresponding
+# elements for output or attachment
+# - %binary wildcard, valid only for %attach section, instructs to attach
+# binary elements
+# - problem directory element names prefixed by "-",
+# which excludes given element from all wildcards
+#
+# Nonexistent elements are silently ignored.
+# If none of elements exists, the section will not be created.
+
+%summary:: [abrt] %pkg_name%[[: %crash_function%()]][[: %reason%]][[: TAINTED %tainted_short%]]
+
+Description of problem:: %bare_comment
+
+Version-Release number of selected component:: %bare_package
+
+Truncated backtrace:: %bare_%short_backtrace
+
+%Additional info::
+:: -pkg_arch,-pkg_epoch,-pkg_name,-pkg_release,-pkg_version,\
+ -component,-architecture,\
+ -analyzer,-count,-duphash,-uuid,-abrt_version,\
+ -username,-hostname,-os_release,-os_info,\
+ -time,-pid,-pwd,-last_occurrence,-ureports_counter,\
+ %reporter,\
+ %oneline
+
+%attach:: -comment,-reason,-reported_to,-event_log,%multiline,\
+ -coredump,%binary
diff --git a/src/plugins/mantisbt_formatdup.conf b/src/plugins/mantisbt_formatdup.conf
new file mode 100644
index 0000000..b1552ac
--- /dev/null
+++ b/src/plugins/mantisbt_formatdup.conf
@@ -0,0 +1,65 @@
+# Lines starting with # are ignored.
+# Lines can be continued on the next line using trailing backslash.
+#
+# Format:
+# %summary:: summary format
+# section:: element1[,element2]...
+# The literal text line to be added to Bugzilla comment. Can be empty.
+# (IOW: empty lines are NOT ignored!)
+#
+# Summary format is a line of text, where %element% is replaced by
+# text element's content, and [[...%element%...]] block is used only if
+# %element% exists. [[...]] blocks can nest.
+#
+# Sections can be:
+# - %summary: bug summary format string.
+# - %attach: a list of elements to attach.
+# - text, double colon (::) and the list of comma-separated elements.
+# Text can be empty (":: elem1, elem2, elem3" works),
+# in this case "Text:" header line will be omitted.
+#
+# Elements can be:
+# - problem directory element names, which get formatted as
+# <element_name>: <contents>
+# or
+# <element_name>:
+# :<contents>
+# :<contents>
+# :<contents>
+# - problem directory element names prefixed by "%bare_",
+# which is formatted as-is, without "<element_name>:" and colons
+# - %oneline, %multiline, %text wildcards, which select all corresponding
+# elements for output or attachment
+# - %binary wildcard, valid only for %attach section, instructs to attach
+# binary elements
+# - problem directory element names prefixed by "-",
+# which excludes given element from all wildcards
+#
+# Nonexistent elements are silently ignored.
+# If none of elements exists, the section will not be created.
+
+# When we add a comment to an existing BZ, %summary is ignored
+# (it specifies *new bug* summary field):
+# %summary:: blah blah
+
+# When dup is detected, BZ reporter adds a comment to it.
+# This comment may interrupt an ongoing conversation in the BZ.
+# (Three people independently filed a bug against abrt about this).
+# Need to clearly explain what this comment is, to prevent confusion.
+# Hopefully, this line would suffice:
+Another user experienced a similar problem:
+
+# If user filled out comment field, show it:
+:: %bare_comment
+
+# var_log_messages has too much variance (time/date),
+# we exclude it from message so that dup message elimination has more chances to work
+:: \
+ -pkg_arch,-pkg_epoch,-pkg_name,-pkg_release,-pkg_version,\
+ -component,-architecture,\
+ -analyzer,-count,-duphash,-uuid,-abrt_version,\
+ -username,-hostname,-os_release,-os_info,\
+ -time,-pid,-pwd,-last_occurrence,-ureports_counter,\
+ -var_log_messages,\
+ %reporter,\
+ %oneline
diff --git a/src/plugins/report_CentOSBugTracker.conf b/src/plugins/report_CentOSBugTracker.conf
new file mode 100644
index 0000000..04cab13
--- /dev/null
+++ b/src/plugins/report_CentOSBugTracker.conf
@@ -0,0 +1,4 @@
+Mantisbt_MantisbtURL = https://bugs.centos.org
+Mantisbt_Login =
+Mantisbt_Password
+Mantisbt_SSLVerify = yes
diff --git a/src/plugins/report_CentOSBugTracker.xml.in b/src/plugins/report_CentOSBugTracker.xml.in
new file mode 100644
index 0000000..81f909b
--- /dev/null
+++ b/src/plugins/report_CentOSBugTracker.xml.in
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<event>
+ <_name>CentOS Bug Tracker</_name>
+ <_description>Report to CentOS Bug Tracker</_description>
+
+ <requires-items>component,duphash,os_release</requires-items>
+ <exclude-items-by-default>coredump,count,event_log,reported_to,vmcore</exclude-items-by-default>
+ <exclude-items-always></exclude-items-always>
+ <exclude-binary-items>no</exclude-binary-items>
+ <include-items-by-default></include-items-by-default>
+ <minimal-rating>3</minimal-rating>
+ <gui-review-elements>yes</gui-review-elements>
+
+ <options>
+ <option type="text" name="Mantisbt_Login">
+ <_label>User name</_label>
+ <allow-empty>no</allow-empty>
+ <_description>CentOS Bug Tracker account user name</_description>
+ <_note-html>You can create bugs.centos.org account <a href="https://bugs.centos.org/signup_page.php">here</a></_note-html>
+ </option>
+ <option type="password" name="Mantisbt_Password">
+ <_label>Password</_label>
+ <allow-empty>no</allow-empty>
+ <_description>CentOS Bug Tracker account password</_description>
+ </option>
+ <advanced-options>
+ <option type="text" name="Mantisbt_MantisbtURL">
+ <_label>CentOS Bug Tracker URL</_label>
+ <allow-empty>no</allow-empty>
+ <_note-html>Address of CentOS Bug Tracker server</_note-html>
+ <default-value>https://bugs.centos.org</default-value>
+ </option>
+ <option type="bool" name="Mantisbt_SSLVerify">
+ <_label>Verify SSL</_label>
+ <_note-html>Check SSL key validity</_note-html>
+ <default-value>yes</default-value>
+ </option>
+ <option type="text" name="Mantisbt_Project">
+ <_label>CentOS Bug Tracker project</_label>
+ <allow-empty>yes</allow-empty>
+ <_note-html>Specify this only if you needed different project than specified in /etc/os-release</_note-html>
+ </option>
+ <option type="text" name="Mantisbt_ProjectVersion">
+ <_label>CentOS Bug Tracker project version</_label>
+ <allow-empty>yes</allow-empty>
+ <_note-html>Specify this only if you needed different project version than specified in /etc/os-release</_note-html>
+ </option>
+ <option type="text" name="http_proxy">
+ <_label>HTTP Proxy</_label>
+ <allow-empty>yes</allow-empty>
+ <_note-html>Sets the proxy server to use for HTTP</_note-html>
+ </option>
+ <option type="text" name="HTTPS_PROXY">
+ <_label>HTTPS Proxy</_label>
+ <allow-empty>yes</allow-empty>
+ <_note-html>Sets the proxy server to use for HTTPS</_note-html>
+ </option>
+ <option type="bool" name="Mantisbt_CreatePrivate">
+ <_label>Restrict access</_label>
+ <_note-html>Restrict access to the created CentOS Bug Tracker issue allowing only users from specified groups to view it (see advanced settings for more details)</_note-html>
+ <default-value>no</default-value>
+ </option>
+ </advanced-options>
+ </options>
+</event>
diff --git a/src/plugins/reporter-mantisbt.c b/src/plugins/reporter-mantisbt.c
new file mode 100644
index 0000000..d281cdb
--- /dev/null
+++ b/src/plugins/reporter-mantisbt.c
@@ -0,0 +1,696 @@
+/*
+ Copyright (C) 2014 ABRT team
+ Copyright (C) 2014 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 "internal_libreport.h"
+#include "client.h"
+#include "mantisbt.h"
+#include "problem_report.h"
+
+static void
+parse_osinfo_for_mantisbt(map_string_t *osinfo, char** project, char** version)
+{
+ const char *name = get_map_string_item_or_NULL(osinfo, "CENTOS_MANTISBT_PROJECT");
+ if (!name)
+ name = get_map_string_item_or_NULL(osinfo, OSINFO_NAME);
+
+ const char *version_id = get_map_string_item_or_NULL(osinfo, "CENTOS_MANTISBT_PROJECT_VERSION");
+ if (!version_id)
+ version_id = get_map_string_item_or_NULL(osinfo, OSINFO_VERSION_ID);
+
+ if (name && version_id)
+ {
+ *project = xstrdup(name);
+ *version = xstrdup(version_id);
+ return;
+ }
+
+ /* something bad happend */
+ *project = NULL;
+ *version = NULL;
+}
+
+static char *
+ask_mantisbt_login(const char *message)
+{
+ char *login = ask(message);
+ if (login == NULL || login[0] == '\0')
+ {
+ set_xfunc_error_retval(EXIT_CANCEL_BY_USER);
+ error_msg_and_die(_("Can't continue without login"));
+ }
+
+ return login;
+}
+
+static char *
+ask_mantisbt_password(const char *message)
+{
+ char *password = ask_password(message);
+ /* TODO: this should be fixed in ask_password() as other tools have the same problem */
+ putchar('\n');
+ if (password == NULL || password[0] == '\0')
+ {
+ set_xfunc_error_retval(EXIT_CANCEL_BY_USER);
+ error_msg_and_die(_("Can't continue without password"));
+ }
+
+ return password;
+}
+
+static void
+ask_mantisbt_credentials(mantisbt_settings_t *settings, const char *pre_message)
+{
+ free(settings->m_login);
+ free(settings->m_password);
+
+ char *question = xasprintf("%s %s", pre_message, _("Please enter your CentOS Bug Tracker login:"));
+ settings->m_login = ask_mantisbt_login(question);
+ free(question);
+
+ question = xasprintf("%s %s '%s':", pre_message, _("Please enter the password for"), settings->m_login);
+ settings->m_password = ask_mantisbt_password(question);
+ free(question);
+
+ return;
+}
+
+static void
+verify_credentials(mantisbt_settings_t *settings)
+{
+ if (settings->m_login[0] == '\0' || settings->m_password[0] == '\0')
+ ask_mantisbt_credentials(settings, _("Credentials are not provided by configuration."));
+
+ while (true)
+ {
+ soap_request_t *req = soap_request_new_for_method("mc_login");
+ soap_request_add_credentials_parameter(req, settings);
+
+ mantisbt_result_t *result = mantisbt_soap_call(settings, req);
+ soap_request_free(req);
+
+ if (g_verbose > 2)
+ {
+ GList *ids = response_get_main_ids_list(result->mr_body);
+ if (ids != NULL)
+ log("%s", (char *)ids->data);
+ response_values_free(ids);
+ }
+
+ int result_val = result->mr_http_resp_code;
+ mantisbt_result_free(result);
+
+ if (result_val == 200)
+ return;
+
+ ask_mantisbt_credentials(settings, _("Invalid password or login."));
+ }
+}
+
+static void
+set_settings(mantisbt_settings_t *m, map_string_t *settings, struct dump_dir *dd)
+{
+ const char *environ;
+
+ environ = getenv("Mantisbt_Login");
+ m->m_login = xstrdup(environ ? environ : get_map_string_item_or_empty(settings, "Login"));
+
+ environ = getenv("Mantisbt_Password");
+ m->m_password = xstrdup(environ ? environ : get_map_string_item_or_empty(settings, "Password"));
+
+ environ = getenv("Mantisbt_MantisbtURL");
+ m->m_mantisbt_url = environ ? environ : get_map_string_item_or_empty(settings, "MantisbtURL");
+ if (!m->m_mantisbt_url[0])
+ m->m_mantisbt_url = "https://bugs.centos.org/";
+ else
+ {
+ /* We don't want trailing '/': "https://host/dir/" -> "https://host/dir" */
+ char *last_slash = strrchr(m->m_mantisbt_url, '/');
+ if (last_slash && last_slash[1] == '\0')
+ *last_slash = '\0';
+ }
+ m->m_mantisbt_soap_url = concat_path_file(m->m_mantisbt_url, "api/soap/mantisconnect.php");
+
+ environ = getenv("Mantisbt_Project");
+ if (environ)
+ {
+ m->m_project = xstrdup(environ);
+ environ = getenv("Mantisbt_ProjectVersion");
+ if (environ)
+ m->m_project_version = xstrdup(environ);
+ }
+ else
+ {
+ const char *option = get_map_string_item_or_NULL(settings, "Project");
+ if (option)
+ m->m_project = xstrdup(option);
+ option = get_map_string_item_or_NULL(settings, "ProjectVersion");
+ if (option)
+ m->m_project_version = xstrdup(option);
+ }
+
+ if (!m->m_project || !*m->m_project) /* if not overridden or empty... */
+ {
+ free(m->m_project);
+ free(m->m_project_version);
+
+ if (dd != NULL)
+ {
+ map_string_t *osinfo = new_map_string();
+
+ char *os_info_data = dd_load_text(dd, FILENAME_OS_INFO);
+ parse_osinfo(os_info_data, osinfo);
+ free(os_info_data);
+
+ parse_osinfo_for_mantisbt(osinfo, &m->m_project, &m->m_project_version);
+ free_map_string(osinfo);
+ }
+ }
+
+ environ = getenv("Mantisbt_SSLVerify");
+ m->m_ssl_verify = string_to_bool(environ ? environ : get_map_string_item_or_empty(settings, "SSLVerify"));
+
+ environ = getenv("Mantisbt_DontMatchComponents");
+ m->m_DontMatchComponents = environ ? environ : get_map_string_item_or_empty(settings, "DontMatchComponents");
+
+ environ = getenv(CREATE_PRIVATE_TICKET);
+ if (environ)
+ m->m_create_private = string_to_bool(environ);
+
+ if (!m->m_create_private)
+ {
+ environ = getenv("Mantisbt_CreatePrivate");
+ m->m_create_private = string_to_bool(environ ? environ : get_map_string_item_or_empty(settings, "CreatePrivate"));
+ }
+ log_notice("create private CentOS Bug Tracker ticket: '%s'", m->m_create_private ? "YES": "NO");
+}
+
+int main(int argc, char **argv)
+{
+ abrt_init(argv);
+
+ /* I18n */
+ setlocale(LC_ALL, "");
+#if ENABLE_NLS
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+#endif
+
+ const char *program_usage_string = _(
+ "\n& [-vf] [-c CONFFILE]... [-F FMTFILE] [-A FMTFILE2] -d DIR"
+ "\nor:"
+ "\n& [-v] [-c CONFFILE]... [-d DIR] -t[ID] FILE..."
+ "\nor:"
+ "\n& [-v] [-c CONFFILE]... [-d DIR] -t[ID] -w"
+ "\nor:"
+ "\n& [-v] [-c CONFFILE]... -h DUPHASH"
+ "\n"
+ "\nReports problem to CentOS Bug Tracker."
+ "\n"
+ "\nThe tool reads DIR. Then it tries to find an issue"
+ "\nwith the same abrt_hash in custom field 'abrt_hash'."
+ "\n"
+ "\nIf such issue is not found, then a new issue is created. Elements of DIR"
+ "\nare stored in the issue as part of issue description or as attachments,"
+ "\ndepending on their type and size."
+ "\n"
+ "\nOtherwise, if such issue is found and it is marked as CLOSED DUPLICATE,"
+ "\nthe tool follows the chain of duplicates until it finds a non-DUPLICATE issue."
+ "\nThe tool adds a new comment to found issue."
+ "\n"
+ "\nThe URL to new or modified issue is printed to stdout and recorded in"
+ "\n'reported_to' element."
+ "\n"
+ "\nOption -t uploads FILEs to the already created issue on CentOS Bug Tracker site."
+ "\nThe issue ID is retrieved from directory specified by -d DIR."
+ "\nIf problem data in DIR was never reported to CentOS Bug Tracker, upload will fail."
+ "\n"
+ "\nOption -tID uploads FILEs to the issue with specified ID on CentOS Bug Tracker site."
+ "\n-d DIR is ignored."
+ "\n"
+ "\nOption -r sets the last url from reporter_to element which is prefixed with"
+ "\nTRACKER_NAME to URL field. This option is applied only when a new issue is to be"
+ "\nfiled. The default value is 'ABRT Server'"
+ "\n"
+ "\nIf not specified, CONFFILE defaults to "CONF_DIR"/plugins/mantisb.conf"
+ "\nIts lines should have 'PARAM = VALUE' format."
+ "\nRecognized string parameters: MantisbtURL, Login, Password, Project, ProjectVersion."
+ "\nRecognized boolean parameter (VALUE should be 1/0, yes/no): SSLVerify, CreatePrivate."
+ "\nParameters can be overridden via $Mantisbt_PARAM environment variables."
+ "\n"
+ "\nFMTFILE and FMTFILE2 default to "CONF_DIR"/plugins/mantisbt_format.conf"
+ );
+
+ enum {
+ OPT_v = 1 << 0,
+ OPT_d = 1 << 1,
+ OPT_c = 1 << 2,
+ OPT_F = 1 << 3,
+ OPT_A = 1 << 4,
+ OPT_t = 1 << 5,
+ OPT_f = 1 << 6,
+ OPT_h = 1 << 7,
+ OPT_r = 1 << 8,
+ OPT_D = 1 << 9,
+ };
+
+ const char *dump_dir_name = ".";
+ GList *conf_file = NULL;
+ const char *fmt_file = CONF_DIR"/plugins/mantisbt_format.conf";
+ const char *fmt_file2 = fmt_file;
+ char *abrt_hash = NULL;
+ char *ticket_no = NULL;
+ const char *tracker_str = "ABRT Server";
+ char *debug_str = NULL;
+ mantisbt_settings_t mbt_settings = { 0 };
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_STRING( 'd', NULL, &dump_dir_name , "DIR" , _("Problem directory")),
+ OPT_LIST( 'c', NULL, &conf_file , "FILE" , _("Configuration file (may be given many times)")),
+ OPT_STRING( 'F', NULL, &fmt_file , "FILE" , _("Formatting file for initial comment")),
+ OPT_STRING( 'A', NULL, &fmt_file2 , "FILE" , _("Formatting file for duplicates")),
+ OPT_OPTSTRING('t', "ticket", &ticket_no , "ID" , _("Attach FILEs [to issue with this ID]")),
+ OPT_BOOL( 'f', NULL, NULL, _("Force reporting even if this problem is already reported")),
+ OPT_STRING( 'h', "duphash", &abrt_hash, "DUPHASH", _("Print BUG_ID which has given DUPHASH")),
+ OPT_STRING( 'r', "tracker", &tracker_str, "TRACKER_NAME", _("A name of bug tracker for an additional URL from 'reported_to'")),
+
+ OPT_OPTSTRING('D', "debug", &debug_str , "STR" , _("Debug")),
+ OPT_END()
+ };
+
+ unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
+ argv += optind;
+
+ export_abrt_envvars(0);
+
+ map_string_t *settings = new_map_string();
+
+ {
+ if (!conf_file)
+ conf_file = g_list_append(conf_file, (char*) CONF_DIR"/plugins/mantisbt.conf");
+ while (conf_file)
+ {
+ char *fn = (char *)conf_file->data;
+ log_notice("Loading settings from '%s'", fn);
+ load_conf_file(fn, settings, /*skip key w/o values:*/ false);
+ log_debug("Loaded '%s'", fn);
+ conf_file = g_list_delete_link(conf_file, conf_file);
+ }
+
+ struct dump_dir *dd = NULL;
+ if (abrt_hash == NULL)
+ {
+ dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
+ error_msg_and_die(_("Can't open problem dir '%s'."), dump_dir_name);
+ }
+
+ set_settings(&mbt_settings, settings, dd);
+ dd_close(dd);
+ /* WRONG! set_settings() does not copy the strings, it merely sets up pointers
+ * to settings[] dictionary:
+ */
+ /*free_map_string(settings);*/
+ }
+
+ /* No connection is opened between client and server. Users authentication
+ * is performed on every SOAP method call. In the first step we verify the
+ * credentials by calling 'mc_login' method. In the case the credentials are
+ * correctly applies the reporter uses them in the next requests. It is not
+ * necessary to call 'mc_login' method because the method provides only
+ * verification of credentials.
+ */
+ verify_credentials(&mbt_settings);
+
+ if (abrt_hash)
+ {
+ log(_("Looking for similar problems in CentOS Bug Tracker"));
+ GList *ids = mantisbt_search_by_abrt_hash(&mbt_settings, abrt_hash);
+ mantisbt_settings_free(&mbt_settings);
+
+ if (ids == NULL)
+ return EXIT_FAILURE;
+
+ puts(ids->data);
+ response_values_free(ids);
+ return EXIT_SUCCESS;
+ }
+
+ mantisbt_get_project_id_from_name(&mbt_settings);
+
+ if (opts & OPT_t)
+ {
+ if (!argv[0])
+ show_usage_and_die(program_usage_string, program_options);
+
+ if (!ticket_no)
+ {
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
+ xfunc_die();
+ report_result_t *reported_to = find_in_reported_to(dd, "CentOS Bug Tracker");
+ dd_close(dd);
+
+ if (!reported_to || !reported_to->url)
+ error_msg_and_die(_("Can't get MantisBT ID because this problem has not yet been reported to MantisBT."));
+
+ char *url = reported_to->url;
+ reported_to->url = NULL;
+ free_report_result(reported_to);
+
+ if (prefixcmp(url, mbt_settings.m_mantisbt_url) != 0)
+ error_msg_and_die(_("This problem has been reported to MantisBT '%s' which differs from the configured MantisBT '%s'."), url, mbt_settings.m_mantisbt_url);
+
+ ticket_no = strrchr(url, '=');
+ if (!ticket_no)
+ error_msg_and_die(_("Malformed url to MantisBT '%s'."), url);
+
+ /* won't ever call free on it - it simplifies the code a lot */
+ ticket_no = xstrdup(ticket_no + 1);
+ log(_("Using CentOS Bug Tracker ID '%s'"), ticket_no);
+ }
+
+ /* Attach files to existing MantisBT issues */
+ while (*argv)
+ {
+ const char *path = *argv++;
+ char *filename = basename(path);
+ log(_("Attaching file '%s' to issue %s"), filename, ticket_no);
+ mantisbt_attach_file(&mbt_settings, ticket_no, filename, path);
+ }
+
+ return 0;
+ }
+
+ /* Create new issue in MantisBT */
+
+ if (!(opts & OPT_f))
+ {
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
+ xfunc_die();
+ report_result_t *reported_to = find_in_reported_to(dd, "CentOS Bug Tracker");
+ dd_close(dd);
+
+ if (reported_to && reported_to->url)
+ {
+ char *msg = xasprintf(_("This problem was already reported to CentOS Bug Tracker (see '%s')."
+ " Do you still want to create a new issue?"),
+ reported_to->url);
+ int yes = ask_yes_no(msg);
+ free(msg);
+ if (!yes)
+ return 0;
+ }
+ free_report_result(reported_to);
+ }
+
+ problem_data_t *problem_data = create_problem_data_for_reporting(dump_dir_name);
+ if (!problem_data)
+ xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */
+
+ const char *category = problem_data_get_content_or_die(problem_data, FILENAME_COMPONENT);
+ const char *duphash = problem_data_get_content_or_die(problem_data, FILENAME_DUPHASH);
+
+ if (opts & OPT_D)
+ {
+ problem_formatter_t *pf = problem_formatter_new();
+ problem_formatter_add_section(pf, PR_SEC_ADDITIONAL_INFO, /* optional section */ 0);
+
+ if (problem_formatter_load_file(pf, fmt_file))
+ error_msg_and_die("Invalid format file: %s", fmt_file);
+
+ problem_report_t *pr = NULL;
+ if (problem_formatter_generate_report(pf, problem_data, &pr))
+ error_msg_and_die("Failed to format issue report from problem data");
+
+ printf("summary: %s\n"
+ "\n"
+ "Description:\n%s\n"
+ "Additional info:\n%s\n"
+ , problem_report_get_summary(pr)
+ , problem_report_get_description(pr)
+ , problem_report_get_section(pr, PR_SEC_ADDITIONAL_INFO)
+ );
+
+ puts("attachments:");
+ for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a))
+ printf(" %s\n", (const char *)a->data);
+
+ problem_report_free(pr);
+ problem_formatter_free(pf);
+ exit(0);
+ }
+
+ int bug_id = 0;
+
+ /* If REMOTE_RESULT contains "DUPLICATE 12345", we consider it a dup of 12345
+ * and won't search on MantisBT server.
+ */
+ char *remote_result;
+ remote_result = problem_data_get_content_or_NULL(problem_data, FILENAME_REMOTE_RESULT);
+ if (remote_result)
+ {
+ char *cmd = strtok(remote_result, " \n");
+ char *id = strtok(NULL, " \n");
+
+ if (!prefixcmp(cmd, "DUPLICATE"))
+ {
+ errno = 0;
+ char *e;
+ bug_id = strtoul(id, &e, 10);
+ if (errno || id == e || *e != '\0' || bug_id > INT_MAX)
+ {
+ /* error / no digits / illegal trailing chars / too big a number */
+ bug_id = 0;
+ }
+ }
+ }
+
+ mantisbt_issue_info_t *ii;
+ if (!bug_id)
+ {
+ log(_("Checking for duplicates"));
+
+ int existing_id = -1;
+ int crossver_id = -1;
+ {
+ /* Figure out whether we want to match category
+ * when doing dup search.
+ */
+ const char *category_substitute = is_in_comma_separated_list(category, mbt_settings.m_DontMatchComponents) ? NULL : category;
+
+ /* We don't do dup detection across versions (see below why),
+ * but we do add a note if cross-version potential dup exists.
+ * For that, we search for cross version dups first:
+ */
+ // SOAP API searching method is not in the final version, it's possible the project will be string
+ GList *crossver_bugs_ids = mantisbt_search_duplicate_issues(&mbt_settings, category_substitute, /*version*/ NULL, duphash);
+
+ unsigned crossver_bugs_count = g_list_length(crossver_bugs_ids);
+ log_debug("CentOS Bug Tracker has %i reports with duphash '%s' including cross-version ones",
+ crossver_bugs_count, duphash);
+ if (crossver_bugs_count > 0)
+ crossver_id = atoi(g_list_first(crossver_bugs_ids)->data);
+
+ if (crossver_bugs_count > 0)
+ {
+ // SOAP API searching method is not in the final version, it's possible the project will be string
+ GList *dup_bugs_ids = mantisbt_search_duplicate_issues(&mbt_settings, category_substitute, mbt_settings.m_project_version, duphash);
+
+ unsigned dup_bugs_count = g_list_length(dup_bugs_ids);
+ log_debug("CentOS Bug Tracker has %i reports with duphash '%s'",
+ dup_bugs_count, duphash);
+ if (dup_bugs_count > 0)
+ existing_id = atoi(g_list_first(dup_bugs_ids)->data);
+ }
+ }
+
+ if (existing_id < 0)
+ {
+ /* Create new issue */
+ log(_("Creating a new issue"));
+ problem_formatter_t *pf = problem_formatter_new();
+ problem_formatter_add_section(pf, PR_SEC_ADDITIONAL_INFO, 0);
+
+ if (problem_formatter_load_file(pf, fmt_file))
+ error_msg_and_die(_("Invalid format file: %s"), fmt_file);
+
+ problem_report_t *pr = NULL;
+ if (problem_formatter_generate_report(pf, problem_data, &pr))
+ error_msg_and_die(_("Failed to format problem data"));
+
+ if (crossver_id >= 0)
+ problem_report_buffer_printf(
+ problem_report_get_buffer(pr, PR_SEC_DESCRIPTION),
+ "\nPotential duplicate: issue %u\n", crossver_id);
+
+ problem_formatter_free(pf);
+
+ /* get tracker URL if exists */
+ struct dump_dir *dd = dd_opendir(dump_dir_name, 0);
+ char *tracker_url = NULL;
+ if (dd)
+ {
+ report_result_t *reported_to = find_in_reported_to(dd, tracker_str);
+ dd_close(dd);
+
+ if (reported_to && reported_to->url)
+ {
+ log(_("Adding External URL to issue"));
+ tracker_url = xstrdup(reported_to->url);
+ free_report_result(reported_to);
+ }
+ }
+
+ int new_id = mantisbt_create_new_issue(&mbt_settings, problem_data, pr, tracker_url);
+
+ free(tracker_url);
+
+ if (new_id == -1)
+ return EXIT_FAILURE;
+
+ log(_("Adding attachments to issue %i"), new_id);
+ char *new_id_str = xasprintf("%u", new_id);
+
+ for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a))
+ {
+ const char *item_name = (const char *)a->data;
+ struct problem_item *item = problem_data_get_item_or_NULL(problem_data, item_name);
+ if (!item)
+ continue;
+ else if (item->flags & CD_FLAG_TXT)
+ mantisbt_attach_data(&mbt_settings, new_id_str, item_name, item->content, strlen(item->content));
+ else if (item->flags & CD_FLAG_BIN)
+ mantisbt_attach_file(&mbt_settings, new_id_str, item_name, item->content);
+ }
+
+ free(new_id_str);
+ problem_report_free(pr);
+ ii = mantisbt_issue_info_new();
+ ii->mii_id = new_id;
+ ii->mii_status = xstrdup("new");
+
+ goto finish;
+ }
+
+ bug_id = existing_id;
+ }
+
+ ii = mantisbt_get_issue_info(&mbt_settings, bug_id);
+
+ log(_("Bug is already reported: %i"), ii->mii_id);
+
+ /* Follow duplicates */
+ if ((strcmp(ii->mii_status, "closed") == 0)
+ && (strcmp(ii->mii_resolution, "duplicate") == 0)
+ ) {
+ mantisbt_issue_info_t *origin = mantisbt_find_origin_bug_closed_duplicate(&mbt_settings, ii);
+ if (origin)
+ {
+ mantisbt_issue_info_free(ii);
+ ii = origin;
+ }
+ }
+
+ /* TODO CC list
+ * Is no MantisBT SOAP API method which allows adding users to CC list
+ * without updating issue.
+ */
+
+ /* Add comment and bt */
+ const char *comment = problem_data_get_content_or_NULL(problem_data, FILENAME_COMMENT);
+ if (comment && comment[0])
+ {
+ problem_formatter_t *pf = problem_formatter_new();
+
+ if (problem_formatter_load_file(pf, fmt_file2))
+ error_msg_and_die(_("Invalid duplicate format file: '%s"), fmt_file2);
+
+ problem_report_t *pr;
+ if (problem_formatter_generate_report(pf, problem_data, &pr))
+ error_msg_and_die(_("Failed to format duplicate comment from problem data"));
+
+ const char *mbtcomment = problem_report_get_description(pr);
+
+ int dup_comment = is_comment_dup(ii->mii_notes, mbtcomment);
+ if (!dup_comment)
+ {
+ log(_("Adding new comment to issue %d"), ii->mii_id);
+ mantisbt_add_issue_note(&mbt_settings, ii->mii_id, mbtcomment);
+
+ const char *bt = problem_data_get_content_or_NULL(problem_data, FILENAME_BACKTRACE);
+ unsigned rating = 0;
+ const char *rating_str = problem_data_get_content_or_NULL(problem_data, FILENAME_RATING);
+ /* python doesn't have rating file */
+ if (rating_str)
+ rating = xatou(rating_str);
+ if (bt && rating > ii->mii_best_bt_rating)
+ {
+ char *bug_id_str = xasprintf("%i", ii->mii_id);
+
+ log(_("Attaching better backtrace"));
+
+ // find unique filename of attachment
+ char *name = NULL;
+ for (int i = 0;; ++i)
+ {
+ if (i == 0)
+ name = xasprintf("%s", FILENAME_BACKTRACE);
+ else
+ name = xasprintf("%s%d", FILENAME_BACKTRACE, i);
+
+ if (g_list_find_custom(ii->mii_attachments, name, (GCompareFunc) strcmp) == NULL)
+ break;
+
+ free(name);
+ }
+ mantisbt_attach_data(&mbt_settings, bug_id_str, name, bt, strlen(bt));
+
+ free(name);
+ free(bug_id_str);
+ }
+ }
+ else
+ log(_("Found the same comment in the issue history, not adding a new one"));
+
+ problem_report_free(pr);
+ problem_formatter_free(pf);
+ }
+
+finish:
+ log(_("Status: %s%s%s %s/view.php?id=%u"),
+ ii->mii_status,
+ ii->mii_resolution ? " " : "",
+ ii->mii_resolution ? ii->mii_resolution : "",
+ mbt_settings.m_mantisbt_url,
+ ii->mii_id);
+
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (dd)
+ {
+ char *msg = xasprintf("CentOS Bug Tracker: URL=%s/view.php?id=%u", mbt_settings.m_mantisbt_url, ii->mii_id);
+ add_reported_to(dd, msg);
+ free(msg);
+ dd_close(dd);
+ }
+
+ mantisbt_settings_free(&mbt_settings);
+ return 0;
+}
diff --git a/src/workflows/Makefile.am b/src/workflows/Makefile.am
index 0fc1019..5b8376a 100644
--- a/src/workflows/Makefile.am
+++ b/src/workflows/Makefile.am
@@ -22,6 +22,18 @@ dist_workflows_DATA = \
workflow_Logger.xml \
workflow_LoggerCCpp.xml
+if BUILD_MANTISBT
+dist_workflows_DATA += \
+ workflow_CentOSCCpp.xml \
+ workflow_CentOSKerneloops.xml \
+ workflow_CentOSPython.xml \
+ workflow_CentOSPython3.xml \
+ workflow_CentOSVmcore.xml \
+ workflow_CentOSXorg.xml \
+ workflow_CentOSLibreport.xml \
+ workflow_CentOSJava.xml
+endif
+
if BUILD_BUGZILLA
dist_workflows_DATA += \
workflow_AnacondaFedora.xml \
@@ -44,7 +56,8 @@ dist_workflowsdef_DATA =\
report_rhel.conf \
report_mailx.conf \
report_logger.conf \
- report_uploader.conf
+ report_uploader.conf \
+ report_centos.conf
if BUILD_BUGZILLA
dist_workflowsdef_DATA += \
diff --git a/src/workflows/report_centos.conf b/src/workflows/report_centos.conf
new file mode 100644
index 0000000..07b0d40
--- /dev/null
+++ b/src/workflows/report_centos.conf
@@ -0,0 +1,31 @@
+EVENT=workflow_CentOSLibreport analyzer=libreport
+# this is just a meta event which consists of other events
+# the list is defined in the xml file
+
+EVENT=workflow_CentOSCCpp analyzer=CCpp
+# this is just a meta event which consists of other events
+# the list is defined in the xml file
+
+EVENT=workflow_CentOSPython analyzer=Python component!=anaconda
+# this is just a meta event which consists of other events
+# the list is defined in the xml file
+
+EVENT=workflow_CentOSPython3 analyzer=Python3 component!=anaconda
+# this is just a meta event which consists of other events
+# the list is defined in the xml file
+
+EVENT=workflow_CentOSKerneloops analyzer=Kerneloops
+# this is just a meta event which consists of other events
+# the list is defined in the xml file
+
+EVENT=workflow_CentOSVmcore analyzer=vmcore
+# this is just a meta event which consists of other events
+# the list is defined in the xml file
+
+EVENT=workflow_CentOSXorg analyzer=xorg
+# this is just a meta event which consists of other events
+# the list is defined in the xml file
+
+EVENT=workflow_CentOSJava analyzer=Java
+# this is just a meta event which consists of other events
+# the list is defined in the xml file
diff --git a/src/workflows/workflow_CentOSCCpp.xml.in b/src/workflows/workflow_CentOSCCpp.xml.in
new file mode 100644
index 0000000..ea94d56
--- /dev/null
+++ b/src/workflows/workflow_CentOSCCpp.xml.in
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<workflow>
+ <_name>Report to CentOS Bug Tracker</_name>
+ <_description>Process the C/C++ crash using the CentOS infrastructure</_description>
+
+ <events>
+ <event>report_uReport</event>
+ <event>collect_*</event>
+ <event>analyze_CCpp</event>
+ <event>report_CentOSBugTracker</event>
+ </events>
+</workflow>
diff --git a/src/workflows/workflow_CentOSJava.xml.in b/src/workflows/workflow_CentOSJava.xml.in
new file mode 100644
index 0000000..89f9295
--- /dev/null
+++ b/src/workflows/workflow_CentOSJava.xml.in
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<workflow>
+ <_name>Report to CentOS Bug Tracker</_name>
+ <_description>Process the Java exception using the CentOS infrastructure</_description>
+
+ <events>
+ <event>report_uReport</event>
+ <event>collect_*</event>
+ <event>report_CentOSBugTracker</event>
+ </events>
+</workflow>
diff --git a/src/workflows/workflow_CentOSKerneloops.xml.in b/src/workflows/workflow_CentOSKerneloops.xml.in
new file mode 100644
index 0000000..c43c681
--- /dev/null
+++ b/src/workflows/workflow_CentOSKerneloops.xml.in
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<workflow>
+ <_name>Report to CentOS Bug Tracker</_name>
+ <_description>Process the kerneloops using the CentOS infrastructure</_description>
+
+ <events>
+ <event>report_uReport</event>
+ <event>collect_*</event>
+ <event>report_CentOSBugTracker</event>
+ </events>
+</workflow>
diff --git a/src/workflows/workflow_CentOSLibreport.xml.in b/src/workflows/workflow_CentOSLibreport.xml.in
new file mode 100644
index 0000000..2f6ed82
--- /dev/null
+++ b/src/workflows/workflow_CentOSLibreport.xml.in
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<workflow>
+ <_name>Report to CentOS Bug Tracker</_name>
+ <_description>Process the problem using the CentOS infrastructure</_description>
+
+ <events>
+ <event>report_CentOSBugTracker</event>
+ </events>
+</workflow>
diff --git a/src/workflows/workflow_CentOSPython.xml.in b/src/workflows/workflow_CentOSPython.xml.in
new file mode 100644
index 0000000..7e26c6c
--- /dev/null
+++ b/src/workflows/workflow_CentOSPython.xml.in
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<workflow>
+ <_name>Report to CentOS Bug Tracker</_name>
+ <_description>Process the python exception using the CentOS infrastructure</_description>
+
+ <events>
+ <event>report_uReport</event>
+ <event>collect_*</event>
+ <event>report_CentOSBugTracker</event>
+ </events>
+</workflow>
diff --git a/src/workflows/workflow_CentOSPython3.xml.in b/src/workflows/workflow_CentOSPython3.xml.in
new file mode 100644
index 0000000..f1dd8a9
--- /dev/null
+++ b/src/workflows/workflow_CentOSPython3.xml.in
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<workflow>
+ <_name>Report to CentOS Bug Tracker</_name>
+ <_description>Process the python 3 exception using the CentOS infrastructure</_description>
+
+ <events>
+ <event>report_uReport</event>
+ <event>collect_*</event>
+ <event>report_CentOSBugTracker</event>
+ </events>
+</workflow>
diff --git a/src/workflows/workflow_CentOSVmcore.xml.in b/src/workflows/workflow_CentOSVmcore.xml.in
new file mode 100644
index 0000000..c876594
--- /dev/null
+++ b/src/workflows/workflow_CentOSVmcore.xml.in
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<workflow>
+ <_name>Report to CentOS Bug Tracker</_name>
+ <_description>Process the kernel crash using the CentOS infrastructure</_description>
+
+ <events>
+ <event>analyze_VMcore</event>
+ <event>report_uReport</event>
+ <event>collect_*</event>
+ <event>report_CentOSBugTracker</event>
+ </events>
+</workflow>
diff --git a/src/workflows/workflow_CentOSXorg.xml.in b/src/workflows/workflow_CentOSXorg.xml.in
new file mode 100644
index 0000000..bf52221
--- /dev/null
+++ b/src/workflows/workflow_CentOSXorg.xml.in
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<workflow>
+ <_name>Report to CentOS Bug Tracker</_name>
+ <_description>Process the X Server problem using the CentOS infrastructure</_description>
+
+ <events>
+ <event>report_CentOSBugTracker</event>
+ </events>
+</workflow>
--
1.8.3.1