diff --git a/SOURCES/0159-wizard-fix-save-users-changes-after-reviewing-dump-d.patch b/SOURCES/0159-wizard-fix-save-users-changes-after-reviewing-dump-d.patch new file mode 100644 index 0000000..a240cc3 --- /dev/null +++ b/SOURCES/0159-wizard-fix-save-users-changes-after-reviewing-dump-d.patch @@ -0,0 +1,66 @@ +From 46d7cc6b317334cdd119ceaec9887eab0afac39c Mon Sep 17 00:00:00 2001 +From: Matej Habrnal <mhabrnal@redhat.com> +Date: Mon, 12 Oct 2015 09:14:12 +0200 +Subject: [PATCH] wizard: fix save users changes after reviewing dump dir files + +If the user reviewed the dump dir's files during reporting the crash, the +changes was thrown away and original data was passed to the bugzilla bug +report. + +report-gtk saves the first text view buffer and then reloads data from the +reported problem directory, which causes that the changes made to those text +views are thrown away. + +Function save_text_if_changed(), except of saving text, also reload the files +from dump dir and update gui state from the dump dir. The commit moves the +reloading and updating gui functions away from this function. + +Related to rhbz#1270235 + +Signed-off-by: Matej Habrnal <mhabrnal@redhat.com> + +Resolves: CVE-2015-5302 + +Signed-off-by: Jakub Filak <jfilak@redhat.com> +--- + src/gui-wizard-gtk/wizard.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/src/gui-wizard-gtk/wizard.c b/src/gui-wizard-gtk/wizard.c +index 71964a9..3a7db9c 100644 +--- a/src/gui-wizard-gtk/wizard.c ++++ b/src/gui-wizard-gtk/wizard.c +@@ -495,8 +495,6 @@ static void save_text_if_changed(const char *name, const char *new_value) + + //FIXME: else: what to do with still-unsaved data in the widget?? + dd_close(dd); +- problem_data_reload_from_dump_dir(); +- update_gui_state_from_problem_data(/* don't update selected event */ 0); + } + } + +@@ -839,7 +837,11 @@ static void tv_details_row_activated( + load_text_to_text_view(GTK_TEXT_VIEW(textview), item_name); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) ++ { + save_text_from_text_view(GTK_TEXT_VIEW(textview), item_name); ++ problem_data_reload_from_dump_dir(); ++ update_gui_state_from_problem_data(/* don't update selected event */ 0); ++ } + + gtk_widget_destroy(textview); + gtk_widget_destroy(scrolled); +@@ -2662,7 +2664,8 @@ static void on_page_prepare(GtkNotebook *assistant, GtkWidget *page, gpointer us + * these tabs will be lost */ + save_items_from_notepad(); + save_text_from_text_view(g_tv_comment, FILENAME_COMMENT); +- ++ problem_data_reload_from_dump_dir(); ++ update_gui_state_from_problem_data(/* don't update selected event */ 0); + + if (pages[PAGENO_SUMMARY].page_widget == page) + { +-- +1.8.3.1 + diff --git a/SOURCES/bugzilla-port-to-Problem-Format-API.patch b/SOURCES/bugzilla-port-to-Problem-Format-API.patch deleted file mode 100644 index 19e2ed8..0000000 --- a/SOURCES/bugzilla-port-to-Problem-Format-API.patch +++ /dev/null @@ -1,781 +0,0 @@ -From 914bdfee5da272a99d8dc3f68652534eb132e007 Mon Sep 17 00:00:00 2001 -From: Jakub Filak <jfilak@redhat.com> -Date: Thu, 4 Dec 2014 08:45:07 +0100 -Subject: [PATCH] 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 097924e..38f48ef 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 { -@@ -1102,18 +500,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); - } - -@@ -1196,22 +605,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 | (opts & OPT_g)), // either we got Bugzilla_CreatePrivate from settings or -g was specified on cmdline - rhbz.b_private_groups - ); -- free(bzcomment); -- free(summary); - - if (new_id == -1) - { -@@ -1236,9 +652,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"); -@@ -1302,18 +726,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; -@@ -1331,10 +758,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/conf-changed-URL-for-sending-uReport.patch b/SOURCES/conf-changed-URL-for-sending-uReport.patch deleted file mode 100644 index 119ecee..0000000 --- a/SOURCES/conf-changed-URL-for-sending-uReport.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 0d4c8deb8252ad8ffb60568cf0cc33a74b8759fc Mon Sep 17 00:00:00 2001 -From: Matej Habrnal <mhabrnal@redhat.com> -Date: Mon, 2 Feb 2015 21:41:36 +0100 -Subject: [PATCH] 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 63dfc22..aca857a 100644 ---- a/src/plugins/report_uReport.xml.in -+++ b/src/plugins/report_uReport.xml.in -@@ -11,7 +11,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/lib-add-Problem-Format-API.patch b/SOURCES/lib-add-Problem-Format-API.patch deleted file mode 100644 index 0fabf65..0000000 --- a/SOURCES/lib-add-Problem-Format-API.patch +++ /dev/null @@ -1,2244 +0,0 @@ -From 26eae803a52322683daa377006b25a3d59f0acf6 Mon Sep 17 00:00:00 2001 -From: Jakub Filak <jfilak@redhat.com> -Date: Thu, 4 Dec 2014 08:43:17 +0100 -Subject: [PATCH] lib: add Problem Format API - -Related to #303 - -Signed-off-by: Jakub Filak <jfilak@redhat.com> ---- - po/POTFILES.in | 1 + - src/include/Makefile.am | 1 + - src/include/problem_report.h | 225 ++++++++ - src/lib/Makefile.am | 5 +- - src/lib/problem_report.c | 1209 ++++++++++++++++++++++++++++++++++++++++++ - tests/Makefile.am | 3 +- - tests/problem_report.at | 690 ++++++++++++++++++++++++ - tests/testsuite.at | 1 + - 8 files changed, 2133 insertions(+), 2 deletions(-) - create mode 100644 src/include/problem_report.h - create mode 100644 src/lib/problem_report.c - create mode 100644 tests/problem_report.at - -diff --git a/po/POTFILES.in b/po/POTFILES.in -index 00046e2..c597b11 100644 ---- a/po/POTFILES.in -+++ b/po/POTFILES.in -@@ -23,6 +23,7 @@ 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/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 de44cda..47ba399 100644 ---- a/src/include/Makefile.am -+++ b/src/include/Makefile.am -@@ -5,6 +5,7 @@ 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 -new file mode 100644 -index 0000000..30781e6 ---- /dev/null -+++ b/src/include/problem_report.h -@@ -0,0 +1,225 @@ -+/* -+ 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 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 7d9722a..a0001ef 100644 ---- a/src/lib/Makefile.am -+++ b/src/lib/Makefile.am -@@ -38,6 +38,7 @@ 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 \ -@@ -76,6 +77,7 @@ libreport_la_CPPFLAGS = \ - $(GLIB_CFLAGS) \ - $(GOBJECT_CFLAGS) \ - $(AUGEAS_CFLAGS) \ -+ $(SATYR_CFLAGS) \ - -D_GNU_SOURCE - libreport_la_LDFLAGS = \ - -version-info 0:1:0 -@@ -84,7 +86,8 @@ libreport_la_LIBADD = \ - $(GLIB_LIBS) \ - $(JOURNAL_LIBS) \ - $(GOBJECT_LIBS) \ -- $(AUGEAS_LIBS) -+ $(AUGEAS_LIBS) \ -+ $(SATYR_LIBS) - - libreportconfdir = $(CONF_DIR) - dist_libreportconf_DATA = \ -diff --git a/src/lib/problem_report.c b/src/lib/problem_report.c -new file mode 100644 -index 0000000..0afc1ca ---- /dev/null -+++ b/src/lib/problem_report.c -@@ -0,0 +1,1209 @@ -+/* -+ 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]) -+ { -+ fseek(result, old_pos[opt_depth], SEEK_SET); -+ 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 *analyzer = problem_data_get_content_or_NULL(problem_data, FILENAME_ANALYZER); -+ if (!analyzer) -+ { -+ log_debug("Problem data does not contain '"FILENAME_ANALYZER"' 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(analyzer); -+ if (strcmp(analyzer, "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/tests/Makefile.am b/tests/Makefile.am -index eaf1ac2..d1a2b8b 100644 ---- a/tests/Makefile.am -+++ b/tests/Makefile.am -@@ -43,7 +43,8 @@ TESTSUITE_AT = \ - xfuncs.at \ - string_list.at \ - ureport.at \ -- dump_dir.at -+ dump_dir.at \ -+ problem_report.at - - EXTRA_DIST += $(TESTSUITE_AT) - TESTSUITE = $(srcdir)/testsuite -diff --git a/tests/problem_report.at b/tests/problem_report.at -new file mode 100644 -index 0000000..8bc469f ---- /dev/null -+++ b/tests/problem_report.at -@@ -0,0 +1,690 @@ -+# -*- Autotest -*- -+ -+AT_BANNER([problem report]) -+ -+## ------- ## -+## summary ## -+## ------- ## -+ -+AT_TESTFUN([summary], -+[[ -+#include "problem_report.h" -+#include "internal_libreport.h" -+#include <assert.h> -+ -+int main(int argc, char **argv) -+{ -+ const char *const test_format_result[][2] = { -+ { -+ "%summary:: [abrt] trivial string", -+ "[abrt] trivial string" -+ }, -+ -+ { -+ "%summary:: [abrt] %package%", -+ "[abrt] libreport" -+ }, -+ -+ { -+ "%summary:: [abrt] %package%[[ : %crash_function%()]][[ : %does_not_exist%]][[ : %reason%]][[ TAINTED: %taint_flags%]]", -+ "[abrt] libreport : run_event() : Killed by SIGSEGV" -+ }, -+ -+ { -+ "%summary:: [abrt] %package%[[ : %crash_function%[[ : %does_not_exist%]]()]][[ : %reason%]]", -+ "[abrt] libreport : run_event() : Killed by SIGSEGV" -+ }, -+ -+ { -+ "%summary:: [abrt] %package%[[ : %does_not_exist%[[ : %crash_function%()]]]][[ : %reason%]]", -+ "[abrt] libreport : Killed by SIGSEGV" -+ }, -+ -+ { -+ "%summary:: [[%does_not_exist%]][[%once_more%]][abrt] %package%", -+ "[abrt] libreport" -+ }, -+ -+ { -+ "", -+ "Killed by SIGSEGV" -+ }, -+ }; -+ -+ g_verbose = 3; -+ -+ problem_data_t *data = problem_data_new(); -+ problem_data_add_text_noteditable(data, "package", "libreport"); -+ problem_data_add_text_noteditable(data, "crash_function", "run_event"); -+ problem_data_add_text_noteditable(data, "reason", "Killed by SIGSEGV"); -+ -+ for (size_t i = 0; i < sizeof(test_format_result)/sizeof(*test_format_result); ++i) { -+ problem_formatter_t *pf = problem_formatter_new(); -+ assert(!problem_formatter_load_string(pf, test_format_result[i][0])); -+ -+ problem_report_t *pr = NULL; -+ assert(!problem_formatter_generate_report(pf, data, &pr)); -+ assert(pr != NULL); -+ -+ const char *summary = problem_report_get_summary(pr); -+ assert(summary != NULL); -+ -+ fprintf(stderr, "expected: '%s'\n", test_format_result[i][1]); -+ fprintf(stderr, "result : '%s'\n", summary); -+ -+ assert(strcmp(test_format_result[i][1], summary) == 0); -+ -+ problem_report_free(pr); -+ problem_formatter_free(pf); -+ } -+ -+ problem_data_free(data); -+ -+ return 0; -+} -+]]) -+ -+## ---------- ## -+## desciption ## -+## ---------- ## -+ -+AT_TESTFUN([description], -+[[ -+#include "problem_report.h" -+#include "internal_libreport.h" -+#include <assert.h> -+ -+int main(int argc, char **argv) -+{ -+ const char *const test_format_result[][2] = { -+ { -+ "\n"\ -+ "\n"\ -+ "\n"\ -+ "Single line\n" -+ "\n"\ -+ "\n"\ -+ "\n", -+ -+ "Single line\n" -+ }, -+ -+ { -+ "\n"\ -+ "\n"\ -+ "\n"\ -+ "Comment:: %bare_comment" -+ "\n"\ -+ "\n"\ -+ "\n"\ -+ "Ad-hoc line\n" -+ "\n"\ -+ "\n"\ -+ "\n"\ -+ "Additional:: package,\\\n" -+ "uuid,cwd\\\\" -+ ",user_name" -+ "\n"\ -+ "\n", -+ -+ "Comment:\n" \ -+ "Hello, world!\n" -+ "\n"\ -+ "\n"\ -+ "Ad-hoc line\n" -+ "\n"\ -+ "\n"\ -+ "\n"\ -+ "Additional:\n"\ -+ "package: libreport\n"\ -+ "uuid: 123456789ABCDEF\n"\ -+ "user_name: abrt\n", -+ }, -+ -+ { -+ ":: %bare_description", -+ -+ "I run will_segfault and\n"\ -+ "it crashed as expected\n" -+ }, -+ -+ { -+ "User:: %bare_user_name,uid\n"\ -+ "Additional info:: -uuid,%oneline,-comment,-package", -+ -+ "User:\n"\ -+ "abrt\n"\ -+ "uid: 69\n"\ -+ "Additional info:\n"\ -+ "analyzer: CCpp\n"\ -+ "root: /var/run/mock/abrt\n"\ -+ "type: CCpp\n" -+ }, -+ -+ { -+ ":: %reporter", -+ NULL /* do no check results*/ -+ }, -+ -+ { -+ "Truncated backtrace:: %bare_%short_backtrace", -+ -+ "Truncated backtrace:\n" -+ "Thread no. 0 (7 frames)\n" -+ " #1 crash at will_segfault.c:19\n" -+ " #2 varargs at will_segfault.c:31\n" -+ " #3 inlined at will_segfault.c:40\n" -+ " #4 f at will_segfault.c:45\n" -+ " #5 callback at will_segfault.c:50\n" -+ " #6 call_me_back at libwillcrash.c:8\n" -+ " #7 recursive at will_segfault.c:59\n" -+ }, -+ }; -+ -+ g_verbose = 3; -+ -+ problem_data_t *data = problem_data_new(); -+ problem_data_add_text_noteditable(data, "package", "libreport"); -+ problem_data_add_text_noteditable(data, "analyzer", "CCpp"); -+ problem_data_add_text_noteditable(data, "type", "CCpp"); -+ problem_data_add_text_noteditable(data, "comment", "Hello, world!"); -+ problem_data_add_text_noteditable(data, "uuid", "123456789ABCDEF"); -+ problem_data_add_text_noteditable(data, "uid", "69"); -+ problem_data_add_text_noteditable(data, "user_name", "abrt"); -+ problem_data_add_text_noteditable(data, "root", "/var/run/mock/abrt"); -+ problem_data_add_text_noteditable(data, "description", "I run will_segfault and\nit crashed as expected\n"); -+ problem_data_add_text_noteditable(data, "backtrace", -+"Thread 1 (LWP 11865):\n"\ -+"#0 printf (__fmt=0x400acf \"Result: %d\\n\") at /usr/include/bits/stdio2.h:104\n"\ -+"No locals.\n"\ -+"#1 crash (p=p@entry=0x0) at will_segfault.c:19\n"\ -+"i = <error reading variable i (Cannot access memory at address 0x0)>\n"\ -+"#2 0x0000000000400964 in varargs (num_args=1, num_args@entry=2) at will_segfault.c:31\n"\ -+"p = <optimized out>\n"\ -+"ap = {{gp_offset = 24, fp_offset = 32767, overflow_arg_area = 0x7fff4fc8a0c0, reg_save_area = 0x7fff4fc8a080}}\n"\ -+"#3 0x00000000004009be in inlined (p=0x0) at will_segfault.c:40\n"\ -+"num = 42\n"\ -+"#4 f (p=p@entry=0x0) at will_segfault.c:45\n"\ -+"No locals.\n"\ -+"#5 0x00000000004009e9 in callback (data=data@entry=0x0) at will_segfault.c:50\n"\ -+"No locals.\n"\ -+"#6 0x00000032f76006f9 in call_me_back (cb=cb@entry=0x4009e0 <callback>, data=data@entry=0x0) at libwillcrash.c:8\n"\ -+"res = <optimized out>\n"\ -+"#7 0x0000000000400a14 in recursive (i=i@entry=0) at will_segfault.c:59\n"\ -+"p = <optimized out>\n"\ -+"#8 0x0000000000400a00 in recursive (i=i@entry=1) at will_segfault.c:66\n"\ -+"No locals.\n"\ -+"#9 0x0000000000400a00 in recursive (i=i@entry=2) at will_segfault.c:66\n"\ -+"No locals.\n"\ -+"#10 0x0000000000400775 in main (argc=<optimized out>, argv=<optimized out>) at will_segfault.c:83\n"\ -+"No locals.\n"\ -+"From To Syms Read Shared Object Library\n"\ -+"0x00000032f76005f0 0x00000032f7600712 Yes /lib64/libwillcrash.so.0\n"\ -+"0x0000003245c1f4f0 0x0000003245d6aca4 Yes /lib64/libc.so.6\n"\ -+"0x0000003245800b10 0x000000324581b6d0 Yes /lib64/ld-linux-x86-64.so.2\n"\ -+"$1 = 0x0\n" -+"No symbol \"__glib_assert_msg\" in current context.\n"\ -+"rax 0xf 15\n"\ -+"rbx 0x0 0\n"\ -+"rcx 0x7ff96f752000 140709293531136\n"\ -+"rdx 0x3245fb9a40 215922481728\n"\ -+"rsi 0x7ff96f752000 140709293531136\n"\ -+"rdi 0x1 1\n"\ -+"rbp 0x400a30 0x400a30 <__libc_csu_init>\n"\ -+"rsp 0x7fff4fc8a050 0x7fff4fc8a050\n"\ -+"r8 0xffffffff 4294967295\n"\ -+"r9 0x0 0\n"\ -+"r10 0x22 34\n"\ -+"r11 0x246 582\n"\ -+"r12 0x40079f 4196255\n"\ -+"r13 0x7fff4fc8a210 140734531936784\n"\ -+"r14 0x0 0\n"\ -+"r15 0x0 0\n"\ -+"rip 0x4008ae 0x4008ae <crash+14>\n"\ -+"eflags 0x10246 [ PF ZF IF RF ]\n"\ -+"cs 0x33 51\n"\ -+"ss 0x2b 43\n"\ -+"ds 0x0 0\n"\ -+"es 0x0 0\n"\ -+"fs 0x0 0\n"\ -+"gs 0x0 0\n"\ -+"st0 0 (raw 0x00000000000000000000)\n"\ -+"st1 0 (raw 0x00000000000000000000)\n"\ -+"st2 0 (raw 0x00000000000000000000)\n"\ -+"st3 0 (raw 0x00000000000000000000)\n"\ -+"st4 0 (raw 0x00000000000000000000)\n"\ -+"st5 0 (raw 0x00000000000000000000)\n"\ -+"st6 0 (raw 0x00000000000000000000)\n"\ -+"st7 0 (raw 0x00000000000000000000)\n"\ -+"fctrl 0x37f 895\n"\ -+"fstat 0x0 0\n"\ -+"ftag 0xffff 65535\n"\ -+"fiseg 0x0 0\n"\ -+"fioff 0x0 0\n"\ -+"foseg 0x0 0\n"\ -+"fooff 0x0 0\n"\ -+"fop 0x0 0\n"\ -+"xmm0 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000}\n"\ -+"xmm1 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x2f <repeats 16 times>}, v8_int16 = {0x2f2f, 0x2f2f, 0x2f2f, 0x2f2f, 0x2f2f, 0x2f2f, 0x2f2f, 0x2f2f}, v4_int32 = {0x2f2f2f2f, 0x2f2f2f2f, 0x2f2f2f2f, 0x2f2f2f2f}, v2_int64 = {0x2f2f2f2f2f2f2f2f, 0x2f2f2f2f2f2f2f2f}, uint128 = 0x2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f}\n"\ -+"xmm2 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000}\n"\ -+"xmm3 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 13 times>, 0xff, 0x0, 0x0}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff00, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0xff00}, v2_int64 = {0x0, 0xff0000000000}, uint128 = 0x0000ff00000000000000000000000000}\n"\ -+"xmm4 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0xff0000, 0x0}, v2_int64 = {0x0, 0xff0000}, uint128 = 0x0000000000ff00000000000000000000}\n"\ -+"xmm5 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000}\n"\ -+"xmm6 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000}\n"\ -+"xmm7 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000}\n"\ -+"xmm8 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000}\n"\ -+"xmm9 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000}\n"\ -+"xmm10 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000}\n"\ -+"xmm11 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000}\n"\ -+"xmm12 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 14 times>, 0xff, 0x0}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff}, v4_int32 = {0x0, 0x0, 0x0, 0xff0000}, v2_int64 = {0x0, 0xff000000000000}, uint128 = 0x00ff0000000000000000000000000000}\n"\ -+"xmm13 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000}\n"\ -+"xmm14 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000}\n"\ -+"xmm15 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000}\n"\ -+"mxcsr 0x1f80 [ IM DM ZM OM UM PM ]\n"\ -+"Dump of assembler code for function crash:\n"\ -+" 0x00000000004008a0 <+0>: push %rbx\n"\ -+" 0x00000000004008a1 <+1>: mov %rdi,%rbx\n"\ -+" 0x00000000004008a4 <+4>: mov $0x400ac0,%edi\n"\ -+" 0x00000000004008a9 <+9>: callq 0x4006f0 <puts@plt>\n"\ -+" => 0x00000000004008ae <+14>: mov (%rbx),%edx\n"\ -+" 0x00000000004008b0 <+16>: mov $0x400acf,%esi\n"\ -+" 0x00000000004008b5 <+21>: mov $0x1,%edi\n"\ -+" 0x00000000004008ba <+26>: xor %eax,%eax\n"\ -+" 0x00000000004008bc <+28>: callq 0x400740 <__printf_chk@plt>\n"\ -+" 0x00000000004008c1 <+33>: pop %rbx\n"\ -+" 0x00000000004008c2 <+34>: retq\n"\ -+"End of assembler dump.\n" -+ ); -+ -+ for (size_t i = 0; i < sizeof(test_format_result)/sizeof(*test_format_result); ++i) { -+ problem_formatter_t *pf = problem_formatter_new(); -+ assert(!problem_formatter_load_string(pf, test_format_result[i][0])); -+ -+ problem_report_t *pr = NULL; -+ assert(!problem_formatter_generate_report(pf, data, &pr)); -+ assert(pr != NULL); -+ -+ const char *summary = problem_report_get_description(pr); -+ assert(summary != NULL); -+ -+ if (test_format_result[i][1] != NULL) { -+ fprintf(stderr, "####\n"); -+ fprintf(stderr, "expected: '%s'\n", test_format_result[i][1]); -+ fprintf(stderr, "====\n"); -+ fprintf(stderr, "result : '%s'\n", summary); -+ fprintf(stderr, "####\n"); -+ -+ assert(strcmp(test_format_result[i][1], summary) == 0); -+ } -+ -+ problem_report_free(pr); -+ problem_formatter_free(pf); -+ } -+ -+ problem_data_free(data); -+ -+ return 0; -+} -+]]) -+ -+## ------ ## -+## attach ## -+## ------ ## -+ -+AT_TESTFUN([attach], -+[[ -+#include "problem_report.h" -+#include "internal_libreport.h" -+#include <assert.h> -+ -+int main(int argc, char **argv) -+{ -+ g_verbose = 3; -+ -+ const char *fst[] = { "backtrace", "screenshot", "description", NULL }; -+ -+ struct test_case { -+ const char *format; -+ const char *const *files; -+ } cases [] = { -+ { -+ .format = "%attach:: %multiline,%binary,-coredump", -+ .files = fst, -+ } -+ }; -+ -+ problem_data_t *data = problem_data_new(); -+ problem_data_add_text_noteditable(data, "package", "libreport"); -+ problem_data_add_text_noteditable(data, "analyzer", "CCpp"); -+ problem_data_add_text_noteditable(data, "type", "CCpp"); -+ problem_data_add_text_noteditable(data, "comment", "Hello, world!"); -+ problem_data_add_text_noteditable(data, "uuid", "123456789ABCDEF"); -+ problem_data_add_text_noteditable(data, "uid", "69"); -+ problem_data_add_text_noteditable(data, "user_name", "abrt"); -+ problem_data_add_text_noteditable(data, "root", "/var/run/mock/abrt"); -+ problem_data_add_text_noteditable(data, "description", "I run will_segfault and\nit crashed as expected\n"); -+ problem_data_add_file(data, "coredump", "/what/ever/path/to/coredump"); -+ problem_data_add_file(data, "screenshot", "/what/ever/path/to/screenshot"); -+ problem_data_add_text_noteditable(data, "backtrace", -+"Thread 1 (LWP 11865):\n"\ -+"#0 printf (__fmt=0x400acf \"Result: %d\\n\") at /usr/include/bits/stdio2.h:104\n"\ -+"No locals.\n"\ -+"#1 crash (p=p@entry=0x0) at will_segfault.c:19\n"\ -+"i = <error reading variable i (Cannot access memory at address 0x0)>\n"\ -+"#2 0x0000000000400964 in varargs (num_args=1, num_args@entry=2) at will_segfault.c:31\n"\ -+"p = <optimized out>\n"\ -+"ap = {{gp_offset = 24, fp_offset = 32767, overflow_arg_area = 0x7fff4fc8a0c0, reg_save_area = 0x7fff4fc8a080}}\n"\ -+"#3 0x00000000004009be in inlined (p=0x0) at will_segfault.c:40\n"\ -+"num = 42\n"\ -+"#4 f (p=p@entry=0x0) at will_segfault.c:45\n"\ -+"No locals.\n"\ -+"#5 0x00000000004009e9 in callback (data=data@entry=0x0) at will_segfault.c:50\n"\ -+"No locals.\n"\ -+"#6 0x00000032f76006f9 in call_me_back (cb=cb@entry=0x4009e0 <callback>, data=data@entry=0x0) at libwillcrash.c:8\n"\ -+"res = <optimized out>\n"\ -+"#7 0x0000000000400a14 in recursive (i=i@entry=0) at will_segfault.c:59\n"\ -+"p = <optimized out>\n"\ -+"#8 0x0000000000400a00 in recursive (i=i@entry=1) at will_segfault.c:66\n"\ -+"No locals.\n"\ -+"#9 0x0000000000400a00 in recursive (i=i@entry=2) at will_segfault.c:66\n"\ -+"No locals.\n"\ -+"#10 0x0000000000400775 in main (argc=<optimized out>, argv=<optimized out>) at will_segfault.c:83\n"\ -+"No locals.\n"); -+ -+ for (size_t i = 0; i < sizeof(cases)/sizeof(*cases); ++i) { -+ fprintf(stderr, "%s", cases[i].format); -+ -+ problem_formatter_t *pf = problem_formatter_new(); -+ assert(!problem_formatter_load_string(pf, cases[i].format)); -+ -+ problem_report_t *pr = NULL; -+ assert(!problem_formatter_generate_report(pf, data, &pr)); -+ assert(pr != NULL); -+ -+ const char *const *iter = cases[i].files; -+ -+ if (*iter != NULL) { -+ assert(problem_report_get_attachments(pr)); -+ } -+ -+ GList *clone = g_list_copy_deep(problem_report_get_attachments(pr), (GCopyFunc)xstrdup, NULL); -+ -+ while (*iter) { -+ GList *item = g_list_find_custom(clone, *iter, (GCompareFunc)strcmp); -+ -+ if (item == NULL) { -+ fprintf(stderr, "format: '%s'\n", cases[i].format); -+ fprintf(stderr, "missing file: '%s'\n", *iter); -+ abort(); -+ } -+ else { -+ free(item->data); -+ clone = g_list_delete_link(clone, item); -+ } -+ -+ ++iter; -+ } -+ -+ if (clone != NULL) { -+ for (GList *iter = clone; iter; iter = g_list_next(iter)) { -+ fprintf(stderr, "extra : '%s'\n", (const char *)iter->data); -+ } -+ abort(); -+ } -+ -+ problem_report_free(pr); -+ problem_formatter_free(pf); -+ } -+ -+ problem_data_free(data); -+ -+ return 0; -+} -+]]) -+ -+ -+## -------------- ## -+## custom_section ## -+## -------------- ## -+ -+AT_TESTFUN([custom_section], -+[[ -+#include "problem_report.h" -+#include "internal_libreport.h" -+#include <assert.h> -+ -+int main(int argc, char **argv) -+{ -+ g_verbose = 3; -+ -+ struct test_case; -+ struct test_case { -+ const char *section_name; -+ int section_flags; -+ int retval_load; -+ int retval_generate; -+ const char *format; -+ const char *output; -+ } cases [] = { -+ { .section_name = NULL, -+ .section_flags = 0, -+ .retval_load = 1, -+ .retval_generate = 0, -+ .format = -+ "%additional_info::\n" -+ ":: package,\\\n" -+ "uuid,cwd\\\\" -+ ",user_name" -+ , -+ .output = NULL, -+ }, -+ -+ { .section_name = "additional_info", -+ .section_flags = 0, -+ .retval_load = 0, -+ .retval_generate = 0, -+ .format = -+ "%additional_info::\n" -+ ":: package,\\\n" -+ "uuid,cwd\\\\" -+ ",user_name" -+ , -+ .output = -+ "package: libreport\n" -+ "uuid: 0123456789ABCDEF\n" -+ "user_name: abrt\n" -+ , -+ }, -+ -+ { .section_name = "additional_info", -+ .section_flags = PFFF_REQUIRED, -+ .retval_load = 1, -+ .retval_generate = 0, -+ .format = "%summary:: [abrt] %package%\n:: %reporter\n", -+ .output = NULL, -+ }, -+ -+ { .section_name = "additional_info", -+ .section_flags = 0, -+ .retval_load = 0, -+ .retval_generate = 0, -+ .format = "%summary:: [abrt] %package%\n:: %reporter\n", -+ .output = "", -+ }, -+ -+ { .section_name = "additional_info", -+ .section_flags = 0, -+ .retval_load = 0, -+ .retval_generate = 0, -+ .format = -+ "%additional_info:: root\n" -+ "Info:: package,\\\n" -+ "uuid,cwd\\\\" -+ ",user_name" -+ , -+ .output = -+ "Info:\n" -+ "package: libreport\n" -+ "uuid: 0123456789ABCDEF\n" -+ "user_name: abrt\n" -+ , -+ }, -+ }; -+ -+ problem_data_t *data = problem_data_new(); -+ problem_data_add_text_noteditable(data, "package", "libreport"); -+ problem_data_add_text_noteditable(data, "crash_function", "run_event"); -+ problem_data_add_text_noteditable(data, "reason", "Killed by SIGSEGV"); -+ problem_data_add_text_noteditable(data, "uuid", "0123456789ABCDEF"); -+ problem_data_add_text_noteditable(data, "user_name", "abrt"); -+ -+ for (size_t i = 0; i < sizeof(cases)/sizeof(*cases); ++i) { -+ fprintf(stderr, "#### Test case %zd ####\n", i + 1); -+ -+ struct test_case *tcase = cases + i; -+ problem_formatter_t *pf = problem_formatter_new(); -+ -+ if (tcase->section_name != NULL) { -+ problem_formatter_add_section(pf, tcase->section_name, tcase->section_flags); -+ } -+ -+ int r = problem_formatter_load_string(pf, tcase->format); -+ if (r != tcase->retval_load) { -+ fprintf(stderr, "Load : expected : %d , got : %d\n", tcase->retval_load, r); -+ abort(); -+ } -+ -+ if (tcase->retval_load != 0) { -+ goto next_test_case; -+ } -+ -+ problem_report_t *pr = NULL; -+ r = problem_formatter_generate_report(pf, data, &pr); -+ if (r != tcase->retval_generate) { -+ fprintf(stderr, "Generaet : expected : %d , got : %d\n", tcase->retval_generate, r); -+ abort(); -+ } -+ -+ if (tcase->retval_generate != 0) { -+ goto next_test_case; -+ } -+ -+ const char *output = problem_report_get_section(pr, tcase->section_name); -+ assert(output); -+ -+ if (strcmp(output, tcase->output) != 0) { -+ fprintf(stderr, "expected:\n'%s'\n", tcase->output); -+ fprintf(stderr, "result :\n'%s'\n", output); -+ abort(); -+ } -+ -+ problem_report_free(pr); -+ -+next_test_case: -+ problem_formatter_free(pf); -+ fprintf(stderr, "#### Finished - Test case %zd ####\n", i + 1); -+ } -+ -+ problem_data_free(data); -+ -+ return 0; -+} -+]]) -+ -+## ------ ## -+## sanity ## -+## ------ ## -+ -+AT_TESTFUN([sanity], -+[[ -+#include "problem_report.h" -+#include "internal_libreport.h" -+#include <errno.h> -+#include <assert.h> -+ -+void assert_equal_strings(const char *res, const char *exp) -+{ -+ if ( (res == NULL && exp != NULL) -+ || (res != NULL && exp == NULL) -+ || ((res != NULL && exp != NULL) && strcmp(res, exp) != 0) -+ ) { -+ fprintf(stderr, "expected : '%s'\n", exp); -+ fprintf(stderr, "result : '%s'\n", res); -+ abort(); -+ } -+} -+ -+int main(int argc, char **argv) -+{ -+ g_verbose = 3; -+ -+ problem_formatter_t *pf = problem_formatter_new(); -+ -+ assert(problem_formatter_add_section(pf, "summary", 0) == -EEXIST); -+ assert(problem_formatter_add_section(pf, "description", 0) == -EEXIST); -+ assert(problem_formatter_add_section(pf, "attach", 0) == -EEXIST); -+ -+ assert(problem_formatter_add_section(pf, "additional_info", 0) == 0); -+ assert(problem_formatter_add_section(pf, "additional_info", 0) == -EEXIST); -+ -+ problem_data_t *data = problem_data_new(); -+ problem_data_add_text_noteditable(data, "package", "libreport"); -+ problem_data_add_text_noteditable(data, "crash_function", "run_event"); -+ problem_data_add_text_noteditable(data, "reason", "Killed by SIGSEGV"); -+ problem_data_add_text_noteditable(data, "comment", "Hello, world!"); -+ problem_data_add_text_noteditable(data, "root", "/var/run/mock/abrt"); -+ problem_data_add_text_noteditable(data, "user_name", "abrt"); -+ problem_data_add_file(data, "screenshot", "/what/ever/path/to/screenshot"); -+ -+ problem_formatter_load_string(pf, -+ "%summary:: [abrt] %package% : %reason%\n\n" -+ "Description:: %bare_comment\n\n" -+ "%attach:: screenshot\n\n" -+ "%additional_info::\n\n" -+ "User:: root,user_name,uid\n\n\n" -+ ); -+ -+ problem_report_t *pr = NULL; -+ problem_formatter_generate_report(pf, data, &pr); -+ -+ assert_equal_strings(problem_report_get_summary(pr), -+ "[abrt] libreport : Killed by SIGSEGV"); -+ -+ problem_report_buffer_printf(problem_report_get_buffer(pr, PR_SEC_SUMMARY), " - test"); -+ -+ assert_equal_strings(problem_report_get_summary(pr), -+ "[abrt] libreport : Killed by SIGSEGV - test"); -+ -+ -+ assert_equal_strings(problem_report_get_description(pr), -+ "Description:\nHello, world!\n"); -+ -+ problem_report_buffer_printf(problem_report_get_buffer(pr, PR_SEC_DESCRIPTION), "Test line\n"); -+ -+ assert_equal_strings(problem_report_get_description(pr), -+ "Description:\nHello, world!\nTest line\n"); -+ -+ -+ assert_equal_strings(problem_report_get_section(pr, "additional_info"), -+ "User:\n" -+ "root: /var/run/mock/abrt\n" -+ "user_name: abrt\n" -+ ); -+ -+ problem_report_buffer_printf(problem_report_get_buffer(pr, "additional_info"), "uid: 42\n"); -+ -+ assert_equal_strings(problem_report_get_section(pr, "additional_info"), -+ "User:\n" -+ "root: /var/run/mock/abrt\n" -+ "user_name: abrt\n" -+ "uid: 42\n" -+ ); -+ -+ -+ problem_report_free(pr); -+ problem_data_free(data); -+ problem_formatter_free(pf); -+ -+ return 0; -+} -+]]) -diff --git a/tests/testsuite.at b/tests/testsuite.at -index 41107e7..f287b32 100644 ---- a/tests/testsuite.at -+++ b/tests/testsuite.at -@@ -18,3 +18,4 @@ m4_include([report_python.at]) - m4_include([string_list.at]) - m4_include([ureport.at]) - m4_include([dump_dir.at]) -+m4_include([problem_report.at]) --- -1.8.3.1 - diff --git a/SOURCES/lib-created-a-new-lib-file-for-reporters.patch b/SOURCES/lib-created-a-new-lib-file-for-reporters.patch deleted file mode 100644 index 3a38799..0000000 --- a/SOURCES/lib-created-a-new-lib-file-for-reporters.patch +++ /dev/null @@ -1,405 +0,0 @@ -From 10662c5e05c5cd2c60eacd5e4a3c3f3361a0ebd4 Mon Sep 17 00:00:00 2001 -From: Matej Habrnal <mhabrnal@redhat.com> -Date: Tue, 13 Jan 2015 19:23:08 -0500 -Subject: [PATCH] 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 c597b11..5588540 100644 ---- a/po/POTFILES.in -+++ b/po/POTFILES.in -@@ -24,6 +24,7 @@ src/lib/make_descr.c - src/lib/parse_options.c - src/lib/problem_data.c - src/lib/problem_report.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 47ba399..a13e04d 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 d35d715..b801b1a 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 a0001ef..3ec463f 100644 ---- a/src/lib/Makefile.am -+++ b/src/lib/Makefile.am -@@ -56,7 +56,8 @@ libreport_la_SOURCES = \ - workflow_xml_parser.c \ - config_item_info.c \ - xml_parser.c \ -- libreport_init.c -+ libreport_init.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 bad9ed4..a376c13 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 976d333..2f91962 100644 ---- a/src/plugins/rhbz.h -+++ b/src/plugins/rhbz.h -@@ -101,8 +101,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/libreport-centos-branding-changes.patch b/SOURCES/libreport-centos-branding-changes.patch deleted file mode 100644 index 327f79f..0000000 --- a/SOURCES/libreport-centos-branding-changes.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 30bca072cb8104cb07e937ee44bb0c3eaec19eb2 Mon Sep 17 00:00:00 2001 -From: rpm-build <rpm-build> -Date: Wed, 18 Jun 2014 17:20:02 -0500 -Subject: [PATCH] centos branding changes - ---- - 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 29c44b3..ca5e94d 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 58261bb..e803686 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 --- -1.8.3.1 - diff --git a/SOURCES/move-problem_report-to-plugins.patch b/SOURCES/move-problem_report-to-plugins.patch deleted file mode 100644 index 537d148..0000000 --- a/SOURCES/move-problem_report-to-plugins.patch +++ /dev/null @@ -1,3039 +0,0 @@ -From 9988b70c37d8c93a472fd153c0bc8f1a40320449 Mon Sep 17 00:00:00 2001 -From: Jakub Filak <jfilak@redhat.com> -Date: Wed, 25 Mar 2015 16:43:19 +0100 -Subject: [PATCH] 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 | 225 -------- - src/lib/Makefile.am | 7 +- - src/lib/problem_report.c | 1209 ------------------------------------------ - src/plugins/Makefile.am | 17 +- - src/plugins/problem_report.c | 1209 ++++++++++++++++++++++++++++++++++++++++++ - src/plugins/problem_report.h | 225 ++++++++ - tests/testsuite.at | 2 +- - 9 files changed, 1453 insertions(+), 1444 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 3415b03..485b116 100644 ---- a/po/POTFILES.in -+++ b/po/POTFILES.in -@@ -23,10 +23,10 @@ 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/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 a13e04d..578dba8 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 30781e6..0000000 ---- a/src/include/problem_report.h -+++ /dev/null -@@ -1,225 +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. --*/ --#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 3ec463f..b99036c 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 \ -@@ -78,7 +77,6 @@ libreport_la_CPPFLAGS = \ - $(GLIB_CFLAGS) \ - $(GOBJECT_CFLAGS) \ - $(AUGEAS_CFLAGS) \ -- $(SATYR_CFLAGS) \ - -D_GNU_SOURCE - libreport_la_LDFLAGS = \ - -version-info 0:1:0 -@@ -87,8 +85,7 @@ libreport_la_LIBADD = \ - $(GLIB_LIBS) \ - $(JOURNAL_LIBS) \ - $(GOBJECT_LIBS) \ -- $(AUGEAS_LIBS) \ -- $(SATYR_LIBS) -+ $(AUGEAS_LIBS) - - libreportconfdir = $(CONF_DIR) - dist_libreportconf_DATA = \ -@@ -149,8 +146,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 0afc1ca..0000000 ---- a/src/lib/problem_report.c -+++ /dev/null -@@ -1,1209 +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]) -- { -- fseek(result, old_pos[opt_depth], SEEK_SET); -- 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 *analyzer = problem_data_get_content_or_NULL(problem_data, FILENAME_ANALYZER); -- if (!analyzer) -- { -- log_debug("Problem data does not contain '"FILENAME_ANALYZER"' 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(analyzer); -- if (strcmp(analyzer, "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 77423c4..fc2fd46 100644 ---- a/src/plugins/Makefile.am -+++ b/src/plugins/Makefile.am -@@ -123,6 +123,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 -@@ -144,7 +155,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 -@@ -167,7 +179,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 = \ -diff --git a/src/plugins/problem_report.c b/src/plugins/problem_report.c -new file mode 100644 -index 0000000..0afc1ca ---- /dev/null -+++ b/src/plugins/problem_report.c -@@ -0,0 +1,1209 @@ -+/* -+ 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]) -+ { -+ fseek(result, old_pos[opt_depth], SEEK_SET); -+ 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 *analyzer = problem_data_get_content_or_NULL(problem_data, FILENAME_ANALYZER); -+ if (!analyzer) -+ { -+ log_debug("Problem data does not contain '"FILENAME_ANALYZER"' 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(analyzer); -+ if (strcmp(analyzer, "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..30781e6 ---- /dev/null -+++ b/src/plugins/problem_report.h -@@ -0,0 +1,225 @@ -+/* -+ 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 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 f287b32..1e5ea68 100644 ---- a/tests/testsuite.at -+++ b/tests/testsuite.at -@@ -18,4 +18,4 @@ m4_include([report_python.at]) - m4_include([string_list.at]) - m4_include([ureport.at]) - m4_include([dump_dir.at]) --m4_include([problem_report.at]) -+# m4_include([problem_report.at]) --- -1.8.3.1 - diff --git a/SOURCES/reporter-mantisbt-add-event-for-reporting-AVCs.patch b/SOURCES/reporter-mantisbt-add-event-for-reporting-AVCs.patch deleted file mode 100644 index 73feb44..0000000 --- a/SOURCES/reporter-mantisbt-add-event-for-reporting-AVCs.patch +++ /dev/null @@ -1,245 +0,0 @@ -From dab14191917b919ab91ddac3cf0460be680d7e87 Mon Sep 17 00:00:00 2001 -From: Matej Habrnal <mhabrnal@redhat.com> -Date: Wed, 13 May 2015 16:37:19 +0200 -Subject: [PATCH] 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 b574a41..aaacf7d 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 fc2fd46..41716b3 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/reporter-mantisbt-adds-man-pages-for-reporter-mantis.patch b/SOURCES/reporter-mantisbt-adds-man-pages-for-reporter-mantis.patch deleted file mode 100644 index e1ab03f..0000000 --- a/SOURCES/reporter-mantisbt-adds-man-pages-for-reporter-mantis.patch +++ /dev/null @@ -1,525 +0,0 @@ -From c43e0af711742ae8e5c6b38efc7c3dbf16e57f01 Mon Sep 17 00:00:00 2001 -From: Matej Habrnal <mhabrnal@redhat.com> -Date: Fri, 20 Feb 2015 03:14:54 +0100 -Subject: [PATCH] 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 da4785e..b574a41 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,20 +49,25 @@ 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 - MAN5_TXT += report_logger.conf.txt - MAN5_TXT += report_mailx.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_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 - -@@ -73,5 +82,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 d70b4db..77423c4 100644 ---- a/src/plugins/Makefile.am -+++ b/src/plugins/Makefile.am -@@ -107,6 +107,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 5b8376a..b922e23 100644 ---- a/src/workflows/Makefile.am -+++ b/src/workflows/Makefile.am -@@ -104,3 +104,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/reporter-mantisbt-change-default-formating-file-for-.patch b/SOURCES/reporter-mantisbt-change-default-formating-file-for-.patch deleted file mode 100644 index 56c898c..0000000 --- a/SOURCES/reporter-mantisbt-change-default-formating-file-for-.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 0f913e6c9e9539f9c91f819250176dc5c65f0363 Mon Sep 17 00:00:00 2001 -From: Matej Habrnal <mhabrnal@redhat.com> -Date: Fri, 20 Feb 2015 00:27:05 +0100 -Subject: [PATCH] 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/reporter-mantisbt-first-version-of-the-reporter-mant.patch b/SOURCES/reporter-mantisbt-first-version-of-the-reporter-mant.patch deleted file mode 100644 index c1751aa..0000000 --- a/SOURCES/reporter-mantisbt-first-version-of-the-reporter-mant.patch +++ /dev/null @@ -1,2658 +0,0 @@ -From 611ca48b8d81e5edc0907885ece48cb0dce986fa Mon Sep 17 00:00:00 2001 -From: Matej Habrnal <mhabrnal@redhat.com> -Date: Tue, 13 Jan 2015 19:30:27 -0500 -Subject: [PATCH] reporter-mantisbt: first version of the reporter-mantisbt - -Related to #272 - -Signed-off-by: Matej Habrnal <mhabrnal@redhat.com> ---- - configure.ac | 33 + - po/POTFILES.in | 11 + - src/plugins/Makefile.am | 43 +- - src/plugins/centos_report_event.conf | 37 + - src/plugins/mantisbt.c | 1111 ++++++++++++++++++++++++ - src/plugins/mantisbt.conf | 8 + - src/plugins/mantisbt.h | 140 +++ - src/plugins/mantisbt_format.conf | 59 ++ - src/plugins/mantisbt_formatdup.conf | 65 ++ - src/plugins/report_CentOSBugTracker.conf | 4 + - src/plugins/report_CentOSBugTracker.xml.in | 65 ++ - src/plugins/reporter-mantisbt.c | 696 +++++++++++++++ - src/workflows/Makefile.am | 15 +- - src/workflows/report_centos.conf | 31 + - src/workflows/workflow_CentOSCCpp.xml.in | 12 + - src/workflows/workflow_CentOSJava.xml.in | 11 + - src/workflows/workflow_CentOSKerneloops.xml.in | 11 + - src/workflows/workflow_CentOSLibreport.xml.in | 9 + - src/workflows/workflow_CentOSPython.xml.in | 11 + - src/workflows/workflow_CentOSPython3.xml.in | 11 + - src/workflows/workflow_CentOSVmcore.xml.in | 12 + - src/workflows/workflow_CentOSXorg.xml.in | 9 + - 22 files changed, 2402 insertions(+), 2 deletions(-) - create mode 100644 src/plugins/centos_report_event.conf - create mode 100644 src/plugins/mantisbt.c - create mode 100644 src/plugins/mantisbt.conf - create mode 100644 src/plugins/mantisbt.h - create mode 100644 src/plugins/mantisbt_format.conf - create mode 100644 src/plugins/mantisbt_formatdup.conf - create mode 100644 src/plugins/report_CentOSBugTracker.conf - create mode 100644 src/plugins/report_CentOSBugTracker.xml.in - create mode 100644 src/plugins/reporter-mantisbt.c - create mode 100644 src/workflows/report_centos.conf - create mode 100644 src/workflows/workflow_CentOSCCpp.xml.in - create mode 100644 src/workflows/workflow_CentOSJava.xml.in - create mode 100644 src/workflows/workflow_CentOSKerneloops.xml.in - create mode 100644 src/workflows/workflow_CentOSLibreport.xml.in - create mode 100644 src/workflows/workflow_CentOSPython.xml.in - create mode 100644 src/workflows/workflow_CentOSPython3.xml.in - create mode 100644 src/workflows/workflow_CentOSVmcore.xml.in - create mode 100644 src/workflows/workflow_CentOSXorg.xml.in - -diff --git a/configure.ac b/configure.ac -index 8aea410..66508c0 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -131,6 +131,39 @@ do - done - fi dnl end NO_BUGZILLA - -+AC_ARG_WITH(mantisbt, -+AS_HELP_STRING([--with-mantisbt],[use MantisBT plugin (default is YES)]), -+LIBREPORT_PARSE_WITH([mantisbt])) -+ -+if test -z "$NO_MANTISBT"; then -+AM_CONDITIONAL(BUILD_MANTISBT, true) -+ -+# enable mantisbt & deps translations -+for FILE in `grep -e "#.*antisbt.*" -e "#.*naconda.*" po/POTFILES.in` -+do -+ sed -ie "s,$FILE,${FILE:1}," po/POTFILES.in -+ sed -ie "\,^${FILE:1}$,d" po/POTFILES.skip -+done -+else -+AM_CONDITIONAL(BUILD_MANTISBT, false) -+ -+# disablie mantisbt & deps translations -+for FILE in `grep -e "antisbt" -e "naconda" po/POTFILES.in` -+do -+ if test "${FILE:0:1}" = "#" -+ then -+ continue -+ fi -+ -+ sed -ie "s,$FILE,#$FILE," po/POTFILES.in -+ grep "$FILE" po/POTFILES.skip > /dev/null 2>&1 -+ if test $? -+ then -+ echo "$FILE" >> po/POTFILES.skip -+ fi -+done -+fi dnl end NO_MANTISBT -+ - AC_PATH_PROG([PYTHON_CONFIG], [python-config], [no]) - [if test "$PYTHON_CONFIG" = "no"] - [then] -diff --git a/po/POTFILES.in b/po/POTFILES.in -index 5588540..3415b03 100644 ---- a/po/POTFILES.in -+++ b/po/POTFILES.in -@@ -36,6 +36,8 @@ src/plugins/reporter-print.c - src/plugins/reporter-rhtsupport.c - src/plugins/reporter-rhtsupport-parse.c - src/plugins/reporter-upload.c -+src/plugins/reporter-mantisbt.c -+src/plugins/report_CentOSBugTracker.xml.in - src/plugins/report_Kerneloops.xml.in - src/plugins/report_Logger.xml.in - src/plugins/report_Mailx.xml.in -@@ -44,12 +46,21 @@ src/plugins/report_Uploader.xml.in - src/plugins/report_uReport.xml.in - src/plugins/report_EmergencyAnalysis.xml.in - src/plugins/rhbz.c -+src/plugins/mantisbt.c - src/plugins/reporter-ureport.c - src/report-newt/report-newt.c - src/workflows/workflow_AnacondaFedora.xml.in - src/workflows/workflow_AnacondaRHEL.xml.in - src/workflows/workflow_AnacondaRHELBugzilla.xml.in - src/workflows/workflow_AnacondaUpload.xml.in -+src/workflows/workflow_CentOSCCpp.xml.in -+src/workflows/workflow_CentOSJava.xml.in -+src/workflows/workflow_CentOSKerneloops.xml.in -+src/workflows/workflow_CentOSLibreport.xml.in -+src/workflows/workflow_CentOSPython.xml.in -+src/workflows/workflow_CentOSPython3.xml.in -+src/workflows/workflow_CentOSVmcore.xml.in -+src/workflows/workflow_CentOSXorg.xml.in - src/workflows/workflow_FedoraCCpp.xml.in - src/workflows/workflow_FedoraKerneloops.xml.in - src/workflows/workflow_FedoraPython.xml.in -diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am -index 7ec08d7..d70b4db 100644 ---- a/src/plugins/Makefile.am -+++ b/src/plugins/Makefile.am -@@ -7,6 +7,11 @@ reporters_bin += \ - report - endif - -+if BUILD_MANTISBT -+reporters_bin += \ -+ reporter-mantisbt -+endif -+ - if BUILD_UREPORT - reporters_bin += reporter-ureport - endif -@@ -34,6 +39,12 @@ reporters_plugin_format_conf += bugzilla_format.conf \ - bugzilla_formatdup_anaconda.conf - endif - -+if BUILD_MANTISBT -+reporters_plugin_conf += mantisbt.conf -+reporters_plugin_format_conf += mantisbt_format.conf \ -+ mantisbt_formatdup.conf -+endif -+ - defaultreportpluginsconfdir = $(DEFAULT_REPORT_PLUGINS_CONF_DIR) - dist_defaultreportpluginsconf_DATA = $(reporters_plugin_conf) \ - rhtsupport.conf \ -@@ -53,6 +64,12 @@ reporters_events += report_Bugzilla.xml - reporters_events_conf += report_Bugzilla.conf - endif - -+if BUILD_MANTISBT -+reporters_events += report_CentOSBugTracker.xml -+ -+reporters_events_conf += report_CentOSBugTracker.conf -+endif -+ - if BUILD_UREPORT - reporters_events += report_uReport.xml - endif -@@ -77,7 +94,8 @@ dist_eventsdef_DATA = \ - print_event.conf \ - rhtsupport_event.conf \ - uploader_event.conf \ -- emergencyanalysis_event.conf -+ emergencyanalysis_event.conf \ -+ centos_report_event.conf - - reporters_extra_dist = - if BUILD_BUGZILLA -@@ -125,6 +143,29 @@ reporter_bugzilla_LDADD = \ - ../lib/libreport.la - endif - -+if BUILD_MANTISBT -+reporter_mantisbt_SOURCES = \ -+ reporter-mantisbt.c mantisbt.c mantisbt.h -+reporter_mantisbt_CPPFLAGS = \ -+ -I$(srcdir)/../include \ -+ -I$(srcdir)/../lib \ -+ -DBIN_DIR=\"$(bindir)\" \ -+ -DCONF_DIR=\"$(CONF_DIR)\" \ -+ -DLOCALSTATEDIR='"$(localstatedir)"' \ -+ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ -+ -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \ -+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ -+ -DPLUGINS_CONF_DIR=\"$(REPORT_PLUGINS_CONF_DIR)\" \ -+ $(GLIB_CFLAGS) \ -+ $(LIBREPORT_CFLAGS) \ -+ $(LIBXML_CFLAGS) \ -+ -D_GNU_SOURCE -+reporter_mantisbt_LDADD = \ -+ $(GLIB_LIBS) \ -+ ../lib/libreport-web.la \ -+ ../lib/libreport.la -+endif -+ - reporter_rhtsupport_SOURCES = \ - abrt_rh_support.h abrt_rh_support.c \ - reporter-rhtsupport.h \ -diff --git a/src/plugins/centos_report_event.conf b/src/plugins/centos_report_event.conf -new file mode 100644 -index 0000000..53f12d8 ---- /dev/null -+++ b/src/plugins/centos_report_event.conf -@@ -0,0 +1,37 @@ -+EVENT=report_CentOSBugTracker analyzer=xorg -+ reporter-mantisbt -+ -+EVENT=report_CentOSBugTracker analyzer=Kerneloops -+ reporter-mantisbt -+ -+EVENT=report_CentOSBugTracker analyzer=vmcore -+ reporter-mantisbt -+ -+EVENT=report_CentOSBugTracker analyzer=Python component!=anaconda -+ test -f component || abrt-action-save-package-data -+ reporter-mantisbt \ -+ -c /etc/libreport/plugins/mantisbt.conf \ -+ -F /etc/libreport/plugins/mantisbt_format.conf \ -+ -A /etc/libreport/plugins/mantisbt_formatdup.conf -+ -+EVENT=report_CentOSBugTracker analyzer=Python3 component!=anaconda -+ test -f component || abrt-action-save-package-data -+ reporter-mantisbt \ -+ -c /etc/libreport/plugins/mantisbt.conf \ -+ -F /etc/libreport/plugins/mantisbt_format.conf \ -+ -A /etc/libreport/plugins/mantisbt_formatdup.conf -+ -+EVENT=report_CentOSBugTracker analyzer=CCpp duphash!= -+ test -f component || abrt-action-save-package-data -+ component="`cat component`" -+ format="mantisbt_format.conf" -+ test -f "/etc/libreport/plugins/mantisbt_format_$component.conf" \ -+ && format="mantisbt_format_$component.conf" -+ formatdup="mantisbt_formatdup.conf" -+ test -f "/etc/libreport/plugins/mantisbt_formatdup_$component.conf" \ -+ && formatdup="mantisbt_formatdup_$component.conf" -+ reporter-mantisbt \ -+ -c /etc/libreport/plugins/mantisbt.conf \ -+ -F "/etc/libreport/plugins/$format" \ -+ -A "/etc/libreport/plugins/$formatdup" -+ -diff --git a/src/plugins/mantisbt.c b/src/plugins/mantisbt.c -new file mode 100644 -index 0000000..1c496b4 ---- /dev/null -+++ b/src/plugins/mantisbt.c -@@ -0,0 +1,1111 @@ -+/* -+ Copyright (C) 2014 ABRT team -+ Copyright (C) 2014 RedHat Inc -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation; either version 2 of the License, or -+ (at your option) any later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License along -+ with this program; if not, write to the Free Software Foundation, Inc., -+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -+*/ -+ -+#include <curl/curl.h> -+ -+#include <libxml/xmlreader.h> -+ -+#include "internal_libreport.h" -+#include "libreport_curl.h" -+#include "mantisbt.h" -+#include "client.h" -+ -+/* -+ * SOAP -+*/ -+ -+#define XML_VERSION "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" -+ -+/* fprint string */ -+#define SOAP_TEMPLATE \ -+ "<SOAP-ENV:Envelope xmlns:ns3=\"http://schemas.xmlsoap.org/soap/encoding/\" " \ -+ "xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\" " \ -+ "xmlns:ns0=\"http://schemas.xmlsoap.org/soap/encoding/\" " \ -+ "xmlns:ns1=\"http://schemas.xmlsoap.org/soap/envelope/\" " \ -+ "xmlns:ns2=\"http://www.w3.org/2001/XMLSchema\" " \ -+ "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \ -+ "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" " \ -+ "SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" \ -+ "<SOAP-ENV:Header/>" \ -+ "<ns1:Body>" \ -+ "<ns3:%s>" \ -+ "</ns3:%s>" \ -+ "</ns1:Body>" \ -+ "</SOAP-ENV:Envelope>" -+ -+#define MAX_SUMMARY_LENGTH 128 -+#define CUSTOMFIELD_DUPHASH "abrt_hash" -+#define CUSTOMFIELD_URL "URL" -+#define MAX_HOPS 5 -+ -+/* MantisBT limit is 2MB by default -+ */ -+#define MANTISBT_MAX_FILE_UPLOAD_SIZE (2 * 1024 * 1024) -+ -+typedef struct mantisbt_custom_fields -+{ -+ char *cf_abrt_hash_id; -+ char *cf_url_id; -+} mantisbt_custom_fields_t; -+ -+/* -+ * MantisBT settings issue info -+ */ -+void -+mantisbt_settings_free(mantisbt_settings_t *s) -+{ -+ if (s == NULL) -+ return; -+ -+ free(s->m_login); -+ free(s->m_password); -+ free(s->m_project); -+ free(s->m_project_id); -+ free(s->m_project_version); -+} -+ -+/* -+ * MantisBT issue info -+ */ -+mantisbt_issue_info_t * -+mantisbt_issue_info_new() -+{ -+ mantisbt_issue_info_t *info = xzalloc(sizeof(mantisbt_issue_info_t)); -+ info->mii_id = -1; -+ info->mii_dup_id = -1; -+ -+ return info; -+} -+ -+void -+mantisbt_issue_info_free(mantisbt_issue_info_t *info) -+{ -+ if (info == NULL) -+ return; -+ -+ free(info->mii_status); -+ free(info->mii_resolution); -+ free(info->mii_reporter); -+ free(info->mii_project); -+ -+ list_free_with_free(info->mii_notes); -+ list_free_with_free(info->mii_attachments); -+ -+ free(info); -+} -+ -+mantisbt_issue_info_t * -+mantisbt_find_origin_bug_closed_duplicate(mantisbt_settings_t *settings, mantisbt_issue_info_t *info) -+{ -+ mantisbt_issue_info_t *info_tmp = mantisbt_issue_info_new(); -+ info_tmp->mii_id = info->mii_id; -+ info_tmp->mii_dup_id = info->mii_dup_id; -+ -+ for (int ii = 0; ii <= MAX_HOPS; ii++) -+ { -+ if (ii == MAX_HOPS) -+ error_msg_and_die(_("MantisBT couldn't find parent of issue %d"), info->mii_id); -+ -+ log("Issue %d is a duplicate, using parent issue %d", info_tmp->mii_id, info_tmp->mii_dup_id); -+ int issue_id = info_tmp->mii_dup_id; -+ -+ mantisbt_issue_info_free(info_tmp); -+ info_tmp = mantisbt_get_issue_info(settings, issue_id); -+ -+ // found a issue which is not CLOSED as DUPLICATE -+ if (info_tmp->mii_dup_id == -1) -+ break; -+ } -+ -+ return info_tmp; -+} -+ -+/* -+ * SOAP request -+ */ -+static soap_request_t * -+soap_request_new() -+{ -+ soap_request_t *req = xzalloc(sizeof(*req)); -+ -+ return req; -+} -+ -+void -+soap_request_free(soap_request_t *req) -+{ -+ if (req == NULL) -+ return; -+ -+ if (req->sr_root != NULL) -+ xmlFreeDoc(req->sr_root->doc); -+ -+ free(req); -+ -+ return; -+} -+ -+static xmlNodePtr -+soap_node_get_next_element_node(xmlNodePtr node) -+{ -+ for (; node != NULL; node = node->next) -+ if (node->type == XML_ELEMENT_NODE) -+ break; -+ -+ return node; -+} -+ -+static xmlNodePtr -+soap_node_get_child_element(xmlNodePtr node) -+{ -+ if (node == NULL) -+ error_msg_and_die(_("SOAP: Failed to get child element because of no parent.")); -+ -+ return soap_node_get_next_element_node(node->xmlChildrenNode); -+} -+ -+static xmlNodePtr -+soap_node_get_next_sibling(xmlNodePtr node) -+{ -+ if (node == NULL) -+ error_msg_and_die(_("SOAP: Failed to get next element because of no node.")); -+ -+ return soap_node_get_next_element_node(node->next); -+} -+ -+static xmlNodePtr -+soap_node_get_child_node(xmlNodePtr parent, const char *name) -+{ -+ if (parent == NULL) -+ error_msg_and_die(_("SOAP: Failed to get child node because of no parent.")); -+ -+ xmlNodePtr node; -+ for (node = soap_node_get_child_element(parent); node != NULL; node = soap_node_get_next_sibling(node)) -+ { -+ if (xmlStrcmp(node->name, BAD_CAST name) == 0) -+ return node; -+ } -+ -+ return NULL; -+} -+ -+soap_request_t * -+soap_request_new_for_method(const char *method) -+{ -+ char *xml_str = xasprintf(SOAP_TEMPLATE, method, method); -+ -+ xmlDocPtr doc = xmlParseDoc(BAD_CAST xml_str); -+ free(xml_str); -+ -+ if (doc == NULL) -+ error_msg_and_die(_("SOAP: Failed to parse xml during creating request.")); -+ -+ soap_request_t *req = soap_request_new(); -+ -+ req->sr_root = xmlDocGetRootElement(doc); -+ if (req->sr_root == NULL) -+ { -+ soap_request_free(req); -+ error_msg_and_die(_("SOAP: Failed to get xml root element.")); -+ } -+ -+ req->sr_body = soap_node_get_child_node(req->sr_root, "Body"); -+ req->sr_method = soap_node_get_child_node(req->sr_body, method); -+ -+ return req; -+} -+ -+static xmlNodePtr -+soap_node_add_child_node(xmlNodePtr node, const char *name, const char *type, const char *value) -+{ -+ if (node == NULL || name == NULL) -+ error_msg_and_die(_("SOAP: Failed to add a new child node because of no node or no child name.")); -+ -+ xmlNodePtr new_node = xmlNewTextChild(node, /* namespace */ NULL, BAD_CAST name, BAD_CAST value); -+ -+ if (new_node == NULL) -+ error_msg_and_die(_("SOAP: Failed to create a new xml child item.")); -+ -+ if (type != NULL) -+ { -+ if (xmlNewProp(new_node, BAD_CAST "xsi:type", BAD_CAST type) == NULL) -+ error_msg_and_die(_("SOAP: Failed to create a new property.")); -+ } -+ -+ return new_node; -+} -+ -+void -+soap_request_add_method_parameter(soap_request_t *req, const char *name, const char *type, const char *value) -+{ -+ if (req == NULL || req->sr_method == NULL) -+ error_msg_and_die(_("SOAP: Failed to add method parametr.")); -+ -+ soap_node_add_child_node(req->sr_method, name, type, value); -+ return; -+} -+ -+void -+soap_request_add_credentials_parameter(soap_request_t *req, const mantisbt_settings_t *settings) -+{ -+ soap_request_add_method_parameter(req, "username", SOAP_STRING, settings->m_login); -+ soap_request_add_method_parameter(req, "password", SOAP_STRING, settings->m_password); -+ -+ return; -+} -+ -+static void -+soap_add_new_issue_parameters(soap_request_t *req, -+ const char *project, -+ const char *version, -+ const char *category, -+ const char *summary, -+ const char *description, -+ const char *additional_information, -+ bool private, -+ mantisbt_custom_fields_t *fields, -+ const char *duphash, -+ const char *tracker_url) -+{ -+ if (req == NULL || req->sr_method == NULL) -+ error_msg_and_die(_("SOAP: Failed to add new issue parametrs.")); -+ -+ if (project == NULL || category == NULL || summary == NULL || description == NULL) -+ error_msg_and_die(_("SOAP: Failed to add new issue parameters because the required items are missing.")); -+ -+ xmlNodePtr issue_node = soap_node_add_child_node(req->sr_method, "issue", SOAP_ISSUEDATA, /* content */ NULL); -+ -+ // project -+ xmlNodePtr project_node = soap_node_add_child_node(issue_node, "project", SOAP_OBJECTREF, /* content */ NULL); -+ soap_node_add_child_node(project_node, "name", SOAP_STRING, project); -+ -+ // view status -+ xmlNodePtr view_node = soap_node_add_child_node(issue_node, "view_state", SOAP_OBJECTREF, /* content */ NULL); -+ soap_node_add_child_node(view_node, "name", SOAP_STRING, (private) ? "private" : "public"); -+ -+ /* if any custom field exists */ -+ int custom_fields_count = 0; -+ xmlNodePtr duphash_node; -+ if (fields->cf_abrt_hash_id != NULL || fields->cf_url_id != NULL) -+ duphash_node = soap_node_add_child_node(issue_node, "custom_fields", SOAP_CUSTOMFIELD_ARRAY, /* content */ NULL); -+ -+ // custom fields (duphash and URL to tracker) -+ xmlNodePtr item_node, field_node; -+ if (fields->cf_abrt_hash_id != NULL) -+ { -+ item_node = soap_node_add_child_node(duphash_node, "item", SOAP_CUSTOMFIELD, /* content */ NULL); -+ field_node = soap_node_add_child_node(item_node, "field", SOAP_OBJECTREF, /* content */ NULL); -+ soap_node_add_child_node(field_node, "id", SOAP_INTEGER, /* custom_field id */ fields->cf_abrt_hash_id); -+ soap_node_add_child_node(item_node, "value", SOAP_STRING, duphash); -+ ++custom_fields_count; -+ } -+ -+ // if tracker url exists, attach it to the issue -+ if (tracker_url != NULL && fields->cf_url_id != NULL) -+ { -+ item_node = soap_node_add_child_node(duphash_node, "item", SOAP_CUSTOMFIELD, /* content */ NULL); -+ field_node = soap_node_add_child_node(item_node, "field", SOAP_OBJECTREF, /* content */ NULL); -+ soap_node_add_child_node(field_node, "id", SOAP_INTEGER, /* custom_field */ fields->cf_url_id); -+ soap_node_add_child_node(item_node, "value", SOAP_STRING, tracker_url); -+ ++custom_fields_count; -+ } -+ -+ if (custom_fields_count > 0) -+ { -+ char *type = xasprintf("%s[%i]", SOAP_CUSTOMFIELD, custom_fields_count); -+ -+ if (xmlNewProp(duphash_node, BAD_CAST "ns3:arrayType", BAD_CAST type) == NULL) -+ error_msg_and_die(_("SOAP: Failed to create a new property in custom fields.")); -+ -+ free(type); -+ } -+ -+ soap_node_add_child_node(issue_node, "os_build", SOAP_STRING, version); -+ soap_node_add_child_node(issue_node, "category", SOAP_STRING, category); -+ soap_node_add_child_node(issue_node, "summary", SOAP_STRING, summary); -+ soap_node_add_child_node(issue_node, "description", SOAP_STRING, description); -+ soap_node_add_child_node(issue_node, "additional_information", SOAP_STRING, additional_information); -+ -+ return; -+} -+ -+char * -+soap_request_to_str(const soap_request_t *req) -+{ -+ if (req == NULL || req->sr_root == NULL || req->sr_root->doc == NULL) -+ error_msg_and_die(_("SOAP: Failed to create SOAP string because of invalid function arguments.")); -+ -+ xmlBufferPtr buffer = xmlBufferCreate(); -+ int err = xmlNodeDump(buffer, req->sr_root->doc, req->sr_root, 1, /* formatting */ 0); -+ if (err == -1) -+ { -+ xmlBufferFree(buffer); -+ error_msg_and_die(_("SOAP: Failed to dump xml node.")); -+ } -+ -+ char *ret = xasprintf("%s%s", XML_VERSION, (const char *) xmlBufferContent(buffer)); -+ xmlBufferFree(buffer); -+ -+ return ret; -+} -+ -+#if 0 -+void -+soap_request_print(soap_request_t *req) -+{ -+ if (req == NULL || req->sr_root == NULL || req->sr_root->doc == NULL) -+ error_msg_and_die(_("SOAP: Failed to print SOAP string.")); -+ -+ xmlBufferPtr buffer = xmlBufferCreate(); -+ int err = xmlNodeDump(buffer, req->sr_root->doc, req->sr_root, 1, /* formatting */ 0); -+ if (err == -1) -+ { -+ xmlBufferFree(buffer); -+ error_msg_and_die(_("Failed to dump xml node.")); -+ } -+ -+ puts((const char *) xmlBufferContent(buffer)); -+ -+ xmlBufferFree(buffer); -+ return; -+} -+#endif -+ -+static bool -+reader_move_reader_if_node_type_is_element_with_name_and_verify_its_value(xmlTextReaderPtr reader, const char *name) -+{ -+ /* is not element node */ -+ if (xmlTextReaderNodeType(reader) != XML_ELEMENT_NODE) -+ return false; -+ -+ /* is not required name */ -+ if (xmlStrcmp(xmlTextReaderConstName(reader), BAD_CAST name) != 0) -+ return false; -+ -+ /* read next node */ -+ if (xmlTextReaderRead(reader) != 1) -+ return false; -+ -+ /* no value node */ -+ if (xmlTextReaderHasValue(reader) == 0) -+ return false; -+ -+ /* no text node */ -+ if (xmlTextReaderNodeType(reader) != XML_TEXT_NODE) -+ return false; -+ -+ return true; -+} -+ -+static void -+reader_find_element_by_name(xmlTextReaderPtr reader, const char *name) -+{ -+ while (xmlTextReaderRead(reader) == 1) -+ { -+ /* is not element node */ -+ if (xmlTextReaderNodeType(reader) != XML_ELEMENT_NODE) -+ continue; -+ -+ /* is not required name */ -+ if (xmlStrcmp(xmlTextReaderConstName(reader), BAD_CAST name) != 0) -+ continue; -+ -+ break; -+ } -+ -+ return; -+} -+ -+/* It is not possible to search only by name because the response contains -+ * different node with the same name. (e.g. id - user id, project id, issue id etc.) -+ * We are interested in only about issues id which is located at a different depth than others. -+ * ... -+ * <item xsi:type="ns1:IssueData"> -+ * <id xsi:type="xsd:integer">10</id> <-- This is issue ID (required) -+ * <view_state xsi:type="ns1:ObjectRef"> -+ * <id xsi:type="xsd:integer">10</id> <-- This is view_state ID (not required) -+ * <name xsi:type="xsd:string">public</name> -+ * </view_state> -+ * <project xsi:type="ns1:ObjectRef"> -+ * <id xsi:type="xsd:integer">1</id> <-- This is project ID (not required) -+ * <name xsi:type="xsd:string">test</name> -+ * </project> -+ * ... -+ */ -+static GList * -+response_values_at_depth_by_name(const char *xml, const char *name, int depth) -+{ -+ xmlDocPtr doc = xmlParseDoc(BAD_CAST xml); -+ if (doc == NULL) -+ error_msg_and_die(_("SOAP: Failed to parse xml (searching value at depth by name).")); -+ -+ xmlTextReaderPtr reader = xmlReaderWalker(doc); -+ if (reader == NULL) -+ error_msg_and_die(_("SOAP: Failed to create xml text reader.")); -+ -+ GList *result = NULL; -+ -+ const xmlChar *value; -+ while (xmlTextReaderRead(reader) == 1) -+ { -+ /* is not right depth */ -+ if (depth != -1 && xmlTextReaderDepth(reader) != depth) -+ continue; -+ -+ if (reader_move_reader_if_node_type_is_element_with_name_and_verify_its_value(reader, name) == false) -+ continue; -+ -+ if ((value = xmlTextReaderConstValue(reader)) != NULL) -+ result = g_list_append(result, xstrdup((const char *) value)); -+ } -+ xmlFreeTextReader(reader); -+ -+ return result; -+} -+ -+/* -+ * Finds an element named 'elem' and returns a text of a child named 'name' -+ * -+ * Example: -+ * For -+ * <elem> -+ * <id>1</id> -+ * <name>foo</name> -+ * </elem> -+ * -+ * returns "foo" -+ */ -+static char * -+response_get_name_value_of_element(const char *xml, const char *element) -+{ -+ xmlDocPtr doc = xmlParseDoc(BAD_CAST xml); -+ if (doc == NULL) -+ error_msg_and_die(_("SOAP: Failed to parse xml.")); -+ -+ xmlTextReaderPtr reader = xmlReaderWalker(doc); -+ if (reader == NULL) -+ error_msg_and_die(_("SOAP: Failed to create xml text reader.")); -+ -+ const xmlChar *value = NULL; -+ -+ reader_find_element_by_name(reader, element); -+ -+ /* find 'name' element and return its text */ -+ while (xmlTextReaderRead(reader) == 1) -+ { -+ if (reader_move_reader_if_node_type_is_element_with_name_and_verify_its_value(reader, "name") == false) -+ continue; -+ -+ if ((value = xmlTextReaderConstValue(reader)) != NULL) -+ break; -+ } -+ xmlFreeTextReader(reader); -+ -+ return (char *) value; -+} -+ -+static int -+response_get_id_of_relatedto_issue(const char *xml) -+{ -+ xmlDocPtr doc = xmlParseDoc(BAD_CAST xml); -+ if (doc == NULL) -+ error_msg_and_die(_("SOAP: Failed to parse xml (get related to issue).")); -+ -+ xmlTextReaderPtr reader = xmlReaderWalker(doc); -+ if (reader == NULL) -+ error_msg_and_die(_("SOAP: Failed to create xml text reader.")); -+ -+ const xmlChar *value = NULL; -+ const xmlChar *id = NULL; -+ -+ /* find relationships section */ -+ reader_find_element_by_name(reader, "relationships"); -+ -+ /* find "name" value of 'name' element */ -+ while (xmlTextReaderRead(reader) == 1) -+ { -+ /* find type of relattionship */ -+ if (reader_move_reader_if_node_type_is_element_with_name_and_verify_its_value(reader, "name") == false) -+ continue; -+ -+ if ((value = xmlTextReaderConstValue(reader)) == NULL) -+ continue; -+ -+ /* we need 'duplicate of' realtionship type */ -+ if (xmlStrcmp(value, BAD_CAST "duplicate of") != 0) -+ continue; -+ -+ /* find id of duplicate issues */ -+ reader_find_element_by_name(reader, "target_id"); -+ -+ /* verify target_id node */ -+ if (reader_move_reader_if_node_type_is_element_with_name_and_verify_its_value(reader, "target_id") == false) -+ continue; -+ -+ /* get its value */ -+ if ((id = xmlTextReaderConstValue(reader)) != NULL) -+ break; -+ } -+ xmlFreeTextReader(reader); -+ -+ return (id == NULL) ? -1 : atoi((const char *) id); -+} -+ -+GList * -+response_get_main_ids_list(const char *xml) -+{ -+ return response_values_at_depth_by_name(xml, "id", 5); -+} -+ -+int -+response_get_main_id(const char *xml) -+{ -+ GList *l = response_values_at_depth_by_name(xml, "id", 5); -+ return (l != NULL) ? atoi(l->data) : -1; -+} -+ -+static int -+response_get_return_value(const char *xml) -+{ -+ GList *l = response_values_at_depth_by_name(xml, "return", 3); -+ return (l != NULL) ? atoi(l->data) : -1; -+} -+ -+static char* -+response_get_return_value_as_string(const char *xml) -+{ -+ GList *l = response_values_at_depth_by_name(xml, "return", 3); -+ return (l != NULL) ? l->data : NULL; -+} -+ -+static char * -+response_get_error_msg(const char *xml) -+{ -+ GList *l = response_values_at_depth_by_name(xml, "faultstring", 3); -+ return (l != NULL) ? xstrdup(l->data) : NULL; -+} -+ -+static char * -+response_get_additioanl_information(const char *xml) -+{ -+ GList *l = response_values_at_depth_by_name(xml, "additional_information", -1); -+ return (l != NULL) ? xstrdup(l->data) : NULL; -+} -+ -+void -+response_values_free(GList *values) -+{ -+ g_list_free_full(values, free); -+} -+ -+/* -+ * POST -+ */ -+ -+void -+mantisbt_result_free(mantisbt_result_t *result) -+{ -+ if (result == NULL) -+ return; -+ -+ free(result->mr_url); -+ free(result->mr_msg); -+ free(result->mr_body); -+ free(result); -+} -+ -+mantisbt_result_t * -+mantisbt_soap_call(const mantisbt_settings_t *settings, const soap_request_t *req) -+{ -+ char *request = soap_request_to_str(req); -+ -+ const char *url = settings->m_mantisbt_soap_url; -+ -+ mantisbt_result_t *result = xzalloc(sizeof(*result)); -+ -+ if (url == NULL || request == NULL) -+ { -+ result->mr_error = -2; -+ result->mr_msg = xasprintf(_("Url or request isn't specified.")); -+ free(request); -+ -+ return result; -+ } -+ -+ char *url_copy = NULL; -+ -+ int redirect_count = 0; -+ char *errmsg; -+ post_state_t *post_state; -+ -+redirect: -+ post_state = new_post_state(0 -+ + POST_WANT_HEADERS -+ + POST_WANT_BODY -+ + POST_WANT_ERROR_MSG -+ + (settings->m_ssl_verify ? POST_WANT_SSL_VERIFY : 0) -+ ); -+ -+ post_string(post_state, settings->m_mantisbt_soap_url, "text/xml", NULL, request); -+ -+ char *location = find_header_in_post_state(post_state, "Location:"); -+ -+ switch (post_state->http_resp_code) -+ { -+ case 404: -+ result->mr_error = -1; -+ result->mr_msg = xasprintf(_("Error in HTTP POST, " -+ "HTTP code: 404 (Not found), URL:'%s'"), url); -+ break; -+ case 500: -+ result->mr_error = -1; -+ result->mr_msg = response_get_error_msg(post_state->body); -+ -+ break; -+ case 301: /* "301 Moved Permanently" (for example, used to move http:// to https://) */ -+ case 302: /* "302 Found" (just in case) */ -+ case 305: /* "305 Use Proxy" */ -+ if (++redirect_count < 10 && location) -+ { -+ free(url_copy); -+ url = url_copy = xstrdup(location); -+ free_post_state(post_state); -+ goto redirect; -+ } -+ /* fall through */ -+ -+ default: -+ result->mr_error = -1; -+ errmsg = post_state->curl_error_msg; -+ if (errmsg && errmsg[0]) -+ result->mr_msg = xasprintf(_("Error in MantisBT request at '%s': %s"), url, errmsg); -+ else -+ result->mr_msg = xasprintf(_("Error in MantisBT request at '%s'"), url); -+ break; -+ -+ case 200: -+ case 201: -+ /* sent successfully */ -+ result->mr_url = xstrdup(location); /* note: xstrdup(NULL) returns NULL */ -+ } /* switch (HTTP code) */ -+ -+ result->mr_http_resp_code = post_state->http_resp_code; -+ result->mr_body = post_state->body; -+ post_state->body = NULL; -+ -+ free_post_state(post_state); -+ free(url_copy); -+ free(request); -+ -+ return result; -+} -+ -+int -+mantisbt_attach_data(const mantisbt_settings_t *settings, const char *bug_id, -+ const char *att_name, const char *data, int size) -+{ -+ soap_request_t *req = soap_request_new_for_method("mc_issue_attachment_add"); -+ soap_request_add_credentials_parameter(req, settings); -+ -+ soap_request_add_method_parameter(req, "issue_id", SOAP_INTEGER, bug_id); -+ soap_request_add_method_parameter(req, "name", SOAP_STRING, att_name); -+ -+ soap_request_add_method_parameter(req, "file_type", SOAP_STRING, "text"); -+ soap_request_add_method_parameter(req, "content", SOAP_BASE64, encode_base64(data, size)); -+ -+ mantisbt_result_t *result = mantisbt_soap_call(settings, req); -+ soap_request_free(req); -+ -+ if (result->mr_http_resp_code != 200) -+ { -+ int ret = -1; -+ if (strcmp(result->mr_msg, "Duplicate filename.") == 0) -+ ret = -2; -+ -+ error_msg(_("Failed to attach file: '%s'"), result->mr_msg); -+ mantisbt_result_free(result); -+ return ret; -+ } -+ -+ int id = response_get_return_value(result->mr_body); -+ -+ mantisbt_result_free(result); -+ -+ return id; -+} -+ -+static int -+mantisbt_attach_fd(const mantisbt_settings_t *settings, const char *bug_id, -+ const char *att_name, int fd) -+{ -+ off_t size = lseek(fd, 0, SEEK_END); -+ if (size < 0) -+ { -+ perror_msg(_("Can't lseek '%s'"), att_name); -+ return -1; -+ } -+ -+ if (size >= MANTISBT_MAX_FILE_UPLOAD_SIZE) -+ { -+ error_msg(_("Can't upload '%s', it's too large (%llu bytes)"), att_name, (long long)size); -+ return -1; -+ } -+ lseek(fd, 0, SEEK_SET); -+ -+ char *data = xmalloc(size + 1); -+ ssize_t r = full_read(fd, data, size); -+ if (r < 0) -+ { -+ free(data); -+ perror_msg(_("Can't read '%s'"), att_name); -+ return -1; -+ } -+ -+ int res = mantisbt_attach_data(settings, bug_id, att_name, data, size); -+ free(data); -+ return res; -+} -+ -+int -+mantisbt_attach_file(const mantisbt_settings_t *settings, const char *bug_id, -+ const char *att_name, const char *path) -+{ -+ int fd = open(path, O_RDONLY); -+ if (fd < 0) -+ { -+ perror_msg(_("Can't open '%s'"), path); -+ return 0; -+ } -+ errno = 0; -+ struct stat st; -+ if (fstat(fd, &st) != 0 || !S_ISREG(st.st_mode)) -+ { -+ perror_msg("'%s': not a regular file", path); -+ close(fd); -+ return 0; -+ } -+ log_debug("attaching '%s' as file", att_name); -+ int ret = mantisbt_attach_fd(settings, bug_id, att_name, fd); -+ close(fd); -+ return ret; -+} -+ -+static void -+soap_filter_add_new_array_parameter(xmlNodePtr filter_node, const char *name, const char *type, const char *value) -+{ -+ const char *array_type = NULL; -+ if( strcmp(type, SOAP_INTEGER) == 0 ) -+ array_type = SOAP_INTEGERARRAY; -+ else -+ array_type = SOAP_STRINGARRAY; -+ -+ xmlNodePtr filter_item = soap_node_add_child_node(filter_node, name, array_type, /* content */ NULL); -+ soap_node_add_child_node(filter_item, "item", type, value); -+} -+ -+static void -+soap_filter_custom_fields_add_new_item(xmlNodePtr filter_node, const char *custom_field_name, const char *value) -+{ -+ xmlNodePtr item_node = soap_node_add_child_node(filter_node, "item", SOAP_FILTER_CUSTOMFIELD, /* content */ NULL); -+ -+ xmlNodePtr field_node = soap_node_add_child_node(item_node, "field", SOAP_OBJECTREF, /* content */ NULL); -+ soap_node_add_child_node(field_node, "name", SOAP_STRING, custom_field_name); -+ -+ xmlNodePtr value_node = soap_node_add_child_node(item_node, "value", SOAP_STRINGARRAY, /* content */ NULL); -+ soap_node_add_child_node(value_node, "item", SOAP_STRING, value); -+} -+ -+GList * -+mantisbt_search_by_abrt_hash(mantisbt_settings_t *settings, const char *abrt_hash) -+{ -+ soap_request_t *req = soap_request_new_for_method("mc_filter_search_issues"); -+ soap_request_add_credentials_parameter(req, settings); -+ -+ xmlNodePtr filter_node = soap_node_add_child_node(req->sr_method, "filter", SOAP_FILTER_SEARCH_DATA, /* content */ NULL); -+ -+ /* 'hide_status_is : -2' means, searching within all status */ -+ soap_filter_add_new_array_parameter(filter_node, "hide_status_id", SOAP_INTEGERARRAY, "-2"); -+ -+ // custom fields -+ xmlNodePtr custom_fields_node = soap_node_add_child_node(filter_node, "custom_fields", SOAP_FILTER_CUSTOMFIELD_ARRAY, /* content */ NULL); -+ -+ // custom field 'abrt_hash' -+ soap_filter_custom_fields_add_new_item(custom_fields_node, "abrt_hash", abrt_hash); -+ -+ soap_request_add_method_parameter(req, "page_number", SOAP_INTEGER, "1"); -+ soap_request_add_method_parameter(req, "per_page", SOAP_INTEGER, /* -1 means get all issues */ "-1"); -+ -+ mantisbt_result_t *result = mantisbt_soap_call(settings, req); -+ soap_request_free(req); -+ -+ if (result->mr_error == -1) -+ { -+ error_msg(_("Failed to search MantisBT issue by duphash: '%s'"), result->mr_msg); -+ mantisbt_result_free(result); -+ return NULL; -+ } -+ -+ GList *ids = response_get_main_ids_list(result->mr_body); -+ -+ return ids; -+} -+ -+GList * -+mantisbt_search_duplicate_issues(mantisbt_settings_t *settings, const char *category, -+ const char *version, const char *abrt_hash) -+{ -+ soap_request_t *req = soap_request_new_for_method("mc_filter_search_issues"); -+ soap_request_add_credentials_parameter(req, settings); -+ -+ xmlNodePtr filter_node = soap_node_add_child_node(req->sr_method, "filter", SOAP_FILTER_SEARCH_DATA, /* content */ NULL); -+ -+ soap_filter_add_new_array_parameter(filter_node, "project_id", SOAP_INTEGERARRAY, settings->m_project_id); -+ -+ /* 'hide_status_is : -2' means, searching within all status */ -+ soap_filter_add_new_array_parameter(filter_node, "hide_status_id", SOAP_INTEGERARRAY, "-2"); -+ -+ soap_filter_add_new_array_parameter(filter_node, "category", SOAP_STRINGARRAY, category); -+ -+ // custom fields -+ xmlNodePtr custom_fields_node = soap_node_add_child_node(filter_node, "custom_fields", SOAP_FILTER_CUSTOMFIELD_ARRAY, /* content */ NULL); -+ -+ // custom field 'abrt_hash' -+ soap_filter_custom_fields_add_new_item(custom_fields_node, "abrt_hash", abrt_hash); -+ -+ // version -+ if (version != NULL) -+ soap_filter_add_new_array_parameter(filter_node, "os_build", SOAP_STRINGARRAY, version); -+ -+ soap_request_add_method_parameter(req, "page_number", SOAP_INTEGER, "1"); -+ soap_request_add_method_parameter(req, "per_page", SOAP_INTEGER, /* -1 means get all issues */ "-1"); -+ -+ mantisbt_result_t *result = mantisbt_soap_call(settings, req); -+ soap_request_free(req); -+ -+ if (result->mr_error == -1) -+ { -+ error_msg(_("Failed to search MantisBT duplicate issue: '%s'"), result->mr_msg); -+ mantisbt_result_free(result); -+ return NULL; -+ } -+ -+ GList *ids = response_get_main_ids_list(result->mr_body); -+ -+ return ids; -+} -+ -+static char * -+custom_field_get_id_from_name(GList *ids, GList *names, const char *name) -+{ -+ GList *i = ids; -+ GList *n = names; -+ for (; i != NULL; i = i->next, n = n->next) -+ { -+ if (strcmp(n->data, name) == 0) -+ return i->data; -+ } -+ -+ return NULL; -+} -+ -+static void -+custom_field_ask(const char *name) -+{ -+ char *msg = xasprintf(_("CentOS Bug Tracker doesn't contain custom field '%s', which is required for full functionality of the reporter. Do you still want to create a new issue?"), name); -+ int yes = ask_yes_no(msg); -+ free(msg); -+ -+ if (!yes) -+ { -+ set_xfunc_error_retval(EXIT_CANCEL_BY_USER); -+ xfunc_die(); -+ } -+ -+ return; -+} -+ -+static void -+mantisbt_get_custom_fields(const mantisbt_settings_t *settings, mantisbt_custom_fields_t *fields, const char *project_id) -+{ -+ soap_request_t *req = soap_request_new_for_method("mc_project_get_custom_fields"); -+ soap_request_add_credentials_parameter(req, settings); -+ soap_request_add_method_parameter(req, "project_id", SOAP_INTEGER, project_id); -+ -+ mantisbt_result_t *result = mantisbt_soap_call(settings, req); -+ soap_request_free(req); -+ -+ if (result->mr_http_resp_code != 200) -+ error_msg_and_die(_("Failed to get custom fields for '%s' project"), settings->m_project); -+ -+ GList *ids = response_values_at_depth_by_name(result->mr_body, "id", -1); -+ GList *names = response_values_at_depth_by_name(result->mr_body, "name", -1); -+ -+ mantisbt_result_free(result); -+ -+ if ((fields->cf_abrt_hash_id = custom_field_get_id_from_name(ids, names, CUSTOMFIELD_DUPHASH)) == NULL) -+ custom_field_ask(CUSTOMFIELD_DUPHASH); -+ -+ if ((fields->cf_url_id = custom_field_get_id_from_name(ids, names, CUSTOMFIELD_URL)) == NULL) -+ custom_field_ask(CUSTOMFIELD_URL); -+ -+ return; -+} -+ -+int -+mantisbt_create_new_issue(const mantisbt_settings_t *settings, -+ problem_data_t *problem_data, -+ const problem_report_t *pr, -+ const char *tracker_url) -+{ -+ -+ const char *category = problem_data_get_content_or_NULL(problem_data, FILENAME_COMPONENT); -+ const char *duphash = problem_data_get_content_or_NULL(problem_data, FILENAME_DUPHASH); -+ -+ char *summary = shorten_string_to_length(problem_report_get_summary(pr), MAX_SUMMARY_LENGTH); -+ -+ const char *description = problem_report_get_description(pr); -+ const char *additional_information = problem_report_get_section(pr, PR_SEC_ADDITIONAL_INFO); -+ -+ mantisbt_custom_fields_t fields; -+ mantisbt_get_custom_fields(settings, &fields, settings->m_project_id); -+ -+ soap_request_t *req = soap_request_new_for_method("mc_issue_add"); -+ soap_request_add_credentials_parameter(req, settings); -+ soap_add_new_issue_parameters(req, settings->m_project, settings->m_project_version, category, summary, description, additional_information, settings->m_create_private, &fields, duphash, tracker_url); -+ -+ mantisbt_result_t *result = mantisbt_soap_call(settings, req); -+ soap_request_free(req); -+ free(summary); -+ -+ if (result->mr_error == -1) -+ { -+ error_msg(_("Failed to create a new issue: '%s'"), result->mr_msg); -+ mantisbt_result_free(result); -+ return -1; -+ } -+ -+ int id = response_get_return_value(result->mr_body); -+ -+ mantisbt_result_free(result); -+ return id; -+} -+ -+mantisbt_issue_info_t * -+mantisbt_get_issue_info(const mantisbt_settings_t *settings, int issue_id) -+{ -+ soap_request_t *req = soap_request_new_for_method("mc_issue_get"); -+ soap_request_add_credentials_parameter(req, settings); -+ -+ char *issue_id_str = xasprintf("%d", issue_id); -+ soap_request_add_method_parameter(req, "issue_id", SOAP_INTEGER, issue_id_str); -+ free(issue_id_str); -+ -+ mantisbt_result_t *result = mantisbt_soap_call(settings, req); -+ soap_request_free(req); -+ -+ if (result->mr_error == -1) -+ { -+ error_msg(_("Failed to get MantisBT issue: '%s'"), result->mr_msg); -+ mantisbt_result_free(result); -+ return NULL; -+ } -+ -+ mantisbt_issue_info_t *issue_info = mantisbt_issue_info_new(); -+ -+ issue_info->mii_id = issue_id; -+ issue_info->mii_status = response_get_name_value_of_element(result->mr_body, "status"); -+ issue_info->mii_resolution = response_get_name_value_of_element(result->mr_body, "resolution"); -+ issue_info->mii_reporter = response_get_name_value_of_element(result->mr_body, "reporter"); -+ issue_info->mii_project = response_get_name_value_of_element(result->mr_body, "project"); -+ -+ if (strcmp(issue_info->mii_status, "closed") == 0 && !issue_info->mii_resolution) -+ error_msg(_("Issue %i is CLOSED, but it has no RESOLUTION"), issue_info->mii_id); -+ -+ issue_info->mii_dup_id = response_get_id_of_relatedto_issue(result->mr_body); -+ -+ if (strcmp(issue_info->mii_status, "closed") == 0 -+ && strcmp(issue_info->mii_resolution, "duplicate") == 0 -+ && issue_info->mii_dup_id == -1 ) -+ { -+ error_msg(_("Issue %i is CLOSED as DUPLICATE, but it has no DUPLICATE_ID"), -+ issue_info->mii_id); -+ } -+ -+ /* notes are stored in <text> element */ -+ issue_info->mii_notes = response_values_at_depth_by_name(result->mr_body, "text", -1); -+ -+ /* looking for bt rating in additional information too */ -+ char *add_info = response_get_additioanl_information(result->mr_body); -+ if (add_info != NULL) -+ issue_info->mii_notes = g_list_append (issue_info->mii_notes, add_info); -+ issue_info->mii_attachments = response_values_at_depth_by_name(result->mr_body, "filename", -1); -+ issue_info->mii_best_bt_rating = comments_find_best_bt_rating(issue_info->mii_notes); -+ -+ mantisbt_result_free(result); -+ return issue_info; -+} -+ -+int -+mantisbt_add_issue_note(const mantisbt_settings_t *settings, int issue_id, const char *note) -+{ -+ soap_request_t *req = soap_request_new_for_method("mc_issue_note_add"); -+ soap_request_add_credentials_parameter(req, settings); -+ -+ char *issue_id_str = xasprintf("%i", issue_id); -+ soap_node_add_child_node(req->sr_method, "issue_id", SOAP_INTEGER, issue_id_str); -+ -+ xmlNodePtr note_node = soap_node_add_child_node(req->sr_method, "note", SOAP_ISSUENOTE, /* content */ NULL); -+ soap_node_add_child_node(note_node, "text", SOAP_STRING, note); -+ -+ mantisbt_result_t *result = mantisbt_soap_call(settings, req); -+ -+ free(issue_id_str); -+ soap_request_free(req); -+ -+ if (result->mr_error == -1) -+ { -+ error_msg(_("Failed to add MantisBT issue note: '%s'"), result->mr_msg); -+ mantisbt_result_free(result); -+ return -1; -+ } -+ int id = response_get_return_value(result->mr_body); -+ -+ mantisbt_result_free(result); -+ return id; -+} -+ -+void -+mantisbt_get_project_id_from_name(mantisbt_settings_t *settings) -+{ -+ if (settings->m_project == NULL) -+ error_msg_and_die(_("The MantisBT project has not been deretmined.")); -+ -+ soap_request_t *req = soap_request_new_for_method("mc_project_get_id_from_name"); -+ soap_request_add_credentials_parameter(req, settings); -+ soap_node_add_child_node(req->sr_method, "project_name", SOAP_STRING, settings->m_project); -+ -+ mantisbt_result_t *result = mantisbt_soap_call(settings, req); -+ -+ if (result->mr_http_resp_code != 200) -+ error_msg_and_die(_("Failed to get project id from name")); -+ -+ settings->m_project_id = response_get_return_value_as_string(result->mr_body); -+ -+ return; -+} -diff --git a/src/plugins/mantisbt.conf b/src/plugins/mantisbt.conf -new file mode 100644 -index 0000000..15a1065 ---- /dev/null -+++ b/src/plugins/mantisbt.conf -@@ -0,0 +1,8 @@ -+# MantisBT URL -+MantisbtURL = https://bugs.centos.org/ -+# yes means that ssl certificates will be checked -+SSLVerify = yes -+# your login has to exist, if you don have any, please create one -+Login = -+# your password -+Password = -diff --git a/src/plugins/mantisbt.h b/src/plugins/mantisbt.h -new file mode 100644 -index 0000000..c31c174 ---- /dev/null -+++ b/src/plugins/mantisbt.h -@@ -0,0 +1,140 @@ -+/* -+ Copyright (C) 2014 ABRT team -+ Copyright (C) 2014 RedHat Inc -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation; either version 2 of the License, or -+ (at your option) any later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License along -+ with this program; if not, write to the Free Software Foundation, Inc., -+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -+*/ -+ -+#ifndef MANTISBT_H -+#define MANTISBT_H -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+#include <libxml/encoding.h> -+#include "problem_report.h" -+ -+#define SOAP_STRING "ns2:string" -+#define SOAP_INTEGER "ns2:integer" -+#define SOAP_INTEGERARRAY "ns2:IntegerArray" -+#define SOAP_STRINGARRAY "ns2:StringArray" -+#define SOAP_ISSUEDATA "ns3:IssueData" -+#define SOAP_OBJECTREF "ns3:ObjectRef" -+#define SOAP_CUSTOMFIELD_ARRAY "ns2:CustomFieldValueForIssueDataArray" -+#define SOAP_FILTER_CUSTOMFIELD "ns2:FilterCustomField" -+#define SOAP_FILTER_CUSTOMFIELD_ARRAY "ns2:FilterCustomFieldArray" -+#define SOAP_FILTER_SEARCH_DATA "ns2:FilterSearchData" -+#define SOAP_CUSTOMFIELD "ns2:CustomFieldValueForIssueData" -+#define SOAP_BASE64 "SOAP-ENC:base64" -+#define SOAP_ISSUENOTE "ns3:IssueNoteData" -+ -+#define PR_SEC_ADDITIONAL_INFO "Additional info" -+ -+typedef struct soap_request -+{ -+ xmlNodePtr sr_root; -+ xmlNodePtr sr_body; -+ xmlNodePtr sr_method; -+} soap_request_t; -+ -+typedef struct mantisbt_settings -+{ -+ char *m_login; -+ char *m_password; -+ const char *m_mantisbt_url; -+ const char *m_mantisbt_soap_url; -+ char *m_project; -+ char *m_project_id; -+ char *m_project_version; -+ const char *m_DontMatchComponents; -+ int m_ssl_verify; -+ int m_create_private; -+} mantisbt_settings_t; -+ -+typedef struct mantisbt_result -+{ -+ int mr_http_resp_code; -+ int mr_error; -+ char *mr_msg; -+ char *mr_url; -+ char *mr_body; -+} mantisbt_result_t; -+ -+typedef struct mantisbt_issue_info -+{ -+ int mii_id; -+ int mii_dup_id; -+ unsigned mii_best_bt_rating; -+ -+ char *mii_status; -+ char *mii_resolution; -+ char *mii_reporter; -+ char *mii_project; -+ -+ GList *mii_notes; -+ GList *mii_attachments; -+} mantisbt_issue_info_t; -+ -+void mantisbt_settings_free(mantisbt_settings_t *settings); -+ -+mantisbt_issue_info_t * mantisbt_issue_info_new(); -+void mantisbt_issue_info_free(mantisbt_issue_info_t *info); -+mantisbt_issue_info_t * mantisbt_find_origin_bug_closed_duplicate(mantisbt_settings_t *settings, mantisbt_issue_info_t *info); -+ -+void soap_request_free(soap_request_t *req); -+ -+soap_request_t *soap_request_new_for_method(const char *method); -+ -+void soap_request_add_method_parameter(soap_request_t *req, const char *name, const char *type, const char *value); -+void soap_request_add_credentials_parameter(soap_request_t *req, const mantisbt_settings_t *settings); -+ -+char *soap_request_to_str(const soap_request_t *req); -+ -+#if 0 -+void soap_request_print(soap_request_t *req); -+#endif -+ -+GList * response_get_main_ids_list(const char *xml); -+int response_get_main_id(const char *xml); -+void response_values_free(GList *values); -+ -+void mantisbt_result_free(mantisbt_result_t *result); -+mantisbt_result_t *mantisbt_soap_call(const mantisbt_settings_t *settings, const soap_request_t *req); -+ -+int mantisbt_attach_data(const mantisbt_settings_t *settings, const char *bug_id, -+ const char *att_name, const char *data, int size); -+ -+int mantisbt_attach_file(const mantisbt_settings_t *settings, const char *bug_id, -+ const char *att_name, const char *data); -+ -+GList * mantisbt_search_by_abrt_hash(mantisbt_settings_t *settings, const char *abrt_hash); -+GList * mantisbt_search_duplicate_issues(mantisbt_settings_t *settings, const char *category, -+ const char *version, const char *abrt_hash); -+ -+int mantisbt_create_new_issue(const mantisbt_settings_t *settings, problem_data_t *problem_data, -+ const problem_report_t *pr, const char *tracker_url); -+ -+mantisbt_issue_info_t * mantisbt_get_issue_info(const mantisbt_settings_t *settings, int issue_id); -+int mantisbt_add_issue_note(const mantisbt_settings_t *settings, int issue_id, const char *note); -+ -+void mantisbt_get_project_id_from_name(mantisbt_settings_t *settings); -+ -+#ifdef __cplusplus -+} -+#endif -+ -+#endif -+ -diff --git a/src/plugins/mantisbt_format.conf b/src/plugins/mantisbt_format.conf -new file mode 100644 -index 0000000..da08aa6 ---- /dev/null -+++ b/src/plugins/mantisbt_format.conf -@@ -0,0 +1,59 @@ -+# Lines starting with # are ignored. -+# Lines can be continued on the next line using trailing backslash. -+# -+# Format: -+# %summary:: summary format -+# section:: element1[,element2]... -+# The literal text line to be added to Bugzilla comment. Can be empty. -+# (IOW: empty lines are NOT ignored!) -+# -+# Summary format is a line of text, where %element% is replaced by -+# text element's content, and [[...%element%...]] block is used only if -+# %element% exists. [[...]] blocks can nest. -+# -+# Sections can be: -+# - %summary: bug summary format string. -+# - %attach: a list of elements to attach. -+# - text, double colon (::) and the list of comma-separated elements. -+# Text can be empty (":: elem1, elem2, elem3" works), -+# in this case "Text:" header line will be omitted. -+# -+# Elements can be: -+# - problem directory element names, which get formatted as -+# <element_name>: <contents> -+# or -+# <element_name>: -+# :<contents> -+# :<contents> -+# :<contents> -+# - problem directory element names prefixed by "%bare_", -+# which is formatted as-is, without "<element_name>:" and colons -+# - %oneline, %multiline, %text wildcards, which select all corresponding -+# elements for output or attachment -+# - %binary wildcard, valid only for %attach section, instructs to attach -+# binary elements -+# - problem directory element names prefixed by "-", -+# which excludes given element from all wildcards -+# -+# Nonexistent elements are silently ignored. -+# If none of elements exists, the section will not be created. -+ -+%summary:: [abrt] %pkg_name%[[: %crash_function%()]][[: %reason%]][[: TAINTED %tainted_short%]] -+ -+Description of problem:: %bare_comment -+ -+Version-Release number of selected component:: %bare_package -+ -+Truncated backtrace:: %bare_%short_backtrace -+ -+%Additional info:: -+:: -pkg_arch,-pkg_epoch,-pkg_name,-pkg_release,-pkg_version,\ -+ -component,-architecture,\ -+ -analyzer,-count,-duphash,-uuid,-abrt_version,\ -+ -username,-hostname,-os_release,-os_info,\ -+ -time,-pid,-pwd,-last_occurrence,-ureports_counter,\ -+ %reporter,\ -+ %oneline -+ -+%attach:: -comment,-reason,-reported_to,-event_log,%multiline,\ -+ -coredump,%binary -diff --git a/src/plugins/mantisbt_formatdup.conf b/src/plugins/mantisbt_formatdup.conf -new file mode 100644 -index 0000000..b1552ac ---- /dev/null -+++ b/src/plugins/mantisbt_formatdup.conf -@@ -0,0 +1,65 @@ -+# Lines starting with # are ignored. -+# Lines can be continued on the next line using trailing backslash. -+# -+# Format: -+# %summary:: summary format -+# section:: element1[,element2]... -+# The literal text line to be added to Bugzilla comment. Can be empty. -+# (IOW: empty lines are NOT ignored!) -+# -+# Summary format is a line of text, where %element% is replaced by -+# text element's content, and [[...%element%...]] block is used only if -+# %element% exists. [[...]] blocks can nest. -+# -+# Sections can be: -+# - %summary: bug summary format string. -+# - %attach: a list of elements to attach. -+# - text, double colon (::) and the list of comma-separated elements. -+# Text can be empty (":: elem1, elem2, elem3" works), -+# in this case "Text:" header line will be omitted. -+# -+# Elements can be: -+# - problem directory element names, which get formatted as -+# <element_name>: <contents> -+# or -+# <element_name>: -+# :<contents> -+# :<contents> -+# :<contents> -+# - problem directory element names prefixed by "%bare_", -+# which is formatted as-is, without "<element_name>:" and colons -+# - %oneline, %multiline, %text wildcards, which select all corresponding -+# elements for output or attachment -+# - %binary wildcard, valid only for %attach section, instructs to attach -+# binary elements -+# - problem directory element names prefixed by "-", -+# which excludes given element from all wildcards -+# -+# Nonexistent elements are silently ignored. -+# If none of elements exists, the section will not be created. -+ -+# When we add a comment to an existing BZ, %summary is ignored -+# (it specifies *new bug* summary field): -+# %summary:: blah blah -+ -+# When dup is detected, BZ reporter adds a comment to it. -+# This comment may interrupt an ongoing conversation in the BZ. -+# (Three people independently filed a bug against abrt about this). -+# Need to clearly explain what this comment is, to prevent confusion. -+# Hopefully, this line would suffice: -+Another user experienced a similar problem: -+ -+# If user filled out comment field, show it: -+:: %bare_comment -+ -+# var_log_messages has too much variance (time/date), -+# we exclude it from message so that dup message elimination has more chances to work -+:: \ -+ -pkg_arch,-pkg_epoch,-pkg_name,-pkg_release,-pkg_version,\ -+ -component,-architecture,\ -+ -analyzer,-count,-duphash,-uuid,-abrt_version,\ -+ -username,-hostname,-os_release,-os_info,\ -+ -time,-pid,-pwd,-last_occurrence,-ureports_counter,\ -+ -var_log_messages,\ -+ %reporter,\ -+ %oneline -diff --git a/src/plugins/report_CentOSBugTracker.conf b/src/plugins/report_CentOSBugTracker.conf -new file mode 100644 -index 0000000..04cab13 ---- /dev/null -+++ b/src/plugins/report_CentOSBugTracker.conf -@@ -0,0 +1,4 @@ -+Mantisbt_MantisbtURL = https://bugs.centos.org -+Mantisbt_Login = -+Mantisbt_Password -+Mantisbt_SSLVerify = yes -diff --git a/src/plugins/report_CentOSBugTracker.xml.in b/src/plugins/report_CentOSBugTracker.xml.in -new file mode 100644 -index 0000000..81f909b ---- /dev/null -+++ b/src/plugins/report_CentOSBugTracker.xml.in -@@ -0,0 +1,65 @@ -+<?xml version="1.0" encoding="UTF-8" ?> -+<event> -+ <_name>CentOS Bug Tracker</_name> -+ <_description>Report to CentOS Bug Tracker</_description> -+ -+ <requires-items>component,duphash,os_release</requires-items> -+ <exclude-items-by-default>coredump,count,event_log,reported_to,vmcore</exclude-items-by-default> -+ <exclude-items-always></exclude-items-always> -+ <exclude-binary-items>no</exclude-binary-items> -+ <include-items-by-default></include-items-by-default> -+ <minimal-rating>3</minimal-rating> -+ <gui-review-elements>yes</gui-review-elements> -+ -+ <options> -+ <option type="text" name="Mantisbt_Login"> -+ <_label>User name</_label> -+ <allow-empty>no</allow-empty> -+ <_description>CentOS Bug Tracker account user name</_description> -+ <_note-html>You can create bugs.centos.org account <a href="https://bugs.centos.org/signup_page.php">here</a></_note-html> -+ </option> -+ <option type="password" name="Mantisbt_Password"> -+ <_label>Password</_label> -+ <allow-empty>no</allow-empty> -+ <_description>CentOS Bug Tracker account password</_description> -+ </option> -+ <advanced-options> -+ <option type="text" name="Mantisbt_MantisbtURL"> -+ <_label>CentOS Bug Tracker URL</_label> -+ <allow-empty>no</allow-empty> -+ <_note-html>Address of CentOS Bug Tracker server</_note-html> -+ <default-value>https://bugs.centos.org</default-value> -+ </option> -+ <option type="bool" name="Mantisbt_SSLVerify"> -+ <_label>Verify SSL</_label> -+ <_note-html>Check SSL key validity</_note-html> -+ <default-value>yes</default-value> -+ </option> -+ <option type="text" name="Mantisbt_Project"> -+ <_label>CentOS Bug Tracker project</_label> -+ <allow-empty>yes</allow-empty> -+ <_note-html>Specify this only if you needed different project than specified in /etc/os-release</_note-html> -+ </option> -+ <option type="text" name="Mantisbt_ProjectVersion"> -+ <_label>CentOS Bug Tracker project version</_label> -+ <allow-empty>yes</allow-empty> -+ <_note-html>Specify this only if you needed different project version than specified in /etc/os-release</_note-html> -+ </option> -+ <option type="text" name="http_proxy"> -+ <_label>HTTP Proxy</_label> -+ <allow-empty>yes</allow-empty> -+ <_note-html>Sets the proxy server to use for HTTP</_note-html> -+ </option> -+ <option type="text" name="HTTPS_PROXY"> -+ <_label>HTTPS Proxy</_label> -+ <allow-empty>yes</allow-empty> -+ <_note-html>Sets the proxy server to use for HTTPS</_note-html> -+ </option> -+ <option type="bool" name="Mantisbt_CreatePrivate"> -+ <_label>Restrict access</_label> -+ <_note-html>Restrict access to the created CentOS Bug Tracker issue allowing only users from specified groups to view it (see advanced settings for more details)</_note-html> -+ <default-value>no</default-value> -+ </option> -+ </advanced-options> -+ </options> -+</event> -diff --git a/src/plugins/reporter-mantisbt.c b/src/plugins/reporter-mantisbt.c -new file mode 100644 -index 0000000..d281cdb ---- /dev/null -+++ b/src/plugins/reporter-mantisbt.c -@@ -0,0 +1,696 @@ -+/* -+ Copyright (C) 2014 ABRT team -+ Copyright (C) 2014 RedHat Inc -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation; either version 2 of the License, or -+ (at your option) any later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License along -+ with this program; if not, write to the Free Software Foundation, Inc., 51 -+ Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -+*/ -+ -+#include "internal_libreport.h" -+#include "client.h" -+#include "mantisbt.h" -+#include "problem_report.h" -+ -+static void -+parse_osinfo_for_mantisbt(map_string_t *osinfo, char** project, char** version) -+{ -+ const char *name = get_map_string_item_or_NULL(osinfo, "CENTOS_MANTISBT_PROJECT"); -+ if (!name) -+ name = get_map_string_item_or_NULL(osinfo, OSINFO_NAME); -+ -+ const char *version_id = get_map_string_item_or_NULL(osinfo, "CENTOS_MANTISBT_PROJECT_VERSION"); -+ if (!version_id) -+ version_id = get_map_string_item_or_NULL(osinfo, OSINFO_VERSION_ID); -+ -+ if (name && version_id) -+ { -+ *project = xstrdup(name); -+ *version = xstrdup(version_id); -+ return; -+ } -+ -+ /* something bad happend */ -+ *project = NULL; -+ *version = NULL; -+} -+ -+static char * -+ask_mantisbt_login(const char *message) -+{ -+ char *login = ask(message); -+ if (login == NULL || login[0] == '\0') -+ { -+ set_xfunc_error_retval(EXIT_CANCEL_BY_USER); -+ error_msg_and_die(_("Can't continue without login")); -+ } -+ -+ return login; -+} -+ -+static char * -+ask_mantisbt_password(const char *message) -+{ -+ char *password = ask_password(message); -+ /* TODO: this should be fixed in ask_password() as other tools have the same problem */ -+ putchar('\n'); -+ if (password == NULL || password[0] == '\0') -+ { -+ set_xfunc_error_retval(EXIT_CANCEL_BY_USER); -+ error_msg_and_die(_("Can't continue without password")); -+ } -+ -+ return password; -+} -+ -+static void -+ask_mantisbt_credentials(mantisbt_settings_t *settings, const char *pre_message) -+{ -+ free(settings->m_login); -+ free(settings->m_password); -+ -+ char *question = xasprintf("%s %s", pre_message, _("Please enter your CentOS Bug Tracker login:")); -+ settings->m_login = ask_mantisbt_login(question); -+ free(question); -+ -+ question = xasprintf("%s %s '%s':", pre_message, _("Please enter the password for"), settings->m_login); -+ settings->m_password = ask_mantisbt_password(question); -+ free(question); -+ -+ return; -+} -+ -+static void -+verify_credentials(mantisbt_settings_t *settings) -+{ -+ if (settings->m_login[0] == '\0' || settings->m_password[0] == '\0') -+ ask_mantisbt_credentials(settings, _("Credentials are not provided by configuration.")); -+ -+ while (true) -+ { -+ soap_request_t *req = soap_request_new_for_method("mc_login"); -+ soap_request_add_credentials_parameter(req, settings); -+ -+ mantisbt_result_t *result = mantisbt_soap_call(settings, req); -+ soap_request_free(req); -+ -+ if (g_verbose > 2) -+ { -+ GList *ids = response_get_main_ids_list(result->mr_body); -+ if (ids != NULL) -+ log("%s", (char *)ids->data); -+ response_values_free(ids); -+ } -+ -+ int result_val = result->mr_http_resp_code; -+ mantisbt_result_free(result); -+ -+ if (result_val == 200) -+ return; -+ -+ ask_mantisbt_credentials(settings, _("Invalid password or login.")); -+ } -+} -+ -+static void -+set_settings(mantisbt_settings_t *m, map_string_t *settings, struct dump_dir *dd) -+{ -+ const char *environ; -+ -+ environ = getenv("Mantisbt_Login"); -+ m->m_login = xstrdup(environ ? environ : get_map_string_item_or_empty(settings, "Login")); -+ -+ environ = getenv("Mantisbt_Password"); -+ m->m_password = xstrdup(environ ? environ : get_map_string_item_or_empty(settings, "Password")); -+ -+ environ = getenv("Mantisbt_MantisbtURL"); -+ m->m_mantisbt_url = environ ? environ : get_map_string_item_or_empty(settings, "MantisbtURL"); -+ if (!m->m_mantisbt_url[0]) -+ m->m_mantisbt_url = "https://bugs.centos.org/"; -+ else -+ { -+ /* We don't want trailing '/': "https://host/dir/" -> "https://host/dir" */ -+ char *last_slash = strrchr(m->m_mantisbt_url, '/'); -+ if (last_slash && last_slash[1] == '\0') -+ *last_slash = '\0'; -+ } -+ m->m_mantisbt_soap_url = concat_path_file(m->m_mantisbt_url, "api/soap/mantisconnect.php"); -+ -+ environ = getenv("Mantisbt_Project"); -+ if (environ) -+ { -+ m->m_project = xstrdup(environ); -+ environ = getenv("Mantisbt_ProjectVersion"); -+ if (environ) -+ m->m_project_version = xstrdup(environ); -+ } -+ else -+ { -+ const char *option = get_map_string_item_or_NULL(settings, "Project"); -+ if (option) -+ m->m_project = xstrdup(option); -+ option = get_map_string_item_or_NULL(settings, "ProjectVersion"); -+ if (option) -+ m->m_project_version = xstrdup(option); -+ } -+ -+ if (!m->m_project || !*m->m_project) /* if not overridden or empty... */ -+ { -+ free(m->m_project); -+ free(m->m_project_version); -+ -+ if (dd != NULL) -+ { -+ map_string_t *osinfo = new_map_string(); -+ -+ char *os_info_data = dd_load_text(dd, FILENAME_OS_INFO); -+ parse_osinfo(os_info_data, osinfo); -+ free(os_info_data); -+ -+ parse_osinfo_for_mantisbt(osinfo, &m->m_project, &m->m_project_version); -+ free_map_string(osinfo); -+ } -+ } -+ -+ environ = getenv("Mantisbt_SSLVerify"); -+ m->m_ssl_verify = string_to_bool(environ ? environ : get_map_string_item_or_empty(settings, "SSLVerify")); -+ -+ environ = getenv("Mantisbt_DontMatchComponents"); -+ m->m_DontMatchComponents = environ ? environ : get_map_string_item_or_empty(settings, "DontMatchComponents"); -+ -+ environ = getenv(CREATE_PRIVATE_TICKET); -+ if (environ) -+ m->m_create_private = string_to_bool(environ); -+ -+ if (!m->m_create_private) -+ { -+ environ = getenv("Mantisbt_CreatePrivate"); -+ m->m_create_private = string_to_bool(environ ? environ : get_map_string_item_or_empty(settings, "CreatePrivate")); -+ } -+ log_notice("create private CentOS Bug Tracker ticket: '%s'", m->m_create_private ? "YES": "NO"); -+} -+ -+int main(int argc, char **argv) -+{ -+ abrt_init(argv); -+ -+ /* I18n */ -+ setlocale(LC_ALL, ""); -+#if ENABLE_NLS -+ bindtextdomain(PACKAGE, LOCALEDIR); -+ textdomain(PACKAGE); -+#endif -+ -+ const char *program_usage_string = _( -+ "\n& [-vf] [-c CONFFILE]... [-F FMTFILE] [-A FMTFILE2] -d DIR" -+ "\nor:" -+ "\n& [-v] [-c CONFFILE]... [-d DIR] -t[ID] FILE..." -+ "\nor:" -+ "\n& [-v] [-c CONFFILE]... [-d DIR] -t[ID] -w" -+ "\nor:" -+ "\n& [-v] [-c CONFFILE]... -h DUPHASH" -+ "\n" -+ "\nReports problem to CentOS Bug Tracker." -+ "\n" -+ "\nThe tool reads DIR. Then it tries to find an issue" -+ "\nwith the same abrt_hash in custom field 'abrt_hash'." -+ "\n" -+ "\nIf such issue is not found, then a new issue is created. Elements of DIR" -+ "\nare stored in the issue as part of issue description or as attachments," -+ "\ndepending on their type and size." -+ "\n" -+ "\nOtherwise, if such issue is found and it is marked as CLOSED DUPLICATE," -+ "\nthe tool follows the chain of duplicates until it finds a non-DUPLICATE issue." -+ "\nThe tool adds a new comment to found issue." -+ "\n" -+ "\nThe URL to new or modified issue is printed to stdout and recorded in" -+ "\n'reported_to' element." -+ "\n" -+ "\nOption -t uploads FILEs to the already created issue on CentOS Bug Tracker site." -+ "\nThe issue ID is retrieved from directory specified by -d DIR." -+ "\nIf problem data in DIR was never reported to CentOS Bug Tracker, upload will fail." -+ "\n" -+ "\nOption -tID uploads FILEs to the issue with specified ID on CentOS Bug Tracker site." -+ "\n-d DIR is ignored." -+ "\n" -+ "\nOption -r sets the last url from reporter_to element which is prefixed with" -+ "\nTRACKER_NAME to URL field. This option is applied only when a new issue is to be" -+ "\nfiled. The default value is 'ABRT Server'" -+ "\n" -+ "\nIf not specified, CONFFILE defaults to "CONF_DIR"/plugins/mantisb.conf" -+ "\nIts lines should have 'PARAM = VALUE' format." -+ "\nRecognized string parameters: MantisbtURL, Login, Password, Project, ProjectVersion." -+ "\nRecognized boolean parameter (VALUE should be 1/0, yes/no): SSLVerify, CreatePrivate." -+ "\nParameters can be overridden via $Mantisbt_PARAM environment variables." -+ "\n" -+ "\nFMTFILE and FMTFILE2 default to "CONF_DIR"/plugins/mantisbt_format.conf" -+ ); -+ -+ enum { -+ OPT_v = 1 << 0, -+ OPT_d = 1 << 1, -+ OPT_c = 1 << 2, -+ OPT_F = 1 << 3, -+ OPT_A = 1 << 4, -+ OPT_t = 1 << 5, -+ OPT_f = 1 << 6, -+ OPT_h = 1 << 7, -+ OPT_r = 1 << 8, -+ OPT_D = 1 << 9, -+ }; -+ -+ const char *dump_dir_name = "."; -+ GList *conf_file = NULL; -+ const char *fmt_file = CONF_DIR"/plugins/mantisbt_format.conf"; -+ const char *fmt_file2 = fmt_file; -+ char *abrt_hash = NULL; -+ char *ticket_no = NULL; -+ const char *tracker_str = "ABRT Server"; -+ char *debug_str = NULL; -+ mantisbt_settings_t mbt_settings = { 0 }; -+ /* Keep enum above and order of options below in sync! */ -+ struct options program_options[] = { -+ OPT__VERBOSE(&g_verbose), -+ OPT_STRING( 'd', NULL, &dump_dir_name , "DIR" , _("Problem directory")), -+ OPT_LIST( 'c', NULL, &conf_file , "FILE" , _("Configuration file (may be given many times)")), -+ OPT_STRING( 'F', NULL, &fmt_file , "FILE" , _("Formatting file for initial comment")), -+ OPT_STRING( 'A', NULL, &fmt_file2 , "FILE" , _("Formatting file for duplicates")), -+ OPT_OPTSTRING('t', "ticket", &ticket_no , "ID" , _("Attach FILEs [to issue with this ID]")), -+ OPT_BOOL( 'f', NULL, NULL, _("Force reporting even if this problem is already reported")), -+ OPT_STRING( 'h', "duphash", &abrt_hash, "DUPHASH", _("Print BUG_ID which has given DUPHASH")), -+ OPT_STRING( 'r', "tracker", &tracker_str, "TRACKER_NAME", _("A name of bug tracker for an additional URL from 'reported_to'")), -+ -+ OPT_OPTSTRING('D', "debug", &debug_str , "STR" , _("Debug")), -+ OPT_END() -+ }; -+ -+ unsigned opts = parse_opts(argc, argv, program_options, program_usage_string); -+ argv += optind; -+ -+ export_abrt_envvars(0); -+ -+ map_string_t *settings = new_map_string(); -+ -+ { -+ if (!conf_file) -+ conf_file = g_list_append(conf_file, (char*) CONF_DIR"/plugins/mantisbt.conf"); -+ while (conf_file) -+ { -+ char *fn = (char *)conf_file->data; -+ log_notice("Loading settings from '%s'", fn); -+ load_conf_file(fn, settings, /*skip key w/o values:*/ false); -+ log_debug("Loaded '%s'", fn); -+ conf_file = g_list_delete_link(conf_file, conf_file); -+ } -+ -+ struct dump_dir *dd = NULL; -+ if (abrt_hash == NULL) -+ { -+ dd = dd_opendir(dump_dir_name, /*flags:*/ 0); -+ if (!dd) -+ error_msg_and_die(_("Can't open problem dir '%s'."), dump_dir_name); -+ } -+ -+ set_settings(&mbt_settings, settings, dd); -+ dd_close(dd); -+ /* WRONG! set_settings() does not copy the strings, it merely sets up pointers -+ * to settings[] dictionary: -+ */ -+ /*free_map_string(settings);*/ -+ } -+ -+ /* No connection is opened between client and server. Users authentication -+ * is performed on every SOAP method call. In the first step we verify the -+ * credentials by calling 'mc_login' method. In the case the credentials are -+ * correctly applies the reporter uses them in the next requests. It is not -+ * necessary to call 'mc_login' method because the method provides only -+ * verification of credentials. -+ */ -+ verify_credentials(&mbt_settings); -+ -+ if (abrt_hash) -+ { -+ log(_("Looking for similar problems in CentOS Bug Tracker")); -+ GList *ids = mantisbt_search_by_abrt_hash(&mbt_settings, abrt_hash); -+ mantisbt_settings_free(&mbt_settings); -+ -+ if (ids == NULL) -+ return EXIT_FAILURE; -+ -+ puts(ids->data); -+ response_values_free(ids); -+ return EXIT_SUCCESS; -+ } -+ -+ mantisbt_get_project_id_from_name(&mbt_settings); -+ -+ if (opts & OPT_t) -+ { -+ if (!argv[0]) -+ show_usage_and_die(program_usage_string, program_options); -+ -+ if (!ticket_no) -+ { -+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); -+ if (!dd) -+ xfunc_die(); -+ report_result_t *reported_to = find_in_reported_to(dd, "CentOS Bug Tracker"); -+ dd_close(dd); -+ -+ if (!reported_to || !reported_to->url) -+ error_msg_and_die(_("Can't get MantisBT ID because this problem has not yet been reported to MantisBT.")); -+ -+ char *url = reported_to->url; -+ reported_to->url = NULL; -+ free_report_result(reported_to); -+ -+ if (prefixcmp(url, mbt_settings.m_mantisbt_url) != 0) -+ error_msg_and_die(_("This problem has been reported to MantisBT '%s' which differs from the configured MantisBT '%s'."), url, mbt_settings.m_mantisbt_url); -+ -+ ticket_no = strrchr(url, '='); -+ if (!ticket_no) -+ error_msg_and_die(_("Malformed url to MantisBT '%s'."), url); -+ -+ /* won't ever call free on it - it simplifies the code a lot */ -+ ticket_no = xstrdup(ticket_no + 1); -+ log(_("Using CentOS Bug Tracker ID '%s'"), ticket_no); -+ } -+ -+ /* Attach files to existing MantisBT issues */ -+ while (*argv) -+ { -+ const char *path = *argv++; -+ char *filename = basename(path); -+ log(_("Attaching file '%s' to issue %s"), filename, ticket_no); -+ mantisbt_attach_file(&mbt_settings, ticket_no, filename, path); -+ } -+ -+ return 0; -+ } -+ -+ /* Create new issue in MantisBT */ -+ -+ if (!(opts & OPT_f)) -+ { -+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); -+ if (!dd) -+ xfunc_die(); -+ report_result_t *reported_to = find_in_reported_to(dd, "CentOS Bug Tracker"); -+ dd_close(dd); -+ -+ if (reported_to && reported_to->url) -+ { -+ char *msg = xasprintf(_("This problem was already reported to CentOS Bug Tracker (see '%s')." -+ " Do you still want to create a new issue?"), -+ reported_to->url); -+ int yes = ask_yes_no(msg); -+ free(msg); -+ if (!yes) -+ return 0; -+ } -+ free_report_result(reported_to); -+ } -+ -+ problem_data_t *problem_data = create_problem_data_for_reporting(dump_dir_name); -+ if (!problem_data) -+ xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */ -+ -+ const char *category = problem_data_get_content_or_die(problem_data, FILENAME_COMPONENT); -+ const char *duphash = problem_data_get_content_or_die(problem_data, FILENAME_DUPHASH); -+ -+ if (opts & OPT_D) -+ { -+ problem_formatter_t *pf = problem_formatter_new(); -+ problem_formatter_add_section(pf, PR_SEC_ADDITIONAL_INFO, /* optional section */ 0); -+ -+ if (problem_formatter_load_file(pf, fmt_file)) -+ error_msg_and_die("Invalid format file: %s", fmt_file); -+ -+ problem_report_t *pr = NULL; -+ if (problem_formatter_generate_report(pf, problem_data, &pr)) -+ error_msg_and_die("Failed to format issue report from problem data"); -+ -+ printf("summary: %s\n" -+ "\n" -+ "Description:\n%s\n" -+ "Additional info:\n%s\n" -+ , problem_report_get_summary(pr) -+ , problem_report_get_description(pr) -+ , problem_report_get_section(pr, PR_SEC_ADDITIONAL_INFO) -+ ); -+ -+ puts("attachments:"); -+ for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a)) -+ printf(" %s\n", (const char *)a->data); -+ -+ problem_report_free(pr); -+ problem_formatter_free(pf); -+ exit(0); -+ } -+ -+ int bug_id = 0; -+ -+ /* If REMOTE_RESULT contains "DUPLICATE 12345", we consider it a dup of 12345 -+ * and won't search on MantisBT server. -+ */ -+ char *remote_result; -+ remote_result = problem_data_get_content_or_NULL(problem_data, FILENAME_REMOTE_RESULT); -+ if (remote_result) -+ { -+ char *cmd = strtok(remote_result, " \n"); -+ char *id = strtok(NULL, " \n"); -+ -+ if (!prefixcmp(cmd, "DUPLICATE")) -+ { -+ errno = 0; -+ char *e; -+ bug_id = strtoul(id, &e, 10); -+ if (errno || id == e || *e != '\0' || bug_id > INT_MAX) -+ { -+ /* error / no digits / illegal trailing chars / too big a number */ -+ bug_id = 0; -+ } -+ } -+ } -+ -+ mantisbt_issue_info_t *ii; -+ if (!bug_id) -+ { -+ log(_("Checking for duplicates")); -+ -+ int existing_id = -1; -+ int crossver_id = -1; -+ { -+ /* Figure out whether we want to match category -+ * when doing dup search. -+ */ -+ const char *category_substitute = is_in_comma_separated_list(category, mbt_settings.m_DontMatchComponents) ? NULL : category; -+ -+ /* We don't do dup detection across versions (see below why), -+ * but we do add a note if cross-version potential dup exists. -+ * For that, we search for cross version dups first: -+ */ -+ // SOAP API searching method is not in the final version, it's possible the project will be string -+ GList *crossver_bugs_ids = mantisbt_search_duplicate_issues(&mbt_settings, category_substitute, /*version*/ NULL, duphash); -+ -+ unsigned crossver_bugs_count = g_list_length(crossver_bugs_ids); -+ log_debug("CentOS Bug Tracker has %i reports with duphash '%s' including cross-version ones", -+ crossver_bugs_count, duphash); -+ if (crossver_bugs_count > 0) -+ crossver_id = atoi(g_list_first(crossver_bugs_ids)->data); -+ -+ if (crossver_bugs_count > 0) -+ { -+ // SOAP API searching method is not in the final version, it's possible the project will be string -+ GList *dup_bugs_ids = mantisbt_search_duplicate_issues(&mbt_settings, category_substitute, mbt_settings.m_project_version, duphash); -+ -+ unsigned dup_bugs_count = g_list_length(dup_bugs_ids); -+ log_debug("CentOS Bug Tracker has %i reports with duphash '%s'", -+ dup_bugs_count, duphash); -+ if (dup_bugs_count > 0) -+ existing_id = atoi(g_list_first(dup_bugs_ids)->data); -+ } -+ } -+ -+ if (existing_id < 0) -+ { -+ /* Create new issue */ -+ log(_("Creating a new issue")); -+ problem_formatter_t *pf = problem_formatter_new(); -+ problem_formatter_add_section(pf, PR_SEC_ADDITIONAL_INFO, 0); -+ -+ if (problem_formatter_load_file(pf, fmt_file)) -+ error_msg_and_die(_("Invalid format file: %s"), fmt_file); -+ -+ problem_report_t *pr = NULL; -+ if (problem_formatter_generate_report(pf, problem_data, &pr)) -+ error_msg_and_die(_("Failed to format problem data")); -+ -+ if (crossver_id >= 0) -+ problem_report_buffer_printf( -+ problem_report_get_buffer(pr, PR_SEC_DESCRIPTION), -+ "\nPotential duplicate: issue %u\n", crossver_id); -+ -+ problem_formatter_free(pf); -+ -+ /* get tracker URL if exists */ -+ struct dump_dir *dd = dd_opendir(dump_dir_name, 0); -+ char *tracker_url = NULL; -+ if (dd) -+ { -+ report_result_t *reported_to = find_in_reported_to(dd, tracker_str); -+ dd_close(dd); -+ -+ if (reported_to && reported_to->url) -+ { -+ log(_("Adding External URL to issue")); -+ tracker_url = xstrdup(reported_to->url); -+ free_report_result(reported_to); -+ } -+ } -+ -+ int new_id = mantisbt_create_new_issue(&mbt_settings, problem_data, pr, tracker_url); -+ -+ free(tracker_url); -+ -+ if (new_id == -1) -+ return EXIT_FAILURE; -+ -+ log(_("Adding attachments to issue %i"), new_id); -+ char *new_id_str = xasprintf("%u", new_id); -+ -+ for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a)) -+ { -+ const char *item_name = (const char *)a->data; -+ struct problem_item *item = problem_data_get_item_or_NULL(problem_data, item_name); -+ if (!item) -+ continue; -+ else if (item->flags & CD_FLAG_TXT) -+ mantisbt_attach_data(&mbt_settings, new_id_str, item_name, item->content, strlen(item->content)); -+ else if (item->flags & CD_FLAG_BIN) -+ mantisbt_attach_file(&mbt_settings, new_id_str, item_name, item->content); -+ } -+ -+ free(new_id_str); -+ problem_report_free(pr); -+ ii = mantisbt_issue_info_new(); -+ ii->mii_id = new_id; -+ ii->mii_status = xstrdup("new"); -+ -+ goto finish; -+ } -+ -+ bug_id = existing_id; -+ } -+ -+ ii = mantisbt_get_issue_info(&mbt_settings, bug_id); -+ -+ log(_("Bug is already reported: %i"), ii->mii_id); -+ -+ /* Follow duplicates */ -+ if ((strcmp(ii->mii_status, "closed") == 0) -+ && (strcmp(ii->mii_resolution, "duplicate") == 0) -+ ) { -+ mantisbt_issue_info_t *origin = mantisbt_find_origin_bug_closed_duplicate(&mbt_settings, ii); -+ if (origin) -+ { -+ mantisbt_issue_info_free(ii); -+ ii = origin; -+ } -+ } -+ -+ /* TODO CC list -+ * Is no MantisBT SOAP API method which allows adding users to CC list -+ * without updating issue. -+ */ -+ -+ /* Add comment and bt */ -+ const char *comment = problem_data_get_content_or_NULL(problem_data, FILENAME_COMMENT); -+ if (comment && comment[0]) -+ { -+ problem_formatter_t *pf = problem_formatter_new(); -+ -+ if (problem_formatter_load_file(pf, fmt_file2)) -+ error_msg_and_die(_("Invalid duplicate format file: '%s"), fmt_file2); -+ -+ problem_report_t *pr; -+ if (problem_formatter_generate_report(pf, problem_data, &pr)) -+ error_msg_and_die(_("Failed to format duplicate comment from problem data")); -+ -+ const char *mbtcomment = problem_report_get_description(pr); -+ -+ int dup_comment = is_comment_dup(ii->mii_notes, mbtcomment); -+ if (!dup_comment) -+ { -+ log(_("Adding new comment to issue %d"), ii->mii_id); -+ mantisbt_add_issue_note(&mbt_settings, ii->mii_id, mbtcomment); -+ -+ const char *bt = problem_data_get_content_or_NULL(problem_data, FILENAME_BACKTRACE); -+ unsigned rating = 0; -+ const char *rating_str = problem_data_get_content_or_NULL(problem_data, FILENAME_RATING); -+ /* python doesn't have rating file */ -+ if (rating_str) -+ rating = xatou(rating_str); -+ if (bt && rating > ii->mii_best_bt_rating) -+ { -+ char *bug_id_str = xasprintf("%i", ii->mii_id); -+ -+ log(_("Attaching better backtrace")); -+ -+ // find unique filename of attachment -+ char *name = NULL; -+ for (int i = 0;; ++i) -+ { -+ if (i == 0) -+ name = xasprintf("%s", FILENAME_BACKTRACE); -+ else -+ name = xasprintf("%s%d", FILENAME_BACKTRACE, i); -+ -+ if (g_list_find_custom(ii->mii_attachments, name, (GCompareFunc) strcmp) == NULL) -+ break; -+ -+ free(name); -+ } -+ mantisbt_attach_data(&mbt_settings, bug_id_str, name, bt, strlen(bt)); -+ -+ free(name); -+ free(bug_id_str); -+ } -+ } -+ else -+ log(_("Found the same comment in the issue history, not adding a new one")); -+ -+ problem_report_free(pr); -+ problem_formatter_free(pf); -+ } -+ -+finish: -+ log(_("Status: %s%s%s %s/view.php?id=%u"), -+ ii->mii_status, -+ ii->mii_resolution ? " " : "", -+ ii->mii_resolution ? ii->mii_resolution : "", -+ mbt_settings.m_mantisbt_url, -+ ii->mii_id); -+ -+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); -+ if (dd) -+ { -+ char *msg = xasprintf("CentOS Bug Tracker: URL=%s/view.php?id=%u", mbt_settings.m_mantisbt_url, ii->mii_id); -+ add_reported_to(dd, msg); -+ free(msg); -+ dd_close(dd); -+ } -+ -+ mantisbt_settings_free(&mbt_settings); -+ return 0; -+} -diff --git a/src/workflows/Makefile.am b/src/workflows/Makefile.am -index 0fc1019..5b8376a 100644 ---- a/src/workflows/Makefile.am -+++ b/src/workflows/Makefile.am -@@ -22,6 +22,18 @@ dist_workflows_DATA = \ - workflow_Logger.xml \ - workflow_LoggerCCpp.xml - -+if BUILD_MANTISBT -+dist_workflows_DATA += \ -+ workflow_CentOSCCpp.xml \ -+ workflow_CentOSKerneloops.xml \ -+ workflow_CentOSPython.xml \ -+ workflow_CentOSPython3.xml \ -+ workflow_CentOSVmcore.xml \ -+ workflow_CentOSXorg.xml \ -+ workflow_CentOSLibreport.xml \ -+ workflow_CentOSJava.xml -+endif -+ - if BUILD_BUGZILLA - dist_workflows_DATA += \ - workflow_AnacondaFedora.xml \ -@@ -44,7 +56,8 @@ dist_workflowsdef_DATA =\ - report_rhel.conf \ - report_mailx.conf \ - report_logger.conf \ -- report_uploader.conf -+ report_uploader.conf \ -+ report_centos.conf - - if BUILD_BUGZILLA - dist_workflowsdef_DATA += \ -diff --git a/src/workflows/report_centos.conf b/src/workflows/report_centos.conf -new file mode 100644 -index 0000000..07b0d40 ---- /dev/null -+++ b/src/workflows/report_centos.conf -@@ -0,0 +1,31 @@ -+EVENT=workflow_CentOSLibreport analyzer=libreport -+# this is just a meta event which consists of other events -+# the list is defined in the xml file -+ -+EVENT=workflow_CentOSCCpp analyzer=CCpp -+# this is just a meta event which consists of other events -+# the list is defined in the xml file -+ -+EVENT=workflow_CentOSPython analyzer=Python component!=anaconda -+# this is just a meta event which consists of other events -+# the list is defined in the xml file -+ -+EVENT=workflow_CentOSPython3 analyzer=Python3 component!=anaconda -+# this is just a meta event which consists of other events -+# the list is defined in the xml file -+ -+EVENT=workflow_CentOSKerneloops analyzer=Kerneloops -+# this is just a meta event which consists of other events -+# the list is defined in the xml file -+ -+EVENT=workflow_CentOSVmcore analyzer=vmcore -+# this is just a meta event which consists of other events -+# the list is defined in the xml file -+ -+EVENT=workflow_CentOSXorg analyzer=xorg -+# this is just a meta event which consists of other events -+# the list is defined in the xml file -+ -+EVENT=workflow_CentOSJava analyzer=Java -+# this is just a meta event which consists of other events -+# the list is defined in the xml file -diff --git a/src/workflows/workflow_CentOSCCpp.xml.in b/src/workflows/workflow_CentOSCCpp.xml.in -new file mode 100644 -index 0000000..ea94d56 ---- /dev/null -+++ b/src/workflows/workflow_CentOSCCpp.xml.in -@@ -0,0 +1,12 @@ -+<?xml version="1.0" encoding="UTF-8" ?> -+<workflow> -+ <_name>Report to CentOS Bug Tracker</_name> -+ <_description>Process the C/C++ crash using the CentOS infrastructure</_description> -+ -+ <events> -+ <event>report_uReport</event> -+ <event>collect_*</event> -+ <event>analyze_CCpp</event> -+ <event>report_CentOSBugTracker</event> -+ </events> -+</workflow> -diff --git a/src/workflows/workflow_CentOSJava.xml.in b/src/workflows/workflow_CentOSJava.xml.in -new file mode 100644 -index 0000000..89f9295 ---- /dev/null -+++ b/src/workflows/workflow_CentOSJava.xml.in -@@ -0,0 +1,11 @@ -+<?xml version="1.0" encoding="UTF-8" ?> -+<workflow> -+ <_name>Report to CentOS Bug Tracker</_name> -+ <_description>Process the Java exception using the CentOS infrastructure</_description> -+ -+ <events> -+ <event>report_uReport</event> -+ <event>collect_*</event> -+ <event>report_CentOSBugTracker</event> -+ </events> -+</workflow> -diff --git a/src/workflows/workflow_CentOSKerneloops.xml.in b/src/workflows/workflow_CentOSKerneloops.xml.in -new file mode 100644 -index 0000000..c43c681 ---- /dev/null -+++ b/src/workflows/workflow_CentOSKerneloops.xml.in -@@ -0,0 +1,11 @@ -+<?xml version="1.0" encoding="UTF-8" ?> -+<workflow> -+ <_name>Report to CentOS Bug Tracker</_name> -+ <_description>Process the kerneloops using the CentOS infrastructure</_description> -+ -+ <events> -+ <event>report_uReport</event> -+ <event>collect_*</event> -+ <event>report_CentOSBugTracker</event> -+ </events> -+</workflow> -diff --git a/src/workflows/workflow_CentOSLibreport.xml.in b/src/workflows/workflow_CentOSLibreport.xml.in -new file mode 100644 -index 0000000..2f6ed82 ---- /dev/null -+++ b/src/workflows/workflow_CentOSLibreport.xml.in -@@ -0,0 +1,9 @@ -+<?xml version="1.0" encoding="UTF-8" ?> -+<workflow> -+ <_name>Report to CentOS Bug Tracker</_name> -+ <_description>Process the problem using the CentOS infrastructure</_description> -+ -+ <events> -+ <event>report_CentOSBugTracker</event> -+ </events> -+</workflow> -diff --git a/src/workflows/workflow_CentOSPython.xml.in b/src/workflows/workflow_CentOSPython.xml.in -new file mode 100644 -index 0000000..7e26c6c ---- /dev/null -+++ b/src/workflows/workflow_CentOSPython.xml.in -@@ -0,0 +1,11 @@ -+<?xml version="1.0" encoding="UTF-8" ?> -+<workflow> -+ <_name>Report to CentOS Bug Tracker</_name> -+ <_description>Process the python exception using the CentOS infrastructure</_description> -+ -+ <events> -+ <event>report_uReport</event> -+ <event>collect_*</event> -+ <event>report_CentOSBugTracker</event> -+ </events> -+</workflow> -diff --git a/src/workflows/workflow_CentOSPython3.xml.in b/src/workflows/workflow_CentOSPython3.xml.in -new file mode 100644 -index 0000000..f1dd8a9 ---- /dev/null -+++ b/src/workflows/workflow_CentOSPython3.xml.in -@@ -0,0 +1,11 @@ -+<?xml version="1.0" encoding="UTF-8" ?> -+<workflow> -+ <_name>Report to CentOS Bug Tracker</_name> -+ <_description>Process the python 3 exception using the CentOS infrastructure</_description> -+ -+ <events> -+ <event>report_uReport</event> -+ <event>collect_*</event> -+ <event>report_CentOSBugTracker</event> -+ </events> -+</workflow> -diff --git a/src/workflows/workflow_CentOSVmcore.xml.in b/src/workflows/workflow_CentOSVmcore.xml.in -new file mode 100644 -index 0000000..c876594 ---- /dev/null -+++ b/src/workflows/workflow_CentOSVmcore.xml.in -@@ -0,0 +1,12 @@ -+<?xml version="1.0" encoding="UTF-8" ?> -+<workflow> -+ <_name>Report to CentOS Bug Tracker</_name> -+ <_description>Process the kernel crash using the CentOS infrastructure</_description> -+ -+ <events> -+ <event>analyze_VMcore</event> -+ <event>report_uReport</event> -+ <event>collect_*</event> -+ <event>report_CentOSBugTracker</event> -+ </events> -+</workflow> -diff --git a/src/workflows/workflow_CentOSXorg.xml.in b/src/workflows/workflow_CentOSXorg.xml.in -new file mode 100644 -index 0000000..bf52221 ---- /dev/null -+++ b/src/workflows/workflow_CentOSXorg.xml.in -@@ -0,0 +1,9 @@ -+<?xml version="1.0" encoding="UTF-8" ?> -+<workflow> -+ <_name>Report to CentOS Bug Tracker</_name> -+ <_description>Process the X Server problem using the CentOS infrastructure</_description> -+ -+ <events> -+ <event>report_CentOSBugTracker</event> -+ </events> -+</workflow> --- -1.8.3.1 - diff --git a/SOURCES/ureport-set-url-to-public-faf-server.patch b/SOURCES/ureport-set-url-to-public-faf-server.patch deleted file mode 100644 index 953c364..0000000 --- a/SOURCES/ureport-set-url-to-public-faf-server.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 212cc1961c62d9c4e39ae3a13b2fe5525ad78744 Mon Sep 17 00:00:00 2001 -From: Matej Habrnal <mhabrnal@redhat.com> -Date: Mon, 2 Feb 2015 16:31:51 +0100 -Subject: [PATCH] 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 e04bf56..4d64ee7 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/SPECS/libreport.spec b/SPECS/libreport.spec index 121e285..0a40e28 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: 30%{?dist} +Release: 31%{?dist} License: GPLv2+ Group: System Environment/Libraries URL: https://fedorahosted.org/abrt/ @@ -180,17 +180,9 @@ Patch156: 0156-ureport-improve-curl-s-error-messages.patch # git format-patch 2.1.11-29.el7 -N --start-number 157 --topo-order Patch157: 0157-testsuite-ureport-initialize-post_state.patch Patch158: 0158-testsuite-ureport-use-less-strange-testing-error-mes.patch -Patch159: libreport-centos-branding-changes.patch -Patch160: lib-add-Problem-Format-API.patch -Patch161: bugzilla-port-to-Problem-Format-API.patch -Patch162: lib-created-a-new-lib-file-for-reporters.patch -Patch163: ureport-set-url-to-public-faf-server.patch -Patch164: conf-changed-URL-for-sending-uReport.patch -Patch165: reporter-mantisbt-first-version-of-the-reporter-mant.patch -Patch166: reporter-mantisbt-change-default-formating-file-for-.patch -Patch167: reporter-mantisbt-adds-man-pages-for-reporter-mantis.patch -Patch168: move-problem_report-to-plugins.patch -Patch169: reporter-mantisbt-add-event-for-reporting-AVCs.patch +# git format-patch 2.1.11-30.el7 -N --start-number 159 --topo-order +Patch159: 0159-wizard-fix-save-users-changes-after-reviewing-dump-d.patch +# git format-patch 2.1.11-31.el7 -N --start-number 160 --topo-order # git is need for '%%autosetup -S git' which automatically applies all the # patches above. Please, be aware that the patches must be generated @@ -380,25 +372,6 @@ 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 @@ -640,7 +613,6 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : %{_includedir}/libreport/config_item_info.h %{_includedir}/libreport/workflow.h %{_includedir}/libreport/ureport.h -%{_includedir}/libreport/reporters.h # Private api headers: %{_includedir}/libreport/internal_abrt_dbus.h %{_includedir}/libreport/internal_libreport.h @@ -760,40 +732,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 @@ -887,10 +825,10 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : %changelog -* Thu Nov 19 2015 CentOS Sources <bugs@centos.org> - 2.1.11-30.el7.centos -- Roll in patch to remove RH support for libreport -- mantisbt plugin -- Resolves bugs.centos#8422 +* Thu Oct 29 2015 Jakub Filak <jfilak@redhat.com> - 2.1.11-31 +- save all files changed by the reporter in the reporting GUI +- Fixes CVE-2015-5302 +- Related: #1266853 * Thu Sep 17 2015 Richard Marko <rmarko@redhat.com> - 2.1.11-30 - ureport test fix