Blame src/plugins/abrt-retrace-client.c

Packit 8ea169
/*
Packit 8ea169
    Copyright (C) 2010  Red Hat, Inc.
Packit 8ea169
Packit 8ea169
    This program is free software; you can redistribute it and/or modify
Packit 8ea169
    it under the terms of the GNU General Public License as published by
Packit 8ea169
    the Free Software Foundation; either version 2 of the License, or
Packit 8ea169
    (at your option) any later version.
Packit 8ea169
Packit 8ea169
    This program is distributed in the hope that it will be useful,
Packit 8ea169
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8ea169
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8ea169
    GNU General Public License for more details.
Packit 8ea169
Packit 8ea169
    You should have received a copy of the GNU General Public License along
Packit 8ea169
    with this program; if not, write to the Free Software Foundation, Inc.,
Packit 8ea169
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Packit 8ea169
*/
Packit 8ea169
#include "https-utils.h"
Packit 8ea169
Packit 8ea169
#define MAX_FORMATS 16
Packit 8ea169
#define MAX_RELEASES 32
Packit 8ea169
#define MAX_DOTS_PER_LINE 80
Packit 8ea169
#define MIN_EXPLOITABLE_RATING 4
Packit 8ea169
Packit 8ea169
enum
Packit 8ea169
{
Packit 8ea169
    TASK_RETRACE,
Packit 8ea169
    TASK_DEBUG,
Packit 8ea169
    TASK_VMCORE,
Packit 8ea169
};
Packit 8ea169
Packit 8ea169
static struct language lang;
Packit 8ea169
Packit 8ea169
struct retrace_settings
Packit 8ea169
{
Packit 8ea169
    int running_tasks;
Packit 8ea169
    int max_running_tasks;
Packit 8ea169
    long long max_packed_size;
Packit 8ea169
    long long max_unpacked_size;
Packit 8ea169
    char *supported_formats[MAX_FORMATS];
Packit 8ea169
    char *supported_releases[MAX_RELEASES];
Packit 8ea169
};
Packit 8ea169
Packit 8ea169
static const char *dump_dir_name = NULL;
Packit 8ea169
static const char *coredump = NULL;
Packit 8ea169
static const char *required_retrace[] = { FILENAME_COREDUMP,
Packit 8ea169
                                          FILENAME_EXECUTABLE,
Packit 8ea169
                                          FILENAME_PACKAGE,
Packit 8ea169
                                          FILENAME_OS_RELEASE,
Packit 8ea169
                                          NULL };
Packit 8ea169
static const char *optional_retrace[] = { FILENAME_ROOTDIR,
Packit 8ea169
                                          FILENAME_OS_RELEASE_IN_ROOTDIR,
Packit 8ea169
                                          NULL };
Packit 8ea169
static const char *required_vmcore[] = { FILENAME_VMCORE,
Packit 8ea169
                                         NULL };
Packit 8ea169
static unsigned delay = 0;
Packit 8ea169
static int task_type = TASK_RETRACE;
Packit 8ea169
static bool http_show_headers;
Packit 8ea169
static bool no_pkgcheck;
Packit 8ea169
Packit 8ea169
static struct https_cfg cfg =
Packit 8ea169
{
Packit 8ea169
    .url = "retrace.fedoraproject.org",
Packit 8ea169
    .port = 443,
Packit 8ea169
    .ssl_allow_insecure = false,
Packit 8ea169
};
Packit 8ea169
Packit 8ea169
static void alert_crash_too_large()
Packit 8ea169
{
Packit 8ea169
    alert(_("Retrace server can not be used, because the crash "
Packit 8ea169
            "is too large. Try local retracing."));
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
/* Add an entry name to the args array if the entry name exists in a
Packit 8ea169
 * problem directory. The entry is added to argindex offset to the array,
Packit 8ea169
 * and the argindex is then increased.
Packit 8ea169
 */
Packit 8ea169
static void args_add_if_exists(const char *args[],
Packit 8ea169
                               struct dump_dir *dd,
Packit 8ea169
                               const char *name,
Packit 8ea169
                               int *argindex)
Packit 8ea169
{
Packit 8ea169
    if (dd_exist(dd, name))
Packit 8ea169
    {
Packit 8ea169
        args[*argindex] = name;
Packit 8ea169
        *argindex += 1;
Packit 8ea169
    }
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
/* Create an archive with files required for retrace server and return
Packit 8ea169
 * a file descriptor. Returns -1 if it fails.
Packit 8ea169
 */
Packit 8ea169
static int create_archive(bool unlink_temp)
Packit 8ea169
{
Packit 8ea169
    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
Packit 8ea169
    if (!dd)
Packit 8ea169
        return -1;
Packit 8ea169
Packit 8ea169
    /* Open a temporary file. */
Packit 8ea169
    char *filename = xstrdup(LARGE_DATA_TMP_DIR"/abrt-retrace-client-archive-XXXXXX.tar.xz");
Packit 8ea169
    int tempfd = mkstemps(filename, /*suffixlen:*/7);
Packit 8ea169
    if (tempfd == -1)
Packit 8ea169
        perror_msg_and_die(_("Can't create temporary file in "LARGE_DATA_TMP_DIR));
Packit 8ea169
    if (unlink_temp)
Packit 8ea169
        xunlink(filename);
Packit 8ea169
    free(filename);
Packit 8ea169
Packit 8ea169
    /* Run xz:
Packit 8ea169
     * - xz reads input from a pipe
Packit 8ea169
     * - xz writes output to the temporary file.
Packit 8ea169
     */
Packit 8ea169
    const char *xz_args[4];
Packit 8ea169
    xz_args[0] = "xz";
Packit 8ea169
    xz_args[1] = "-2";
Packit 8ea169
    xz_args[2] = "-";
Packit 8ea169
    xz_args[3] = NULL;
Packit 8ea169
Packit 8ea169
    int tar_xz_pipe[2];
Packit 8ea169
    xpipe(tar_xz_pipe);
Packit 8ea169
Packit 8ea169
    fflush(NULL); /* paranoia */
Packit 8ea169
    pid_t xz_child = vfork();
Packit 8ea169
    if (xz_child == -1)
Packit 8ea169
        perror_msg_and_die("vfork");
Packit 8ea169
    if (xz_child == 0)
Packit 8ea169
    {
Packit 8ea169
        close(tar_xz_pipe[1]);
Packit 8ea169
        xmove_fd(tar_xz_pipe[0], STDIN_FILENO);
Packit 8ea169
        xmove_fd(tempfd, STDOUT_FILENO);
Packit 8ea169
        execvp(xz_args[0], (char * const*)xz_args);
Packit 8ea169
        perror_msg_and_die(_("Can't execute '%s'"), xz_args[0]);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    close(tar_xz_pipe[0]);
Packit 8ea169
Packit 8ea169
    /* Run tar, and set output to a pipe with xz waiting on the other
Packit 8ea169
     * end.
Packit 8ea169
     */
Packit 8ea169
    const char *tar_args[10];
Packit 8ea169
    tar_args[0] = "tar";
Packit 8ea169
    tar_args[1] = "cO";
Packit 8ea169
    tar_args[2] = xasprintf("--directory=%s", dump_dir_name);
Packit 8ea169
Packit 8ea169
    const char **required_files = task_type == TASK_VMCORE ? required_vmcore : required_retrace;
Packit 8ea169
    int index = 3;
Packit 8ea169
    while (required_files[index - 3])
Packit 8ea169
        args_add_if_exists(tar_args, dd, required_files[index - 3], &index);
Packit 8ea169
Packit 8ea169
    if (task_type == TASK_RETRACE || task_type == TASK_DEBUG)
Packit 8ea169
    {
Packit 8ea169
        int i;
Packit 8ea169
        for (i = 0; optional_retrace[i]; ++i)
Packit 8ea169
            args_add_if_exists(tar_args, dd, optional_retrace[i], &index);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    tar_args[index] = NULL;
Packit 8ea169
    dd_close(dd);
Packit 8ea169
Packit 8ea169
    fflush(NULL); /* paranoia */
Packit 8ea169
    pid_t tar_child = vfork();
Packit 8ea169
    if (tar_child == -1)
Packit 8ea169
        perror_msg_and_die("vfork");
Packit 8ea169
    if (tar_child == 0)
Packit 8ea169
    {
Packit 8ea169
        xmove_fd(xopen("/dev/null", O_RDWR), STDIN_FILENO);
Packit 8ea169
        xmove_fd(tar_xz_pipe[1], STDOUT_FILENO);
Packit 8ea169
        execvp(tar_args[0], (char * const*)tar_args);
Packit 8ea169
        perror_msg_and_die(_("Can't execute '%s'"), tar_args[0]);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    free((void*)tar_args[2]);
Packit 8ea169
    close(tar_xz_pipe[1]);
Packit 8ea169
Packit 8ea169
    /* Wait for tar and xz to finish successfully */
Packit 8ea169
    int status;
Packit 8ea169
    log_notice("Waiting for tar...");
Packit 8ea169
    safe_waitpid(tar_child, &status, 0);
Packit 8ea169
    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
Packit 8ea169
        /* Hopefully, by this time child emitted more meaningful
Packit 8ea169
         * error message. But just in case it didn't:
Packit 8ea169
         */
Packit 8ea169
        error_msg_and_die(_("Can't create temporary file in "LARGE_DATA_TMP_DIR));
Packit 8ea169
    log_notice("Waiting for xz...");
Packit 8ea169
    safe_waitpid(xz_child, &status, 0);
Packit 8ea169
    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
Packit 8ea169
        error_msg_and_die(_("Can't create temporary file in "LARGE_DATA_TMP_DIR));
Packit 8ea169
    log_notice("Done...");
Packit 8ea169
Packit 8ea169
    xlseek(tempfd, 0, SEEK_SET);
Packit 8ea169
    return tempfd;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
struct retrace_settings *get_settings()
Packit 8ea169
{
Packit 8ea169
    struct retrace_settings *settings = xzalloc(sizeof(struct retrace_settings));
Packit 8ea169
Packit 8ea169
    PRFileDesc *tcp_sock, *ssl_sock;
Packit 8ea169
    ssl_connect(&cfg, &tcp_sock, &ssl_sock);
Packit 8ea169
    struct strbuf *http_request = strbuf_new();
Packit 8ea169
    strbuf_append_strf(http_request,
Packit 8ea169
                       "GET /settings HTTP/1.1\r\n"
Packit 8ea169
                       "Host: %s\r\n"
Packit 8ea169
                       "Content-Length: 0\r\n"
Packit 8ea169
                       "Connection: close\r\n"
Packit 8ea169
                       "\r\n", cfg.url);
Packit 8ea169
    PRInt32 written = PR_Send(tcp_sock, http_request->buf, http_request->len,
Packit 8ea169
                              /*flags:*/0, PR_INTERVAL_NO_TIMEOUT);
Packit 8ea169
    if (written == -1)
Packit 8ea169
    {
Packit 8ea169
        alert_connection_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Failed to send HTTP header of length %d: NSS error %d"),
Packit 8ea169
                          http_request->len, PR_GetError());
Packit 8ea169
    }
Packit 8ea169
    strbuf_free(http_request);
Packit 8ea169
Packit 8ea169
    char *http_response = tcp_read_response(tcp_sock);
Packit 8ea169
    if (http_show_headers)
Packit 8ea169
        http_print_headers(stderr, http_response);
Packit 8ea169
    int response_code = http_get_response_code(http_response);
Packit 8ea169
    if (response_code != 200)
Packit 8ea169
    {
Packit 8ea169
        alert_server_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Unexpected HTTP response from server: %d\n%s"),
Packit 8ea169
                          response_code, http_response);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    char *headers_end = strstr(http_response, "\r\n\r\n");
Packit 8ea169
    char *c, *row, *value;
Packit 8ea169
    if (!headers_end)
Packit 8ea169
    {
Packit 8ea169
        alert_server_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Invalid response from server: missing HTTP message body."));
Packit 8ea169
    }
Packit 8ea169
    row = headers_end + strlen("\r\n\r\n");
Packit 8ea169
Packit 8ea169
    do
Packit 8ea169
    {
Packit 8ea169
        /* split rows */
Packit 8ea169
        c = strchr(row, '\n');
Packit 8ea169
        if (c)
Packit 8ea169
            *c = '\0';
Packit 8ea169
Packit 8ea169
        /* split key and values */
Packit 8ea169
        value = strchr(row, ' ');
Packit 8ea169
        if (!value)
Packit 8ea169
        {
Packit 8ea169
            row = c + 1;
Packit 8ea169
            continue;
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        *value = '\0';
Packit 8ea169
        ++value;
Packit 8ea169
Packit 8ea169
        if (0 == strcasecmp("running_tasks", row))
Packit 8ea169
            settings->running_tasks = atoi(value);
Packit 8ea169
        else if (0 == strcasecmp("max_running_tasks", row))
Packit 8ea169
            settings->max_running_tasks = atoi(value);
Packit 8ea169
        else if (0 == strcasecmp("max_packed_size", row))
Packit 8ea169
            settings->max_packed_size = atoll(value) * 1024 * 1024;
Packit 8ea169
        else if (0 == strcasecmp("max_unpacked_size", row))
Packit 8ea169
            settings->max_unpacked_size = atoll(value) * 1024 * 1024;
Packit 8ea169
        else if (0 == strcasecmp("supported_formats", row))
Packit 8ea169
        {
Packit 8ea169
            char *space;
Packit 8ea169
            int i;
Packit 8ea169
            for (i = 0; i < MAX_FORMATS - 1 && (space = strchr(value, ' ')); ++i)
Packit 8ea169
            {
Packit 8ea169
                *space = '\0';
Packit 8ea169
                settings->supported_formats[i] = xstrdup(value);
Packit 8ea169
                value = space + 1;
Packit 8ea169
            }
Packit 8ea169
Packit 8ea169
            /* last element */
Packit 8ea169
            settings->supported_formats[i] = xstrdup(value);
Packit 8ea169
        }
Packit 8ea169
        else if (0 == strcasecmp("supported_releases", row))
Packit 8ea169
        {
Packit 8ea169
            char *space;
Packit 8ea169
            int i;
Packit 8ea169
            for (i = 0; i < MAX_RELEASES - 1 && (space = strchr(value, ' ')); ++i)
Packit 8ea169
            {
Packit 8ea169
                *space = '\0';
Packit 8ea169
                settings->supported_releases[i] = xstrdup(value);
Packit 8ea169
                value = space + 1;
Packit 8ea169
            }
Packit 8ea169
Packit 8ea169
            /* last element */
Packit 8ea169
            settings->supported_releases[i] = xstrdup(value);
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        /* the beginning of the next row */
Packit 8ea169
        row = c + 1;
Packit 8ea169
    } while (c);
Packit 8ea169
Packit 8ea169
    free(http_response);
Packit 8ea169
    ssl_disconnect(ssl_sock);
Packit 8ea169
Packit 8ea169
    return settings;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void free_settings(struct retrace_settings *settings)
Packit 8ea169
{
Packit 8ea169
    if (!settings)
Packit 8ea169
        return;
Packit 8ea169
Packit 8ea169
    int i;
Packit 8ea169
    for (i = 0; i < MAX_FORMATS; ++i)
Packit 8ea169
        free(settings->supported_formats[i]);
Packit 8ea169
Packit 8ea169
    for (i = 0; i < MAX_RELEASES; ++i)
Packit 8ea169
        free(settings->supported_releases[i]);
Packit 8ea169
Packit 8ea169
    free(settings);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
/* returns release identifier as dist-ver-arch */
Packit 8ea169
/* or NULL if unknown */
Packit 8ea169
static char *get_release_id(map_string_t *osinfo, const char *architecture)
Packit 8ea169
{
Packit 8ea169
    char *arch = xstrdup(architecture);
Packit 8ea169
Packit 8ea169
    if (strcmp("i686", arch) == 0 || strcmp("i586", arch) == 0)
Packit 8ea169
    {
Packit 8ea169
        free(arch);
Packit 8ea169
        arch = xstrdup("i386");
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    char *result = NULL;
Packit 8ea169
    char *release = NULL;
Packit 8ea169
    char *version = NULL;
Packit 8ea169
    parse_osinfo_for_rhts(osinfo, (char **)&release, (char **)&version);
Packit 8ea169
Packit 8ea169
    if (release == NULL || version == NULL)
Packit 8ea169
        error_msg_and_die("Can't parse OS release name or version");
Packit 8ea169
Packit 8ea169
    char *space = strchr(version, ' ');
Packit 8ea169
    if (space)
Packit 8ea169
        *space = '\0';
Packit 8ea169
Packit 8ea169
    if (strcmp("Fedora", release) == 0)
Packit 8ea169
    {
Packit 8ea169
        /* Because of inconsistency between Fedora's os-release and retrace
Packit 8ea169
         * server.
Packit 8ea169
         *
Packit 8ea169
         * Adding the reporting fields to Fedora's os-release was a bit
Packit 8ea169
         * frustrating for all participants and fixing it on the retrace server
Packit 8ea169
         * side is neither feasible nor acceptable.
Packit 8ea169
         *
Packit 8ea169
         * Therefore, we have decided to add the following hack.
Packit 8ea169
         */
Packit 8ea169
        if (strcmp("Rawhide", version) == 0)
Packit 8ea169
        {
Packit 8ea169
            /* Rawhide -> rawhide */
Packit 8ea169
            version[0] = 'r';
Packit 8ea169
        }
Packit 8ea169
        /* Fedora -> fedora */
Packit 8ea169
        release[0] = 'f';
Packit 8ea169
    }
Packit 8ea169
    else if (strcmp("Red Hat Enterprise Linux", release) == 0)
Packit 8ea169
        strcpy(release, "rhel");
Packit 8ea169
Packit 8ea169
    result = xasprintf("%s-%s-%s", release, version, arch);
Packit 8ea169
Packit 8ea169
    free(release);
Packit 8ea169
    free(version);
Packit 8ea169
    free(arch);
Packit 8ea169
    return result;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static int check_package(const char *nvr, const char *arch, map_string_t *osinfo, char **msg)
Packit 8ea169
{
Packit 8ea169
    char *releaseid = get_release_id(osinfo, arch);
Packit 8ea169
Packit 8ea169
    PRFileDesc *tcp_sock, *ssl_sock;
Packit 8ea169
    ssl_connect(&cfg, &tcp_sock, &ssl_sock);
Packit 8ea169
    struct strbuf *http_request = strbuf_new();
Packit 8ea169
    strbuf_append_strf(http_request,
Packit 8ea169
                       "GET /checkpackage HTTP/1.1\r\n"
Packit 8ea169
                       "Host: %s\r\n"
Packit 8ea169
                       "Content-Length: 0\r\n"
Packit 8ea169
                       "Connection: close\r\n"
Packit 8ea169
                       "X-Package-NVR: %s\r\n"
Packit 8ea169
                       "X-Package-Arch: %s\r\n"
Packit 8ea169
                       "X-OS-Release: %s\r\n"
Packit 8ea169
                       "%s"
Packit 8ea169
                       "%s"
Packit 8ea169
                       "\r\n",
Packit 8ea169
                       cfg.url, nvr, arch, releaseid,
Packit 8ea169
                       lang.accept_charset,
Packit 8ea169
                       lang.accept_language
Packit 8ea169
    );
Packit 8ea169
Packit 8ea169
    PRInt32 written = PR_Send(tcp_sock, http_request->buf, http_request->len,
Packit 8ea169
                              /*flags:*/0, PR_INTERVAL_NO_TIMEOUT);
Packit 8ea169
    if (written == -1)
Packit 8ea169
    {
Packit 8ea169
        alert_connection_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Failed to send HTTP header of length %d: NSS error %d"),
Packit 8ea169
                          http_request->len, PR_GetError());
Packit 8ea169
    }
Packit 8ea169
    strbuf_free(http_request);
Packit 8ea169
    char *http_response = tcp_read_response(tcp_sock);
Packit 8ea169
    ssl_disconnect(ssl_sock);
Packit 8ea169
    if (http_show_headers)
Packit 8ea169
        http_print_headers(stderr, http_response);
Packit 8ea169
    int response_code = http_get_response_code(http_response);
Packit 8ea169
    /* we are expecting either 302 or 404 */
Packit 8ea169
    if (response_code != 302 && response_code != 404)
Packit 8ea169
    {
Packit 8ea169
        char *http_body = http_get_body(http_response);
Packit 8ea169
        alert_server_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Unexpected HTTP response from server: %d\n%s"),
Packit 8ea169
                            response_code, http_body);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    if (msg)
Packit 8ea169
    {
Packit 8ea169
        if (response_code == 404)
Packit 8ea169
        {
Packit 8ea169
            const char *os = get_map_string_item_or_empty(osinfo, OSINFO_PRETTY_NAME);
Packit 8ea169
            if (!os)
Packit 8ea169
                os = get_map_string_item_or_empty(osinfo, OSINFO_NAME);
Packit 8ea169
Packit 8ea169
            *msg = xasprintf(_("Retrace server is unable to process package "
Packit 8ea169
                               "'%s.%s'.\nIs it a part of official '%s' repositories?"),
Packit 8ea169
                               nvr, arch, os);
Packit 8ea169
        }
Packit 8ea169
        else
Packit 8ea169
            *msg = NULL;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    free(http_response);
Packit 8ea169
    free(releaseid);
Packit 8ea169
Packit 8ea169
    return response_code == 302;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static int create(bool delete_temp_archive,
Packit 8ea169
                  char **task_id,
Packit 8ea169
                  char **task_password)
Packit 8ea169
{
Packit 8ea169
    if (delay)
Packit 8ea169
    {
Packit 8ea169
        puts(_("Querying server settings"));
Packit 8ea169
        fflush(stdout);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    struct retrace_settings *settings = get_settings();
Packit 8ea169
Packit 8ea169
    if (settings->running_tasks >= settings->max_running_tasks)
Packit 8ea169
    {
Packit 8ea169
        alert(_("The server is fully occupied. Try again later."));
Packit 8ea169
        error_msg_and_die(_("The server denied your request."));
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    long long unpacked_size = 0;
Packit 8ea169
    struct stat file_stat;
Packit 8ea169
Packit 8ea169
    /* get raw size */
Packit 8ea169
    if (coredump)
Packit 8ea169
    {
Packit 8ea169
        xstat(coredump, &file_stat);
Packit 8ea169
        unpacked_size = (long long)file_stat.st_size;
Packit 8ea169
    }
Packit 8ea169
    else if (dump_dir_name != NULL)
Packit 8ea169
    {
Packit 8ea169
        struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags*/ 0);
Packit 8ea169
        if (!dd)
Packit 8ea169
            xfunc_die(); /* dd_opendir already emitted error message */
Packit 8ea169
        if (dd_exist(dd, FILENAME_VMCORE))
Packit 8ea169
            task_type = TASK_VMCORE;
Packit 8ea169
        dd_close(dd);
Packit 8ea169
Packit 8ea169
        char *path;
Packit 8ea169
        int i = 0;
Packit 8ea169
        const char **required_files = task_type == TASK_VMCORE ? required_vmcore : required_retrace;
Packit 8ea169
        while (required_files[i])
Packit 8ea169
        {
Packit 8ea169
            path = concat_path_file(dump_dir_name, required_files[i]);
Packit 8ea169
            xstat(path, &file_stat);
Packit 8ea169
            free(path);
Packit 8ea169
Packit 8ea169
            if (!S_ISREG(file_stat.st_mode))
Packit 8ea169
                error_msg_and_die(_("'%s' must be a regular file in "
Packit 8ea169
                                    "order to use Retrace server."),
Packit 8ea169
                                  required_files[i]);
Packit 8ea169
Packit 8ea169
            unpacked_size += (long long)file_stat.st_size;
Packit 8ea169
            ++i;
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        if (task_type == TASK_RETRACE || task_type == TASK_DEBUG)
Packit 8ea169
        {
Packit 8ea169
            for (i = 0; optional_retrace[i]; ++i)
Packit 8ea169
            {
Packit 8ea169
                path = concat_path_file(dump_dir_name, optional_retrace[i]);
Packit 8ea169
                if (stat(path, &file_stat) != -1)
Packit 8ea169
                {
Packit 8ea169
                    if (!S_ISREG(file_stat.st_mode))
Packit 8ea169
                        error_msg_and_die(_("'%s' must be a regular file in "
Packit 8ea169
                                            "order to use Retrace server."),
Packit 8ea169
                                          required_files[i]);
Packit 8ea169
Packit 8ea169
                    unpacked_size += (long long)file_stat.st_size;
Packit 8ea169
                }
Packit 8ea169
                free(path);
Packit 8ea169
            }
Packit 8ea169
        }
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    if (unpacked_size > settings->max_unpacked_size)
Packit 8ea169
    {
Packit 8ea169
        alert_crash_too_large();
Packit 8ea169
Packit 8ea169
        /* Leaking size and max_size in hope the memory will be released in
Packit 8ea169
         * error_msg_and_die() */
Packit 8ea169
        gchar *size = g_format_size_full(unpacked_size, G_FORMAT_SIZE_IEC_UNITS);
Packit 8ea169
        gchar *max_size = g_format_size_full(settings->max_unpacked_size, G_FORMAT_SIZE_IEC_UNITS);
Packit 8ea169
Packit 8ea169
        error_msg_and_die(_("The size of your crash is %s, "
Packit 8ea169
                            "but the retrace server only accepts "
Packit 8ea169
                            "crashes smaller or equal to %s."),
Packit 8ea169
                            size, max_size);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    if (settings->supported_formats)
Packit 8ea169
    {
Packit 8ea169
        int i;
Packit 8ea169
        bool supported = false;
Packit 8ea169
        for (i = 0; i < MAX_FORMATS && settings->supported_formats[i]; ++i)
Packit 8ea169
            if (strcmp("application/x-xz-compressed-tar", settings->supported_formats[i]) == 0)
Packit 8ea169
            {
Packit 8ea169
                supported = true;
Packit 8ea169
                break;
Packit 8ea169
            }
Packit 8ea169
Packit 8ea169
        if (!supported)
Packit 8ea169
        {
Packit 8ea169
            alert_server_error(cfg.url);
Packit 8ea169
            error_msg_and_die(_("The server does not support "
Packit 8ea169
                                "xz-compressed tarballs."));
Packit 8ea169
        }
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
Packit 8ea169
    if (task_type != TASK_VMCORE && dump_dir_name)
Packit 8ea169
    {
Packit 8ea169
        struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY);
Packit 8ea169
        if (!dd)
Packit 8ea169
            xfunc_die();
Packit 8ea169
        problem_data_t *pd = create_problem_data_from_dump_dir(dd);
Packit 8ea169
        dd_close(dd);
Packit 8ea169
Packit 8ea169
        char *package = problem_data_get_content_or_NULL(pd, FILENAME_PACKAGE);
Packit 8ea169
        char *arch = problem_data_get_content_or_NULL(pd, FILENAME_ARCHITECTURE);
Packit 8ea169
        map_string_t *osinfo = new_map_string();
Packit 8ea169
        problem_data_get_osinfo(pd, osinfo);
Packit 8ea169
Packit 8ea169
        /* not needed for TASK_VMCORE - the information is kept in the vmcore itself */
Packit 8ea169
        if (settings->supported_releases)
Packit 8ea169
        {
Packit 8ea169
            char *releaseid = get_release_id(osinfo, arch);
Packit 8ea169
            if (!releaseid)
Packit 8ea169
                error_msg_and_die("Unable to parse release.");
Packit 8ea169
Packit 8ea169
            int i;
Packit 8ea169
            bool supported = false;
Packit 8ea169
            for (i = 0; i < MAX_RELEASES && settings->supported_releases[i]; ++i)
Packit 8ea169
                if (strcmp(releaseid, settings->supported_releases[i]) == 0)
Packit 8ea169
                {
Packit 8ea169
                    supported = true;
Packit 8ea169
                    break;
Packit 8ea169
                }
Packit 8ea169
Packit 8ea169
            if (!supported)
Packit 8ea169
            {
Packit 8ea169
                char *msg = xasprintf(_("The release '%s' is not supported by the"
Packit 8ea169
                                        " Retrace server."), releaseid);
Packit 8ea169
                alert(msg);
Packit 8ea169
                free(msg);
Packit 8ea169
                error_msg_and_die(_("The server is not able to"
Packit 8ea169
                                    " handle your request."));
Packit 8ea169
            }
Packit 8ea169
Packit 8ea169
            free(releaseid);
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        /* not relevant for vmcores - it may take a long time to get package from vmcore */
Packit 8ea169
        if (!no_pkgcheck)
Packit 8ea169
        {
Packit 8ea169
            char *msg;
Packit 8ea169
            int known = check_package(package, arch, osinfo, &msg;;
Packit 8ea169
            if (msg)
Packit 8ea169
            {
Packit 8ea169
                alert(msg);
Packit 8ea169
                free(msg);
Packit 8ea169
            }
Packit 8ea169
Packit 8ea169
            if (!known)
Packit 8ea169
                error_msg_and_die(_("Unknown package sent to Retrace server."));
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        free_map_string(osinfo);
Packit 8ea169
        problem_data_free(pd);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    if (delay)
Packit 8ea169
    {
Packit 8ea169
        puts(_("Preparing an archive to upload"));
Packit 8ea169
        fflush(stdout);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    int tempfd = create_archive(delete_temp_archive);
Packit 8ea169
    if (-1 == tempfd)
Packit 8ea169
        return 1;
Packit 8ea169
Packit 8ea169
    /* Get the file size. */
Packit 8ea169
    fstat(tempfd, &file_stat);
Packit 8ea169
    gchar *human_size = g_format_size_full((long long)file_stat.st_size, G_FORMAT_SIZE_IEC_UNITS);
Packit 8ea169
    if ((long long)file_stat.st_size > settings->max_packed_size)
Packit 8ea169
    {
Packit 8ea169
        alert_crash_too_large();
Packit 8ea169
Packit 8ea169
        /* Leaking human_size and max_size in hope the memory will be released in
Packit 8ea169
         * error_msg_and_die() */
Packit 8ea169
        gchar *max_size = g_format_size_full(settings->max_packed_size, G_FORMAT_SIZE_IEC_UNITS);
Packit 8ea169
Packit 8ea169
        error_msg_and_die(_("The size of your archive is %s, "
Packit 8ea169
                            "but the retrace server only accepts "
Packit 8ea169
                            "archives smaller or equal to %s."),
Packit 8ea169
                          human_size, max_size);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    free_settings(settings);
Packit 8ea169
Packit 8ea169
    int size_mb = file_stat.st_size / (1024 * 1024);
Packit 8ea169
Packit 8ea169
    if (size_mb > 8) /* 8 MB - should be configurable */
Packit 8ea169
    {
Packit 8ea169
        char *question = xasprintf(_("You are going to upload %s. "
Packit 8ea169
                                     "Continue?"), human_size);
Packit 8ea169
Packit 8ea169
        int response = ask_yes_no(question);
Packit 8ea169
        free(question);
Packit 8ea169
Packit 8ea169
        if (!response)
Packit 8ea169
        {
Packit 8ea169
            set_xfunc_error_retval(EXIT_CANCEL_BY_USER);
Packit 8ea169
            error_msg_and_die(_("Cancelled by user"));
Packit 8ea169
        }
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    PRFileDesc *tcp_sock, *ssl_sock;
Packit 8ea169
    ssl_connect(&cfg, &tcp_sock, &ssl_sock);
Packit 8ea169
    /* Upload the archive. */
Packit 8ea169
    struct strbuf *http_request = strbuf_new();
Packit 8ea169
    strbuf_append_strf(http_request,
Packit 8ea169
                       "POST /create HTTP/1.1\r\n"
Packit 8ea169
                       "Host: %s\r\n"
Packit 8ea169
                       "Content-Type: application/x-xz-compressed-tar\r\n"
Packit 8ea169
                       "Content-Length: %lld\r\n"
Packit 8ea169
                       "Connection: close\r\n"
Packit 8ea169
                       "X-Task-Type: %d\r\n"
Packit 8ea169
                       "%s"
Packit 8ea169
                       "%s"
Packit 8ea169
                       "\r\n",
Packit 8ea169
                       cfg.url, (long long)file_stat.st_size, task_type,
Packit 8ea169
                       lang.accept_charset,
Packit 8ea169
                       lang.accept_language
Packit 8ea169
    );
Packit 8ea169
Packit 8ea169
    PRInt32 written = PR_Send(tcp_sock, http_request->buf, http_request->len,
Packit 8ea169
                              /*flags:*/0, PR_INTERVAL_NO_TIMEOUT);
Packit 8ea169
    if (written == -1)
Packit 8ea169
    {
Packit 8ea169
        alert_connection_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Failed to send HTTP header of length %d: NSS error %d"),
Packit 8ea169
                          http_request->len, PR_GetError());
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    if (delay)
Packit 8ea169
    {
Packit 8ea169
        printf(_("Uploading %s\n"), human_size);
Packit 8ea169
        fflush(stdout);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    g_free(human_size);
Packit 8ea169
Packit 8ea169
    strbuf_free(http_request);
Packit 8ea169
    int result = 0;
Packit 8ea169
    int i;
Packit 8ea169
    char buf[32768];
Packit 8ea169
Packit 8ea169
    time_t start, now;
Packit 8ea169
    time(&start;;
Packit 8ea169
Packit 8ea169
    for (i = 0;; ++i)
Packit 8ea169
    {
Packit 8ea169
        if (delay)
Packit 8ea169
        {
Packit 8ea169
            time(&now;;
Packit 8ea169
            if (now - start >= delay)
Packit 8ea169
            {
Packit 8ea169
                time(&start;;
Packit 8ea169
                int progress = 100 * i * sizeof(buf) / file_stat.st_size;
Packit 8ea169
                if (progress > 100)
Packit 8ea169
                    continue;
Packit 8ea169
Packit 8ea169
                printf(_("Uploading %d%%\n"), progress);
Packit 8ea169
                fflush(stdout);
Packit 8ea169
            }
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        int r = read(tempfd, buf, sizeof(buf));
Packit 8ea169
        if (r <= 0)
Packit 8ea169
        {
Packit 8ea169
            if (r == -1)
Packit 8ea169
            {
Packit 8ea169
                if (EINTR == errno || EAGAIN == errno || EWOULDBLOCK == errno)
Packit 8ea169
                    continue;
Packit 8ea169
                perror_msg_and_die(_("Failed to read from a pipe"));
Packit 8ea169
            }
Packit 8ea169
            break;
Packit 8ea169
        }
Packit 8ea169
        written = PR_Send(tcp_sock, buf, r,
Packit 8ea169
                          /*flags:*/0, PR_INTERVAL_NO_TIMEOUT);
Packit 8ea169
        if (written == -1)
Packit 8ea169
        {
Packit 8ea169
            /* Print error message, but do not exit.  We need to check
Packit 8ea169
               if the server send some explanation regarding the
Packit 8ea169
               error. */
Packit 8ea169
            result = 1;
Packit 8ea169
            alert_connection_error(cfg.url);
Packit 8ea169
            error_msg(_("Failed to send data: NSS error %d (%s): %s"),
Packit 8ea169
                      PR_GetError(),
Packit 8ea169
                      PR_ErrorToName(PR_GetError()),
Packit 8ea169
                      PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT));
Packit 8ea169
            break;
Packit 8ea169
        }
Packit 8ea169
    }
Packit 8ea169
    close(tempfd);
Packit 8ea169
Packit 8ea169
    if (delay)
Packit 8ea169
    {
Packit 8ea169
        puts(_("Upload successful"));
Packit 8ea169
        fflush(stdout);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    /* Read the HTTP header of the response from server. */
Packit 8ea169
    char *http_response = tcp_read_response(tcp_sock);
Packit 8ea169
    char *http_body = http_get_body(http_response);
Packit 8ea169
    if (!http_body)
Packit 8ea169
    {
Packit 8ea169
        alert_server_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Invalid response from server: missing HTTP message body."));
Packit 8ea169
    }
Packit 8ea169
    if (http_show_headers)
Packit 8ea169
        http_print_headers(stderr, http_response);
Packit 8ea169
    int response_code = http_get_response_code(http_response);
Packit 8ea169
    if (response_code == 500 || response_code == 507)
Packit 8ea169
    {
Packit 8ea169
        alert_server_error(cfg.url);
Packit 8ea169
        error_msg_and_die("%s", http_body);
Packit 8ea169
    }
Packit 8ea169
    else if (response_code == 403)
Packit 8ea169
    {
Packit 8ea169
        alert(_("Your problem directory is corrupted and can not "
Packit 8ea169
                "be processed by the Retrace server."));
Packit 8ea169
        error_msg_and_die(_("The archive contains malicious files (such as symlinks) "
Packit 8ea169
                            "and thus can not be processed."));
Packit 8ea169
    }
Packit 8ea169
    else if (response_code != 201)
Packit 8ea169
    {
Packit 8ea169
        alert_server_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Unexpected HTTP response from server: %d\n%s"), response_code, http_body);
Packit 8ea169
    }
Packit 8ea169
    free(http_body);
Packit 8ea169
    *task_id = http_get_header_value(http_response, "X-Task-Id");
Packit 8ea169
    if (!*task_id)
Packit 8ea169
    {
Packit 8ea169
        alert_server_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Invalid response from server: missing X-Task-Id."));
Packit 8ea169
    }
Packit 8ea169
    *task_password = http_get_header_value(http_response, "X-Task-Password");
Packit 8ea169
    if (!*task_password)
Packit 8ea169
    {
Packit 8ea169
        alert_server_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Invalid response from server: missing X-Task-Password."));
Packit 8ea169
    }
Packit 8ea169
    free(http_response);
Packit 8ea169
    ssl_disconnect(ssl_sock);
Packit 8ea169
Packit 8ea169
    if (delay)
Packit 8ea169
    {
Packit 8ea169
        puts(_("Retrace job started"));
Packit 8ea169
        fflush(stdout);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    return result;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static int run_create(bool delete_temp_archive)
Packit 8ea169
{
Packit 8ea169
    char *task_id, *task_password;
Packit 8ea169
    int result = create(delete_temp_archive, &task_id, &task_password);
Packit 8ea169
    if (0 != result)
Packit 8ea169
        return result;
Packit 8ea169
    printf(_("Task Id: %s\nTask Password: %s\n"), task_id, task_password);
Packit 8ea169
    free(task_id);
Packit 8ea169
    free(task_password);
Packit 8ea169
    return 0;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
/* Caller must free task_status and status_message */
Packit 8ea169
static void status(const char *task_id,
Packit 8ea169
                   const char *task_password,
Packit 8ea169
                   char **task_status,
Packit 8ea169
                   char **status_message)
Packit 8ea169
{
Packit 8ea169
    PRFileDesc *tcp_sock, *ssl_sock;
Packit 8ea169
    ssl_connect(&cfg, &tcp_sock, &ssl_sock);
Packit 8ea169
    struct strbuf *http_request = strbuf_new();
Packit 8ea169
    strbuf_append_strf(http_request,
Packit 8ea169
                       "GET /%s HTTP/1.1\r\n"
Packit 8ea169
                       "Host: %s\r\n"
Packit 8ea169
                       "X-Task-Password: %s\r\n"
Packit 8ea169
                       "Content-Length: 0\r\n"
Packit 8ea169
                       "Connection: close\r\n"
Packit 8ea169
                       "%s"
Packit 8ea169
                       "%s"
Packit 8ea169
                       "\r\n",
Packit 8ea169
                       task_id, cfg.url, task_password,
Packit 8ea169
                       lang.accept_charset,
Packit 8ea169
                       lang.accept_language
Packit 8ea169
    );
Packit 8ea169
Packit 8ea169
    PRInt32 written = PR_Send(tcp_sock, http_request->buf, http_request->len,
Packit 8ea169
                              /*flags:*/0, PR_INTERVAL_NO_TIMEOUT);
Packit 8ea169
    if (written == -1)
Packit 8ea169
    {
Packit 8ea169
        alert_connection_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Failed to send HTTP header of length %d: NSS error %d"),
Packit 8ea169
                          http_request->len, PR_GetError());
Packit 8ea169
    }
Packit 8ea169
    strbuf_free(http_request);
Packit 8ea169
    char *http_response = tcp_read_response(tcp_sock);
Packit 8ea169
    char *http_body = http_get_body(http_response);
Packit 8ea169
    if (!*http_body)
Packit 8ea169
    {
Packit 8ea169
        alert_server_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Invalid response from server: missing HTTP message body."));
Packit 8ea169
    }
Packit 8ea169
    if (http_show_headers)
Packit 8ea169
        http_print_headers(stderr, http_response);
Packit 8ea169
    int response_code = http_get_response_code(http_response);
Packit 8ea169
    if (response_code != 200)
Packit 8ea169
    {
Packit 8ea169
        alert_server_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Unexpected HTTP response from server: %d\n%s"),
Packit 8ea169
                          response_code, http_body);
Packit 8ea169
    }
Packit 8ea169
    *task_status = http_get_header_value(http_response, "X-Task-Status");
Packit 8ea169
    if (!*task_status)
Packit 8ea169
    {
Packit 8ea169
        alert_server_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Invalid response from server: missing X-Task-Status."));
Packit 8ea169
    }
Packit 8ea169
    *status_message = http_body;
Packit 8ea169
    free(http_response);
Packit 8ea169
    ssl_disconnect(ssl_sock);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void run_status(const char *task_id, const char *task_password)
Packit 8ea169
{
Packit 8ea169
    char *task_status;
Packit 8ea169
    char *status_message;
Packit 8ea169
    status(task_id, task_password, &task_status, &status_message);
Packit 8ea169
    printf(_("Task Status: %s\n%s\n"), task_status, status_message);
Packit 8ea169
    free(task_status);
Packit 8ea169
    free(status_message);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
/* Caller must free backtrace */
Packit 8ea169
static void backtrace(const char *task_id, const char *task_password,
Packit 8ea169
                      char **backtrace)
Packit 8ea169
{
Packit 8ea169
    PRFileDesc *tcp_sock, *ssl_sock;
Packit 8ea169
    ssl_connect(&cfg, &tcp_sock, &ssl_sock);
Packit 8ea169
    struct strbuf *http_request = strbuf_new();
Packit 8ea169
    strbuf_append_strf(http_request,
Packit 8ea169
                       "GET /%s/backtrace HTTP/1.1\r\n"
Packit 8ea169
                       "Host: %s\r\n"
Packit 8ea169
                       "X-Task-Password: %s\r\n"
Packit 8ea169
                       "Content-Length: 0\r\n"
Packit 8ea169
                       "Connection: close\r\n"
Packit 8ea169
                       "%s"
Packit 8ea169
                       "%s"
Packit 8ea169
                       "\r\n",
Packit 8ea169
                       task_id, cfg.url, task_password,
Packit 8ea169
                       lang.accept_charset,
Packit 8ea169
                       lang.accept_language
Packit 8ea169
    );
Packit 8ea169
Packit 8ea169
    PRInt32 written = PR_Send(tcp_sock, http_request->buf, http_request->len,
Packit 8ea169
                              /*flags:*/0, PR_INTERVAL_NO_TIMEOUT);
Packit 8ea169
    if (written == -1)
Packit 8ea169
    {
Packit 8ea169
        alert_connection_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Failed to send HTTP header of length %d: NSS error %d."),
Packit 8ea169
                          http_request->len, PR_GetError());
Packit 8ea169
    }
Packit 8ea169
    strbuf_free(http_request);
Packit 8ea169
    char *http_response = tcp_read_response(tcp_sock);
Packit 8ea169
    char *http_body = http_get_body(http_response);
Packit 8ea169
    if (!http_body)
Packit 8ea169
    {
Packit 8ea169
        alert_server_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Invalid response from server: missing HTTP message body."));
Packit 8ea169
    }
Packit 8ea169
    if (http_show_headers)
Packit 8ea169
        http_print_headers(stderr, http_response);
Packit 8ea169
    int response_code = http_get_response_code(http_response);
Packit 8ea169
    if (response_code != 200)
Packit 8ea169
    {
Packit 8ea169
        alert_server_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Unexpected HTTP response from server: %d\n%s"),
Packit 8ea169
                          response_code, http_body);
Packit 8ea169
    }
Packit 8ea169
    *backtrace = http_body;
Packit 8ea169
    free(http_response);
Packit 8ea169
    ssl_disconnect(ssl_sock);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void run_backtrace(const char *task_id, const char *task_password)
Packit 8ea169
{
Packit 8ea169
    char *backtrace_text;
Packit 8ea169
    backtrace(task_id, task_password, &backtrace_text);
Packit 8ea169
    printf("%s", backtrace_text);
Packit 8ea169
    free(backtrace_text);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
/* This is not robust at all but will work for now */
Packit 8ea169
static int get_exploitable_rating(const char *exploitable_text)
Packit 8ea169
{
Packit 8ea169
    const char *colon = strrchr(exploitable_text, ':');
Packit 8ea169
    int result;
Packit 8ea169
    if (!colon || sscanf(colon, ": %d", &result) != 1)
Packit 8ea169
    {
Packit 8ea169
        log_notice("Unable to determine exploitable rating");
Packit 8ea169
        return -1;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    log_notice("Exploitable rating: %d", result);
Packit 8ea169
    return result;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
/* Caller must free exploitable_text */
Packit 8ea169
static void exploitable(const char *task_id, const char *task_password,
Packit 8ea169
                        char **exploitable_text)
Packit 8ea169
{
Packit 8ea169
    PRFileDesc *tcp_sock, *ssl_sock;
Packit 8ea169
    ssl_connect(&cfg, &tcp_sock, &ssl_sock);
Packit 8ea169
    struct strbuf *http_request = strbuf_new();
Packit 8ea169
    strbuf_append_strf(http_request,
Packit 8ea169
                       "GET /%s/exploitable HTTP/1.1\r\n"
Packit 8ea169
                       "Host: %s\r\n"
Packit 8ea169
                       "X-Task-Password: %s\r\n"
Packit 8ea169
                       "Content-Length: 0\r\n"
Packit 8ea169
                       "Connection: close\r\n"
Packit 8ea169
                       "%s"
Packit 8ea169
                       "%s"
Packit 8ea169
                       "\r\n",
Packit 8ea169
                       task_id, cfg.url, task_password,
Packit 8ea169
                       lang.accept_charset,
Packit 8ea169
                       lang.accept_language
Packit 8ea169
    );
Packit 8ea169
Packit 8ea169
    PRInt32 written = PR_Send(tcp_sock, http_request->buf, http_request->len,
Packit 8ea169
                              /*flags:*/0, PR_INTERVAL_NO_TIMEOUT);
Packit 8ea169
    if (written == -1)
Packit 8ea169
    {
Packit 8ea169
        alert_connection_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Failed to send HTTP header of length %d: NSS error %d."),
Packit 8ea169
                          http_request->len, PR_GetError());
Packit 8ea169
    }
Packit 8ea169
    strbuf_free(http_request);
Packit 8ea169
    char *http_response = tcp_read_response(tcp_sock);
Packit 8ea169
    char *http_body = http_get_body(http_response);
Packit 8ea169
    if (!http_body)
Packit 8ea169
    {
Packit 8ea169
        alert_server_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Invalid response from server: missing HTTP message body."));
Packit 8ea169
    }
Packit 8ea169
    if (http_show_headers)
Packit 8ea169
        http_print_headers(stderr, http_response);
Packit 8ea169
    int response_code = http_get_response_code(http_response);
Packit 8ea169
Packit 8ea169
    free(http_response);
Packit 8ea169
    ssl_disconnect(ssl_sock);
Packit 8ea169
Packit 8ea169
    /* 404 = exploitability results not available
Packit 8ea169
       200 = OK
Packit 8ea169
       anything else = error */
Packit 8ea169
    if (response_code == 404)
Packit 8ea169
        *exploitable_text = NULL;
Packit 8ea169
    else if (response_code == 200)
Packit 8ea169
        *exploitable_text = http_body;
Packit 8ea169
    else
Packit 8ea169
    {
Packit 8ea169
        alert_server_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Unexpected HTTP response from server: %d\n%s"),
Packit 8ea169
                          response_code, http_body);
Packit 8ea169
    }
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void run_exploitable(const char *task_id, const char *task_password)
Packit 8ea169
{
Packit 8ea169
    char *exploitable_text;
Packit 8ea169
    exploitable(task_id, task_password, &exploitable_text);
Packit 8ea169
    if (exploitable_text)
Packit 8ea169
    {
Packit 8ea169
        printf("%s\n", exploitable_text);
Packit 8ea169
        free(exploitable_text);
Packit 8ea169
    }
Packit 8ea169
    else
Packit 8ea169
        puts("No exploitability information available.");
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void run_log(const char *task_id, const char *task_password)
Packit 8ea169
{
Packit 8ea169
    PRFileDesc *tcp_sock, *ssl_sock;
Packit 8ea169
    ssl_connect(&cfg, &tcp_sock, &ssl_sock);
Packit 8ea169
    struct strbuf *http_request = strbuf_new();
Packit 8ea169
    strbuf_append_strf(http_request,
Packit 8ea169
                       "GET /%s/log HTTP/1.1\r\n"
Packit 8ea169
                       "Host: %s\r\n"
Packit 8ea169
                       "X-Task-Password: %s\r\n"
Packit 8ea169
                       "Content-Length: 0\r\n"
Packit 8ea169
                       "Connection: close\r\n"
Packit 8ea169
                       "%s"
Packit 8ea169
                       "%s"
Packit 8ea169
                       "\r\n",
Packit 8ea169
                       task_id, cfg.url, task_password,
Packit 8ea169
                       lang.accept_charset,
Packit 8ea169
                       lang.accept_language
Packit 8ea169
    );
Packit 8ea169
Packit 8ea169
    PRInt32 written = PR_Send(tcp_sock, http_request->buf, http_request->len,
Packit 8ea169
                              /*flags:*/0, PR_INTERVAL_NO_TIMEOUT);
Packit 8ea169
    if (written == -1)
Packit 8ea169
    {
Packit 8ea169
        alert_connection_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Failed to send HTTP header of length %d: NSS error %d."),
Packit 8ea169
                          http_request->len, PR_GetError());
Packit 8ea169
    }
Packit 8ea169
    strbuf_free(http_request);
Packit 8ea169
    char *http_response = tcp_read_response(tcp_sock);
Packit 8ea169
    char *http_body = http_get_body(http_response);
Packit 8ea169
    if (!http_body)
Packit 8ea169
    {
Packit 8ea169
        alert_server_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Invalid response from server: missing HTTP message body."));
Packit 8ea169
    }
Packit 8ea169
    if (http_show_headers)
Packit 8ea169
        http_print_headers(stderr, http_response);
Packit 8ea169
    int response_code = http_get_response_code(http_response);
Packit 8ea169
    if (response_code != 200)
Packit 8ea169
    {
Packit 8ea169
        alert_server_error(cfg.url);
Packit 8ea169
        error_msg_and_die(_("Unexpected HTTP response from server: %d\n%s"),
Packit 8ea169
                          response_code, http_body);
Packit 8ea169
    }
Packit 8ea169
    puts(http_body);
Packit 8ea169
    free(http_body);
Packit 8ea169
    free(http_response);
Packit 8ea169
    ssl_disconnect(ssl_sock);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static int run_batch(bool delete_temp_archive)
Packit 8ea169
{
Packit 8ea169
    char *task_id, *task_password;
Packit 8ea169
    int retcode = create(delete_temp_archive, &task_id, &task_password);
Packit 8ea169
    if (0 != retcode)
Packit 8ea169
        return retcode;
Packit 8ea169
    char *task_status = xstrdup("");
Packit 8ea169
    char *status_message = xstrdup("");
Packit 8ea169
    int status_delay = delay ? delay : 10;
Packit 8ea169
    int dots = 0;
Packit 8ea169
    while (0 != strncmp(task_status, "FINISHED", strlen("finished")))
Packit 8ea169
    {
Packit 8ea169
        char *previous_status_message = status_message;
Packit 8ea169
        free(task_status);
Packit 8ea169
        sleep(status_delay);
Packit 8ea169
        status(task_id, task_password, &task_status, &status_message);
Packit 8ea169
        if (g_verbose > 0 || 0 != strcmp(previous_status_message, status_message))
Packit 8ea169
        {
Packit 8ea169
            if (dots)
Packit 8ea169
            {   /* A same message was received and a period was printed instead
Packit 8ea169
                 * but the period wasn't followed by new line and now we are 
Packit 8ea169
                 * goning to print a new message thus we want to start at next line
Packit 8ea169
                 */
Packit 8ea169
                dots = 0;
Packit 8ea169
                putchar('\n');
Packit 8ea169
            }
Packit 8ea169
            puts(status_message);
Packit 8ea169
            fflush(stdout);
Packit 8ea169
        }
Packit 8ea169
        else
Packit 8ea169
        {
Packit 8ea169
            if (dots >= MAX_DOTS_PER_LINE)
Packit 8ea169
            {
Packit 8ea169
                dots = 0;
Packit 8ea169
                putchar('\n');
Packit 8ea169
            }
Packit 8ea169
            ++dots;
Packit 8ea169
            client_log(".");
Packit 8ea169
            fflush(stdout);
Packit 8ea169
        }
Packit 8ea169
        free(previous_status_message);
Packit 8ea169
        previous_status_message = status_message;
Packit 8ea169
    }
Packit 8ea169
    if (0 == strcmp(task_status, "FINISHED_SUCCESS"))
Packit 8ea169
    {
Packit 8ea169
        char *backtrace_text;
Packit 8ea169
        backtrace(task_id, task_password, &backtrace_text);
Packit 8ea169
        char *exploitable_text = NULL;
Packit 8ea169
        if (task_type == TASK_RETRACE)
Packit 8ea169
        {
Packit 8ea169
            exploitable(task_id, task_password, &exploitable_text);
Packit 8ea169
            if (!exploitable_text)
Packit 8ea169
                log_notice("No exploitable data available");
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        if (dump_dir_name)
Packit 8ea169
        {
Packit 8ea169
            struct dump_dir *dd = dd_opendir(dump_dir_name, 0/* flags */);
Packit 8ea169
            if (!dd)
Packit 8ea169
            {
Packit 8ea169
                free(backtrace_text);
Packit 8ea169
                xfunc_die();
Packit 8ea169
            }
Packit 8ea169
Packit 8ea169
            /* the result of TASK_VMCORE is not backtrace, but kernel log */
Packit 8ea169
            const char *target = task_type == TASK_VMCORE ? FILENAME_KERNEL_LOG : FILENAME_BACKTRACE;
Packit 8ea169
            dd_save_text(dd, target, backtrace_text);
Packit 8ea169
Packit 8ea169
            if (exploitable_text)
Packit 8ea169
            {
Packit 8ea169
                int exploitable_rating = get_exploitable_rating(exploitable_text);
Packit 8ea169
                if (exploitable_rating >= MIN_EXPLOITABLE_RATING)
Packit 8ea169
                    dd_save_text(dd, FILENAME_EXPLOITABLE, exploitable_text);
Packit 8ea169
                else
Packit 8ea169
                    log_notice("Not saving exploitable data, rating < %d",
Packit 8ea169
                                  MIN_EXPLOITABLE_RATING);
Packit 8ea169
            }
Packit 8ea169
Packit 8ea169
            dd_close(dd);
Packit 8ea169
        }
Packit 8ea169
        else
Packit 8ea169
        {
Packit 8ea169
            printf("%s\n", backtrace_text);
Packit 8ea169
            if (exploitable_text)
Packit 8ea169
                printf("%s\n", exploitable_text);
Packit 8ea169
        }
Packit 8ea169
        free(backtrace_text);
Packit 8ea169
        free(exploitable_text);
Packit 8ea169
    }
Packit 8ea169
    else
Packit 8ea169
    {
Packit 8ea169
        alert(_("Retrace failed. Try again later and if the problem persists "
Packit 8ea169
                "report this issue please."));
Packit 8ea169
        run_log(task_id, task_password);
Packit 8ea169
        retcode = 1;
Packit 8ea169
    }
Packit 8ea169
    free(task_status);
Packit 8ea169
    free(status_message);
Packit 8ea169
    free(task_id);
Packit 8ea169
    free(task_password);
Packit 8ea169
    return retcode;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
int main(int argc, char **argv)
Packit 8ea169
{
Packit 8ea169
    setlocale(LC_ALL, "");
Packit 8ea169
#if ENABLE_NLS
Packit 8ea169
    bindtextdomain(PACKAGE, LOCALEDIR);
Packit 8ea169
    textdomain(PACKAGE);
Packit 8ea169
#endif
Packit 8ea169
Packit 8ea169
    abrt_init(argv);
Packit 8ea169
    get_language(&lang);
Packit 8ea169
Packit 8ea169
    const char *task_id = NULL;
Packit 8ea169
    const char *task_password = NULL;
Packit 8ea169
Packit 8ea169
    enum {
Packit 8ea169
        OPT_verbose   = 1 << 0,
Packit 8ea169
        OPT_syslog    = 1 << 1,
Packit 8ea169
        OPT_insecure  = 1 << 2,
Packit 8ea169
        OPT_no_pkgchk = 1 << 3,
Packit 8ea169
        OPT_url       = 1 << 4,
Packit 8ea169
        OPT_port      = 1 << 5,
Packit 8ea169
        OPT_headers   = 1 << 6,
Packit 8ea169
        OPT_group_1   = 1 << 7,
Packit 8ea169
        OPT_dir       = 1 << 8,
Packit 8ea169
        OPT_core      = 1 << 9,
Packit 8ea169
        OPT_delay     = 1 << 10,
Packit 8ea169
        OPT_no_unlink = 1 << 11,
Packit 8ea169
        OPT_group_2   = 1 << 12,
Packit 8ea169
        OPT_task      = 1 << 13,
Packit 8ea169
        OPT_password  = 1 << 14
Packit 8ea169
    };
Packit 8ea169
Packit 8ea169
    /* Keep enum above and order of options below in sync! */
Packit 8ea169
    struct options options[] = {
Packit 8ea169
        OPT__VERBOSE(&g_verbose),
Packit 8ea169
        OPT_BOOL('s', "syslog", NULL, _("log to syslog")),
Packit 8ea169
        OPT_BOOL('k', "insecure", NULL,
Packit 8ea169
                 _("allow insecure connection to retrace server")),
Packit 8ea169
        OPT_BOOL(0, "no-pkgcheck", NULL,
Packit 8ea169
                 _("do not check whether retrace server is able to "
Packit 8ea169
                   "process given package before uploading the archive")),
Packit 8ea169
        OPT_STRING(0, "url", &(cfg.url), "URL",
Packit 8ea169
                   _("retrace server URL")),
Packit 8ea169
        OPT_INTEGER(0, "port", &(cfg.port),
Packit 8ea169
                    _("retrace server port")),
Packit 8ea169
        OPT_BOOL(0, "headers", NULL,
Packit 8ea169
                 _("(debug) show received HTTP headers")),
Packit 8ea169
        OPT_GROUP(_("For create and batch operations")),
Packit 8ea169
        OPT_STRING('d', "dir", &dump_dir_name, "DIR",
Packit 8ea169
                   _("read data from ABRT problem directory")),
Packit 8ea169
        OPT_STRING('c', "core", &coredump, "COREDUMP",
Packit 8ea169
                   _("read data from coredump")),
Packit 8ea169
        OPT_INTEGER('l', "status-delay", &delay,
Packit 8ea169
                    _("Delay for polling operations")),
Packit 8ea169
        OPT_BOOL(0, "no-unlink", NULL,
Packit 8ea169
                 _("(debug) do not delete temporary archive created"
Packit 8ea169
                   " from dump dir in "LARGE_DATA_TMP_DIR)),
Packit 8ea169
        OPT_GROUP(_("For status, backtrace, and log operations")),
Packit 8ea169
        OPT_STRING('t', "task", &task_id, "ID",
Packit 8ea169
                   _("id of your task on server")),
Packit 8ea169
        OPT_STRING('p', "password", &task_password, "PWD",
Packit 8ea169
                   _("password of your task on server")),
Packit 8ea169
        OPT_END()
Packit 8ea169
    };
Packit 8ea169
Packit 8ea169
    const char *usage = _("abrt-retrace-client <operation> [options]\n"
Packit 8ea169
        "Operations: create/status/backtrace/log/batch/exploitable");
Packit 8ea169
Packit 8ea169
    char *env_url = getenv("RETRACE_SERVER_URL");
Packit 8ea169
    if (env_url)
Packit 8ea169
        cfg.url = env_url;
Packit 8ea169
Packit 8ea169
    char *env_port = getenv("RETRACE_SERVER_PORT");
Packit 8ea169
    if (env_port)
Packit 8ea169
        cfg.port = xatou(env_port);
Packit 8ea169
Packit 8ea169
    char *env_delay = getenv("ABRT_STATUS_DELAY");
Packit 8ea169
    if (env_delay)
Packit 8ea169
        delay = xatou(env_delay);
Packit 8ea169
Packit 8ea169
    char *env_insecure = getenv("RETRACE_SERVER_INSECURE");
Packit 8ea169
    if (env_insecure)
Packit 8ea169
        cfg.ssl_allow_insecure = strncmp(env_insecure, "insecure", strlen("insecure")) == 0;
Packit 8ea169
Packit 8ea169
    unsigned opts = parse_opts(argc, argv, options, usage);
Packit 8ea169
    if (opts & OPT_syslog)
Packit 8ea169
    {
Packit 8ea169
        logmode = LOGMODE_JOURNAL;
Packit 8ea169
    }
Packit 8ea169
    const char *operation = NULL;
Packit 8ea169
    if (optind < argc)
Packit 8ea169
        operation = argv[optind];
Packit 8ea169
    else
Packit 8ea169
        show_usage_and_die(usage, options);
Packit 8ea169
Packit 8ea169
    if (!cfg.ssl_allow_insecure)
Packit 8ea169
        cfg.ssl_allow_insecure = opts & OPT_insecure;
Packit 8ea169
    http_show_headers = opts & OPT_headers;
Packit 8ea169
    no_pkgcheck = opts & OPT_no_pkgchk;
Packit 8ea169
Packit 8ea169
    /* Initialize NSS */
Packit 8ea169
    SECMODModule *mod;
Packit 8ea169
    nss_init(&mod);
Packit 8ea169
Packit 8ea169
    /* Run the desired operation. */
Packit 8ea169
    int result = 0;
Packit 8ea169
    if (0 == strcasecmp(operation, "create"))
Packit 8ea169
    {
Packit 8ea169
        if (!dump_dir_name && !coredump)
Packit 8ea169
            error_msg_and_die(_("Either problem directory or coredump is needed."));
Packit 8ea169
        result = run_create(0 == (opts & OPT_no_unlink));
Packit 8ea169
    }
Packit 8ea169
    else if (0 == strcasecmp(operation, "batch"))
Packit 8ea169
    {
Packit 8ea169
        if (!dump_dir_name && !coredump)
Packit 8ea169
            error_msg_and_die(_("Either problem directory or coredump is needed."));
Packit 8ea169
        result = run_batch(0 == (opts & OPT_no_unlink));
Packit 8ea169
    }
Packit 8ea169
    else if (0 == strcasecmp(operation, "status"))
Packit 8ea169
    {
Packit 8ea169
        if (!task_id)
Packit 8ea169
            error_msg_and_die(_("Task id is needed."));
Packit 8ea169
        if (!task_password)
Packit 8ea169
            error_msg_and_die(_("Task password is needed."));
Packit 8ea169
        run_status(task_id, task_password);
Packit 8ea169
    }
Packit 8ea169
    else if (0 == strcasecmp(operation, "backtrace"))
Packit 8ea169
    {
Packit 8ea169
        if (!task_id)
Packit 8ea169
            error_msg_and_die(_("Task id is needed."));
Packit 8ea169
        if (!task_password)
Packit 8ea169
            error_msg_and_die(_("Task password is needed."));
Packit 8ea169
        run_backtrace(task_id, task_password);
Packit 8ea169
    }
Packit 8ea169
    else if (0 == strcasecmp(operation, "log"))
Packit 8ea169
    {
Packit 8ea169
        if (!task_id)
Packit 8ea169
            error_msg_and_die(_("Task id is needed."));
Packit 8ea169
        if (!task_password)
Packit 8ea169
            error_msg_and_die(_("Task password is needed."));
Packit 8ea169
        run_log(task_id, task_password);
Packit 8ea169
    }
Packit 8ea169
    else if (0 == strcasecmp(operation, "exploitable"))
Packit 8ea169
    {
Packit 8ea169
        if (!task_id)
Packit 8ea169
            error_msg_and_die(_("Task id is needed."));
Packit 8ea169
        if (!task_password)
Packit 8ea169
            error_msg_and_die(_("Task password is needed."));
Packit 8ea169
        run_exploitable(task_id, task_password);
Packit 8ea169
    }
Packit 8ea169
    else
Packit 8ea169
        error_msg_and_die(_("Unknown operation: %s."), operation);
Packit 8ea169
Packit 8ea169
    /* Shutdown NSS. */
Packit 8ea169
    nss_close(mod);
Packit 8ea169
Packit 8ea169
    return result;
Packit 8ea169
}