Ejemplo n.º 1
0
char *problem_data_get_content_or_NULL(problem_data_t *problem_data, const char *key)
{
    struct problem_item *item = problem_data_get_item_or_NULL(problem_data, key);
    if (!item)
        return NULL;
    return item->content;
}
Ejemplo n.º 2
0
char *problem_data_get_content_or_die(problem_data_t *problem_data, const char *key)
{
    struct problem_item *item = problem_data_get_item_or_NULL(problem_data, key);
    if (!item)
        error_msg_and_die(_("Essential element '%s' is missing, can't continue"), key);
    return item->content;
}
Ejemplo n.º 3
0
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)
    {
        struct sr_location location;
        sr_location_init(&location);

        /* sr_gdb_stacktrace_parse modifies the input parameter */
        char *content = item->content;
        struct sr_gdb_stacktrace *backtrace = sr_gdb_stacktrace_parse((const char **)&content, &location);

        if (!backtrace)
        {
            log(_("Can't parse backtrace"));
            return 0;
        }

        /* Get optimized thread stack trace for 10 top most frames */
        struct sr_gdb_thread *thread = sr_gdb_stacktrace_get_optimized_thread(backtrace, 10);

        sr_gdb_stacktrace_free(backtrace);

        if (!thread)
        {
            log(_("Can't find crash thread"));
            return 0;
        }

        /* Cannot be NULL, it dies on memory error */
        struct sr_strbuf *bt = sr_strbuf_new();

        sr_gdb_thread_append_to_str(thread, bt, true);

        sr_gdb_thread_free(thread);

        truncated = sr_strbuf_free_nobuf(bt);
    }

    append_text(result,
                /*item_name:*/ truncated ? "truncated_backtrace" : FILENAME_BACKTRACE,
                /*content:*/   truncated ? truncated             : item->content,
                print_item_name
    );
    free(truncated);
    return 1;
}
Ejemplo n.º 4
0
static msg_content_t *
create_journal_message(problem_data_t *problem_data, problem_report_t *pr, unsigned dump_opts)
{
    msg_content_t *msg_c = msg_content_new();

    /* mandatory fields */
    msg_content_add(msg_c, "MESSAGE", problem_report_get_summary(pr));
    msg_content_add(msg_c, "PRIORITY", MESSAGE_PRIORITY);

    /* add problem report description into PROBLEM_REPORT field */
    char *description = NULL;
    if (strcmp(problem_report_get_description(pr), "") != 0)
        description = xasprintf("\n%s", problem_report_get_description(pr));

    msg_content_add(msg_c, "PROBLEM_REPORT", description ? description : "");
    free(description);

    if (!(dump_opts & DUMP_FULL))
    {
        msg_content_add_fields(msg_c, problem_data, fields_default_no_prefix);
        msg_content_add_fields_ext(msg_c, problem_data, fields_default, FIELD_PREFIX);

        /* add defined default fields */
        if (dump_opts & DUMP_ESSENTIAL)
            msg_content_add_fields_ext(msg_c, problem_data, fields_essential, FIELD_PREFIX);
    }
    /* add all fields from problem directory */
    else
    {
        /* iterate over all problem_data elements */
        for (GList *elem = problem_data_get_all_elements(problem_data); elem != NULL; elem = elem->next)
        {
            const problem_item *item = problem_data_get_item_or_NULL(problem_data, elem->data);
            /* add only text elements */
            if (item && (item->flags & CD_FLAG_TXT))
            {
                /* elements listed in fields_default_no_prefix are added withou prefix */
                if (is_in_string_list(elem->data, fields_default_no_prefix))
                    msg_content_add(msg_c, elem->data, item->content);
                else
                    msg_content_add_ext(msg_c, elem->data, item->content, FIELD_PREFIX);
            }
        }
    }

    return msg_c;
}
Ejemplo n.º 5
0
void problem_data_reload_from_dump_dir(void)
{
    free(g_events);

    struct dump_dir *dd = dd_opendir(g_dump_dir_name, DD_OPEN_READONLY);
    if (!dd)
        xfunc_die(); /* dd_opendir already logged error msg */

    problem_data_t *new_cd = create_problem_data_from_dump_dir(dd);
    problem_data_add_text_noteditable(new_cd, CD_DUMPDIR, g_dump_dir_name);

    g_events = list_possible_events(dd, NULL, "");
    dd_close(dd);

    /* Copy "selected for reporting" flags */
    GHashTableIter iter;
    char *name;
    struct problem_item *new_item;
    g_hash_table_iter_init(&iter, new_cd);
    while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&new_item))
    {
        struct problem_item *old_item = g_cd ? problem_data_get_item_or_NULL(g_cd, name) : NULL;
        if (old_item)
        {
            new_item->selected_by_user = old_item->selected_by_user;
            new_item->allowed_by_reporter = old_item->allowed_by_reporter;
            new_item->default_by_reporter = old_item->default_by_reporter;
            new_item->required_by_reporter = old_item->required_by_reporter;
        }
        else
        {
            new_item->selected_by_user = 0;
            new_item->allowed_by_reporter = 0;
            new_item->default_by_reporter = 0;
            new_item->required_by_reporter = 0;
        }
        //log("%s: was ->selected_by_user=%d", __func__, new_item->selected_by_user);
    }
    problem_data_free(g_cd);
    g_cd = new_cd;
}
Ejemplo n.º 6
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_binary_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;
    }

    VERB3 log("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_binary_item(ax, bug_id, name, item);
    }

    g_list_free(sorted_names); /* names themselves are not freed */


    VERB3 log("...Done iterating over '%s' for attach", item_name);

    return done;
}
Ejemplo n.º 7
0
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;
}
Ejemplo n.º 8
0
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);
}
Ejemplo n.º 9
0
static int
append_short_backtrace(struct strbuf *result, problem_data_t *problem_data, bool print_item_name, problem_report_settings_t *settings)
{
    const problem_item *backtrace_item = problem_data_get_item_or_NULL(problem_data,
                                                                       FILENAME_BACKTRACE);
    const problem_item *core_stacktrace_item = NULL;
    if (!backtrace_item || !(backtrace_item->flags & CD_FLAG_TXT))
    {
        backtrace_item = NULL;

        core_stacktrace_item = problem_data_get_item_or_NULL(problem_data,
                                                             FILENAME_CORE_BACKTRACE);

        if (!core_stacktrace_item || !(core_stacktrace_item->flags & CD_FLAG_TXT))
            return 0;
    }

    char *truncated = NULL;

    if (core_stacktrace_item || strlen(backtrace_item->content) >= settings->prs_shortbt_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_type returns SR_REPORT_CORE
         * by default for CCpp crashes.
         */
        enum sr_report_type report_type = sr_abrt_type_from_type(type);
        if (backtrace_item && strcmp(type, "CCpp") == 0)
        {
            log_debug("Successfully identified 'CCpp' abrt type");
            report_type = SR_REPORT_GDB;
        }

        const char *content = backtrace_item ? backtrace_item->content : core_stacktrace_item->content;
        struct sr_stacktrace *backtrace = sr_stacktrace_parse(report_type, content, &error_msg);

        if (!backtrace)
        {
            log(_("Can't parse backtrace: %s"), error_msg);
            free(error_msg);
            return 0;
        }

        /* normalize */
        struct sr_thread *thread = sr_stacktrace_find_crash_thread(backtrace);
        sr_thread_normalize(thread);

        /* Get optimized thread stack trace for max_frames top most frames */
        truncated = sr_stacktrace_to_short_text(backtrace, settings->prs_shortbt_max_frames);
        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");
    }

    /* full item content  */
    append_text(result,
                /*item_name:*/ truncated ? "truncated_backtrace" : FILENAME_BACKTRACE,
                /*content:*/   truncated ? truncated             : backtrace_item->content,
                print_item_name
    );
    free(truncated);
    return 1;
}
Ejemplo n.º 10
0
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;
}
Ejemplo n.º 11
0
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 MantisBT."
        "\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 MantisBT site."
        "\nThe issue ID is retrieved from directory specified by -d DIR."
        "\nIf problem data in DIR was never reported to MantisBT, upload will fail."
        "\n"
        "\nOption -tID uploads FILEs to the issue with specified ID on MantisBT 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/mantisbt.conf"
        "\nand user's local ~"USER_HOME_CONFIG_PATH"/mantisbt.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."
        "\nUser's local configuration overrides the system wide configuration."
        "\nParameters can be overridden via $Mantisbt_PARAM environment variables."
        "\n"
        "\nFMTFILE default to "CONF_DIR"/plugins/mantisbt_format.conf."
        "\nFMTFILE2 default to "CONF_DIR"/plugins/mantisbt_formatdup.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 = CONF_DIR"/plugins/mantisbt_formatdup.conf";
    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();

    {
        char *local_conf = NULL;
        if (!conf_file)
        {
            conf_file = g_list_append(conf_file, (char*) CONF_DIR"/plugins/mantisbt.conf");
            local_conf = xasprintf("%s"USER_HOME_CONFIG_PATH"/mantisbt.conf", getenv("HOME"));
            conf_file = g_list_append(conf_file, local_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);
        }
        free(local_conf);

        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_warning(_("Looking for similar problems in MantisBT"));
        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, "MantisBT");
            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_warning(_("Using MantisBT ID '%s'"), ticket_no);
        }

        /* Attach files to existing MantisBT issues */
        while (*argv)
        {
            const char *path = *argv++;
            char *filename = basename(path);
            log_warning(_("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, "MantisBT");
        dd_close(dd);

        if (reported_to && reported_to->url)
        {
            char *msg = xasprintf(_("This problem was already reported to MantisBT (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);
    }

     unsigned long 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_warning(_("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("MantisBT 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("MantisBT 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_warning(_("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_warning(_("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_warning(_("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_warning(_("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_warning(_("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_warning(_("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_warning(_("Found the same comment in the issue history, not adding a new one"));

        problem_report_free(pr);
        problem_formatter_free(pf);
    }

finish:
    log_warning(_("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)
    {
        report_result_t rr = { .label = (char *)"MantisBT" };
        rr.url = xasprintf("%s/view.php?id=%u", mbt_settings.m_mantisbt_url, ii->mii_id);
        add_reported_to_entry(dd, &rr);
        free(rr.url);
        dd_close(dd);
    }

    mantisbt_settings_free(&mbt_settings);
    return 0;
}