diff --git a/README.debrand b/README.debrand
deleted file mode 100644
index 01c46d2..0000000
--- a/README.debrand
+++ /dev/null
@@ -1,2 +0,0 @@
-Warning: This package was configured for automatic debranding, but the changes
-failed to apply.
diff --git a/SOURCES/1000-bugzilla-port-to-Problem-Format-API.patch b/SOURCES/1000-bugzilla-port-to-Problem-Format-API.patch
new file mode 100644
index 0000000..5ab0a57
--- /dev/null
+++ b/SOURCES/1000-bugzilla-port-to-Problem-Format-API.patch
@@ -0,0 +1,781 @@
+From ff32b9ee7a7e396e33f1e9aeaa5bafd26ccbb273 Mon Sep 17 00:00:00 2001
+From: Jakub Filak <jfilak@redhat.com>
+Date: Thu, 4 Dec 2014 08:45:07 +0100
+Subject: [PATCH 1000/1015] bugzilla: port to Problem Format API
+
+Related to #303
+
+Signed-off-by: Jakub Filak <jfilak@redhat.com>
+---
+ src/plugins/reporter-bugzilla.c | 691 ++++------------------------------------
+ 1 file changed, 59 insertions(+), 632 deletions(-)
+
+diff --git a/src/plugins/reporter-bugzilla.c b/src/plugins/reporter-bugzilla.c
+index fbe7873..9ff3df3 100644
+--- a/src/plugins/reporter-bugzilla.c
++++ b/src/plugins/reporter-bugzilla.c
+@@ -17,515 +17,11 @@
+     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+ #include "internal_libreport.h"
++#include "problem_report.h"
+ #include "client.h"
+ #include "abrt_xmlrpc.h"
+ #include "rhbz.h"
+ 
+-#include <satyr/stacktrace.h>
+-#include <satyr/abrt.h>
+-
+-struct section_t {
+-    char *name;
+-    GList *items;
+-};
+-typedef struct section_t section_t;
+-
+-
+-/* Utility functions */
+-
+-static
+-GList* split_string_on_char(const char *str, char ch)
+-{
+-    GList *list = NULL;
+-    for (;;)
+-    {
+-        const char *delim = strchrnul(str, ch);
+-        list = g_list_prepend(list, xstrndup(str, delim - str));
+-        if (*delim == '\0')
+-            break;
+-        str = delim + 1;
+-    }
+-    return g_list_reverse(list);
+-}
+-
+-static
+-int compare_item_name(const char *lookup, const char *name)
+-{
+-    if (lookup[0] == '-')
+-        lookup++;
+-    else if (strncmp(lookup, "%bare_", 6) == 0)
+-        lookup += 6;
+-    return strcmp(lookup, name);
+-}
+-
+-static
+-int is_item_name_in_section(const section_t *lookup, const char *name)
+-{
+-    if (g_list_find_custom(lookup->items, name, (GCompareFunc)compare_item_name))
+-        return 0; /* "found it!" */
+-    return 1;
+-}
+-
+-static
+-bool is_explicit_or_forbidden(const char *name, GList *comment_fmt_spec)
+-{
+-    return g_list_find_custom(comment_fmt_spec, name, (GCompareFunc)is_item_name_in_section);
+-}
+-
+-static
+-GList* load_bzrep_conf_file(const char *path)
+-{
+-    FILE *fp = stdin;
+-    if (strcmp(path, "-") != 0)
+-    {
+-        fp = fopen(path, "r");
+-        if (!fp)
+-            return NULL;
+-    }
+-
+-    GList *sections = NULL;
+-
+-    char *line;
+-    while ((line = xmalloc_fgetline(fp)) != NULL)
+-    {
+-        /* Skip comments */
+-        char first = *skip_whitespace(line);
+-        if (first == '#')
+-            goto free_line;
+-
+-        /* Handle trailing backslash continuation */
+- check_continuation: ;
+-        unsigned len = strlen(line);
+-        if (len && line[len-1] == '\\')
+-        {
+-            line[len-1] = '\0';
+-            char *next_line = xmalloc_fgetline(fp);
+-            if (next_line)
+-            {
+-                line = append_to_malloced_string(line, next_line);
+-                free(next_line);
+-                goto check_continuation;
+-            }
+-        }
+-
+-        /* We are reusing line buffer to form temporary
+-         * "key\0values\0..." in its beginning
+-         */
+-        bool summary_line = false;
+-        char *value = NULL;
+-        char *src;
+-        char *dst;
+-        for (src = dst = line; *src; src++)
+-        {
+-            char c = *src;
+-            /* did we reach the value list? */
+-            if (!value && c == ':' && src[1] == ':')
+-            {
+-                *dst++ = '\0'; /* terminate key */
+-                src += 2;
+-                value = dst; /* remember where value starts */
+-                summary_line = (strcmp(line, "%summary") == 0);
+-                if (summary_line)
+-                {
+-                    value = src;
+-                    break;
+-                }
+-                continue;
+-            }
+-            /* skip whitespace in value list */
+-            if (value && isspace(c))
+-                continue;
+-            *dst++ = c; /* store next key or value char */
+-        }
+-
+-        GList *item_list = NULL;
+-        if (summary_line)
+-        {
+-            /* %summary is special */
+-            item_list = g_list_append(NULL, xstrdup(skip_whitespace(value)));
+-        }
+-        else
+-        {
+-            *dst = '\0'; /* terminate value (or key) */
+-            if (value)
+-                item_list = split_string_on_char(value, ',');
+-        }
+-
+-        section_t *sec = xzalloc(sizeof(*sec));
+-        sec->name = xstrdup(line);
+-        sec->items = item_list;
+-        sections = g_list_prepend(sections, sec);
+-
+- free_line:
+-        free(line);
+-    }
+-
+-    if (fp != stdin)
+-        fclose(fp);
+-
+-    return g_list_reverse(sections);
+-}
+-
+-
+-/* Summary generation */
+-
+-#define MAX_OPT_DEPTH 10
+-static
+-char *format_percented_string(const char *str, problem_data_t *pd)
+-{
+-    size_t old_pos[MAX_OPT_DEPTH] = { 0 };
+-    int okay[MAX_OPT_DEPTH] = { 1 };
+-    int opt_depth = 1;
+-    struct strbuf *result = strbuf_new();
+-
+-    while (*str) {
+-        switch (*str) {
+-        default:
+-            strbuf_append_char(result, *str);
+-            str++;
+-            break;
+-        case '\\':
+-            if (str[1])
+-                str++;
+-            strbuf_append_char(result, *str);
+-            str++;
+-            break;
+-        case '[':
+-            if (str[1] == '[' && opt_depth < MAX_OPT_DEPTH)
+-            {
+-                old_pos[opt_depth] = result->len;
+-                okay[opt_depth] = 1;
+-                opt_depth++;
+-                str += 2;
+-            } else {
+-                strbuf_append_char(result, *str);
+-                str++;
+-            }
+-            break;
+-        case ']':
+-            if (str[1] == ']' && opt_depth > 1)
+-            {
+-                opt_depth--;
+-                if (!okay[opt_depth])
+-                {
+-                    result->len = old_pos[opt_depth];
+-                    result->buf[result->len] = '\0';
+-                }
+-                str += 2;
+-            } else {
+-                strbuf_append_char(result, *str);
+-                str++;
+-            }
+-            break;
+-        case '%': ;
+-            char *nextpercent = strchr(++str, '%');
+-            if (!nextpercent)
+-            {
+-                error_msg_and_die("Unterminated %%element%%: '%s'", str - 1);
+-            }
+-
+-            *nextpercent = '\0';
+-            const problem_item *item = problem_data_get_item_or_NULL(pd, str);
+-            *nextpercent = '%';
+-
+-            if (item && (item->flags & CD_FLAG_TXT))
+-                strbuf_append_str(result, item->content);
+-            else
+-                okay[opt_depth - 1] = 0;
+-            str = nextpercent + 1;
+-            break;
+-        }
+-    }
+-
+-    if (opt_depth > 1)
+-    {
+-        error_msg_and_die("Unbalanced [[ ]] bracket");
+-    }
+-
+-    if (!okay[0])
+-    {
+-        error_msg("Undefined variable outside of [[ ]] bracket");
+-    }
+-
+-    return strbuf_free_nobuf(result);
+-}
+-
+-static
+-char *create_summary_string(problem_data_t *pd, GList *comment_fmt_spec)
+-{
+-    GList *l = comment_fmt_spec;
+-    while (l)
+-    {
+-        section_t *sec = l->data;
+-        l = l->next;
+-
+-        /* Find %summary" */
+-        if (strcmp(sec->name, "%summary") != 0)
+-            continue;
+-
+-        GList *item = sec->items;
+-        if (!item)
+-            /* not supposed to happen, there will be at least "" */
+-            error_msg_and_die("BUG in %%summary parser");
+-
+-        const char *str = item->data;
+-        return format_percented_string(str, pd);
+-    }
+-
+-    return format_percented_string("%reason%", pd);
+-}
+-
+-
+-/* BZ comment generation */
+-
+-static
+-int append_text(struct strbuf *result, const char *item_name, const char *content, bool print_item_name)
+-{
+-    char *eol = strchrnul(content, '\n');
+-    if (eol[0] == '\0' || eol[1] == '\0')
+-    {
+-        /* one-liner */
+-        int pad = 16 - (strlen(item_name) + 2);
+-        if (pad < 0)
+-            pad = 0;
+-        if (print_item_name)
+-            strbuf_append_strf(result,
+-                    eol[0] == '\0' ? "%s: %*s%s\n" : "%s: %*s%s",
+-                    item_name, pad, "", content
+-            );
+-        else
+-            strbuf_append_strf(result,
+-                    eol[0] == '\0' ? "%s\n" : "%s",
+-                    content
+-            );
+-    }
+-    else
+-    {
+-        /* multi-line item */
+-        if (print_item_name)
+-            strbuf_append_strf(result, "%s:\n", item_name);
+-        for (;;)
+-        {
+-            eol = strchrnul(content, '\n');
+-            strbuf_append_strf(result,
+-                    /* For %bare_multiline_item, we don't want to print colons */
+-                    (print_item_name ? ":%.*s\n" : "%.*s\n"),
+-                    (int)(eol - content), content
+-            );
+-            if (eol[0] == '\0' || eol[1] == '\0')
+-                break;
+-            content = eol + 1;
+-        }
+-    }
+-    return 1;
+-}
+-
+-static
+-int append_short_backtrace(struct strbuf *result, problem_data_t *problem_data, size_t max_text_size, bool print_item_name)
+-{
+-    const problem_item *item = problem_data_get_item_or_NULL(problem_data,
+-                                                             FILENAME_BACKTRACE);
+-    if (!item)
+-        return 0; /* "I did not print anything" */
+-    if (!(item->flags & CD_FLAG_TXT))
+-        return 0; /* "I did not print anything" */
+-
+-    char *truncated = NULL;
+-
+-    if (strlen(item->content) >= max_text_size)
+-    {
+-        char *error_msg = NULL;
+-        const char *analyzer = problem_data_get_content_or_NULL(problem_data, FILENAME_ANALYZER);
+-        if (!analyzer)
+-            return 0;
+-
+-        /* For CCpp crashes, use the GDB-produced backtrace which should be
+-         * available by now. sr_abrt_type_from_analyzer returns SR_REPORT_CORE
+-         * by default for CCpp crashes.
+-         */
+-        enum sr_report_type report_type = sr_abrt_type_from_analyzer(analyzer);
+-        if (strcmp(analyzer, "CCpp") == 0)
+-            report_type = SR_REPORT_GDB;
+-
+-        struct sr_stacktrace *backtrace = sr_stacktrace_parse(report_type,
+-                item->content, &error_msg);
+-
+-        if (!backtrace)
+-        {
+-            log(_("Can't parse backtrace: %s"), error_msg);
+-            free(error_msg);
+-            return 0;
+-        }
+-
+-        /* Get optimized thread stack trace for 10 top most frames */
+-        truncated = sr_stacktrace_to_short_text(backtrace, 10);
+-        sr_stacktrace_free(backtrace);
+-
+-        if (!truncated)
+-        {
+-            log(_("Can't generate stacktrace description (no crash thread?)"));
+-            return 0;
+-        }
+-    }
+-
+-    append_text(result,
+-                /*item_name:*/ truncated ? "truncated_backtrace" : FILENAME_BACKTRACE,
+-                /*content:*/   truncated ? truncated             : item->content,
+-                print_item_name
+-    );
+-    free(truncated);
+-    return 1;
+-}
+-
+-static
+-int append_item(struct strbuf *result, const char *item_name, problem_data_t *pd, GList *comment_fmt_spec)
+-{
+-    bool print_item_name = (strncmp(item_name, "%bare_", strlen("%bare_")) != 0);
+-    if (!print_item_name)
+-        item_name += strlen("%bare_");
+-
+-    if (item_name[0] != '%')
+-    {
+-        struct problem_item *item = problem_data_get_item_or_NULL(pd, item_name);
+-        if (!item)
+-            return 0; /* "I did not print anything" */
+-        if (!(item->flags & CD_FLAG_TXT))
+-            return 0; /* "I did not print anything" */
+-
+-        char *formatted = problem_item_format(item);
+-        char *content = formatted ? formatted : item->content;
+-        append_text(result, item_name, content, print_item_name);
+-        free(formatted);
+-        return 1; /* "I printed something" */
+-    }
+-
+-    /* Special item name */
+-
+-    /* Compat with previously-existed ad-hockery: %short_backtrace */
+-    if (strcmp(item_name, "%short_backtrace") == 0)
+-        return append_short_backtrace(result, pd, CD_TEXT_ATT_SIZE_BZ, print_item_name);
+-
+-    /* Compat with previously-existed ad-hockery: %reporter */
+-    if (strcmp(item_name, "%reporter") == 0)
+-        return append_text(result, "reporter", PACKAGE"-"VERSION, print_item_name);
+-
+-    /* %oneline,%multiline,%text */
+-    bool oneline   = (strcmp(item_name+1, "oneline"  ) == 0);
+-    bool multiline = (strcmp(item_name+1, "multiline") == 0);
+-    bool text      = (strcmp(item_name+1, "text"     ) == 0);
+-    if (!oneline && !multiline && !text)
+-    {
+-        log("Unknown or unsupported element specifier '%s'", item_name);
+-        return 0; /* "I did not print anything" */
+-    }
+-
+-    int printed = 0;
+-
+-    /* Iterate over _sorted_ items */
+-    GList *sorted_names = g_hash_table_get_keys(pd);
+-    sorted_names = g_list_sort(sorted_names, (GCompareFunc)strcmp);
+-
+-    /* %text => do as if %oneline, then repeat as if %multiline */
+-    if (text)
+-        oneline = 1;
+-
+- again: ;
+-    GList *l = sorted_names;
+-    while (l)
+-    {
+-        const char *name = l->data;
+-        l = l->next;
+-        struct problem_item *item = g_hash_table_lookup(pd, name);
+-        if (!item)
+-            continue; /* paranoia, won't happen */
+-
+-        if (!(item->flags & CD_FLAG_TXT))
+-            continue;
+-
+-        if (is_explicit_or_forbidden(name, comment_fmt_spec))
+-            continue;
+-
+-        char *formatted = problem_item_format(item);
+-        char *content = formatted ? formatted : item->content;
+-        char *eol = strchrnul(content, '\n');
+-        bool is_oneline = (eol[0] == '\0' || eol[1] == '\0');
+-        if (oneline == is_oneline)
+-            printed |= append_text(result, name, content, print_item_name);
+-        free(formatted);
+-    }
+-    if (text && oneline)
+-    {
+-        /* %text, and we just did %oneline. Repeat as if %multiline */
+-        oneline = 0;
+-        /*multiline = 1; - not checked in fact, so why bother setting? */
+-        goto again;
+-    }
+-
+-    g_list_free(sorted_names); /* names themselves are not freed */
+-
+-    return printed;
+-}
+-
+-static
+-void generate_bz_comment(struct strbuf *result, problem_data_t *pd, GList *comment_fmt_spec)
+-{
+-    bool last_line_is_empty = true;
+-    GList *l = comment_fmt_spec;
+-    while (l)
+-    {
+-        section_t *sec = l->data;
+-        l = l->next;
+-
+-        /* Skip special sections such as "%attach" */
+-        if (sec->name[0] == '%')
+-            continue;
+-
+-        if (sec->items)
+-        {
+-            /* "Text: item[,item]..." */
+-            struct strbuf *output = strbuf_new();
+-            GList *item = sec->items;
+-            while (item)
+-            {
+-                const char *str = item->data;
+-                item = item->next;
+-                if (str[0] == '-') /* "-name", ignore it */
+-                    continue;
+-                append_item(output, str, pd, comment_fmt_spec);
+-            }
+-
+-            if (output->len != 0)
+-            {
+-                strbuf_append_strf(result,
+-                            sec->name[0] ? "%s:\n%s" : "%s%s",
+-                            sec->name,
+-                            output->buf
+-                );
+-                last_line_is_empty = false;
+-            }
+-            strbuf_free(output);
+-        }
+-        else
+-        {
+-            /* Just "Text" (can be "") */
+-
+-            /* Filter out consecutive empty lines */
+-            if (sec->name[0] != '\0' || !last_line_is_empty)
+-                strbuf_append_strf(result, "%s\n", sec->name);
+-            last_line_is_empty = (sec->name[0] == '\0');
+-        }
+-    }
+-
+-    /* Nuke any trailing empty lines */
+-    while (result->len >= 1
+-     && result->buf[result->len-1] == '\n'
+-     && (result->len == 1 || result->buf[result->len-2] == '\n')
+-    ) {
+-        result->buf[--result->len] = '\0';
+-    }
+-}
+-
+-
+ /* BZ attachments */
+ 
+ static
+@@ -573,104 +69,6 @@ int attach_file_item(struct abrt_xmlrpc *ax, const char *bug_id,
+     return (r == 0);
+ }
+ 
+-static
+-int attach_item(struct abrt_xmlrpc *ax, const char *bug_id,
+-                const char *item_name, problem_data_t *pd, GList *comment_fmt_spec)
+-{
+-    if (item_name[0] != '%')
+-    {
+-        struct problem_item *item = problem_data_get_item_or_NULL(pd, item_name);
+-        if (!item)
+-            return 0;
+-        if (item->flags & CD_FLAG_TXT)
+-            return attach_text_item(ax, bug_id, item_name, item);
+-        if (item->flags & CD_FLAG_BIN)
+-            return attach_file_item(ax, bug_id, item_name, item);
+-        return 0;
+-    }
+-
+-    /* Special item name */
+-
+-    /* %oneline,%multiline,%text,%binary */
+-    bool oneline   = (strcmp(item_name+1, "oneline"  ) == 0);
+-    bool multiline = (strcmp(item_name+1, "multiline") == 0);
+-    bool text      = (strcmp(item_name+1, "text"     ) == 0);
+-    bool binary    = (strcmp(item_name+1, "binary"   ) == 0);
+-    if (!oneline && !multiline && !text && !binary)
+-    {
+-        log("Unknown or unsupported element specifier '%s'", item_name);
+-        return 0;
+-    }
+-
+-    log_debug("Special item_name '%s', iterating for attach...", item_name);
+-    int done = 0;
+-
+-    /* Iterate over _sorted_ items */
+-    GList *sorted_names = g_hash_table_get_keys(pd);
+-    sorted_names = g_list_sort(sorted_names, (GCompareFunc)strcmp);
+-
+-    GList *l = sorted_names;
+-    while (l)
+-    {
+-        const char *name = l->data;
+-        l = l->next;
+-        struct problem_item *item = g_hash_table_lookup(pd, name);
+-        if (!item)
+-            continue; /* paranoia, won't happen */
+-
+-        if (is_explicit_or_forbidden(name, comment_fmt_spec))
+-            continue;
+-
+-        if ((item->flags & CD_FLAG_TXT) && !binary)
+-        {
+-            char *content = item->content;
+-            char *eol = strchrnul(content, '\n');
+-            bool is_oneline = (eol[0] == '\0' || eol[1] == '\0');
+-            if (text || oneline == is_oneline)
+-                done |= attach_text_item(ax, bug_id, name, item);
+-        }
+-        if ((item->flags & CD_FLAG_BIN) && binary)
+-            done |= attach_file_item(ax, bug_id, name, item);
+-    }
+-
+-    g_list_free(sorted_names); /* names themselves are not freed */
+-
+-
+-    log_debug("...Done iterating over '%s' for attach", item_name);
+-
+-    return done;
+-}
+-
+-static
+-int attach_files(struct abrt_xmlrpc *ax, const char *bug_id,
+-                problem_data_t *pd, GList *comment_fmt_spec)
+-{
+-    int done = 0;
+-    GList *l = comment_fmt_spec;
+-    while (l)
+-    {
+-        section_t *sec = l->data;
+-        l = l->next;
+-
+-        /* Find %attach" */
+-        if (strcmp(sec->name, "%attach") != 0)
+-            continue;
+-
+-        GList *item = sec->items;
+-        while (item)
+-        {
+-            const char *str = item->data;
+-            item = item->next;
+-            if (str[0] == '-') /* "-name", ignore it */
+-                continue;
+-            done |= attach_item(ax, bug_id, str, pd, comment_fmt_spec);
+-        }
+-    }
+-
+-    return done;
+-}
+-
+-
+ /* Main */
+ 
+ struct bugzilla_struct {
+@@ -1103,18 +501,29 @@ int main(int argc, char **argv)
+ 
+     if (opts & OPT_D)
+     {
+-        GList *comment_fmt_spec = load_bzrep_conf_file(fmt_file);
+-        struct strbuf *bzcomment_buf = strbuf_new();
+-        generate_bz_comment(bzcomment_buf, problem_data, comment_fmt_spec);
+-        char *bzcomment = strbuf_free_nobuf(bzcomment_buf);
+-        char *summary = create_summary_string(problem_data, comment_fmt_spec);
++        problem_formatter_t *pf = problem_formatter_new();
++
++        if (problem_formatter_load_file(pf, fmt_file))
++            error_msg_and_die("Invalid format file: %s", fmt_file);
++
++        problem_report_t *pr = NULL;
++        if (problem_formatter_generate_report(pf, problem_data, &pr))
++            error_msg_and_die("Failed to format bug report from problem data");
++
+         printf("summary: %s\n"
+                 "\n"
+                 "%s"
+-                , summary, bzcomment
++                "\n"
++                , problem_report_get_summary(pr)
++                , problem_report_get_description(pr)
+         );
+-        free(bzcomment);
+-        free(summary);
++
++        puts("attachments:");
++        for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a))
++            printf(" %s\n", (const char *)a->data);
++
++        problem_report_free(pr);
++        problem_formatter_free(pf);
+         exit(0);
+     }
+ 
+@@ -1227,22 +636,29 @@ int main(int argc, char **argv)
+             /* Create new bug */
+             log(_("Creating a new bug"));
+ 
+-            GList *comment_fmt_spec = load_bzrep_conf_file(fmt_file);
++            problem_formatter_t *pf = problem_formatter_new();
++
++            if (problem_formatter_load_file(pf, fmt_file))
++                error_msg_and_die("Invalid format file: %s", fmt_file);
++
++            problem_report_t *pr = NULL;
++            if (problem_formatter_generate_report(pf, problem_data, &pr))
++                error_msg_and_die("Failed to format problem data");
+ 
+-            struct strbuf *bzcomment_buf = strbuf_new();
+-            generate_bz_comment(bzcomment_buf, problem_data, comment_fmt_spec);
+             if (crossver_id >= 0)
+-                strbuf_append_strf(bzcomment_buf, "\nPotential duplicate: bug %u\n", crossver_id);
+-            char *bzcomment = strbuf_free_nobuf(bzcomment_buf);
+-            char *summary = create_summary_string(problem_data, comment_fmt_spec);
++                problem_report_buffer_printf(
++                        problem_report_get_buffer(pr, PR_SEC_DESCRIPTION),
++                        "\nPotential duplicate: bug %u\n", crossver_id);
++
++            problem_formatter_free(pf);
++
+             int new_id = rhbz_new_bug(client,
+                     problem_data, rhbz.b_product, rhbz.b_product_version,
+-                    summary, bzcomment,
++                    problem_report_get_summary(pr),
++                    problem_report_get_description(pr),
+                     rhbz.b_create_private,
+                     rhbz.b_private_groups
+                     );
+-            free(bzcomment);
+-            free(summary);
+ 
+             if (new_id == -1)
+             {
+@@ -1267,9 +683,17 @@ int main(int argc, char **argv)
+             char new_id_str[sizeof(int)*3 + 2];
+             sprintf(new_id_str, "%i", new_id);
+ 
+-            attach_files(client, new_id_str, problem_data, comment_fmt_spec);
+-
+-//TODO: free_comment_fmt_spec(comment_fmt_spec);
++            for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a))
++            {
++                const char *item_name = (const char *)a->data;
++                struct problem_item *item = problem_data_get_item_or_NULL(problem_data, item_name);
++                if (!item)
++                    continue;
++                else if (item->flags & CD_FLAG_TXT)
++                    attach_text_item(client, new_id_str, item_name, item);
++                else if (item->flags & CD_FLAG_BIN)
++                    attach_file_item(client, new_id_str, item_name, item);
++            }
+ 
+             bz = new_bug_info();
+             bz->bi_status = xstrdup("NEW");
+@@ -1340,18 +764,21 @@ int main(int argc, char **argv)
+     const char *comment = problem_data_get_content_or_NULL(problem_data, FILENAME_COMMENT);
+     if (comment && comment[0])
+     {
+-        GList *comment_fmt_spec = load_bzrep_conf_file(fmt_file2);
+-        struct strbuf *bzcomment_buf = strbuf_new();
+-        generate_bz_comment(bzcomment_buf, problem_data, comment_fmt_spec);
+-        char *bzcomment = strbuf_free_nobuf(bzcomment_buf);
+-//TODO: free_comment_fmt_spec(comment_fmt_spec);
++        problem_formatter_t *pf = problem_formatter_new();
++        if (problem_formatter_load_file(pf, fmt_file2))
++            error_msg_and_die("Invalid duplicate format file: '%s", fmt_file2);
++
++        problem_report_t *pr;
++        if (problem_formatter_generate_report(pf, problem_data, &pr))
++            error_msg_and_die("Failed to format duplicate comment from problem data");
++
++        const char *bzcomment = problem_report_get_description(pr);
+ 
+         int dup_comment = is_comment_dup(bz->bi_comments, bzcomment);
+         if (!dup_comment)
+         {
+             log(_("Adding new comment to bug %d"), bz->bi_id);
+             rhbz_add_comment(client, bz->bi_id, bzcomment, 0);
+-            free(bzcomment);
+ 
+             const char *bt = problem_data_get_content_or_NULL(problem_data, FILENAME_BACKTRACE);
+             unsigned rating = 0;
+@@ -1369,10 +796,10 @@ int main(int argc, char **argv)
+             }
+         }
+         else
+-        {
+-            free(bzcomment);
+             log(_("Found the same comment in the bug history, not adding a new one"));
+-        }
++
++        problem_report_free(pr);
++        problem_formatter_free(pf);
+     }
+ 
+  log_out:
+-- 
+1.8.3.1
+
diff --git a/SOURCES/1001-lib-created-a-new-lib-file-for-reporters.patch b/SOURCES/1001-lib-created-a-new-lib-file-for-reporters.patch
new file mode 100644
index 0000000..d0e74f9
--- /dev/null
+++ b/SOURCES/1001-lib-created-a-new-lib-file-for-reporters.patch
@@ -0,0 +1,405 @@
+From 8d9f7ba8a42ec1cfd39e6c249aef15e9295fe0a1 Mon Sep 17 00:00:00 2001
+From: Matej Habrnal <mhabrnal@redhat.com>
+Date: Tue, 13 Jan 2015 19:23:08 -0500
+Subject: [PATCH 1001/1015] lib: created a new lib file for reporters
+
+Moved some functions from rhbz.c to src/lib/reporters.c and src/lib/strbuf.c.
+
+Related to #272
+
+Signed-off-by: Matej Habrnal <mhabrnal@redhat.com>
+---
+ po/POTFILES.in                   |  1 +
+ src/include/Makefile.am          |  3 +-
+ src/include/internal_libreport.h |  5 +++
+ src/include/reporters.h          | 36 ++++++++++++++++++
+ src/lib/Makefile.am              |  3 +-
+ src/lib/reporters.c              | 80 ++++++++++++++++++++++++++++++++++++++++
+ src/lib/strbuf.c                 | 60 ++++++++++++++++++++++++++++++
+ src/plugins/rhbz.c               | 78 +--------------------------------------
+ src/plugins/rhbz.h               |  2 -
+ 9 files changed, 188 insertions(+), 80 deletions(-)
+ create mode 100644 src/include/reporters.h
+ create mode 100644 src/lib/reporters.c
+
+diff --git a/po/POTFILES.in b/po/POTFILES.in
+index 4246e06..003e686 100644
+--- a/po/POTFILES.in
++++ b/po/POTFILES.in
+@@ -27,6 +27,7 @@ src/lib/parse_options.c
+ src/lib/problem_data.c
+ src/lib/problem_report.c
+ src/lib/reported_to.c
++src/lib/reporters.c
+ src/lib/run_event.c
+ src/plugins/abrt_rh_support.c
+ src/plugins/report_Bugzilla.xml.in
+diff --git a/src/include/Makefile.am b/src/include/Makefile.am
+index 87e5e60..4d8c6a5 100644
+--- a/src/include/Makefile.am
++++ b/src/include/Makefile.am
+@@ -15,7 +15,8 @@ libreport_include_HEADERS = \
+     file_obj.h \
+     internal_libreport.h \
+     internal_abrt_dbus.h \
+-    xml_parser.h
++    xml_parser.h \
++    reporters.h
+ 
+ if BUILD_UREPORT
+ libreport_include_HEADERS += ureport.h
+diff --git a/src/include/internal_libreport.h b/src/include/internal_libreport.h
+index cf5730c..a867649 100644
+--- a/src/include/internal_libreport.h
++++ b/src/include/internal_libreport.h
+@@ -98,6 +98,7 @@ int vdprintf(int d, const char *format, va_list ap);
+ #include "workflow.h"
+ #include "file_obj.h"
+ #include "libreport_types.h"
++#include "reporters.h"
+ 
+ #ifdef __cplusplus
+ extern "C" {
+@@ -107,6 +108,10 @@ extern "C" {
+ int prefixcmp(const char *str, const char *prefix);
+ #define suffixcmp libreport_suffixcmp
+ int suffixcmp(const char *str, const char *suffix);
++#define trim_all_whitespace libreport_trim_all_whitespace
++char *trim_all_whitespace(const char *str);
++#define shorten_string_to_length libreport_shorten_string_to_length
++char *shorten_string_to_length(const char *str, unsigned length);
+ #define strtrim libreport_strtrim
+ char *strtrim(char *str);
+ #define strtrimch libreport_strtrimch
+diff --git a/src/include/reporters.h b/src/include/reporters.h
+new file mode 100644
+index 0000000..d415b7f
+--- /dev/null
++++ b/src/include/reporters.h
+@@ -0,0 +1,36 @@
++/*
++    Copyright (C) 2014  ABRT team
++    Copyright (C) 2014  RedHat Inc
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 2 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License along
++    with this program; if not, write to the Free Software Foundation, Inc.,
++    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++*/
++
++#ifndef REPORTERS_H
++#define REPORTERS_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#define is_comment_dup libreport_is_comment_dup
++int is_comment_dup(GList *comments, const char *comment);
++#define comments_find_best_bt_rating libreport_comments_find_best_bt_rating
++unsigned comments_find_best_bt_rating(GList *comments);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
+index c11a42d..d41e543 100644
+--- a/src/lib/Makefile.am
++++ b/src/lib/Makefile.am
+@@ -58,7 +58,8 @@ libreport_la_SOURCES = \
+     xml_parser.c \
+     libreport_init.c \
+     global_configuration.c \
+-    uriparser.c
++    uriparser.c \
++    reporters.c
+ 
+ libreport_la_CPPFLAGS = \
+     -I$(srcdir)/../include \
+diff --git a/src/lib/reporters.c b/src/lib/reporters.c
+new file mode 100644
+index 0000000..e3305ca
+--- /dev/null
++++ b/src/lib/reporters.c
+@@ -0,0 +1,80 @@
++/*
++    String buffer implementation
++
++    Copyright (C) 2015  RedHat inc.
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 2 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License along
++    with this program; if not, write to the Free Software Foundation, Inc.,
++    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++*/
++
++#include "internal_libreport.h"
++
++int
++is_comment_dup(GList *comments, const char *comment)
++{
++    char * const trim_comment = trim_all_whitespace(comment);
++    bool same_comments = false;
++
++    for (GList *l = comments; l && !same_comments; l = l->next)
++    {
++        const char * const comment_body = (const char *) l->data;
++        char * const trim_comment_body = trim_all_whitespace(comment_body);
++        same_comments = (strcmp(trim_comment_body, trim_comment) == 0);
++        free(trim_comment_body);
++    }
++
++    free(trim_comment);
++    return same_comments;
++}
++
++unsigned
++comments_find_best_bt_rating(GList *comments)
++{
++    if (comments == NULL)
++        return 0;
++
++    unsigned best_rating = 0;
++    for (GList *l = comments; l; l = l->next)
++    {
++        char *comment_body = (char *) l->data;
++
++        char *start_rating_line = strstr(comment_body, FILENAME_RATING": ");
++        if (!start_rating_line)
++        {
++            log_debug(_("Note does not contain rating"));
++            continue;
++        }
++
++        start_rating_line += strlen(FILENAME_RATING": ");
++
++        errno = 0;
++        char *e;
++        long rating = strtoul(start_rating_line, &e, 10);
++        /*
++         * Note: we intentionally check for '\n'. Any other terminator
++         * (even '\0') is not ok in this case.
++         */
++        if (errno || e == start_rating_line || (*e != '\n' && *e != '\r') || (unsigned long)rating > UINT_MAX)
++        {
++            /* error / no digits / illegal trailing chars */
++            continue;
++        }
++
++        if (rating > best_rating)
++            best_rating = rating;
++    }
++
++    return best_rating;
++}
++
+diff --git a/src/lib/strbuf.c b/src/lib/strbuf.c
+index ef8bda8..f0cd1b8 100644
+--- a/src/lib/strbuf.c
++++ b/src/lib/strbuf.c
+@@ -37,6 +37,66 @@ int suffixcmp(const char *str, const char *suffix)
+         return strcmp(str + len_minus_suflen, suffix);
+ }
+ 
++char *trim_all_whitespace(const char *str)
++{
++    char *trim = xzalloc(sizeof(char) * strlen(str) + 1);
++    int i = 0;
++    while (*str)
++    {
++        if (!isspace(*str))
++            trim[i++] = *str;
++        str++;
++    }
++
++    return trim;
++}
++
++/* If str is longer than max allowed length then
++ * try to find first ' ' from the end of acceptable long str string
++ *
++ * If ' ' is found replace string after that by "..."
++ *
++ * If ' ' is NOT found in maximal allowed range, cut str string on
++ * lenght (MAX_SUMMARY_LENGTH - strlen("...")) and append "..."
++ *
++ * If MAX_LENGTH is 15 and max allowed cut is 5:
++ *
++ *   0123456789ABCDEF -> 0123456789AB...
++ *   0123456789 BCDEF -> 0123456789 ...
++ *   012345 789ABCDEF -> 012345 789AB...
++ */
++char *
++shorten_string_to_length(const char *str, unsigned length)
++{
++    char *dup_str = xstrdup(str);
++    if (strlen(str) > length)
++    {
++        char *max_end = dup_str + (length - strlen("..."));
++
++        /* maximal number of characters to cut due to attempt cut dup_str
++         * string after last ' '
++         */
++        int max_cut = 16;
++
++        /* start looking for ' ' one char before the last possible character */
++        char *buf = max_end - 1;
++        while (buf[0] != ' ' && max_cut--)
++            --buf;
++
++        if (buf[0] != ' ')
++            buf = max_end;
++        else
++            ++buf;
++
++        buf[0] = '.';
++        buf[1] = '.';
++        buf[2] = '.';
++        buf[3] = '\0';
++    }
++
++    return dup_str;
++}
++
+ /*
+  * Trims whitespace characters both from left and right side of a string.
+  * Modifies the string in-place. Returns the trimmed string.
+diff --git a/src/plugins/rhbz.c b/src/plugins/rhbz.c
+index a227c62..fdcfff9 100644
+--- a/src/plugins/rhbz.c
++++ b/src/plugins/rhbz.c
+@@ -133,41 +133,6 @@ static GList *rhbz_comments(struct abrt_xmlrpc *ax, int bug_id)
+     return g_list_reverse(comments);
+ }
+ 
+-static char *trim_all_whitespace(const char *str)
+-{
+-    func_entry();
+-
+-    char *trim = xzalloc(sizeof(char) * strlen(str) + 1);
+-    int i = 0;
+-    while (*str)
+-    {
+-        if (!isspace(*str))
+-            trim[i++] = *str;
+-        str++;
+-    }
+-
+-    return trim;
+-}
+-
+-int is_comment_dup(GList *comments, const char *comment)
+-{
+-    func_entry();
+-
+-    char * const trim_comment = trim_all_whitespace(comment);
+-    bool same_comments = false;
+-
+-    for (GList *l = comments; l && !same_comments; l = l->next)
+-    {
+-        const char * const comment_body = (const char *) l->data;
+-        char * const trim_comment_body = trim_all_whitespace(comment_body);
+-        same_comments = (strcmp(trim_comment_body, trim_comment) == 0);
+-        free(trim_comment_body);
+-    }
+-
+-    free(trim_comment);
+-    return same_comments;
+-}
+-
+ static unsigned find_best_bt_rating_in_comments(GList *comments)
+ {
+     func_entry();
+@@ -553,46 +518,7 @@ int rhbz_new_bug(struct abrt_xmlrpc *ax,
+     if (!duphash) duphash    = problem_data_get_content_or_NULL(problem_data,
+                                                                 "global_uuid");
+ 
+-    /* If summary is longer than max allowed summary length then
+-     * try to find first ' ' from the end of acceptable long summary string
+-     *
+-     * If ' ' is found replace string after that by "..."
+-     *
+-     * If ' ' is NOT found in maximal allowed range, cut summary string on
+-     * lenght (MAX_SUMMARY_LENGTH - strlen("...")) and append "..."
+-     *
+-     * If MAX_SUMMARY_LENGTH is 15 and max allowed cut is 5:
+-     *
+-     *   0123456789ABCDEF -> 0123456789AB...
+-     *   0123456789 BCDEF -> 0123456789 ...
+-     *   012345 789ABCDEF -> 012345 789AB...
+-     */
+-    char *summary = NULL;
+-    if (strlen(bzsummary) > MAX_SUMMARY_LENGTH)
+-    {
+-        summary = xstrdup(bzsummary);
+-        char *max_end = summary + (MAX_SUMMARY_LENGTH - strlen("..."));
+-
+-        /* maximal number of characters to cut due to attempt cut summary
+-         * string after last ' '
+-         */
+-        int max_cut = 16;
+-
+-        /* start looking for ' ' one char before the last possible character */
+-        char *buf = max_end - 1;
+-        while (buf[0] != ' ' && max_cut--)
+-            --buf;
+-
+-        if (buf[0] != ' ')
+-            buf = max_end;
+-        else
+-            ++buf;
+-
+-        buf[0] = '.';
+-        buf[1] = '.';
+-        buf[2] = '.';
+-        buf[3] = '\0';
+-    }
++    char *summary = shorten_string_to_length(bzsummary, MAX_SUMMARY_LENGTH);
+ 
+     char *status_whiteboard = xasprintf("abrt_hash:%s", duphash);
+ 
+@@ -604,7 +530,7 @@ int rhbz_new_bug(struct abrt_xmlrpc *ax,
+     abrt_xmlrpc_params_add_string(&env, params, "product", product);
+     abrt_xmlrpc_params_add_string(&env, params, "component", component);
+     abrt_xmlrpc_params_add_string(&env, params, "version", version);
+-    abrt_xmlrpc_params_add_string(&env, params, "summary", (summary ? summary : bzsummary));
++    abrt_xmlrpc_params_add_string(&env, params, "summary", summary);
+     abrt_xmlrpc_params_add_string(&env, params, "description", bzcomment);
+     abrt_xmlrpc_params_add_string(&env, params, "status_whiteboard", status_whiteboard);
+ 
+diff --git a/src/plugins/rhbz.h b/src/plugins/rhbz.h
+index 15e7699..86632a3 100644
+--- a/src/plugins/rhbz.h
++++ b/src/plugins/rhbz.h
+@@ -105,8 +105,6 @@ int rhbz_attach_blob(struct abrt_xmlrpc *ax, const char *bug_id,
+ int rhbz_attach_fd(struct abrt_xmlrpc *ax, const char *bug_id,
+                 const char *att_name, int fd, int flags);
+ 
+-int is_comment_dup(GList *comments, const char *comment);
+-
+ GList *rhbz_bug_cc(xmlrpc_value *result_xml);
+ 
+ struct bug_info *rhbz_bug_info(struct abrt_xmlrpc *ax, int bug_id);
+-- 
+1.8.3.1
+
diff --git a/SOURCES/1003-ureport-set-url-to-public-faf-server.patch b/SOURCES/1003-ureport-set-url-to-public-faf-server.patch
new file mode 100644
index 0000000..ee9a33c
--- /dev/null
+++ b/SOURCES/1003-ureport-set-url-to-public-faf-server.patch
@@ -0,0 +1,27 @@
+From 2221fee2d3b930f171fa5181439ded9a1748ff00 Mon Sep 17 00:00:00 2001
+From: Matej Habrnal <mhabrnal@redhat.com>
+Date: Mon, 2 Feb 2015 16:31:51 +0100
+Subject: [PATCH 1003/1015] ureport: set url to public faf server
+
+Set url to public faf server because the private one doesn't return URL to
+known issues, bthash, possible solution etc.
+
+Signed-off-by: Matej Habrnal <mhabrnal@redhat.com>
+---
+ src/plugins/ureport.conf | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/plugins/ureport.conf b/src/plugins/ureport.conf
+index 2256a7f..b82b0e1 100644
+--- a/src/plugins/ureport.conf
++++ b/src/plugins/ureport.conf
+@@ -1,5 +1,5 @@
+ # Base URL to uReport server
+-# URL = http://bug-report.itos.redhat.com
++URL = https://retrace.fedoraproject.org/faf
+ 
+ # no means that ssl certificates will not be checked
+ # SSLVerify = no
+-- 
+1.8.3.1
+
diff --git a/SOURCES/1004-conf-changed-URL-for-sending-uReport.patch b/SOURCES/1004-conf-changed-URL-for-sending-uReport.patch
new file mode 100644
index 0000000..f0386e8
--- /dev/null
+++ b/SOURCES/1004-conf-changed-URL-for-sending-uReport.patch
@@ -0,0 +1,29 @@
+From e8bb90e42d0356cdcf91d22c9deb6635d1d3c376 Mon Sep 17 00:00:00 2001
+From: Matej Habrnal <mhabrnal@redhat.com>
+Date: Mon, 2 Feb 2015 21:41:36 +0100
+Subject: [PATCH 1004/1015] conf: changed URL for sending uReport
+
+Changed faf server url in report_uReport.xml.in.
+uReports are sending to https://retrace.fedoraproject.org/faf by default.
+
+Signed-off-by: Matej Habrnal <mhabrnal@redhat.com>
+---
+ src/plugins/report_uReport.xml.in | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/plugins/report_uReport.xml.in b/src/plugins/report_uReport.xml.in
+index b997851..eca30e0 100644
+--- a/src/plugins/report_uReport.xml.in
++++ b/src/plugins/report_uReport.xml.in
+@@ -12,7 +12,7 @@
+             <_label>uReport Server URL</_label>
+             <allow-empty>no</allow-empty>
+             <_description>Address of uReport webservice</_description>
+-            <default-value>http://bug-report.itos.redhat.com</default-value>
++            <default-value>https://retrace.fedoraproject.org/faf</default-value>
+         </option>
+         <option type="text" name="uReport_ContactEmail">
+             <_label>Contact email address</_label>
+-- 
+1.8.3.1
+
diff --git a/SOURCES/1005-reporter-mantisbt-first-version-of-the-reporter-mant.patch b/SOURCES/1005-reporter-mantisbt-first-version-of-the-reporter-mant.patch
new file mode 100644
index 0000000..b267fe4
--- /dev/null
+++ b/SOURCES/1005-reporter-mantisbt-first-version-of-the-reporter-mant.patch
@@ -0,0 +1,2659 @@
+From be6691dc649962cad24ad7d7a89451b2c43de606 Mon Sep 17 00:00:00 2001
+From: Matej Habrnal <mhabrnal@redhat.com>
+Date: Tue, 13 Jan 2015 19:30:27 -0500
+Subject: [PATCH 1005/1015] reporter-mantisbt: first version of the
+ reporter-mantisbt
+
+Related to #272
+
+Signed-off-by: Matej Habrnal <mhabrnal@redhat.com>
+---
+ configure.ac                                   |   33 +
+ po/POTFILES.in                                 |   11 +
+ src/plugins/Makefile.am                        |   43 +-
+ src/plugins/centos_report_event.conf           |   37 +
+ src/plugins/mantisbt.c                         | 1111 ++++++++++++++++++++++++
+ src/plugins/mantisbt.conf                      |    8 +
+ src/plugins/mantisbt.h                         |  140 +++
+ src/plugins/mantisbt_format.conf               |   59 ++
+ src/plugins/mantisbt_formatdup.conf            |   65 ++
+ src/plugins/report_CentOSBugTracker.conf       |    4 +
+ src/plugins/report_CentOSBugTracker.xml.in     |   65 ++
+ src/plugins/reporter-mantisbt.c                |  696 +++++++++++++++
+ src/workflows/Makefile.am                      |   15 +-
+ src/workflows/report_centos.conf               |   31 +
+ src/workflows/workflow_CentOSCCpp.xml.in       |   12 +
+ src/workflows/workflow_CentOSJava.xml.in       |   11 +
+ src/workflows/workflow_CentOSKerneloops.xml.in |   11 +
+ src/workflows/workflow_CentOSLibreport.xml.in  |    9 +
+ src/workflows/workflow_CentOSPython.xml.in     |   11 +
+ src/workflows/workflow_CentOSPython3.xml.in    |   11 +
+ src/workflows/workflow_CentOSVmcore.xml.in     |   12 +
+ src/workflows/workflow_CentOSXorg.xml.in       |    9 +
+ 22 files changed, 2402 insertions(+), 2 deletions(-)
+ create mode 100644 src/plugins/centos_report_event.conf
+ create mode 100644 src/plugins/mantisbt.c
+ create mode 100644 src/plugins/mantisbt.conf
+ create mode 100644 src/plugins/mantisbt.h
+ create mode 100644 src/plugins/mantisbt_format.conf
+ create mode 100644 src/plugins/mantisbt_formatdup.conf
+ create mode 100644 src/plugins/report_CentOSBugTracker.conf
+ create mode 100644 src/plugins/report_CentOSBugTracker.xml.in
+ create mode 100644 src/plugins/reporter-mantisbt.c
+ create mode 100644 src/workflows/report_centos.conf
+ create mode 100644 src/workflows/workflow_CentOSCCpp.xml.in
+ create mode 100644 src/workflows/workflow_CentOSJava.xml.in
+ create mode 100644 src/workflows/workflow_CentOSKerneloops.xml.in
+ create mode 100644 src/workflows/workflow_CentOSLibreport.xml.in
+ create mode 100644 src/workflows/workflow_CentOSPython.xml.in
+ create mode 100644 src/workflows/workflow_CentOSPython3.xml.in
+ create mode 100644 src/workflows/workflow_CentOSVmcore.xml.in
+ create mode 100644 src/workflows/workflow_CentOSXorg.xml.in
+
+diff --git a/configure.ac b/configure.ac
+index a7f67c9..83c1a52 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -144,6 +144,39 @@ do
+ done
+ fi dnl end NO_BUGZILLA
+ 
++AC_ARG_WITH(mantisbt,
++AS_HELP_STRING([--with-mantisbt],[use MantisBT plugin (default is YES)]),
++LIBREPORT_PARSE_WITH([mantisbt]))
++
++if test -z "$NO_MANTISBT"; then
++AM_CONDITIONAL(BUILD_MANTISBT, true)
++
++# enable mantisbt & deps translations
++for FILE in `grep -e "#.*antisbt.*" -e "#.*naconda.*" po/POTFILES.in`
++do
++  sed -ie "s,$FILE,${FILE:1}," po/POTFILES.in
++  sed -ie "\,^${FILE:1}$,d" po/POTFILES.skip
++done
++else
++AM_CONDITIONAL(BUILD_MANTISBT, false)
++
++# disablie mantisbt & deps translations
++for FILE in `grep -e "antisbt" -e "naconda" po/POTFILES.in`
++do
++  if test "${FILE:0:1}" = "#"
++  then
++    continue
++  fi
++
++  sed -ie "s,$FILE,#$FILE," po/POTFILES.in
++  grep "$FILE" po/POTFILES.skip > /dev/null 2>&1
++  if test $?
++  then
++    echo "$FILE" >> po/POTFILES.skip
++  fi
++done
++fi dnl end NO_MANTISBT
++
+ AC_PATH_PROG([PYTHON_CONFIG], [python-config], [no])
+ [if test "$PYTHON_CONFIG" = "no"]
+ [then]
+diff --git a/po/POTFILES.in b/po/POTFILES.in
+index 003e686..9cf8f72 100644
+--- a/po/POTFILES.in
++++ b/po/POTFILES.in
+@@ -39,6 +39,8 @@ src/plugins/reporter-print.c
+ src/plugins/reporter-rhtsupport.c
+ src/plugins/reporter-rhtsupport-parse.c
+ src/plugins/reporter-upload.c
++src/plugins/reporter-mantisbt.c
++src/plugins/report_CentOSBugTracker.xml.in
+ src/plugins/report_Kerneloops.xml.in
+ src/plugins/report_Logger.xml.in
+ src/plugins/report_Mailx.xml.in
+@@ -47,12 +49,21 @@ src/plugins/report_Uploader.xml.in
+ src/plugins/report_uReport.xml.in
+ src/plugins/report_EmergencyAnalysis.xml.in
+ src/plugins/rhbz.c
++src/plugins/mantisbt.c
+ src/plugins/reporter-ureport.c
+ src/report-newt/report-newt.c
+ src/workflows/workflow_AnacondaFedora.xml.in
+ src/workflows/workflow_AnacondaRHEL.xml.in
+ src/workflows/workflow_AnacondaRHELBugzilla.xml.in
+ src/workflows/workflow_AnacondaUpload.xml.in
++src/workflows/workflow_CentOSCCpp.xml.in
++src/workflows/workflow_CentOSJava.xml.in
++src/workflows/workflow_CentOSKerneloops.xml.in
++src/workflows/workflow_CentOSLibreport.xml.in
++src/workflows/workflow_CentOSPython.xml.in
++src/workflows/workflow_CentOSPython3.xml.in
++src/workflows/workflow_CentOSVmcore.xml.in
++src/workflows/workflow_CentOSXorg.xml.in
+ src/workflows/workflow_FedoraCCpp.xml.in
+ src/workflows/workflow_FedoraKerneloops.xml.in
+ src/workflows/workflow_FedoraPython.xml.in
+diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
+index 8e1a166..fd3f477 100644
+--- a/src/plugins/Makefile.am
++++ b/src/plugins/Makefile.am
+@@ -7,6 +7,11 @@ reporters_bin += \
+     report
+ endif
+ 
++if BUILD_MANTISBT
++reporters_bin += \
++    reporter-mantisbt
++endif
++
+ if BUILD_UREPORT
+ reporters_bin +=  reporter-ureport
+ endif
+@@ -34,6 +39,12 @@ reporters_plugin_format_conf += bugzilla_format.conf \
+     bugzilla_formatdup_anaconda.conf
+ endif
+ 
++if BUILD_MANTISBT
++reporters_plugin_conf += mantisbt.conf
++reporters_plugin_format_conf += mantisbt_format.conf \
++    mantisbt_formatdup.conf
++endif
++
+ defaultreportpluginsconfdir = $(DEFAULT_REPORT_PLUGINS_CONF_DIR)
+ dist_defaultreportpluginsconf_DATA = $(reporters_plugin_conf) \
+     rhtsupport.conf \
+@@ -54,6 +65,12 @@ reporters_events += report_Bugzilla.xml
+ reporters_events_conf += report_Bugzilla.conf
+ endif
+ 
++if BUILD_MANTISBT
++reporters_events += report_CentOSBugTracker.xml
++
++reporters_events_conf += report_CentOSBugTracker.conf
++endif
++
+ if BUILD_UREPORT
+ reporters_events += report_uReport.xml
+ endif
+@@ -79,7 +96,8 @@ dist_eventsdef_DATA = \
+     print_event.conf \
+     rhtsupport_event.conf \
+     uploader_event.conf \
+-    emergencyanalysis_event.conf
++    emergencyanalysis_event.conf \
++    centos_report_event.conf
+ 
+ reporters_extra_dist =
+ if BUILD_BUGZILLA
+@@ -127,6 +145,29 @@ reporter_bugzilla_LDADD = \
+     ../lib/libreport.la
+ endif
+ 
++if BUILD_MANTISBT
++reporter_mantisbt_SOURCES = \
++    reporter-mantisbt.c mantisbt.c mantisbt.h
++reporter_mantisbt_CPPFLAGS = \
++    -I$(srcdir)/../include \
++    -I$(srcdir)/../lib \
++    -DBIN_DIR=\"$(bindir)\" \
++    -DCONF_DIR=\"$(CONF_DIR)\" \
++    -DLOCALSTATEDIR='"$(localstatedir)"' \
++    -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
++    -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \
++    -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
++    -DPLUGINS_CONF_DIR=\"$(REPORT_PLUGINS_CONF_DIR)\" \
++    $(GLIB_CFLAGS) \
++    $(LIBREPORT_CFLAGS) \
++    $(LIBXML_CFLAGS) \
++    -D_GNU_SOURCE
++reporter_mantisbt_LDADD = \
++    $(GLIB_LIBS) \
++    ../lib/libreport-web.la \
++    ../lib/libreport.la
++endif
++
+ reporter_rhtsupport_SOURCES = \
+     abrt_rh_support.h abrt_rh_support.c \
+     reporter-rhtsupport.h \
+diff --git a/src/plugins/centos_report_event.conf b/src/plugins/centos_report_event.conf
+new file mode 100644
+index 0000000..53f12d8
+--- /dev/null
++++ b/src/plugins/centos_report_event.conf
+@@ -0,0 +1,37 @@
++EVENT=report_CentOSBugTracker analyzer=xorg
++    reporter-mantisbt
++
++EVENT=report_CentOSBugTracker analyzer=Kerneloops
++    reporter-mantisbt
++
++EVENT=report_CentOSBugTracker analyzer=vmcore
++        reporter-mantisbt
++
++EVENT=report_CentOSBugTracker analyzer=Python component!=anaconda
++        test -f component || abrt-action-save-package-data
++        reporter-mantisbt \
++                -c /etc/libreport/plugins/mantisbt.conf \
++                -F /etc/libreport/plugins/mantisbt_format.conf \
++                -A /etc/libreport/plugins/mantisbt_formatdup.conf
++
++EVENT=report_CentOSBugTracker analyzer=Python3 component!=anaconda
++        test -f component || abrt-action-save-package-data
++        reporter-mantisbt \
++                -c /etc/libreport/plugins/mantisbt.conf \
++                -F /etc/libreport/plugins/mantisbt_format.conf \
++                -A /etc/libreport/plugins/mantisbt_formatdup.conf
++
++EVENT=report_CentOSBugTracker analyzer=CCpp duphash!=
++        test -f component || abrt-action-save-package-data
++        component="`cat component`"
++        format="mantisbt_format.conf"
++        test -f "/etc/libreport/plugins/mantisbt_format_$component.conf" \
++                && format="mantisbt_format_$component.conf"
++        formatdup="mantisbt_formatdup.conf"
++        test -f "/etc/libreport/plugins/mantisbt_formatdup_$component.conf" \
++                && formatdup="mantisbt_formatdup_$component.conf"
++        reporter-mantisbt \
++                -c /etc/libreport/plugins/mantisbt.conf \
++                -F "/etc/libreport/plugins/$format" \
++                -A "/etc/libreport/plugins/$formatdup"
++
+diff --git a/src/plugins/mantisbt.c b/src/plugins/mantisbt.c
+new file mode 100644
+index 0000000..1c496b4
+--- /dev/null
++++ b/src/plugins/mantisbt.c
+@@ -0,0 +1,1111 @@
++/*
++    Copyright (C) 2014  ABRT team
++    Copyright (C) 2014  RedHat Inc
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 2 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License along
++    with this program; if not, write to the Free Software Foundation, Inc.,
++    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++*/
++
++#include <curl/curl.h>
++
++#include <libxml/xmlreader.h>
++
++#include "internal_libreport.h"
++#include "libreport_curl.h"
++#include "mantisbt.h"
++#include "client.h"
++
++/*
++ * SOAP
++*/
++
++#define XML_VERSION "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
++
++/* fprint string */
++#define SOAP_TEMPLATE \
++    "<SOAP-ENV:Envelope xmlns:ns3=\"http://schemas.xmlsoap.org/soap/encoding/\" " \
++        "xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\" " \
++        "xmlns:ns0=\"http://schemas.xmlsoap.org/soap/encoding/\" " \
++        "xmlns:ns1=\"http://schemas.xmlsoap.org/soap/envelope/\" " \
++        "xmlns:ns2=\"http://www.w3.org/2001/XMLSchema\" " \
++        "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
++        "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" " \
++        "SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" \
++    "<SOAP-ENV:Header/>" \
++    "<ns1:Body>" \
++        "<ns3:%s>" \
++        "</ns3:%s>" \
++    "</ns1:Body>" \
++    "</SOAP-ENV:Envelope>"
++
++#define MAX_SUMMARY_LENGTH  128
++#define CUSTOMFIELD_DUPHASH "abrt_hash"
++#define CUSTOMFIELD_URL "URL"
++#define MAX_HOPS 5
++
++/* MantisBT limit is 2MB by default
++ */
++#define MANTISBT_MAX_FILE_UPLOAD_SIZE (2 * 1024 * 1024)
++
++typedef struct mantisbt_custom_fields
++{
++    char *cf_abrt_hash_id;
++    char *cf_url_id;
++} mantisbt_custom_fields_t;
++
++/*
++ * MantisBT settings issue info
++ */
++void
++mantisbt_settings_free(mantisbt_settings_t *s)
++{
++    if (s == NULL)
++        return;
++
++    free(s->m_login);
++    free(s->m_password);
++    free(s->m_project);
++    free(s->m_project_id);
++    free(s->m_project_version);
++}
++
++/*
++ * MantisBT issue info
++ */
++mantisbt_issue_info_t *
++mantisbt_issue_info_new()
++{
++    mantisbt_issue_info_t *info = xzalloc(sizeof(mantisbt_issue_info_t));
++    info->mii_id = -1;
++    info->mii_dup_id = -1;
++
++    return info;
++}
++
++void
++mantisbt_issue_info_free(mantisbt_issue_info_t *info)
++{
++    if (info == NULL)
++        return;
++
++    free(info->mii_status);
++    free(info->mii_resolution);
++    free(info->mii_reporter);
++    free(info->mii_project);
++
++    list_free_with_free(info->mii_notes);
++    list_free_with_free(info->mii_attachments);
++
++    free(info);
++}
++
++mantisbt_issue_info_t *
++mantisbt_find_origin_bug_closed_duplicate(mantisbt_settings_t *settings, mantisbt_issue_info_t *info)
++{
++    mantisbt_issue_info_t *info_tmp = mantisbt_issue_info_new();
++    info_tmp->mii_id = info->mii_id;
++    info_tmp->mii_dup_id = info->mii_dup_id;
++
++    for (int ii = 0; ii <= MAX_HOPS; ii++)
++    {
++        if (ii == MAX_HOPS)
++            error_msg_and_die(_("MantisBT couldn't find parent of issue %d"), info->mii_id);
++
++        log("Issue %d is a duplicate, using parent issue %d", info_tmp->mii_id, info_tmp->mii_dup_id);
++        int issue_id = info_tmp->mii_dup_id;
++
++        mantisbt_issue_info_free(info_tmp);
++        info_tmp = mantisbt_get_issue_info(settings, issue_id);
++
++        // found a issue which is not CLOSED as DUPLICATE
++        if (info_tmp->mii_dup_id == -1)
++            break;
++    }
++
++    return info_tmp;
++}
++
++/*
++ * SOAP request
++ */
++static soap_request_t *
++soap_request_new()
++{
++    soap_request_t *req = xzalloc(sizeof(*req));
++
++    return req;
++}
++
++void
++soap_request_free(soap_request_t *req)
++{
++    if (req == NULL)
++        return;
++
++    if (req->sr_root != NULL)
++        xmlFreeDoc(req->sr_root->doc);
++
++    free(req);
++
++    return;
++}
++
++static xmlNodePtr
++soap_node_get_next_element_node(xmlNodePtr node)
++{
++    for (; node != NULL; node = node->next)
++        if (node->type == XML_ELEMENT_NODE)
++            break;
++
++    return node;
++}
++
++static xmlNodePtr
++soap_node_get_child_element(xmlNodePtr node)
++{
++    if (node == NULL)
++        error_msg_and_die(_("SOAP: Failed to get child element because of no parent."));
++
++    return soap_node_get_next_element_node(node->xmlChildrenNode);
++}
++
++static xmlNodePtr
++soap_node_get_next_sibling(xmlNodePtr node)
++{
++    if (node == NULL)
++        error_msg_and_die(_("SOAP: Failed to get next element because of no node."));
++
++    return soap_node_get_next_element_node(node->next);
++}
++
++static xmlNodePtr
++soap_node_get_child_node(xmlNodePtr parent, const char *name)
++{
++    if (parent == NULL)
++        error_msg_and_die(_("SOAP: Failed to get child node because of no parent."));
++
++    xmlNodePtr node;
++    for (node = soap_node_get_child_element(parent); node != NULL; node = soap_node_get_next_sibling(node))
++    {
++        if (xmlStrcmp(node->name, BAD_CAST name) == 0)
++            return node;
++    }
++
++    return NULL;
++}
++
++soap_request_t *
++soap_request_new_for_method(const char *method)
++{
++    char *xml_str = xasprintf(SOAP_TEMPLATE, method, method);
++
++    xmlDocPtr doc = xmlParseDoc(BAD_CAST xml_str);
++    free(xml_str);
++
++    if (doc == NULL)
++        error_msg_and_die(_("SOAP: Failed to parse xml during creating request."));
++
++    soap_request_t *req = soap_request_new();
++
++    req->sr_root = xmlDocGetRootElement(doc);
++    if (req->sr_root == NULL)
++    {
++        soap_request_free(req);
++        error_msg_and_die(_("SOAP: Failed to get xml root element."));
++    }
++
++    req->sr_body = soap_node_get_child_node(req->sr_root, "Body");
++    req->sr_method = soap_node_get_child_node(req->sr_body, method);
++
++    return req;
++}
++
++static xmlNodePtr
++soap_node_add_child_node(xmlNodePtr node, const char *name, const char *type, const char *value)
++{
++    if (node == NULL || name == NULL)
++        error_msg_and_die(_("SOAP: Failed to add a new child node because of no node or no child name."));
++
++    xmlNodePtr new_node = xmlNewTextChild(node, /* namespace */ NULL, BAD_CAST name, BAD_CAST value);
++
++    if (new_node == NULL)
++        error_msg_and_die(_("SOAP: Failed to create a new xml child item."));
++
++    if (type != NULL)
++    {
++        if (xmlNewProp(new_node, BAD_CAST "xsi:type", BAD_CAST type) == NULL)
++            error_msg_and_die(_("SOAP: Failed to create a new property."));
++    }
++
++    return new_node;
++}
++
++void
++soap_request_add_method_parameter(soap_request_t *req, const char *name, const char *type, const char *value)
++{
++    if (req == NULL || req->sr_method == NULL)
++        error_msg_and_die(_("SOAP: Failed to add method parametr."));
++
++    soap_node_add_child_node(req->sr_method, name, type, value);
++    return;
++}
++
++void
++soap_request_add_credentials_parameter(soap_request_t *req, const mantisbt_settings_t *settings)
++{
++    soap_request_add_method_parameter(req, "username", SOAP_STRING, settings->m_login);
++    soap_request_add_method_parameter(req, "password", SOAP_STRING, settings->m_password);
++
++    return;
++}
++
++static void
++soap_add_new_issue_parameters(soap_request_t *req,
++                               const char *project,
++                               const char *version,
++                               const char *category,
++                               const char *summary,
++                               const char *description,
++                               const char *additional_information,
++                               bool private,
++                               mantisbt_custom_fields_t *fields,
++                               const char *duphash,
++                               const char *tracker_url)
++{
++    if (req == NULL || req->sr_method == NULL)
++        error_msg_and_die(_("SOAP: Failed to add new issue parametrs."));
++
++    if (project == NULL || category == NULL || summary == NULL || description == NULL)
++        error_msg_and_die(_("SOAP: Failed to add new issue parameters because the required items are missing."));
++
++    xmlNodePtr issue_node = soap_node_add_child_node(req->sr_method, "issue", SOAP_ISSUEDATA, /* content */ NULL);
++
++    // project
++    xmlNodePtr project_node = soap_node_add_child_node(issue_node, "project", SOAP_OBJECTREF, /* content */ NULL);
++    soap_node_add_child_node(project_node, "name", SOAP_STRING, project);
++
++    // view status
++    xmlNodePtr view_node = soap_node_add_child_node(issue_node, "view_state", SOAP_OBJECTREF, /* content */ NULL);
++    soap_node_add_child_node(view_node, "name", SOAP_STRING, (private) ? "private" : "public");
++
++    /* if any custom field exists */
++    int custom_fields_count = 0;
++    xmlNodePtr duphash_node;
++    if (fields->cf_abrt_hash_id != NULL || fields->cf_url_id != NULL)
++        duphash_node = soap_node_add_child_node(issue_node, "custom_fields", SOAP_CUSTOMFIELD_ARRAY, /* content */ NULL);
++
++    // custom fields (duphash and URL to tracker)
++    xmlNodePtr item_node, field_node;
++    if (fields->cf_abrt_hash_id != NULL)
++    {
++        item_node = soap_node_add_child_node(duphash_node, "item", SOAP_CUSTOMFIELD, /* content */ NULL);
++        field_node = soap_node_add_child_node(item_node, "field", SOAP_OBJECTREF, /* content */ NULL);
++        soap_node_add_child_node(field_node, "id", SOAP_INTEGER, /* custom_field id */ fields->cf_abrt_hash_id);
++        soap_node_add_child_node(item_node, "value", SOAP_STRING, duphash);
++        ++custom_fields_count;
++    }
++
++    // if tracker url exists, attach it to the issue
++    if (tracker_url != NULL && fields->cf_url_id != NULL)
++    {
++        item_node = soap_node_add_child_node(duphash_node, "item", SOAP_CUSTOMFIELD, /* content */ NULL);
++        field_node = soap_node_add_child_node(item_node, "field", SOAP_OBJECTREF, /* content */ NULL);
++        soap_node_add_child_node(field_node, "id", SOAP_INTEGER, /* custom_field */ fields->cf_url_id);
++        soap_node_add_child_node(item_node, "value", SOAP_STRING, tracker_url);
++        ++custom_fields_count;
++    }
++
++    if (custom_fields_count > 0)
++    {
++        char *type = xasprintf("%s[%i]", SOAP_CUSTOMFIELD, custom_fields_count);
++
++        if (xmlNewProp(duphash_node, BAD_CAST "ns3:arrayType", BAD_CAST type) == NULL)
++            error_msg_and_die(_("SOAP: Failed to create a new property in custom fields."));
++
++        free(type);
++    }
++
++    soap_node_add_child_node(issue_node, "os_build", SOAP_STRING, version);
++    soap_node_add_child_node(issue_node, "category", SOAP_STRING, category);
++    soap_node_add_child_node(issue_node, "summary", SOAP_STRING, summary);
++    soap_node_add_child_node(issue_node, "description", SOAP_STRING, description);
++    soap_node_add_child_node(issue_node, "additional_information", SOAP_STRING, additional_information);
++
++    return;
++}
++
++char *
++soap_request_to_str(const soap_request_t *req)
++{
++    if (req == NULL || req->sr_root == NULL || req->sr_root->doc == NULL)
++        error_msg_and_die(_("SOAP: Failed to create SOAP string because of invalid function arguments."));
++
++    xmlBufferPtr buffer = xmlBufferCreate();
++    int err = xmlNodeDump(buffer, req->sr_root->doc, req->sr_root, 1, /* formatting */ 0);
++    if (err == -1)
++    {
++        xmlBufferFree(buffer);
++        error_msg_and_die(_("SOAP: Failed to dump xml node."));
++    }
++
++    char *ret = xasprintf("%s%s", XML_VERSION, (const char *) xmlBufferContent(buffer));
++    xmlBufferFree(buffer);
++
++    return ret;
++}
++
++#if 0
++void
++soap_request_print(soap_request_t *req)
++{
++    if (req == NULL || req->sr_root == NULL || req->sr_root->doc == NULL)
++        error_msg_and_die(_("SOAP: Failed to print SOAP string."));
++
++    xmlBufferPtr buffer = xmlBufferCreate();
++    int err = xmlNodeDump(buffer, req->sr_root->doc, req->sr_root, 1, /* formatting */ 0);
++    if (err == -1)
++    {
++        xmlBufferFree(buffer);
++        error_msg_and_die(_("Failed to dump xml node."));
++    }
++
++    puts((const char *) xmlBufferContent(buffer));
++
++    xmlBufferFree(buffer);
++    return;
++}
++#endif
++
++static bool
++reader_move_reader_if_node_type_is_element_with_name_and_verify_its_value(xmlTextReaderPtr reader, const char *name)
++{
++    /* is not element node */
++    if (xmlTextReaderNodeType(reader) != XML_ELEMENT_NODE)
++        return false;
++
++    /* is not required name */
++    if (xmlStrcmp(xmlTextReaderConstName(reader), BAD_CAST name) != 0)
++        return false;
++
++    /* read next node */
++    if (xmlTextReaderRead(reader) != 1)
++        return false;
++
++    /* no value node */
++    if (xmlTextReaderHasValue(reader) == 0)
++        return false;
++
++    /* no text node */
++    if (xmlTextReaderNodeType(reader) != XML_TEXT_NODE)
++        return false;
++
++    return true;
++}
++
++static void
++reader_find_element_by_name(xmlTextReaderPtr reader, const char *name)
++{
++    while (xmlTextReaderRead(reader) == 1)
++    {
++        /* is not element node */
++        if (xmlTextReaderNodeType(reader) != XML_ELEMENT_NODE)
++            continue;
++
++        /* is not required name */
++        if (xmlStrcmp(xmlTextReaderConstName(reader), BAD_CAST name) != 0)
++            continue;
++
++        break;
++    }
++
++    return;
++}
++
++/* It is not possible to search only by name because the response contains
++ * different node with the same name. (e.g. id - user id, project id, issue id etc.)
++ * We are interested in only about issues id which is located at a different depth than others.
++ * ...
++ *   <item xsi:type="ns1:IssueData">
++ *      <id xsi:type="xsd:integer">10</id>      <-- This is issue ID (required)
++ *      <view_state xsi:type="ns1:ObjectRef">
++ *          <id xsi:type="xsd:integer">10</id>  <-- This is view_state ID (not required)
++ *          <name xsi:type="xsd:string">public</name>
++ *      </view_state>
++ *      <project xsi:type="ns1:ObjectRef">
++ *          <id xsi:type="xsd:integer">1</id>   <-- This is project ID (not required)
++ *          <name xsi:type="xsd:string">test</name>
++ *      </project>
++ * ...
++ */
++static GList *
++response_values_at_depth_by_name(const char *xml, const char *name, int depth)
++{
++    xmlDocPtr doc = xmlParseDoc(BAD_CAST xml);
++    if (doc == NULL)
++        error_msg_and_die(_("SOAP: Failed to parse xml (searching value at depth by name)."));
++
++    xmlTextReaderPtr reader = xmlReaderWalker(doc);
++    if (reader == NULL)
++        error_msg_and_die(_("SOAP: Failed to create xml text reader."));
++
++    GList *result = NULL;
++
++    const xmlChar *value;
++    while (xmlTextReaderRead(reader) == 1)
++    {
++        /* is not right depth */
++        if (depth != -1 && xmlTextReaderDepth(reader) != depth)
++            continue;
++
++        if (reader_move_reader_if_node_type_is_element_with_name_and_verify_its_value(reader, name) == false)
++            continue;
++
++        if ((value = xmlTextReaderConstValue(reader)) != NULL)
++            result = g_list_append(result, xstrdup((const char *) value));
++    }
++    xmlFreeTextReader(reader);
++
++    return result;
++}
++
++/*
++ * Finds an element named 'elem' and returns a text of a child named 'name'
++ *
++ * Example:
++ *  For
++ *  <elem>
++ *      <id>1</id>
++ *      <name>foo</name>
++ *  </elem>
++ *
++ *  returns "foo"
++ */
++static char *
++response_get_name_value_of_element(const char *xml, const char *element)
++{
++    xmlDocPtr doc = xmlParseDoc(BAD_CAST xml);
++    if (doc == NULL)
++        error_msg_and_die(_("SOAP: Failed to parse xml."));
++
++    xmlTextReaderPtr reader = xmlReaderWalker(doc);
++    if (reader == NULL)
++        error_msg_and_die(_("SOAP: Failed to create xml text reader."));
++
++    const xmlChar *value = NULL;
++
++    reader_find_element_by_name(reader, element);
++
++    /* find 'name' element and return its text */
++    while (xmlTextReaderRead(reader) == 1)
++    {
++        if (reader_move_reader_if_node_type_is_element_with_name_and_verify_its_value(reader, "name") == false)
++            continue;
++
++        if ((value = xmlTextReaderConstValue(reader)) != NULL)
++            break;
++    }
++    xmlFreeTextReader(reader);
++
++    return (char *) value;
++}
++
++static int
++response_get_id_of_relatedto_issue(const char *xml)
++{
++    xmlDocPtr doc = xmlParseDoc(BAD_CAST xml);
++    if (doc == NULL)
++        error_msg_and_die(_("SOAP: Failed to parse xml (get related to issue)."));
++
++    xmlTextReaderPtr reader = xmlReaderWalker(doc);
++    if (reader == NULL)
++        error_msg_and_die(_("SOAP: Failed to create xml text reader."));
++
++    const xmlChar *value = NULL;
++    const xmlChar *id = NULL;
++
++    /* find relationships section */
++    reader_find_element_by_name(reader, "relationships");
++
++    /* find "name" value of 'name' element */
++    while (xmlTextReaderRead(reader) == 1)
++    {
++        /* find type of relattionship */
++        if (reader_move_reader_if_node_type_is_element_with_name_and_verify_its_value(reader, "name") == false)
++            continue;
++
++        if ((value = xmlTextReaderConstValue(reader)) == NULL)
++            continue;
++
++        /* we need 'duplicate of' realtionship type */
++        if (xmlStrcmp(value, BAD_CAST "duplicate of") != 0)
++            continue;
++
++        /* find id of duplicate issues */
++        reader_find_element_by_name(reader, "target_id");
++
++        /* verify target_id node */
++        if (reader_move_reader_if_node_type_is_element_with_name_and_verify_its_value(reader, "target_id") == false)
++            continue;
++
++        /* get its value */
++        if ((id = xmlTextReaderConstValue(reader)) != NULL)
++            break;
++    }
++    xmlFreeTextReader(reader);
++
++    return (id == NULL) ? -1 : atoi((const char *) id);
++}
++
++GList *
++response_get_main_ids_list(const char *xml)
++{
++    return response_values_at_depth_by_name(xml, "id", 5);
++}
++
++int
++response_get_main_id(const char *xml)
++{
++    GList *l = response_values_at_depth_by_name(xml, "id", 5);
++    return (l != NULL) ? atoi(l->data) : -1;
++}
++
++static int
++response_get_return_value(const char *xml)
++{
++    GList *l = response_values_at_depth_by_name(xml, "return", 3);
++    return (l != NULL) ? atoi(l->data) : -1;
++}
++
++static char*
++response_get_return_value_as_string(const char *xml)
++{
++    GList *l = response_values_at_depth_by_name(xml, "return", 3);
++    return (l != NULL) ? l->data : NULL;
++}
++
++static char *
++response_get_error_msg(const char *xml)
++{
++    GList *l = response_values_at_depth_by_name(xml, "faultstring", 3);
++    return (l != NULL) ? xstrdup(l->data) : NULL;
++}
++
++static char *
++response_get_additioanl_information(const char *xml)
++{
++    GList *l = response_values_at_depth_by_name(xml, "additional_information", -1);
++    return (l != NULL) ? xstrdup(l->data) : NULL;
++}
++
++void
++response_values_free(GList *values)
++{
++    g_list_free_full(values, free);
++}
++
++/*
++ * POST
++ */
++
++void
++mantisbt_result_free(mantisbt_result_t *result)
++{
++    if (result == NULL)
++        return;
++
++    free(result->mr_url);
++    free(result->mr_msg);
++    free(result->mr_body);
++    free(result);
++}
++
++mantisbt_result_t *
++mantisbt_soap_call(const mantisbt_settings_t *settings, const soap_request_t *req)
++{
++    char *request = soap_request_to_str(req);
++
++    const char *url = settings->m_mantisbt_soap_url;
++
++    mantisbt_result_t *result = xzalloc(sizeof(*result));
++
++    if (url == NULL || request == NULL)
++    {
++        result->mr_error = -2;
++        result->mr_msg = xasprintf(_("Url or request isn't specified."));
++        free(request);
++
++        return result;
++    }
++
++    char *url_copy = NULL;
++
++    int redirect_count = 0;
++    char *errmsg;
++    post_state_t *post_state;
++
++redirect:
++    post_state = new_post_state(0
++            + POST_WANT_HEADERS
++            + POST_WANT_BODY
++            + POST_WANT_ERROR_MSG
++            + (settings->m_ssl_verify ? POST_WANT_SSL_VERIFY : 0)
++    );
++
++    post_string(post_state, settings->m_mantisbt_soap_url, "text/xml", NULL, request);
++
++    char *location = find_header_in_post_state(post_state, "Location:");
++
++    switch (post_state->http_resp_code)
++    {
++    case 404:
++        result->mr_error = -1;
++        result->mr_msg = xasprintf(_("Error in HTTP POST, "
++                        "HTTP code: 404 (Not found), URL:'%s'"), url);
++        break;
++    case 500:
++        result->mr_error = -1;
++        result->mr_msg = response_get_error_msg(post_state->body);
++
++        break;
++    case 301: /* "301 Moved Permanently" (for example, used to move http:// to https://) */
++    case 302: /* "302 Found" (just in case) */
++    case 305: /* "305 Use Proxy" */
++        if (++redirect_count < 10 && location)
++        {
++            free(url_copy);
++            url = url_copy = xstrdup(location);
++            free_post_state(post_state);
++            goto redirect;
++        }
++        /* fall through */
++
++    default:
++        result->mr_error = -1;
++        errmsg = post_state->curl_error_msg;
++        if (errmsg && errmsg[0])
++            result->mr_msg = xasprintf(_("Error in MantisBT request at '%s': %s"), url, errmsg);
++        else
++            result->mr_msg = xasprintf(_("Error in MantisBT request at '%s'"), url);
++        break;
++
++    case 200:
++    case 201:
++        /* sent successfully */
++        result->mr_url = xstrdup(location); /* note: xstrdup(NULL) returns NULL */
++    } /* switch (HTTP code) */
++
++    result->mr_http_resp_code = post_state->http_resp_code;
++    result->mr_body = post_state->body;
++    post_state->body = NULL;
++
++    free_post_state(post_state);
++    free(url_copy);
++    free(request);
++
++    return result;
++}
++
++int
++mantisbt_attach_data(const mantisbt_settings_t *settings, const char *bug_id,
++                    const char *att_name, const char *data, int size)
++{
++    soap_request_t *req = soap_request_new_for_method("mc_issue_attachment_add");
++    soap_request_add_credentials_parameter(req, settings);
++
++    soap_request_add_method_parameter(req, "issue_id", SOAP_INTEGER, bug_id);
++    soap_request_add_method_parameter(req, "name", SOAP_STRING, att_name);
++
++    soap_request_add_method_parameter(req, "file_type", SOAP_STRING, "text");
++    soap_request_add_method_parameter(req, "content", SOAP_BASE64, encode_base64(data, size));
++
++    mantisbt_result_t *result = mantisbt_soap_call(settings, req);
++    soap_request_free(req);
++
++    if (result->mr_http_resp_code != 200)
++    {
++        int ret = -1;
++        if (strcmp(result->mr_msg, "Duplicate filename.") == 0)
++            ret = -2;
++
++        error_msg(_("Failed to attach file: '%s'"), result->mr_msg);
++        mantisbt_result_free(result);
++        return ret;
++    }
++
++    int id = response_get_return_value(result->mr_body);
++
++    mantisbt_result_free(result);
++
++    return id;
++}
++
++static int
++mantisbt_attach_fd(const mantisbt_settings_t *settings, const char *bug_id,
++                const char *att_name, int fd)
++{
++    off_t size = lseek(fd, 0, SEEK_END);
++    if (size < 0)
++    {
++        perror_msg(_("Can't lseek '%s'"), att_name);
++        return -1;
++    }
++
++    if (size >= MANTISBT_MAX_FILE_UPLOAD_SIZE)
++    {
++        error_msg(_("Can't upload '%s', it's too large (%llu bytes)"), att_name, (long long)size);
++        return -1;
++    }
++    lseek(fd, 0, SEEK_SET);
++
++    char *data = xmalloc(size + 1);
++    ssize_t r = full_read(fd, data, size);
++    if (r < 0)
++    {
++        free(data);
++        perror_msg(_("Can't read '%s'"), att_name);
++        return -1;
++    }
++
++    int res = mantisbt_attach_data(settings, bug_id, att_name, data, size);
++    free(data);
++    return res;
++}
++
++int
++mantisbt_attach_file(const mantisbt_settings_t *settings, const char *bug_id,
++                    const char *att_name, const char *path)
++{
++    int fd = open(path, O_RDONLY);
++    if (fd < 0)
++    {
++        perror_msg(_("Can't open '%s'"), path);
++        return 0;
++    }
++    errno = 0;
++    struct stat st;
++    if (fstat(fd, &st) != 0 || !S_ISREG(st.st_mode))
++    {
++        perror_msg("'%s': not a regular file", path);
++        close(fd);
++        return 0;
++    }
++    log_debug("attaching '%s' as file", att_name);
++    int ret = mantisbt_attach_fd(settings, bug_id, att_name, fd);
++    close(fd);
++    return ret;
++}
++
++static void
++soap_filter_add_new_array_parameter(xmlNodePtr filter_node, const char *name, const char *type, const char *value)
++{
++    const char *array_type = NULL;
++    if( strcmp(type, SOAP_INTEGER) == 0 )
++        array_type = SOAP_INTEGERARRAY;
++    else
++        array_type = SOAP_STRINGARRAY;
++
++    xmlNodePtr filter_item = soap_node_add_child_node(filter_node, name, array_type, /* content */ NULL);
++    soap_node_add_child_node(filter_item, "item", type, value);
++}
++
++static void
++soap_filter_custom_fields_add_new_item(xmlNodePtr filter_node, const char *custom_field_name, const char *value)
++{
++    xmlNodePtr item_node = soap_node_add_child_node(filter_node, "item", SOAP_FILTER_CUSTOMFIELD, /* content */ NULL);
++
++    xmlNodePtr field_node = soap_node_add_child_node(item_node, "field", SOAP_OBJECTREF, /* content */ NULL);
++    soap_node_add_child_node(field_node, "name", SOAP_STRING, custom_field_name);
++
++    xmlNodePtr value_node = soap_node_add_child_node(item_node, "value", SOAP_STRINGARRAY, /* content */ NULL);
++    soap_node_add_child_node(value_node, "item", SOAP_STRING, value);
++}
++
++GList *
++mantisbt_search_by_abrt_hash(mantisbt_settings_t *settings, const char *abrt_hash)
++{
++    soap_request_t *req = soap_request_new_for_method("mc_filter_search_issues");
++    soap_request_add_credentials_parameter(req, settings);
++
++    xmlNodePtr filter_node = soap_node_add_child_node(req->sr_method, "filter", SOAP_FILTER_SEARCH_DATA, /* content */ NULL);
++
++    /* 'hide_status_is : -2' means, searching within all status */
++    soap_filter_add_new_array_parameter(filter_node, "hide_status_id", SOAP_INTEGERARRAY, "-2");
++
++    // custom fields
++    xmlNodePtr custom_fields_node = soap_node_add_child_node(filter_node, "custom_fields", SOAP_FILTER_CUSTOMFIELD_ARRAY, /* content */ NULL);
++
++    // custom field 'abrt_hash'
++    soap_filter_custom_fields_add_new_item(custom_fields_node, "abrt_hash", abrt_hash);
++
++    soap_request_add_method_parameter(req, "page_number", SOAP_INTEGER, "1");
++    soap_request_add_method_parameter(req, "per_page", SOAP_INTEGER, /* -1 means get all issues */ "-1");
++
++    mantisbt_result_t *result = mantisbt_soap_call(settings, req);
++    soap_request_free(req);
++
++    if (result->mr_error == -1)
++    {
++        error_msg(_("Failed to search MantisBT issue by duphash: '%s'"), result->mr_msg);
++        mantisbt_result_free(result);
++        return NULL;
++    }
++
++    GList *ids = response_get_main_ids_list(result->mr_body);
++
++    return ids;
++}
++
++GList *
++mantisbt_search_duplicate_issues(mantisbt_settings_t *settings, const char *category,
++                                     const char *version, const char *abrt_hash)
++{
++    soap_request_t *req = soap_request_new_for_method("mc_filter_search_issues");
++    soap_request_add_credentials_parameter(req, settings);
++
++    xmlNodePtr filter_node = soap_node_add_child_node(req->sr_method, "filter", SOAP_FILTER_SEARCH_DATA, /* content */ NULL);
++
++    soap_filter_add_new_array_parameter(filter_node, "project_id", SOAP_INTEGERARRAY, settings->m_project_id);
++
++    /* 'hide_status_is : -2' means, searching within all status */
++    soap_filter_add_new_array_parameter(filter_node, "hide_status_id", SOAP_INTEGERARRAY, "-2");
++
++    soap_filter_add_new_array_parameter(filter_node, "category", SOAP_STRINGARRAY, category);
++
++    // custom fields
++    xmlNodePtr custom_fields_node = soap_node_add_child_node(filter_node, "custom_fields", SOAP_FILTER_CUSTOMFIELD_ARRAY, /* content */ NULL);
++
++    // custom field 'abrt_hash'
++    soap_filter_custom_fields_add_new_item(custom_fields_node, "abrt_hash", abrt_hash);
++
++    // version
++    if (version != NULL)
++        soap_filter_add_new_array_parameter(filter_node, "os_build", SOAP_STRINGARRAY, version);
++
++    soap_request_add_method_parameter(req, "page_number", SOAP_INTEGER, "1");
++    soap_request_add_method_parameter(req, "per_page", SOAP_INTEGER, /* -1 means get all issues */ "-1");
++
++    mantisbt_result_t *result = mantisbt_soap_call(settings, req);
++    soap_request_free(req);
++
++    if (result->mr_error == -1)
++    {
++        error_msg(_("Failed to search MantisBT duplicate issue: '%s'"), result->mr_msg);
++        mantisbt_result_free(result);
++        return NULL;
++    }
++
++    GList *ids = response_get_main_ids_list(result->mr_body);
++
++    return ids;
++}
++
++static char *
++custom_field_get_id_from_name(GList *ids, GList *names, const char *name)
++{
++    GList *i = ids;
++    GList *n = names;
++    for (; i != NULL; i = i->next, n = n->next)
++    {
++        if (strcmp(n->data, name) == 0)
++            return i->data;
++    }
++
++    return NULL;
++}
++
++static void
++custom_field_ask(const char *name)
++{
++    char *msg = xasprintf(_("CentOS Bug Tracker doesn't contain custom field '%s', which is required for full functionality of the reporter. Do you still want to create a new issue?"), name);
++    int yes = ask_yes_no(msg);
++    free(msg);
++
++    if (!yes)
++    {
++        set_xfunc_error_retval(EXIT_CANCEL_BY_USER);
++        xfunc_die();
++    }
++
++    return;
++}
++
++static void
++mantisbt_get_custom_fields(const mantisbt_settings_t *settings, mantisbt_custom_fields_t *fields, const char *project_id)
++{
++    soap_request_t *req = soap_request_new_for_method("mc_project_get_custom_fields");
++    soap_request_add_credentials_parameter(req, settings);
++    soap_request_add_method_parameter(req, "project_id", SOAP_INTEGER, project_id);
++
++    mantisbt_result_t *result = mantisbt_soap_call(settings, req);
++    soap_request_free(req);
++
++    if (result->mr_http_resp_code != 200)
++        error_msg_and_die(_("Failed to get custom fields for '%s' project"), settings->m_project);
++
++    GList *ids = response_values_at_depth_by_name(result->mr_body, "id", -1);
++    GList *names = response_values_at_depth_by_name(result->mr_body, "name", -1);
++
++    mantisbt_result_free(result);
++
++    if ((fields->cf_abrt_hash_id = custom_field_get_id_from_name(ids, names, CUSTOMFIELD_DUPHASH)) == NULL)
++        custom_field_ask(CUSTOMFIELD_DUPHASH);
++
++    if ((fields->cf_url_id = custom_field_get_id_from_name(ids, names, CUSTOMFIELD_URL)) == NULL)
++        custom_field_ask(CUSTOMFIELD_URL);
++
++    return;
++}
++
++int
++mantisbt_create_new_issue(const mantisbt_settings_t *settings,
++                   problem_data_t *problem_data,
++                   const problem_report_t *pr,
++                   const char *tracker_url)
++{
++
++    const char *category = problem_data_get_content_or_NULL(problem_data, FILENAME_COMPONENT);
++    const char *duphash = problem_data_get_content_or_NULL(problem_data, FILENAME_DUPHASH);
++
++    char *summary = shorten_string_to_length(problem_report_get_summary(pr), MAX_SUMMARY_LENGTH);
++
++    const char *description = problem_report_get_description(pr);
++    const char *additional_information = problem_report_get_section(pr, PR_SEC_ADDITIONAL_INFO);
++
++    mantisbt_custom_fields_t fields;
++    mantisbt_get_custom_fields(settings, &fields, settings->m_project_id);
++
++    soap_request_t *req = soap_request_new_for_method("mc_issue_add");
++    soap_request_add_credentials_parameter(req, settings);
++    soap_add_new_issue_parameters(req, settings->m_project, settings->m_project_version, category, summary, description, additional_information, settings->m_create_private, &fields, duphash, tracker_url);
++
++    mantisbt_result_t *result = mantisbt_soap_call(settings, req);
++    soap_request_free(req);
++    free(summary);
++
++    if (result->mr_error == -1)
++    {
++        error_msg(_("Failed to create a new issue: '%s'"), result->mr_msg);
++        mantisbt_result_free(result);
++        return -1;
++    }
++
++    int id = response_get_return_value(result->mr_body);
++
++    mantisbt_result_free(result);
++    return id;
++}
++
++mantisbt_issue_info_t *
++mantisbt_get_issue_info(const mantisbt_settings_t *settings, int issue_id)
++{
++    soap_request_t *req = soap_request_new_for_method("mc_issue_get");
++    soap_request_add_credentials_parameter(req, settings);
++
++    char *issue_id_str = xasprintf("%d", issue_id);
++    soap_request_add_method_parameter(req, "issue_id", SOAP_INTEGER, issue_id_str);
++    free(issue_id_str);
++
++    mantisbt_result_t *result = mantisbt_soap_call(settings, req);
++    soap_request_free(req);
++
++    if (result->mr_error == -1)
++    {
++        error_msg(_("Failed to get MantisBT issue: '%s'"), result->mr_msg);
++        mantisbt_result_free(result);
++        return NULL;
++    }
++
++    mantisbt_issue_info_t *issue_info = mantisbt_issue_info_new();
++
++    issue_info->mii_id = issue_id;
++    issue_info->mii_status = response_get_name_value_of_element(result->mr_body, "status");
++    issue_info->mii_resolution = response_get_name_value_of_element(result->mr_body, "resolution");
++    issue_info->mii_reporter = response_get_name_value_of_element(result->mr_body, "reporter");
++    issue_info->mii_project = response_get_name_value_of_element(result->mr_body, "project");
++
++    if (strcmp(issue_info->mii_status, "closed") == 0 && !issue_info->mii_resolution)
++        error_msg(_("Issue %i is CLOSED, but it has no RESOLUTION"), issue_info->mii_id);
++
++    issue_info->mii_dup_id = response_get_id_of_relatedto_issue(result->mr_body);
++
++    if (strcmp(issue_info->mii_status, "closed") == 0
++        && strcmp(issue_info->mii_resolution, "duplicate") == 0
++        && issue_info->mii_dup_id == -1 )
++    {
++        error_msg(_("Issue %i is CLOSED as DUPLICATE, but it has no DUPLICATE_ID"),
++                            issue_info->mii_id);
++    }
++
++    /* notes are stored in <text> element */
++    issue_info->mii_notes = response_values_at_depth_by_name(result->mr_body, "text", -1);
++
++    /* looking for bt rating in additional information too */
++    char *add_info = response_get_additioanl_information(result->mr_body);
++    if (add_info != NULL)
++        issue_info->mii_notes = g_list_append (issue_info->mii_notes, add_info);
++    issue_info->mii_attachments = response_values_at_depth_by_name(result->mr_body, "filename", -1);
++    issue_info->mii_best_bt_rating = comments_find_best_bt_rating(issue_info->mii_notes);
++
++    mantisbt_result_free(result);
++    return issue_info;
++}
++
++int
++mantisbt_add_issue_note(const mantisbt_settings_t *settings, int issue_id, const char *note)
++{
++    soap_request_t *req = soap_request_new_for_method("mc_issue_note_add");
++    soap_request_add_credentials_parameter(req, settings);
++
++    char *issue_id_str = xasprintf("%i", issue_id);
++    soap_node_add_child_node(req->sr_method, "issue_id", SOAP_INTEGER, issue_id_str);
++
++    xmlNodePtr note_node = soap_node_add_child_node(req->sr_method, "note", SOAP_ISSUENOTE, /* content */ NULL);
++    soap_node_add_child_node(note_node, "text", SOAP_STRING, note);
++
++    mantisbt_result_t *result = mantisbt_soap_call(settings, req);
++
++    free(issue_id_str);
++    soap_request_free(req);
++
++    if (result->mr_error == -1)
++    {
++        error_msg(_("Failed to add MantisBT issue note: '%s'"), result->mr_msg);
++        mantisbt_result_free(result);
++        return -1;
++    }
++    int id = response_get_return_value(result->mr_body);
++
++    mantisbt_result_free(result);
++    return id;
++}
++
++void
++mantisbt_get_project_id_from_name(mantisbt_settings_t *settings)
++{
++    if (settings->m_project == NULL)
++        error_msg_and_die(_("The MantisBT project has not been deretmined."));
++
++    soap_request_t *req = soap_request_new_for_method("mc_project_get_id_from_name");
++    soap_request_add_credentials_parameter(req, settings);
++    soap_node_add_child_node(req->sr_method, "project_name", SOAP_STRING, settings->m_project);
++
++    mantisbt_result_t *result = mantisbt_soap_call(settings, req);
++
++    if (result->mr_http_resp_code != 200)
++        error_msg_and_die(_("Failed to get project id from name"));
++
++    settings->m_project_id = response_get_return_value_as_string(result->mr_body);
++
++    return;
++}
+diff --git a/src/plugins/mantisbt.conf b/src/plugins/mantisbt.conf
+new file mode 100644
+index 0000000..15a1065
+--- /dev/null
++++ b/src/plugins/mantisbt.conf
+@@ -0,0 +1,8 @@
++# MantisBT URL
++MantisbtURL = https://bugs.centos.org/
++# yes means that ssl certificates will be checked
++SSLVerify = yes
++# your login has to exist, if you don have any, please create one
++Login =
++# your password
++Password =
+diff --git a/src/plugins/mantisbt.h b/src/plugins/mantisbt.h
+new file mode 100644
+index 0000000..c31c174
+--- /dev/null
++++ b/src/plugins/mantisbt.h
+@@ -0,0 +1,140 @@
++/*
++    Copyright (C) 2014  ABRT team
++    Copyright (C) 2014  RedHat Inc
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 2 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License along
++    with this program; if not, write to the Free Software Foundation, Inc.,
++    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++*/
++
++#ifndef MANTISBT_H
++#define MANTISBT_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <libxml/encoding.h>
++#include "problem_report.h"
++
++#define SOAP_STRING "ns2:string"
++#define SOAP_INTEGER "ns2:integer"
++#define SOAP_INTEGERARRAY "ns2:IntegerArray"
++#define SOAP_STRINGARRAY "ns2:StringArray"
++#define SOAP_ISSUEDATA "ns3:IssueData"
++#define SOAP_OBJECTREF "ns3:ObjectRef"
++#define SOAP_CUSTOMFIELD_ARRAY "ns2:CustomFieldValueForIssueDataArray"
++#define SOAP_FILTER_CUSTOMFIELD "ns2:FilterCustomField"
++#define SOAP_FILTER_CUSTOMFIELD_ARRAY "ns2:FilterCustomFieldArray"
++#define SOAP_FILTER_SEARCH_DATA "ns2:FilterSearchData"
++#define SOAP_CUSTOMFIELD "ns2:CustomFieldValueForIssueData"
++#define SOAP_BASE64 "SOAP-ENC:base64"
++#define SOAP_ISSUENOTE "ns3:IssueNoteData"
++
++#define PR_SEC_ADDITIONAL_INFO "Additional info"
++
++typedef struct soap_request
++{
++    xmlNodePtr sr_root;
++    xmlNodePtr sr_body;
++    xmlNodePtr sr_method;
++} soap_request_t;
++
++typedef struct mantisbt_settings
++{
++    char *m_login;
++    char *m_password;
++    const char *m_mantisbt_url;
++    const char *m_mantisbt_soap_url;
++    char *m_project;
++    char *m_project_id;
++    char *m_project_version;
++    const char *m_DontMatchComponents;
++    int         m_ssl_verify;
++    int         m_create_private;
++} mantisbt_settings_t;
++
++typedef struct mantisbt_result
++{
++    int mr_http_resp_code;
++    int mr_error;
++    char *mr_msg;
++    char *mr_url;
++    char *mr_body;
++} mantisbt_result_t;
++
++typedef struct mantisbt_issue_info
++{
++    int mii_id;
++    int mii_dup_id;
++    unsigned mii_best_bt_rating;
++
++    char *mii_status;
++    char *mii_resolution;
++    char *mii_reporter;
++    char *mii_project;
++
++    GList *mii_notes;
++    GList *mii_attachments;
++} mantisbt_issue_info_t;
++
++void mantisbt_settings_free(mantisbt_settings_t *settings);
++
++mantisbt_issue_info_t * mantisbt_issue_info_new();
++void mantisbt_issue_info_free(mantisbt_issue_info_t *info);
++mantisbt_issue_info_t * mantisbt_find_origin_bug_closed_duplicate(mantisbt_settings_t *settings, mantisbt_issue_info_t *info);
++
++void soap_request_free(soap_request_t *req);
++
++soap_request_t *soap_request_new_for_method(const char *method);
++
++void soap_request_add_method_parameter(soap_request_t *req, const char *name, const char *type, const char *value);
++void soap_request_add_credentials_parameter(soap_request_t *req, const mantisbt_settings_t *settings);
++
++char *soap_request_to_str(const soap_request_t *req);
++
++#if 0
++void soap_request_print(soap_request_t *req);
++#endif
++
++GList * response_get_main_ids_list(const char *xml);
++int response_get_main_id(const char *xml);
++void response_values_free(GList *values);
++
++void mantisbt_result_free(mantisbt_result_t *result);
++mantisbt_result_t *mantisbt_soap_call(const mantisbt_settings_t *settings, const soap_request_t *req);
++
++int mantisbt_attach_data(const mantisbt_settings_t *settings, const char *bug_id,
++                         const char *att_name, const char *data, int size);
++
++int mantisbt_attach_file(const mantisbt_settings_t *settings, const char *bug_id,
++                    const char *att_name, const char *data);
++
++GList * mantisbt_search_by_abrt_hash(mantisbt_settings_t *settings, const char *abrt_hash);
++GList * mantisbt_search_duplicate_issues(mantisbt_settings_t *settings, const char *category,
++                    const char *version, const char *abrt_hash);
++
++int mantisbt_create_new_issue(const mantisbt_settings_t *settings, problem_data_t *problem_data,
++                       const problem_report_t *pr, const char *tracker_url);
++
++mantisbt_issue_info_t * mantisbt_get_issue_info(const mantisbt_settings_t *settings, int issue_id);
++int mantisbt_add_issue_note(const mantisbt_settings_t *settings, int issue_id, const char *note);
++
++void mantisbt_get_project_id_from_name(mantisbt_settings_t *settings);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
++
+diff --git a/src/plugins/mantisbt_format.conf b/src/plugins/mantisbt_format.conf
+new file mode 100644
+index 0000000..da08aa6
+--- /dev/null
++++ b/src/plugins/mantisbt_format.conf
+@@ -0,0 +1,59 @@
++# Lines starting with # are ignored.
++# Lines can be continued on the next line using trailing backslash.
++#
++# Format:
++# %summary:: summary format
++# section:: element1[,element2]...
++# The literal text line to be added to Bugzilla comment. Can be empty.
++# (IOW: empty lines are NOT ignored!)
++#
++# Summary format is a line of text, where %element% is replaced by
++# text element's content, and [[...%element%...]] block is used only if
++# %element% exists. [[...]] blocks can nest.
++#
++# Sections can be:
++# - %summary: bug summary format string.
++# - %attach: a list of elements to attach.
++# - text, double colon (::) and the list of comma-separated elements.
++#   Text can be empty (":: elem1, elem2, elem3" works),
++#   in this case "Text:" header line will be omitted.
++#
++# Elements can be:
++# - problem directory element names, which get formatted as
++#   <element_name>: <contents>
++#   or
++#   <element_name>:
++#   :<contents>
++#   :<contents>
++#   :<contents>
++# - problem directory element names prefixed by "%bare_",
++#   which is formatted as-is, without "<element_name>:" and colons
++# - %oneline, %multiline, %text wildcards, which select all corresponding
++#   elements for output or attachment
++# - %binary wildcard, valid only for %attach section, instructs to attach
++#   binary elements
++# - problem directory element names prefixed by "-",
++#   which excludes given element from all wildcards
++#
++#   Nonexistent elements are silently ignored.
++#   If none of elements exists, the section will not be created.
++
++%summary:: [abrt] %pkg_name%[[: %crash_function%()]][[: %reason%]][[: TAINTED %tainted_short%]]
++
++Description of problem:: %bare_comment
++
++Version-Release number of selected component:: %bare_package
++
++Truncated backtrace:: %bare_%short_backtrace
++
++%Additional info::
++:: -pkg_arch,-pkg_epoch,-pkg_name,-pkg_release,-pkg_version,\
++		-component,-architecture,\
++	-analyzer,-count,-duphash,-uuid,-abrt_version,\
++	-username,-hostname,-os_release,-os_info,\
++	-time,-pid,-pwd,-last_occurrence,-ureports_counter,\
++	%reporter,\
++	%oneline
++
++%attach:: -comment,-reason,-reported_to,-event_log,%multiline,\
++	-coredump,%binary
+diff --git a/src/plugins/mantisbt_formatdup.conf b/src/plugins/mantisbt_formatdup.conf
+new file mode 100644
+index 0000000..b1552ac
+--- /dev/null
++++ b/src/plugins/mantisbt_formatdup.conf
+@@ -0,0 +1,65 @@
++# Lines starting with # are ignored.
++# Lines can be continued on the next line using trailing backslash.
++#
++# Format:
++# %summary:: summary format
++# section:: element1[,element2]...
++# The literal text line to be added to Bugzilla comment. Can be empty.
++# (IOW: empty lines are NOT ignored!)
++#
++# Summary format is a line of text, where %element% is replaced by
++# text element's content, and [[...%element%...]] block is used only if
++# %element% exists. [[...]] blocks can nest.
++#
++# Sections can be:
++# - %summary: bug summary format string.
++# - %attach: a list of elements to attach.
++# - text, double colon (::) and the list of comma-separated elements.
++#   Text can be empty (":: elem1, elem2, elem3" works),
++#   in this case "Text:" header line will be omitted.
++#
++# Elements can be:
++# - problem directory element names, which get formatted as
++#   <element_name>: <contents>
++#   or
++#   <element_name>:
++#   :<contents>
++#   :<contents>
++#   :<contents>
++# - problem directory element names prefixed by "%bare_",
++#   which is formatted as-is, without "<element_name>:" and colons
++# - %oneline, %multiline, %text wildcards, which select all corresponding
++#   elements for output or attachment
++# - %binary wildcard, valid only for %attach section, instructs to attach
++#   binary elements
++# - problem directory element names prefixed by "-",
++#   which excludes given element from all wildcards
++#
++#   Nonexistent elements are silently ignored.
++#   If none of elements exists, the section will not be created.
++
++# When we add a comment to an existing BZ, %summary is ignored
++# (it specifies *new bug* summary field):
++# %summary:: blah blah
++
++# When dup is detected, BZ reporter adds a comment to it.
++# This comment may interrupt an ongoing conversation in the BZ.
++# (Three people independently filed a bug against abrt about this).
++# Need to clearly explain what this comment is, to prevent confusion.
++# Hopefully, this line would suffice:
++Another user experienced a similar problem:
++
++# If user filled out comment field, show it:
++:: %bare_comment
++
++# var_log_messages has too much variance (time/date),
++# we exclude it from message so that dup message elimination has more chances to work
++:: \
++	-pkg_arch,-pkg_epoch,-pkg_name,-pkg_release,-pkg_version,\
++		-component,-architecture,\
++	-analyzer,-count,-duphash,-uuid,-abrt_version,\
++	-username,-hostname,-os_release,-os_info,\
++	-time,-pid,-pwd,-last_occurrence,-ureports_counter,\
++	-var_log_messages,\
++	%reporter,\
++	%oneline
+diff --git a/src/plugins/report_CentOSBugTracker.conf b/src/plugins/report_CentOSBugTracker.conf
+new file mode 100644
+index 0000000..04cab13
+--- /dev/null
++++ b/src/plugins/report_CentOSBugTracker.conf
+@@ -0,0 +1,4 @@
++Mantisbt_MantisbtURL = https://bugs.centos.org
++Mantisbt_Login =
++Mantisbt_Password
++Mantisbt_SSLVerify = yes
+diff --git a/src/plugins/report_CentOSBugTracker.xml.in b/src/plugins/report_CentOSBugTracker.xml.in
+new file mode 100644
+index 0000000..81f909b
+--- /dev/null
++++ b/src/plugins/report_CentOSBugTracker.xml.in
+@@ -0,0 +1,65 @@
++<?xml version="1.0" encoding="UTF-8" ?>
++<event>
++    <_name>CentOS Bug Tracker</_name>
++    <_description>Report to CentOS Bug Tracker</_description>
++
++    <requires-items>component,duphash,os_release</requires-items>
++    <exclude-items-by-default>coredump,count,event_log,reported_to,vmcore</exclude-items-by-default>
++    <exclude-items-always></exclude-items-always>
++    <exclude-binary-items>no</exclude-binary-items>
++    <include-items-by-default></include-items-by-default>
++    <minimal-rating>3</minimal-rating>
++    <gui-review-elements>yes</gui-review-elements>
++
++    <options>
++        <option type="text" name="Mantisbt_Login">
++            <_label>User name</_label>
++            <allow-empty>no</allow-empty>
++            <_description>CentOS Bug Tracker account user name</_description>
++            <_note-html>You can create bugs.centos.org account &lt;a href="https://bugs.centos.org/signup_page.php"&gt;here&lt;/a&gt;</_note-html>
++        </option>
++        <option type="password" name="Mantisbt_Password">
++            <_label>Password</_label>
++            <allow-empty>no</allow-empty>
++            <_description>CentOS Bug Tracker account password</_description>
++        </option>
++        <advanced-options>
++            <option type="text" name="Mantisbt_MantisbtURL">
++                <_label>CentOS Bug Tracker URL</_label>
++                <allow-empty>no</allow-empty>
++                <_note-html>Address of CentOS Bug Tracker server</_note-html>
++                <default-value>https://bugs.centos.org</default-value>
++            </option>
++            <option type="bool" name="Mantisbt_SSLVerify">
++                <_label>Verify SSL</_label>
++                <_note-html>Check SSL key validity</_note-html>
++                <default-value>yes</default-value>
++            </option>
++            <option type="text" name="Mantisbt_Project">
++                <_label>CentOS Bug Tracker project</_label>
++                <allow-empty>yes</allow-empty>
++                <_note-html>Specify this only if you needed different project than specified in /etc/os-release</_note-html>
++            </option>
++            <option type="text" name="Mantisbt_ProjectVersion">
++                <_label>CentOS Bug Tracker project version</_label>
++                <allow-empty>yes</allow-empty>
++                <_note-html>Specify this only if you needed different project version than specified in /etc/os-release</_note-html>
++            </option>
++            <option type="text" name="http_proxy">
++                <_label>HTTP Proxy</_label>
++                <allow-empty>yes</allow-empty>
++                <_note-html>Sets the proxy server to use for HTTP</_note-html>
++            </option>
++            <option type="text" name="HTTPS_PROXY">
++                <_label>HTTPS Proxy</_label>
++                <allow-empty>yes</allow-empty>
++                <_note-html>Sets the proxy server to use for HTTPS</_note-html>
++            </option>
++            <option type="bool" name="Mantisbt_CreatePrivate">
++                <_label>Restrict access</_label>
++                <_note-html>Restrict access to the created CentOS Bug Tracker issue allowing only users from specified groups to view it (see advanced settings for more details)</_note-html>
++                <default-value>no</default-value>
++            </option>
++        </advanced-options>
++    </options>
++</event>
+diff --git a/src/plugins/reporter-mantisbt.c b/src/plugins/reporter-mantisbt.c
+new file mode 100644
+index 0000000..d281cdb
+--- /dev/null
++++ b/src/plugins/reporter-mantisbt.c
+@@ -0,0 +1,696 @@
++/*
++    Copyright (C) 2014  ABRT team
++    Copyright (C) 2014  RedHat Inc
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 2 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License along
++    with this program; if not, write to the Free Software Foundation, Inc., 51
++    Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++*/
++
++#include "internal_libreport.h"
++#include "client.h"
++#include "mantisbt.h"
++#include "problem_report.h"
++
++static void
++parse_osinfo_for_mantisbt(map_string_t *osinfo, char** project, char** version)
++{
++    const char *name = get_map_string_item_or_NULL(osinfo, "CENTOS_MANTISBT_PROJECT");
++    if (!name)
++        name = get_map_string_item_or_NULL(osinfo, OSINFO_NAME);
++
++    const char *version_id = get_map_string_item_or_NULL(osinfo, "CENTOS_MANTISBT_PROJECT_VERSION");
++    if (!version_id)
++        version_id = get_map_string_item_or_NULL(osinfo, OSINFO_VERSION_ID);
++
++    if (name && version_id)
++    {
++        *project = xstrdup(name);
++        *version = xstrdup(version_id);
++        return;
++    }
++
++    /* something bad happend */
++    *project = NULL;
++    *version = NULL;
++}
++
++static char *
++ask_mantisbt_login(const char *message)
++{
++    char *login = ask(message);
++    if (login == NULL || login[0] == '\0')
++    {
++        set_xfunc_error_retval(EXIT_CANCEL_BY_USER);
++        error_msg_and_die(_("Can't continue without login"));
++    }
++
++    return login;
++}
++
++static char *
++ask_mantisbt_password(const char *message)
++{
++    char *password = ask_password(message);
++    /* TODO: this should be fixed in ask_password() as other tools have the same problem */
++    putchar('\n');
++    if (password == NULL || password[0] == '\0')
++    {
++        set_xfunc_error_retval(EXIT_CANCEL_BY_USER);
++        error_msg_and_die(_("Can't continue without password"));
++    }
++
++    return password;
++}
++
++static void
++ask_mantisbt_credentials(mantisbt_settings_t *settings, const char *pre_message)
++{
++    free(settings->m_login);
++    free(settings->m_password);
++
++    char *question = xasprintf("%s %s", pre_message, _("Please enter your CentOS Bug Tracker login:"));
++    settings->m_login = ask_mantisbt_login(question);
++    free(question);
++
++    question = xasprintf("%s %s '%s':", pre_message, _("Please enter the password for"), settings->m_login);
++    settings->m_password = ask_mantisbt_password(question);
++    free(question);
++
++    return;
++}
++
++static void
++verify_credentials(mantisbt_settings_t *settings)
++{
++    if (settings->m_login[0] == '\0' || settings->m_password[0] == '\0')
++        ask_mantisbt_credentials(settings, _("Credentials are not provided by configuration."));
++
++    while (true)
++    {
++        soap_request_t *req = soap_request_new_for_method("mc_login");
++        soap_request_add_credentials_parameter(req, settings);
++
++        mantisbt_result_t *result = mantisbt_soap_call(settings, req);
++        soap_request_free(req);
++
++        if (g_verbose > 2)
++        {
++            GList *ids = response_get_main_ids_list(result->mr_body);
++            if (ids != NULL)
++                log("%s", (char *)ids->data);
++            response_values_free(ids);
++        }
++
++        int result_val = result->mr_http_resp_code;
++        mantisbt_result_free(result);
++
++        if (result_val == 200)
++            return;
++
++        ask_mantisbt_credentials(settings, _("Invalid password or login."));
++    }
++}
++
++static void
++set_settings(mantisbt_settings_t *m, map_string_t *settings, struct dump_dir *dd)
++{
++    const char *environ;
++
++    environ = getenv("Mantisbt_Login");
++    m->m_login = xstrdup(environ ? environ : get_map_string_item_or_empty(settings, "Login"));
++
++    environ = getenv("Mantisbt_Password");
++    m->m_password = xstrdup(environ ? environ : get_map_string_item_or_empty(settings, "Password"));
++
++    environ = getenv("Mantisbt_MantisbtURL");
++    m->m_mantisbt_url = environ ? environ : get_map_string_item_or_empty(settings, "MantisbtURL");
++    if (!m->m_mantisbt_url[0])
++        m->m_mantisbt_url = "https://bugs.centos.org/";
++    else
++    {
++        /* We don't want trailing '/': "https://host/dir/" -> "https://host/dir" */
++        char *last_slash = strrchr(m->m_mantisbt_url, '/');
++        if (last_slash && last_slash[1] == '\0')
++            *last_slash = '\0';
++    }
++    m->m_mantisbt_soap_url = concat_path_file(m->m_mantisbt_url, "api/soap/mantisconnect.php");
++
++    environ = getenv("Mantisbt_Project");
++    if (environ)
++    {
++        m->m_project = xstrdup(environ);
++        environ = getenv("Mantisbt_ProjectVersion");
++        if (environ)
++            m->m_project_version = xstrdup(environ);
++    }
++    else
++    {
++        const char *option = get_map_string_item_or_NULL(settings, "Project");
++        if (option)
++            m->m_project = xstrdup(option);
++        option = get_map_string_item_or_NULL(settings, "ProjectVersion");
++        if (option)
++            m->m_project_version = xstrdup(option);
++    }
++
++    if (!m->m_project || !*m->m_project) /* if not overridden or empty... */
++    {
++        free(m->m_project);
++        free(m->m_project_version);
++
++        if (dd != NULL)
++        {
++            map_string_t *osinfo = new_map_string();
++
++            char *os_info_data = dd_load_text(dd, FILENAME_OS_INFO);
++            parse_osinfo(os_info_data, osinfo);
++            free(os_info_data);
++
++            parse_osinfo_for_mantisbt(osinfo, &m->m_project, &m->m_project_version);
++            free_map_string(osinfo);
++        }
++    }
++
++    environ = getenv("Mantisbt_SSLVerify");
++    m->m_ssl_verify = string_to_bool(environ ? environ : get_map_string_item_or_empty(settings, "SSLVerify"));
++
++    environ = getenv("Mantisbt_DontMatchComponents");
++    m->m_DontMatchComponents = environ ? environ : get_map_string_item_or_empty(settings, "DontMatchComponents");
++
++    environ = getenv(CREATE_PRIVATE_TICKET);
++    if (environ)
++        m->m_create_private = string_to_bool(environ);
++
++    if (!m->m_create_private)
++    {
++        environ = getenv("Mantisbt_CreatePrivate");
++        m->m_create_private = string_to_bool(environ ? environ : get_map_string_item_or_empty(settings, "CreatePrivate"));
++    }
++    log_notice("create private CentOS Bug Tracker ticket: '%s'", m->m_create_private ? "YES": "NO");
++}
++
++int main(int argc, char **argv)
++{
++    abrt_init(argv);
++
++    /* I18n */
++    setlocale(LC_ALL, "");
++#if ENABLE_NLS
++    bindtextdomain(PACKAGE, LOCALEDIR);
++    textdomain(PACKAGE);
++#endif
++
++    const char *program_usage_string = _(
++        "\n& [-vf] [-c CONFFILE]... [-F FMTFILE] [-A FMTFILE2] -d DIR"
++        "\nor:"
++        "\n& [-v] [-c CONFFILE]... [-d DIR] -t[ID] FILE..."
++        "\nor:"
++        "\n& [-v] [-c CONFFILE]... [-d DIR] -t[ID] -w"
++        "\nor:"
++        "\n& [-v] [-c CONFFILE]... -h DUPHASH"
++        "\n"
++        "\nReports problem to CentOS Bug Tracker."
++        "\n"
++        "\nThe tool reads DIR. Then it tries to find an issue"
++        "\nwith the same abrt_hash in custom field 'abrt_hash'."
++        "\n"
++        "\nIf such issue is not found, then a new issue is created. Elements of DIR"
++        "\nare stored in the issue as part of issue description or as attachments,"
++        "\ndepending on their type and size."
++        "\n"
++        "\nOtherwise, if such issue is found and it is marked as CLOSED DUPLICATE,"
++        "\nthe tool follows the chain of duplicates until it finds a non-DUPLICATE issue."
++        "\nThe tool adds a new comment to found issue."
++        "\n"
++        "\nThe URL to new or modified issue is printed to stdout and recorded in"
++        "\n'reported_to' element."
++        "\n"
++        "\nOption -t uploads FILEs to the already created issue on CentOS Bug Tracker site."
++        "\nThe issue ID is retrieved from directory specified by -d DIR."
++        "\nIf problem data in DIR was never reported to CentOS Bug Tracker, upload will fail."
++        "\n"
++        "\nOption -tID uploads FILEs to the issue with specified ID on CentOS Bug Tracker site."
++        "\n-d DIR is ignored."
++        "\n"
++        "\nOption -r sets the last url from reporter_to element which is prefixed with"
++        "\nTRACKER_NAME to URL field. This option is applied only when a new issue is to be"
++        "\nfiled. The default value is 'ABRT Server'"
++        "\n"
++        "\nIf not specified, CONFFILE defaults to "CONF_DIR"/plugins/mantisb.conf"
++        "\nIts lines should have 'PARAM = VALUE' format."
++        "\nRecognized string parameters: MantisbtURL, Login, Password, Project, ProjectVersion."
++        "\nRecognized boolean parameter (VALUE should be 1/0, yes/no): SSLVerify, CreatePrivate."
++        "\nParameters can be overridden via $Mantisbt_PARAM environment variables."
++        "\n"
++        "\nFMTFILE and FMTFILE2 default to "CONF_DIR"/plugins/mantisbt_format.conf"
++    );
++
++    enum {
++        OPT_v = 1 << 0,
++        OPT_d = 1 << 1,
++        OPT_c = 1 << 2,
++        OPT_F = 1 << 3,
++        OPT_A = 1 << 4,
++        OPT_t = 1 << 5,
++        OPT_f = 1 << 6,
++        OPT_h = 1 << 7,
++        OPT_r = 1 << 8,
++        OPT_D = 1 << 9,
++    };
++
++    const char *dump_dir_name = ".";
++    GList *conf_file = NULL;
++    const char *fmt_file = CONF_DIR"/plugins/mantisbt_format.conf";
++    const char *fmt_file2 = fmt_file;
++    char *abrt_hash = NULL;
++    char *ticket_no = NULL;
++    const char *tracker_str = "ABRT Server";
++    char *debug_str = NULL;
++    mantisbt_settings_t mbt_settings = { 0 };
++    /* Keep enum above and order of options below in sync! */
++    struct options program_options[] = {
++        OPT__VERBOSE(&g_verbose),
++        OPT_STRING(   'd', NULL, &dump_dir_name , "DIR"    , _("Problem directory")),
++        OPT_LIST(     'c', NULL, &conf_file     , "FILE"   , _("Configuration file (may be given many times)")),
++        OPT_STRING(   'F', NULL, &fmt_file      , "FILE"   , _("Formatting file for initial comment")),
++        OPT_STRING(   'A', NULL, &fmt_file2     , "FILE"   , _("Formatting file for duplicates")),
++        OPT_OPTSTRING('t', "ticket", &ticket_no , "ID"     , _("Attach FILEs [to issue with this ID]")),
++        OPT_BOOL(     'f', NULL, NULL,                       _("Force reporting even if this problem is already reported")),
++        OPT_STRING(   'h', "duphash", &abrt_hash, "DUPHASH", _("Print BUG_ID which has given DUPHASH")),
++        OPT_STRING(   'r', "tracker", &tracker_str, "TRACKER_NAME", _("A name of bug tracker for an additional URL from 'reported_to'")),
++
++        OPT_OPTSTRING('D', "debug", &debug_str  , "STR"    , _("Debug")),
++        OPT_END()
++    };
++
++    unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
++    argv += optind;
++
++    export_abrt_envvars(0);
++
++    map_string_t *settings = new_map_string();
++
++    {
++        if (!conf_file)
++            conf_file = g_list_append(conf_file, (char*) CONF_DIR"/plugins/mantisbt.conf");
++        while (conf_file)
++        {
++            char *fn = (char *)conf_file->data;
++            log_notice("Loading settings from '%s'", fn);
++            load_conf_file(fn, settings, /*skip key w/o values:*/ false);
++            log_debug("Loaded '%s'", fn);
++            conf_file = g_list_delete_link(conf_file, conf_file);
++        }
++
++        struct dump_dir *dd = NULL;
++        if (abrt_hash == NULL)
++        {
++            dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
++            if (!dd)
++                error_msg_and_die(_("Can't open problem dir '%s'."), dump_dir_name);
++        }
++
++        set_settings(&mbt_settings, settings, dd);
++        dd_close(dd);
++        /* WRONG! set_settings() does not copy the strings, it merely sets up pointers
++         * to settings[] dictionary:
++         */
++        /*free_map_string(settings);*/
++    }
++
++    /* No connection is opened between client and server. Users authentication
++     * is performed on every SOAP method call. In the first step we verify the
++     * credentials by calling 'mc_login' method.  In the case the credentials are
++     * correctly applies the reporter uses them in the next requests. It is not
++     * necessary to call 'mc_login' method because the method provides only
++     * verification of credentials.
++     */
++    verify_credentials(&mbt_settings);
++
++    if (abrt_hash)
++    {
++        log(_("Looking for similar problems in CentOS Bug Tracker"));
++        GList *ids = mantisbt_search_by_abrt_hash(&mbt_settings, abrt_hash);
++        mantisbt_settings_free(&mbt_settings);
++
++        if (ids == NULL)
++            return EXIT_FAILURE;
++
++        puts(ids->data);
++        response_values_free(ids);
++        return EXIT_SUCCESS;
++    }
++
++    mantisbt_get_project_id_from_name(&mbt_settings);
++
++    if (opts & OPT_t)
++    {
++        if (!argv[0])
++            show_usage_and_die(program_usage_string, program_options);
++
++        if (!ticket_no)
++        {
++            struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
++            if (!dd)
++                xfunc_die();
++            report_result_t *reported_to = find_in_reported_to(dd, "CentOS Bug Tracker");
++            dd_close(dd);
++
++            if (!reported_to || !reported_to->url)
++                error_msg_and_die(_("Can't get MantisBT ID because this problem has not yet been reported to MantisBT."));
++
++            char *url = reported_to->url;
++            reported_to->url = NULL;
++            free_report_result(reported_to);
++
++            if (prefixcmp(url, mbt_settings.m_mantisbt_url) != 0)
++                error_msg_and_die(_("This problem has been reported to MantisBT '%s' which differs from the configured MantisBT '%s'."), url, mbt_settings.m_mantisbt_url);
++
++            ticket_no = strrchr(url, '=');
++            if (!ticket_no)
++                error_msg_and_die(_("Malformed url to MantisBT '%s'."), url);
++
++            /* won't ever call free on it - it simplifies the code a lot */
++            ticket_no = xstrdup(ticket_no + 1);
++            log(_("Using CentOS Bug Tracker ID '%s'"), ticket_no);
++        }
++
++        /* Attach files to existing MantisBT issues */
++        while (*argv)
++        {
++            const char *path = *argv++;
++            char *filename = basename(path);
++            log(_("Attaching file '%s' to issue %s"), filename, ticket_no);
++            mantisbt_attach_file(&mbt_settings, ticket_no, filename, path);
++        }
++
++        return 0;
++    }
++
++    /* Create new issue in MantisBT */
++
++    if (!(opts & OPT_f))
++    {
++        struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
++        if (!dd)
++            xfunc_die();
++        report_result_t *reported_to = find_in_reported_to(dd, "CentOS Bug Tracker");
++        dd_close(dd);
++
++        if (reported_to && reported_to->url)
++        {
++            char *msg = xasprintf(_("This problem was already reported to CentOS Bug Tracker (see '%s')."
++                            " Do you still want to create a new issue?"),
++                            reported_to->url);
++            int yes = ask_yes_no(msg);
++            free(msg);
++            if (!yes)
++                return 0;
++        }
++        free_report_result(reported_to);
++    }
++
++    problem_data_t *problem_data = create_problem_data_for_reporting(dump_dir_name);
++    if (!problem_data)
++        xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */
++
++    const char *category = problem_data_get_content_or_die(problem_data, FILENAME_COMPONENT);
++    const char *duphash   = problem_data_get_content_or_die(problem_data, FILENAME_DUPHASH);
++
++    if (opts & OPT_D)
++    {
++        problem_formatter_t *pf = problem_formatter_new();
++        problem_formatter_add_section(pf, PR_SEC_ADDITIONAL_INFO, /* optional section */ 0);
++
++        if (problem_formatter_load_file(pf, fmt_file))
++            error_msg_and_die("Invalid format file: %s", fmt_file);
++
++        problem_report_t *pr = NULL;
++        if (problem_formatter_generate_report(pf, problem_data, &pr))
++            error_msg_and_die("Failed to format issue report from problem data");
++
++        printf("summary: %s\n"
++                "\n"
++                "Description:\n%s\n"
++                "Additional info:\n%s\n"
++                , problem_report_get_summary(pr)
++                , problem_report_get_description(pr)
++                , problem_report_get_section(pr, PR_SEC_ADDITIONAL_INFO)
++        );
++
++        puts("attachments:");
++        for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a))
++            printf(" %s\n", (const char *)a->data);
++
++        problem_report_free(pr);
++        problem_formatter_free(pf);
++        exit(0);
++    }
++
++     int bug_id = 0;
++
++    /* If REMOTE_RESULT contains "DUPLICATE 12345", we consider it a dup of 12345
++     * and won't search on MantisBT server.
++     */
++    char *remote_result;
++    remote_result = problem_data_get_content_or_NULL(problem_data, FILENAME_REMOTE_RESULT);
++    if (remote_result)
++    {
++        char *cmd = strtok(remote_result, " \n");
++        char *id = strtok(NULL, " \n");
++
++        if (!prefixcmp(cmd, "DUPLICATE"))
++        {
++            errno = 0;
++            char *e;
++            bug_id = strtoul(id, &e, 10);
++            if (errno || id == e || *e != '\0' || bug_id > INT_MAX)
++            {
++                /* error / no digits / illegal trailing chars / too big a number */
++                bug_id = 0;
++            }
++        }
++    }
++
++    mantisbt_issue_info_t *ii;
++    if (!bug_id)
++    {
++        log(_("Checking for duplicates"));
++
++        int existing_id = -1;
++        int crossver_id = -1;
++        {
++            /* Figure out whether we want to match category
++             * when doing dup search.
++             */
++            const char *category_substitute = is_in_comma_separated_list(category, mbt_settings.m_DontMatchComponents) ? NULL : category;
++
++            /* We don't do dup detection across versions (see below why),
++             * but we do add a note if cross-version potential dup exists.
++             * For that, we search for cross version dups first:
++             */
++            // SOAP API searching method is not in the final version, it's possible the project will be string
++            GList *crossver_bugs_ids = mantisbt_search_duplicate_issues(&mbt_settings, category_substitute, /*version*/ NULL, duphash);
++
++            unsigned crossver_bugs_count = g_list_length(crossver_bugs_ids);
++            log_debug("CentOS Bug Tracker has %i reports with duphash '%s' including cross-version ones",
++                    crossver_bugs_count, duphash);
++            if (crossver_bugs_count > 0)
++                crossver_id = atoi(g_list_first(crossver_bugs_ids)->data);
++
++            if (crossver_bugs_count > 0)
++            {
++                // SOAP API searching method is not in the final version, it's possible the project will be string
++                GList *dup_bugs_ids = mantisbt_search_duplicate_issues(&mbt_settings, category_substitute, mbt_settings.m_project_version, duphash);
++
++                unsigned dup_bugs_count =  g_list_length(dup_bugs_ids);
++                log_debug("CentOS Bug Tracker has %i reports with duphash '%s'",
++                        dup_bugs_count, duphash);
++                if (dup_bugs_count > 0)
++                    existing_id = atoi(g_list_first(dup_bugs_ids)->data);
++            }
++        }
++
++        if (existing_id < 0)
++        {
++            /* Create new issue */
++            log(_("Creating a new issue"));
++            problem_formatter_t *pf = problem_formatter_new();
++            problem_formatter_add_section(pf, PR_SEC_ADDITIONAL_INFO, 0);
++
++            if (problem_formatter_load_file(pf, fmt_file))
++                error_msg_and_die(_("Invalid format file: %s"), fmt_file);
++
++            problem_report_t *pr = NULL;
++            if (problem_formatter_generate_report(pf, problem_data, &pr))
++                error_msg_and_die(_("Failed to format problem data"));
++
++            if (crossver_id >= 0)
++                problem_report_buffer_printf(
++                        problem_report_get_buffer(pr, PR_SEC_DESCRIPTION),
++                        "\nPotential duplicate: issue %u\n", crossver_id);
++
++            problem_formatter_free(pf);
++
++            /* get tracker URL if exists */
++            struct dump_dir *dd = dd_opendir(dump_dir_name, 0);
++            char *tracker_url = NULL;
++            if (dd)
++            {
++                report_result_t *reported_to = find_in_reported_to(dd, tracker_str);
++                dd_close(dd);
++
++                if (reported_to && reported_to->url)
++                {
++                    log(_("Adding External URL to issue"));
++                    tracker_url = xstrdup(reported_to->url);
++                    free_report_result(reported_to);
++                }
++            }
++
++            int new_id = mantisbt_create_new_issue(&mbt_settings, problem_data, pr, tracker_url);
++
++            free(tracker_url);
++
++            if (new_id == -1)
++                return EXIT_FAILURE;
++
++            log(_("Adding attachments to issue %i"), new_id);
++            char *new_id_str = xasprintf("%u", new_id);
++
++            for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a))
++            {
++                const char *item_name = (const char *)a->data;
++                struct problem_item *item = problem_data_get_item_or_NULL(problem_data, item_name);
++                if (!item)
++                    continue;
++                else if (item->flags & CD_FLAG_TXT)
++                    mantisbt_attach_data(&mbt_settings, new_id_str, item_name, item->content, strlen(item->content));
++                else if (item->flags & CD_FLAG_BIN)
++                    mantisbt_attach_file(&mbt_settings, new_id_str, item_name, item->content);
++            }
++
++            free(new_id_str);
++            problem_report_free(pr);
++            ii = mantisbt_issue_info_new();
++            ii->mii_id = new_id;
++            ii->mii_status = xstrdup("new");
++
++            goto finish;
++        }
++
++        bug_id = existing_id;
++    }
++
++    ii = mantisbt_get_issue_info(&mbt_settings, bug_id);
++
++    log(_("Bug is already reported: %i"), ii->mii_id);
++
++    /* Follow duplicates */
++    if ((strcmp(ii->mii_status, "closed") == 0)
++     && (strcmp(ii->mii_resolution, "duplicate") == 0)
++    ) {
++        mantisbt_issue_info_t *origin = mantisbt_find_origin_bug_closed_duplicate(&mbt_settings, ii);
++        if (origin)
++        {
++            mantisbt_issue_info_free(ii);
++            ii = origin;
++        }
++    }
++
++    /* TODO CC list
++     * Is no MantisBT SOAP API method which allows adding users to CC list
++     * without updating issue.
++     */
++
++    /* Add comment and bt */
++    const char *comment = problem_data_get_content_or_NULL(problem_data, FILENAME_COMMENT);
++    if (comment && comment[0])
++    {
++        problem_formatter_t *pf = problem_formatter_new();
++
++        if (problem_formatter_load_file(pf, fmt_file2))
++            error_msg_and_die(_("Invalid duplicate format file: '%s"), fmt_file2);
++
++        problem_report_t *pr;
++        if (problem_formatter_generate_report(pf, problem_data, &pr))
++            error_msg_and_die(_("Failed to format duplicate comment from problem data"));
++
++        const char *mbtcomment = problem_report_get_description(pr);
++
++        int dup_comment = is_comment_dup(ii->mii_notes, mbtcomment);
++        if (!dup_comment)
++        {
++            log(_("Adding new comment to issue %d"), ii->mii_id);
++            mantisbt_add_issue_note(&mbt_settings, ii->mii_id, mbtcomment);
++
++            const char *bt = problem_data_get_content_or_NULL(problem_data, FILENAME_BACKTRACE);
++            unsigned rating = 0;
++            const char *rating_str = problem_data_get_content_or_NULL(problem_data, FILENAME_RATING);
++            /* python doesn't have rating file */
++            if (rating_str)
++                rating = xatou(rating_str);
++            if (bt && rating > ii->mii_best_bt_rating)
++            {
++                char *bug_id_str = xasprintf("%i", ii->mii_id);
++
++                log(_("Attaching better backtrace"));
++
++                // find unique filename of attachment
++                char *name = NULL;
++                for (int i = 0;; ++i)
++                {
++                    if (i == 0)
++                        name = xasprintf("%s", FILENAME_BACKTRACE);
++                    else
++                        name = xasprintf("%s%d", FILENAME_BACKTRACE, i);
++
++                    if (g_list_find_custom(ii->mii_attachments, name, (GCompareFunc) strcmp) == NULL)
++                        break;
++
++                    free(name);
++                }
++                mantisbt_attach_data(&mbt_settings, bug_id_str, name, bt, strlen(bt));
++
++                free(name);
++                free(bug_id_str);
++            }
++        }
++        else
++            log(_("Found the same comment in the issue history, not adding a new one"));
++
++        problem_report_free(pr);
++        problem_formatter_free(pf);
++    }
++
++finish:
++    log(_("Status: %s%s%s %s/view.php?id=%u"),
++                ii->mii_status,
++                ii->mii_resolution ? " " : "",
++                ii->mii_resolution ? ii->mii_resolution : "",
++                mbt_settings.m_mantisbt_url,
++                ii->mii_id);
++
++    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
++    if (dd)
++    {
++        char *msg = xasprintf("CentOS Bug Tracker: URL=%s/view.php?id=%u", mbt_settings.m_mantisbt_url, ii->mii_id);
++        add_reported_to(dd, msg);
++        free(msg);
++        dd_close(dd);
++    }
++
++    mantisbt_settings_free(&mbt_settings);
++    return 0;
++}
+diff --git a/src/workflows/Makefile.am b/src/workflows/Makefile.am
+index 72502ca..7ecb34e 100644
+--- a/src/workflows/Makefile.am
++++ b/src/workflows/Makefile.am
+@@ -23,6 +23,18 @@ dist_workflows_DATA = \
+     workflow_Logger.xml \
+     workflow_LoggerCCpp.xml
+ 
++if BUILD_MANTISBT
++dist_workflows_DATA += \
++    workflow_CentOSCCpp.xml \
++    workflow_CentOSKerneloops.xml \
++    workflow_CentOSPython.xml \
++    workflow_CentOSPython3.xml \
++    workflow_CentOSVmcore.xml \
++    workflow_CentOSXorg.xml \
++    workflow_CentOSLibreport.xml \
++    workflow_CentOSJava.xml
++endif
++
+ if BUILD_BUGZILLA
+ dist_workflows_DATA += \
+     workflow_AnacondaFedora.xml \
+@@ -46,7 +58,8 @@ dist_workflowsdef_DATA =\
+     report_uReport.conf \
+     report_mailx.conf \
+     report_logger.conf \
+-    report_uploader.conf
++    report_uploader.conf \
++    report_centos.conf
+ 
+ if BUILD_BUGZILLA
+ dist_workflowsdef_DATA += \
+diff --git a/src/workflows/report_centos.conf b/src/workflows/report_centos.conf
+new file mode 100644
+index 0000000..07b0d40
+--- /dev/null
++++ b/src/workflows/report_centos.conf
+@@ -0,0 +1,31 @@
++EVENT=workflow_CentOSLibreport analyzer=libreport
++# this is just a meta event which consists of other events
++# the list is defined in the xml file
++
++EVENT=workflow_CentOSCCpp analyzer=CCpp
++# this is just a meta event which consists of other events
++# the list is defined in the xml file
++
++EVENT=workflow_CentOSPython analyzer=Python component!=anaconda
++# this is just a meta event which consists of other events
++# the list is defined in the xml file
++
++EVENT=workflow_CentOSPython3 analyzer=Python3 component!=anaconda
++# this is just a meta event which consists of other events
++# the list is defined in the xml file
++
++EVENT=workflow_CentOSKerneloops analyzer=Kerneloops
++# this is just a meta event which consists of other events
++# the list is defined in the xml file
++
++EVENT=workflow_CentOSVmcore analyzer=vmcore
++# this is just a meta event which consists of other events
++# the list is defined in the xml file
++
++EVENT=workflow_CentOSXorg analyzer=xorg
++# this is just a meta event which consists of other events
++# the list is defined in the xml file
++
++EVENT=workflow_CentOSJava analyzer=Java
++# this is just a meta event which consists of other events
++# the list is defined in the xml file
+diff --git a/src/workflows/workflow_CentOSCCpp.xml.in b/src/workflows/workflow_CentOSCCpp.xml.in
+new file mode 100644
+index 0000000..ea94d56
+--- /dev/null
++++ b/src/workflows/workflow_CentOSCCpp.xml.in
+@@ -0,0 +1,12 @@
++<?xml version="1.0" encoding="UTF-8" ?>
++<workflow>
++    <_name>Report to CentOS Bug Tracker</_name>
++    <_description>Process the C/C++ crash using the CentOS infrastructure</_description>
++
++    <events>
++        <event>report_uReport</event>
++        <event>collect_*</event>
++        <event>analyze_CCpp</event>
++        <event>report_CentOSBugTracker</event>
++    </events>
++</workflow>
+diff --git a/src/workflows/workflow_CentOSJava.xml.in b/src/workflows/workflow_CentOSJava.xml.in
+new file mode 100644
+index 0000000..89f9295
+--- /dev/null
++++ b/src/workflows/workflow_CentOSJava.xml.in
+@@ -0,0 +1,11 @@
++<?xml version="1.0" encoding="UTF-8" ?>
++<workflow>
++    <_name>Report to CentOS Bug Tracker</_name>
++    <_description>Process the Java exception using the CentOS infrastructure</_description>
++
++    <events>
++        <event>report_uReport</event>
++        <event>collect_*</event>
++        <event>report_CentOSBugTracker</event>
++    </events>
++</workflow>
+diff --git a/src/workflows/workflow_CentOSKerneloops.xml.in b/src/workflows/workflow_CentOSKerneloops.xml.in
+new file mode 100644
+index 0000000..c43c681
+--- /dev/null
++++ b/src/workflows/workflow_CentOSKerneloops.xml.in
+@@ -0,0 +1,11 @@
++<?xml version="1.0" encoding="UTF-8" ?>
++<workflow>
++    <_name>Report to CentOS Bug Tracker</_name>
++    <_description>Process the kerneloops using the CentOS infrastructure</_description>
++
++    <events>
++        <event>report_uReport</event>
++        <event>collect_*</event>
++        <event>report_CentOSBugTracker</event>
++    </events>
++</workflow>
+diff --git a/src/workflows/workflow_CentOSLibreport.xml.in b/src/workflows/workflow_CentOSLibreport.xml.in
+new file mode 100644
+index 0000000..2f6ed82
+--- /dev/null
++++ b/src/workflows/workflow_CentOSLibreport.xml.in
+@@ -0,0 +1,9 @@
++<?xml version="1.0" encoding="UTF-8" ?>
++<workflow>
++    <_name>Report to CentOS Bug Tracker</_name>
++    <_description>Process the problem using the CentOS infrastructure</_description>
++
++    <events>
++        <event>report_CentOSBugTracker</event>
++    </events>
++</workflow>
+diff --git a/src/workflows/workflow_CentOSPython.xml.in b/src/workflows/workflow_CentOSPython.xml.in
+new file mode 100644
+index 0000000..7e26c6c
+--- /dev/null
++++ b/src/workflows/workflow_CentOSPython.xml.in
+@@ -0,0 +1,11 @@
++<?xml version="1.0" encoding="UTF-8" ?>
++<workflow>
++    <_name>Report to CentOS Bug Tracker</_name>
++    <_description>Process the python exception using the CentOS infrastructure</_description>
++
++    <events>
++        <event>report_uReport</event>
++        <event>collect_*</event>
++        <event>report_CentOSBugTracker</event>
++    </events>
++</workflow>
+diff --git a/src/workflows/workflow_CentOSPython3.xml.in b/src/workflows/workflow_CentOSPython3.xml.in
+new file mode 100644
+index 0000000..f1dd8a9
+--- /dev/null
++++ b/src/workflows/workflow_CentOSPython3.xml.in
+@@ -0,0 +1,11 @@
++<?xml version="1.0" encoding="UTF-8" ?>
++<workflow>
++    <_name>Report to CentOS Bug Tracker</_name>
++    <_description>Process the python 3 exception using the CentOS infrastructure</_description>
++
++    <events>
++        <event>report_uReport</event>
++        <event>collect_*</event>
++        <event>report_CentOSBugTracker</event>
++    </events>
++</workflow>
+diff --git a/src/workflows/workflow_CentOSVmcore.xml.in b/src/workflows/workflow_CentOSVmcore.xml.in
+new file mode 100644
+index 0000000..c876594
+--- /dev/null
++++ b/src/workflows/workflow_CentOSVmcore.xml.in
+@@ -0,0 +1,12 @@
++<?xml version="1.0" encoding="UTF-8" ?>
++<workflow>
++    <_name>Report to CentOS Bug Tracker</_name>
++    <_description>Process the kernel crash using the CentOS infrastructure</_description>
++
++    <events>
++        <event>analyze_VMcore</event>
++        <event>report_uReport</event>
++        <event>collect_*</event>
++        <event>report_CentOSBugTracker</event>
++    </events>
++</workflow>
+diff --git a/src/workflows/workflow_CentOSXorg.xml.in b/src/workflows/workflow_CentOSXorg.xml.in
+new file mode 100644
+index 0000000..bf52221
+--- /dev/null
++++ b/src/workflows/workflow_CentOSXorg.xml.in
+@@ -0,0 +1,9 @@
++<?xml version="1.0" encoding="UTF-8" ?>
++<workflow>
++    <_name>Report to CentOS Bug Tracker</_name>
++    <_description>Process the X Server problem using the CentOS infrastructure</_description>
++
++    <events>
++        <event>report_CentOSBugTracker</event>
++    </events>
++</workflow>
+-- 
+1.8.3.1
+
diff --git a/SOURCES/1007-reporter-mantisbt-change-default-formating-file-for-.patch b/SOURCES/1007-reporter-mantisbt-change-default-formating-file-for-.patch
new file mode 100644
index 0000000..6f1777a
--- /dev/null
+++ b/SOURCES/1007-reporter-mantisbt-change-default-formating-file-for-.patch
@@ -0,0 +1,43 @@
+From 8d54f840cdb094ca8e32f02e967393d7498ee26d Mon Sep 17 00:00:00 2001
+From: Matej Habrnal <mhabrnal@redhat.com>
+Date: Fri, 20 Feb 2015 00:27:05 +0100
+Subject: [PATCH 1007/1015] reporter-mantisbt: change default formating file
+ for duplicate issues
+
+reporter-mantisbt doesn't work well with mantisbt_format.conf as a default
+format conf file for creating duplicate issues because the note which is added
+doesn't contain an 'Additional information' section.
+
+Default formating file for duplicate is mantisbt_formatdup.conf
+
+Signed-off-by: Matej Habrnal <mhabrnal@redhat.com>
+---
+ src/plugins/reporter-mantisbt.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/plugins/reporter-mantisbt.c b/src/plugins/reporter-mantisbt.c
+index d281cdb..dc9968f 100644
+--- a/src/plugins/reporter-mantisbt.c
++++ b/src/plugins/reporter-mantisbt.c
+@@ -253,7 +253,8 @@ int main(int argc, char **argv)
+         "\nRecognized boolean parameter (VALUE should be 1/0, yes/no): SSLVerify, CreatePrivate."
+         "\nParameters can be overridden via $Mantisbt_PARAM environment variables."
+         "\n"
+-        "\nFMTFILE and FMTFILE2 default to "CONF_DIR"/plugins/mantisbt_format.conf"
++        "\nFMTFILE default to "CONF_DIR"/plugins/mantisbt_format.conf."
++        "\nFMTFILE2 default to "CONF_DIR"/plugins/mantisbt_formatdup.conf."
+     );
+ 
+     enum {
+@@ -272,7 +273,7 @@ int main(int argc, char **argv)
+     const char *dump_dir_name = ".";
+     GList *conf_file = NULL;
+     const char *fmt_file = CONF_DIR"/plugins/mantisbt_format.conf";
+-    const char *fmt_file2 = fmt_file;
++    const char *fmt_file2 = CONF_DIR"/plugins/mantisbt_formatdup.conf";
+     char *abrt_hash = NULL;
+     char *ticket_no = NULL;
+     const char *tracker_str = "ABRT Server";
+-- 
+1.8.3.1
+
diff --git a/SOURCES/1009-reporter-mantisbt-adds-man-pages-for-reporter-mantis.patch b/SOURCES/1009-reporter-mantisbt-adds-man-pages-for-reporter-mantis.patch
new file mode 100644
index 0000000..efb3a9f
--- /dev/null
+++ b/SOURCES/1009-reporter-mantisbt-adds-man-pages-for-reporter-mantis.patch
@@ -0,0 +1,528 @@
+From 9c819b8c77ea5c05e3a37117a2174162160b1c57 Mon Sep 17 00:00:00 2001
+From: Matej Habrnal <mhabrnal@redhat.com>
+Date: Fri, 20 Feb 2015 03:14:54 +0100
+Subject: [PATCH 1009/1015] reporter-mantisbt: adds man pages for
+ reporter-mantisbt
+
+Signed-off-by: Matej Habrnal <mhabrnal@redhat.com>
+
+Conflicts:
+	doc/Makefile.am
+---
+ doc/Makefile.am                      |  13 ++-
+ doc/centos_report_event.conf.5       |   1 +
+ doc/mantisbt.conf.txt                |  18 +++
+ doc/mantisbt_format.conf.txt         |  18 +++
+ doc/mantisbt_formatdup.conf.txt      |  18 +++
+ doc/report_CentOSBugTracker.conf.txt |  45 +++++++
+ doc/report_centos.conf.txt           |  41 +++++++
+ doc/reporter-mantisbt.txt            | 219 +++++++++++++++++++++++++++++++++++
+ src/plugins/Makefile.am              |   4 +
+ src/workflows/Makefile.am            |  12 ++
+ 10 files changed, 387 insertions(+), 2 deletions(-)
+ create mode 100644 doc/centos_report_event.conf.5
+ create mode 100644 doc/mantisbt.conf.txt
+ create mode 100644 doc/mantisbt_format.conf.txt
+ create mode 100644 doc/mantisbt_formatdup.conf.txt
+ create mode 100644 doc/report_CentOSBugTracker.conf.txt
+ create mode 100644 doc/report_centos.conf.txt
+ create mode 100644 doc/reporter-mantisbt.txt
+
+diff --git a/doc/Makefile.am b/doc/Makefile.am
+index 83a41d9..e437388 100644
+--- a/doc/Makefile.am
++++ b/doc/Makefile.am
+@@ -25,6 +25,7 @@ MAN1_TXT += reporter-print.txt
+ MAN1_TXT += reporter-rhtsupport.txt
+ MAN1_TXT += reporter-upload.txt
+ MAN1_TXT += reporter-ureport.txt
++MAN1_TXT += reporter-mantisbt.txt
+ 
+ MAN5_TXT =
+ MAN5_TXT += anaconda_event.conf.txt
+@@ -37,6 +38,9 @@ MAN5_TXT += bugzilla_formatdup_anaconda.conf.txt
+ MAN5_TXT += bugzilla_formatdup.conf.txt
+ MAN5_TXT += bugzilla_format_kernel.conf.txt
+ MAN5_TXT += bugzilla_format_libreport.conf.txt
++MAN5_TXT += mantisbt.conf.txt
++MAN5_TXT += mantisbt_format.conf.txt
++MAN5_TXT += mantisbt_formatdup.conf.txt
+ MAN5_TXT += emergencyanalysis_event.conf.txt
+ MAN5_TXT += forbidden_words.conf.txt
+ MAN5_TXT += mailx.conf.txt
+@@ -45,6 +49,7 @@ MAN5_TXT += print_event.conf.txt
+ MAN5_TXT += report_Bugzilla.conf.txt
+ MAN5_TXT += report_event.conf.txt
+ MAN5_TXT += report_fedora.conf.txt
++MAN5_TXT += report_centos.conf.txt
+ MAN5_TXT += report_Logger.conf.txt
+ MAN5_TXT += report_rhel.conf.txt
+ MAN5_TXT += report_rhel_bugzilla.conf.txt
+@@ -53,15 +58,19 @@ MAN5_TXT += report_logger.conf.txt
+ MAN5_TXT += report_mailx.conf.txt
+ MAN5_TXT += report_uploader.conf.txt
+ MAN5_TXT += report_Uploader.conf.txt
++MAN5_TXT += report_CentOSBugTracker.conf.txt
+ MAN5_TXT += rhtsupport.conf.txt
+ MAN5_TXT += rhtsupport_event.conf.txt
+ MAN5_TXT += uploader_event.conf.txt
+ MAN5_TXT += ureport.conf.txt
+ MAN5_TXT += upload.conf.txt
+ 
++MAN5_PREFORMATTED =
++MAN5_PREFORMATTED += centos_report_event.conf.5
++
+ # Manual pages are generated from .txt via Docbook
+ man1_MANS = ${MAN1_TXT:%.txt=%.1}
+-man5_MANS = ${MAN5_TXT:%.txt=%.5}
++man5_MANS = ${MAN5_TXT:%.txt=%.5} ${MAN5_PREFORMATTED}
+ 
+ SUFFIXES = .1 .5
+ 
+@@ -76,5 +85,5 @@ SUFFIXES = .1 .5
+                                     --conf-file ../asciidoc.conf \
+                                     -alibreport_version=$(PACKAGE_VERSION) -o $@ $<
+ 
+-EXTRA_DIST = $(MAN1_TXT) $(MAN5_TXT)
++EXTRA_DIST = $(MAN1_TXT) $(MAN5_TXT) $(MAN5_PREFORMATTED)
+ CLEANFILES = $(man1_MANS)
+diff --git a/doc/centos_report_event.conf.5 b/doc/centos_report_event.conf.5
+new file mode 100644
+index 0000000..71c3fcb
+--- /dev/null
++++ b/doc/centos_report_event.conf.5
+@@ -0,0 +1 @@
++.so man5/report_event.conf.5
+diff --git a/doc/mantisbt.conf.txt b/doc/mantisbt.conf.txt
+new file mode 100644
+index 0000000..d4ba605
+--- /dev/null
++++ b/doc/mantisbt.conf.txt
+@@ -0,0 +1,18 @@
++mantisbt.conf(5)
++===============
++
++NAME
++----
++mantisbt.conf - configuration file for libreport.
++
++DESCRIPTION
++-----------
++This configuration file provides default configuration for 'reporter-mantisbt'.
++
++SEE ALSO
++--------
++reporter-mantisbt(1)
++
++AUTHOR
++------
++* ABRT team
+diff --git a/doc/mantisbt_format.conf.txt b/doc/mantisbt_format.conf.txt
+new file mode 100644
+index 0000000..860d911
+--- /dev/null
++++ b/doc/mantisbt_format.conf.txt
+@@ -0,0 +1,18 @@
++mantisbt_format.conf(5)
++=======================
++
++NAME
++----
++mantisbt_format.conf - configuration file for libreport.
++
++DESCRIPTION
++-----------
++This configuration file provides definition of general formatting for new MantisBT issues.
++
++SEE ALSO
++--------
++reporter-mantisbt(1)
++
++AUTHOR
++------
++* ABRT Team
+diff --git a/doc/mantisbt_formatdup.conf.txt b/doc/mantisbt_formatdup.conf.txt
+new file mode 100644
+index 0000000..a617226
+--- /dev/null
++++ b/doc/mantisbt_formatdup.conf.txt
+@@ -0,0 +1,18 @@
++mantisbt_formatdup.conf(5)
++==========================
++
++NAME
++----
++mantisbt_formatdup.conf - configuration file for libreport.
++
++DESCRIPTION
++-----------
++This configuration file provides definition of general formatting for duplicate MantisBT issues.
++
++SEE ALSO
++--------
++reporter-mantisbt(1)
++
++AUTHOR
++------
++* ABRT Team
+diff --git a/doc/report_CentOSBugTracker.conf.txt b/doc/report_CentOSBugTracker.conf.txt
+new file mode 100644
+index 0000000..6ba35d3
+--- /dev/null
++++ b/doc/report_CentOSBugTracker.conf.txt
+@@ -0,0 +1,45 @@
++report_CentOSBugTracker.conf(5)
++===============================
++
++NAME
++----
++report_CentOSBugTracker.conf - libreport's configuration file for 'report_CentOSBugTracker' events.
++
++DESCRIPTION
++-----------
++This configuration file contains values for options defined in
++/usr/share/libreport/events/report_CentOSBugTracker.xml
++
++Configuration file lines should have 'PARAM = VALUE' format. The parameters are:
++
++'Mantisbt_Login'::
++	Login to MantisBT account.
++
++'Mantisbt_Password'::
++	Password to MantisBT account.
++
++'Mantisbt_MantisbtURL'::
++	MantisBT HTTP(S) address. (default: https://bugs.centos.org)
++
++'Mantisbt_SSLVerify'::
++	Use yes/true/on/1 to verify server's SSL certificate. (default: yes)
++
++'Mantisbt_Project'::
++	Project issue field value. Useful if you needed different project than specified in /etc/os-release
++
++'Mantisbt_ProjectVersion'::
++	Version issue field value. Useful if you needed different project version than specified in /etc/os-release
++
++'http_proxy'::
++	the proxy server to use for HTTP
++
++'HTTPS_PROXY'::
++	the proxy server to use for HTTPS
++
++SEE ALSO
++--------
++report_event.conf(5), reporter-mantisbt(1)
++
++AUTHOR
++------
++* ABRT team
+diff --git a/doc/report_centos.conf.txt b/doc/report_centos.conf.txt
+new file mode 100644
+index 0000000..23a5fde
+--- /dev/null
++++ b/doc/report_centos.conf.txt
+@@ -0,0 +1,41 @@
++report_centos.conf(5)
++=====================
++
++NAME
++----
++report_centos.conf - configuration file for libreport.
++
++DESCRIPTION
++-----------
++This configuration file specifies which of the reporting work flow definitions
++are applicable for all problems types on CentOS.
++
++All applicable work flows are presented to users in User Interface as
++possibilities for processing of any problems. A particular work flow becomes
++applicable if its conditions are satisfied.
++
++This configuration file consists from one condition per line.
++
++Each condition line must start with EVENT=workflow_NAME where "workflow_" is
++constant prefix and "workflow_NAME" is base name of path to reporting work flow
++configuration file.
++
++The rest of condition line has form VAR=VAL, VAR!=VAL or VAL~=REGEX, where VAR
++is a name of problem directory element to be checked (for example,
++"executable", "package", hostname" etc). The condition may consists
++from as many element checks as it is necessary.
++
++EXAMPLES
++--------
++Condition line::
++    EVENT=workflow_CentOSCCpp analyzer=CCpp
++
++The condition line above expects existence of /usr/share/libreport/workflows/workflow_CentOSCCpp.xml
++
++SEE ALSO
++--------
++report-gtk(1)
++
++AUTHOR
++------
++* ABRT team
+diff --git a/doc/reporter-mantisbt.txt b/doc/reporter-mantisbt.txt
+new file mode 100644
+index 0000000..92255b0
+--- /dev/null
++++ b/doc/reporter-mantisbt.txt
+@@ -0,0 +1,219 @@
++reporter-mantisbt(1)
++====================
++
++NAME
++----
++reporter-mantisbt - Reports problem to Mantis Bug Tracker.
++
++SYNOPSIS
++--------
++'reporter-mantisbt' [-vrf] [-c CONFFILE]... [-F FMTFILE] [-A FMTFILE2] -d DIR
++
++Or:
++
++'reporter-mantisbt' [-v] [-c CONFFILE]... [-d DIR] -t[ID] FILE...
++
++Or:
++
++'reporter-mantisbt' [-v] [-c CONFFILE]... -h DUPHASH
++
++DESCRIPTION
++-----------
++The tool reads problem directory DIR. Then it logs in to MantisBT
++and tries to find an issue with the same duphash HEXSTRING in 'abrt_hash' field.
++
++If such issue is not found, then a new issue is created. Elements of DIR
++are stored in the issue as part of issue description or as attachments,
++depending on their type and size.
++
++Otherwise, if such issue is found and it is marked as CLOSED DUPLICATE,
++the tool follows the chain of duplicates until it finds a non-DUPLICATE issue.
++The tool adds a new note to found issue.
++
++The URL to new or modified issue is printed to stdout and recorded in
++'reported_to' element in DIR.
++
++Option -t uploads FILEs to the already created issue on MantisBT site.
++The issue ID is retrieved from directory specified by -d DIR.
++If problem data in DIR was never reported to MantisBT, upload will fail.
++
++Option -tID uploads FILEs to the issue with specified ID on MantisBT site.
++-d DIR is ignored.
++
++Option -r sets the last url from reporter_to element which is prefixed with
++TRACKER_NAME to URL field. This option is applied only when a new issue is to be
++filed. The default value is 'ABRT Server'"
++
++Configuration file
++~~~~~~~~~~~~~~~~~~
++If not specified, CONFFILE defaults to /etc/libreport/plugins/mantisbt.conf.
++Configuration file lines should have 'PARAM = VALUE' format. The parameters are:
++
++'Login'::
++	Login to MantisBT account.
++
++'Password'::
++	Password to MantisBT account.
++
++'MantisbtURL'::
++	MantisBT HTTP(S) address. (default: http://localhost/mantisbt)
++
++'SSLVerify'::
++	Use yes/true/on/1 to verify server's SSL certificate. (default: no)
++
++'Project'::
++	Project issue field value. Useful if you needed different project than specified in /etc/os-release
++
++'ProjectVersion'::
++	Version issue field value. Useful if you needed different project version than specified in /etc/os-release
++
++'CreatePrivate'::
++    Create private MantisBT issue. (default: no)
++
++Parameters can be overridden via $Mantisbt_PARAM environment variables.
++
++Formatting configuration files
++~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++Lines starting with # are ignored.
++
++Lines can be continued on the next line using trailing backslash.
++
++Format:
++
++   "%summary:: summary format"
++   "section:: element1[,element2]..."
++   The literal text line to be added to MantisBT Description or Additional information. Can be empty.
++   (Empty lines are NOT ignored!)
++
++   Summary format is a line of text, where %element% is replaced by
++   text element's content, and [[...%element%...]] block is used only if
++   %element% exists. [[...]] blocks can nest.
++
++   Sections can be:
++   - %summary: issue Summary format string.
++   - %attach: a list of elements to attach.
++   - %Additional info: issue Additional Information content.
++   - text, double colon (::) and the list of comma-separated elements.
++
++   Description and Additional information MantisBT's fields:
++   All text, double colons (::) and lists of comma-separated elements which
++   are placed above the section '%Additional info::' in the configuration file are
++   stored in the 'Description' field in MantisBT. All text etc. which are placed
++   under the '%Additional info::' are stored in the 'Additional information' field.
++
++   For example:
++   |:: comment               |  (Description)
++   |                         |  (Description)
++   |Package:: package        |  (Description)
++   |                         |  (Description)
++   |%Additional_info::       |
++   |%reporter%               |  (Additional info)
++   |User:: user_name,uid     |  (Additional info)
++   |                         |  (Additional info)
++   |Directories:: root,cwd   |  (Additional info)
++
++   Elements can be:
++   - problem directory element names, which get formatted as
++     <element_name>: <contents>
++     or
++     <element_name>:
++     :<contents>
++     :<contents>
++     :<contents>
++   - problem directory element names prefixed by "%bare_",
++     which is formatted as-is, without "<element_name>:" and colons
++   - %oneline, %multiline, %text wildcards, which select all corresponding
++     elements for output or attachment
++   - %binary wildcard, valid only for %attach section, instructs to attach
++     binary elements
++   - problem directory element names prefixed by "-",
++     which excludes given element from all wildcards
++
++     Nonexistent elements are silently ignored.
++     If none of elements exists, the section will not be created.
++
++Integration with ABRT events
++~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++'reporter-mantisbt' can be used as an ABRT reporter. Example
++fragment for /etc/libreport/report_event.conf:
++
++------------
++# Report Python crashes
++EVENT=report_CentOSBugTracker analyzer=Python
++      reporter-mantisbt -d . -c /etc/libreport/plugins/mantisbt.conf
++------------
++
++OPTIONS
++-------
++-d DIR::
++   Path to problem directory.
++
++-c CONFFILE::
++   Path to configuration file.
++
++-f::
++   Force reporting even if this problem is already reported.
++
++-F CONF_FORMAT_FILE::
++   Formatting file for new issues. Default: /etc/libreport/plugins/mantisbt_format.conf
++
++-A CONF_FORMAT_FILE::
++   Formatting file for duplicates. Default: /etc/libreport/plugins/mantisbt_formatdup.conf
++
++-t[ID]::
++   Upload FILEs to the already created issue on MantisBT site.
++
++-h::
++--duphash DUPHASH::
++   Search in MantisBT by abrt's DUPHASH and print ISSUE_ID.
++
++-r TRACKER_NAME::
++   Set the last url from reporter_to element which is prefixed with TRACKER_NAME to URL field in MantisBT.
++
++ENVIRONMENT VARIABLES
++---------------------
++Environment variables take precedence over values provided in
++the configuration file.
++
++'Mantisbt_Login'::
++	Login to MantisBT account.
++
++'Mantisbt_Password'::
++	Password to MantisBT account.
++
++'Mantisbt_MantisbtURL'::
++	MantisBT HTTP(S) address. (default: http://localhost/mantisbt)
++
++'Mantisbt_SSLVerify'::
++	Use yes/true/on/1 to verify server's SSL certificate. (default: no)
++
++'Mantisbt_Project'::
++	Project issue field value. Useful if you needed different project than specified in /etc/os-release
++
++'Mantisbt_ProjectVersion'::
++	Version issue field value. Useful if you needed different project version than specified in /etc/os-release
++
++'Mantisbt_CreatePrivate'::
++    Create private MantisBT issue. (default: no)
++
++FILES
++-----
++/usr/share/libreport/conf.d/plugins/mantisbt.conf::
++    Readonly default configuration files.
++
++/etc/libreport/plugins/mantisbt.conf::
++    Configuration file.
++
++/etc/libreport/plugins/mantisbt_format.conf::
++    Configure formating for reporting.
++
++/etc/libreport/plugins/mantisbt_formatdup.conf::
++    Configure formating for reporting duplicates.
++
++SEE ALSO
++--------
++report_event.conf(5), mantisbt_format.conf(5), mantisbt_formatdup.conf(5)
++
++AUTHORS
++-------
++* ABRT team
+diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
+index fd3f477..27194a4 100644
+--- a/src/plugins/Makefile.am
++++ b/src/plugins/Makefile.am
+@@ -109,6 +109,10 @@ if BUILD_UREPORT
+ reporters_extra_dist += report_uReport.xml.in
+ endif
+ 
++if BUILD_MANTISBT
++reporters_extra_dist += report_CentOSBugTracker.xml.in
++endif
++
+ EXTRA_DIST = $(reporters_extra_dist) \
+     report_Logger.conf \
+     report_Logger.xml.in \
+diff --git a/src/workflows/Makefile.am b/src/workflows/Makefile.am
+index 7ecb34e..17127a0 100644
+--- a/src/workflows/Makefile.am
++++ b/src/workflows/Makefile.am
+@@ -107,3 +107,15 @@ EXTRA_DIST += \
+     workflow_RHELBugzillaLibreport.xml.in \
+     workflow_RHELBugzillaJava.xml.in
+ endif
++
++if BUILD_MANTISBT
++EXTRA_DIST += \
++    workflow_CentOSCCpp.xml.in \
++    workflow_CentOSKerneloops.xml.in \
++    workflow_CentOSPython.xml.in \
++    workflow_CentOSPython3.xml.in \
++    workflow_CentOSVmcore.xml.in \
++    workflow_CentOSXorg.xml.in \
++    workflow_CentOSLibreport.xml.in \
++    workflow_CentOSJava.xml.in
++endif
+-- 
+1.8.3.1
+
diff --git a/SOURCES/1010-move-problem_report-to-plugins.patch b/SOURCES/1010-move-problem_report-to-plugins.patch
new file mode 100644
index 0000000..9ba00e7
--- /dev/null
+++ b/SOURCES/1010-move-problem_report-to-plugins.patch
@@ -0,0 +1,3284 @@
+From a24be1f0915646dd0390884ceb4ee1bfae7fbe0c Mon Sep 17 00:00:00 2001
+From: Jakub Filak <jfilak@redhat.com>
+Date: Wed, 25 Mar 2015 16:43:19 +0100
+Subject: [PATCH 1010/1015] move problem_report to plugins
+
+Get rid of satyr from libreport.
+
+Signed-off-by: Jakub Filak <jfilak@redhat.com>
+---
+ po/POTFILES.in               |    2 +-
+ src/include/Makefile.am      |    1 -
+ src/include/problem_report.h |  334 ------------
+ src/lib/Makefile.am          |    7 +-
+ src/lib/problem_report.c     | 1210 ------------------------------------------
+ src/plugins/Makefile.am      |   24 +-
+ src/plugins/problem_report.c | 1210 ++++++++++++++++++++++++++++++++++++++++++
+ src/plugins/problem_report.h |  334 ++++++++++++
+ tests/testsuite.at           |    2 +-
+ 9 files changed, 1568 insertions(+), 1556 deletions(-)
+ delete mode 100644 src/include/problem_report.h
+ delete mode 100644 src/lib/problem_report.c
+ create mode 100644 src/plugins/problem_report.c
+ create mode 100644 src/plugins/problem_report.h
+
+diff --git a/po/POTFILES.in b/po/POTFILES.in
+index 9cf8f72..8b62b59 100644
+--- a/po/POTFILES.in
++++ b/po/POTFILES.in
+@@ -25,11 +25,11 @@ src/lib/ureport.c
+ src/lib/make_descr.c
+ src/lib/parse_options.c
+ src/lib/problem_data.c
+-src/lib/problem_report.c
+ src/lib/reported_to.c
+ src/lib/reporters.c
+ src/lib/run_event.c
+ src/plugins/abrt_rh_support.c
++src/plugins/problem_report.c
+ src/plugins/report_Bugzilla.xml.in
+ src/plugins/report.c
+ src/plugins/reporter-bugzilla.c
+diff --git a/src/include/Makefile.am b/src/include/Makefile.am
+index 4d8c6a5..7a76cf4 100644
+--- a/src/include/Makefile.am
++++ b/src/include/Makefile.am
+@@ -5,7 +5,6 @@ libreport_include_HEADERS = \
+     dump_dir.h \
+     event_config.h \
+     problem_data.h \
+-    problem_report.h \
+     report.h \
+     run_event.h \
+     libreport_curl.h \
+diff --git a/src/include/problem_report.h b/src/include/problem_report.h
+deleted file mode 100644
+index f2d41d8..0000000
+--- a/src/include/problem_report.h
++++ /dev/null
+@@ -1,334 +0,0 @@
+-/*
+-    Copyright (C) 2014  ABRT team
+-    Copyright (C) 2014  RedHat Inc
+-
+-    This program is free software; you can redistribute it and/or modify
+-    it under the terms of the GNU General Public License as published by
+-    the Free Software Foundation; either version 2 of the License, or
+-    (at your option) any later version.
+-
+-    This program is distributed in the hope that it will be useful,
+-    but WITHOUT ANY WARRANTY; without even the implied warranty of
+-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-    GNU General Public License for more details.
+-
+-    You should have received a copy of the GNU General Public License along
+-    with this program; if not, write to the Free Software Foundation, Inc.,
+-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-
+-    @brief API for formating of problem data
+-
+-    These functions can be used to convert a problem data to its string
+-    representation.
+-
+-    The output format can be parsed from a string:
+-
+-        problem_formatter_t *formatter = problem_formatter_new();
+-        problem_formatter_load_string(formatter, MY_FORMAT_STRING);
+-
+-    or loaded from a file:
+-
+-        problem_formatter_t *formatter = problem_formatter_new();
+-        problem_formatter_load_file(formatter, MY_FORMAT_FILE);
+-
+-    Once you have configured your formatter you can convert problem_data to
+-    problem_report by calling:
+-
+-        problem_report_t *report;
+-        if (problem_formatter_generate_report(formatter, data, &report) != 0)
+-            errx(EXIT_FAILURE, "Problem data cannot be converted to problem report.");
+-
+-    Now you can print the report:
+-
+-        printf("Problem: %s\n", problem_report_get_summary());
+-        printf("%s\n",          problem_report_get_description());
+-
+-        puts("Problem attachments:");
+-        for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a))
+-            printf(" %s\n", a->data);
+-
+-    Format description:
+-
+-         ----
+-         %summary:: summary format
+-         %attach:: elemnt1[,element2]...
+-         section:: element1[,element2]...
+-         The literal text line to be added to report.
+-         ----
+-
+-         Summary format is a line of text, where %element% is replaced by
+-         text element's content, and [[...%element%...]] block is used only if
+-         %element% exists. [[...]] blocks can nest.
+-
+-         Sections can be:
+-         - %summary: bug summary format string.
+-
+-         - %attach: a list of elements to attach.
+-
+-         - text, double colon (::) and the list of comma-separated elements.
+-           Text can be empty (":: elem1, elem2, elem3" works),
+-           in this case "Text:" header line will be omitted.
+-
+-         - %description: this section is implicit and contains all text
+-           sections unless another section was specified (%summary and %attach
+-           are ignored when determining text section's placement)
+-
+-         - every text element belongs to the last specified section (%summary
+-           and %attach sections are ignored). If no section was specified,
+-           the text element belogns to %description.
+-
+-         - If none of elements exists, the section will not be created.
+-
+-         - Empty lines are NOT ignored.
+-
+-         Elements can be:
+-         - problem directory element names, which get formatted as
+-           <element_name>: <contents>
+-           or
+-           <element_name>:
+-           :<contents>
+-           :<contents>
+-           :<contents>
+-
+-         - problem directory element names prefixed by "%bare_",
+-           which is formatted as-is, without "<element_name>:" and colons
+-
+-         - %oneline, %multiline, %text wildcards, which select all corresponding
+-           elements for output or attachment
+-
+-         - %binary wildcard, valid only for %attach section, instructs to attach
+-           binary elements
+-
+-         - problem directory element names prefixed by "-",
+-           which excludes given element from all wildcards
+-
+-         - Nonexistent elements are silently ignored.
+-
+-    You can add your own section:
+-
+-        problem_formatter_t *formatter = problem_formatter_new();
+-        problem_formatter_add_section(formatter, "additional_info", PFFF_REQUIRED);
+-
+-    and then you can use the section in the formatting string:
+-
+-        problem_formatter_load_string(formatter,
+-                "::comment\n"
+-                "%additional_info:: maps");
+-        problem_formatter_generate_report(formatter, data, &report);
+-
+-        printf("Problem: %s\n",         problem_report_get_summary());
+-        printf("%s\n",                  problem_report_get_description());
+-        printf("Additional info: %s\n", problem_report_get_section(report, "additiona_info"));
+-
+-    The lines above are equivalent to the following lines:
+-
+-        printf("Problem: %s\n",         problem_data_get_content_or_NULL(data, "reason"));
+-        printf("%s\n",                  problem_data_get_content_or_NULL(data, "comment"));
+-        printf("Additional info: %s\n", problem_data_get_content_or_NULL(data, "maps"));
+-*/
+-#ifndef LIBREPORT_PROBLEM_REPORT_H
+-#define LIBREPORT_PROBLEM_REPORT_H
+-
+-#include <glib.h>
+-#include <stdio.h>
+-#include "problem_data.h"
+-
+-#ifdef __cplusplus
+-extern "C" {
+-#endif
+-
+-#define PR_SEC_SUMMARY "summary"
+-#define PR_SEC_DESCRIPTION "description"
+-
+-/*
+- * The problem report structure represents a problem data formatted according
+- * to a format string.
+- *
+- * A problem report is composed of well-known sections:
+- *   - summary
+- *   - descritpion
+- *   - attach
+- *
+- * and custom sections accessed by:
+- *   problem_report_get_section();
+- */
+-struct problem_report;
+-typedef struct problem_report problem_report_t;
+-
+-/*
+- * Helpers for easily switching between FILE and struct strbuf
+- */
+-
+-/*
+- * Type of buffer used by Problem report
+- */
+-typedef FILE problem_report_buffer;
+-
+-/*
+- * Wrapper for the proble buffer's formated output function.
+- */
+-#define problem_report_buffer_printf(buf, fmt, ...)\
+-    fprintf((buf), (fmt), ##__VA_ARGS__)
+-
+-
+-/*
+- * Get a section buffer
+- *
+- * Use this function if you need to amend something to a formatted section.
+- *
+- * @param self Problem report
+- * @param section_name Name of required section
+- * @return Always valid pointer to a section buffer
+- */
+-problem_report_buffer *problem_report_get_buffer(const problem_report_t *self,
+-        const char *section_name);
+-
+-/*
+- * Get Summary string
+- *
+- * The returned pointer is valid as long as you perform no further output to
+- * the summary buffer.
+- *
+- * @param self Problem report
+- * @return Non-NULL pointer to summary data
+- */
+-const char *problem_report_get_summary(const problem_report_t *self);
+-
+-/*
+- * Get Description string
+- *
+- * The returned pointer is valid as long as you perform no further output to
+- * the description buffer.
+- *
+- * @param self Problem report
+- * @return Non-NULL pointer to description data
+- */
+-const char *problem_report_get_description(const problem_report_t *self);
+-
+-/*
+- * Get Section's string
+- *
+- * The returned pointer is valid as long as you perform no further output to
+- * the section's buffer.
+- *
+- * @param self Problem report
+- * @param section_name Name of the required section
+- * @return Non-NULL pointer to description data
+- */
+-const char *problem_report_get_section(const problem_report_t *self,
+-        const char *section_name);
+-
+-/*
+- * Get GList of the problem data items that are to be attached
+- *
+- * @param self Problem report
+- * @return A pointer to GList (NULL means empty list)
+- */
+-GList *problem_report_get_attachments(const problem_report_t *self);
+-
+-/*
+- * Releases all resources allocated by a problem report
+- *
+- * @param self Problem report
+- */
+-void problem_report_free(problem_report_t *self);
+-
+-
+-/*
+- * An enum of Extra section flags
+- */
+-enum problem_formatter_section_flags {
+-    PFFF_REQUIRED = 1 << 0, ///< section must be present in the format spec
+-};
+-
+-/*
+- * The problem formatter structure formats a problem data according to a format
+- * string and stores result a problem report.
+- *
+- * The problem formatter uses '%reason%' as %summary section format string, if
+- * %summary is not provided by a format string.
+- */
+-struct problem_formatter;
+-typedef struct problem_formatter problem_formatter_t;
+-
+-/*
+- * Constructs a new problem formatter.
+- *
+- * @return Non-NULL pointer to the new problem formatter
+- */
+-problem_formatter_t *problem_formatter_new(void);
+-
+-/*
+- * Releases all resources allocated by a problem formatter
+- *
+- * @param self Problem formatter
+- */
+-void problem_formatter_free(problem_formatter_t *self);
+-
+-/*
+- * Adds a new recognized section
+- *
+- * The problem formatter ignores a section in the format spec if the section is
+- * not one of the default nor added by this function.
+- *
+- * How the problem formatter handles these extra sections:
+- *
+- * A custom section is something like %description section. %description is the
+- * default section where all text (sub)sections are stored. If the formatter
+- * finds the custom section in format string, then starts storing text
+- * (sub)sections in the custom section.
+- *
+- * (%description)    |:: comment
+- * (%description)    |
+- * (%description)    |Package:: package
+- * (%description)    |
+- * (%additiona_info) |%additional_info::
+- * (%additiona_info) |%reporter%
+- * (%additiona_info) |User:: user_name,uid
+- * (%additiona_info) |
+- * (%additiona_info) |Directories:: root,cwd
+- *
+- *
+- * @param self Problem formatter
+- * @param name Name of the added section
+- * @param flags Info about the added section
+- * @return Zero on success. -EEXIST if the name is already known by the formatter
+- */
+-int problem_formatter_add_section(problem_formatter_t *self, const char *name, int flags);
+-
+-/*
+- * Loads a problem format from a string.
+- *
+- * @param self Problem formatter
+- * @param fmt Format
+- * @return Zero on success or number of warnings (e.g. missing section,
+- * unrecognized section).
+- */
+-int problem_formatter_load_string(problem_formatter_t* self, const char *fmt);
+-
+-/*
+- * Loads a problem format from a file.
+- *
+- * @param self Problem formatter
+- * @param pat Path to the format file
+- * @return Zero on success or number of warnings (e.g. missing section,
+- * unrecognized section).
+- */
+-int problem_formatter_load_file(problem_formatter_t* self, const char *path);
+-
+-/*
+- * Creates a new problem report, formats the data according to the loaded
+- * format string and stores output in the report.
+- *
+- * @param self Problem formatter
+- * @param data Problem data to format
+- * @param report Pointer where the created problem report is to be stored
+- * @return Zero on success, otherwise non-zero value.
+- */
+-int problem_formatter_generate_report(const problem_formatter_t *self, problem_data_t *data, problem_report_t **report);
+-
+-#ifdef __cplusplus
+-}
+-#endif
+-
+-#endif // LIBREPORT_PROBLEM_REPORT_H
+diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
+index d41e543..80a7c4d 100644
+--- a/src/lib/Makefile.am
++++ b/src/lib/Makefile.am
+@@ -38,7 +38,6 @@ libreport_la_SOURCES = \
+     make_descr.c \
+     run_event.c \
+     problem_data.c \
+-    problem_report.c \
+     create_dump_dir.c \
+     abrt_types.c \
+     parse_release.c \
+@@ -80,7 +79,6 @@ libreport_la_CPPFLAGS = \
+     $(GLIB_CFLAGS) \
+     $(GOBJECT_CFLAGS) \
+     $(AUGEAS_CFLAGS) \
+-    $(SATYR_CFLAGS) \
+     -D_GNU_SOURCE
+ libreport_la_LDFLAGS = \
+     -ltar \
+@@ -90,8 +88,7 @@ libreport_la_LIBADD = \
+     $(GLIB_LIBS) \
+     $(JOURNAL_LIBS) \
+     $(GOBJECT_LIBS) \
+-    $(AUGEAS_LIBS) \
+-    $(SATYR_LIBS)
++    $(AUGEAS_LIBS)
+ 
+ libreportconfdir = $(CONF_DIR)
+ dist_libreportconf_DATA = \
+@@ -152,8 +149,8 @@ libreport_web_la_LIBADD = \
+     $(PROXY_LIBS) \
+     $(LIBXML_LIBS) \
+     $(JSON_C_LIBS) \
+-    $(SATYR_LIBS) \
+     $(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS) \
++    $(SATYR_LIBS) \
+     libreport.la
+ 
+ DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
+diff --git a/src/lib/problem_report.c b/src/lib/problem_report.c
+deleted file mode 100644
+index 2bf5530..0000000
+--- a/src/lib/problem_report.c
++++ /dev/null
+@@ -1,1210 +0,0 @@
+-/*
+-    Copyright (C) 2014  ABRT team
+-    Copyright (C) 2014  RedHat Inc
+-
+-    This program is free software; you can redistribute it and/or modify
+-    it under the terms of the GNU General Public License as published by
+-    the Free Software Foundation; either version 2 of the License, or
+-    (at your option) any later version.
+-
+-    This program is distributed in the hope that it will be useful,
+-    but WITHOUT ANY WARRANTY; without even the implied warranty of
+-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-    GNU General Public License for more details.
+-
+-    You should have received a copy of the GNU General Public License along
+-    with this program; if not, write to the Free Software Foundation, Inc.,
+-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-*/
+-
+-#include "problem_report.h"
+-#include "internal_libreport.h"
+-
+-#include <satyr/stacktrace.h>
+-#include <satyr/abrt.h>
+-
+-#include <assert.h>
+-
+-#define DESTROYED_POINTER (void *)0xdeadbeef
+-
+-/* FORMAT:
+- * |%summary:: Hello, world
+- * |Problem description:: %bare_comment
+- * |
+- * |Package:: package
+- * |
+- * |%attach: %binary, backtrace
+- * |
+- * |%additional_info::
+- * |%reporter%
+- * |User:: user_name,uid
+- * |
+- * |Directories:: root,cwd
+- *
+- * PARSED DATA (list of struct section_t):
+- * {
+- *   section_t {
+- *      .name     = '%summary';
+- *      .items    = { 'Hello, world' };
+- *      .children = NULL;
+- *   },
+- *   section_t {
+- *      .name     = '%attach'
+- *      .items    = { '%binary', 'backtrace' };
+- *      .children = NULL;
+- *   },
+- *   section_t {
+- *      .name     = '%description'
+- *      .items    = NULL;
+- *      .children = {
+- *        section_t {
+- *          .name     = 'Problem description:';
+- *          .items    = { '%bare_comment' };
+- *          .children = NULL;
+- *        },
+- *        section_t {
+- *          .name     = '';
+- *          .items    = NULL;
+- *          .children = NULL;
+- *        },
+- *        section_t {
+- *          .name     = 'Package:';
+- *          .items    = { 'package' };
+- *          .children = NULL;
+- *        },
+- *      }
+- *   },
+- *   section_t {
+- *      .name     = '%additional_info'
+- *      .items    = { '%reporter%' };
+- *      .children = {
+- *        section_t {
+- *          .name     = 'User:';
+- *          .items    = { 'user_name', 'uid' };
+- *          .children = NULL;
+- *        },
+- *        section_t {
+- *          .name     = '';
+- *          .items    = NULL;
+- *          .children = NULL;
+- *        },
+- *        section_t {
+- *          .name     = 'Directories:';
+- *          .items    = { 'root', 'cwd' };
+- *          .children = NULL;
+- *        },
+- *      }
+- *   }
+- * }
+- */
+-struct section_t {
+-    char *name;      ///< name or output text (%summar, 'Package version:');
+-    GList *items;    ///< list of file names and special items (%reporter, %binar, ...)
+-    GList *children; ///< list of sub sections (struct section_t)
+-};
+-
+-typedef struct section_t section_t;
+-
+-static section_t *
+-section_new(const char *name)
+-{
+-    section_t *self = xmalloc(sizeof(*self));
+-    self->name = xstrdup(name);
+-    self->items = NULL;
+-    self->children = NULL;
+-
+-    return self;
+-}
+-
+-static void
+-section_free(section_t *self)
+-{
+-    if (self == NULL)
+-        return;
+-
+-    free(self->name);
+-    g_list_free_full(self->items, free);
+-    g_list_free_full(self->children, (GDestroyNotify)section_free);
+-
+-    free(self);
+-}
+-
+-static int
+-section_name_cmp(section_t *lhs, const char *rhs)
+-{
+-    return strcmp((lhs->name + 1), rhs);
+-}
+-
+-/* Utility functions */
+-
+-static GList*
+-split_string_on_char(const char *str, char ch)
+-{
+-    GList *list = NULL;
+-    for (;;)
+-    {
+-        const char *delim = strchrnul(str, ch);
+-        list = g_list_prepend(list, xstrndup(str, delim - str));
+-        if (*delim == '\0')
+-            break;
+-        str = delim + 1;
+-    }
+-    return g_list_reverse(list);
+-}
+-
+-static int
+-compare_item_name(const char *lookup, const char *name)
+-{
+-    if (lookup[0] == '-')
+-        lookup++;
+-    else if (strncmp(lookup, "%bare_", 6) == 0)
+-        lookup += 6;
+-    return strcmp(lookup, name);
+-}
+-
+-static int
+-is_item_name_in_section(const section_t *lookup, const char *name)
+-{
+-    if (g_list_find_custom(lookup->items, name, (GCompareFunc)compare_item_name))
+-        return 0; /* "found it!" */
+-    return 1;
+-}
+-
+-static bool is_explicit_or_forbidden(const char *name, GList *comment_fmt_spec);
+-
+-static int
+-is_explicit_or_forbidden_child(const section_t *master_section, const char *name)
+-{
+-    if (is_explicit_or_forbidden(name, master_section->children))
+-        return 0; /* "found it!" */
+-    return 1;
+-}
+-
+-/* For example: 'package' belongs to '%oneline', but 'package' is used in
+- * 'Version of component', so it is not very helpful to include that file once
+- * more in another section
+- */
+-static bool
+-is_explicit_or_forbidden(const char *name, GList *comment_fmt_spec)
+-{
+-    return    g_list_find_custom(comment_fmt_spec, name, (GCompareFunc)is_item_name_in_section)
+-           || g_list_find_custom(comment_fmt_spec, name, (GCompareFunc)is_explicit_or_forbidden_child);
+-}
+-
+-static GList*
+-load_stream(FILE *fp)
+-{
+-    assert(fp);
+-
+-    GList *sections = NULL;
+-    section_t *master = section_new("%description");
+-    section_t *sec = NULL;
+-
+-    sections = g_list_append(sections, master);
+-
+-    char *line;
+-    while ((line = xmalloc_fgetline(fp)) != NULL)
+-    {
+-        /* Skip comments */
+-        char first = *skip_whitespace(line);
+-        if (first == '#')
+-            goto free_line;
+-
+-        /* Handle trailing backslash continuation */
+- check_continuation: ;
+-        unsigned len = strlen(line);
+-        if (len && line[len-1] == '\\')
+-        {
+-            line[len-1] = '\0';
+-            char *next_line = xmalloc_fgetline(fp);
+-            if (next_line)
+-            {
+-                line = append_to_malloced_string(line, next_line);
+-                free(next_line);
+-                goto check_continuation;
+-            }
+-        }
+-
+-        /* We are reusing line buffer to form temporary
+-         * "key\0values\0..." in its beginning
+-         */
+-        bool summary_line = false;
+-        char *value = NULL;
+-        char *src;
+-        char *dst;
+-        for (src = dst = line; *src; src++)
+-        {
+-            char c = *src;
+-            /* did we reach the value list? */
+-            if (!value && c == ':' && src[1] == ':')
+-            {
+-                *dst++ = '\0'; /* terminate key */
+-                src += 1;
+-                value = dst; /* remember where value starts */
+-                summary_line = (strcmp(line, "%summary") == 0);
+-                if (summary_line)
+-                {
+-                    value = (src + 1);
+-                    break;
+-                }
+-                continue;
+-            }
+-            /* skip whitespace in value list */
+-            if (value && isspace(c))
+-                continue;
+-            *dst++ = c; /* store next key or value char */
+-        }
+-
+-        GList *item_list = NULL;
+-        if (summary_line)
+-        {
+-            /* %summary is special */
+-            item_list = g_list_append(NULL, xstrdup(skip_whitespace(value)));
+-        }
+-        else
+-        {
+-            *dst = '\0'; /* terminate value (or key) */
+-            if (value)
+-                item_list = split_string_on_char(value, ',');
+-        }
+-
+-        sec = section_new(line);
+-        sec->items = item_list;
+-
+-        if (sec->name[0] == '%')
+-        {
+-            if (!summary_line && strcmp(sec->name, "%attach") != 0)
+-            {
+-                master->children = g_list_reverse(master->children);
+-                master = sec;
+-            }
+-
+-            sections = g_list_prepend(sections, sec);
+-        }
+-        else
+-            master->children = g_list_prepend(master->children, sec);
+-
+- free_line:
+-        free(line);
+-    }
+-
+-    /* If master equals sec, then master's children list was not yet reversed.
+-     *
+-     * %description is the default section (i.e is not explicitly mentioned)
+-     * and %summary nor %attach cause its children list to reverse.
+-     */
+-    if (master == sec || strcmp(master->name, "%description") == 0)
+-        master->children = g_list_reverse(master->children);
+-
+-    return sections;
+-}
+-
+-
+-/* Summary generation */
+-
+-#define MAX_OPT_DEPTH 10
+-static int
+-format_percented_string(const char *str, problem_data_t *pd, FILE *result)
+-{
+-    long old_pos[MAX_OPT_DEPTH] = { 0 };
+-    int okay[MAX_OPT_DEPTH] = { 1 };
+-    long len = 0;
+-    int opt_depth = 1;
+-
+-    while (*str) {
+-        switch (*str) {
+-        default:
+-            putc(*str, result);
+-            len++;
+-            str++;
+-            break;
+-        case '\\':
+-            if (str[1])
+-                str++;
+-            putc(*str, result);
+-            len++;
+-            str++;
+-            break;
+-        case '[':
+-            if (str[1] == '[' && opt_depth < MAX_OPT_DEPTH)
+-            {
+-                old_pos[opt_depth] = len;
+-                okay[opt_depth] = 1;
+-                opt_depth++;
+-                str += 2;
+-            } else {
+-                putc(*str, result);
+-                len++;
+-                str++;
+-            }
+-            break;
+-        case ']':
+-            if (str[1] == ']' && opt_depth > 1)
+-            {
+-                opt_depth--;
+-                if (!okay[opt_depth])
+-                {
+-                    if (fseek(result, old_pos[opt_depth], SEEK_SET) < 0)
+-                        perror_msg_and_die("fseek");
+-                    len = old_pos[opt_depth];
+-                }
+-                str += 2;
+-            } else {
+-                putc(*str, result);
+-                len++;
+-                str++;
+-            }
+-            break;
+-        case '%': ;
+-            char *nextpercent = strchr(++str, '%');
+-            if (!nextpercent)
+-            {
+-                error_msg_and_die("Unterminated %%element%%: '%s'", str - 1);
+-            }
+-
+-            *nextpercent = '\0';
+-            const problem_item *item = problem_data_get_item_or_NULL(pd, str);
+-            *nextpercent = '%';
+-
+-            if (item && (item->flags & CD_FLAG_TXT))
+-            {
+-                fputs(item->content, result);
+-                len += strlen(item->content);
+-            }
+-            else
+-                okay[opt_depth - 1] = 0;
+-            str = nextpercent + 1;
+-            break;
+-        }
+-    }
+-
+-    if (opt_depth > 1)
+-    {
+-        error_msg_and_die("Unbalanced [[ ]] bracket");
+-    }
+-
+-    if (!okay[0])
+-    {
+-        error_msg("Undefined variable outside of [[ ]] bracket");
+-    }
+-
+-    return 0;
+-}
+-
+-/* BZ comment generation */
+-
+-static int
+-append_text(struct strbuf *result, const char *item_name, const char *content, bool print_item_name)
+-{
+-    char *eol = strchrnul(content, '\n');
+-    if (eol[0] == '\0' || eol[1] == '\0')
+-    {
+-        /* one-liner */
+-        int pad = 16 - (strlen(item_name) + 2);
+-        if (pad < 0)
+-            pad = 0;
+-        if (print_item_name)
+-            strbuf_append_strf(result,
+-                    eol[0] == '\0' ? "%s: %*s%s\n" : "%s: %*s%s",
+-                    item_name, pad, "", content
+-            );
+-        else
+-            strbuf_append_strf(result,
+-                    eol[0] == '\0' ? "%s\n" : "%s",
+-                    content
+-            );
+-    }
+-    else
+-    {
+-        /* multi-line item */
+-        if (print_item_name)
+-            strbuf_append_strf(result, "%s:\n", item_name);
+-        for (;;)
+-        {
+-            eol = strchrnul(content, '\n');
+-            strbuf_append_strf(result,
+-                    /* For %bare_multiline_item, we don't want to print colons */
+-                    (print_item_name ? ":%.*s\n" : "%.*s\n"),
+-                    (int)(eol - content), content
+-            );
+-            if (eol[0] == '\0' || eol[1] == '\0')
+-                break;
+-            content = eol + 1;
+-        }
+-    }
+-    return 1;
+-}
+-
+-static int
+-append_short_backtrace(struct strbuf *result, problem_data_t *problem_data, size_t max_text_size, bool print_item_name)
+-{
+-    const problem_item *item = problem_data_get_item_or_NULL(problem_data,
+-                                                             FILENAME_BACKTRACE);
+-    if (!item)
+-        return 0; /* "I did not print anything" */
+-    if (!(item->flags & CD_FLAG_TXT))
+-        return 0; /* "I did not print anything" */
+-
+-    char *truncated = NULL;
+-
+-    if (strlen(item->content) >= max_text_size)
+-    {
+-        log_debug("'backtrace' exceeds the text file size, going to append its short version");
+-
+-        char *error_msg = NULL;
+-        const char *type = problem_data_get_content_or_NULL(problem_data, FILENAME_TYPE);
+-        if (!type)
+-        {
+-            log_debug("Problem data does not contain '"FILENAME_TYPE"' file");
+-            return 0;
+-        }
+-
+-        /* For CCpp crashes, use the GDB-produced backtrace which should be
+-         * available by now. sr_abrt_type_from_analyzer returns SR_REPORT_CORE
+-         * by default for CCpp crashes.
+-         */
+-        enum sr_report_type report_type = sr_abrt_type_from_analyzer(type);
+-        if (strcmp(type, "CCpp") == 0)
+-        {
+-            log_debug("Successfully identified 'CCpp' abrt type");
+-            report_type = SR_REPORT_GDB;
+-        }
+-
+-        struct sr_stacktrace *backtrace = sr_stacktrace_parse(report_type,
+-                item->content, &error_msg);
+-
+-        if (!backtrace)
+-        {
+-            log(_("Can't parse backtrace: %s"), error_msg);
+-            free(error_msg);
+-            return 0;
+-        }
+-
+-        /* Get optimized thread stack trace for 10 top most frames */
+-        truncated = sr_stacktrace_to_short_text(backtrace, 10);
+-        sr_stacktrace_free(backtrace);
+-
+-        if (!truncated)
+-        {
+-            log(_("Can't generate stacktrace description (no crash thread?)"));
+-            return 0;
+-        }
+-    }
+-    else
+-    {
+-        log_debug("'backtrace' is small enough to be included as is");
+-    }
+-
+-    append_text(result,
+-                /*item_name:*/ truncated ? "truncated_backtrace" : FILENAME_BACKTRACE,
+-                /*content:*/   truncated ? truncated             : item->content,
+-                print_item_name
+-    );
+-    free(truncated);
+-    return 1;
+-}
+-
+-static int
+-append_item(struct strbuf *result, const char *item_name, problem_data_t *pd, GList *comment_fmt_spec)
+-{
+-    bool print_item_name = (strncmp(item_name, "%bare_", strlen("%bare_")) != 0);
+-    if (!print_item_name)
+-        item_name += strlen("%bare_");
+-
+-    if (item_name[0] != '%')
+-    {
+-        struct problem_item *item = problem_data_get_item_or_NULL(pd, item_name);
+-        if (!item)
+-            return 0; /* "I did not print anything" */
+-        if (!(item->flags & CD_FLAG_TXT))
+-            return 0; /* "I did not print anything" */
+-
+-        char *formatted = problem_item_format(item);
+-        char *content = formatted ? formatted : item->content;
+-        append_text(result, item_name, content, print_item_name);
+-        free(formatted);
+-        return 1; /* "I printed something" */
+-    }
+-
+-    /* Special item name */
+-
+-    /* Compat with previously-existed ad-hockery: %short_backtrace */
+-    if (strcmp(item_name, "%short_backtrace") == 0)
+-        return append_short_backtrace(result, pd, CD_TEXT_ATT_SIZE_BZ, print_item_name);
+-
+-    /* Compat with previously-existed ad-hockery: %reporter */
+-    if (strcmp(item_name, "%reporter") == 0)
+-        return append_text(result, "reporter", PACKAGE"-"VERSION, print_item_name);
+-
+-    /* %oneline,%multiline,%text */
+-    bool oneline   = (strcmp(item_name+1, "oneline"  ) == 0);
+-    bool multiline = (strcmp(item_name+1, "multiline") == 0);
+-    bool text      = (strcmp(item_name+1, "text"     ) == 0);
+-    if (!oneline && !multiline && !text)
+-    {
+-        log("Unknown or unsupported element specifier '%s'", item_name);
+-        return 0; /* "I did not print anything" */
+-    }
+-
+-    int printed = 0;
+-
+-    /* Iterate over _sorted_ items */
+-    GList *sorted_names = g_hash_table_get_keys(pd);
+-    sorted_names = g_list_sort(sorted_names, (GCompareFunc)strcmp);
+-
+-    /* %text => do as if %oneline, then repeat as if %multiline */
+-    if (text)
+-        oneline = 1;
+-
+- again: ;
+-    GList *l = sorted_names;
+-    while (l)
+-    {
+-        const char *name = l->data;
+-        l = l->next;
+-        struct problem_item *item = g_hash_table_lookup(pd, name);
+-        if (!item)
+-            continue; /* paranoia, won't happen */
+-
+-        if (!(item->flags & CD_FLAG_TXT))
+-            continue;
+-
+-        if (is_explicit_or_forbidden(name, comment_fmt_spec))
+-            continue;
+-
+-        char *formatted = problem_item_format(item);
+-        char *content = formatted ? formatted : item->content;
+-        char *eol = strchrnul(content, '\n');
+-        bool is_oneline = (eol[0] == '\0' || eol[1] == '\0');
+-        if (oneline == is_oneline)
+-            printed |= append_text(result, name, content, print_item_name);
+-        free(formatted);
+-    }
+-    if (text && oneline)
+-    {
+-        /* %text, and we just did %oneline. Repeat as if %multiline */
+-        oneline = 0;
+-        /*multiline = 1; - not checked in fact, so why bother setting? */
+-        goto again;
+-    }
+-
+-    g_list_free(sorted_names); /* names themselves are not freed */
+-
+-    return printed;
+-}
+-
+-#define add_to_section_output(format, ...) \
+-    do { \
+-    for (; empty_lines > 0; --empty_lines) fputc('\n', result); \
+-    empty_lines = 0; \
+-    fprintf(result, format, __VA_ARGS__); \
+-    } while (0)
+-
+-static void
+-format_section(section_t *section, problem_data_t *pd, GList *comment_fmt_spec, FILE *result)
+-{
+-    int empty_lines = -1;
+-
+-    for (GList *iter = section->children; iter; iter = g_list_next(iter))
+-    {
+-        section_t *child = (section_t *)iter->data;
+-        if (child->items)
+-        {
+-            /* "Text: item[,item]..." */
+-            struct strbuf *output = strbuf_new();
+-            GList *item = child->items;
+-            while (item)
+-            {
+-                const char *str = item->data;
+-                item = item->next;
+-                if (str[0] == '-') /* "-name", ignore it */
+-                    continue;
+-                append_item(output, str, pd, comment_fmt_spec);
+-            }
+-
+-            if (output->len != 0)
+-                add_to_section_output((child->name[0] ? "%s:\n%s" : "%s%s"),
+-                                      child->name, output->buf);
+-
+-            strbuf_free(output);
+-        }
+-        else
+-        {
+-            /* Just "Text" (can be "") */
+-
+-            /* Filter out trailint empty lines */
+-            if (child->name[0] != '\0')
+-                add_to_section_output("%s\n", child->name);
+-            /* Do not count empty lines, if output wasn't yet produced */
+-            else if (empty_lines >= 0)
+-                ++empty_lines;
+-        }
+-    }
+-}
+-
+-static GList *
+-get_special_items(const char *item_name, problem_data_t *pd, GList *comment_fmt_spec)
+-{
+-    /* %oneline,%multiline,%text,%binary */
+-    bool oneline   = (strcmp(item_name+1, "oneline"  ) == 0);
+-    bool multiline = (strcmp(item_name+1, "multiline") == 0);
+-    bool text      = (strcmp(item_name+1, "text"     ) == 0);
+-    bool binary    = (strcmp(item_name+1, "binary"   ) == 0);
+-    if (!oneline && !multiline && !text && !binary)
+-    {
+-        log("Unknown or unsupported element specifier '%s'", item_name);
+-        return NULL;
+-    }
+-
+-    log_debug("Special item_name '%s', iterating for attach...", item_name);
+-    GList *result = 0;
+-
+-    /* Iterate over _sorted_ items */
+-    GList *sorted_names = g_hash_table_get_keys(pd);
+-    sorted_names = g_list_sort(sorted_names, (GCompareFunc)strcmp);
+-
+-    GList *l = sorted_names;
+-    while (l)
+-    {
+-        const char *name = l->data;
+-        l = l->next;
+-        struct problem_item *item = g_hash_table_lookup(pd, name);
+-        if (!item)
+-            continue; /* paranoia, won't happen */
+-
+-        if (is_explicit_or_forbidden(name, comment_fmt_spec))
+-            continue;
+-
+-        if ((item->flags & CD_FLAG_TXT) && !binary)
+-        {
+-            char *content = item->content;
+-            char *eol = strchrnul(content, '\n');
+-            bool is_oneline = (eol[0] == '\0' || eol[1] == '\0');
+-            if (text || oneline == is_oneline)
+-                result = g_list_append(result, xstrdup(name));
+-        }
+-        else if ((item->flags & CD_FLAG_BIN) && binary)
+-            result = g_list_append(result, xstrdup(name));
+-    }
+-
+-    g_list_free(sorted_names); /* names themselves are not freed */
+-
+-
+-    log_debug("...Done iterating over '%s' for attach", item_name);
+-
+-    return result;
+-}
+-
+-static GList *
+-get_attached_files(problem_data_t *pd, GList *items, GList *comment_fmt_spec)
+-{
+-    GList *result = NULL;
+-    GList *item = items;
+-    while (item != NULL)
+-    {
+-        const char *item_name = item->data;
+-        item = item->next;
+-        if (item_name[0] == '-') /* "-name", ignore it */
+-            continue;
+-
+-        if (item_name[0] != '%')
+-        {
+-            result = g_list_append(result, xstrdup(item_name));
+-            continue;
+-        }
+-
+-        GList *special = get_special_items(item_name, pd, comment_fmt_spec);
+-        if (special == NULL)
+-        {
+-            log_notice("No attachment found for '%s'", item_name);
+-            continue;
+-        }
+-
+-        result = g_list_concat(result, special);
+-    }
+-
+-    return result;
+-}
+-
+-/*
+- * Problem Report - memor stream
+- *
+- * A wrapper for POSIX memory stream.
+- *
+- * A memory stream is presented as FILE *.
+- *
+- * A memory stream is associated with a pointer to written data and a pointer
+- * to size of the written data.
+- *
+- * This structure holds all of the used pointers.
+- */
+-struct memstream_buffer
+-{
+-    char *msb_buffer;
+-    size_t msb_size;
+-    FILE *msb_stream;
+-};
+-
+-static struct memstream_buffer *
+-memstream_buffer_new()
+-{
+-    struct memstream_buffer *self = xmalloc(sizeof(*self));
+-
+-    self->msb_buffer = NULL;
+-    self->msb_stream = open_memstream(&(self->msb_buffer), &(self->msb_size));
+-
+-    return self;
+-}
+-
+-static void
+-memstream_buffer_free(struct memstream_buffer *self)
+-{
+-    if (self == NULL)
+-        return;
+-
+-    fclose(self->msb_stream);
+-    self->msb_stream = DESTROYED_POINTER;
+-
+-    free(self->msb_buffer);
+-    self->msb_buffer = DESTROYED_POINTER;
+-
+-    free(self);
+-}
+-
+-static FILE *
+-memstream_get_stream(struct memstream_buffer *self)
+-{
+-    assert(self != NULL);
+-
+-    return self->msb_stream;
+-}
+-
+-static const char *
+-memstream_get_string(struct memstream_buffer *self)
+-{
+-    assert(self != NULL);
+-    assert(self->msb_stream != NULL);
+-
+-    fflush(self->msb_stream);
+-
+-    return self->msb_buffer;
+-}
+-
+-
+-/*
+- * Problem Report
+- *
+- * The formated strings are internaly stored in "buffer"s. If a programer wants
+- * to get a formated section data, a getter function extracts those data from
+- * the apropriate buffer and returns them in form of null-terminated string.
+- *
+- * Each section has own buffer.
+- *
+- * There are three common sections that are always present:
+- * 1. summary
+- * 2. description
+- * 3. attach
+- * Buffers of these sections has own structure member for the sake of
+- * efficiency.
+- *
+- * The custom sections hash their buffers stored in a map where key is a
+- * section's name and value is a section's buffer.
+- *
+- * Problem report provides the programers with the possibility to ammend
+- * formated output to any section buffer.
+- */
+-struct problem_report
+-{
+-    struct memstream_buffer *pr_sec_summ; ///< %summary buffer
+-    struct memstream_buffer *pr_sec_desc; ///< %description buffer
+-    GList            *pr_attachments;     ///< %attach - list of file names
+-    GHashTable       *pr_sec_custom;      ///< map : %(custom section) -> buffer
+-};
+-
+-static problem_report_t *
+-problem_report_new()
+-{
+-    problem_report_t *self = xmalloc(sizeof(*self));
+-
+-    self->pr_sec_summ = memstream_buffer_new();
+-    self->pr_sec_desc = memstream_buffer_new();
+-    self->pr_attachments = NULL;
+-    self->pr_sec_custom = NULL;
+-
+-    return self;
+-}
+-
+-static void
+-problem_report_initialize_custom_sections(problem_report_t *self)
+-{
+-    assert(self != NULL);
+-    assert(self->pr_sec_custom == NULL);
+-
+-    self->pr_sec_custom = g_hash_table_new_full(g_str_hash, g_str_equal, free,
+-                                                (GDestroyNotify)memstream_buffer_free);
+-}
+-
+-static void
+-problem_report_destroy_custom_sections(problem_report_t *self)
+-{
+-    assert(self != NULL);
+-    assert(self->pr_sec_custom != NULL);
+-
+-    g_hash_table_destroy(self->pr_sec_custom);
+-}
+-
+-static int
+-problem_report_add_custom_section(problem_report_t *self, const char *name)
+-{
+-    assert(self != NULL);
+-
+-    if (self->pr_sec_custom == NULL)
+-    {
+-        problem_report_initialize_custom_sections(self);
+-    }
+-
+-    if (problem_report_get_buffer(self, name))
+-    {
+-        log_warning("Custom section already exists : '%s'", name);
+-        return -EEXIST;
+-    }
+-
+-    log_debug("Problem report enriched with section : '%s'", name);
+-    g_hash_table_insert(self->pr_sec_custom, xstrdup(name), memstream_buffer_new());
+-    return 0;
+-}
+-
+-static struct memstream_buffer *
+-problem_report_get_section_buffer(const problem_report_t *self, const char *section_name)
+-{
+-    if (self->pr_sec_custom == NULL)
+-    {
+-        log_debug("Couldn't find section '%s': no custom section added", section_name);
+-        return NULL;
+-    }
+-
+-    return (struct memstream_buffer *)g_hash_table_lookup(self->pr_sec_custom, section_name);
+-}
+-
+-problem_report_buffer *
+-problem_report_get_buffer(const problem_report_t *self, const char *section_name)
+-{
+-    assert(self != NULL);
+-    assert(section_name != NULL);
+-
+-    if (strcmp(PR_SEC_SUMMARY, section_name) == 0)
+-        return memstream_get_stream(self->pr_sec_summ);
+-
+-    if (strcmp(PR_SEC_DESCRIPTION, section_name) == 0)
+-        return memstream_get_stream(self->pr_sec_desc);
+-
+-    struct memstream_buffer *buf = problem_report_get_section_buffer(self, section_name);
+-    return buf == NULL ? NULL : memstream_get_stream(buf);
+-}
+-
+-const char *
+-problem_report_get_summary(const problem_report_t *self)
+-{
+-    assert(self != NULL);
+-
+-    return memstream_get_string(self->pr_sec_summ);
+-}
+-
+-const char *
+-problem_report_get_description(const problem_report_t *self)
+-{
+-    assert(self != NULL);
+-
+-    return memstream_get_string(self->pr_sec_desc);
+-}
+-
+-const char *
+-problem_report_get_section(const problem_report_t *self, const char *section_name)
+-{
+-    assert(self != NULL);
+-    assert(section_name);
+-
+-    struct memstream_buffer *buf = problem_report_get_section_buffer(self, section_name);
+-
+-    if (buf == NULL)
+-        return NULL;
+-
+-    return memstream_get_string(buf);
+-}
+-
+-static void
+-problem_report_set_attachments(problem_report_t *self, GList *attachments)
+-{
+-    assert(self != NULL);
+-    assert(self->pr_attachments == NULL);
+-
+-    self->pr_attachments = attachments;
+-}
+-
+-GList *
+-problem_report_get_attachments(const problem_report_t *self)
+-{
+-    assert(self != NULL);
+-
+-    return self->pr_attachments;
+-}
+-
+-void
+-problem_report_free(problem_report_t *self)
+-{
+-    if (self == NULL)
+-        return;
+-
+-    memstream_buffer_free(self->pr_sec_summ);
+-    self->pr_sec_summ = DESTROYED_POINTER;
+-
+-    memstream_buffer_free(self->pr_sec_desc);
+-    self->pr_sec_desc = DESTROYED_POINTER;
+-
+-    g_list_free_full(self->pr_attachments, free);
+-    self->pr_attachments = DESTROYED_POINTER;
+-
+-    if (self->pr_sec_custom)
+-    {
+-        problem_report_destroy_custom_sections(self);
+-        self->pr_sec_custom = DESTROYED_POINTER;
+-    }
+-
+-    free(self);
+-}
+-
+-/*
+- * Problem Formatter - extra section
+- */
+-struct extra_section
+-{
+-    char *pfes_name;    ///< name with % prefix
+-    int   pfes_flags;   ///< whether is required or not
+-};
+-
+-static struct extra_section *
+-extra_section_new(const char *name, int flags)
+-{
+-    struct extra_section *self = xmalloc(sizeof(*self));
+-
+-    self->pfes_name = xstrdup(name);
+-    self->pfes_flags = flags;
+-
+-    return self;
+-}
+-
+-static void
+-extra_section_free(struct extra_section *self)
+-{
+-    if (self == NULL)
+-        return;
+-
+-    free(self->pfes_name);
+-    self->pfes_name = DESTROYED_POINTER;
+-
+-    free(self);
+-}
+-
+-static int
+-extra_section_name_cmp(struct extra_section *lhs, const char *rhs)
+-{
+-    return strcmp(lhs->pfes_name, rhs);
+-}
+-
+-/*
+- * Problem Formatter
+- *
+- * Holds parsed sections lists.
+- */
+-struct problem_formatter
+-{
+-    GList *pf_sections;         ///< parsed sections (struct section_t)
+-    GList *pf_extra_sections;   ///< user configured sections (struct extra_section)
+-    char  *pf_default_summary;  ///< default summary format
+-};
+-
+-problem_formatter_t *
+-problem_formatter_new(void)
+-{
+-    problem_formatter_t *self = xzalloc(sizeof(*self));
+-
+-    self->pf_default_summary = xstrdup("%reason%");
+-
+-    return self;
+-}
+-
+-void
+-problem_formatter_free(problem_formatter_t *self)
+-{
+-    if (self == NULL)
+-        return;
+-
+-    g_list_free_full(self->pf_sections, (GDestroyNotify)section_free);
+-    self->pf_sections = DESTROYED_POINTER;
+-
+-    g_list_free_full(self->pf_extra_sections, (GDestroyNotify)extra_section_free);
+-    self->pf_extra_sections = DESTROYED_POINTER;
+-
+-    free(self->pf_default_summary);
+-    self->pf_default_summary = DESTROYED_POINTER;
+-
+-    free(self);
+-}
+-
+-static int
+-problem_formatter_is_section_known(problem_formatter_t *self, const char *name)
+-{
+-  return    strcmp(name, "summary")     == 0
+-         || strcmp(name, "attach")      == 0
+-         || strcmp(name, "description") == 0
+-         || NULL != g_list_find_custom(self->pf_extra_sections, name, (GCompareFunc)extra_section_name_cmp);
+-}
+-
+-// i.e additional_info -> no flags
+-int
+-problem_formatter_add_section(problem_formatter_t *self, const char *name, int flags)
+-{
+-    /* Do not add already added sections */
+-    if (problem_formatter_is_section_known(self, name))
+-    {
+-        log_debug("Extra section already exists : '%s' ", name);
+-        return -EEXIST;
+-    }
+-
+-    self->pf_extra_sections = g_list_prepend(self->pf_extra_sections,
+-                                             extra_section_new(name, flags));
+-
+-    return 0;
+-}
+-
+-// check format validity and produce warnings
+-static int
+-problem_formatter_validate(problem_formatter_t *self)
+-{
+-    int retval = 0;
+-
+-    /* Go through all (struct extra_section)s and check whete those having flag
+-     * PFFF_REQUIRED are present in the parsed (struct section_t)s.
+-     */
+-    for (GList *iter = self->pf_extra_sections; iter; iter = g_list_next(iter))
+-    {
+-        struct extra_section *section = (struct extra_section *)iter->data;
+-
+-        log_debug("Validating extra section : '%s'", section->pfes_name);
+-
+-        if (   (PFFF_REQUIRED & section->pfes_flags)
+-            && NULL == g_list_find_custom(self->pf_sections, section->pfes_name, (GCompareFunc)section_name_cmp))
+-        {
+-            log_warning("Problem format misses required section : '%s'", section->pfes_name);
+-            ++retval;
+-        }
+-    }
+-
+-    /* Go through all the parsed (struct section_t)s check whether are all
+-     * known, i.e. each section is either one of the common sections (summary,
+-     * description, attach) or is present in the (struct extra_section)s.
+-     */
+-    for (GList *iter = self->pf_sections; iter; iter = g_list_next(iter))
+-    {
+-        section_t *section = (section_t *)iter->data;
+-
+-        if (!problem_formatter_is_section_known(self, (section->name + 1)))
+-        {
+-            log_warning("Problem format contains unrecognized section : '%s'", section->name);
+-            ++retval;
+-        }
+-    }
+-
+-    return retval;
+-}
+-
+-int
+-problem_formatter_load_string(problem_formatter_t *self, const char *fmt)
+-{
+-    const size_t len = strlen(fmt);
+-    if (len != 0)
+-    {
+-        FILE *fp = fmemopen((void *)fmt, len, "r");
+-        if (fp == NULL)
+-        {
+-            error_msg("Not enough memory to open a stream for reading format string.");
+-            return -ENOMEM;
+-        }
+-
+-        self->pf_sections = load_stream(fp);
+-        fclose(fp);
+-    }
+-
+-    return problem_formatter_validate(self);
+-}
+-
+-int
+-problem_formatter_load_file(problem_formatter_t *self, const char *path)
+-{
+-    FILE *fp = stdin;
+-    if (strcmp(path, "-") != 0)
+-    {
+-        fp = fopen(path, "r");
+-        if (!fp)
+-            return -ENOENT;
+-    }
+-
+-    self->pf_sections = load_stream(fp);
+-
+-    if (fp != stdin)
+-        fclose(fp);
+-
+-    return problem_formatter_validate(self);
+-}
+-
+-// generates report
+-int
+-problem_formatter_generate_report(const problem_formatter_t *self, problem_data_t *data, problem_report_t **report)
+-{
+-    problem_report_t *pr = problem_report_new();
+-
+-    for (GList *iter = self->pf_extra_sections; iter; iter = g_list_next(iter))
+-        problem_report_add_custom_section(pr, ((struct extra_section *)iter->data)->pfes_name);
+-
+-    bool has_summary = false;
+-    for (GList *iter = self->pf_sections; iter; iter = g_list_next(iter))
+-    {
+-        section_t *section = (section_t *)iter->data;
+-
+-        /* %summary is something special */
+-        if (strcmp(section->name, "%summary") == 0)
+-        {
+-            has_summary = true;
+-            format_percented_string((const char *)section->items->data, data,
+-                                    problem_report_get_buffer(pr, PR_SEC_SUMMARY));
+-        }
+-        /* %attach as well */
+-        else if (strcmp(section->name, "%attach") == 0)
+-        {
+-            problem_report_set_attachments(pr, get_attached_files(data, section->items, self->pf_sections));
+-        }
+-        else /* %description or a custom section (e.g. %additional_info) */
+-        {
+-            FILE *buffer = problem_report_get_buffer(pr, section->name + 1);
+-
+-            if (buffer != NULL)
+-            {
+-                log_debug("Formatting section : '%s'", section->name);
+-                format_section(section, data, self->pf_sections, buffer);
+-            }
+-            else
+-                log_warning("Unsupported section '%s'", section->name);
+-        }
+-    }
+-
+-    if (!has_summary) {
+-        log_debug("Problem format misses section '%%summary'. Using the default one : '%s'.",
+-                    self->pf_default_summary);
+-
+-        format_percented_string(self->pf_default_summary,
+-                   data, problem_report_get_buffer(pr, PR_SEC_SUMMARY));
+-    }
+-
+-    *report = pr;
+-    return 0;
+-}
+diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
+index 27194a4..f4f94ff 100644
+--- a/src/plugins/Makefile.am
++++ b/src/plugins/Makefile.am
+@@ -125,6 +125,17 @@ EXTRA_DIST = $(reporters_extra_dist) \
+ $(DESTDIR)/$(DEBUG_INFO_DIR):
+ 	$(mkdir_p) '$@'
+ 
++noinst_LIBRARIES = libreport-problem-report.a
++libreport_problem_report_a_SOURCES = \
++    problem_report.c \
++    problem_report.h
++libreport_problem_report_a_CFLAGS = \
++    -I$(srcdir)/../include \
++    $(LIBREPORT_CFLAGS) \
++    $(GLIB_CFLAGS) \
++    $(SATYR_CFLAGS) \
++    -D_GNU_SOURCE
++
+ if BUILD_BUGZILLA
+ reporter_bugzilla_SOURCES = \
+     reporter-bugzilla.c rhbz.c rhbz.h
+@@ -146,7 +157,8 @@ reporter_bugzilla_LDADD = \
+     $(GLIB_LIBS) \
+     $(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS) \
+     ../lib/libreport-web.la \
+-    ../lib/libreport.la
++    ../lib/libreport.la \
++    libreport-problem-report.a
+ endif
+ 
+ if BUILD_MANTISBT
+@@ -169,7 +181,8 @@ reporter_mantisbt_CPPFLAGS = \
+ reporter_mantisbt_LDADD = \
+     $(GLIB_LIBS) \
+     ../lib/libreport-web.la \
+-    ../lib/libreport.la
++    ../lib/libreport.la \
++    libreport-problem-report.a
+ endif
+ 
+ reporter_rhtsupport_SOURCES = \
+@@ -196,7 +209,8 @@ reporter_rhtsupport_LDADD = \
+     $(GLIB_LIBS) \
+     $(LIBXML_LIBS) \
+     ../lib/libreport-web.la \
+-    ../lib/libreport.la
++    ../lib/libreport.la \
++    libreport-problem-report.a
+ 
+ reporter_upload_SOURCES = \
+     reporter-upload.c
+@@ -255,7 +269,9 @@ reporter_mailx_CPPFLAGS = \
+     $(LIBREPORT_CFLAGS) \
+     -D_GNU_SOURCE
+ reporter_mailx_LDADD = \
+-    ../lib/libreport.la
++    ../lib/libreport.la \
++    $(SATYR_LIBS) \
++    libreport-problem-report.a
+ 
+ reporter_print_SOURCES = \
+     reporter-print.c
+diff --git a/src/plugins/problem_report.c b/src/plugins/problem_report.c
+new file mode 100644
+index 0000000..2bf5530
+--- /dev/null
++++ b/src/plugins/problem_report.c
+@@ -0,0 +1,1210 @@
++/*
++    Copyright (C) 2014  ABRT team
++    Copyright (C) 2014  RedHat Inc
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 2 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License along
++    with this program; if not, write to the Free Software Foundation, Inc.,
++    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++*/
++
++#include "problem_report.h"
++#include "internal_libreport.h"
++
++#include <satyr/stacktrace.h>
++#include <satyr/abrt.h>
++
++#include <assert.h>
++
++#define DESTROYED_POINTER (void *)0xdeadbeef
++
++/* FORMAT:
++ * |%summary:: Hello, world
++ * |Problem description:: %bare_comment
++ * |
++ * |Package:: package
++ * |
++ * |%attach: %binary, backtrace
++ * |
++ * |%additional_info::
++ * |%reporter%
++ * |User:: user_name,uid
++ * |
++ * |Directories:: root,cwd
++ *
++ * PARSED DATA (list of struct section_t):
++ * {
++ *   section_t {
++ *      .name     = '%summary';
++ *      .items    = { 'Hello, world' };
++ *      .children = NULL;
++ *   },
++ *   section_t {
++ *      .name     = '%attach'
++ *      .items    = { '%binary', 'backtrace' };
++ *      .children = NULL;
++ *   },
++ *   section_t {
++ *      .name     = '%description'
++ *      .items    = NULL;
++ *      .children = {
++ *        section_t {
++ *          .name     = 'Problem description:';
++ *          .items    = { '%bare_comment' };
++ *          .children = NULL;
++ *        },
++ *        section_t {
++ *          .name     = '';
++ *          .items    = NULL;
++ *          .children = NULL;
++ *        },
++ *        section_t {
++ *          .name     = 'Package:';
++ *          .items    = { 'package' };
++ *          .children = NULL;
++ *        },
++ *      }
++ *   },
++ *   section_t {
++ *      .name     = '%additional_info'
++ *      .items    = { '%reporter%' };
++ *      .children = {
++ *        section_t {
++ *          .name     = 'User:';
++ *          .items    = { 'user_name', 'uid' };
++ *          .children = NULL;
++ *        },
++ *        section_t {
++ *          .name     = '';
++ *          .items    = NULL;
++ *          .children = NULL;
++ *        },
++ *        section_t {
++ *          .name     = 'Directories:';
++ *          .items    = { 'root', 'cwd' };
++ *          .children = NULL;
++ *        },
++ *      }
++ *   }
++ * }
++ */
++struct section_t {
++    char *name;      ///< name or output text (%summar, 'Package version:');
++    GList *items;    ///< list of file names and special items (%reporter, %binar, ...)
++    GList *children; ///< list of sub sections (struct section_t)
++};
++
++typedef struct section_t section_t;
++
++static section_t *
++section_new(const char *name)
++{
++    section_t *self = xmalloc(sizeof(*self));
++    self->name = xstrdup(name);
++    self->items = NULL;
++    self->children = NULL;
++
++    return self;
++}
++
++static void
++section_free(section_t *self)
++{
++    if (self == NULL)
++        return;
++
++    free(self->name);
++    g_list_free_full(self->items, free);
++    g_list_free_full(self->children, (GDestroyNotify)section_free);
++
++    free(self);
++}
++
++static int
++section_name_cmp(section_t *lhs, const char *rhs)
++{
++    return strcmp((lhs->name + 1), rhs);
++}
++
++/* Utility functions */
++
++static GList*
++split_string_on_char(const char *str, char ch)
++{
++    GList *list = NULL;
++    for (;;)
++    {
++        const char *delim = strchrnul(str, ch);
++        list = g_list_prepend(list, xstrndup(str, delim - str));
++        if (*delim == '\0')
++            break;
++        str = delim + 1;
++    }
++    return g_list_reverse(list);
++}
++
++static int
++compare_item_name(const char *lookup, const char *name)
++{
++    if (lookup[0] == '-')
++        lookup++;
++    else if (strncmp(lookup, "%bare_", 6) == 0)
++        lookup += 6;
++    return strcmp(lookup, name);
++}
++
++static int
++is_item_name_in_section(const section_t *lookup, const char *name)
++{
++    if (g_list_find_custom(lookup->items, name, (GCompareFunc)compare_item_name))
++        return 0; /* "found it!" */
++    return 1;
++}
++
++static bool is_explicit_or_forbidden(const char *name, GList *comment_fmt_spec);
++
++static int
++is_explicit_or_forbidden_child(const section_t *master_section, const char *name)
++{
++    if (is_explicit_or_forbidden(name, master_section->children))
++        return 0; /* "found it!" */
++    return 1;
++}
++
++/* For example: 'package' belongs to '%oneline', but 'package' is used in
++ * 'Version of component', so it is not very helpful to include that file once
++ * more in another section
++ */
++static bool
++is_explicit_or_forbidden(const char *name, GList *comment_fmt_spec)
++{
++    return    g_list_find_custom(comment_fmt_spec, name, (GCompareFunc)is_item_name_in_section)
++           || g_list_find_custom(comment_fmt_spec, name, (GCompareFunc)is_explicit_or_forbidden_child);
++}
++
++static GList*
++load_stream(FILE *fp)
++{
++    assert(fp);
++
++    GList *sections = NULL;
++    section_t *master = section_new("%description");
++    section_t *sec = NULL;
++
++    sections = g_list_append(sections, master);
++
++    char *line;
++    while ((line = xmalloc_fgetline(fp)) != NULL)
++    {
++        /* Skip comments */
++        char first = *skip_whitespace(line);
++        if (first == '#')
++            goto free_line;
++
++        /* Handle trailing backslash continuation */
++ check_continuation: ;
++        unsigned len = strlen(line);
++        if (len && line[len-1] == '\\')
++        {
++            line[len-1] = '\0';
++            char *next_line = xmalloc_fgetline(fp);
++            if (next_line)
++            {
++                line = append_to_malloced_string(line, next_line);
++                free(next_line);
++                goto check_continuation;
++            }
++        }
++
++        /* We are reusing line buffer to form temporary
++         * "key\0values\0..." in its beginning
++         */
++        bool summary_line = false;
++        char *value = NULL;
++        char *src;
++        char *dst;
++        for (src = dst = line; *src; src++)
++        {
++            char c = *src;
++            /* did we reach the value list? */
++            if (!value && c == ':' && src[1] == ':')
++            {
++                *dst++ = '\0'; /* terminate key */
++                src += 1;
++                value = dst; /* remember where value starts */
++                summary_line = (strcmp(line, "%summary") == 0);
++                if (summary_line)
++                {
++                    value = (src + 1);
++                    break;
++                }
++                continue;
++            }
++            /* skip whitespace in value list */
++            if (value && isspace(c))
++                continue;
++            *dst++ = c; /* store next key or value char */
++        }
++
++        GList *item_list = NULL;
++        if (summary_line)
++        {
++            /* %summary is special */
++            item_list = g_list_append(NULL, xstrdup(skip_whitespace(value)));
++        }
++        else
++        {
++            *dst = '\0'; /* terminate value (or key) */
++            if (value)
++                item_list = split_string_on_char(value, ',');
++        }
++
++        sec = section_new(line);
++        sec->items = item_list;
++
++        if (sec->name[0] == '%')
++        {
++            if (!summary_line && strcmp(sec->name, "%attach") != 0)
++            {
++                master->children = g_list_reverse(master->children);
++                master = sec;
++            }
++
++            sections = g_list_prepend(sections, sec);
++        }
++        else
++            master->children = g_list_prepend(master->children, sec);
++
++ free_line:
++        free(line);
++    }
++
++    /* If master equals sec, then master's children list was not yet reversed.
++     *
++     * %description is the default section (i.e is not explicitly mentioned)
++     * and %summary nor %attach cause its children list to reverse.
++     */
++    if (master == sec || strcmp(master->name, "%description") == 0)
++        master->children = g_list_reverse(master->children);
++
++    return sections;
++}
++
++
++/* Summary generation */
++
++#define MAX_OPT_DEPTH 10
++static int
++format_percented_string(const char *str, problem_data_t *pd, FILE *result)
++{
++    long old_pos[MAX_OPT_DEPTH] = { 0 };
++    int okay[MAX_OPT_DEPTH] = { 1 };
++    long len = 0;
++    int opt_depth = 1;
++
++    while (*str) {
++        switch (*str) {
++        default:
++            putc(*str, result);
++            len++;
++            str++;
++            break;
++        case '\\':
++            if (str[1])
++                str++;
++            putc(*str, result);
++            len++;
++            str++;
++            break;
++        case '[':
++            if (str[1] == '[' && opt_depth < MAX_OPT_DEPTH)
++            {
++                old_pos[opt_depth] = len;
++                okay[opt_depth] = 1;
++                opt_depth++;
++                str += 2;
++            } else {
++                putc(*str, result);
++                len++;
++                str++;
++            }
++            break;
++        case ']':
++            if (str[1] == ']' && opt_depth > 1)
++            {
++                opt_depth--;
++                if (!okay[opt_depth])
++                {
++                    if (fseek(result, old_pos[opt_depth], SEEK_SET) < 0)
++                        perror_msg_and_die("fseek");
++                    len = old_pos[opt_depth];
++                }
++                str += 2;
++            } else {
++                putc(*str, result);
++                len++;
++                str++;
++            }
++            break;
++        case '%': ;
++            char *nextpercent = strchr(++str, '%');
++            if (!nextpercent)
++            {
++                error_msg_and_die("Unterminated %%element%%: '%s'", str - 1);
++            }
++
++            *nextpercent = '\0';
++            const problem_item *item = problem_data_get_item_or_NULL(pd, str);
++            *nextpercent = '%';
++
++            if (item && (item->flags & CD_FLAG_TXT))
++            {
++                fputs(item->content, result);
++                len += strlen(item->content);
++            }
++            else
++                okay[opt_depth - 1] = 0;
++            str = nextpercent + 1;
++            break;
++        }
++    }
++
++    if (opt_depth > 1)
++    {
++        error_msg_and_die("Unbalanced [[ ]] bracket");
++    }
++
++    if (!okay[0])
++    {
++        error_msg("Undefined variable outside of [[ ]] bracket");
++    }
++
++    return 0;
++}
++
++/* BZ comment generation */
++
++static int
++append_text(struct strbuf *result, const char *item_name, const char *content, bool print_item_name)
++{
++    char *eol = strchrnul(content, '\n');
++    if (eol[0] == '\0' || eol[1] == '\0')
++    {
++        /* one-liner */
++        int pad = 16 - (strlen(item_name) + 2);
++        if (pad < 0)
++            pad = 0;
++        if (print_item_name)
++            strbuf_append_strf(result,
++                    eol[0] == '\0' ? "%s: %*s%s\n" : "%s: %*s%s",
++                    item_name, pad, "", content
++            );
++        else
++            strbuf_append_strf(result,
++                    eol[0] == '\0' ? "%s\n" : "%s",
++                    content
++            );
++    }
++    else
++    {
++        /* multi-line item */
++        if (print_item_name)
++            strbuf_append_strf(result, "%s:\n", item_name);
++        for (;;)
++        {
++            eol = strchrnul(content, '\n');
++            strbuf_append_strf(result,
++                    /* For %bare_multiline_item, we don't want to print colons */
++                    (print_item_name ? ":%.*s\n" : "%.*s\n"),
++                    (int)(eol - content), content
++            );
++            if (eol[0] == '\0' || eol[1] == '\0')
++                break;
++            content = eol + 1;
++        }
++    }
++    return 1;
++}
++
++static int
++append_short_backtrace(struct strbuf *result, problem_data_t *problem_data, size_t max_text_size, bool print_item_name)
++{
++    const problem_item *item = problem_data_get_item_or_NULL(problem_data,
++                                                             FILENAME_BACKTRACE);
++    if (!item)
++        return 0; /* "I did not print anything" */
++    if (!(item->flags & CD_FLAG_TXT))
++        return 0; /* "I did not print anything" */
++
++    char *truncated = NULL;
++
++    if (strlen(item->content) >= max_text_size)
++    {
++        log_debug("'backtrace' exceeds the text file size, going to append its short version");
++
++        char *error_msg = NULL;
++        const char *type = problem_data_get_content_or_NULL(problem_data, FILENAME_TYPE);
++        if (!type)
++        {
++            log_debug("Problem data does not contain '"FILENAME_TYPE"' file");
++            return 0;
++        }
++
++        /* For CCpp crashes, use the GDB-produced backtrace which should be
++         * available by now. sr_abrt_type_from_analyzer returns SR_REPORT_CORE
++         * by default for CCpp crashes.
++         */
++        enum sr_report_type report_type = sr_abrt_type_from_analyzer(type);
++        if (strcmp(type, "CCpp") == 0)
++        {
++            log_debug("Successfully identified 'CCpp' abrt type");
++            report_type = SR_REPORT_GDB;
++        }
++
++        struct sr_stacktrace *backtrace = sr_stacktrace_parse(report_type,
++                item->content, &error_msg);
++
++        if (!backtrace)
++        {
++            log(_("Can't parse backtrace: %s"), error_msg);
++            free(error_msg);
++            return 0;
++        }
++
++        /* Get optimized thread stack trace for 10 top most frames */
++        truncated = sr_stacktrace_to_short_text(backtrace, 10);
++        sr_stacktrace_free(backtrace);
++
++        if (!truncated)
++        {
++            log(_("Can't generate stacktrace description (no crash thread?)"));
++            return 0;
++        }
++    }
++    else
++    {
++        log_debug("'backtrace' is small enough to be included as is");
++    }
++
++    append_text(result,
++                /*item_name:*/ truncated ? "truncated_backtrace" : FILENAME_BACKTRACE,
++                /*content:*/   truncated ? truncated             : item->content,
++                print_item_name
++    );
++    free(truncated);
++    return 1;
++}
++
++static int
++append_item(struct strbuf *result, const char *item_name, problem_data_t *pd, GList *comment_fmt_spec)
++{
++    bool print_item_name = (strncmp(item_name, "%bare_", strlen("%bare_")) != 0);
++    if (!print_item_name)
++        item_name += strlen("%bare_");
++
++    if (item_name[0] != '%')
++    {
++        struct problem_item *item = problem_data_get_item_or_NULL(pd, item_name);
++        if (!item)
++            return 0; /* "I did not print anything" */
++        if (!(item->flags & CD_FLAG_TXT))
++            return 0; /* "I did not print anything" */
++
++        char *formatted = problem_item_format(item);
++        char *content = formatted ? formatted : item->content;
++        append_text(result, item_name, content, print_item_name);
++        free(formatted);
++        return 1; /* "I printed something" */
++    }
++
++    /* Special item name */
++
++    /* Compat with previously-existed ad-hockery: %short_backtrace */
++    if (strcmp(item_name, "%short_backtrace") == 0)
++        return append_short_backtrace(result, pd, CD_TEXT_ATT_SIZE_BZ, print_item_name);
++
++    /* Compat with previously-existed ad-hockery: %reporter */
++    if (strcmp(item_name, "%reporter") == 0)
++        return append_text(result, "reporter", PACKAGE"-"VERSION, print_item_name);
++
++    /* %oneline,%multiline,%text */
++    bool oneline   = (strcmp(item_name+1, "oneline"  ) == 0);
++    bool multiline = (strcmp(item_name+1, "multiline") == 0);
++    bool text      = (strcmp(item_name+1, "text"     ) == 0);
++    if (!oneline && !multiline && !text)
++    {
++        log("Unknown or unsupported element specifier '%s'", item_name);
++        return 0; /* "I did not print anything" */
++    }
++
++    int printed = 0;
++
++    /* Iterate over _sorted_ items */
++    GList *sorted_names = g_hash_table_get_keys(pd);
++    sorted_names = g_list_sort(sorted_names, (GCompareFunc)strcmp);
++
++    /* %text => do as if %oneline, then repeat as if %multiline */
++    if (text)
++        oneline = 1;
++
++ again: ;
++    GList *l = sorted_names;
++    while (l)
++    {
++        const char *name = l->data;
++        l = l->next;
++        struct problem_item *item = g_hash_table_lookup(pd, name);
++        if (!item)
++            continue; /* paranoia, won't happen */
++
++        if (!(item->flags & CD_FLAG_TXT))
++            continue;
++
++        if (is_explicit_or_forbidden(name, comment_fmt_spec))
++            continue;
++
++        char *formatted = problem_item_format(item);
++        char *content = formatted ? formatted : item->content;
++        char *eol = strchrnul(content, '\n');
++        bool is_oneline = (eol[0] == '\0' || eol[1] == '\0');
++        if (oneline == is_oneline)
++            printed |= append_text(result, name, content, print_item_name);
++        free(formatted);
++    }
++    if (text && oneline)
++    {
++        /* %text, and we just did %oneline. Repeat as if %multiline */
++        oneline = 0;
++        /*multiline = 1; - not checked in fact, so why bother setting? */
++        goto again;
++    }
++
++    g_list_free(sorted_names); /* names themselves are not freed */
++
++    return printed;
++}
++
++#define add_to_section_output(format, ...) \
++    do { \
++    for (; empty_lines > 0; --empty_lines) fputc('\n', result); \
++    empty_lines = 0; \
++    fprintf(result, format, __VA_ARGS__); \
++    } while (0)
++
++static void
++format_section(section_t *section, problem_data_t *pd, GList *comment_fmt_spec, FILE *result)
++{
++    int empty_lines = -1;
++
++    for (GList *iter = section->children; iter; iter = g_list_next(iter))
++    {
++        section_t *child = (section_t *)iter->data;
++        if (child->items)
++        {
++            /* "Text: item[,item]..." */
++            struct strbuf *output = strbuf_new();
++            GList *item = child->items;
++            while (item)
++            {
++                const char *str = item->data;
++                item = item->next;
++                if (str[0] == '-') /* "-name", ignore it */
++                    continue;
++                append_item(output, str, pd, comment_fmt_spec);
++            }
++
++            if (output->len != 0)
++                add_to_section_output((child->name[0] ? "%s:\n%s" : "%s%s"),
++                                      child->name, output->buf);
++
++            strbuf_free(output);
++        }
++        else
++        {
++            /* Just "Text" (can be "") */
++
++            /* Filter out trailint empty lines */
++            if (child->name[0] != '\0')
++                add_to_section_output("%s\n", child->name);
++            /* Do not count empty lines, if output wasn't yet produced */
++            else if (empty_lines >= 0)
++                ++empty_lines;
++        }
++    }
++}
++
++static GList *
++get_special_items(const char *item_name, problem_data_t *pd, GList *comment_fmt_spec)
++{
++    /* %oneline,%multiline,%text,%binary */
++    bool oneline   = (strcmp(item_name+1, "oneline"  ) == 0);
++    bool multiline = (strcmp(item_name+1, "multiline") == 0);
++    bool text      = (strcmp(item_name+1, "text"     ) == 0);
++    bool binary    = (strcmp(item_name+1, "binary"   ) == 0);
++    if (!oneline && !multiline && !text && !binary)
++    {
++        log("Unknown or unsupported element specifier '%s'", item_name);
++        return NULL;
++    }
++
++    log_debug("Special item_name '%s', iterating for attach...", item_name);
++    GList *result = 0;
++
++    /* Iterate over _sorted_ items */
++    GList *sorted_names = g_hash_table_get_keys(pd);
++    sorted_names = g_list_sort(sorted_names, (GCompareFunc)strcmp);
++
++    GList *l = sorted_names;
++    while (l)
++    {
++        const char *name = l->data;
++        l = l->next;
++        struct problem_item *item = g_hash_table_lookup(pd, name);
++        if (!item)
++            continue; /* paranoia, won't happen */
++
++        if (is_explicit_or_forbidden(name, comment_fmt_spec))
++            continue;
++
++        if ((item->flags & CD_FLAG_TXT) && !binary)
++        {
++            char *content = item->content;
++            char *eol = strchrnul(content, '\n');
++            bool is_oneline = (eol[0] == '\0' || eol[1] == '\0');
++            if (text || oneline == is_oneline)
++                result = g_list_append(result, xstrdup(name));
++        }
++        else if ((item->flags & CD_FLAG_BIN) && binary)
++            result = g_list_append(result, xstrdup(name));
++    }
++
++    g_list_free(sorted_names); /* names themselves are not freed */
++
++
++    log_debug("...Done iterating over '%s' for attach", item_name);
++
++    return result;
++}
++
++static GList *
++get_attached_files(problem_data_t *pd, GList *items, GList *comment_fmt_spec)
++{
++    GList *result = NULL;
++    GList *item = items;
++    while (item != NULL)
++    {
++        const char *item_name = item->data;
++        item = item->next;
++        if (item_name[0] == '-') /* "-name", ignore it */
++            continue;
++
++        if (item_name[0] != '%')
++        {
++            result = g_list_append(result, xstrdup(item_name));
++            continue;
++        }
++
++        GList *special = get_special_items(item_name, pd, comment_fmt_spec);
++        if (special == NULL)
++        {
++            log_notice("No attachment found for '%s'", item_name);
++            continue;
++        }
++
++        result = g_list_concat(result, special);
++    }
++
++    return result;
++}
++
++/*
++ * Problem Report - memor stream
++ *
++ * A wrapper for POSIX memory stream.
++ *
++ * A memory stream is presented as FILE *.
++ *
++ * A memory stream is associated with a pointer to written data and a pointer
++ * to size of the written data.
++ *
++ * This structure holds all of the used pointers.
++ */
++struct memstream_buffer
++{
++    char *msb_buffer;
++    size_t msb_size;
++    FILE *msb_stream;
++};
++
++static struct memstream_buffer *
++memstream_buffer_new()
++{
++    struct memstream_buffer *self = xmalloc(sizeof(*self));
++
++    self->msb_buffer = NULL;
++    self->msb_stream = open_memstream(&(self->msb_buffer), &(self->msb_size));
++
++    return self;
++}
++
++static void
++memstream_buffer_free(struct memstream_buffer *self)
++{
++    if (self == NULL)
++        return;
++
++    fclose(self->msb_stream);
++    self->msb_stream = DESTROYED_POINTER;
++
++    free(self->msb_buffer);
++    self->msb_buffer = DESTROYED_POINTER;
++
++    free(self);
++}
++
++static FILE *
++memstream_get_stream(struct memstream_buffer *self)
++{
++    assert(self != NULL);
++
++    return self->msb_stream;
++}
++
++static const char *
++memstream_get_string(struct memstream_buffer *self)
++{
++    assert(self != NULL);
++    assert(self->msb_stream != NULL);
++
++    fflush(self->msb_stream);
++
++    return self->msb_buffer;
++}
++
++
++/*
++ * Problem Report
++ *
++ * The formated strings are internaly stored in "buffer"s. If a programer wants
++ * to get a formated section data, a getter function extracts those data from
++ * the apropriate buffer and returns them in form of null-terminated string.
++ *
++ * Each section has own buffer.
++ *
++ * There are three common sections that are always present:
++ * 1. summary
++ * 2. description
++ * 3. attach
++ * Buffers of these sections has own structure member for the sake of
++ * efficiency.
++ *
++ * The custom sections hash their buffers stored in a map where key is a
++ * section's name and value is a section's buffer.
++ *
++ * Problem report provides the programers with the possibility to ammend
++ * formated output to any section buffer.
++ */
++struct problem_report
++{
++    struct memstream_buffer *pr_sec_summ; ///< %summary buffer
++    struct memstream_buffer *pr_sec_desc; ///< %description buffer
++    GList            *pr_attachments;     ///< %attach - list of file names
++    GHashTable       *pr_sec_custom;      ///< map : %(custom section) -> buffer
++};
++
++static problem_report_t *
++problem_report_new()
++{
++    problem_report_t *self = xmalloc(sizeof(*self));
++
++    self->pr_sec_summ = memstream_buffer_new();
++    self->pr_sec_desc = memstream_buffer_new();
++    self->pr_attachments = NULL;
++    self->pr_sec_custom = NULL;
++
++    return self;
++}
++
++static void
++problem_report_initialize_custom_sections(problem_report_t *self)
++{
++    assert(self != NULL);
++    assert(self->pr_sec_custom == NULL);
++
++    self->pr_sec_custom = g_hash_table_new_full(g_str_hash, g_str_equal, free,
++                                                (GDestroyNotify)memstream_buffer_free);
++}
++
++static void
++problem_report_destroy_custom_sections(problem_report_t *self)
++{
++    assert(self != NULL);
++    assert(self->pr_sec_custom != NULL);
++
++    g_hash_table_destroy(self->pr_sec_custom);
++}
++
++static int
++problem_report_add_custom_section(problem_report_t *self, const char *name)
++{
++    assert(self != NULL);
++
++    if (self->pr_sec_custom == NULL)
++    {
++        problem_report_initialize_custom_sections(self);
++    }
++
++    if (problem_report_get_buffer(self, name))
++    {
++        log_warning("Custom section already exists : '%s'", name);
++        return -EEXIST;
++    }
++
++    log_debug("Problem report enriched with section : '%s'", name);
++    g_hash_table_insert(self->pr_sec_custom, xstrdup(name), memstream_buffer_new());
++    return 0;
++}
++
++static struct memstream_buffer *
++problem_report_get_section_buffer(const problem_report_t *self, const char *section_name)
++{
++    if (self->pr_sec_custom == NULL)
++    {
++        log_debug("Couldn't find section '%s': no custom section added", section_name);
++        return NULL;
++    }
++
++    return (struct memstream_buffer *)g_hash_table_lookup(self->pr_sec_custom, section_name);
++}
++
++problem_report_buffer *
++problem_report_get_buffer(const problem_report_t *self, const char *section_name)
++{
++    assert(self != NULL);
++    assert(section_name != NULL);
++
++    if (strcmp(PR_SEC_SUMMARY, section_name) == 0)
++        return memstream_get_stream(self->pr_sec_summ);
++
++    if (strcmp(PR_SEC_DESCRIPTION, section_name) == 0)
++        return memstream_get_stream(self->pr_sec_desc);
++
++    struct memstream_buffer *buf = problem_report_get_section_buffer(self, section_name);
++    return buf == NULL ? NULL : memstream_get_stream(buf);
++}
++
++const char *
++problem_report_get_summary(const problem_report_t *self)
++{
++    assert(self != NULL);
++
++    return memstream_get_string(self->pr_sec_summ);
++}
++
++const char *
++problem_report_get_description(const problem_report_t *self)
++{
++    assert(self != NULL);
++
++    return memstream_get_string(self->pr_sec_desc);
++}
++
++const char *
++problem_report_get_section(const problem_report_t *self, const char *section_name)
++{
++    assert(self != NULL);
++    assert(section_name);
++
++    struct memstream_buffer *buf = problem_report_get_section_buffer(self, section_name);
++
++    if (buf == NULL)
++        return NULL;
++
++    return memstream_get_string(buf);
++}
++
++static void
++problem_report_set_attachments(problem_report_t *self, GList *attachments)
++{
++    assert(self != NULL);
++    assert(self->pr_attachments == NULL);
++
++    self->pr_attachments = attachments;
++}
++
++GList *
++problem_report_get_attachments(const problem_report_t *self)
++{
++    assert(self != NULL);
++
++    return self->pr_attachments;
++}
++
++void
++problem_report_free(problem_report_t *self)
++{
++    if (self == NULL)
++        return;
++
++    memstream_buffer_free(self->pr_sec_summ);
++    self->pr_sec_summ = DESTROYED_POINTER;
++
++    memstream_buffer_free(self->pr_sec_desc);
++    self->pr_sec_desc = DESTROYED_POINTER;
++
++    g_list_free_full(self->pr_attachments, free);
++    self->pr_attachments = DESTROYED_POINTER;
++
++    if (self->pr_sec_custom)
++    {
++        problem_report_destroy_custom_sections(self);
++        self->pr_sec_custom = DESTROYED_POINTER;
++    }
++
++    free(self);
++}
++
++/*
++ * Problem Formatter - extra section
++ */
++struct extra_section
++{
++    char *pfes_name;    ///< name with % prefix
++    int   pfes_flags;   ///< whether is required or not
++};
++
++static struct extra_section *
++extra_section_new(const char *name, int flags)
++{
++    struct extra_section *self = xmalloc(sizeof(*self));
++
++    self->pfes_name = xstrdup(name);
++    self->pfes_flags = flags;
++
++    return self;
++}
++
++static void
++extra_section_free(struct extra_section *self)
++{
++    if (self == NULL)
++        return;
++
++    free(self->pfes_name);
++    self->pfes_name = DESTROYED_POINTER;
++
++    free(self);
++}
++
++static int
++extra_section_name_cmp(struct extra_section *lhs, const char *rhs)
++{
++    return strcmp(lhs->pfes_name, rhs);
++}
++
++/*
++ * Problem Formatter
++ *
++ * Holds parsed sections lists.
++ */
++struct problem_formatter
++{
++    GList *pf_sections;         ///< parsed sections (struct section_t)
++    GList *pf_extra_sections;   ///< user configured sections (struct extra_section)
++    char  *pf_default_summary;  ///< default summary format
++};
++
++problem_formatter_t *
++problem_formatter_new(void)
++{
++    problem_formatter_t *self = xzalloc(sizeof(*self));
++
++    self->pf_default_summary = xstrdup("%reason%");
++
++    return self;
++}
++
++void
++problem_formatter_free(problem_formatter_t *self)
++{
++    if (self == NULL)
++        return;
++
++    g_list_free_full(self->pf_sections, (GDestroyNotify)section_free);
++    self->pf_sections = DESTROYED_POINTER;
++
++    g_list_free_full(self->pf_extra_sections, (GDestroyNotify)extra_section_free);
++    self->pf_extra_sections = DESTROYED_POINTER;
++
++    free(self->pf_default_summary);
++    self->pf_default_summary = DESTROYED_POINTER;
++
++    free(self);
++}
++
++static int
++problem_formatter_is_section_known(problem_formatter_t *self, const char *name)
++{
++  return    strcmp(name, "summary")     == 0
++         || strcmp(name, "attach")      == 0
++         || strcmp(name, "description") == 0
++         || NULL != g_list_find_custom(self->pf_extra_sections, name, (GCompareFunc)extra_section_name_cmp);
++}
++
++// i.e additional_info -> no flags
++int
++problem_formatter_add_section(problem_formatter_t *self, const char *name, int flags)
++{
++    /* Do not add already added sections */
++    if (problem_formatter_is_section_known(self, name))
++    {
++        log_debug("Extra section already exists : '%s' ", name);
++        return -EEXIST;
++    }
++
++    self->pf_extra_sections = g_list_prepend(self->pf_extra_sections,
++                                             extra_section_new(name, flags));
++
++    return 0;
++}
++
++// check format validity and produce warnings
++static int
++problem_formatter_validate(problem_formatter_t *self)
++{
++    int retval = 0;
++
++    /* Go through all (struct extra_section)s and check whete those having flag
++     * PFFF_REQUIRED are present in the parsed (struct section_t)s.
++     */
++    for (GList *iter = self->pf_extra_sections; iter; iter = g_list_next(iter))
++    {
++        struct extra_section *section = (struct extra_section *)iter->data;
++
++        log_debug("Validating extra section : '%s'", section->pfes_name);
++
++        if (   (PFFF_REQUIRED & section->pfes_flags)
++            && NULL == g_list_find_custom(self->pf_sections, section->pfes_name, (GCompareFunc)section_name_cmp))
++        {
++            log_warning("Problem format misses required section : '%s'", section->pfes_name);
++            ++retval;
++        }
++    }
++
++    /* Go through all the parsed (struct section_t)s check whether are all
++     * known, i.e. each section is either one of the common sections (summary,
++     * description, attach) or is present in the (struct extra_section)s.
++     */
++    for (GList *iter = self->pf_sections; iter; iter = g_list_next(iter))
++    {
++        section_t *section = (section_t *)iter->data;
++
++        if (!problem_formatter_is_section_known(self, (section->name + 1)))
++        {
++            log_warning("Problem format contains unrecognized section : '%s'", section->name);
++            ++retval;
++        }
++    }
++
++    return retval;
++}
++
++int
++problem_formatter_load_string(problem_formatter_t *self, const char *fmt)
++{
++    const size_t len = strlen(fmt);
++    if (len != 0)
++    {
++        FILE *fp = fmemopen((void *)fmt, len, "r");
++        if (fp == NULL)
++        {
++            error_msg("Not enough memory to open a stream for reading format string.");
++            return -ENOMEM;
++        }
++
++        self->pf_sections = load_stream(fp);
++        fclose(fp);
++    }
++
++    return problem_formatter_validate(self);
++}
++
++int
++problem_formatter_load_file(problem_formatter_t *self, const char *path)
++{
++    FILE *fp = stdin;
++    if (strcmp(path, "-") != 0)
++    {
++        fp = fopen(path, "r");
++        if (!fp)
++            return -ENOENT;
++    }
++
++    self->pf_sections = load_stream(fp);
++
++    if (fp != stdin)
++        fclose(fp);
++
++    return problem_formatter_validate(self);
++}
++
++// generates report
++int
++problem_formatter_generate_report(const problem_formatter_t *self, problem_data_t *data, problem_report_t **report)
++{
++    problem_report_t *pr = problem_report_new();
++
++    for (GList *iter = self->pf_extra_sections; iter; iter = g_list_next(iter))
++        problem_report_add_custom_section(pr, ((struct extra_section *)iter->data)->pfes_name);
++
++    bool has_summary = false;
++    for (GList *iter = self->pf_sections; iter; iter = g_list_next(iter))
++    {
++        section_t *section = (section_t *)iter->data;
++
++        /* %summary is something special */
++        if (strcmp(section->name, "%summary") == 0)
++        {
++            has_summary = true;
++            format_percented_string((const char *)section->items->data, data,
++                                    problem_report_get_buffer(pr, PR_SEC_SUMMARY));
++        }
++        /* %attach as well */
++        else if (strcmp(section->name, "%attach") == 0)
++        {
++            problem_report_set_attachments(pr, get_attached_files(data, section->items, self->pf_sections));
++        }
++        else /* %description or a custom section (e.g. %additional_info) */
++        {
++            FILE *buffer = problem_report_get_buffer(pr, section->name + 1);
++
++            if (buffer != NULL)
++            {
++                log_debug("Formatting section : '%s'", section->name);
++                format_section(section, data, self->pf_sections, buffer);
++            }
++            else
++                log_warning("Unsupported section '%s'", section->name);
++        }
++    }
++
++    if (!has_summary) {
++        log_debug("Problem format misses section '%%summary'. Using the default one : '%s'.",
++                    self->pf_default_summary);
++
++        format_percented_string(self->pf_default_summary,
++                   data, problem_report_get_buffer(pr, PR_SEC_SUMMARY));
++    }
++
++    *report = pr;
++    return 0;
++}
+diff --git a/src/plugins/problem_report.h b/src/plugins/problem_report.h
+new file mode 100644
+index 0000000..f2d41d8
+--- /dev/null
++++ b/src/plugins/problem_report.h
+@@ -0,0 +1,334 @@
++/*
++    Copyright (C) 2014  ABRT team
++    Copyright (C) 2014  RedHat Inc
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 2 of the License, or
++    (at your option) any later version.
++
++    This program is distributed in the hope that it will be useful,
++    but WITHOUT ANY WARRANTY; without even the implied warranty of
++    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++    GNU General Public License for more details.
++
++    You should have received a copy of the GNU General Public License along
++    with this program; if not, write to the Free Software Foundation, Inc.,
++    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++
++    @brief API for formating of problem data
++
++    These functions can be used to convert a problem data to its string
++    representation.
++
++    The output format can be parsed from a string:
++
++        problem_formatter_t *formatter = problem_formatter_new();
++        problem_formatter_load_string(formatter, MY_FORMAT_STRING);
++
++    or loaded from a file:
++
++        problem_formatter_t *formatter = problem_formatter_new();
++        problem_formatter_load_file(formatter, MY_FORMAT_FILE);
++
++    Once you have configured your formatter you can convert problem_data to
++    problem_report by calling:
++
++        problem_report_t *report;
++        if (problem_formatter_generate_report(formatter, data, &report) != 0)
++            errx(EXIT_FAILURE, "Problem data cannot be converted to problem report.");
++
++    Now you can print the report:
++
++        printf("Problem: %s\n", problem_report_get_summary());
++        printf("%s\n",          problem_report_get_description());
++
++        puts("Problem attachments:");
++        for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a))
++            printf(" %s\n", a->data);
++
++    Format description:
++
++         ----
++         %summary:: summary format
++         %attach:: elemnt1[,element2]...
++         section:: element1[,element2]...
++         The literal text line to be added to report.
++         ----
++
++         Summary format is a line of text, where %element% is replaced by
++         text element's content, and [[...%element%...]] block is used only if
++         %element% exists. [[...]] blocks can nest.
++
++         Sections can be:
++         - %summary: bug summary format string.
++
++         - %attach: a list of elements to attach.
++
++         - text, double colon (::) and the list of comma-separated elements.
++           Text can be empty (":: elem1, elem2, elem3" works),
++           in this case "Text:" header line will be omitted.
++
++         - %description: this section is implicit and contains all text
++           sections unless another section was specified (%summary and %attach
++           are ignored when determining text section's placement)
++
++         - every text element belongs to the last specified section (%summary
++           and %attach sections are ignored). If no section was specified,
++           the text element belogns to %description.
++
++         - If none of elements exists, the section will not be created.
++
++         - Empty lines are NOT ignored.
++
++         Elements can be:
++         - problem directory element names, which get formatted as
++           <element_name>: <contents>
++           or
++           <element_name>:
++           :<contents>
++           :<contents>
++           :<contents>
++
++         - problem directory element names prefixed by "%bare_",
++           which is formatted as-is, without "<element_name>:" and colons
++
++         - %oneline, %multiline, %text wildcards, which select all corresponding
++           elements for output or attachment
++
++         - %binary wildcard, valid only for %attach section, instructs to attach
++           binary elements
++
++         - problem directory element names prefixed by "-",
++           which excludes given element from all wildcards
++
++         - Nonexistent elements are silently ignored.
++
++    You can add your own section:
++
++        problem_formatter_t *formatter = problem_formatter_new();
++        problem_formatter_add_section(formatter, "additional_info", PFFF_REQUIRED);
++
++    and then you can use the section in the formatting string:
++
++        problem_formatter_load_string(formatter,
++                "::comment\n"
++                "%additional_info:: maps");
++        problem_formatter_generate_report(formatter, data, &report);
++
++        printf("Problem: %s\n",         problem_report_get_summary());
++        printf("%s\n",                  problem_report_get_description());
++        printf("Additional info: %s\n", problem_report_get_section(report, "additiona_info"));
++
++    The lines above are equivalent to the following lines:
++
++        printf("Problem: %s\n",         problem_data_get_content_or_NULL(data, "reason"));
++        printf("%s\n",                  problem_data_get_content_or_NULL(data, "comment"));
++        printf("Additional info: %s\n", problem_data_get_content_or_NULL(data, "maps"));
++*/
++#ifndef LIBREPORT_PROBLEM_REPORT_H
++#define LIBREPORT_PROBLEM_REPORT_H
++
++#include <glib.h>
++#include <stdio.h>
++#include "problem_data.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#define PR_SEC_SUMMARY "summary"
++#define PR_SEC_DESCRIPTION "description"
++
++/*
++ * The problem report structure represents a problem data formatted according
++ * to a format string.
++ *
++ * A problem report is composed of well-known sections:
++ *   - summary
++ *   - descritpion
++ *   - attach
++ *
++ * and custom sections accessed by:
++ *   problem_report_get_section();
++ */
++struct problem_report;
++typedef struct problem_report problem_report_t;
++
++/*
++ * Helpers for easily switching between FILE and struct strbuf
++ */
++
++/*
++ * Type of buffer used by Problem report
++ */
++typedef FILE problem_report_buffer;
++
++/*
++ * Wrapper for the proble buffer's formated output function.
++ */
++#define problem_report_buffer_printf(buf, fmt, ...)\
++    fprintf((buf), (fmt), ##__VA_ARGS__)
++
++
++/*
++ * Get a section buffer
++ *
++ * Use this function if you need to amend something to a formatted section.
++ *
++ * @param self Problem report
++ * @param section_name Name of required section
++ * @return Always valid pointer to a section buffer
++ */
++problem_report_buffer *problem_report_get_buffer(const problem_report_t *self,
++        const char *section_name);
++
++/*
++ * Get Summary string
++ *
++ * The returned pointer is valid as long as you perform no further output to
++ * the summary buffer.
++ *
++ * @param self Problem report
++ * @return Non-NULL pointer to summary data
++ */
++const char *problem_report_get_summary(const problem_report_t *self);
++
++/*
++ * Get Description string
++ *
++ * The returned pointer is valid as long as you perform no further output to
++ * the description buffer.
++ *
++ * @param self Problem report
++ * @return Non-NULL pointer to description data
++ */
++const char *problem_report_get_description(const problem_report_t *self);
++
++/*
++ * Get Section's string
++ *
++ * The returned pointer is valid as long as you perform no further output to
++ * the section's buffer.
++ *
++ * @param self Problem report
++ * @param section_name Name of the required section
++ * @return Non-NULL pointer to description data
++ */
++const char *problem_report_get_section(const problem_report_t *self,
++        const char *section_name);
++
++/*
++ * Get GList of the problem data items that are to be attached
++ *
++ * @param self Problem report
++ * @return A pointer to GList (NULL means empty list)
++ */
++GList *problem_report_get_attachments(const problem_report_t *self);
++
++/*
++ * Releases all resources allocated by a problem report
++ *
++ * @param self Problem report
++ */
++void problem_report_free(problem_report_t *self);
++
++
++/*
++ * An enum of Extra section flags
++ */
++enum problem_formatter_section_flags {
++    PFFF_REQUIRED = 1 << 0, ///< section must be present in the format spec
++};
++
++/*
++ * The problem formatter structure formats a problem data according to a format
++ * string and stores result a problem report.
++ *
++ * The problem formatter uses '%reason%' as %summary section format string, if
++ * %summary is not provided by a format string.
++ */
++struct problem_formatter;
++typedef struct problem_formatter problem_formatter_t;
++
++/*
++ * Constructs a new problem formatter.
++ *
++ * @return Non-NULL pointer to the new problem formatter
++ */
++problem_formatter_t *problem_formatter_new(void);
++
++/*
++ * Releases all resources allocated by a problem formatter
++ *
++ * @param self Problem formatter
++ */
++void problem_formatter_free(problem_formatter_t *self);
++
++/*
++ * Adds a new recognized section
++ *
++ * The problem formatter ignores a section in the format spec if the section is
++ * not one of the default nor added by this function.
++ *
++ * How the problem formatter handles these extra sections:
++ *
++ * A custom section is something like %description section. %description is the
++ * default section where all text (sub)sections are stored. If the formatter
++ * finds the custom section in format string, then starts storing text
++ * (sub)sections in the custom section.
++ *
++ * (%description)    |:: comment
++ * (%description)    |
++ * (%description)    |Package:: package
++ * (%description)    |
++ * (%additiona_info) |%additional_info::
++ * (%additiona_info) |%reporter%
++ * (%additiona_info) |User:: user_name,uid
++ * (%additiona_info) |
++ * (%additiona_info) |Directories:: root,cwd
++ *
++ *
++ * @param self Problem formatter
++ * @param name Name of the added section
++ * @param flags Info about the added section
++ * @return Zero on success. -EEXIST if the name is already known by the formatter
++ */
++int problem_formatter_add_section(problem_formatter_t *self, const char *name, int flags);
++
++/*
++ * Loads a problem format from a string.
++ *
++ * @param self Problem formatter
++ * @param fmt Format
++ * @return Zero on success or number of warnings (e.g. missing section,
++ * unrecognized section).
++ */
++int problem_formatter_load_string(problem_formatter_t* self, const char *fmt);
++
++/*
++ * Loads a problem format from a file.
++ *
++ * @param self Problem formatter
++ * @param pat Path to the format file
++ * @return Zero on success or number of warnings (e.g. missing section,
++ * unrecognized section).
++ */
++int problem_formatter_load_file(problem_formatter_t* self, const char *path);
++
++/*
++ * Creates a new problem report, formats the data according to the loaded
++ * format string and stores output in the report.
++ *
++ * @param self Problem formatter
++ * @param data Problem data to format
++ * @param report Pointer where the created problem report is to be stored
++ * @return Zero on success, otherwise non-zero value.
++ */
++int problem_formatter_generate_report(const problem_formatter_t *self, problem_data_t *data, problem_report_t **report);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif // LIBREPORT_PROBLEM_REPORT_H
+diff --git a/tests/testsuite.at b/tests/testsuite.at
+index ccb37d1..d4648d4 100644
+--- a/tests/testsuite.at
++++ b/tests/testsuite.at
+@@ -17,7 +17,7 @@ m4_include([xml_definition.at])
+ m4_include([report_python.at])
+ m4_include([string_list.at])
+ m4_include([ureport.at])
+-m4_include([problem_report.at])
++# m4_include([problem_report.at])
+ m4_include([dump_dir.at])
+ m4_include([global_config.at])
+ m4_include([iso_date.at])
+-- 
+1.8.3.1
+
diff --git a/SOURCES/1012-reporter-mantisbt-add-event-for-reporting-AVCs.patch b/SOURCES/1012-reporter-mantisbt-add-event-for-reporting-AVCs.patch
new file mode 100644
index 0000000..84924bd
--- /dev/null
+++ b/SOURCES/1012-reporter-mantisbt-add-event-for-reporting-AVCs.patch
@@ -0,0 +1,245 @@
+From 9a7a376a236e24c448eb650328d0d4841026568b Mon Sep 17 00:00:00 2001
+From: Matej Habrnal <mhabrnal@redhat.com>
+Date: Wed, 13 May 2015 16:37:19 +0200
+Subject: [PATCH 1012/1015] reporter-mantisbt: add event for reporting AVCs
+
+Without this commit is not possible to report AVCs because there are not event
+for 'report_CentOSBugTracker' with analyzer=libreport which is used for
+reporting AVCs.
+
+Related to bugs.centos#8422 and libreport#348
+
+Signed-off-by: Matej Habrnal <mhabrnal@redhat.com>
+---
+ doc/Makefile.am                                    |  2 +
+ doc/mantisbt_format_analyzer_libreport.conf.txt    | 18 +++++++
+ doc/mantisbt_formatdup_analyzer_libreport.conf.txt | 18 +++++++
+ src/plugins/Makefile.am                            |  4 +-
+ src/plugins/centos_report_event.conf               |  5 ++
+ .../mantisbt_format_analyzer_libreport.conf        | 59 ++++++++++++++++++++++
+ .../mantisbt_formatdup_analyzer_libreport.conf     | 56 ++++++++++++++++++++
+ 7 files changed, 161 insertions(+), 1 deletion(-)
+ create mode 100644 doc/mantisbt_format_analyzer_libreport.conf.txt
+ create mode 100644 doc/mantisbt_formatdup_analyzer_libreport.conf.txt
+ create mode 100644 src/plugins/mantisbt_format_analyzer_libreport.conf
+ create mode 100644 src/plugins/mantisbt_formatdup_analyzer_libreport.conf
+
+diff --git a/doc/Makefile.am b/doc/Makefile.am
+index e437388..c10deb4 100644
+--- a/doc/Makefile.am
++++ b/doc/Makefile.am
+@@ -41,6 +41,8 @@ MAN5_TXT += bugzilla_format_libreport.conf.txt
+ MAN5_TXT += mantisbt.conf.txt
+ MAN5_TXT += mantisbt_format.conf.txt
+ MAN5_TXT += mantisbt_formatdup.conf.txt
++MAN5_TXT += mantisbt_format_analyzer_libreport.conf.txt
++MAN5_TXT += mantisbt_formatdup_analyzer_libreport.conf.txt
+ MAN5_TXT += emergencyanalysis_event.conf.txt
+ MAN5_TXT += forbidden_words.conf.txt
+ MAN5_TXT += mailx.conf.txt
+diff --git a/doc/mantisbt_format_analyzer_libreport.conf.txt b/doc/mantisbt_format_analyzer_libreport.conf.txt
+new file mode 100644
+index 0000000..8cbd327
+--- /dev/null
++++ b/doc/mantisbt_format_analyzer_libreport.conf.txt
+@@ -0,0 +1,18 @@
++mantisbt_format_analyzer_libreport.conf(5)
++==========================================
++
++NAME
++----
++mantisbt_format_analyzer_libreport.conf - configuration file for libreport.
++
++DESCRIPTION
++-----------
++This configuration file provides definition of general formatting for duplicate MantisBT issues.
++
++SEE ALSO
++--------
++reporter-mantisbt(1)
++
++AUTHOR
++------
++* ABRT Team
+diff --git a/doc/mantisbt_formatdup_analyzer_libreport.conf.txt b/doc/mantisbt_formatdup_analyzer_libreport.conf.txt
+new file mode 100644
+index 0000000..cd082de
+--- /dev/null
++++ b/doc/mantisbt_formatdup_analyzer_libreport.conf.txt
+@@ -0,0 +1,18 @@
++mantisbt_formatdup_analyzer_libreport.conf(5)
++=============================================
++
++NAME
++----
++mantisbt_formatdup_analyzer_libreport.conf - configuration file for libreport.
++
++DESCRIPTION
++-----------
++This configuration file provides definition of general formatting for duplicate MantisBT issues.
++
++SEE ALSO
++--------
++reporter-mantisbt(1)
++
++AUTHOR
++------
++* ABRT Team
+diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
+index f4f94ff..3c1dfff 100644
+--- a/src/plugins/Makefile.am
++++ b/src/plugins/Makefile.am
+@@ -42,7 +42,9 @@ endif
+ if BUILD_MANTISBT
+ reporters_plugin_conf += mantisbt.conf
+ reporters_plugin_format_conf += mantisbt_format.conf \
+-    mantisbt_formatdup.conf
++    mantisbt_formatdup.conf \
++    mantisbt_format_analyzer_libreport.conf \
++    mantisbt_formatdup_analyzer_libreport.conf
+ endif
+ 
+ defaultreportpluginsconfdir = $(DEFAULT_REPORT_PLUGINS_CONF_DIR)
+diff --git a/src/plugins/centos_report_event.conf b/src/plugins/centos_report_event.conf
+index 53f12d8..adbca93 100644
+--- a/src/plugins/centos_report_event.conf
++++ b/src/plugins/centos_report_event.conf
+@@ -35,3 +35,8 @@ EVENT=report_CentOSBugTracker analyzer=CCpp duphash!=
+                 -F "/etc/libreport/plugins/$format" \
+                 -A "/etc/libreport/plugins/$formatdup"
+ 
++EVENT=report_CentOSBugTracker analyzer=libreport
++    reporter-mantisbt \
++        -c /etc/libreport/plugins/mantisbt.conf \
++        -F /etc/libreport/plugins/mantisbt_format_analyzer_libreport.conf \
++        -A /etc/libreport/plugins/mantisbt_formatdup_analyzer_libreport.conf
+diff --git a/src/plugins/mantisbt_format_analyzer_libreport.conf b/src/plugins/mantisbt_format_analyzer_libreport.conf
+new file mode 100644
+index 0000000..a514e38
+--- /dev/null
++++ b/src/plugins/mantisbt_format_analyzer_libreport.conf
+@@ -0,0 +1,59 @@
++# Lines starting with # are ignored.
++# Lines can be continued on the next line using trailing backslash.
++#
++# Format:
++# %summary:: summary format
++# section:: element1[,element2]...
++# The literal text line to be added to Bugzilla comment. Can be empty.
++# (IOW: empty lines are NOT ignored!)
++#
++# Summary format is a line of text, where %element% is replaced by
++# text element's content, and [[...%element%...]] block is used only if
++# %element% exists. [[...]] blocks can nest.
++#
++# Sections can be:
++# - %summary: bug summary format string.
++# - %attach: a list of elements to attach.
++# - text, double colon (::) and the list of comma-separated elements.
++#   Text can be empty (":: elem1, elem2, elem3" works),
++#   in this case "Text:" header line will be omitted.
++#
++# Elements can be:
++# - problem directory element names, which get formatted as
++#   <element_name>: <contents>
++#   or
++#   <element_name>:
++#   :<contents>
++#   :<contents>
++#   :<contents>
++# - problem directory element names prefixed by "%bare_",
++#   which is formatted as-is, without "<element_name>:" and colons
++# - %oneline, %multiline, %text wildcards, which select all corresponding
++#   elements for output or attachment
++# - %binary wildcard, valid only for %attach section, instructs to attach
++#   binary elements
++# - problem directory element names prefixed by "-",
++#   which excludes given element from all wildcards
++#
++#   Nonexistent elements are silently ignored.
++#   If none of elements exists, the section will not be created.
++
++%summary:: %reason%
++
++Description of problem:: %bare_comment, %bare_description
++
++Version-Release number of selected component:: %bare_package
++
++Truncated backtrace:: %bare_%short_backtrace
++
++%Additional info::
++:: -pkg_arch,-pkg_epoch,-pkg_name,-pkg_release,-pkg_version,\
++    -component,-architecture,\
++    -analyzer,-count,-duphash,-uuid,-abrt_version,\
++    -username,-hostname,-os_release,-os_info,\
++    -time,-pid,-pwd,-last_occurrence,-ureports_counter,\
++    %reporter,\
++    %oneline
++
++%attach:: -reported_to,-comment,-reason,-event_log,%multiline,\
++    -coredump,%binary
+diff --git a/src/plugins/mantisbt_formatdup_analyzer_libreport.conf b/src/plugins/mantisbt_formatdup_analyzer_libreport.conf
+new file mode 100644
+index 0000000..d9ab0e3
+--- /dev/null
++++ b/src/plugins/mantisbt_formatdup_analyzer_libreport.conf
+@@ -0,0 +1,56 @@
++# Lines starting with # are ignored.
++# Lines can be continued on the next line using trailing backslash.
++#
++# Format:
++# %summary:: summary format
++# section:: element1[,element2]...
++# The literal text line to be added to Bugzilla comment. Can be empty.
++# (IOW: empty lines are NOT ignored!)
++#
++# Summary format is a line of text, where %element% is replaced by
++# text element's content, and [[...%element%...]] block is used only if
++# %element% exists. [[...]] blocks can nest.
++#
++# Sections can be:
++# - %summary: bug summary format string.
++# - %attach: a list of elements to attach.
++# - text, double colon (::) and the list of comma-separated elements.
++#   Text can be empty (":: elem1, elem2, elem3" works),
++#   in this case "Text:" header line will be omitted.
++#
++# Elements can be:
++# - problem directory element names, which get formatted as
++#   <element_name>: <contents>
++#   or
++#   <element_name>:
++#   :<contents>
++#   :<contents>
++#   :<contents>
++# - problem directory element names prefixed by "%bare_",
++#   which is formatted as-is, without "<element_name>:" and colons
++# - %oneline, %multiline, %text wildcards, which select all corresponding
++#   elements for output or attachment
++# - %binary wildcard, valid only for %attach section, instructs to attach
++#   binary elements
++# - problem directory element names prefixed by "-",
++#   which excludes given element from all wildcards
++#
++#   Nonexistent elements are silently ignored.
++#   If none of elements exists, the section will not be created.
++
++Another user experienced a similar problem:
++
++# If user filled out comment field, show it:
++:: %bare_comment
++
++# var_log_messages has too much variance (time/date),
++# we exclude it from message so that dup message elimination has more chances to work
++:: \
++    -pkg_arch,-pkg_epoch,-pkg_name,-pkg_release,-pkg_version,\
++        -component,-architecture,\
++    -analyzer,-count,-duphash,-uuid,-abrt_version,\
++    -username,-hostname,-os_release,-os_info,\
++    -time,-pid,-pwd,-last_occurrence,-ureports_counter,\
++    -var_log_messages,\
++    %reporter,\
++    %oneline
+-- 
+1.8.3.1
+
diff --git a/SOURCES/1014-event-disable-report_RHTSupport-event-and-change-URL.patch b/SOURCES/1014-event-disable-report_RHTSupport-event-and-change-URL.patch
new file mode 100644
index 0000000..3765c62
--- /dev/null
+++ b/SOURCES/1014-event-disable-report_RHTSupport-event-and-change-URL.patch
@@ -0,0 +1,39 @@
+From 93cfff1cd5ca8f418709e4fa0e1b1c9d5b15ddcf Mon Sep 17 00:00:00 2001
+From: Matej Habrnal <mhabrnal@redhat.com>
+Date: Fri, 12 Jun 2015 07:51:50 +0200
+Subject: [PATCH 1014/1015] event: disable report_RHTSupport event and change
+ URL for bugzilla
+
+Set the URL for bugzilla to non-exist one because we do not want the centos
+users report to bugzilla.
+
+Signed-off-by: Matej Habrnal <mhabrnal@redhat.com>
+---
+ src/plugins/bugzilla.conf         | 2 +-
+ src/plugins/rhtsupport_event.conf | 4 ++--
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/plugins/bugzilla.conf b/src/plugins/bugzilla.conf
+index 51648de..159b188 100644
+--- a/src/plugins/bugzilla.conf
++++ b/src/plugins/bugzilla.conf
+@@ -1,5 +1,5 @@
+ # Bugzilla URL
+-BugzillaURL = https://bugzilla.redhat.com/
++BugzillaURL = https://bugzilla.example.com/
+ # yes means that ssl certificates will be checked
+ SSLVerify = yes
+ # your login has to exist, if you don have any, please create one
+diff --git a/src/plugins/rhtsupport_event.conf b/src/plugins/rhtsupport_event.conf
+index f6a9d47..4ccf9f5 100644
+--- a/src/plugins/rhtsupport_event.conf
++++ b/src/plugins/rhtsupport_event.conf
+@@ -1,3 +1,3 @@
+-EVENT=report_RHTSupport
++#EVENT=report_RHTSupport
+     # Submit an uReport and create a case in Red Hat Customer Portal
+-    reporter-rhtsupport -u
++    # reporter-rhtsupport -u
+-- 
+1.8.3.1
+
diff --git a/SPECS/libreport.spec b/SPECS/libreport.spec
index 5a8e49a..d1b71a2 100644
--- a/SPECS/libreport.spec
+++ b/SPECS/libreport.spec
@@ -238,6 +238,23 @@ Patch210: 0210-RHTSupport-include-count-in-Support-cases.patch
 Patch211: 0211-configure-set-version-to-2.1.11.1.patch
 Patch212: 0212-Translation-updates.patch
 
+Patch1000: 1000-bugzilla-port-to-Problem-Format-API.patch
+Patch1001: 1001-lib-created-a-new-lib-file-for-reporters.patch
+#Patch1002: 1002-spec-changed-spec-file-to-work-with-last-commit.patch
+Patch1003: 1003-ureport-set-url-to-public-faf-server.patch
+Patch1004: 1004-conf-changed-URL-for-sending-uReport.patch
+Patch1005: 1005-reporter-mantisbt-first-version-of-the-reporter-mant.patch
+#Patch1006: 1006-spec-changed-spec-file-to-work-with-reporter-mantisb.patch
+Patch1007: 1007-reporter-mantisbt-change-default-formating-file-for-.patch
+#Patch1008: 1008-spec-change-spec-file-to-work-with-last-commit.patch
+Patch1009: 1009-reporter-mantisbt-adds-man-pages-for-reporter-mantis.patch
+Patch1010: 1010-move-problem_report-to-plugins.patch
+#Patch1011: 1011-spec-change-related-to-moving-problem_report-to-plug.patch
+Patch1012: 1012-reporter-mantisbt-add-event-for-reporting-AVCs.patch
+#Patch1013: 1013-spec-add-files-related-to-reporting-AVCs-by-reporter.patch
+Patch1014: 1014-event-disable-report_RHTSupport-event-and-change-URL.patch
+#Patch1015: 1015-spec-remove-dependecy-on-redhat-access-insights.patch
+
 # git is need for '%%autosetup -S git' which automatically applies all the
 # patches above. Please, be aware that the patches must be generated
 # by 'git format-patch'
@@ -426,12 +443,32 @@ Uploads micro-report to abrt server
 %description plugin-bugzilla
 Plugin to report bugs into the bugzilla.
 
+%package plugin-mantisbt
+Summary: %{name}'s mantisbt plugin
+Group: System Environment/Libraries
+Requires: %{name} = %{version}-%{release}
+Requires: libreport-web = %{version}-%{release}
+
+%description plugin-mantisbt
+Plugin to report bugs into the mantisbt.
+
+%package centos
+Summary: %{name}'s CentOS Bug Tracker workflow
+Group: System Environment/Libraries
+Requires: %{name} = %{version}-%{release}
+Requires: libreport-web = %{version}-%{release}
+Requires: libreport-plugin-mantisbt = %{version}-%{release}
+
+%description centos
+Workflows to report issues into the CentOS Bug Tracker.
+
 %package plugin-rhtsupport
 Summary: %{name}'s RHTSupport plugin
 Group: System Environment/Libraries
 Requires: %{name} = %{version}-%{release}
 Requires: libreport-web = %{version}-%{release}
-Requires: redhat-access-insights
+# This feature is only for RHEL so we do not need another dependency
+# Requires: redhat-access-insights
 
 %description plugin-rhtsupport
 Plugin to report bugs into RH support system.
@@ -664,7 +701,6 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :
 %{_includedir}/libreport/dump_dir.h
 %{_includedir}/libreport/event_config.h
 %{_includedir}/libreport/problem_data.h
-%{_includedir}/libreport/problem_report.h
 %{_includedir}/libreport/report.h
 %{_includedir}/libreport/run_event.h
 %{_includedir}/libreport/file_obj.h
@@ -672,6 +708,7 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :
 %{_includedir}/libreport/workflow.h
 %{_includedir}/libreport/ureport.h
 %{_includedir}/libreport/global_configuration.h
+%{_includedir}/libreport/reporters.h
 # Private api headers:
 %{_includedir}/libreport/internal_abrt_dbus.h
 %{_includedir}/libreport/internal_libreport.h
@@ -791,6 +828,40 @@ 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
@@ -892,7 +963,7 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :
 
 
 %changelog
-* Thu Sep  1 2016 Matej Habrnal <mhabrnal@redhat.com> - 2.1.11-35
+* Thu Sep  1 2016 Matej Habrnal <mhabrnal@redhat.com> - 2.1.11-35.el7.centos
 - Translation updates
 - Related: #1304240