diff --git a/SOURCES/0213-reportclient-honor-ABRT_VERBOSE.patch b/SOURCES/0213-reportclient-honor-ABRT_VERBOSE.patch new file mode 100644 index 0000000..fedb796 --- /dev/null +++ b/SOURCES/0213-reportclient-honor-ABRT_VERBOSE.patch @@ -0,0 +1,136 @@ +From 128490bb35079d77c0fa04f2e254bd6a0acc382a Mon Sep 17 00:00:00 2001 +From: Jakub Filak <jfilak@redhat.com> +Date: Thu, 24 Nov 2016 07:54:21 +0100 +Subject: [PATCH] reportclient: honor ABRT_VERBOSE + +libreport's verbosity can be configured via ABRT_VERBOSE environment +variable and reportclient must be aware of that. + +Related to #1257159 + +Signed-off-by: Jakub Filak <jfilak@redhat.com> +--- + src/client-python/__init__.py | 9 ++++++++ + tests/Makefile.am | 1 + + tests/client_python.at | 53 +++++++++++++++++++++++++++++++++++++++++++ + tests/testsuite.at | 1 + + 4 files changed, 64 insertions(+) + create mode 100644 tests/client_python.at + +diff --git a/src/client-python/__init__.py b/src/client-python/__init__.py +index 8966f22..f10f710 100644 +--- a/src/client-python/__init__.py ++++ b/src/client-python/__init__.py +@@ -31,6 +31,7 @@ from report import EXIT_STOP_EVENT_RUN as RETURN_STOP_EVENT_RUN + + + GETTEXT_PROGNAME = "libreport" ++import os + import locale + import gettext + +@@ -52,10 +53,18 @@ def init_gettext(): + init_gettext() + + verbose = 0 ++ABRT_VERBOSE = os.getenv("ABRT_VERBOSE") ++if ABRT_VERBOSE: ++ try: ++ verbose = int(ABRT_VERBOSE) ++ except: ++ pass ++ + + def set_verbosity(verbosity): + global verbose + verbose = verbosity ++ os.environ["ABRT_VERBOSE"] = str(verbose) + + def log(fmt, *args): + sys.stderr.write("%s\n" % (fmt % args)) +diff --git a/tests/Makefile.am b/tests/Makefile.am +index b45f2d9..52dfce4 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -40,6 +40,7 @@ TESTSUITE_AT = \ + libreport_types.at \ + xml_definition.at \ + report_python.at \ ++ client_python.at \ + xfuncs.at \ + string_list.at \ + ureport.at \ +diff --git a/tests/client_python.at b/tests/client_python.at +new file mode 100644 +index 0000000..656b1b2 +--- /dev/null ++++ b/tests/client_python.at +@@ -0,0 +1,53 @@ ++# -*- Autotest -*- ++ ++AT_BANNER([client_python]) ++ ++## --------- ## ++## verbosity ## ++## --------- ## ++ ++AT_PYTESTFUN([verbosity], [[ ++import sys ++import os ++import unittest ++ ++sys.path.insert(0, "../../../src/client-python") ++sys.path.insert(0, "../../../src/client-python/.libs") ++ ++ ++class TestReportClientVerbose(unittest.TestCase): ++ def setUp(self): ++ try: ++ del os.environ["ABRT_VERBOSE"] ++ except: ++ pass ++ os.unsetenv("ABRT_VERBOSE") ++ self.clientpython = __import__("client-python", globals(), locals(), [], -1) ++ reload(self.clientpython) ++ sys.modules["clientpython"] = self.clientpython ++ ++ def tearDown(self): ++ del sys.modules["clientpython"] ++ ++ def test_default(self): ++ self.assertEquals(self.clientpython.verbose, 0) ++ ++ def test_assign(self): ++ self.clientpython.set_verbosity(1) ++ self.assertEquals(self.clientpython.verbose, 1) ++ self.assertEquals(os.environ["ABRT_VERBOSE"], "1") ++ ++ def test_load_from_environ(self): ++ os.environ["ABRT_VERBOSE"] = "2" ++ reload(self.clientpython) ++ self.assertEquals(self.clientpython.verbose, 2) ++ ++ def test_recover_from_invalid_environ(self): ++ os.environ["ABRT_VERBOSE"] = "foo" ++ reload(self.clientpython) ++ self.assertEquals(self.clientpython.verbose, 0) ++ ++ ++if __name__ == "__main__": ++ unittest.main() ++]]) +diff --git a/tests/testsuite.at b/tests/testsuite.at +index ccb37d1..8ded735 100644 +--- a/tests/testsuite.at ++++ b/tests/testsuite.at +@@ -15,6 +15,7 @@ m4_include([make_description.at]) + m4_include([libreport_types.at]) + m4_include([xml_definition.at]) + m4_include([report_python.at]) ++m4_include([client_python.at]) + m4_include([string_list.at]) + m4_include([ureport.at]) + m4_include([problem_report.at]) +-- +1.8.3.1 + diff --git a/SOURCES/0214-tree-wide-introduce-stop_on_not_reportable-option.patch b/SOURCES/0214-tree-wide-introduce-stop_on_not_reportable-option.patch new file mode 100644 index 0000000..cf2e243 --- /dev/null +++ b/SOURCES/0214-tree-wide-introduce-stop_on_not_reportable-option.patch @@ -0,0 +1,303 @@ +From 8ee8cf6d0467241d2886a095be25c2885d3a8666 Mon Sep 17 00:00:00 2001 +From: Jakub Filak <jfilak@redhat.com> +Date: Wed, 23 Nov 2016 16:22:51 +0100 +Subject: [PATCH] tree-wide: introduce 'stop_on_not_reportable' option + +Make it possible to ignore existence of not-reportable file and continue +in reporting. + +The new configuration option is not yet persistent and can be configured +via an environment variable. + +Related to #1257159 +Related to abrt/abrt#1166 + +Signed-off-by: Jakub Filak <jfilak@redhat.com> +--- + src/cli/cli-report.c | 3 +- + src/gui-wizard-gtk/wizard.c | 21 ++++++++---- + src/include/global_configuration.h | 28 ++++++++++++++++ + src/include/internal_libreport.h | 1 + + src/include/report.h | 3 ++ + src/lib/global_configuration.c | 22 +++++++++++++ + src/lib/report.c | 6 ++++ + src/report-newt/report-newt.c | 4 ++- + src/report-python/reportmodule.c | 1 + + tests/global_config.at | 67 ++++++++++++++++++++++++++++++++++++++ + 10 files changed, 148 insertions(+), 8 deletions(-) + +diff --git a/src/cli/cli-report.c b/src/cli/cli-report.c +index 68baa8b..adb58a7 100644 +--- a/src/cli/cli-report.c ++++ b/src/cli/cli-report.c +@@ -525,7 +525,8 @@ static int is_not_reportable(problem_data_t *problem_data) + if (not_reportable) + { + printf("%s\n", not_reportable); +- return 1; ++ if (get_global_stop_on_not_reportable()) ++ return 1; + } + return 0; + } +diff --git a/src/gui-wizard-gtk/wizard.c b/src/gui-wizard-gtk/wizard.c +index 31861a1..35c6fc3 100644 +--- a/src/gui-wizard-gtk/wizard.c ++++ b/src/gui-wizard-gtk/wizard.c +@@ -2039,7 +2039,7 @@ static gboolean consume_cmd_output(GIOChannel *source, GIOCondition condition, g + dd_sanitize_mode_and_owner(dd); + } + +- if (retval == 0 && !g_expert_mode) ++ if (retval == 0 && !g_expert_mode && get_global_stop_on_not_reportable()) + { + /* Check whether NOT_REPORTABLE element appeared. If it did, we'll stop + * even if exit code is "success". +@@ -3103,16 +3103,25 @@ static gint select_next_page_no(gint current_page_no, gpointer data) + + if (problem_data_get_content_or_NULL(g_cd, FILENAME_NOT_REPORTABLE)) + { +- free(event); + + char *msg = xasprintf(_("This problem should not be reported " + "(it is likely a known problem). %s"), + problem_data_get_content_or_NULL(g_cd, FILENAME_NOT_REPORTABLE) + ); +- cancel_processing(g_lbl_event_log, msg, TERMINATE_NOFLAGS); +- free(msg); +- current_page_no = pages[PAGENO_EVENT_PROGRESS].page_no - 1; +- goto again; ++ ++ if (get_global_stop_on_not_reportable()) ++ { ++ free(event); ++ cancel_processing(g_lbl_event_log, msg, TERMINATE_NOFLAGS); ++ free(msg); ++ current_page_no = pages[PAGENO_EVENT_PROGRESS].page_no - 1; ++ goto again; ++ } ++ else ++ { ++ log(msg); ++ free(msg); ++ } + } + + /* must set g_event_selected otherwise if the event was not +diff --git a/src/include/global_configuration.h b/src/include/global_configuration.h +index bc5513d..0eb18c6 100644 +--- a/src/include/global_configuration.h ++++ b/src/include/global_configuration.h +@@ -53,6 +53,34 @@ bool get_global_create_private_ticket(void); + #define set_global_create_private_ticket libreport_set_global_create_private_ticket + void set_global_create_private_ticket(bool enabled, int flags); + ++/** ++ * Returns logical true if the reporting process shall not start or contine if ++ * the not-reportable files exists. ++ * ++ * The option can be enabled by ABRT_STOP_ON_NOT_REPORTABLE environment ++ * variable. ++ * ++ * @return true if the process shall stop; otherwise the function returns ++ * false. ++ */ ++#define get_global_stop_on_not_reportable libreport_get_global_stop_on_not_reportable ++bool get_global_stop_on_not_reportable(void); ++ ++/** ++ * Configures the stop on not reportable global option ++ * ++ * The function changes the configuration only for the current process by ++ * default. ++ * ++ * The option can be enabled by ABRT_STOP_ON_NOT_REPORTABLE environment ++ * variable. ++ * ++ * @param enabled The option's value ++ * @param flags For future needs (enable persistent configuration) ++ */ ++#define set_global_stop_on_not_reportable libreport_set_global_stop_on_not_reportable ++void set_global_stop_on_not_reportable(bool enabled, int flags); ++ + #ifdef __cplusplus + } + #endif +diff --git a/src/include/internal_libreport.h b/src/include/internal_libreport.h +index cf5730c..23cdfa0 100644 +--- a/src/include/internal_libreport.h ++++ b/src/include/internal_libreport.h +@@ -87,6 +87,7 @@ int vdprintf(int d, const char *format, va_list ap); + + /* consts used across whole libreport */ + #define CREATE_PRIVATE_TICKET "ABRT_CREATE_PRIVATE_TICKET" ++#define STOP_ON_NOT_REPORTABLE "ABRT_STOP_ON_NOT_REPORTABLE" + + /* Pull in entire public libreport API */ + #include "dump_dir.h" +diff --git a/src/include/report.h b/src/include/report.h +index d31eb0a..03f3dc6 100644 +--- a/src/include/report.h ++++ b/src/include/report.h +@@ -35,6 +35,9 @@ enum { + LIBREPORT_DEL_DIR = (1 << 6), /* delete directory after reporting (passes --delete to child) */ + LIBREPORT_RUN_CLI = (1 << 7), /* run 'cli' instead of 'gui' */ + LIBREPORT_RUN_NEWT = (1 << 8), /* run 'report-newt' */ ++ LIBREPORT_IGNORE_NOT_REPORTABLE = (1 << 9), /* do not terminate the ++ reporting process if the ++ not-repotrable file exits. */ + }; + + int report_problem_in_dir(const char *dirname, int flags); +diff --git a/src/lib/global_configuration.c b/src/lib/global_configuration.c +index ef921e9..46b9a63 100644 +--- a/src/lib/global_configuration.c ++++ b/src/lib/global_configuration.c +@@ -163,3 +163,25 @@ void set_global_create_private_ticket(bool enabled, int flags/*unused - persiste + else + safe_unsetenv(CREATE_PRIVATE_TICKET); + } ++ ++bool get_global_stop_on_not_reportable(void) ++{ ++ assert_global_configuration_initialized(); ++ ++ char *env_create_private = getenv(STOP_ON_NOT_REPORTABLE); ++ ++ if (env_create_private == NULL) ++ return true; ++ ++ return string_to_bool(env_create_private); ++} ++ ++void set_global_stop_on_not_reportable(bool enabled, int flags/*unused - persistent*/) ++{ ++ assert_global_configuration_initialized(); ++ ++ if (enabled) ++ xsetenv(STOP_ON_NOT_REPORTABLE, "1"); ++ else ++ xsetenv(STOP_ON_NOT_REPORTABLE, "0"); ++} +diff --git a/src/lib/report.c b/src/lib/report.c +index 7a655fd..3380a52 100644 +--- a/src/lib/report.c ++++ b/src/lib/report.c +@@ -26,6 +26,12 @@ int report_problem_in_dir(const char *dirname, int flags) + if (prgname) + prgname = xasprintf("LIBREPORT_PRGNAME=%s", prgname); + ++ if (flags & LIBREPORT_IGNORE_NOT_REPORTABLE) ++ { ++ load_global_configuration(); ++ set_global_stop_on_not_reportable(false, 0); ++ } ++ + fflush(NULL); + + pid_t pid = fork(); +diff --git a/src/report-newt/report-newt.c b/src/report-newt/report-newt.c +index f5fca76..278cfb7 100644 +--- a/src/report-newt/report-newt.c ++++ b/src/report-newt/report-newt.c +@@ -336,7 +336,9 @@ static int report(const char *dump_dir_name) + dd_close(dd); + newtWinMessage(_("Error"), _("Ok"), (char *)"%s", t); + free(t); +- return -1; ++ ++ if (get_global_stop_on_not_reportable()) ++ return -1; + } + + dd_close(dd); +diff --git a/src/report-python/reportmodule.c b/src/report-python/reportmodule.c +index b8154ae..4491fd4 100644 +--- a/src/report-python/reportmodule.c ++++ b/src/report-python/reportmodule.c +@@ -98,6 +98,7 @@ init_pyreport(void) + PyModule_AddObject(m, "LIBREPORT_DEL_DIR" , Py_BuildValue("i", LIBREPORT_DEL_DIR )); + PyModule_AddObject(m, "LIBREPORT_RUN_CLI" , Py_BuildValue("i", LIBREPORT_RUN_CLI )); + PyModule_AddObject(m, "LIBREPORT_RUN_NEWT" , Py_BuildValue("i", LIBREPORT_RUN_NEWT )); ++ PyModule_AddObject(m, "LIBREPORT_IGNORE_NOT_REPORTABLE", Py_BuildValue("i", LIBREPORT_IGNORE_NOT_REPORTABLE)); + PyModule_AddObject(m, "EXIT_CANCEL_BY_USER", Py_BuildValue("i", EXIT_CANCEL_BY_USER)); + PyModule_AddObject(m, "EXIT_STOP_EVENT_RUN", Py_BuildValue("i", EXIT_STOP_EVENT_RUN)); + } +diff --git a/tests/global_config.at b/tests/global_config.at +index 05a0ffa..1067128 100644 +--- a/tests/global_config.at ++++ b/tests/global_config.at +@@ -169,3 +169,70 @@ TS_MAIN + } + TS_RETURN_MAIN + ]]) ++ ++ ++## ---------------------- ## ++## stop_on_not_reportable ## ++## ---------------------- ## ++ ++AT_TESTFUN([stop_on_not_reportable], [[ ++#include "testsuite.h" ++ ++TS_MAIN ++{ ++ char cwd_buf[PATH_MAX + 1]; ++ static const char *dirs[] = { ++ NULL, ++ NULL, ++ }; ++ dirs[0] = getcwd(cwd_buf, sizeof(cwd_buf)); ++ ++ static int dir_flags[] = { ++ CONF_DIR_FLAG_NONE, ++ -1, ++ }; ++ ++ unlink("libreport.conf"); ++ FILE *lrf = fopen("libreport.conf", "wx"); ++ assert(lrf != NULL); ++ fclose(lrf); ++ ++ assert(load_global_configuration_from_dirs(dirs, dir_flags)); ++ ++ TS_ASSERT_TRUE_MESSAGE(get_global_stop_on_not_reportable(), "True by default"); ++ ++ set_global_stop_on_not_reportable(true, 0); ++ ++ TS_ASSERT_TRUE_MESSAGE(get_global_stop_on_not_reportable(), "Still true"); ++ ++ set_global_stop_on_not_reportable(false, 0); ++ ++ TS_ASSERT_FALSE_MESSAGE(get_global_stop_on_not_reportable(), "Configuration accepted"); ++ TS_ASSERT_STRING_EQ(getenv(STOP_ON_NOT_REPORTABLE), "0", "Correct ENVIRONMENT value"); ++ ++ set_global_stop_on_not_reportable(true, 0); ++ ++ TS_ASSERT_TRUE_MESSAGE(get_global_stop_on_not_reportable(), "Configuration sanity"); ++ TS_ASSERT_STRING_EQ(getenv(STOP_ON_NOT_REPORTABLE), "1", "Correct ENVIRONMENT value"); ++ ++ set_global_stop_on_not_reportable(false, 0); ++ ++ TS_ASSERT_FALSE_MESSAGE(get_global_stop_on_not_reportable(), "Reverted back to False"); ++ TS_ASSERT_STRING_EQ(getenv(STOP_ON_NOT_REPORTABLE), "0", "Correct ENVIRONMENT value"); ++ ++ xsetenv(STOP_ON_NOT_REPORTABLE, "1"); ++ ++ TS_ASSERT_TRUE_MESSAGE(get_global_stop_on_not_reportable(), "Loaded from environment"); ++ ++ unsetenv(STOP_ON_NOT_REPORTABLE); ++ ++ TS_ASSERT_TRUE_MESSAGE(get_global_stop_on_not_reportable(), "Reflects environment"); ++ ++ xsetenv(STOP_ON_NOT_REPORTABLE, "0"); ++ ++ TS_ASSERT_FALSE_MESSAGE(get_global_stop_on_not_reportable(), "Zero is false"); ++ ++ free_global_configuration(); ++} ++TS_RETURN_MAIN ++]]) +-- +1.8.3.1 + diff --git a/SOURCES/0215-rhtsupport-fix-a-double-free-of-config-at-exit.patch b/SOURCES/0215-rhtsupport-fix-a-double-free-of-config-at-exit.patch new file mode 100644 index 0000000..244f62e --- /dev/null +++ b/SOURCES/0215-rhtsupport-fix-a-double-free-of-config-at-exit.patch @@ -0,0 +1,33 @@ +From 696fcadc7d494fc14ad5ac23f19b7da6a7f98c3b Mon Sep 17 00:00:00 2001 +From: Jakub Filak <jfilak@redhat.com> +Date: Mon, 5 Sep 2016 10:42:11 +0200 +Subject: [PATCH] rhtsupport: fix a double free of config at exit + +Introduced in commit 028b35b where I forgot on the code added in commit +5ff7f36. + +Related: rhbz#1373094 +--- + src/plugins/reporter-rhtsupport.c | 5 ----- + 1 file changed, 5 deletions(-) + +diff --git a/src/plugins/reporter-rhtsupport.c b/src/plugins/reporter-rhtsupport.c +index 63a24a5..14b3864 100644 +--- a/src/plugins/reporter-rhtsupport.c ++++ b/src/plugins/reporter-rhtsupport.c +@@ -811,12 +811,7 @@ int main(int argc, char **argv) + /* Check for hints and show them if we have something */ + log(_("Checking for hints")); + if (check_for_hints(base_api_url, &login, &password, ssl_verify, tempfile)) +- { +- ureport_server_config_destroy(&urconf); +- free_map_string(ursettings); +- free(bthash); + goto ret; +- } + } + + log(_("Creating a new case")); +-- +1.8.3.1 + diff --git a/SOURCES/0216-reportclient-fix-verbosity-test.patch b/SOURCES/0216-reportclient-fix-verbosity-test.patch new file mode 100644 index 0000000..3f6fb62 --- /dev/null +++ b/SOURCES/0216-reportclient-fix-verbosity-test.patch @@ -0,0 +1,33 @@ +From 10fb97f11e27c4ac2144005f0ac28b8beff14733 Mon Sep 17 00:00:00 2001 +From: Matej Habrnal <mhabrnal@redhat.com> +Date: Wed, 8 Feb 2017 14:50:49 +0100 +Subject: [PATCH] reportclient: fix verbosity test + +Imported missing report module. + +Related #1257159 + +Signed-off-by: Matej Habrnal <mhabrnal@redhat.com> +--- + tests/client_python.at | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/tests/client_python.at b/tests/client_python.at +index 656b1b2..9f1c1c1 100644 +--- a/tests/client_python.at ++++ b/tests/client_python.at +@@ -13,7 +13,11 @@ import unittest + + sys.path.insert(0, "../../../src/client-python") + sys.path.insert(0, "../../../src/client-python/.libs") ++sys.path.insert(0, "../../../src/report-python") ++sys.path.insert(0, "../../../src/report-python/.libs") + ++report = __import__("report-python", globals(), locals(), [], -1) ++sys.modules["report"] = report + + class TestReportClientVerbose(unittest.TestCase): + def setUp(self): +-- +1.8.3.1 + diff --git a/SOURCES/0217-report-newt-free-allocated-variables-don-t-close-dd-.patch b/SOURCES/0217-report-newt-free-allocated-variables-don-t-close-dd-.patch new file mode 100644 index 0000000..231a615 --- /dev/null +++ b/SOURCES/0217-report-newt-free-allocated-variables-don-t-close-dd-.patch @@ -0,0 +1,39 @@ +From 79d5c0352a09fc1bfe8cc491b33a745ac33b7855 Mon Sep 17 00:00:00 2001 +From: Matej Habrnal <mhabrnal@redhat.com> +Date: Tue, 21 Feb 2017 12:08:13 +0100 +Subject: [PATCH] report-newt: free allocated variables, don't close dd twice + +Uncovered by coverity. + +Related #1257159 + +Signed-off-by: Matej Habrnal <mhabrnal@redhat.com> +--- + src/report-newt/report-newt.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/report-newt/report-newt.c b/src/report-newt/report-newt.c +index 278cfb7..2427d86 100644 +--- a/src/report-newt/report-newt.c ++++ b/src/report-newt/report-newt.c +@@ -333,12 +333,16 @@ static int report(const char *dump_dir_name) + not_reportable ? " " : "", + reason ? : _("(no description)")); + +- dd_close(dd); + newtWinMessage(_("Error"), _("Ok"), (char *)"%s", t); + free(t); ++ free(not_reportable); ++ free(reason); + + if (get_global_stop_on_not_reportable()) ++ { ++ dd_close(dd); + return -1; ++ } + } + + dd_close(dd); +-- +1.8.3.1 + diff --git a/SOURCES/1000-bugzilla-port-to-Problem-Format-API.patch b/SOURCES/1000-bugzilla-port-to-Problem-Format-API.patch deleted file mode 100644 index 5ab0a57..0000000 --- a/SOURCES/1000-bugzilla-port-to-Problem-Format-API.patch +++ /dev/null @@ -1,781 +0,0 @@ -From ff32b9ee7a7e396e33f1e9aeaa5bafd26ccbb273 Mon Sep 17 00:00:00 2001 -From: Jakub Filak <jfilak@redhat.com> -Date: Thu, 4 Dec 2014 08:45:07 +0100 -Subject: [PATCH 1000/1015] bugzilla: port to Problem Format API - -Related to #303 - -Signed-off-by: Jakub Filak <jfilak@redhat.com> ---- - src/plugins/reporter-bugzilla.c | 691 ++++------------------------------------ - 1 file changed, 59 insertions(+), 632 deletions(-) - -diff --git a/src/plugins/reporter-bugzilla.c b/src/plugins/reporter-bugzilla.c -index fbe7873..9ff3df3 100644 ---- a/src/plugins/reporter-bugzilla.c -+++ b/src/plugins/reporter-bugzilla.c -@@ -17,515 +17,11 @@ - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - #include "internal_libreport.h" -+#include "problem_report.h" - #include "client.h" - #include "abrt_xmlrpc.h" - #include "rhbz.h" - --#include <satyr/stacktrace.h> --#include <satyr/abrt.h> -- --struct section_t { -- char *name; -- GList *items; --}; --typedef struct section_t section_t; -- -- --/* Utility functions */ -- --static --GList* split_string_on_char(const char *str, char ch) --{ -- GList *list = NULL; -- for (;;) -- { -- const char *delim = strchrnul(str, ch); -- list = g_list_prepend(list, xstrndup(str, delim - str)); -- if (*delim == '\0') -- break; -- str = delim + 1; -- } -- return g_list_reverse(list); --} -- --static --int compare_item_name(const char *lookup, const char *name) --{ -- if (lookup[0] == '-') -- lookup++; -- else if (strncmp(lookup, "%bare_", 6) == 0) -- lookup += 6; -- return strcmp(lookup, name); --} -- --static --int is_item_name_in_section(const section_t *lookup, const char *name) --{ -- if (g_list_find_custom(lookup->items, name, (GCompareFunc)compare_item_name)) -- return 0; /* "found it!" */ -- return 1; --} -- --static --bool is_explicit_or_forbidden(const char *name, GList *comment_fmt_spec) --{ -- return g_list_find_custom(comment_fmt_spec, name, (GCompareFunc)is_item_name_in_section); --} -- --static --GList* load_bzrep_conf_file(const char *path) --{ -- FILE *fp = stdin; -- if (strcmp(path, "-") != 0) -- { -- fp = fopen(path, "r"); -- if (!fp) -- return NULL; -- } -- -- GList *sections = NULL; -- -- char *line; -- while ((line = xmalloc_fgetline(fp)) != NULL) -- { -- /* Skip comments */ -- char first = *skip_whitespace(line); -- if (first == '#') -- goto free_line; -- -- /* Handle trailing backslash continuation */ -- check_continuation: ; -- unsigned len = strlen(line); -- if (len && line[len-1] == '\\') -- { -- line[len-1] = '\0'; -- char *next_line = xmalloc_fgetline(fp); -- if (next_line) -- { -- line = append_to_malloced_string(line, next_line); -- free(next_line); -- goto check_continuation; -- } -- } -- -- /* We are reusing line buffer to form temporary -- * "key\0values\0..." in its beginning -- */ -- bool summary_line = false; -- char *value = NULL; -- char *src; -- char *dst; -- for (src = dst = line; *src; src++) -- { -- char c = *src; -- /* did we reach the value list? */ -- if (!value && c == ':' && src[1] == ':') -- { -- *dst++ = '\0'; /* terminate key */ -- src += 2; -- value = dst; /* remember where value starts */ -- summary_line = (strcmp(line, "%summary") == 0); -- if (summary_line) -- { -- value = src; -- break; -- } -- continue; -- } -- /* skip whitespace in value list */ -- if (value && isspace(c)) -- continue; -- *dst++ = c; /* store next key or value char */ -- } -- -- GList *item_list = NULL; -- if (summary_line) -- { -- /* %summary is special */ -- item_list = g_list_append(NULL, xstrdup(skip_whitespace(value))); -- } -- else -- { -- *dst = '\0'; /* terminate value (or key) */ -- if (value) -- item_list = split_string_on_char(value, ','); -- } -- -- section_t *sec = xzalloc(sizeof(*sec)); -- sec->name = xstrdup(line); -- sec->items = item_list; -- sections = g_list_prepend(sections, sec); -- -- free_line: -- free(line); -- } -- -- if (fp != stdin) -- fclose(fp); -- -- return g_list_reverse(sections); --} -- -- --/* Summary generation */ -- --#define MAX_OPT_DEPTH 10 --static --char *format_percented_string(const char *str, problem_data_t *pd) --{ -- size_t old_pos[MAX_OPT_DEPTH] = { 0 }; -- int okay[MAX_OPT_DEPTH] = { 1 }; -- int opt_depth = 1; -- struct strbuf *result = strbuf_new(); -- -- while (*str) { -- switch (*str) { -- default: -- strbuf_append_char(result, *str); -- str++; -- break; -- case '\\': -- if (str[1]) -- str++; -- strbuf_append_char(result, *str); -- str++; -- break; -- case '[': -- if (str[1] == '[' && opt_depth < MAX_OPT_DEPTH) -- { -- old_pos[opt_depth] = result->len; -- okay[opt_depth] = 1; -- opt_depth++; -- str += 2; -- } else { -- strbuf_append_char(result, *str); -- str++; -- } -- break; -- case ']': -- if (str[1] == ']' && opt_depth > 1) -- { -- opt_depth--; -- if (!okay[opt_depth]) -- { -- result->len = old_pos[opt_depth]; -- result->buf[result->len] = '\0'; -- } -- str += 2; -- } else { -- strbuf_append_char(result, *str); -- str++; -- } -- break; -- case '%': ; -- char *nextpercent = strchr(++str, '%'); -- if (!nextpercent) -- { -- error_msg_and_die("Unterminated %%element%%: '%s'", str - 1); -- } -- -- *nextpercent = '\0'; -- const problem_item *item = problem_data_get_item_or_NULL(pd, str); -- *nextpercent = '%'; -- -- if (item && (item->flags & CD_FLAG_TXT)) -- strbuf_append_str(result, item->content); -- else -- okay[opt_depth - 1] = 0; -- str = nextpercent + 1; -- break; -- } -- } -- -- if (opt_depth > 1) -- { -- error_msg_and_die("Unbalanced [[ ]] bracket"); -- } -- -- if (!okay[0]) -- { -- error_msg("Undefined variable outside of [[ ]] bracket"); -- } -- -- return strbuf_free_nobuf(result); --} -- --static --char *create_summary_string(problem_data_t *pd, GList *comment_fmt_spec) --{ -- GList *l = comment_fmt_spec; -- while (l) -- { -- section_t *sec = l->data; -- l = l->next; -- -- /* Find %summary" */ -- if (strcmp(sec->name, "%summary") != 0) -- continue; -- -- GList *item = sec->items; -- if (!item) -- /* not supposed to happen, there will be at least "" */ -- error_msg_and_die("BUG in %%summary parser"); -- -- const char *str = item->data; -- return format_percented_string(str, pd); -- } -- -- return format_percented_string("%reason%", pd); --} -- -- --/* BZ comment generation */ -- --static --int append_text(struct strbuf *result, const char *item_name, const char *content, bool print_item_name) --{ -- char *eol = strchrnul(content, '\n'); -- if (eol[0] == '\0' || eol[1] == '\0') -- { -- /* one-liner */ -- int pad = 16 - (strlen(item_name) + 2); -- if (pad < 0) -- pad = 0; -- if (print_item_name) -- strbuf_append_strf(result, -- eol[0] == '\0' ? "%s: %*s%s\n" : "%s: %*s%s", -- item_name, pad, "", content -- ); -- else -- strbuf_append_strf(result, -- eol[0] == '\0' ? "%s\n" : "%s", -- content -- ); -- } -- else -- { -- /* multi-line item */ -- if (print_item_name) -- strbuf_append_strf(result, "%s:\n", item_name); -- for (;;) -- { -- eol = strchrnul(content, '\n'); -- strbuf_append_strf(result, -- /* For %bare_multiline_item, we don't want to print colons */ -- (print_item_name ? ":%.*s\n" : "%.*s\n"), -- (int)(eol - content), content -- ); -- if (eol[0] == '\0' || eol[1] == '\0') -- break; -- content = eol + 1; -- } -- } -- return 1; --} -- --static --int append_short_backtrace(struct strbuf *result, problem_data_t *problem_data, size_t max_text_size, bool print_item_name) --{ -- const problem_item *item = problem_data_get_item_or_NULL(problem_data, -- FILENAME_BACKTRACE); -- if (!item) -- return 0; /* "I did not print anything" */ -- if (!(item->flags & CD_FLAG_TXT)) -- return 0; /* "I did not print anything" */ -- -- char *truncated = NULL; -- -- if (strlen(item->content) >= max_text_size) -- { -- char *error_msg = NULL; -- const char *analyzer = problem_data_get_content_or_NULL(problem_data, FILENAME_ANALYZER); -- if (!analyzer) -- return 0; -- -- /* For CCpp crashes, use the GDB-produced backtrace which should be -- * available by now. sr_abrt_type_from_analyzer returns SR_REPORT_CORE -- * by default for CCpp crashes. -- */ -- enum sr_report_type report_type = sr_abrt_type_from_analyzer(analyzer); -- if (strcmp(analyzer, "CCpp") == 0) -- report_type = SR_REPORT_GDB; -- -- struct sr_stacktrace *backtrace = sr_stacktrace_parse(report_type, -- item->content, &error_msg); -- -- if (!backtrace) -- { -- log(_("Can't parse backtrace: %s"), error_msg); -- free(error_msg); -- return 0; -- } -- -- /* Get optimized thread stack trace for 10 top most frames */ -- truncated = sr_stacktrace_to_short_text(backtrace, 10); -- sr_stacktrace_free(backtrace); -- -- if (!truncated) -- { -- log(_("Can't generate stacktrace description (no crash thread?)")); -- return 0; -- } -- } -- -- append_text(result, -- /*item_name:*/ truncated ? "truncated_backtrace" : FILENAME_BACKTRACE, -- /*content:*/ truncated ? truncated : item->content, -- print_item_name -- ); -- free(truncated); -- return 1; --} -- --static --int append_item(struct strbuf *result, const char *item_name, problem_data_t *pd, GList *comment_fmt_spec) --{ -- bool print_item_name = (strncmp(item_name, "%bare_", strlen("%bare_")) != 0); -- if (!print_item_name) -- item_name += strlen("%bare_"); -- -- if (item_name[0] != '%') -- { -- struct problem_item *item = problem_data_get_item_or_NULL(pd, item_name); -- if (!item) -- return 0; /* "I did not print anything" */ -- if (!(item->flags & CD_FLAG_TXT)) -- return 0; /* "I did not print anything" */ -- -- char *formatted = problem_item_format(item); -- char *content = formatted ? formatted : item->content; -- append_text(result, item_name, content, print_item_name); -- free(formatted); -- return 1; /* "I printed something" */ -- } -- -- /* Special item name */ -- -- /* Compat with previously-existed ad-hockery: %short_backtrace */ -- if (strcmp(item_name, "%short_backtrace") == 0) -- return append_short_backtrace(result, pd, CD_TEXT_ATT_SIZE_BZ, print_item_name); -- -- /* Compat with previously-existed ad-hockery: %reporter */ -- if (strcmp(item_name, "%reporter") == 0) -- return append_text(result, "reporter", PACKAGE"-"VERSION, print_item_name); -- -- /* %oneline,%multiline,%text */ -- bool oneline = (strcmp(item_name+1, "oneline" ) == 0); -- bool multiline = (strcmp(item_name+1, "multiline") == 0); -- bool text = (strcmp(item_name+1, "text" ) == 0); -- if (!oneline && !multiline && !text) -- { -- log("Unknown or unsupported element specifier '%s'", item_name); -- return 0; /* "I did not print anything" */ -- } -- -- int printed = 0; -- -- /* Iterate over _sorted_ items */ -- GList *sorted_names = g_hash_table_get_keys(pd); -- sorted_names = g_list_sort(sorted_names, (GCompareFunc)strcmp); -- -- /* %text => do as if %oneline, then repeat as if %multiline */ -- if (text) -- oneline = 1; -- -- again: ; -- GList *l = sorted_names; -- while (l) -- { -- const char *name = l->data; -- l = l->next; -- struct problem_item *item = g_hash_table_lookup(pd, name); -- if (!item) -- continue; /* paranoia, won't happen */ -- -- if (!(item->flags & CD_FLAG_TXT)) -- continue; -- -- if (is_explicit_or_forbidden(name, comment_fmt_spec)) -- continue; -- -- char *formatted = problem_item_format(item); -- char *content = formatted ? formatted : item->content; -- char *eol = strchrnul(content, '\n'); -- bool is_oneline = (eol[0] == '\0' || eol[1] == '\0'); -- if (oneline == is_oneline) -- printed |= append_text(result, name, content, print_item_name); -- free(formatted); -- } -- if (text && oneline) -- { -- /* %text, and we just did %oneline. Repeat as if %multiline */ -- oneline = 0; -- /*multiline = 1; - not checked in fact, so why bother setting? */ -- goto again; -- } -- -- g_list_free(sorted_names); /* names themselves are not freed */ -- -- return printed; --} -- --static --void generate_bz_comment(struct strbuf *result, problem_data_t *pd, GList *comment_fmt_spec) --{ -- bool last_line_is_empty = true; -- GList *l = comment_fmt_spec; -- while (l) -- { -- section_t *sec = l->data; -- l = l->next; -- -- /* Skip special sections such as "%attach" */ -- if (sec->name[0] == '%') -- continue; -- -- if (sec->items) -- { -- /* "Text: item[,item]..." */ -- struct strbuf *output = strbuf_new(); -- GList *item = sec->items; -- while (item) -- { -- const char *str = item->data; -- item = item->next; -- if (str[0] == '-') /* "-name", ignore it */ -- continue; -- append_item(output, str, pd, comment_fmt_spec); -- } -- -- if (output->len != 0) -- { -- strbuf_append_strf(result, -- sec->name[0] ? "%s:\n%s" : "%s%s", -- sec->name, -- output->buf -- ); -- last_line_is_empty = false; -- } -- strbuf_free(output); -- } -- else -- { -- /* Just "Text" (can be "") */ -- -- /* Filter out consecutive empty lines */ -- if (sec->name[0] != '\0' || !last_line_is_empty) -- strbuf_append_strf(result, "%s\n", sec->name); -- last_line_is_empty = (sec->name[0] == '\0'); -- } -- } -- -- /* Nuke any trailing empty lines */ -- while (result->len >= 1 -- && result->buf[result->len-1] == '\n' -- && (result->len == 1 || result->buf[result->len-2] == '\n') -- ) { -- result->buf[--result->len] = '\0'; -- } --} -- -- - /* BZ attachments */ - - static -@@ -573,104 +69,6 @@ int attach_file_item(struct abrt_xmlrpc *ax, const char *bug_id, - return (r == 0); - } - --static --int attach_item(struct abrt_xmlrpc *ax, const char *bug_id, -- const char *item_name, problem_data_t *pd, GList *comment_fmt_spec) --{ -- if (item_name[0] != '%') -- { -- struct problem_item *item = problem_data_get_item_or_NULL(pd, item_name); -- if (!item) -- return 0; -- if (item->flags & CD_FLAG_TXT) -- return attach_text_item(ax, bug_id, item_name, item); -- if (item->flags & CD_FLAG_BIN) -- return attach_file_item(ax, bug_id, item_name, item); -- return 0; -- } -- -- /* Special item name */ -- -- /* %oneline,%multiline,%text,%binary */ -- bool oneline = (strcmp(item_name+1, "oneline" ) == 0); -- bool multiline = (strcmp(item_name+1, "multiline") == 0); -- bool text = (strcmp(item_name+1, "text" ) == 0); -- bool binary = (strcmp(item_name+1, "binary" ) == 0); -- if (!oneline && !multiline && !text && !binary) -- { -- log("Unknown or unsupported element specifier '%s'", item_name); -- return 0; -- } -- -- log_debug("Special item_name '%s', iterating for attach...", item_name); -- int done = 0; -- -- /* Iterate over _sorted_ items */ -- GList *sorted_names = g_hash_table_get_keys(pd); -- sorted_names = g_list_sort(sorted_names, (GCompareFunc)strcmp); -- -- GList *l = sorted_names; -- while (l) -- { -- const char *name = l->data; -- l = l->next; -- struct problem_item *item = g_hash_table_lookup(pd, name); -- if (!item) -- continue; /* paranoia, won't happen */ -- -- if (is_explicit_or_forbidden(name, comment_fmt_spec)) -- continue; -- -- if ((item->flags & CD_FLAG_TXT) && !binary) -- { -- char *content = item->content; -- char *eol = strchrnul(content, '\n'); -- bool is_oneline = (eol[0] == '\0' || eol[1] == '\0'); -- if (text || oneline == is_oneline) -- done |= attach_text_item(ax, bug_id, name, item); -- } -- if ((item->flags & CD_FLAG_BIN) && binary) -- done |= attach_file_item(ax, bug_id, name, item); -- } -- -- g_list_free(sorted_names); /* names themselves are not freed */ -- -- -- log_debug("...Done iterating over '%s' for attach", item_name); -- -- return done; --} -- --static --int attach_files(struct abrt_xmlrpc *ax, const char *bug_id, -- problem_data_t *pd, GList *comment_fmt_spec) --{ -- int done = 0; -- GList *l = comment_fmt_spec; -- while (l) -- { -- section_t *sec = l->data; -- l = l->next; -- -- /* Find %attach" */ -- if (strcmp(sec->name, "%attach") != 0) -- continue; -- -- GList *item = sec->items; -- while (item) -- { -- const char *str = item->data; -- item = item->next; -- if (str[0] == '-') /* "-name", ignore it */ -- continue; -- done |= attach_item(ax, bug_id, str, pd, comment_fmt_spec); -- } -- } -- -- return done; --} -- -- - /* Main */ - - struct bugzilla_struct { -@@ -1103,18 +501,29 @@ int main(int argc, char **argv) - - if (opts & OPT_D) - { -- GList *comment_fmt_spec = load_bzrep_conf_file(fmt_file); -- struct strbuf *bzcomment_buf = strbuf_new(); -- generate_bz_comment(bzcomment_buf, problem_data, comment_fmt_spec); -- char *bzcomment = strbuf_free_nobuf(bzcomment_buf); -- char *summary = create_summary_string(problem_data, comment_fmt_spec); -+ problem_formatter_t *pf = problem_formatter_new(); -+ -+ 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 bug report from problem data"); -+ - printf("summary: %s\n" - "\n" - "%s" -- , summary, bzcomment -+ "\n" -+ , problem_report_get_summary(pr) -+ , problem_report_get_description(pr) - ); -- free(bzcomment); -- free(summary); -+ -+ 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); - } - -@@ -1227,22 +636,29 @@ int main(int argc, char **argv) - /* Create new bug */ - log(_("Creating a new bug")); - -- GList *comment_fmt_spec = load_bzrep_conf_file(fmt_file); -+ problem_formatter_t *pf = problem_formatter_new(); -+ -+ 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"); - -- struct strbuf *bzcomment_buf = strbuf_new(); -- generate_bz_comment(bzcomment_buf, problem_data, comment_fmt_spec); - if (crossver_id >= 0) -- strbuf_append_strf(bzcomment_buf, "\nPotential duplicate: bug %u\n", crossver_id); -- char *bzcomment = strbuf_free_nobuf(bzcomment_buf); -- char *summary = create_summary_string(problem_data, comment_fmt_spec); -+ problem_report_buffer_printf( -+ problem_report_get_buffer(pr, PR_SEC_DESCRIPTION), -+ "\nPotential duplicate: bug %u\n", crossver_id); -+ -+ problem_formatter_free(pf); -+ - int new_id = rhbz_new_bug(client, - problem_data, rhbz.b_product, rhbz.b_product_version, -- summary, bzcomment, -+ problem_report_get_summary(pr), -+ problem_report_get_description(pr), - rhbz.b_create_private, - rhbz.b_private_groups - ); -- free(bzcomment); -- free(summary); - - if (new_id == -1) - { -@@ -1267,9 +683,17 @@ int main(int argc, char **argv) - char new_id_str[sizeof(int)*3 + 2]; - sprintf(new_id_str, "%i", new_id); - -- attach_files(client, new_id_str, problem_data, comment_fmt_spec); -- --//TODO: free_comment_fmt_spec(comment_fmt_spec); -+ 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) -+ attach_text_item(client, new_id_str, item_name, item); -+ else if (item->flags & CD_FLAG_BIN) -+ attach_file_item(client, new_id_str, item_name, item); -+ } - - bz = new_bug_info(); - bz->bi_status = xstrdup("NEW"); -@@ -1340,18 +764,21 @@ int main(int argc, char **argv) - const char *comment = problem_data_get_content_or_NULL(problem_data, FILENAME_COMMENT); - if (comment && comment[0]) - { -- GList *comment_fmt_spec = load_bzrep_conf_file(fmt_file2); -- struct strbuf *bzcomment_buf = strbuf_new(); -- generate_bz_comment(bzcomment_buf, problem_data, comment_fmt_spec); -- char *bzcomment = strbuf_free_nobuf(bzcomment_buf); --//TODO: free_comment_fmt_spec(comment_fmt_spec); -+ 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 *bzcomment = problem_report_get_description(pr); - - int dup_comment = is_comment_dup(bz->bi_comments, bzcomment); - if (!dup_comment) - { - log(_("Adding new comment to bug %d"), bz->bi_id); - rhbz_add_comment(client, bz->bi_id, bzcomment, 0); -- free(bzcomment); - - const char *bt = problem_data_get_content_or_NULL(problem_data, FILENAME_BACKTRACE); - unsigned rating = 0; -@@ -1369,10 +796,10 @@ int main(int argc, char **argv) - } - } - else -- { -- free(bzcomment); - log(_("Found the same comment in the bug history, not adding a new one")); -- } -+ -+ problem_report_free(pr); -+ problem_formatter_free(pf); - } - - log_out: --- -1.8.3.1 - diff --git a/SOURCES/1001-lib-created-a-new-lib-file-for-reporters.patch b/SOURCES/1001-lib-created-a-new-lib-file-for-reporters.patch deleted file mode 100644 index d0e74f9..0000000 --- a/SOURCES/1001-lib-created-a-new-lib-file-for-reporters.patch +++ /dev/null @@ -1,405 +0,0 @@ -From 8d9f7ba8a42ec1cfd39e6c249aef15e9295fe0a1 Mon Sep 17 00:00:00 2001 -From: Matej Habrnal <mhabrnal@redhat.com> -Date: Tue, 13 Jan 2015 19:23:08 -0500 -Subject: [PATCH 1001/1015] lib: created a new lib file for reporters - -Moved some functions from rhbz.c to src/lib/reporters.c and src/lib/strbuf.c. - -Related to #272 - -Signed-off-by: Matej Habrnal <mhabrnal@redhat.com> ---- - po/POTFILES.in | 1 + - src/include/Makefile.am | 3 +- - src/include/internal_libreport.h | 5 +++ - src/include/reporters.h | 36 ++++++++++++++++++ - src/lib/Makefile.am | 3 +- - src/lib/reporters.c | 80 ++++++++++++++++++++++++++++++++++++++++ - src/lib/strbuf.c | 60 ++++++++++++++++++++++++++++++ - src/plugins/rhbz.c | 78 +-------------------------------------- - src/plugins/rhbz.h | 2 - - 9 files changed, 188 insertions(+), 80 deletions(-) - create mode 100644 src/include/reporters.h - create mode 100644 src/lib/reporters.c - -diff --git a/po/POTFILES.in b/po/POTFILES.in -index 4246e06..003e686 100644 ---- a/po/POTFILES.in -+++ b/po/POTFILES.in -@@ -27,6 +27,7 @@ src/lib/parse_options.c - src/lib/problem_data.c - src/lib/problem_report.c - src/lib/reported_to.c -+src/lib/reporters.c - src/lib/run_event.c - src/plugins/abrt_rh_support.c - src/plugins/report_Bugzilla.xml.in -diff --git a/src/include/Makefile.am b/src/include/Makefile.am -index 87e5e60..4d8c6a5 100644 ---- a/src/include/Makefile.am -+++ b/src/include/Makefile.am -@@ -15,7 +15,8 @@ libreport_include_HEADERS = \ - file_obj.h \ - internal_libreport.h \ - internal_abrt_dbus.h \ -- xml_parser.h -+ xml_parser.h \ -+ reporters.h - - if BUILD_UREPORT - libreport_include_HEADERS += ureport.h -diff --git a/src/include/internal_libreport.h b/src/include/internal_libreport.h -index cf5730c..a867649 100644 ---- a/src/include/internal_libreport.h -+++ b/src/include/internal_libreport.h -@@ -98,6 +98,7 @@ int vdprintf(int d, const char *format, va_list ap); - #include "workflow.h" - #include "file_obj.h" - #include "libreport_types.h" -+#include "reporters.h" - - #ifdef __cplusplus - extern "C" { -@@ -107,6 +108,10 @@ extern "C" { - int prefixcmp(const char *str, const char *prefix); - #define suffixcmp libreport_suffixcmp - int suffixcmp(const char *str, const char *suffix); -+#define trim_all_whitespace libreport_trim_all_whitespace -+char *trim_all_whitespace(const char *str); -+#define shorten_string_to_length libreport_shorten_string_to_length -+char *shorten_string_to_length(const char *str, unsigned length); - #define strtrim libreport_strtrim - char *strtrim(char *str); - #define strtrimch libreport_strtrimch -diff --git a/src/include/reporters.h b/src/include/reporters.h -new file mode 100644 -index 0000000..d415b7f ---- /dev/null -+++ b/src/include/reporters.h -@@ -0,0 +1,36 @@ -+/* -+ 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 REPORTERS_H -+#define REPORTERS_H -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+#define is_comment_dup libreport_is_comment_dup -+int is_comment_dup(GList *comments, const char *comment); -+#define comments_find_best_bt_rating libreport_comments_find_best_bt_rating -+unsigned comments_find_best_bt_rating(GList *comments); -+ -+#ifdef __cplusplus -+} -+#endif -+ -+#endif -diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am -index c11a42d..d41e543 100644 ---- a/src/lib/Makefile.am -+++ b/src/lib/Makefile.am -@@ -58,7 +58,8 @@ libreport_la_SOURCES = \ - xml_parser.c \ - libreport_init.c \ - global_configuration.c \ -- uriparser.c -+ uriparser.c \ -+ reporters.c - - libreport_la_CPPFLAGS = \ - -I$(srcdir)/../include \ -diff --git a/src/lib/reporters.c b/src/lib/reporters.c -new file mode 100644 -index 0000000..e3305ca ---- /dev/null -+++ b/src/lib/reporters.c -@@ -0,0 +1,80 @@ -+/* -+ String buffer implementation -+ -+ Copyright (C) 2015 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" -+ -+int -+is_comment_dup(GList *comments, const char *comment) -+{ -+ char * const trim_comment = trim_all_whitespace(comment); -+ bool same_comments = false; -+ -+ for (GList *l = comments; l && !same_comments; l = l->next) -+ { -+ const char * const comment_body = (const char *) l->data; -+ char * const trim_comment_body = trim_all_whitespace(comment_body); -+ same_comments = (strcmp(trim_comment_body, trim_comment) == 0); -+ free(trim_comment_body); -+ } -+ -+ free(trim_comment); -+ return same_comments; -+} -+ -+unsigned -+comments_find_best_bt_rating(GList *comments) -+{ -+ if (comments == NULL) -+ return 0; -+ -+ unsigned best_rating = 0; -+ for (GList *l = comments; l; l = l->next) -+ { -+ char *comment_body = (char *) l->data; -+ -+ char *start_rating_line = strstr(comment_body, FILENAME_RATING": "); -+ if (!start_rating_line) -+ { -+ log_debug(_("Note does not contain rating")); -+ continue; -+ } -+ -+ start_rating_line += strlen(FILENAME_RATING": "); -+ -+ errno = 0; -+ char *e; -+ long rating = strtoul(start_rating_line, &e, 10); -+ /* -+ * Note: we intentionally check for '\n'. Any other terminator -+ * (even '\0') is not ok in this case. -+ */ -+ if (errno || e == start_rating_line || (*e != '\n' && *e != '\r') || (unsigned long)rating > UINT_MAX) -+ { -+ /* error / no digits / illegal trailing chars */ -+ continue; -+ } -+ -+ if (rating > best_rating) -+ best_rating = rating; -+ } -+ -+ return best_rating; -+} -+ -diff --git a/src/lib/strbuf.c b/src/lib/strbuf.c -index ef8bda8..f0cd1b8 100644 ---- a/src/lib/strbuf.c -+++ b/src/lib/strbuf.c -@@ -37,6 +37,66 @@ int suffixcmp(const char *str, const char *suffix) - return strcmp(str + len_minus_suflen, suffix); - } - -+char *trim_all_whitespace(const char *str) -+{ -+ char *trim = xzalloc(sizeof(char) * strlen(str) + 1); -+ int i = 0; -+ while (*str) -+ { -+ if (!isspace(*str)) -+ trim[i++] = *str; -+ str++; -+ } -+ -+ return trim; -+} -+ -+/* If str is longer than max allowed length then -+ * try to find first ' ' from the end of acceptable long str string -+ * -+ * If ' ' is found replace string after that by "..." -+ * -+ * If ' ' is NOT found in maximal allowed range, cut str string on -+ * lenght (MAX_SUMMARY_LENGTH - strlen("...")) and append "..." -+ * -+ * If MAX_LENGTH is 15 and max allowed cut is 5: -+ * -+ * 0123456789ABCDEF -> 0123456789AB... -+ * 0123456789 BCDEF -> 0123456789 ... -+ * 012345 789ABCDEF -> 012345 789AB... -+ */ -+char * -+shorten_string_to_length(const char *str, unsigned length) -+{ -+ char *dup_str = xstrdup(str); -+ if (strlen(str) > length) -+ { -+ char *max_end = dup_str + (length - strlen("...")); -+ -+ /* maximal number of characters to cut due to attempt cut dup_str -+ * string after last ' ' -+ */ -+ int max_cut = 16; -+ -+ /* start looking for ' ' one char before the last possible character */ -+ char *buf = max_end - 1; -+ while (buf[0] != ' ' && max_cut--) -+ --buf; -+ -+ if (buf[0] != ' ') -+ buf = max_end; -+ else -+ ++buf; -+ -+ buf[0] = '.'; -+ buf[1] = '.'; -+ buf[2] = '.'; -+ buf[3] = '\0'; -+ } -+ -+ return dup_str; -+} -+ - /* - * Trims whitespace characters both from left and right side of a string. - * Modifies the string in-place. Returns the trimmed string. -diff --git a/src/plugins/rhbz.c b/src/plugins/rhbz.c -index a227c62..fdcfff9 100644 ---- a/src/plugins/rhbz.c -+++ b/src/plugins/rhbz.c -@@ -133,41 +133,6 @@ static GList *rhbz_comments(struct abrt_xmlrpc *ax, int bug_id) - return g_list_reverse(comments); - } - --static char *trim_all_whitespace(const char *str) --{ -- func_entry(); -- -- char *trim = xzalloc(sizeof(char) * strlen(str) + 1); -- int i = 0; -- while (*str) -- { -- if (!isspace(*str)) -- trim[i++] = *str; -- str++; -- } -- -- return trim; --} -- --int is_comment_dup(GList *comments, const char *comment) --{ -- func_entry(); -- -- char * const trim_comment = trim_all_whitespace(comment); -- bool same_comments = false; -- -- for (GList *l = comments; l && !same_comments; l = l->next) -- { -- const char * const comment_body = (const char *) l->data; -- char * const trim_comment_body = trim_all_whitespace(comment_body); -- same_comments = (strcmp(trim_comment_body, trim_comment) == 0); -- free(trim_comment_body); -- } -- -- free(trim_comment); -- return same_comments; --} -- - static unsigned find_best_bt_rating_in_comments(GList *comments) - { - func_entry(); -@@ -553,46 +518,7 @@ int rhbz_new_bug(struct abrt_xmlrpc *ax, - if (!duphash) duphash = problem_data_get_content_or_NULL(problem_data, - "global_uuid"); - -- /* If summary is longer than max allowed summary length then -- * try to find first ' ' from the end of acceptable long summary string -- * -- * If ' ' is found replace string after that by "..." -- * -- * If ' ' is NOT found in maximal allowed range, cut summary string on -- * lenght (MAX_SUMMARY_LENGTH - strlen("...")) and append "..." -- * -- * If MAX_SUMMARY_LENGTH is 15 and max allowed cut is 5: -- * -- * 0123456789ABCDEF -> 0123456789AB... -- * 0123456789 BCDEF -> 0123456789 ... -- * 012345 789ABCDEF -> 012345 789AB... -- */ -- char *summary = NULL; -- if (strlen(bzsummary) > MAX_SUMMARY_LENGTH) -- { -- summary = xstrdup(bzsummary); -- char *max_end = summary + (MAX_SUMMARY_LENGTH - strlen("...")); -- -- /* maximal number of characters to cut due to attempt cut summary -- * string after last ' ' -- */ -- int max_cut = 16; -- -- /* start looking for ' ' one char before the last possible character */ -- char *buf = max_end - 1; -- while (buf[0] != ' ' && max_cut--) -- --buf; -- -- if (buf[0] != ' ') -- buf = max_end; -- else -- ++buf; -- -- buf[0] = '.'; -- buf[1] = '.'; -- buf[2] = '.'; -- buf[3] = '\0'; -- } -+ char *summary = shorten_string_to_length(bzsummary, MAX_SUMMARY_LENGTH); - - char *status_whiteboard = xasprintf("abrt_hash:%s", duphash); - -@@ -604,7 +530,7 @@ int rhbz_new_bug(struct abrt_xmlrpc *ax, - abrt_xmlrpc_params_add_string(&env, params, "product", product); - abrt_xmlrpc_params_add_string(&env, params, "component", component); - abrt_xmlrpc_params_add_string(&env, params, "version", version); -- abrt_xmlrpc_params_add_string(&env, params, "summary", (summary ? summary : bzsummary)); -+ abrt_xmlrpc_params_add_string(&env, params, "summary", summary); - abrt_xmlrpc_params_add_string(&env, params, "description", bzcomment); - abrt_xmlrpc_params_add_string(&env, params, "status_whiteboard", status_whiteboard); - -diff --git a/src/plugins/rhbz.h b/src/plugins/rhbz.h -index 15e7699..86632a3 100644 ---- a/src/plugins/rhbz.h -+++ b/src/plugins/rhbz.h -@@ -105,8 +105,6 @@ int rhbz_attach_blob(struct abrt_xmlrpc *ax, const char *bug_id, - int rhbz_attach_fd(struct abrt_xmlrpc *ax, const char *bug_id, - const char *att_name, int fd, int flags); - --int is_comment_dup(GList *comments, const char *comment); -- - GList *rhbz_bug_cc(xmlrpc_value *result_xml); - - struct bug_info *rhbz_bug_info(struct abrt_xmlrpc *ax, int bug_id); --- -1.8.3.1 - diff --git a/SOURCES/1003-ureport-set-url-to-public-faf-server.patch b/SOURCES/1003-ureport-set-url-to-public-faf-server.patch deleted file mode 100644 index ee9a33c..0000000 --- a/SOURCES/1003-ureport-set-url-to-public-faf-server.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 2221fee2d3b930f171fa5181439ded9a1748ff00 Mon Sep 17 00:00:00 2001 -From: Matej Habrnal <mhabrnal@redhat.com> -Date: Mon, 2 Feb 2015 16:31:51 +0100 -Subject: [PATCH 1003/1015] ureport: set url to public faf server - -Set url to public faf server because the private one doesn't return URL to -known issues, bthash, possible solution etc. - -Signed-off-by: Matej Habrnal <mhabrnal@redhat.com> ---- - src/plugins/ureport.conf | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/plugins/ureport.conf b/src/plugins/ureport.conf -index 2256a7f..b82b0e1 100644 ---- a/src/plugins/ureport.conf -+++ b/src/plugins/ureport.conf -@@ -1,5 +1,5 @@ - # Base URL to uReport server --# URL = http://bug-report.itos.redhat.com -+URL = https://retrace.fedoraproject.org/faf - - # no means that ssl certificates will not be checked - # SSLVerify = no --- -1.8.3.1 - diff --git a/SOURCES/1004-conf-changed-URL-for-sending-uReport.patch b/SOURCES/1004-conf-changed-URL-for-sending-uReport.patch deleted file mode 100644 index f0386e8..0000000 --- a/SOURCES/1004-conf-changed-URL-for-sending-uReport.patch +++ /dev/null @@ -1,29 +0,0 @@ -From e8bb90e42d0356cdcf91d22c9deb6635d1d3c376 Mon Sep 17 00:00:00 2001 -From: Matej Habrnal <mhabrnal@redhat.com> -Date: Mon, 2 Feb 2015 21:41:36 +0100 -Subject: [PATCH 1004/1015] conf: changed URL for sending uReport - -Changed faf server url in report_uReport.xml.in. -uReports are sending to https://retrace.fedoraproject.org/faf by default. - -Signed-off-by: Matej Habrnal <mhabrnal@redhat.com> ---- - src/plugins/report_uReport.xml.in | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/plugins/report_uReport.xml.in b/src/plugins/report_uReport.xml.in -index b997851..eca30e0 100644 ---- a/src/plugins/report_uReport.xml.in -+++ b/src/plugins/report_uReport.xml.in -@@ -12,7 +12,7 @@ - <_label>uReport Server URL</_label> - <allow-empty>no</allow-empty> - <_description>Address of uReport webservice</_description> -- <default-value>http://bug-report.itos.redhat.com</default-value> -+ <default-value>https://retrace.fedoraproject.org/faf</default-value> - </option> - <option type="text" name="uReport_ContactEmail"> - <_label>Contact email address</_label> --- -1.8.3.1 - diff --git a/SOURCES/1005-reporter-mantisbt-first-version-of-the-reporter-mant.patch b/SOURCES/1005-reporter-mantisbt-first-version-of-the-reporter-mant.patch deleted file mode 100644 index b267fe4..0000000 --- a/SOURCES/1005-reporter-mantisbt-first-version-of-the-reporter-mant.patch +++ /dev/null @@ -1,2659 +0,0 @@ -From be6691dc649962cad24ad7d7a89451b2c43de606 Mon Sep 17 00:00:00 2001 -From: Matej Habrnal <mhabrnal@redhat.com> -Date: Tue, 13 Jan 2015 19:30:27 -0500 -Subject: [PATCH 1005/1015] 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 a7f67c9..83c1a52 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -144,6 +144,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 003e686..9cf8f72 100644 ---- a/po/POTFILES.in -+++ b/po/POTFILES.in -@@ -39,6 +39,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 -@@ -47,12 +49,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 8e1a166..fd3f477 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 \ -@@ -54,6 +65,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 -@@ -79,7 +96,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 -@@ -127,6 +145,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 72502ca..7ecb34e 100644 ---- a/src/workflows/Makefile.am -+++ b/src/workflows/Makefile.am -@@ -23,6 +23,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 \ -@@ -46,7 +58,8 @@ dist_workflowsdef_DATA =\ - report_uReport.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 - diff --git a/SOURCES/1007-reporter-mantisbt-change-default-formating-file-for-.patch b/SOURCES/1007-reporter-mantisbt-change-default-formating-file-for-.patch deleted file mode 100644 index 6f1777a..0000000 --- a/SOURCES/1007-reporter-mantisbt-change-default-formating-file-for-.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 8d54f840cdb094ca8e32f02e967393d7498ee26d Mon Sep 17 00:00:00 2001 -From: Matej Habrnal <mhabrnal@redhat.com> -Date: Fri, 20 Feb 2015 00:27:05 +0100 -Subject: [PATCH 1007/1015] reporter-mantisbt: change default formating file - for duplicate issues - -reporter-mantisbt doesn't work well with mantisbt_format.conf as a default -format conf file for creating duplicate issues because the note which is added -doesn't contain an 'Additional information' section. - -Default formating file for duplicate is mantisbt_formatdup.conf - -Signed-off-by: Matej Habrnal <mhabrnal@redhat.com> ---- - src/plugins/reporter-mantisbt.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/src/plugins/reporter-mantisbt.c b/src/plugins/reporter-mantisbt.c -index d281cdb..dc9968f 100644 ---- a/src/plugins/reporter-mantisbt.c -+++ b/src/plugins/reporter-mantisbt.c -@@ -253,7 +253,8 @@ int main(int argc, char **argv) - "\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" -+ "\nFMTFILE default to "CONF_DIR"/plugins/mantisbt_format.conf." -+ "\nFMTFILE2 default to "CONF_DIR"/plugins/mantisbt_formatdup.conf." - ); - - enum { -@@ -272,7 +273,7 @@ int main(int argc, char **argv) - 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; -+ const char *fmt_file2 = CONF_DIR"/plugins/mantisbt_formatdup.conf"; - char *abrt_hash = NULL; - char *ticket_no = NULL; - const char *tracker_str = "ABRT Server"; --- -1.8.3.1 - diff --git a/SOURCES/1009-reporter-mantisbt-adds-man-pages-for-reporter-mantis.patch b/SOURCES/1009-reporter-mantisbt-adds-man-pages-for-reporter-mantis.patch deleted file mode 100644 index efb3a9f..0000000 --- a/SOURCES/1009-reporter-mantisbt-adds-man-pages-for-reporter-mantis.patch +++ /dev/null @@ -1,528 +0,0 @@ -From 9c819b8c77ea5c05e3a37117a2174162160b1c57 Mon Sep 17 00:00:00 2001 -From: Matej Habrnal <mhabrnal@redhat.com> -Date: Fri, 20 Feb 2015 03:14:54 +0100 -Subject: [PATCH 1009/1015] reporter-mantisbt: adds man pages for - reporter-mantisbt - -Signed-off-by: Matej Habrnal <mhabrnal@redhat.com> - -Conflicts: - doc/Makefile.am ---- - doc/Makefile.am | 13 ++- - doc/centos_report_event.conf.5 | 1 + - doc/mantisbt.conf.txt | 18 +++ - doc/mantisbt_format.conf.txt | 18 +++ - doc/mantisbt_formatdup.conf.txt | 18 +++ - doc/report_CentOSBugTracker.conf.txt | 45 +++++++ - doc/report_centos.conf.txt | 41 +++++++ - doc/reporter-mantisbt.txt | 219 +++++++++++++++++++++++++++++++++++ - src/plugins/Makefile.am | 4 + - src/workflows/Makefile.am | 12 ++ - 10 files changed, 387 insertions(+), 2 deletions(-) - create mode 100644 doc/centos_report_event.conf.5 - create mode 100644 doc/mantisbt.conf.txt - create mode 100644 doc/mantisbt_format.conf.txt - create mode 100644 doc/mantisbt_formatdup.conf.txt - create mode 100644 doc/report_CentOSBugTracker.conf.txt - create mode 100644 doc/report_centos.conf.txt - create mode 100644 doc/reporter-mantisbt.txt - -diff --git a/doc/Makefile.am b/doc/Makefile.am -index 83a41d9..e437388 100644 ---- a/doc/Makefile.am -+++ b/doc/Makefile.am -@@ -25,6 +25,7 @@ MAN1_TXT += reporter-print.txt - MAN1_TXT += reporter-rhtsupport.txt - MAN1_TXT += reporter-upload.txt - MAN1_TXT += reporter-ureport.txt -+MAN1_TXT += reporter-mantisbt.txt - - MAN5_TXT = - MAN5_TXT += anaconda_event.conf.txt -@@ -37,6 +38,9 @@ MAN5_TXT += bugzilla_formatdup_anaconda.conf.txt - MAN5_TXT += bugzilla_formatdup.conf.txt - MAN5_TXT += bugzilla_format_kernel.conf.txt - MAN5_TXT += bugzilla_format_libreport.conf.txt -+MAN5_TXT += mantisbt.conf.txt -+MAN5_TXT += mantisbt_format.conf.txt -+MAN5_TXT += mantisbt_formatdup.conf.txt - MAN5_TXT += emergencyanalysis_event.conf.txt - MAN5_TXT += forbidden_words.conf.txt - MAN5_TXT += mailx.conf.txt -@@ -45,6 +49,7 @@ MAN5_TXT += print_event.conf.txt - MAN5_TXT += report_Bugzilla.conf.txt - MAN5_TXT += report_event.conf.txt - MAN5_TXT += report_fedora.conf.txt -+MAN5_TXT += report_centos.conf.txt - MAN5_TXT += report_Logger.conf.txt - MAN5_TXT += report_rhel.conf.txt - MAN5_TXT += report_rhel_bugzilla.conf.txt -@@ -53,15 +58,19 @@ MAN5_TXT += report_logger.conf.txt - MAN5_TXT += report_mailx.conf.txt - MAN5_TXT += report_uploader.conf.txt - MAN5_TXT += report_Uploader.conf.txt -+MAN5_TXT += report_CentOSBugTracker.conf.txt - MAN5_TXT += rhtsupport.conf.txt - MAN5_TXT += rhtsupport_event.conf.txt - MAN5_TXT += uploader_event.conf.txt - MAN5_TXT += ureport.conf.txt - MAN5_TXT += upload.conf.txt - -+MAN5_PREFORMATTED = -+MAN5_PREFORMATTED += centos_report_event.conf.5 -+ - # Manual pages are generated from .txt via Docbook - man1_MANS = ${MAN1_TXT:%.txt=%.1} --man5_MANS = ${MAN5_TXT:%.txt=%.5} -+man5_MANS = ${MAN5_TXT:%.txt=%.5} ${MAN5_PREFORMATTED} - - SUFFIXES = .1 .5 - -@@ -76,5 +85,5 @@ SUFFIXES = .1 .5 - --conf-file ../asciidoc.conf \ - -alibreport_version=$(PACKAGE_VERSION) -o $@ $< - --EXTRA_DIST = $(MAN1_TXT) $(MAN5_TXT) -+EXTRA_DIST = $(MAN1_TXT) $(MAN5_TXT) $(MAN5_PREFORMATTED) - CLEANFILES = $(man1_MANS) -diff --git a/doc/centos_report_event.conf.5 b/doc/centos_report_event.conf.5 -new file mode 100644 -index 0000000..71c3fcb ---- /dev/null -+++ b/doc/centos_report_event.conf.5 -@@ -0,0 +1 @@ -+.so man5/report_event.conf.5 -diff --git a/doc/mantisbt.conf.txt b/doc/mantisbt.conf.txt -new file mode 100644 -index 0000000..d4ba605 ---- /dev/null -+++ b/doc/mantisbt.conf.txt -@@ -0,0 +1,18 @@ -+mantisbt.conf(5) -+=============== -+ -+NAME -+---- -+mantisbt.conf - configuration file for libreport. -+ -+DESCRIPTION -+----------- -+This configuration file provides default configuration for 'reporter-mantisbt'. -+ -+SEE ALSO -+-------- -+reporter-mantisbt(1) -+ -+AUTHOR -+------ -+* ABRT team -diff --git a/doc/mantisbt_format.conf.txt b/doc/mantisbt_format.conf.txt -new file mode 100644 -index 0000000..860d911 ---- /dev/null -+++ b/doc/mantisbt_format.conf.txt -@@ -0,0 +1,18 @@ -+mantisbt_format.conf(5) -+======================= -+ -+NAME -+---- -+mantisbt_format.conf - configuration file for libreport. -+ -+DESCRIPTION -+----------- -+This configuration file provides definition of general formatting for new MantisBT issues. -+ -+SEE ALSO -+-------- -+reporter-mantisbt(1) -+ -+AUTHOR -+------ -+* ABRT Team -diff --git a/doc/mantisbt_formatdup.conf.txt b/doc/mantisbt_formatdup.conf.txt -new file mode 100644 -index 0000000..a617226 ---- /dev/null -+++ b/doc/mantisbt_formatdup.conf.txt -@@ -0,0 +1,18 @@ -+mantisbt_formatdup.conf(5) -+========================== -+ -+NAME -+---- -+mantisbt_formatdup.conf - configuration file for libreport. -+ -+DESCRIPTION -+----------- -+This configuration file provides definition of general formatting for duplicate MantisBT issues. -+ -+SEE ALSO -+-------- -+reporter-mantisbt(1) -+ -+AUTHOR -+------ -+* ABRT Team -diff --git a/doc/report_CentOSBugTracker.conf.txt b/doc/report_CentOSBugTracker.conf.txt -new file mode 100644 -index 0000000..6ba35d3 ---- /dev/null -+++ b/doc/report_CentOSBugTracker.conf.txt -@@ -0,0 +1,45 @@ -+report_CentOSBugTracker.conf(5) -+=============================== -+ -+NAME -+---- -+report_CentOSBugTracker.conf - libreport's configuration file for 'report_CentOSBugTracker' events. -+ -+DESCRIPTION -+----------- -+This configuration file contains values for options defined in -+/usr/share/libreport/events/report_CentOSBugTracker.xml -+ -+Configuration file lines should have 'PARAM = VALUE' format. The parameters are: -+ -+'Mantisbt_Login':: -+ Login to MantisBT account. -+ -+'Mantisbt_Password':: -+ Password to MantisBT account. -+ -+'Mantisbt_MantisbtURL':: -+ MantisBT HTTP(S) address. (default: https://bugs.centos.org) -+ -+'Mantisbt_SSLVerify':: -+ Use yes/true/on/1 to verify server's SSL certificate. (default: yes) -+ -+'Mantisbt_Project':: -+ Project issue field value. Useful if you needed different project than specified in /etc/os-release -+ -+'Mantisbt_ProjectVersion':: -+ Version issue field value. Useful if you needed different project version than specified in /etc/os-release -+ -+'http_proxy':: -+ the proxy server to use for HTTP -+ -+'HTTPS_PROXY':: -+ the proxy server to use for HTTPS -+ -+SEE ALSO -+-------- -+report_event.conf(5), reporter-mantisbt(1) -+ -+AUTHOR -+------ -+* ABRT team -diff --git a/doc/report_centos.conf.txt b/doc/report_centos.conf.txt -new file mode 100644 -index 0000000..23a5fde ---- /dev/null -+++ b/doc/report_centos.conf.txt -@@ -0,0 +1,41 @@ -+report_centos.conf(5) -+===================== -+ -+NAME -+---- -+report_centos.conf - configuration file for libreport. -+ -+DESCRIPTION -+----------- -+This configuration file specifies which of the reporting work flow definitions -+are applicable for all problems types on CentOS. -+ -+All applicable work flows are presented to users in User Interface as -+possibilities for processing of any problems. A particular work flow becomes -+applicable if its conditions are satisfied. -+ -+This configuration file consists from one condition per line. -+ -+Each condition line must start with EVENT=workflow_NAME where "workflow_" is -+constant prefix and "workflow_NAME" is base name of path to reporting work flow -+configuration file. -+ -+The rest of condition line has form VAR=VAL, VAR!=VAL or VAL~=REGEX, where VAR -+is a name of problem directory element to be checked (for example, -+"executable", "package", hostname" etc). The condition may consists -+from as many element checks as it is necessary. -+ -+EXAMPLES -+-------- -+Condition line:: -+ EVENT=workflow_CentOSCCpp analyzer=CCpp -+ -+The condition line above expects existence of /usr/share/libreport/workflows/workflow_CentOSCCpp.xml -+ -+SEE ALSO -+-------- -+report-gtk(1) -+ -+AUTHOR -+------ -+* ABRT team -diff --git a/doc/reporter-mantisbt.txt b/doc/reporter-mantisbt.txt -new file mode 100644 -index 0000000..92255b0 ---- /dev/null -+++ b/doc/reporter-mantisbt.txt -@@ -0,0 +1,219 @@ -+reporter-mantisbt(1) -+==================== -+ -+NAME -+---- -+reporter-mantisbt - Reports problem to Mantis Bug Tracker. -+ -+SYNOPSIS -+-------- -+'reporter-mantisbt' [-vrf] [-c CONFFILE]... [-F FMTFILE] [-A FMTFILE2] -d DIR -+ -+Or: -+ -+'reporter-mantisbt' [-v] [-c CONFFILE]... [-d DIR] -t[ID] FILE... -+ -+Or: -+ -+'reporter-mantisbt' [-v] [-c CONFFILE]... -h DUPHASH -+ -+DESCRIPTION -+----------- -+The tool reads problem directory DIR. Then it logs in to MantisBT -+and tries to find an issue with the same duphash HEXSTRING in 'abrt_hash' field. -+ -+If such issue is not found, then a new issue is created. Elements of DIR -+are stored in the issue as part of issue description or as attachments, -+depending on their type and size. -+ -+Otherwise, if such issue is found and it is marked as CLOSED DUPLICATE, -+the tool follows the chain of duplicates until it finds a non-DUPLICATE issue. -+The tool adds a new note to found issue. -+ -+The URL to new or modified issue is printed to stdout and recorded in -+'reported_to' element in DIR. -+ -+Option -t uploads FILEs to the already created issue on MantisBT site. -+The issue ID is retrieved from directory specified by -d DIR. -+If problem data in DIR was never reported to MantisBT, upload will fail. -+ -+Option -tID uploads FILEs to the issue with specified ID on MantisBT site. -+-d DIR is ignored. -+ -+Option -r sets the last url from reporter_to element which is prefixed with -+TRACKER_NAME to URL field. This option is applied only when a new issue is to be -+filed. The default value is 'ABRT Server'" -+ -+Configuration file -+~~~~~~~~~~~~~~~~~~ -+If not specified, CONFFILE defaults to /etc/libreport/plugins/mantisbt.conf. -+Configuration file lines should have 'PARAM = VALUE' format. The parameters are: -+ -+'Login':: -+ Login to MantisBT account. -+ -+'Password':: -+ Password to MantisBT account. -+ -+'MantisbtURL':: -+ MantisBT HTTP(S) address. (default: http://localhost/mantisbt) -+ -+'SSLVerify':: -+ Use yes/true/on/1 to verify server's SSL certificate. (default: no) -+ -+'Project':: -+ Project issue field value. Useful if you needed different project than specified in /etc/os-release -+ -+'ProjectVersion':: -+ Version issue field value. Useful if you needed different project version than specified in /etc/os-release -+ -+'CreatePrivate':: -+ Create private MantisBT issue. (default: no) -+ -+Parameters can be overridden via $Mantisbt_PARAM environment variables. -+ -+Formatting configuration files -+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+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 MantisBT Description or Additional information. Can be empty. -+ (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: issue Summary format string. -+ - %attach: a list of elements to attach. -+ - %Additional info: issue Additional Information content. -+ - text, double colon (::) and the list of comma-separated elements. -+ -+ Description and Additional information MantisBT's fields: -+ All text, double colons (::) and lists of comma-separated elements which -+ are placed above the section '%Additional info::' in the configuration file are -+ stored in the 'Description' field in MantisBT. All text etc. which are placed -+ under the '%Additional info::' are stored in the 'Additional information' field. -+ -+ For example: -+ |:: comment | (Description) -+ | | (Description) -+ |Package:: package | (Description) -+ | | (Description) -+ |%Additional_info:: | -+ |%reporter% | (Additional info) -+ |User:: user_name,uid | (Additional info) -+ | | (Additional info) -+ |Directories:: root,cwd | (Additional info) -+ -+ 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. -+ -+Integration with ABRT events -+~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+'reporter-mantisbt' can be used as an ABRT reporter. Example -+fragment for /etc/libreport/report_event.conf: -+ -+------------ -+# Report Python crashes -+EVENT=report_CentOSBugTracker analyzer=Python -+ reporter-mantisbt -d . -c /etc/libreport/plugins/mantisbt.conf -+------------ -+ -+OPTIONS -+------- -+-d DIR:: -+ Path to problem directory. -+ -+-c CONFFILE:: -+ Path to configuration file. -+ -+-f:: -+ Force reporting even if this problem is already reported. -+ -+-F CONF_FORMAT_FILE:: -+ Formatting file for new issues. Default: /etc/libreport/plugins/mantisbt_format.conf -+ -+-A CONF_FORMAT_FILE:: -+ Formatting file for duplicates. Default: /etc/libreport/plugins/mantisbt_formatdup.conf -+ -+-t[ID]:: -+ Upload FILEs to the already created issue on MantisBT site. -+ -+-h:: -+--duphash DUPHASH:: -+ Search in MantisBT by abrt's DUPHASH and print ISSUE_ID. -+ -+-r TRACKER_NAME:: -+ Set the last url from reporter_to element which is prefixed with TRACKER_NAME to URL field in MantisBT. -+ -+ENVIRONMENT VARIABLES -+--------------------- -+Environment variables take precedence over values provided in -+the configuration file. -+ -+'Mantisbt_Login':: -+ Login to MantisBT account. -+ -+'Mantisbt_Password':: -+ Password to MantisBT account. -+ -+'Mantisbt_MantisbtURL':: -+ MantisBT HTTP(S) address. (default: http://localhost/mantisbt) -+ -+'Mantisbt_SSLVerify':: -+ Use yes/true/on/1 to verify server's SSL certificate. (default: no) -+ -+'Mantisbt_Project':: -+ Project issue field value. Useful if you needed different project than specified in /etc/os-release -+ -+'Mantisbt_ProjectVersion':: -+ Version issue field value. Useful if you needed different project version than specified in /etc/os-release -+ -+'Mantisbt_CreatePrivate':: -+ Create private MantisBT issue. (default: no) -+ -+FILES -+----- -+/usr/share/libreport/conf.d/plugins/mantisbt.conf:: -+ Readonly default configuration files. -+ -+/etc/libreport/plugins/mantisbt.conf:: -+ Configuration file. -+ -+/etc/libreport/plugins/mantisbt_format.conf:: -+ Configure formating for reporting. -+ -+/etc/libreport/plugins/mantisbt_formatdup.conf:: -+ Configure formating for reporting duplicates. -+ -+SEE ALSO -+-------- -+report_event.conf(5), mantisbt_format.conf(5), mantisbt_formatdup.conf(5) -+ -+AUTHORS -+------- -+* ABRT team -diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am -index fd3f477..27194a4 100644 ---- a/src/plugins/Makefile.am -+++ b/src/plugins/Makefile.am -@@ -109,6 +109,10 @@ if BUILD_UREPORT - reporters_extra_dist += report_uReport.xml.in - endif - -+if BUILD_MANTISBT -+reporters_extra_dist += report_CentOSBugTracker.xml.in -+endif -+ - EXTRA_DIST = $(reporters_extra_dist) \ - report_Logger.conf \ - report_Logger.xml.in \ -diff --git a/src/workflows/Makefile.am b/src/workflows/Makefile.am -index 7ecb34e..17127a0 100644 ---- a/src/workflows/Makefile.am -+++ b/src/workflows/Makefile.am -@@ -107,3 +107,15 @@ EXTRA_DIST += \ - workflow_RHELBugzillaLibreport.xml.in \ - workflow_RHELBugzillaJava.xml.in - endif -+ -+if BUILD_MANTISBT -+EXTRA_DIST += \ -+ workflow_CentOSCCpp.xml.in \ -+ workflow_CentOSKerneloops.xml.in \ -+ workflow_CentOSPython.xml.in \ -+ workflow_CentOSPython3.xml.in \ -+ workflow_CentOSVmcore.xml.in \ -+ workflow_CentOSXorg.xml.in \ -+ workflow_CentOSLibreport.xml.in \ -+ workflow_CentOSJava.xml.in -+endif --- -1.8.3.1 - diff --git a/SOURCES/1010-move-problem_report-to-plugins.patch b/SOURCES/1010-move-problem_report-to-plugins.patch deleted file mode 100644 index 9ba00e7..0000000 --- a/SOURCES/1010-move-problem_report-to-plugins.patch +++ /dev/null @@ -1,3284 +0,0 @@ -From a24be1f0915646dd0390884ceb4ee1bfae7fbe0c Mon Sep 17 00:00:00 2001 -From: Jakub Filak <jfilak@redhat.com> -Date: Wed, 25 Mar 2015 16:43:19 +0100 -Subject: [PATCH 1010/1015] move problem_report to plugins - -Get rid of satyr from libreport. - -Signed-off-by: Jakub Filak <jfilak@redhat.com> ---- - po/POTFILES.in | 2 +- - src/include/Makefile.am | 1 - - src/include/problem_report.h | 334 ------------ - src/lib/Makefile.am | 7 +- - src/lib/problem_report.c | 1210 ------------------------------------------ - src/plugins/Makefile.am | 24 +- - src/plugins/problem_report.c | 1210 ++++++++++++++++++++++++++++++++++++++++++ - src/plugins/problem_report.h | 334 ++++++++++++ - tests/testsuite.at | 2 +- - 9 files changed, 1568 insertions(+), 1556 deletions(-) - delete mode 100644 src/include/problem_report.h - delete mode 100644 src/lib/problem_report.c - create mode 100644 src/plugins/problem_report.c - create mode 100644 src/plugins/problem_report.h - -diff --git a/po/POTFILES.in b/po/POTFILES.in -index 9cf8f72..8b62b59 100644 ---- a/po/POTFILES.in -+++ b/po/POTFILES.in -@@ -25,11 +25,11 @@ src/lib/ureport.c - src/lib/make_descr.c - src/lib/parse_options.c - src/lib/problem_data.c --src/lib/problem_report.c - src/lib/reported_to.c - src/lib/reporters.c - src/lib/run_event.c - src/plugins/abrt_rh_support.c -+src/plugins/problem_report.c - src/plugins/report_Bugzilla.xml.in - src/plugins/report.c - src/plugins/reporter-bugzilla.c -diff --git a/src/include/Makefile.am b/src/include/Makefile.am -index 4d8c6a5..7a76cf4 100644 ---- a/src/include/Makefile.am -+++ b/src/include/Makefile.am -@@ -5,7 +5,6 @@ libreport_include_HEADERS = \ - dump_dir.h \ - event_config.h \ - problem_data.h \ -- problem_report.h \ - report.h \ - run_event.h \ - libreport_curl.h \ -diff --git a/src/include/problem_report.h b/src/include/problem_report.h -deleted file mode 100644 -index f2d41d8..0000000 ---- a/src/include/problem_report.h -+++ /dev/null -@@ -1,334 +0,0 @@ --/* -- 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. -- -- @brief API for formating of problem data -- -- These functions can be used to convert a problem data to its string -- representation. -- -- The output format can be parsed from a string: -- -- problem_formatter_t *formatter = problem_formatter_new(); -- problem_formatter_load_string(formatter, MY_FORMAT_STRING); -- -- or loaded from a file: -- -- problem_formatter_t *formatter = problem_formatter_new(); -- problem_formatter_load_file(formatter, MY_FORMAT_FILE); -- -- Once you have configured your formatter you can convert problem_data to -- problem_report by calling: -- -- problem_report_t *report; -- if (problem_formatter_generate_report(formatter, data, &report) != 0) -- errx(EXIT_FAILURE, "Problem data cannot be converted to problem report."); -- -- Now you can print the report: -- -- printf("Problem: %s\n", problem_report_get_summary()); -- printf("%s\n", problem_report_get_description()); -- -- puts("Problem attachments:"); -- for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a)) -- printf(" %s\n", a->data); -- -- Format description: -- -- ---- -- %summary:: summary format -- %attach:: elemnt1[,element2]... -- section:: element1[,element2]... -- The literal text line to be added to report. -- ---- -- -- 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. -- -- - %description: this section is implicit and contains all text -- sections unless another section was specified (%summary and %attach -- are ignored when determining text section's placement) -- -- - every text element belongs to the last specified section (%summary -- and %attach sections are ignored). If no section was specified, -- the text element belogns to %description. -- -- - If none of elements exists, the section will not be created. -- -- - Empty lines are NOT ignored. -- -- 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. -- -- You can add your own section: -- -- problem_formatter_t *formatter = problem_formatter_new(); -- problem_formatter_add_section(formatter, "additional_info", PFFF_REQUIRED); -- -- and then you can use the section in the formatting string: -- -- problem_formatter_load_string(formatter, -- "::comment\n" -- "%additional_info:: maps"); -- problem_formatter_generate_report(formatter, data, &report); -- -- printf("Problem: %s\n", problem_report_get_summary()); -- printf("%s\n", problem_report_get_description()); -- printf("Additional info: %s\n", problem_report_get_section(report, "additiona_info")); -- -- The lines above are equivalent to the following lines: -- -- printf("Problem: %s\n", problem_data_get_content_or_NULL(data, "reason")); -- printf("%s\n", problem_data_get_content_or_NULL(data, "comment")); -- printf("Additional info: %s\n", problem_data_get_content_or_NULL(data, "maps")); --*/ --#ifndef LIBREPORT_PROBLEM_REPORT_H --#define LIBREPORT_PROBLEM_REPORT_H -- --#include <glib.h> --#include <stdio.h> --#include "problem_data.h" -- --#ifdef __cplusplus --extern "C" { --#endif -- --#define PR_SEC_SUMMARY "summary" --#define PR_SEC_DESCRIPTION "description" -- --/* -- * The problem report structure represents a problem data formatted according -- * to a format string. -- * -- * A problem report is composed of well-known sections: -- * - summary -- * - descritpion -- * - attach -- * -- * and custom sections accessed by: -- * problem_report_get_section(); -- */ --struct problem_report; --typedef struct problem_report problem_report_t; -- --/* -- * Helpers for easily switching between FILE and struct strbuf -- */ -- --/* -- * Type of buffer used by Problem report -- */ --typedef FILE problem_report_buffer; -- --/* -- * Wrapper for the proble buffer's formated output function. -- */ --#define problem_report_buffer_printf(buf, fmt, ...)\ -- fprintf((buf), (fmt), ##__VA_ARGS__) -- -- --/* -- * Get a section buffer -- * -- * Use this function if you need to amend something to a formatted section. -- * -- * @param self Problem report -- * @param section_name Name of required section -- * @return Always valid pointer to a section buffer -- */ --problem_report_buffer *problem_report_get_buffer(const problem_report_t *self, -- const char *section_name); -- --/* -- * Get Summary string -- * -- * The returned pointer is valid as long as you perform no further output to -- * the summary buffer. -- * -- * @param self Problem report -- * @return Non-NULL pointer to summary data -- */ --const char *problem_report_get_summary(const problem_report_t *self); -- --/* -- * Get Description string -- * -- * The returned pointer is valid as long as you perform no further output to -- * the description buffer. -- * -- * @param self Problem report -- * @return Non-NULL pointer to description data -- */ --const char *problem_report_get_description(const problem_report_t *self); -- --/* -- * Get Section's string -- * -- * The returned pointer is valid as long as you perform no further output to -- * the section's buffer. -- * -- * @param self Problem report -- * @param section_name Name of the required section -- * @return Non-NULL pointer to description data -- */ --const char *problem_report_get_section(const problem_report_t *self, -- const char *section_name); -- --/* -- * Get GList of the problem data items that are to be attached -- * -- * @param self Problem report -- * @return A pointer to GList (NULL means empty list) -- */ --GList *problem_report_get_attachments(const problem_report_t *self); -- --/* -- * Releases all resources allocated by a problem report -- * -- * @param self Problem report -- */ --void problem_report_free(problem_report_t *self); -- -- --/* -- * An enum of Extra section flags -- */ --enum problem_formatter_section_flags { -- PFFF_REQUIRED = 1 << 0, ///< section must be present in the format spec --}; -- --/* -- * The problem formatter structure formats a problem data according to a format -- * string and stores result a problem report. -- * -- * The problem formatter uses '%reason%' as %summary section format string, if -- * %summary is not provided by a format string. -- */ --struct problem_formatter; --typedef struct problem_formatter problem_formatter_t; -- --/* -- * Constructs a new problem formatter. -- * -- * @return Non-NULL pointer to the new problem formatter -- */ --problem_formatter_t *problem_formatter_new(void); -- --/* -- * Releases all resources allocated by a problem formatter -- * -- * @param self Problem formatter -- */ --void problem_formatter_free(problem_formatter_t *self); -- --/* -- * Adds a new recognized section -- * -- * The problem formatter ignores a section in the format spec if the section is -- * not one of the default nor added by this function. -- * -- * How the problem formatter handles these extra sections: -- * -- * A custom section is something like %description section. %description is the -- * default section where all text (sub)sections are stored. If the formatter -- * finds the custom section in format string, then starts storing text -- * (sub)sections in the custom section. -- * -- * (%description) |:: comment -- * (%description) | -- * (%description) |Package:: package -- * (%description) | -- * (%additiona_info) |%additional_info:: -- * (%additiona_info) |%reporter% -- * (%additiona_info) |User:: user_name,uid -- * (%additiona_info) | -- * (%additiona_info) |Directories:: root,cwd -- * -- * -- * @param self Problem formatter -- * @param name Name of the added section -- * @param flags Info about the added section -- * @return Zero on success. -EEXIST if the name is already known by the formatter -- */ --int problem_formatter_add_section(problem_formatter_t *self, const char *name, int flags); -- --/* -- * Loads a problem format from a string. -- * -- * @param self Problem formatter -- * @param fmt Format -- * @return Zero on success or number of warnings (e.g. missing section, -- * unrecognized section). -- */ --int problem_formatter_load_string(problem_formatter_t* self, const char *fmt); -- --/* -- * Loads a problem format from a file. -- * -- * @param self Problem formatter -- * @param pat Path to the format file -- * @return Zero on success or number of warnings (e.g. missing section, -- * unrecognized section). -- */ --int problem_formatter_load_file(problem_formatter_t* self, const char *path); -- --/* -- * Creates a new problem report, formats the data according to the loaded -- * format string and stores output in the report. -- * -- * @param self Problem formatter -- * @param data Problem data to format -- * @param report Pointer where the created problem report is to be stored -- * @return Zero on success, otherwise non-zero value. -- */ --int problem_formatter_generate_report(const problem_formatter_t *self, problem_data_t *data, problem_report_t **report); -- --#ifdef __cplusplus --} --#endif -- --#endif // LIBREPORT_PROBLEM_REPORT_H -diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am -index d41e543..80a7c4d 100644 ---- a/src/lib/Makefile.am -+++ b/src/lib/Makefile.am -@@ -38,7 +38,6 @@ libreport_la_SOURCES = \ - make_descr.c \ - run_event.c \ - problem_data.c \ -- problem_report.c \ - create_dump_dir.c \ - abrt_types.c \ - parse_release.c \ -@@ -80,7 +79,6 @@ libreport_la_CPPFLAGS = \ - $(GLIB_CFLAGS) \ - $(GOBJECT_CFLAGS) \ - $(AUGEAS_CFLAGS) \ -- $(SATYR_CFLAGS) \ - -D_GNU_SOURCE - libreport_la_LDFLAGS = \ - -ltar \ -@@ -90,8 +88,7 @@ libreport_la_LIBADD = \ - $(GLIB_LIBS) \ - $(JOURNAL_LIBS) \ - $(GOBJECT_LIBS) \ -- $(AUGEAS_LIBS) \ -- $(SATYR_LIBS) -+ $(AUGEAS_LIBS) - - libreportconfdir = $(CONF_DIR) - dist_libreportconf_DATA = \ -@@ -152,8 +149,8 @@ libreport_web_la_LIBADD = \ - $(PROXY_LIBS) \ - $(LIBXML_LIBS) \ - $(JSON_C_LIBS) \ -- $(SATYR_LIBS) \ - $(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS) \ -+ $(SATYR_LIBS) \ - libreport.la - - DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@ -diff --git a/src/lib/problem_report.c b/src/lib/problem_report.c -deleted file mode 100644 -index 2bf5530..0000000 ---- a/src/lib/problem_report.c -+++ /dev/null -@@ -1,1210 +0,0 @@ --/* -- 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 "problem_report.h" --#include "internal_libreport.h" -- --#include <satyr/stacktrace.h> --#include <satyr/abrt.h> -- --#include <assert.h> -- --#define DESTROYED_POINTER (void *)0xdeadbeef -- --/* FORMAT: -- * |%summary:: Hello, world -- * |Problem description:: %bare_comment -- * | -- * |Package:: package -- * | -- * |%attach: %binary, backtrace -- * | -- * |%additional_info:: -- * |%reporter% -- * |User:: user_name,uid -- * | -- * |Directories:: root,cwd -- * -- * PARSED DATA (list of struct section_t): -- * { -- * section_t { -- * .name = '%summary'; -- * .items = { 'Hello, world' }; -- * .children = NULL; -- * }, -- * section_t { -- * .name = '%attach' -- * .items = { '%binary', 'backtrace' }; -- * .children = NULL; -- * }, -- * section_t { -- * .name = '%description' -- * .items = NULL; -- * .children = { -- * section_t { -- * .name = 'Problem description:'; -- * .items = { '%bare_comment' }; -- * .children = NULL; -- * }, -- * section_t { -- * .name = ''; -- * .items = NULL; -- * .children = NULL; -- * }, -- * section_t { -- * .name = 'Package:'; -- * .items = { 'package' }; -- * .children = NULL; -- * }, -- * } -- * }, -- * section_t { -- * .name = '%additional_info' -- * .items = { '%reporter%' }; -- * .children = { -- * section_t { -- * .name = 'User:'; -- * .items = { 'user_name', 'uid' }; -- * .children = NULL; -- * }, -- * section_t { -- * .name = ''; -- * .items = NULL; -- * .children = NULL; -- * }, -- * section_t { -- * .name = 'Directories:'; -- * .items = { 'root', 'cwd' }; -- * .children = NULL; -- * }, -- * } -- * } -- * } -- */ --struct section_t { -- char *name; ///< name or output text (%summar, 'Package version:'); -- GList *items; ///< list of file names and special items (%reporter, %binar, ...) -- GList *children; ///< list of sub sections (struct section_t) --}; -- --typedef struct section_t section_t; -- --static section_t * --section_new(const char *name) --{ -- section_t *self = xmalloc(sizeof(*self)); -- self->name = xstrdup(name); -- self->items = NULL; -- self->children = NULL; -- -- return self; --} -- --static void --section_free(section_t *self) --{ -- if (self == NULL) -- return; -- -- free(self->name); -- g_list_free_full(self->items, free); -- g_list_free_full(self->children, (GDestroyNotify)section_free); -- -- free(self); --} -- --static int --section_name_cmp(section_t *lhs, const char *rhs) --{ -- return strcmp((lhs->name + 1), rhs); --} -- --/* Utility functions */ -- --static GList* --split_string_on_char(const char *str, char ch) --{ -- GList *list = NULL; -- for (;;) -- { -- const char *delim = strchrnul(str, ch); -- list = g_list_prepend(list, xstrndup(str, delim - str)); -- if (*delim == '\0') -- break; -- str = delim + 1; -- } -- return g_list_reverse(list); --} -- --static int --compare_item_name(const char *lookup, const char *name) --{ -- if (lookup[0] == '-') -- lookup++; -- else if (strncmp(lookup, "%bare_", 6) == 0) -- lookup += 6; -- return strcmp(lookup, name); --} -- --static int --is_item_name_in_section(const section_t *lookup, const char *name) --{ -- if (g_list_find_custom(lookup->items, name, (GCompareFunc)compare_item_name)) -- return 0; /* "found it!" */ -- return 1; --} -- --static bool is_explicit_or_forbidden(const char *name, GList *comment_fmt_spec); -- --static int --is_explicit_or_forbidden_child(const section_t *master_section, const char *name) --{ -- if (is_explicit_or_forbidden(name, master_section->children)) -- return 0; /* "found it!" */ -- return 1; --} -- --/* For example: 'package' belongs to '%oneline', but 'package' is used in -- * 'Version of component', so it is not very helpful to include that file once -- * more in another section -- */ --static bool --is_explicit_or_forbidden(const char *name, GList *comment_fmt_spec) --{ -- return g_list_find_custom(comment_fmt_spec, name, (GCompareFunc)is_item_name_in_section) -- || g_list_find_custom(comment_fmt_spec, name, (GCompareFunc)is_explicit_or_forbidden_child); --} -- --static GList* --load_stream(FILE *fp) --{ -- assert(fp); -- -- GList *sections = NULL; -- section_t *master = section_new("%description"); -- section_t *sec = NULL; -- -- sections = g_list_append(sections, master); -- -- char *line; -- while ((line = xmalloc_fgetline(fp)) != NULL) -- { -- /* Skip comments */ -- char first = *skip_whitespace(line); -- if (first == '#') -- goto free_line; -- -- /* Handle trailing backslash continuation */ -- check_continuation: ; -- unsigned len = strlen(line); -- if (len && line[len-1] == '\\') -- { -- line[len-1] = '\0'; -- char *next_line = xmalloc_fgetline(fp); -- if (next_line) -- { -- line = append_to_malloced_string(line, next_line); -- free(next_line); -- goto check_continuation; -- } -- } -- -- /* We are reusing line buffer to form temporary -- * "key\0values\0..." in its beginning -- */ -- bool summary_line = false; -- char *value = NULL; -- char *src; -- char *dst; -- for (src = dst = line; *src; src++) -- { -- char c = *src; -- /* did we reach the value list? */ -- if (!value && c == ':' && src[1] == ':') -- { -- *dst++ = '\0'; /* terminate key */ -- src += 1; -- value = dst; /* remember where value starts */ -- summary_line = (strcmp(line, "%summary") == 0); -- if (summary_line) -- { -- value = (src + 1); -- break; -- } -- continue; -- } -- /* skip whitespace in value list */ -- if (value && isspace(c)) -- continue; -- *dst++ = c; /* store next key or value char */ -- } -- -- GList *item_list = NULL; -- if (summary_line) -- { -- /* %summary is special */ -- item_list = g_list_append(NULL, xstrdup(skip_whitespace(value))); -- } -- else -- { -- *dst = '\0'; /* terminate value (or key) */ -- if (value) -- item_list = split_string_on_char(value, ','); -- } -- -- sec = section_new(line); -- sec->items = item_list; -- -- if (sec->name[0] == '%') -- { -- if (!summary_line && strcmp(sec->name, "%attach") != 0) -- { -- master->children = g_list_reverse(master->children); -- master = sec; -- } -- -- sections = g_list_prepend(sections, sec); -- } -- else -- master->children = g_list_prepend(master->children, sec); -- -- free_line: -- free(line); -- } -- -- /* If master equals sec, then master's children list was not yet reversed. -- * -- * %description is the default section (i.e is not explicitly mentioned) -- * and %summary nor %attach cause its children list to reverse. -- */ -- if (master == sec || strcmp(master->name, "%description") == 0) -- master->children = g_list_reverse(master->children); -- -- return sections; --} -- -- --/* Summary generation */ -- --#define MAX_OPT_DEPTH 10 --static int --format_percented_string(const char *str, problem_data_t *pd, FILE *result) --{ -- long old_pos[MAX_OPT_DEPTH] = { 0 }; -- int okay[MAX_OPT_DEPTH] = { 1 }; -- long len = 0; -- int opt_depth = 1; -- -- while (*str) { -- switch (*str) { -- default: -- putc(*str, result); -- len++; -- str++; -- break; -- case '\\': -- if (str[1]) -- str++; -- putc(*str, result); -- len++; -- str++; -- break; -- case '[': -- if (str[1] == '[' && opt_depth < MAX_OPT_DEPTH) -- { -- old_pos[opt_depth] = len; -- okay[opt_depth] = 1; -- opt_depth++; -- str += 2; -- } else { -- putc(*str, result); -- len++; -- str++; -- } -- break; -- case ']': -- if (str[1] == ']' && opt_depth > 1) -- { -- opt_depth--; -- if (!okay[opt_depth]) -- { -- if (fseek(result, old_pos[opt_depth], SEEK_SET) < 0) -- perror_msg_and_die("fseek"); -- len = old_pos[opt_depth]; -- } -- str += 2; -- } else { -- putc(*str, result); -- len++; -- str++; -- } -- break; -- case '%': ; -- char *nextpercent = strchr(++str, '%'); -- if (!nextpercent) -- { -- error_msg_and_die("Unterminated %%element%%: '%s'", str - 1); -- } -- -- *nextpercent = '\0'; -- const problem_item *item = problem_data_get_item_or_NULL(pd, str); -- *nextpercent = '%'; -- -- if (item && (item->flags & CD_FLAG_TXT)) -- { -- fputs(item->content, result); -- len += strlen(item->content); -- } -- else -- okay[opt_depth - 1] = 0; -- str = nextpercent + 1; -- break; -- } -- } -- -- if (opt_depth > 1) -- { -- error_msg_and_die("Unbalanced [[ ]] bracket"); -- } -- -- if (!okay[0]) -- { -- error_msg("Undefined variable outside of [[ ]] bracket"); -- } -- -- return 0; --} -- --/* BZ comment generation */ -- --static int --append_text(struct strbuf *result, const char *item_name, const char *content, bool print_item_name) --{ -- char *eol = strchrnul(content, '\n'); -- if (eol[0] == '\0' || eol[1] == '\0') -- { -- /* one-liner */ -- int pad = 16 - (strlen(item_name) + 2); -- if (pad < 0) -- pad = 0; -- if (print_item_name) -- strbuf_append_strf(result, -- eol[0] == '\0' ? "%s: %*s%s\n" : "%s: %*s%s", -- item_name, pad, "", content -- ); -- else -- strbuf_append_strf(result, -- eol[0] == '\0' ? "%s\n" : "%s", -- content -- ); -- } -- else -- { -- /* multi-line item */ -- if (print_item_name) -- strbuf_append_strf(result, "%s:\n", item_name); -- for (;;) -- { -- eol = strchrnul(content, '\n'); -- strbuf_append_strf(result, -- /* For %bare_multiline_item, we don't want to print colons */ -- (print_item_name ? ":%.*s\n" : "%.*s\n"), -- (int)(eol - content), content -- ); -- if (eol[0] == '\0' || eol[1] == '\0') -- break; -- content = eol + 1; -- } -- } -- return 1; --} -- --static int --append_short_backtrace(struct strbuf *result, problem_data_t *problem_data, size_t max_text_size, bool print_item_name) --{ -- const problem_item *item = problem_data_get_item_or_NULL(problem_data, -- FILENAME_BACKTRACE); -- if (!item) -- return 0; /* "I did not print anything" */ -- if (!(item->flags & CD_FLAG_TXT)) -- return 0; /* "I did not print anything" */ -- -- char *truncated = NULL; -- -- if (strlen(item->content) >= max_text_size) -- { -- log_debug("'backtrace' exceeds the text file size, going to append its short version"); -- -- char *error_msg = NULL; -- const char *type = problem_data_get_content_or_NULL(problem_data, FILENAME_TYPE); -- if (!type) -- { -- log_debug("Problem data does not contain '"FILENAME_TYPE"' file"); -- return 0; -- } -- -- /* For CCpp crashes, use the GDB-produced backtrace which should be -- * available by now. sr_abrt_type_from_analyzer returns SR_REPORT_CORE -- * by default for CCpp crashes. -- */ -- enum sr_report_type report_type = sr_abrt_type_from_analyzer(type); -- if (strcmp(type, "CCpp") == 0) -- { -- log_debug("Successfully identified 'CCpp' abrt type"); -- report_type = SR_REPORT_GDB; -- } -- -- struct sr_stacktrace *backtrace = sr_stacktrace_parse(report_type, -- item->content, &error_msg); -- -- if (!backtrace) -- { -- log(_("Can't parse backtrace: %s"), error_msg); -- free(error_msg); -- return 0; -- } -- -- /* Get optimized thread stack trace for 10 top most frames */ -- truncated = sr_stacktrace_to_short_text(backtrace, 10); -- sr_stacktrace_free(backtrace); -- -- if (!truncated) -- { -- log(_("Can't generate stacktrace description (no crash thread?)")); -- return 0; -- } -- } -- else -- { -- log_debug("'backtrace' is small enough to be included as is"); -- } -- -- append_text(result, -- /*item_name:*/ truncated ? "truncated_backtrace" : FILENAME_BACKTRACE, -- /*content:*/ truncated ? truncated : item->content, -- print_item_name -- ); -- free(truncated); -- return 1; --} -- --static int --append_item(struct strbuf *result, const char *item_name, problem_data_t *pd, GList *comment_fmt_spec) --{ -- bool print_item_name = (strncmp(item_name, "%bare_", strlen("%bare_")) != 0); -- if (!print_item_name) -- item_name += strlen("%bare_"); -- -- if (item_name[0] != '%') -- { -- struct problem_item *item = problem_data_get_item_or_NULL(pd, item_name); -- if (!item) -- return 0; /* "I did not print anything" */ -- if (!(item->flags & CD_FLAG_TXT)) -- return 0; /* "I did not print anything" */ -- -- char *formatted = problem_item_format(item); -- char *content = formatted ? formatted : item->content; -- append_text(result, item_name, content, print_item_name); -- free(formatted); -- return 1; /* "I printed something" */ -- } -- -- /* Special item name */ -- -- /* Compat with previously-existed ad-hockery: %short_backtrace */ -- if (strcmp(item_name, "%short_backtrace") == 0) -- return append_short_backtrace(result, pd, CD_TEXT_ATT_SIZE_BZ, print_item_name); -- -- /* Compat with previously-existed ad-hockery: %reporter */ -- if (strcmp(item_name, "%reporter") == 0) -- return append_text(result, "reporter", PACKAGE"-"VERSION, print_item_name); -- -- /* %oneline,%multiline,%text */ -- bool oneline = (strcmp(item_name+1, "oneline" ) == 0); -- bool multiline = (strcmp(item_name+1, "multiline") == 0); -- bool text = (strcmp(item_name+1, "text" ) == 0); -- if (!oneline && !multiline && !text) -- { -- log("Unknown or unsupported element specifier '%s'", item_name); -- return 0; /* "I did not print anything" */ -- } -- -- int printed = 0; -- -- /* Iterate over _sorted_ items */ -- GList *sorted_names = g_hash_table_get_keys(pd); -- sorted_names = g_list_sort(sorted_names, (GCompareFunc)strcmp); -- -- /* %text => do as if %oneline, then repeat as if %multiline */ -- if (text) -- oneline = 1; -- -- again: ; -- GList *l = sorted_names; -- while (l) -- { -- const char *name = l->data; -- l = l->next; -- struct problem_item *item = g_hash_table_lookup(pd, name); -- if (!item) -- continue; /* paranoia, won't happen */ -- -- if (!(item->flags & CD_FLAG_TXT)) -- continue; -- -- if (is_explicit_or_forbidden(name, comment_fmt_spec)) -- continue; -- -- char *formatted = problem_item_format(item); -- char *content = formatted ? formatted : item->content; -- char *eol = strchrnul(content, '\n'); -- bool is_oneline = (eol[0] == '\0' || eol[1] == '\0'); -- if (oneline == is_oneline) -- printed |= append_text(result, name, content, print_item_name); -- free(formatted); -- } -- if (text && oneline) -- { -- /* %text, and we just did %oneline. Repeat as if %multiline */ -- oneline = 0; -- /*multiline = 1; - not checked in fact, so why bother setting? */ -- goto again; -- } -- -- g_list_free(sorted_names); /* names themselves are not freed */ -- -- return printed; --} -- --#define add_to_section_output(format, ...) \ -- do { \ -- for (; empty_lines > 0; --empty_lines) fputc('\n', result); \ -- empty_lines = 0; \ -- fprintf(result, format, __VA_ARGS__); \ -- } while (0) -- --static void --format_section(section_t *section, problem_data_t *pd, GList *comment_fmt_spec, FILE *result) --{ -- int empty_lines = -1; -- -- for (GList *iter = section->children; iter; iter = g_list_next(iter)) -- { -- section_t *child = (section_t *)iter->data; -- if (child->items) -- { -- /* "Text: item[,item]..." */ -- struct strbuf *output = strbuf_new(); -- GList *item = child->items; -- while (item) -- { -- const char *str = item->data; -- item = item->next; -- if (str[0] == '-') /* "-name", ignore it */ -- continue; -- append_item(output, str, pd, comment_fmt_spec); -- } -- -- if (output->len != 0) -- add_to_section_output((child->name[0] ? "%s:\n%s" : "%s%s"), -- child->name, output->buf); -- -- strbuf_free(output); -- } -- else -- { -- /* Just "Text" (can be "") */ -- -- /* Filter out trailint empty lines */ -- if (child->name[0] != '\0') -- add_to_section_output("%s\n", child->name); -- /* Do not count empty lines, if output wasn't yet produced */ -- else if (empty_lines >= 0) -- ++empty_lines; -- } -- } --} -- --static GList * --get_special_items(const char *item_name, problem_data_t *pd, GList *comment_fmt_spec) --{ -- /* %oneline,%multiline,%text,%binary */ -- bool oneline = (strcmp(item_name+1, "oneline" ) == 0); -- bool multiline = (strcmp(item_name+1, "multiline") == 0); -- bool text = (strcmp(item_name+1, "text" ) == 0); -- bool binary = (strcmp(item_name+1, "binary" ) == 0); -- if (!oneline && !multiline && !text && !binary) -- { -- log("Unknown or unsupported element specifier '%s'", item_name); -- return NULL; -- } -- -- log_debug("Special item_name '%s', iterating for attach...", item_name); -- GList *result = 0; -- -- /* Iterate over _sorted_ items */ -- GList *sorted_names = g_hash_table_get_keys(pd); -- sorted_names = g_list_sort(sorted_names, (GCompareFunc)strcmp); -- -- GList *l = sorted_names; -- while (l) -- { -- const char *name = l->data; -- l = l->next; -- struct problem_item *item = g_hash_table_lookup(pd, name); -- if (!item) -- continue; /* paranoia, won't happen */ -- -- if (is_explicit_or_forbidden(name, comment_fmt_spec)) -- continue; -- -- if ((item->flags & CD_FLAG_TXT) && !binary) -- { -- char *content = item->content; -- char *eol = strchrnul(content, '\n'); -- bool is_oneline = (eol[0] == '\0' || eol[1] == '\0'); -- if (text || oneline == is_oneline) -- result = g_list_append(result, xstrdup(name)); -- } -- else if ((item->flags & CD_FLAG_BIN) && binary) -- result = g_list_append(result, xstrdup(name)); -- } -- -- g_list_free(sorted_names); /* names themselves are not freed */ -- -- -- log_debug("...Done iterating over '%s' for attach", item_name); -- -- return result; --} -- --static GList * --get_attached_files(problem_data_t *pd, GList *items, GList *comment_fmt_spec) --{ -- GList *result = NULL; -- GList *item = items; -- while (item != NULL) -- { -- const char *item_name = item->data; -- item = item->next; -- if (item_name[0] == '-') /* "-name", ignore it */ -- continue; -- -- if (item_name[0] != '%') -- { -- result = g_list_append(result, xstrdup(item_name)); -- continue; -- } -- -- GList *special = get_special_items(item_name, pd, comment_fmt_spec); -- if (special == NULL) -- { -- log_notice("No attachment found for '%s'", item_name); -- continue; -- } -- -- result = g_list_concat(result, special); -- } -- -- return result; --} -- --/* -- * Problem Report - memor stream -- * -- * A wrapper for POSIX memory stream. -- * -- * A memory stream is presented as FILE *. -- * -- * A memory stream is associated with a pointer to written data and a pointer -- * to size of the written data. -- * -- * This structure holds all of the used pointers. -- */ --struct memstream_buffer --{ -- char *msb_buffer; -- size_t msb_size; -- FILE *msb_stream; --}; -- --static struct memstream_buffer * --memstream_buffer_new() --{ -- struct memstream_buffer *self = xmalloc(sizeof(*self)); -- -- self->msb_buffer = NULL; -- self->msb_stream = open_memstream(&(self->msb_buffer), &(self->msb_size)); -- -- return self; --} -- --static void --memstream_buffer_free(struct memstream_buffer *self) --{ -- if (self == NULL) -- return; -- -- fclose(self->msb_stream); -- self->msb_stream = DESTROYED_POINTER; -- -- free(self->msb_buffer); -- self->msb_buffer = DESTROYED_POINTER; -- -- free(self); --} -- --static FILE * --memstream_get_stream(struct memstream_buffer *self) --{ -- assert(self != NULL); -- -- return self->msb_stream; --} -- --static const char * --memstream_get_string(struct memstream_buffer *self) --{ -- assert(self != NULL); -- assert(self->msb_stream != NULL); -- -- fflush(self->msb_stream); -- -- return self->msb_buffer; --} -- -- --/* -- * Problem Report -- * -- * The formated strings are internaly stored in "buffer"s. If a programer wants -- * to get a formated section data, a getter function extracts those data from -- * the apropriate buffer and returns them in form of null-terminated string. -- * -- * Each section has own buffer. -- * -- * There are three common sections that are always present: -- * 1. summary -- * 2. description -- * 3. attach -- * Buffers of these sections has own structure member for the sake of -- * efficiency. -- * -- * The custom sections hash their buffers stored in a map where key is a -- * section's name and value is a section's buffer. -- * -- * Problem report provides the programers with the possibility to ammend -- * formated output to any section buffer. -- */ --struct problem_report --{ -- struct memstream_buffer *pr_sec_summ; ///< %summary buffer -- struct memstream_buffer *pr_sec_desc; ///< %description buffer -- GList *pr_attachments; ///< %attach - list of file names -- GHashTable *pr_sec_custom; ///< map : %(custom section) -> buffer --}; -- --static problem_report_t * --problem_report_new() --{ -- problem_report_t *self = xmalloc(sizeof(*self)); -- -- self->pr_sec_summ = memstream_buffer_new(); -- self->pr_sec_desc = memstream_buffer_new(); -- self->pr_attachments = NULL; -- self->pr_sec_custom = NULL; -- -- return self; --} -- --static void --problem_report_initialize_custom_sections(problem_report_t *self) --{ -- assert(self != NULL); -- assert(self->pr_sec_custom == NULL); -- -- self->pr_sec_custom = g_hash_table_new_full(g_str_hash, g_str_equal, free, -- (GDestroyNotify)memstream_buffer_free); --} -- --static void --problem_report_destroy_custom_sections(problem_report_t *self) --{ -- assert(self != NULL); -- assert(self->pr_sec_custom != NULL); -- -- g_hash_table_destroy(self->pr_sec_custom); --} -- --static int --problem_report_add_custom_section(problem_report_t *self, const char *name) --{ -- assert(self != NULL); -- -- if (self->pr_sec_custom == NULL) -- { -- problem_report_initialize_custom_sections(self); -- } -- -- if (problem_report_get_buffer(self, name)) -- { -- log_warning("Custom section already exists : '%s'", name); -- return -EEXIST; -- } -- -- log_debug("Problem report enriched with section : '%s'", name); -- g_hash_table_insert(self->pr_sec_custom, xstrdup(name), memstream_buffer_new()); -- return 0; --} -- --static struct memstream_buffer * --problem_report_get_section_buffer(const problem_report_t *self, const char *section_name) --{ -- if (self->pr_sec_custom == NULL) -- { -- log_debug("Couldn't find section '%s': no custom section added", section_name); -- return NULL; -- } -- -- return (struct memstream_buffer *)g_hash_table_lookup(self->pr_sec_custom, section_name); --} -- --problem_report_buffer * --problem_report_get_buffer(const problem_report_t *self, const char *section_name) --{ -- assert(self != NULL); -- assert(section_name != NULL); -- -- if (strcmp(PR_SEC_SUMMARY, section_name) == 0) -- return memstream_get_stream(self->pr_sec_summ); -- -- if (strcmp(PR_SEC_DESCRIPTION, section_name) == 0) -- return memstream_get_stream(self->pr_sec_desc); -- -- struct memstream_buffer *buf = problem_report_get_section_buffer(self, section_name); -- return buf == NULL ? NULL : memstream_get_stream(buf); --} -- --const char * --problem_report_get_summary(const problem_report_t *self) --{ -- assert(self != NULL); -- -- return memstream_get_string(self->pr_sec_summ); --} -- --const char * --problem_report_get_description(const problem_report_t *self) --{ -- assert(self != NULL); -- -- return memstream_get_string(self->pr_sec_desc); --} -- --const char * --problem_report_get_section(const problem_report_t *self, const char *section_name) --{ -- assert(self != NULL); -- assert(section_name); -- -- struct memstream_buffer *buf = problem_report_get_section_buffer(self, section_name); -- -- if (buf == NULL) -- return NULL; -- -- return memstream_get_string(buf); --} -- --static void --problem_report_set_attachments(problem_report_t *self, GList *attachments) --{ -- assert(self != NULL); -- assert(self->pr_attachments == NULL); -- -- self->pr_attachments = attachments; --} -- --GList * --problem_report_get_attachments(const problem_report_t *self) --{ -- assert(self != NULL); -- -- return self->pr_attachments; --} -- --void --problem_report_free(problem_report_t *self) --{ -- if (self == NULL) -- return; -- -- memstream_buffer_free(self->pr_sec_summ); -- self->pr_sec_summ = DESTROYED_POINTER; -- -- memstream_buffer_free(self->pr_sec_desc); -- self->pr_sec_desc = DESTROYED_POINTER; -- -- g_list_free_full(self->pr_attachments, free); -- self->pr_attachments = DESTROYED_POINTER; -- -- if (self->pr_sec_custom) -- { -- problem_report_destroy_custom_sections(self); -- self->pr_sec_custom = DESTROYED_POINTER; -- } -- -- free(self); --} -- --/* -- * Problem Formatter - extra section -- */ --struct extra_section --{ -- char *pfes_name; ///< name with % prefix -- int pfes_flags; ///< whether is required or not --}; -- --static struct extra_section * --extra_section_new(const char *name, int flags) --{ -- struct extra_section *self = xmalloc(sizeof(*self)); -- -- self->pfes_name = xstrdup(name); -- self->pfes_flags = flags; -- -- return self; --} -- --static void --extra_section_free(struct extra_section *self) --{ -- if (self == NULL) -- return; -- -- free(self->pfes_name); -- self->pfes_name = DESTROYED_POINTER; -- -- free(self); --} -- --static int --extra_section_name_cmp(struct extra_section *lhs, const char *rhs) --{ -- return strcmp(lhs->pfes_name, rhs); --} -- --/* -- * Problem Formatter -- * -- * Holds parsed sections lists. -- */ --struct problem_formatter --{ -- GList *pf_sections; ///< parsed sections (struct section_t) -- GList *pf_extra_sections; ///< user configured sections (struct extra_section) -- char *pf_default_summary; ///< default summary format --}; -- --problem_formatter_t * --problem_formatter_new(void) --{ -- problem_formatter_t *self = xzalloc(sizeof(*self)); -- -- self->pf_default_summary = xstrdup("%reason%"); -- -- return self; --} -- --void --problem_formatter_free(problem_formatter_t *self) --{ -- if (self == NULL) -- return; -- -- g_list_free_full(self->pf_sections, (GDestroyNotify)section_free); -- self->pf_sections = DESTROYED_POINTER; -- -- g_list_free_full(self->pf_extra_sections, (GDestroyNotify)extra_section_free); -- self->pf_extra_sections = DESTROYED_POINTER; -- -- free(self->pf_default_summary); -- self->pf_default_summary = DESTROYED_POINTER; -- -- free(self); --} -- --static int --problem_formatter_is_section_known(problem_formatter_t *self, const char *name) --{ -- return strcmp(name, "summary") == 0 -- || strcmp(name, "attach") == 0 -- || strcmp(name, "description") == 0 -- || NULL != g_list_find_custom(self->pf_extra_sections, name, (GCompareFunc)extra_section_name_cmp); --} -- --// i.e additional_info -> no flags --int --problem_formatter_add_section(problem_formatter_t *self, const char *name, int flags) --{ -- /* Do not add already added sections */ -- if (problem_formatter_is_section_known(self, name)) -- { -- log_debug("Extra section already exists : '%s' ", name); -- return -EEXIST; -- } -- -- self->pf_extra_sections = g_list_prepend(self->pf_extra_sections, -- extra_section_new(name, flags)); -- -- return 0; --} -- --// check format validity and produce warnings --static int --problem_formatter_validate(problem_formatter_t *self) --{ -- int retval = 0; -- -- /* Go through all (struct extra_section)s and check whete those having flag -- * PFFF_REQUIRED are present in the parsed (struct section_t)s. -- */ -- for (GList *iter = self->pf_extra_sections; iter; iter = g_list_next(iter)) -- { -- struct extra_section *section = (struct extra_section *)iter->data; -- -- log_debug("Validating extra section : '%s'", section->pfes_name); -- -- if ( (PFFF_REQUIRED & section->pfes_flags) -- && NULL == g_list_find_custom(self->pf_sections, section->pfes_name, (GCompareFunc)section_name_cmp)) -- { -- log_warning("Problem format misses required section : '%s'", section->pfes_name); -- ++retval; -- } -- } -- -- /* Go through all the parsed (struct section_t)s check whether are all -- * known, i.e. each section is either one of the common sections (summary, -- * description, attach) or is present in the (struct extra_section)s. -- */ -- for (GList *iter = self->pf_sections; iter; iter = g_list_next(iter)) -- { -- section_t *section = (section_t *)iter->data; -- -- if (!problem_formatter_is_section_known(self, (section->name + 1))) -- { -- log_warning("Problem format contains unrecognized section : '%s'", section->name); -- ++retval; -- } -- } -- -- return retval; --} -- --int --problem_formatter_load_string(problem_formatter_t *self, const char *fmt) --{ -- const size_t len = strlen(fmt); -- if (len != 0) -- { -- FILE *fp = fmemopen((void *)fmt, len, "r"); -- if (fp == NULL) -- { -- error_msg("Not enough memory to open a stream for reading format string."); -- return -ENOMEM; -- } -- -- self->pf_sections = load_stream(fp); -- fclose(fp); -- } -- -- return problem_formatter_validate(self); --} -- --int --problem_formatter_load_file(problem_formatter_t *self, const char *path) --{ -- FILE *fp = stdin; -- if (strcmp(path, "-") != 0) -- { -- fp = fopen(path, "r"); -- if (!fp) -- return -ENOENT; -- } -- -- self->pf_sections = load_stream(fp); -- -- if (fp != stdin) -- fclose(fp); -- -- return problem_formatter_validate(self); --} -- --// generates report --int --problem_formatter_generate_report(const problem_formatter_t *self, problem_data_t *data, problem_report_t **report) --{ -- problem_report_t *pr = problem_report_new(); -- -- for (GList *iter = self->pf_extra_sections; iter; iter = g_list_next(iter)) -- problem_report_add_custom_section(pr, ((struct extra_section *)iter->data)->pfes_name); -- -- bool has_summary = false; -- for (GList *iter = self->pf_sections; iter; iter = g_list_next(iter)) -- { -- section_t *section = (section_t *)iter->data; -- -- /* %summary is something special */ -- if (strcmp(section->name, "%summary") == 0) -- { -- has_summary = true; -- format_percented_string((const char *)section->items->data, data, -- problem_report_get_buffer(pr, PR_SEC_SUMMARY)); -- } -- /* %attach as well */ -- else if (strcmp(section->name, "%attach") == 0) -- { -- problem_report_set_attachments(pr, get_attached_files(data, section->items, self->pf_sections)); -- } -- else /* %description or a custom section (e.g. %additional_info) */ -- { -- FILE *buffer = problem_report_get_buffer(pr, section->name + 1); -- -- if (buffer != NULL) -- { -- log_debug("Formatting section : '%s'", section->name); -- format_section(section, data, self->pf_sections, buffer); -- } -- else -- log_warning("Unsupported section '%s'", section->name); -- } -- } -- -- if (!has_summary) { -- log_debug("Problem format misses section '%%summary'. Using the default one : '%s'.", -- self->pf_default_summary); -- -- format_percented_string(self->pf_default_summary, -- data, problem_report_get_buffer(pr, PR_SEC_SUMMARY)); -- } -- -- *report = pr; -- return 0; --} -diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am -index 27194a4..f4f94ff 100644 ---- a/src/plugins/Makefile.am -+++ b/src/plugins/Makefile.am -@@ -125,6 +125,17 @@ EXTRA_DIST = $(reporters_extra_dist) \ - $(DESTDIR)/$(DEBUG_INFO_DIR): - $(mkdir_p) '$@' - -+noinst_LIBRARIES = libreport-problem-report.a -+libreport_problem_report_a_SOURCES = \ -+ problem_report.c \ -+ problem_report.h -+libreport_problem_report_a_CFLAGS = \ -+ -I$(srcdir)/../include \ -+ $(LIBREPORT_CFLAGS) \ -+ $(GLIB_CFLAGS) \ -+ $(SATYR_CFLAGS) \ -+ -D_GNU_SOURCE -+ - if BUILD_BUGZILLA - reporter_bugzilla_SOURCES = \ - reporter-bugzilla.c rhbz.c rhbz.h -@@ -146,7 +157,8 @@ reporter_bugzilla_LDADD = \ - $(GLIB_LIBS) \ - $(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS) \ - ../lib/libreport-web.la \ -- ../lib/libreport.la -+ ../lib/libreport.la \ -+ libreport-problem-report.a - endif - - if BUILD_MANTISBT -@@ -169,7 +181,8 @@ reporter_mantisbt_CPPFLAGS = \ - reporter_mantisbt_LDADD = \ - $(GLIB_LIBS) \ - ../lib/libreport-web.la \ -- ../lib/libreport.la -+ ../lib/libreport.la \ -+ libreport-problem-report.a - endif - - reporter_rhtsupport_SOURCES = \ -@@ -196,7 +209,8 @@ reporter_rhtsupport_LDADD = \ - $(GLIB_LIBS) \ - $(LIBXML_LIBS) \ - ../lib/libreport-web.la \ -- ../lib/libreport.la -+ ../lib/libreport.la \ -+ libreport-problem-report.a - - reporter_upload_SOURCES = \ - reporter-upload.c -@@ -255,7 +269,9 @@ reporter_mailx_CPPFLAGS = \ - $(LIBREPORT_CFLAGS) \ - -D_GNU_SOURCE - reporter_mailx_LDADD = \ -- ../lib/libreport.la -+ ../lib/libreport.la \ -+ $(SATYR_LIBS) \ -+ libreport-problem-report.a - - reporter_print_SOURCES = \ - reporter-print.c -diff --git a/src/plugins/problem_report.c b/src/plugins/problem_report.c -new file mode 100644 -index 0000000..2bf5530 ---- /dev/null -+++ b/src/plugins/problem_report.c -@@ -0,0 +1,1210 @@ -+/* -+ 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 "problem_report.h" -+#include "internal_libreport.h" -+ -+#include <satyr/stacktrace.h> -+#include <satyr/abrt.h> -+ -+#include <assert.h> -+ -+#define DESTROYED_POINTER (void *)0xdeadbeef -+ -+/* FORMAT: -+ * |%summary:: Hello, world -+ * |Problem description:: %bare_comment -+ * | -+ * |Package:: package -+ * | -+ * |%attach: %binary, backtrace -+ * | -+ * |%additional_info:: -+ * |%reporter% -+ * |User:: user_name,uid -+ * | -+ * |Directories:: root,cwd -+ * -+ * PARSED DATA (list of struct section_t): -+ * { -+ * section_t { -+ * .name = '%summary'; -+ * .items = { 'Hello, world' }; -+ * .children = NULL; -+ * }, -+ * section_t { -+ * .name = '%attach' -+ * .items = { '%binary', 'backtrace' }; -+ * .children = NULL; -+ * }, -+ * section_t { -+ * .name = '%description' -+ * .items = NULL; -+ * .children = { -+ * section_t { -+ * .name = 'Problem description:'; -+ * .items = { '%bare_comment' }; -+ * .children = NULL; -+ * }, -+ * section_t { -+ * .name = ''; -+ * .items = NULL; -+ * .children = NULL; -+ * }, -+ * section_t { -+ * .name = 'Package:'; -+ * .items = { 'package' }; -+ * .children = NULL; -+ * }, -+ * } -+ * }, -+ * section_t { -+ * .name = '%additional_info' -+ * .items = { '%reporter%' }; -+ * .children = { -+ * section_t { -+ * .name = 'User:'; -+ * .items = { 'user_name', 'uid' }; -+ * .children = NULL; -+ * }, -+ * section_t { -+ * .name = ''; -+ * .items = NULL; -+ * .children = NULL; -+ * }, -+ * section_t { -+ * .name = 'Directories:'; -+ * .items = { 'root', 'cwd' }; -+ * .children = NULL; -+ * }, -+ * } -+ * } -+ * } -+ */ -+struct section_t { -+ char *name; ///< name or output text (%summar, 'Package version:'); -+ GList *items; ///< list of file names and special items (%reporter, %binar, ...) -+ GList *children; ///< list of sub sections (struct section_t) -+}; -+ -+typedef struct section_t section_t; -+ -+static section_t * -+section_new(const char *name) -+{ -+ section_t *self = xmalloc(sizeof(*self)); -+ self->name = xstrdup(name); -+ self->items = NULL; -+ self->children = NULL; -+ -+ return self; -+} -+ -+static void -+section_free(section_t *self) -+{ -+ if (self == NULL) -+ return; -+ -+ free(self->name); -+ g_list_free_full(self->items, free); -+ g_list_free_full(self->children, (GDestroyNotify)section_free); -+ -+ free(self); -+} -+ -+static int -+section_name_cmp(section_t *lhs, const char *rhs) -+{ -+ return strcmp((lhs->name + 1), rhs); -+} -+ -+/* Utility functions */ -+ -+static GList* -+split_string_on_char(const char *str, char ch) -+{ -+ GList *list = NULL; -+ for (;;) -+ { -+ const char *delim = strchrnul(str, ch); -+ list = g_list_prepend(list, xstrndup(str, delim - str)); -+ if (*delim == '\0') -+ break; -+ str = delim + 1; -+ } -+ return g_list_reverse(list); -+} -+ -+static int -+compare_item_name(const char *lookup, const char *name) -+{ -+ if (lookup[0] == '-') -+ lookup++; -+ else if (strncmp(lookup, "%bare_", 6) == 0) -+ lookup += 6; -+ return strcmp(lookup, name); -+} -+ -+static int -+is_item_name_in_section(const section_t *lookup, const char *name) -+{ -+ if (g_list_find_custom(lookup->items, name, (GCompareFunc)compare_item_name)) -+ return 0; /* "found it!" */ -+ return 1; -+} -+ -+static bool is_explicit_or_forbidden(const char *name, GList *comment_fmt_spec); -+ -+static int -+is_explicit_or_forbidden_child(const section_t *master_section, const char *name) -+{ -+ if (is_explicit_or_forbidden(name, master_section->children)) -+ return 0; /* "found it!" */ -+ return 1; -+} -+ -+/* For example: 'package' belongs to '%oneline', but 'package' is used in -+ * 'Version of component', so it is not very helpful to include that file once -+ * more in another section -+ */ -+static bool -+is_explicit_or_forbidden(const char *name, GList *comment_fmt_spec) -+{ -+ return g_list_find_custom(comment_fmt_spec, name, (GCompareFunc)is_item_name_in_section) -+ || g_list_find_custom(comment_fmt_spec, name, (GCompareFunc)is_explicit_or_forbidden_child); -+} -+ -+static GList* -+load_stream(FILE *fp) -+{ -+ assert(fp); -+ -+ GList *sections = NULL; -+ section_t *master = section_new("%description"); -+ section_t *sec = NULL; -+ -+ sections = g_list_append(sections, master); -+ -+ char *line; -+ while ((line = xmalloc_fgetline(fp)) != NULL) -+ { -+ /* Skip comments */ -+ char first = *skip_whitespace(line); -+ if (first == '#') -+ goto free_line; -+ -+ /* Handle trailing backslash continuation */ -+ check_continuation: ; -+ unsigned len = strlen(line); -+ if (len && line[len-1] == '\\') -+ { -+ line[len-1] = '\0'; -+ char *next_line = xmalloc_fgetline(fp); -+ if (next_line) -+ { -+ line = append_to_malloced_string(line, next_line); -+ free(next_line); -+ goto check_continuation; -+ } -+ } -+ -+ /* We are reusing line buffer to form temporary -+ * "key\0values\0..." in its beginning -+ */ -+ bool summary_line = false; -+ char *value = NULL; -+ char *src; -+ char *dst; -+ for (src = dst = line; *src; src++) -+ { -+ char c = *src; -+ /* did we reach the value list? */ -+ if (!value && c == ':' && src[1] == ':') -+ { -+ *dst++ = '\0'; /* terminate key */ -+ src += 1; -+ value = dst; /* remember where value starts */ -+ summary_line = (strcmp(line, "%summary") == 0); -+ if (summary_line) -+ { -+ value = (src + 1); -+ break; -+ } -+ continue; -+ } -+ /* skip whitespace in value list */ -+ if (value && isspace(c)) -+ continue; -+ *dst++ = c; /* store next key or value char */ -+ } -+ -+ GList *item_list = NULL; -+ if (summary_line) -+ { -+ /* %summary is special */ -+ item_list = g_list_append(NULL, xstrdup(skip_whitespace(value))); -+ } -+ else -+ { -+ *dst = '\0'; /* terminate value (or key) */ -+ if (value) -+ item_list = split_string_on_char(value, ','); -+ } -+ -+ sec = section_new(line); -+ sec->items = item_list; -+ -+ if (sec->name[0] == '%') -+ { -+ if (!summary_line && strcmp(sec->name, "%attach") != 0) -+ { -+ master->children = g_list_reverse(master->children); -+ master = sec; -+ } -+ -+ sections = g_list_prepend(sections, sec); -+ } -+ else -+ master->children = g_list_prepend(master->children, sec); -+ -+ free_line: -+ free(line); -+ } -+ -+ /* If master equals sec, then master's children list was not yet reversed. -+ * -+ * %description is the default section (i.e is not explicitly mentioned) -+ * and %summary nor %attach cause its children list to reverse. -+ */ -+ if (master == sec || strcmp(master->name, "%description") == 0) -+ master->children = g_list_reverse(master->children); -+ -+ return sections; -+} -+ -+ -+/* Summary generation */ -+ -+#define MAX_OPT_DEPTH 10 -+static int -+format_percented_string(const char *str, problem_data_t *pd, FILE *result) -+{ -+ long old_pos[MAX_OPT_DEPTH] = { 0 }; -+ int okay[MAX_OPT_DEPTH] = { 1 }; -+ long len = 0; -+ int opt_depth = 1; -+ -+ while (*str) { -+ switch (*str) { -+ default: -+ putc(*str, result); -+ len++; -+ str++; -+ break; -+ case '\\': -+ if (str[1]) -+ str++; -+ putc(*str, result); -+ len++; -+ str++; -+ break; -+ case '[': -+ if (str[1] == '[' && opt_depth < MAX_OPT_DEPTH) -+ { -+ old_pos[opt_depth] = len; -+ okay[opt_depth] = 1; -+ opt_depth++; -+ str += 2; -+ } else { -+ putc(*str, result); -+ len++; -+ str++; -+ } -+ break; -+ case ']': -+ if (str[1] == ']' && opt_depth > 1) -+ { -+ opt_depth--; -+ if (!okay[opt_depth]) -+ { -+ if (fseek(result, old_pos[opt_depth], SEEK_SET) < 0) -+ perror_msg_and_die("fseek"); -+ len = old_pos[opt_depth]; -+ } -+ str += 2; -+ } else { -+ putc(*str, result); -+ len++; -+ str++; -+ } -+ break; -+ case '%': ; -+ char *nextpercent = strchr(++str, '%'); -+ if (!nextpercent) -+ { -+ error_msg_and_die("Unterminated %%element%%: '%s'", str - 1); -+ } -+ -+ *nextpercent = '\0'; -+ const problem_item *item = problem_data_get_item_or_NULL(pd, str); -+ *nextpercent = '%'; -+ -+ if (item && (item->flags & CD_FLAG_TXT)) -+ { -+ fputs(item->content, result); -+ len += strlen(item->content); -+ } -+ else -+ okay[opt_depth - 1] = 0; -+ str = nextpercent + 1; -+ break; -+ } -+ } -+ -+ if (opt_depth > 1) -+ { -+ error_msg_and_die("Unbalanced [[ ]] bracket"); -+ } -+ -+ if (!okay[0]) -+ { -+ error_msg("Undefined variable outside of [[ ]] bracket"); -+ } -+ -+ return 0; -+} -+ -+/* BZ comment generation */ -+ -+static int -+append_text(struct strbuf *result, const char *item_name, const char *content, bool print_item_name) -+{ -+ char *eol = strchrnul(content, '\n'); -+ if (eol[0] == '\0' || eol[1] == '\0') -+ { -+ /* one-liner */ -+ int pad = 16 - (strlen(item_name) + 2); -+ if (pad < 0) -+ pad = 0; -+ if (print_item_name) -+ strbuf_append_strf(result, -+ eol[0] == '\0' ? "%s: %*s%s\n" : "%s: %*s%s", -+ item_name, pad, "", content -+ ); -+ else -+ strbuf_append_strf(result, -+ eol[0] == '\0' ? "%s\n" : "%s", -+ content -+ ); -+ } -+ else -+ { -+ /* multi-line item */ -+ if (print_item_name) -+ strbuf_append_strf(result, "%s:\n", item_name); -+ for (;;) -+ { -+ eol = strchrnul(content, '\n'); -+ strbuf_append_strf(result, -+ /* For %bare_multiline_item, we don't want to print colons */ -+ (print_item_name ? ":%.*s\n" : "%.*s\n"), -+ (int)(eol - content), content -+ ); -+ if (eol[0] == '\0' || eol[1] == '\0') -+ break; -+ content = eol + 1; -+ } -+ } -+ return 1; -+} -+ -+static int -+append_short_backtrace(struct strbuf *result, problem_data_t *problem_data, size_t max_text_size, bool print_item_name) -+{ -+ const problem_item *item = problem_data_get_item_or_NULL(problem_data, -+ FILENAME_BACKTRACE); -+ if (!item) -+ return 0; /* "I did not print anything" */ -+ if (!(item->flags & CD_FLAG_TXT)) -+ return 0; /* "I did not print anything" */ -+ -+ char *truncated = NULL; -+ -+ if (strlen(item->content) >= max_text_size) -+ { -+ log_debug("'backtrace' exceeds the text file size, going to append its short version"); -+ -+ char *error_msg = NULL; -+ const char *type = problem_data_get_content_or_NULL(problem_data, FILENAME_TYPE); -+ if (!type) -+ { -+ log_debug("Problem data does not contain '"FILENAME_TYPE"' file"); -+ return 0; -+ } -+ -+ /* For CCpp crashes, use the GDB-produced backtrace which should be -+ * available by now. sr_abrt_type_from_analyzer returns SR_REPORT_CORE -+ * by default for CCpp crashes. -+ */ -+ enum sr_report_type report_type = sr_abrt_type_from_analyzer(type); -+ if (strcmp(type, "CCpp") == 0) -+ { -+ log_debug("Successfully identified 'CCpp' abrt type"); -+ report_type = SR_REPORT_GDB; -+ } -+ -+ struct sr_stacktrace *backtrace = sr_stacktrace_parse(report_type, -+ item->content, &error_msg); -+ -+ if (!backtrace) -+ { -+ log(_("Can't parse backtrace: %s"), error_msg); -+ free(error_msg); -+ return 0; -+ } -+ -+ /* Get optimized thread stack trace for 10 top most frames */ -+ truncated = sr_stacktrace_to_short_text(backtrace, 10); -+ sr_stacktrace_free(backtrace); -+ -+ if (!truncated) -+ { -+ log(_("Can't generate stacktrace description (no crash thread?)")); -+ return 0; -+ } -+ } -+ else -+ { -+ log_debug("'backtrace' is small enough to be included as is"); -+ } -+ -+ append_text(result, -+ /*item_name:*/ truncated ? "truncated_backtrace" : FILENAME_BACKTRACE, -+ /*content:*/ truncated ? truncated : item->content, -+ print_item_name -+ ); -+ free(truncated); -+ return 1; -+} -+ -+static int -+append_item(struct strbuf *result, const char *item_name, problem_data_t *pd, GList *comment_fmt_spec) -+{ -+ bool print_item_name = (strncmp(item_name, "%bare_", strlen("%bare_")) != 0); -+ if (!print_item_name) -+ item_name += strlen("%bare_"); -+ -+ if (item_name[0] != '%') -+ { -+ struct problem_item *item = problem_data_get_item_or_NULL(pd, item_name); -+ if (!item) -+ return 0; /* "I did not print anything" */ -+ if (!(item->flags & CD_FLAG_TXT)) -+ return 0; /* "I did not print anything" */ -+ -+ char *formatted = problem_item_format(item); -+ char *content = formatted ? formatted : item->content; -+ append_text(result, item_name, content, print_item_name); -+ free(formatted); -+ return 1; /* "I printed something" */ -+ } -+ -+ /* Special item name */ -+ -+ /* Compat with previously-existed ad-hockery: %short_backtrace */ -+ if (strcmp(item_name, "%short_backtrace") == 0) -+ return append_short_backtrace(result, pd, CD_TEXT_ATT_SIZE_BZ, print_item_name); -+ -+ /* Compat with previously-existed ad-hockery: %reporter */ -+ if (strcmp(item_name, "%reporter") == 0) -+ return append_text(result, "reporter", PACKAGE"-"VERSION, print_item_name); -+ -+ /* %oneline,%multiline,%text */ -+ bool oneline = (strcmp(item_name+1, "oneline" ) == 0); -+ bool multiline = (strcmp(item_name+1, "multiline") == 0); -+ bool text = (strcmp(item_name+1, "text" ) == 0); -+ if (!oneline && !multiline && !text) -+ { -+ log("Unknown or unsupported element specifier '%s'", item_name); -+ return 0; /* "I did not print anything" */ -+ } -+ -+ int printed = 0; -+ -+ /* Iterate over _sorted_ items */ -+ GList *sorted_names = g_hash_table_get_keys(pd); -+ sorted_names = g_list_sort(sorted_names, (GCompareFunc)strcmp); -+ -+ /* %text => do as if %oneline, then repeat as if %multiline */ -+ if (text) -+ oneline = 1; -+ -+ again: ; -+ GList *l = sorted_names; -+ while (l) -+ { -+ const char *name = l->data; -+ l = l->next; -+ struct problem_item *item = g_hash_table_lookup(pd, name); -+ if (!item) -+ continue; /* paranoia, won't happen */ -+ -+ if (!(item->flags & CD_FLAG_TXT)) -+ continue; -+ -+ if (is_explicit_or_forbidden(name, comment_fmt_spec)) -+ continue; -+ -+ char *formatted = problem_item_format(item); -+ char *content = formatted ? formatted : item->content; -+ char *eol = strchrnul(content, '\n'); -+ bool is_oneline = (eol[0] == '\0' || eol[1] == '\0'); -+ if (oneline == is_oneline) -+ printed |= append_text(result, name, content, print_item_name); -+ free(formatted); -+ } -+ if (text && oneline) -+ { -+ /* %text, and we just did %oneline. Repeat as if %multiline */ -+ oneline = 0; -+ /*multiline = 1; - not checked in fact, so why bother setting? */ -+ goto again; -+ } -+ -+ g_list_free(sorted_names); /* names themselves are not freed */ -+ -+ return printed; -+} -+ -+#define add_to_section_output(format, ...) \ -+ do { \ -+ for (; empty_lines > 0; --empty_lines) fputc('\n', result); \ -+ empty_lines = 0; \ -+ fprintf(result, format, __VA_ARGS__); \ -+ } while (0) -+ -+static void -+format_section(section_t *section, problem_data_t *pd, GList *comment_fmt_spec, FILE *result) -+{ -+ int empty_lines = -1; -+ -+ for (GList *iter = section->children; iter; iter = g_list_next(iter)) -+ { -+ section_t *child = (section_t *)iter->data; -+ if (child->items) -+ { -+ /* "Text: item[,item]..." */ -+ struct strbuf *output = strbuf_new(); -+ GList *item = child->items; -+ while (item) -+ { -+ const char *str = item->data; -+ item = item->next; -+ if (str[0] == '-') /* "-name", ignore it */ -+ continue; -+ append_item(output, str, pd, comment_fmt_spec); -+ } -+ -+ if (output->len != 0) -+ add_to_section_output((child->name[0] ? "%s:\n%s" : "%s%s"), -+ child->name, output->buf); -+ -+ strbuf_free(output); -+ } -+ else -+ { -+ /* Just "Text" (can be "") */ -+ -+ /* Filter out trailint empty lines */ -+ if (child->name[0] != '\0') -+ add_to_section_output("%s\n", child->name); -+ /* Do not count empty lines, if output wasn't yet produced */ -+ else if (empty_lines >= 0) -+ ++empty_lines; -+ } -+ } -+} -+ -+static GList * -+get_special_items(const char *item_name, problem_data_t *pd, GList *comment_fmt_spec) -+{ -+ /* %oneline,%multiline,%text,%binary */ -+ bool oneline = (strcmp(item_name+1, "oneline" ) == 0); -+ bool multiline = (strcmp(item_name+1, "multiline") == 0); -+ bool text = (strcmp(item_name+1, "text" ) == 0); -+ bool binary = (strcmp(item_name+1, "binary" ) == 0); -+ if (!oneline && !multiline && !text && !binary) -+ { -+ log("Unknown or unsupported element specifier '%s'", item_name); -+ return NULL; -+ } -+ -+ log_debug("Special item_name '%s', iterating for attach...", item_name); -+ GList *result = 0; -+ -+ /* Iterate over _sorted_ items */ -+ GList *sorted_names = g_hash_table_get_keys(pd); -+ sorted_names = g_list_sort(sorted_names, (GCompareFunc)strcmp); -+ -+ GList *l = sorted_names; -+ while (l) -+ { -+ const char *name = l->data; -+ l = l->next; -+ struct problem_item *item = g_hash_table_lookup(pd, name); -+ if (!item) -+ continue; /* paranoia, won't happen */ -+ -+ if (is_explicit_or_forbidden(name, comment_fmt_spec)) -+ continue; -+ -+ if ((item->flags & CD_FLAG_TXT) && !binary) -+ { -+ char *content = item->content; -+ char *eol = strchrnul(content, '\n'); -+ bool is_oneline = (eol[0] == '\0' || eol[1] == '\0'); -+ if (text || oneline == is_oneline) -+ result = g_list_append(result, xstrdup(name)); -+ } -+ else if ((item->flags & CD_FLAG_BIN) && binary) -+ result = g_list_append(result, xstrdup(name)); -+ } -+ -+ g_list_free(sorted_names); /* names themselves are not freed */ -+ -+ -+ log_debug("...Done iterating over '%s' for attach", item_name); -+ -+ return result; -+} -+ -+static GList * -+get_attached_files(problem_data_t *pd, GList *items, GList *comment_fmt_spec) -+{ -+ GList *result = NULL; -+ GList *item = items; -+ while (item != NULL) -+ { -+ const char *item_name = item->data; -+ item = item->next; -+ if (item_name[0] == '-') /* "-name", ignore it */ -+ continue; -+ -+ if (item_name[0] != '%') -+ { -+ result = g_list_append(result, xstrdup(item_name)); -+ continue; -+ } -+ -+ GList *special = get_special_items(item_name, pd, comment_fmt_spec); -+ if (special == NULL) -+ { -+ log_notice("No attachment found for '%s'", item_name); -+ continue; -+ } -+ -+ result = g_list_concat(result, special); -+ } -+ -+ return result; -+} -+ -+/* -+ * Problem Report - memor stream -+ * -+ * A wrapper for POSIX memory stream. -+ * -+ * A memory stream is presented as FILE *. -+ * -+ * A memory stream is associated with a pointer to written data and a pointer -+ * to size of the written data. -+ * -+ * This structure holds all of the used pointers. -+ */ -+struct memstream_buffer -+{ -+ char *msb_buffer; -+ size_t msb_size; -+ FILE *msb_stream; -+}; -+ -+static struct memstream_buffer * -+memstream_buffer_new() -+{ -+ struct memstream_buffer *self = xmalloc(sizeof(*self)); -+ -+ self->msb_buffer = NULL; -+ self->msb_stream = open_memstream(&(self->msb_buffer), &(self->msb_size)); -+ -+ return self; -+} -+ -+static void -+memstream_buffer_free(struct memstream_buffer *self) -+{ -+ if (self == NULL) -+ return; -+ -+ fclose(self->msb_stream); -+ self->msb_stream = DESTROYED_POINTER; -+ -+ free(self->msb_buffer); -+ self->msb_buffer = DESTROYED_POINTER; -+ -+ free(self); -+} -+ -+static FILE * -+memstream_get_stream(struct memstream_buffer *self) -+{ -+ assert(self != NULL); -+ -+ return self->msb_stream; -+} -+ -+static const char * -+memstream_get_string(struct memstream_buffer *self) -+{ -+ assert(self != NULL); -+ assert(self->msb_stream != NULL); -+ -+ fflush(self->msb_stream); -+ -+ return self->msb_buffer; -+} -+ -+ -+/* -+ * Problem Report -+ * -+ * The formated strings are internaly stored in "buffer"s. If a programer wants -+ * to get a formated section data, a getter function extracts those data from -+ * the apropriate buffer and returns them in form of null-terminated string. -+ * -+ * Each section has own buffer. -+ * -+ * There are three common sections that are always present: -+ * 1. summary -+ * 2. description -+ * 3. attach -+ * Buffers of these sections has own structure member for the sake of -+ * efficiency. -+ * -+ * The custom sections hash their buffers stored in a map where key is a -+ * section's name and value is a section's buffer. -+ * -+ * Problem report provides the programers with the possibility to ammend -+ * formated output to any section buffer. -+ */ -+struct problem_report -+{ -+ struct memstream_buffer *pr_sec_summ; ///< %summary buffer -+ struct memstream_buffer *pr_sec_desc; ///< %description buffer -+ GList *pr_attachments; ///< %attach - list of file names -+ GHashTable *pr_sec_custom; ///< map : %(custom section) -> buffer -+}; -+ -+static problem_report_t * -+problem_report_new() -+{ -+ problem_report_t *self = xmalloc(sizeof(*self)); -+ -+ self->pr_sec_summ = memstream_buffer_new(); -+ self->pr_sec_desc = memstream_buffer_new(); -+ self->pr_attachments = NULL; -+ self->pr_sec_custom = NULL; -+ -+ return self; -+} -+ -+static void -+problem_report_initialize_custom_sections(problem_report_t *self) -+{ -+ assert(self != NULL); -+ assert(self->pr_sec_custom == NULL); -+ -+ self->pr_sec_custom = g_hash_table_new_full(g_str_hash, g_str_equal, free, -+ (GDestroyNotify)memstream_buffer_free); -+} -+ -+static void -+problem_report_destroy_custom_sections(problem_report_t *self) -+{ -+ assert(self != NULL); -+ assert(self->pr_sec_custom != NULL); -+ -+ g_hash_table_destroy(self->pr_sec_custom); -+} -+ -+static int -+problem_report_add_custom_section(problem_report_t *self, const char *name) -+{ -+ assert(self != NULL); -+ -+ if (self->pr_sec_custom == NULL) -+ { -+ problem_report_initialize_custom_sections(self); -+ } -+ -+ if (problem_report_get_buffer(self, name)) -+ { -+ log_warning("Custom section already exists : '%s'", name); -+ return -EEXIST; -+ } -+ -+ log_debug("Problem report enriched with section : '%s'", name); -+ g_hash_table_insert(self->pr_sec_custom, xstrdup(name), memstream_buffer_new()); -+ return 0; -+} -+ -+static struct memstream_buffer * -+problem_report_get_section_buffer(const problem_report_t *self, const char *section_name) -+{ -+ if (self->pr_sec_custom == NULL) -+ { -+ log_debug("Couldn't find section '%s': no custom section added", section_name); -+ return NULL; -+ } -+ -+ return (struct memstream_buffer *)g_hash_table_lookup(self->pr_sec_custom, section_name); -+} -+ -+problem_report_buffer * -+problem_report_get_buffer(const problem_report_t *self, const char *section_name) -+{ -+ assert(self != NULL); -+ assert(section_name != NULL); -+ -+ if (strcmp(PR_SEC_SUMMARY, section_name) == 0) -+ return memstream_get_stream(self->pr_sec_summ); -+ -+ if (strcmp(PR_SEC_DESCRIPTION, section_name) == 0) -+ return memstream_get_stream(self->pr_sec_desc); -+ -+ struct memstream_buffer *buf = problem_report_get_section_buffer(self, section_name); -+ return buf == NULL ? NULL : memstream_get_stream(buf); -+} -+ -+const char * -+problem_report_get_summary(const problem_report_t *self) -+{ -+ assert(self != NULL); -+ -+ return memstream_get_string(self->pr_sec_summ); -+} -+ -+const char * -+problem_report_get_description(const problem_report_t *self) -+{ -+ assert(self != NULL); -+ -+ return memstream_get_string(self->pr_sec_desc); -+} -+ -+const char * -+problem_report_get_section(const problem_report_t *self, const char *section_name) -+{ -+ assert(self != NULL); -+ assert(section_name); -+ -+ struct memstream_buffer *buf = problem_report_get_section_buffer(self, section_name); -+ -+ if (buf == NULL) -+ return NULL; -+ -+ return memstream_get_string(buf); -+} -+ -+static void -+problem_report_set_attachments(problem_report_t *self, GList *attachments) -+{ -+ assert(self != NULL); -+ assert(self->pr_attachments == NULL); -+ -+ self->pr_attachments = attachments; -+} -+ -+GList * -+problem_report_get_attachments(const problem_report_t *self) -+{ -+ assert(self != NULL); -+ -+ return self->pr_attachments; -+} -+ -+void -+problem_report_free(problem_report_t *self) -+{ -+ if (self == NULL) -+ return; -+ -+ memstream_buffer_free(self->pr_sec_summ); -+ self->pr_sec_summ = DESTROYED_POINTER; -+ -+ memstream_buffer_free(self->pr_sec_desc); -+ self->pr_sec_desc = DESTROYED_POINTER; -+ -+ g_list_free_full(self->pr_attachments, free); -+ self->pr_attachments = DESTROYED_POINTER; -+ -+ if (self->pr_sec_custom) -+ { -+ problem_report_destroy_custom_sections(self); -+ self->pr_sec_custom = DESTROYED_POINTER; -+ } -+ -+ free(self); -+} -+ -+/* -+ * Problem Formatter - extra section -+ */ -+struct extra_section -+{ -+ char *pfes_name; ///< name with % prefix -+ int pfes_flags; ///< whether is required or not -+}; -+ -+static struct extra_section * -+extra_section_new(const char *name, int flags) -+{ -+ struct extra_section *self = xmalloc(sizeof(*self)); -+ -+ self->pfes_name = xstrdup(name); -+ self->pfes_flags = flags; -+ -+ return self; -+} -+ -+static void -+extra_section_free(struct extra_section *self) -+{ -+ if (self == NULL) -+ return; -+ -+ free(self->pfes_name); -+ self->pfes_name = DESTROYED_POINTER; -+ -+ free(self); -+} -+ -+static int -+extra_section_name_cmp(struct extra_section *lhs, const char *rhs) -+{ -+ return strcmp(lhs->pfes_name, rhs); -+} -+ -+/* -+ * Problem Formatter -+ * -+ * Holds parsed sections lists. -+ */ -+struct problem_formatter -+{ -+ GList *pf_sections; ///< parsed sections (struct section_t) -+ GList *pf_extra_sections; ///< user configured sections (struct extra_section) -+ char *pf_default_summary; ///< default summary format -+}; -+ -+problem_formatter_t * -+problem_formatter_new(void) -+{ -+ problem_formatter_t *self = xzalloc(sizeof(*self)); -+ -+ self->pf_default_summary = xstrdup("%reason%"); -+ -+ return self; -+} -+ -+void -+problem_formatter_free(problem_formatter_t *self) -+{ -+ if (self == NULL) -+ return; -+ -+ g_list_free_full(self->pf_sections, (GDestroyNotify)section_free); -+ self->pf_sections = DESTROYED_POINTER; -+ -+ g_list_free_full(self->pf_extra_sections, (GDestroyNotify)extra_section_free); -+ self->pf_extra_sections = DESTROYED_POINTER; -+ -+ free(self->pf_default_summary); -+ self->pf_default_summary = DESTROYED_POINTER; -+ -+ free(self); -+} -+ -+static int -+problem_formatter_is_section_known(problem_formatter_t *self, const char *name) -+{ -+ return strcmp(name, "summary") == 0 -+ || strcmp(name, "attach") == 0 -+ || strcmp(name, "description") == 0 -+ || NULL != g_list_find_custom(self->pf_extra_sections, name, (GCompareFunc)extra_section_name_cmp); -+} -+ -+// i.e additional_info -> no flags -+int -+problem_formatter_add_section(problem_formatter_t *self, const char *name, int flags) -+{ -+ /* Do not add already added sections */ -+ if (problem_formatter_is_section_known(self, name)) -+ { -+ log_debug("Extra section already exists : '%s' ", name); -+ return -EEXIST; -+ } -+ -+ self->pf_extra_sections = g_list_prepend(self->pf_extra_sections, -+ extra_section_new(name, flags)); -+ -+ return 0; -+} -+ -+// check format validity and produce warnings -+static int -+problem_formatter_validate(problem_formatter_t *self) -+{ -+ int retval = 0; -+ -+ /* Go through all (struct extra_section)s and check whete those having flag -+ * PFFF_REQUIRED are present in the parsed (struct section_t)s. -+ */ -+ for (GList *iter = self->pf_extra_sections; iter; iter = g_list_next(iter)) -+ { -+ struct extra_section *section = (struct extra_section *)iter->data; -+ -+ log_debug("Validating extra section : '%s'", section->pfes_name); -+ -+ if ( (PFFF_REQUIRED & section->pfes_flags) -+ && NULL == g_list_find_custom(self->pf_sections, section->pfes_name, (GCompareFunc)section_name_cmp)) -+ { -+ log_warning("Problem format misses required section : '%s'", section->pfes_name); -+ ++retval; -+ } -+ } -+ -+ /* Go through all the parsed (struct section_t)s check whether are all -+ * known, i.e. each section is either one of the common sections (summary, -+ * description, attach) or is present in the (struct extra_section)s. -+ */ -+ for (GList *iter = self->pf_sections; iter; iter = g_list_next(iter)) -+ { -+ section_t *section = (section_t *)iter->data; -+ -+ if (!problem_formatter_is_section_known(self, (section->name + 1))) -+ { -+ log_warning("Problem format contains unrecognized section : '%s'", section->name); -+ ++retval; -+ } -+ } -+ -+ return retval; -+} -+ -+int -+problem_formatter_load_string(problem_formatter_t *self, const char *fmt) -+{ -+ const size_t len = strlen(fmt); -+ if (len != 0) -+ { -+ FILE *fp = fmemopen((void *)fmt, len, "r"); -+ if (fp == NULL) -+ { -+ error_msg("Not enough memory to open a stream for reading format string."); -+ return -ENOMEM; -+ } -+ -+ self->pf_sections = load_stream(fp); -+ fclose(fp); -+ } -+ -+ return problem_formatter_validate(self); -+} -+ -+int -+problem_formatter_load_file(problem_formatter_t *self, const char *path) -+{ -+ FILE *fp = stdin; -+ if (strcmp(path, "-") != 0) -+ { -+ fp = fopen(path, "r"); -+ if (!fp) -+ return -ENOENT; -+ } -+ -+ self->pf_sections = load_stream(fp); -+ -+ if (fp != stdin) -+ fclose(fp); -+ -+ return problem_formatter_validate(self); -+} -+ -+// generates report -+int -+problem_formatter_generate_report(const problem_formatter_t *self, problem_data_t *data, problem_report_t **report) -+{ -+ problem_report_t *pr = problem_report_new(); -+ -+ for (GList *iter = self->pf_extra_sections; iter; iter = g_list_next(iter)) -+ problem_report_add_custom_section(pr, ((struct extra_section *)iter->data)->pfes_name); -+ -+ bool has_summary = false; -+ for (GList *iter = self->pf_sections; iter; iter = g_list_next(iter)) -+ { -+ section_t *section = (section_t *)iter->data; -+ -+ /* %summary is something special */ -+ if (strcmp(section->name, "%summary") == 0) -+ { -+ has_summary = true; -+ format_percented_string((const char *)section->items->data, data, -+ problem_report_get_buffer(pr, PR_SEC_SUMMARY)); -+ } -+ /* %attach as well */ -+ else if (strcmp(section->name, "%attach") == 0) -+ { -+ problem_report_set_attachments(pr, get_attached_files(data, section->items, self->pf_sections)); -+ } -+ else /* %description or a custom section (e.g. %additional_info) */ -+ { -+ FILE *buffer = problem_report_get_buffer(pr, section->name + 1); -+ -+ if (buffer != NULL) -+ { -+ log_debug("Formatting section : '%s'", section->name); -+ format_section(section, data, self->pf_sections, buffer); -+ } -+ else -+ log_warning("Unsupported section '%s'", section->name); -+ } -+ } -+ -+ if (!has_summary) { -+ log_debug("Problem format misses section '%%summary'. Using the default one : '%s'.", -+ self->pf_default_summary); -+ -+ format_percented_string(self->pf_default_summary, -+ data, problem_report_get_buffer(pr, PR_SEC_SUMMARY)); -+ } -+ -+ *report = pr; -+ return 0; -+} -diff --git a/src/plugins/problem_report.h b/src/plugins/problem_report.h -new file mode 100644 -index 0000000..f2d41d8 ---- /dev/null -+++ b/src/plugins/problem_report.h -@@ -0,0 +1,334 @@ -+/* -+ 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. -+ -+ @brief API for formating of problem data -+ -+ These functions can be used to convert a problem data to its string -+ representation. -+ -+ The output format can be parsed from a string: -+ -+ problem_formatter_t *formatter = problem_formatter_new(); -+ problem_formatter_load_string(formatter, MY_FORMAT_STRING); -+ -+ or loaded from a file: -+ -+ problem_formatter_t *formatter = problem_formatter_new(); -+ problem_formatter_load_file(formatter, MY_FORMAT_FILE); -+ -+ Once you have configured your formatter you can convert problem_data to -+ problem_report by calling: -+ -+ problem_report_t *report; -+ if (problem_formatter_generate_report(formatter, data, &report) != 0) -+ errx(EXIT_FAILURE, "Problem data cannot be converted to problem report."); -+ -+ Now you can print the report: -+ -+ printf("Problem: %s\n", problem_report_get_summary()); -+ printf("%s\n", problem_report_get_description()); -+ -+ puts("Problem attachments:"); -+ for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a)) -+ printf(" %s\n", a->data); -+ -+ Format description: -+ -+ ---- -+ %summary:: summary format -+ %attach:: elemnt1[,element2]... -+ section:: element1[,element2]... -+ The literal text line to be added to report. -+ ---- -+ -+ 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. -+ -+ - %description: this section is implicit and contains all text -+ sections unless another section was specified (%summary and %attach -+ are ignored when determining text section's placement) -+ -+ - every text element belongs to the last specified section (%summary -+ and %attach sections are ignored). If no section was specified, -+ the text element belogns to %description. -+ -+ - If none of elements exists, the section will not be created. -+ -+ - Empty lines are NOT ignored. -+ -+ 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. -+ -+ You can add your own section: -+ -+ problem_formatter_t *formatter = problem_formatter_new(); -+ problem_formatter_add_section(formatter, "additional_info", PFFF_REQUIRED); -+ -+ and then you can use the section in the formatting string: -+ -+ problem_formatter_load_string(formatter, -+ "::comment\n" -+ "%additional_info:: maps"); -+ problem_formatter_generate_report(formatter, data, &report); -+ -+ printf("Problem: %s\n", problem_report_get_summary()); -+ printf("%s\n", problem_report_get_description()); -+ printf("Additional info: %s\n", problem_report_get_section(report, "additiona_info")); -+ -+ The lines above are equivalent to the following lines: -+ -+ printf("Problem: %s\n", problem_data_get_content_or_NULL(data, "reason")); -+ printf("%s\n", problem_data_get_content_or_NULL(data, "comment")); -+ printf("Additional info: %s\n", problem_data_get_content_or_NULL(data, "maps")); -+*/ -+#ifndef LIBREPORT_PROBLEM_REPORT_H -+#define LIBREPORT_PROBLEM_REPORT_H -+ -+#include <glib.h> -+#include <stdio.h> -+#include "problem_data.h" -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+#define PR_SEC_SUMMARY "summary" -+#define PR_SEC_DESCRIPTION "description" -+ -+/* -+ * The problem report structure represents a problem data formatted according -+ * to a format string. -+ * -+ * A problem report is composed of well-known sections: -+ * - summary -+ * - descritpion -+ * - attach -+ * -+ * and custom sections accessed by: -+ * problem_report_get_section(); -+ */ -+struct problem_report; -+typedef struct problem_report problem_report_t; -+ -+/* -+ * Helpers for easily switching between FILE and struct strbuf -+ */ -+ -+/* -+ * Type of buffer used by Problem report -+ */ -+typedef FILE problem_report_buffer; -+ -+/* -+ * Wrapper for the proble buffer's formated output function. -+ */ -+#define problem_report_buffer_printf(buf, fmt, ...)\ -+ fprintf((buf), (fmt), ##__VA_ARGS__) -+ -+ -+/* -+ * Get a section buffer -+ * -+ * Use this function if you need to amend something to a formatted section. -+ * -+ * @param self Problem report -+ * @param section_name Name of required section -+ * @return Always valid pointer to a section buffer -+ */ -+problem_report_buffer *problem_report_get_buffer(const problem_report_t *self, -+ const char *section_name); -+ -+/* -+ * Get Summary string -+ * -+ * The returned pointer is valid as long as you perform no further output to -+ * the summary buffer. -+ * -+ * @param self Problem report -+ * @return Non-NULL pointer to summary data -+ */ -+const char *problem_report_get_summary(const problem_report_t *self); -+ -+/* -+ * Get Description string -+ * -+ * The returned pointer is valid as long as you perform no further output to -+ * the description buffer. -+ * -+ * @param self Problem report -+ * @return Non-NULL pointer to description data -+ */ -+const char *problem_report_get_description(const problem_report_t *self); -+ -+/* -+ * Get Section's string -+ * -+ * The returned pointer is valid as long as you perform no further output to -+ * the section's buffer. -+ * -+ * @param self Problem report -+ * @param section_name Name of the required section -+ * @return Non-NULL pointer to description data -+ */ -+const char *problem_report_get_section(const problem_report_t *self, -+ const char *section_name); -+ -+/* -+ * Get GList of the problem data items that are to be attached -+ * -+ * @param self Problem report -+ * @return A pointer to GList (NULL means empty list) -+ */ -+GList *problem_report_get_attachments(const problem_report_t *self); -+ -+/* -+ * Releases all resources allocated by a problem report -+ * -+ * @param self Problem report -+ */ -+void problem_report_free(problem_report_t *self); -+ -+ -+/* -+ * An enum of Extra section flags -+ */ -+enum problem_formatter_section_flags { -+ PFFF_REQUIRED = 1 << 0, ///< section must be present in the format spec -+}; -+ -+/* -+ * The problem formatter structure formats a problem data according to a format -+ * string and stores result a problem report. -+ * -+ * The problem formatter uses '%reason%' as %summary section format string, if -+ * %summary is not provided by a format string. -+ */ -+struct problem_formatter; -+typedef struct problem_formatter problem_formatter_t; -+ -+/* -+ * Constructs a new problem formatter. -+ * -+ * @return Non-NULL pointer to the new problem formatter -+ */ -+problem_formatter_t *problem_formatter_new(void); -+ -+/* -+ * Releases all resources allocated by a problem formatter -+ * -+ * @param self Problem formatter -+ */ -+void problem_formatter_free(problem_formatter_t *self); -+ -+/* -+ * Adds a new recognized section -+ * -+ * The problem formatter ignores a section in the format spec if the section is -+ * not one of the default nor added by this function. -+ * -+ * How the problem formatter handles these extra sections: -+ * -+ * A custom section is something like %description section. %description is the -+ * default section where all text (sub)sections are stored. If the formatter -+ * finds the custom section in format string, then starts storing text -+ * (sub)sections in the custom section. -+ * -+ * (%description) |:: comment -+ * (%description) | -+ * (%description) |Package:: package -+ * (%description) | -+ * (%additiona_info) |%additional_info:: -+ * (%additiona_info) |%reporter% -+ * (%additiona_info) |User:: user_name,uid -+ * (%additiona_info) | -+ * (%additiona_info) |Directories:: root,cwd -+ * -+ * -+ * @param self Problem formatter -+ * @param name Name of the added section -+ * @param flags Info about the added section -+ * @return Zero on success. -EEXIST if the name is already known by the formatter -+ */ -+int problem_formatter_add_section(problem_formatter_t *self, const char *name, int flags); -+ -+/* -+ * Loads a problem format from a string. -+ * -+ * @param self Problem formatter -+ * @param fmt Format -+ * @return Zero on success or number of warnings (e.g. missing section, -+ * unrecognized section). -+ */ -+int problem_formatter_load_string(problem_formatter_t* self, const char *fmt); -+ -+/* -+ * Loads a problem format from a file. -+ * -+ * @param self Problem formatter -+ * @param pat Path to the format file -+ * @return Zero on success or number of warnings (e.g. missing section, -+ * unrecognized section). -+ */ -+int problem_formatter_load_file(problem_formatter_t* self, const char *path); -+ -+/* -+ * Creates a new problem report, formats the data according to the loaded -+ * format string and stores output in the report. -+ * -+ * @param self Problem formatter -+ * @param data Problem data to format -+ * @param report Pointer where the created problem report is to be stored -+ * @return Zero on success, otherwise non-zero value. -+ */ -+int problem_formatter_generate_report(const problem_formatter_t *self, problem_data_t *data, problem_report_t **report); -+ -+#ifdef __cplusplus -+} -+#endif -+ -+#endif // LIBREPORT_PROBLEM_REPORT_H -diff --git a/tests/testsuite.at b/tests/testsuite.at -index ccb37d1..d4648d4 100644 ---- a/tests/testsuite.at -+++ b/tests/testsuite.at -@@ -17,7 +17,7 @@ m4_include([xml_definition.at]) - m4_include([report_python.at]) - m4_include([string_list.at]) - m4_include([ureport.at]) --m4_include([problem_report.at]) -+# m4_include([problem_report.at]) - m4_include([dump_dir.at]) - m4_include([global_config.at]) - m4_include([iso_date.at]) --- -1.8.3.1 - diff --git a/SOURCES/1012-reporter-mantisbt-add-event-for-reporting-AVCs.patch b/SOURCES/1012-reporter-mantisbt-add-event-for-reporting-AVCs.patch deleted file mode 100644 index 84924bd..0000000 --- a/SOURCES/1012-reporter-mantisbt-add-event-for-reporting-AVCs.patch +++ /dev/null @@ -1,245 +0,0 @@ -From 9a7a376a236e24c448eb650328d0d4841026568b Mon Sep 17 00:00:00 2001 -From: Matej Habrnal <mhabrnal@redhat.com> -Date: Wed, 13 May 2015 16:37:19 +0200 -Subject: [PATCH 1012/1015] reporter-mantisbt: add event for reporting AVCs - -Without this commit is not possible to report AVCs because there are not event -for 'report_CentOSBugTracker' with analyzer=libreport which is used for -reporting AVCs. - -Related to bugs.centos#8422 and libreport#348 - -Signed-off-by: Matej Habrnal <mhabrnal@redhat.com> ---- - doc/Makefile.am | 2 + - doc/mantisbt_format_analyzer_libreport.conf.txt | 18 +++++++ - doc/mantisbt_formatdup_analyzer_libreport.conf.txt | 18 +++++++ - src/plugins/Makefile.am | 4 +- - src/plugins/centos_report_event.conf | 5 ++ - .../mantisbt_format_analyzer_libreport.conf | 59 ++++++++++++++++++++++ - .../mantisbt_formatdup_analyzer_libreport.conf | 56 ++++++++++++++++++++ - 7 files changed, 161 insertions(+), 1 deletion(-) - create mode 100644 doc/mantisbt_format_analyzer_libreport.conf.txt - create mode 100644 doc/mantisbt_formatdup_analyzer_libreport.conf.txt - create mode 100644 src/plugins/mantisbt_format_analyzer_libreport.conf - create mode 100644 src/plugins/mantisbt_formatdup_analyzer_libreport.conf - -diff --git a/doc/Makefile.am b/doc/Makefile.am -index e437388..c10deb4 100644 ---- a/doc/Makefile.am -+++ b/doc/Makefile.am -@@ -41,6 +41,8 @@ MAN5_TXT += bugzilla_format_libreport.conf.txt - MAN5_TXT += mantisbt.conf.txt - MAN5_TXT += mantisbt_format.conf.txt - MAN5_TXT += mantisbt_formatdup.conf.txt -+MAN5_TXT += mantisbt_format_analyzer_libreport.conf.txt -+MAN5_TXT += mantisbt_formatdup_analyzer_libreport.conf.txt - MAN5_TXT += emergencyanalysis_event.conf.txt - MAN5_TXT += forbidden_words.conf.txt - MAN5_TXT += mailx.conf.txt -diff --git a/doc/mantisbt_format_analyzer_libreport.conf.txt b/doc/mantisbt_format_analyzer_libreport.conf.txt -new file mode 100644 -index 0000000..8cbd327 ---- /dev/null -+++ b/doc/mantisbt_format_analyzer_libreport.conf.txt -@@ -0,0 +1,18 @@ -+mantisbt_format_analyzer_libreport.conf(5) -+========================================== -+ -+NAME -+---- -+mantisbt_format_analyzer_libreport.conf - configuration file for libreport. -+ -+DESCRIPTION -+----------- -+This configuration file provides definition of general formatting for duplicate MantisBT issues. -+ -+SEE ALSO -+-------- -+reporter-mantisbt(1) -+ -+AUTHOR -+------ -+* ABRT Team -diff --git a/doc/mantisbt_formatdup_analyzer_libreport.conf.txt b/doc/mantisbt_formatdup_analyzer_libreport.conf.txt -new file mode 100644 -index 0000000..cd082de ---- /dev/null -+++ b/doc/mantisbt_formatdup_analyzer_libreport.conf.txt -@@ -0,0 +1,18 @@ -+mantisbt_formatdup_analyzer_libreport.conf(5) -+============================================= -+ -+NAME -+---- -+mantisbt_formatdup_analyzer_libreport.conf - configuration file for libreport. -+ -+DESCRIPTION -+----------- -+This configuration file provides definition of general formatting for duplicate MantisBT issues. -+ -+SEE ALSO -+-------- -+reporter-mantisbt(1) -+ -+AUTHOR -+------ -+* ABRT Team -diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am -index f4f94ff..3c1dfff 100644 ---- a/src/plugins/Makefile.am -+++ b/src/plugins/Makefile.am -@@ -42,7 +42,9 @@ endif - if BUILD_MANTISBT - reporters_plugin_conf += mantisbt.conf - reporters_plugin_format_conf += mantisbt_format.conf \ -- mantisbt_formatdup.conf -+ mantisbt_formatdup.conf \ -+ mantisbt_format_analyzer_libreport.conf \ -+ mantisbt_formatdup_analyzer_libreport.conf - endif - - defaultreportpluginsconfdir = $(DEFAULT_REPORT_PLUGINS_CONF_DIR) -diff --git a/src/plugins/centos_report_event.conf b/src/plugins/centos_report_event.conf -index 53f12d8..adbca93 100644 ---- a/src/plugins/centos_report_event.conf -+++ b/src/plugins/centos_report_event.conf -@@ -35,3 +35,8 @@ EVENT=report_CentOSBugTracker analyzer=CCpp duphash!= - -F "/etc/libreport/plugins/$format" \ - -A "/etc/libreport/plugins/$formatdup" - -+EVENT=report_CentOSBugTracker analyzer=libreport -+ reporter-mantisbt \ -+ -c /etc/libreport/plugins/mantisbt.conf \ -+ -F /etc/libreport/plugins/mantisbt_format_analyzer_libreport.conf \ -+ -A /etc/libreport/plugins/mantisbt_formatdup_analyzer_libreport.conf -diff --git a/src/plugins/mantisbt_format_analyzer_libreport.conf b/src/plugins/mantisbt_format_analyzer_libreport.conf -new file mode 100644 -index 0000000..a514e38 ---- /dev/null -+++ b/src/plugins/mantisbt_format_analyzer_libreport.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:: %reason% -+ -+Description of problem:: %bare_comment, %bare_description -+ -+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:: -reported_to,-comment,-reason,-event_log,%multiline,\ -+ -coredump,%binary -diff --git a/src/plugins/mantisbt_formatdup_analyzer_libreport.conf b/src/plugins/mantisbt_formatdup_analyzer_libreport.conf -new file mode 100644 -index 0000000..d9ab0e3 ---- /dev/null -+++ b/src/plugins/mantisbt_formatdup_analyzer_libreport.conf -@@ -0,0 +1,56 @@ -+# 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. -+ -+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 --- -1.8.3.1 - diff --git a/SOURCES/1014-event-disable-report_RHTSupport-event-and-change-URL.patch b/SOURCES/1014-event-disable-report_RHTSupport-event-and-change-URL.patch deleted file mode 100644 index 3765c62..0000000 --- a/SOURCES/1014-event-disable-report_RHTSupport-event-and-change-URL.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 93cfff1cd5ca8f418709e4fa0e1b1c9d5b15ddcf Mon Sep 17 00:00:00 2001 -From: Matej Habrnal <mhabrnal@redhat.com> -Date: Fri, 12 Jun 2015 07:51:50 +0200 -Subject: [PATCH 1014/1015] event: disable report_RHTSupport event and change - URL for bugzilla - -Set the URL for bugzilla to non-exist one because we do not want the centos -users report to bugzilla. - -Signed-off-by: Matej Habrnal <mhabrnal@redhat.com> ---- - src/plugins/bugzilla.conf | 2 +- - src/plugins/rhtsupport_event.conf | 4 ++-- - 2 files changed, 3 insertions(+), 3 deletions(-) - -diff --git a/src/plugins/bugzilla.conf b/src/plugins/bugzilla.conf -index 51648de..159b188 100644 ---- a/src/plugins/bugzilla.conf -+++ b/src/plugins/bugzilla.conf -@@ -1,5 +1,5 @@ - # Bugzilla URL --BugzillaURL = https://bugzilla.redhat.com/ -+BugzillaURL = https://bugzilla.example.com/ - # yes means that ssl certificates will be checked - SSLVerify = yes - # your login has to exist, if you don have any, please create one -diff --git a/src/plugins/rhtsupport_event.conf b/src/plugins/rhtsupport_event.conf -index f6a9d47..4ccf9f5 100644 ---- a/src/plugins/rhtsupport_event.conf -+++ b/src/plugins/rhtsupport_event.conf -@@ -1,3 +1,3 @@ --EVENT=report_RHTSupport -+#EVENT=report_RHTSupport - # Submit an uReport and create a case in Red Hat Customer Portal -- reporter-rhtsupport -u -+ # reporter-rhtsupport -u --- -1.8.3.1 - diff --git a/SPECS/libreport.spec b/SPECS/libreport.spec index d1b71a2..36ade42 100644 --- a/SPECS/libreport.spec +++ b/SPECS/libreport.spec @@ -7,7 +7,7 @@ Summary: Generic library for reporting various problems Name: libreport Version: 2.1.11 -Release: 35%{?dist} +Release: 38%{?dist} License: GPLv2+ Group: System Environment/Libraries URL: https://fedorahosted.org/abrt/ @@ -237,23 +237,12 @@ Patch209: 0209-lib-problem-report-API-check-fseek-return-code.patch Patch210: 0210-RHTSupport-include-count-in-Support-cases.patch Patch211: 0211-configure-set-version-to-2.1.11.1.patch Patch212: 0212-Translation-updates.patch +Patch213: 0213-reportclient-honor-ABRT_VERBOSE.patch +Patch214: 0214-tree-wide-introduce-stop_on_not_reportable-option.patch +Patch215: 0215-rhtsupport-fix-a-double-free-of-config-at-exit.patch +Patch216: 0216-reportclient-fix-verbosity-test.patch +Patch217: 0217-report-newt-free-allocated-variables-don-t-close-dd-.patch -Patch1000: 1000-bugzilla-port-to-Problem-Format-API.patch -Patch1001: 1001-lib-created-a-new-lib-file-for-reporters.patch -#Patch1002: 1002-spec-changed-spec-file-to-work-with-last-commit.patch -Patch1003: 1003-ureport-set-url-to-public-faf-server.patch -Patch1004: 1004-conf-changed-URL-for-sending-uReport.patch -Patch1005: 1005-reporter-mantisbt-first-version-of-the-reporter-mant.patch -#Patch1006: 1006-spec-changed-spec-file-to-work-with-reporter-mantisb.patch -Patch1007: 1007-reporter-mantisbt-change-default-formating-file-for-.patch -#Patch1008: 1008-spec-change-spec-file-to-work-with-last-commit.patch -Patch1009: 1009-reporter-mantisbt-adds-man-pages-for-reporter-mantis.patch -Patch1010: 1010-move-problem_report-to-plugins.patch -#Patch1011: 1011-spec-change-related-to-moving-problem_report-to-plug.patch -Patch1012: 1012-reporter-mantisbt-add-event-for-reporting-AVCs.patch -#Patch1013: 1013-spec-add-files-related-to-reporting-AVCs-by-reporter.patch -Patch1014: 1014-event-disable-report_RHTSupport-event-and-change-URL.patch -#Patch1015: 1015-spec-remove-dependecy-on-redhat-access-insights.patch # git is need for '%%autosetup -S git' which automatically applies all the # patches above. Please, be aware that the patches must be generated @@ -443,32 +432,12 @@ Uploads micro-report to abrt server %description plugin-bugzilla Plugin to report bugs into the bugzilla. -%package plugin-mantisbt -Summary: %{name}'s mantisbt plugin -Group: System Environment/Libraries -Requires: %{name} = %{version}-%{release} -Requires: libreport-web = %{version}-%{release} - -%description plugin-mantisbt -Plugin to report bugs into the mantisbt. - -%package centos -Summary: %{name}'s CentOS Bug Tracker workflow -Group: System Environment/Libraries -Requires: %{name} = %{version}-%{release} -Requires: libreport-web = %{version}-%{release} -Requires: libreport-plugin-mantisbt = %{version}-%{release} - -%description centos -Workflows to report issues into the CentOS Bug Tracker. - %package plugin-rhtsupport Summary: %{name}'s RHTSupport plugin Group: System Environment/Libraries Requires: %{name} = %{version}-%{release} Requires: libreport-web = %{version}-%{release} -# This feature is only for RHEL so we do not need another dependency -# Requires: redhat-access-insights +Requires: redhat-access-insights %description plugin-rhtsupport Plugin to report bugs into RH support system. @@ -701,6 +670,7 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : %{_includedir}/libreport/dump_dir.h %{_includedir}/libreport/event_config.h %{_includedir}/libreport/problem_data.h +%{_includedir}/libreport/problem_report.h %{_includedir}/libreport/report.h %{_includedir}/libreport/run_event.h %{_includedir}/libreport/file_obj.h @@ -708,7 +678,6 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : %{_includedir}/libreport/workflow.h %{_includedir}/libreport/ureport.h %{_includedir}/libreport/global_configuration.h -%{_includedir}/libreport/reporters.h # Private api headers: %{_includedir}/libreport/internal_abrt_dbus.h %{_includedir}/libreport/internal_libreport.h @@ -828,40 +797,6 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : %{_mandir}/man5/bugzilla_format_kernel.conf.5.* %{_bindir}/reporter-bugzilla -%files plugin-mantisbt -%defattr(-,root,root,-) -%config(noreplace) %{_sysconfdir}/libreport/plugins/mantisbt.conf -%{_datadir}/%{name}/conf.d/plugins/mantisbt.conf -%config(noreplace) %{_sysconfdir}/libreport/plugins/mantisbt_format.conf -%config(noreplace) %{_sysconfdir}/libreport/plugins/mantisbt_formatdup.conf -%config(noreplace) %{_sysconfdir}/libreport/plugins/mantisbt_format_analyzer_libreport.conf -%config(noreplace) %{_sysconfdir}/libreport/plugins/mantisbt_formatdup_analyzer_libreport.conf -%{_bindir}/reporter-mantisbt -%{_mandir}/man1/reporter-mantisbt.1.gz -%{_mandir}/man5/mantisbt.conf.5.* -%{_mandir}/man5/mantisbt_format.conf.5.* -%{_mandir}/man5/mantisbt_formatdup.conf.5.* -%{_mandir}/man5/mantisbt_format_analyzer_libreport.conf.5.* -%{_mandir}/man5/mantisbt_formatdup_analyzer_libreport.conf.5.* - -%files centos -%{_datadir}/%{name}/workflows/workflow_CentOSCCpp.xml -%{_datadir}/%{name}/workflows/workflow_CentOSKerneloops.xml -%{_datadir}/%{name}/workflows/workflow_CentOSPython.xml -%{_datadir}/%{name}/workflows/workflow_CentOSPython3.xml -%{_datadir}/%{name}/workflows/workflow_CentOSVmcore.xml -%{_datadir}/%{name}/workflows/workflow_CentOSXorg.xml -%{_datadir}/%{name}/workflows/workflow_CentOSLibreport.xml -%{_datadir}/%{name}/workflows/workflow_CentOSJava.xml -%config(noreplace) %{_sysconfdir}/libreport/workflows.d/report_centos.conf -%{_mandir}/man5/report_centos.conf.5.* -%{_datadir}/%{name}/events/report_CentOSBugTracker.xml -%config(noreplace) %{_sysconfdir}/libreport/events/report_CentOSBugTracker.conf -%{_mandir}/man5/report_CentOSBugTracker.conf.5.* -# report_CentOSBugTracker events are shipped by libreport package -%config(noreplace) %{_sysconfdir}/libreport/events.d/centos_report_event.conf -%{_mandir}/man5/centos_report_event.conf.5.gz - %files plugin-rhtsupport %defattr(-,root,root,-) %config(noreplace) %{_sysconfdir}/libreport/plugins/rhtsupport.conf @@ -963,7 +898,19 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : %changelog -* Thu Sep 1 2016 Matej Habrnal <mhabrnal@redhat.com> - 2.1.11-35.el7.centos +* Tue Feb 21 2017 Matej Habrnal <mhabrnal@redhat.com> - 2.1.11-38 +- free allocated variables, don't close dd twice +- Related: #1257159 + +* Thu Feb 9 2017 Matej Habrnal <mhabrnal@redhat.com> - 2.1.11-37 +- rebuild + +* Wed Feb 8 2017 Matej Habrnal <mhabrnal@redhat.com> - 2.1.11-36 +- introduce 'stop_on_not_reportable' option +- fix a double free of config at exit in reporter-rhtsupport +- Related: #1373094, #1257159 + +* Thu Sep 1 2016 Matej Habrnal <mhabrnal@redhat.com> - 2.1.11-35 - Translation updates - Related: #1304240