Blame src/lib/ureport.c

Packit 4f15d5
/*
Packit 4f15d5
    Copyright (C) 2012,2014  ABRT team
Packit 4f15d5
    Copyright (C) 2012,2014  RedHat Inc
Packit 4f15d5
Packit 4f15d5
    This program is free software; you can redistribute it and/or modify
Packit 4f15d5
    it under the terms of the GNU General Public License as published by
Packit 4f15d5
    the Free Software Foundation; either version 2 of the License, or
Packit 4f15d5
    (at your option) any later version.
Packit 4f15d5
Packit 4f15d5
    This program is distributed in the hope that it will be useful,
Packit 4f15d5
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 4f15d5
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 4f15d5
    GNU General Public License for more details.
Packit 4f15d5
Packit 4f15d5
    You should have received a copy of the GNU General Public License along
Packit 4f15d5
    with this program; if not, write to the Free Software Foundation, Inc.,
Packit 4f15d5
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Packit 4f15d5
*/
Packit 4f15d5
#include <json.h>
Packit 4f15d5
Packit 4f15d5
#include <satyr/abrt.h>
Packit 4f15d5
#include <satyr/report.h>
Packit 4f15d5
Packit 4f15d5
#include "internal_libreport.h"
Packit 4f15d5
#include "client.h"
Packit 4f15d5
#include "ureport.h"
Packit 4f15d5
#include "libreport_curl.h"
Packit 4f15d5
Packit 4f15d5
#define DESTROYED_POINTER (void *)0xdeadbeef
Packit 4f15d5
Packit 4f15d5
#define BTHASH_URL_SFX "reports/bthash/"
Packit 4f15d5
Packit 4f15d5
static char *
Packit 4f15d5
puppet_config_print(const char *key)
Packit 4f15d5
{
Packit 4f15d5
    char *command = xasprintf("puppet config print %s", key);
Packit 4f15d5
    char *result = run_in_shell_and_save_output(0, command, NULL, NULL);
Packit 4f15d5
    free(command);
Packit 4f15d5
Packit 4f15d5
    /* run_in_shell_and_save_output always returns non-NULL */
Packit 4f15d5
    if (result[0] != '/')
Packit 4f15d5
        goto error;
Packit 4f15d5
Packit 4f15d5
    char *newline = strchrnul(result, '\n');
Packit 4f15d5
    if (!newline)
Packit 4f15d5
        goto error;
Packit 4f15d5
Packit 4f15d5
    *newline = '\0';
Packit 4f15d5
    return result;
Packit 4f15d5
error:
Packit 4f15d5
    free(result);
Packit 4f15d5
    error_msg_and_die("Unable to determine puppet %s path (puppet not installed?)", key);
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
void
Packit 4f15d5
ureport_server_config_set_url(struct ureport_server_config *config,
Packit 4f15d5
                              char *server_url)
Packit 4f15d5
{
Packit 4f15d5
    free(config->ur_url);
Packit 4f15d5
    config->ur_url = server_url;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
void
Packit 4f15d5
ureport_server_config_set_client_auth(struct ureport_server_config *config,
Packit 4f15d5
                                      const char *client_auth)
Packit 4f15d5
{
Packit 4f15d5
    if (client_auth == NULL)
Packit 4f15d5
        return;
Packit 4f15d5
Packit 4f15d5
    if (strcmp(client_auth, "") == 0)
Packit 4f15d5
    {
Packit 4f15d5
        free(config->ur_client_cert);
Packit 4f15d5
        config->ur_client_cert = NULL;
Packit 4f15d5
Packit 4f15d5
        free(config->ur_client_key);
Packit 4f15d5
        config->ur_client_key = NULL;
Packit 4f15d5
Packit 4f15d5
        log_notice("Not using client authentication");
Packit 4f15d5
    }
Packit 4f15d5
    else if (strcmp(client_auth, "puppet") == 0)
Packit 4f15d5
    {
Packit 4f15d5
        config->ur_client_cert = puppet_config_print("hostcert");
Packit 4f15d5
        config->ur_client_key = puppet_config_print("hostprivkey");
Packit 4f15d5
    }
Packit 4f15d5
    else
Packit 4f15d5
    {
Packit 4f15d5
        char *scratch = xstrdup(client_auth);
Packit 4f15d5
        config->ur_client_cert = xstrdup(strtok(scratch, ":"));
Packit 4f15d5
        config->ur_client_key = xstrdup(strtok(NULL, ":"));
Packit 4f15d5
        free(scratch);
Packit 4f15d5
Packit 4f15d5
        if (config->ur_client_cert == NULL || config->ur_client_key == NULL)
Packit 4f15d5
            error_msg_and_die("Invalid client authentication specification");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (config->ur_client_cert && config->ur_client_key)
Packit 4f15d5
    {
Packit 4f15d5
        log_notice("Using client certificate: %s", config->ur_client_cert);
Packit 4f15d5
        log_notice("Using client private key: %s", config->ur_client_key);
Packit 4f15d5
Packit 4f15d5
        free(config->ur_username);
Packit 4f15d5
        config->ur_username = NULL;
Packit 4f15d5
Packit 4f15d5
        free(config->ur_password);
Packit 4f15d5
        config->ur_password = NULL;
Packit 4f15d5
    }
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
void
Packit 4f15d5
ureport_server_config_set_basic_auth(struct ureport_server_config *config,
Packit 4f15d5
                                     const char *login, const char *password)
Packit 4f15d5
{
Packit 4f15d5
    ureport_server_config_set_client_auth(config, "");
Packit 4f15d5
Packit 4f15d5
    free(config->ur_username);
Packit 4f15d5
    config->ur_username = xstrdup(login);
Packit 4f15d5
Packit 4f15d5
    free(config->ur_password);
Packit 4f15d5
    config->ur_password = xstrdup(password);
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
void
Packit 4f15d5
ureport_server_config_load_basic_auth(struct ureport_server_config *config,
Packit 4f15d5
                                      const char *http_auth_pref)
Packit 4f15d5
{
Packit 4f15d5
    if (http_auth_pref == NULL)
Packit 4f15d5
        return;
Packit 4f15d5
Packit 4f15d5
    map_string_t *settings = NULL;
Packit 4f15d5
Packit 4f15d5
    char *tmp_password = NULL;
Packit 4f15d5
    char *tmp_username = NULL;
Packit 4f15d5
    const char *username = NULL;
Packit 4f15d5
    const char *password = NULL;
Packit 4f15d5
Packit 4f15d5
    username = tmp_username = xstrdup(http_auth_pref);
Packit 4f15d5
    password = strchr(tmp_username, ':');
Packit 4f15d5
Packit 4f15d5
    if (password != NULL)
Packit 4f15d5
        /* It is "char *", see strchr() few lines above. */
Packit 4f15d5
        *((char *)(password++)) = '\0';
Packit 4f15d5
Packit 4f15d5
    if (password == NULL)
Packit 4f15d5
    {
Packit 4f15d5
        char *message = xasprintf("Please provide uReport server password for user '%s':", username);
Packit 4f15d5
        password = tmp_password = ask_password(message);
Packit 4f15d5
        free(message);
Packit 4f15d5
Packit 4f15d5
        if (strcmp(password, "") == 0)
Packit 4f15d5
            error_msg_and_die("Cannot continue without uReport server password!");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    ureport_server_config_set_basic_auth(config, username, password);
Packit 4f15d5
Packit 4f15d5
    free(tmp_password);
Packit 4f15d5
    free(tmp_username);
Packit 4f15d5
    free_map_string(settings);
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
void
Packit 4f15d5
ureport_server_config_load(struct ureport_server_config *config,
Packit 4f15d5
                           map_string_t *settings)
Packit 4f15d5
{
Packit 4f15d5
    UREPORT_OPTION_VALUE_FROM_CONF(settings, "URL", config->ur_url, xstrdup);
Packit 4f15d5
    UREPORT_OPTION_VALUE_FROM_CONF(settings, "SSLVerify", config->ur_ssl_verify, string_to_bool);
Packit 4f15d5
Packit 4f15d5
    const char *http_auth_pref = NULL;
Packit 4f15d5
    UREPORT_OPTION_VALUE_FROM_CONF(settings, "HTTPAuth", http_auth_pref, (const char *));
Packit 4f15d5
    ureport_server_config_load_basic_auth(config, http_auth_pref);
Packit 4f15d5
Packit 4f15d5
    const char *client_auth = NULL;
Packit 4f15d5
    if (http_auth_pref == NULL)
Packit 4f15d5
    {
Packit 4f15d5
        UREPORT_OPTION_VALUE_FROM_CONF(settings, "SSLClientAuth", client_auth, (const char *));
Packit 4f15d5
        ureport_server_config_set_client_auth(config, client_auth);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    /* If SSLClientAuth is configured, include the auth items by default. */
Packit 4f15d5
    bool include_auth = config->ur_client_cert != NULL || config->ur_username != NULL;
Packit 4f15d5
    UREPORT_OPTION_VALUE_FROM_CONF(settings, "IncludeAuthData", include_auth, string_to_bool);
Packit 4f15d5
Packit 4f15d5
    if (include_auth)
Packit 4f15d5
    {
Packit 4f15d5
        const char *auth_items = NULL;
Packit 4f15d5
        UREPORT_OPTION_VALUE_FROM_CONF(settings, "AuthDataItems", auth_items, (const char *));
Packit 4f15d5
        config->ur_prefs.urp_auth_items = parse_list(auth_items);
Packit 4f15d5
Packit 4f15d5
        if (config->ur_prefs.urp_auth_items == NULL)
Packit 4f15d5
            log_warning("IncludeAuthData set to 'yes' but AuthDataItems is empty.");
Packit 4f15d5
    }
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
void
Packit 4f15d5
ureport_server_config_init(struct ureport_server_config *config)
Packit 4f15d5
{
Packit 4f15d5
    config->ur_url = NULL;
Packit 4f15d5
    config->ur_ssl_verify = true;
Packit 4f15d5
    config->ur_client_cert = NULL;
Packit 4f15d5
    config->ur_client_key = NULL;
Packit 4f15d5
    config->ur_cert_authority_cert = NULL;
Packit 4f15d5
    config->ur_username = NULL;
Packit 4f15d5
    config->ur_password = NULL;
Packit 4f15d5
    config->ur_http_headers = new_map_string();
Packit 4f15d5
Packit 4f15d5
    config->ur_prefs.urp_auth_items = NULL;
Packit 4f15d5
    config->ur_prefs.urp_flags = 0;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
void
Packit 4f15d5
ureport_server_config_destroy(struct ureport_server_config *config)
Packit 4f15d5
{
Packit 4f15d5
    free(config->ur_url);
Packit 4f15d5
    config->ur_url = DESTROYED_POINTER;
Packit 4f15d5
Packit 4f15d5
    free(config->ur_client_cert);
Packit 4f15d5
    config->ur_client_cert = DESTROYED_POINTER;
Packit 4f15d5
Packit 4f15d5
    free(config->ur_client_key);
Packit 4f15d5
    config->ur_client_key = DESTROYED_POINTER;
Packit 4f15d5
Packit 4f15d5
    free(config->ur_cert_authority_cert);
Packit 4f15d5
    config->ur_cert_authority_cert = DESTROYED_POINTER;
Packit 4f15d5
Packit 4f15d5
    free(config->ur_username);
Packit 4f15d5
    config->ur_username = DESTROYED_POINTER;
Packit 4f15d5
Packit 4f15d5
    free(config->ur_password);
Packit 4f15d5
    config->ur_password = DESTROYED_POINTER;
Packit 4f15d5
Packit 4f15d5
    g_list_free_full(config->ur_prefs.urp_auth_items, free);
Packit 4f15d5
    config->ur_prefs.urp_auth_items = DESTROYED_POINTER;
Packit 4f15d5
Packit 4f15d5
    free_map_string(config->ur_http_headers);
Packit 4f15d5
    config->ur_http_headers = DESTROYED_POINTER;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
void
Packit 4f15d5
ureport_server_response_free(struct ureport_server_response *resp)
Packit 4f15d5
{
Packit 4f15d5
    if (!resp)
Packit 4f15d5
        return;
Packit 4f15d5
Packit 4f15d5
    free(resp->urr_solution);
Packit 4f15d5
    resp->urr_solution = DESTROYED_POINTER;
Packit 4f15d5
Packit 4f15d5
    g_list_free_full(resp->urr_reported_to_list, g_free);
Packit 4f15d5
    resp->urr_reported_to_list = DESTROYED_POINTER;
Packit 4f15d5
Packit 4f15d5
    free(resp->urr_bthash);
Packit 4f15d5
    resp->urr_bthash = DESTROYED_POINTER;
Packit 4f15d5
Packit 4f15d5
    free(resp->urr_message);
Packit 4f15d5
    resp->urr_message = DESTROYED_POINTER;
Packit 4f15d5
Packit 4f15d5
    free(resp->urr_value);
Packit 4f15d5
    resp->urr_value = DESTROYED_POINTER;
Packit 4f15d5
Packit 4f15d5
    free(resp);
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
static char *
Packit 4f15d5
parse_solution_from_json_list(struct json_object *list,
Packit 4f15d5
                              GList **reported_to)
Packit 4f15d5
{
Packit 4f15d5
    json_object *list_elem, *struct_elem;
Packit 4f15d5
    const char *cause, *note, *url;
Packit 4f15d5
    struct strbuf *solution_buf = strbuf_new();
Packit 4f15d5
Packit 4f15d5
    const unsigned length = json_object_array_length(list);
Packit 4f15d5
Packit 4f15d5
    const char *one_format = _("Your problem seems to be caused by %s\n\n%s\n");
Packit 4f15d5
    if (length > 1)
Packit 4f15d5
    {
Packit 4f15d5
        strbuf_append_str(solution_buf, _("Your problem seems to be caused by one of the following:\n"));
Packit 4f15d5
        one_format = "\n* %s\n\n%s\n";
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    bool empty = true;
Packit 4f15d5
    for (unsigned i = 0; i < length; ++i)
Packit 4f15d5
    {
Packit 4f15d5
        list_elem = json_object_array_get_idx(list, i);
Packit 4f15d5
        if (!list_elem)
Packit 4f15d5
            continue;
Packit 4f15d5
Packit 4f15d5
        if (!json_object_object_get_ex(list_elem, "cause", &struct_elem))
Packit 4f15d5
            continue;
Packit 4f15d5
Packit 4f15d5
        cause = json_object_get_string(struct_elem);
Packit 4f15d5
        if (!cause)
Packit 4f15d5
            continue;
Packit 4f15d5
Packit 4f15d5
        if (!json_object_object_get_ex(list_elem, "note", &struct_elem))
Packit 4f15d5
            continue;
Packit 4f15d5
Packit 4f15d5
        note = json_object_get_string(struct_elem);
Packit 4f15d5
        if (!note)
Packit 4f15d5
            continue;
Packit 4f15d5
Packit 4f15d5
        empty = false;
Packit 4f15d5
        strbuf_append_strf(solution_buf, one_format, cause, note);
Packit 4f15d5
Packit 4f15d5
        if (!json_object_object_get_ex(list_elem, "url", &struct_elem))
Packit 4f15d5
            continue;
Packit 4f15d5
Packit 4f15d5
        url = json_object_get_string(struct_elem);
Packit 4f15d5
        if (url)
Packit 4f15d5
        {
Packit 4f15d5
            char *reported_to_line = xasprintf("%s: URL=%s", cause, url);
Packit 4f15d5
            *reported_to = g_list_append(*reported_to, reported_to_line);
Packit 4f15d5
        }
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (empty)
Packit 4f15d5
    {
Packit 4f15d5
        strbuf_free(solution_buf);
Packit 4f15d5
        return NULL;
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    return strbuf_free_nobuf(solution_buf);
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
/* reported_to json element should be a list of structures
Packit 4f15d5
   {
Packit 4f15d5
     "reporter": "Bugzilla",
Packit 4f15d5
     "type": "url",
Packit 4f15d5
     "value": "https://bugzilla.redhat.com/show_bug.cgi?id=XYZ"
Packit 4f15d5
   }
Packit 4f15d5
 */
Packit 4f15d5
static GList *
Packit 4f15d5
parse_reported_to_from_json_list(struct json_object *list)
Packit 4f15d5
{
Packit 4f15d5
    int i;
Packit 4f15d5
    json_object *list_elem, *struct_elem;
Packit 4f15d5
    const char *reporter, *value, *type;
Packit 4f15d5
    char *reported_to_line, *prefix;
Packit 4f15d5
    GList *result = NULL;
Packit 4f15d5
Packit 4f15d5
    for (i = 0; i < json_object_array_length(list); ++i)
Packit 4f15d5
    {
Packit 4f15d5
        prefix = NULL;
Packit 4f15d5
        list_elem = json_object_array_get_idx(list, i);
Packit 4f15d5
        if (!list_elem)
Packit 4f15d5
            continue;
Packit 4f15d5
Packit 4f15d5
        if (!json_object_object_get_ex(list_elem, "reporter", &struct_elem))
Packit 4f15d5
            continue;
Packit 4f15d5
Packit 4f15d5
        reporter = json_object_get_string(struct_elem);
Packit 4f15d5
        if (!reporter)
Packit 4f15d5
            continue;
Packit 4f15d5
Packit 4f15d5
        if (!json_object_object_get_ex(list_elem, "value", &struct_elem))
Packit 4f15d5
            continue;
Packit 4f15d5
Packit 4f15d5
        value = json_object_get_string(struct_elem);
Packit 4f15d5
        if (!value)
Packit 4f15d5
            continue;
Packit 4f15d5
Packit 4f15d5
        if (!json_object_object_get_ex(list_elem, "type", &struct_elem))
Packit 4f15d5
            continue;
Packit 4f15d5
Packit 4f15d5
        type = json_object_get_string(struct_elem);
Packit 4f15d5
        if (type)
Packit 4f15d5
        {
Packit 4f15d5
            if (strcasecmp("url", type) == 0)
Packit 4f15d5
                prefix = xstrdup("URL=");
Packit 4f15d5
            else if (strcasecmp("bthash", type) == 0)
Packit 4f15d5
                prefix = xstrdup("BTHASH=");
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        if (!prefix)
Packit 4f15d5
            prefix = xstrdup("");
Packit 4f15d5
Packit 4f15d5
        reported_to_line = xasprintf("%s: %s%s", reporter, prefix, value);
Packit 4f15d5
        free(prefix);
Packit 4f15d5
Packit 4f15d5
        result = g_list_append(result, reported_to_line);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    return result;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
/*
Packit 4f15d5
 * Reponse samples:
Packit 4f15d5
 * {"error":"field 'foo' is required"}
Packit 4f15d5
 * {"response":"true"}
Packit 4f15d5
 * {"response":"false"}
Packit 4f15d5
 */
Packit 4f15d5
static struct ureport_server_response *
Packit 4f15d5
ureport_server_parse_json(json_object *json)
Packit 4f15d5
{
Packit 4f15d5
    json_object *obj = NULL;
Packit 4f15d5
    if (json_object_object_get_ex(json, "error", &obj))
Packit 4f15d5
    {
Packit 4f15d5
        struct ureport_server_response *out_response = xzalloc(sizeof(*out_response));
Packit 4f15d5
        out_response->urr_is_error = true;
Packit 4f15d5
        /*
Packit 4f15d5
         * Used to use json_object_to_json_string(obj), but it returns
Packit 4f15d5
         * the string in quote marks (") - IOW, json-formatted string.
Packit 4f15d5
         */
Packit 4f15d5
        out_response->urr_value = xstrdup(json_object_get_string(obj));
Packit 4f15d5
        return out_response;
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (json_object_object_get_ex(json, "result", &obj))
Packit 4f15d5
    {
Packit 4f15d5
        struct ureport_server_response *out_response = xzalloc(sizeof(*out_response));
Packit 4f15d5
        out_response->urr_value = xstrdup(json_object_get_string(obj));
Packit 4f15d5
Packit 4f15d5
        json_object *message = NULL;
Packit 4f15d5
        if (json_object_object_get_ex(json, "message", &message))
Packit 4f15d5
            out_response->urr_message = xstrdup(json_object_get_string(message));
Packit 4f15d5
Packit 4f15d5
        json_object *bthash = NULL;
Packit 4f15d5
        if (json_object_object_get_ex(json, "bthash", &bthash))
Packit 4f15d5
            out_response->urr_bthash = xstrdup(json_object_get_string(bthash));
Packit 4f15d5
Packit 4f15d5
        json_object *reported_to_list = NULL;
Packit 4f15d5
        if (json_object_object_get_ex(json, "reported_to", &reported_to_list))
Packit 4f15d5
            out_response->urr_reported_to_list =
Packit 4f15d5
                parse_reported_to_from_json_list(reported_to_list);
Packit 4f15d5
Packit 4f15d5
        json_object *solutions = NULL;
Packit 4f15d5
        if (json_object_object_get_ex(json, "solutions", &solutions))
Packit 4f15d5
            out_response->urr_solution =
Packit 4f15d5
                parse_solution_from_json_list(solutions, &(out_response->urr_reported_to_list));
Packit 4f15d5
Packit 4f15d5
        return out_response;
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    return NULL;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
struct ureport_server_response *
Packit 4f15d5
ureport_server_response_from_reply(post_state_t *post_state,
Packit 4f15d5
                                   struct ureport_server_config *config)
Packit 4f15d5
{
Packit 4f15d5
    /* Previously, the condition here was (post_state->errmsg[0] != '\0')
Packit 4f15d5
     * however when the server asks for optional client authentication and we do not have the certificates,
Packit 4f15d5
     * then post_state->errmsg contains "NSS: client certificate not found (nickname not specified)" even though
Packit 4f15d5
     * the request succeeded.
Packit 4f15d5
     */
Packit 4f15d5
    if (post_state->curl_result != CURLE_OK)
Packit 4f15d5
    {
Packit 4f15d5
        if (strcmp(post_state->errmsg, "") != 0)
Packit 4f15d5
            error_msg(_("Failed to upload uReport to the server '%s' with curl: %s"),
Packit 4f15d5
                                                                    config->ur_url,
Packit 4f15d5
                                                                    post_state->errmsg);
Packit 4f15d5
        else
Packit 4f15d5
            error_msg(_("Failed to upload uReport to the server '%s'"), config->ur_url);
Packit 4f15d5
Packit 4f15d5
        if (post_state->curl_error_msg != NULL && strcmp(post_state->curl_error_msg, "") != 0)
Packit 4f15d5
            error_msg(_("Error: %s"), post_state->curl_error_msg);
Packit 4f15d5
Packit 4f15d5
        return NULL;
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (post_state->http_resp_code == 404)
Packit 4f15d5
    {
Packit 4f15d5
        error_msg(_("The URL '%s' does not exist (got error 404 from server)"), config->ur_url);
Packit 4f15d5
        return NULL;
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (post_state->http_resp_code == 500)
Packit 4f15d5
    {
Packit 4f15d5
        error_msg(_("The server at '%s' encountered an internal error (got error 500)"), config->ur_url);
Packit 4f15d5
        return NULL;
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (post_state->http_resp_code == 503)
Packit 4f15d5
    {
Packit 4f15d5
        error_msg(_("The server at '%s' currently can't handle the request (got error 503)"), config->ur_url);
Packit 4f15d5
        return NULL;
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (post_state->http_resp_code != 202
Packit 4f15d5
            && post_state->http_resp_code != 400
Packit 4f15d5
            && post_state->http_resp_code != 413)
Packit 4f15d5
    {
Packit 4f15d5
        /* can't print better error message */
Packit 4f15d5
        error_msg(_("Unexpected HTTP response from '%s': %d"), config->ur_url, post_state->http_resp_code);
Packit 4f15d5
        log_notice("%s", post_state->body);
Packit 4f15d5
        return NULL;
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    json_object *const json = json_tokener_parse(post_state->body);
Packit 4f15d5
Packit 4f15d5
    if (json == NULL)
Packit 4f15d5
    {
Packit 4f15d5
        error_msg(_("Unable to parse response from ureport server at '%s'"), config->ur_url);
Packit 4f15d5
        log_notice("%s", post_state->body);
Packit 4f15d5
        json_object_put(json);
Packit 4f15d5
        return NULL;
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    struct ureport_server_response *response = ureport_server_parse_json(json);
Packit 4f15d5
    json_object_put(json);
Packit 4f15d5
Packit 4f15d5
    if (!response)
Packit 4f15d5
        error_msg(_("The response from '%s' has invalid format"), config->ur_url);
Packit 4f15d5
    else if ((post_state->http_resp_code == 202 && response->urr_is_error)
Packit 4f15d5
                || (post_state->http_resp_code != 202 && !response->urr_is_error))
Packit 4f15d5
    {
Packit 4f15d5
        /* HTTP CODE 202 means that call was successful but the response */
Packit 4f15d5
        /* has an error message */
Packit 4f15d5
        error_msg(_("Type mismatch has been detected in the response from '%s'"), config->ur_url);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    return response;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
bool
Packit 4f15d5
ureport_server_response_save_in_dump_dir(struct ureport_server_response *resp,
Packit 4f15d5
                                         const char *dump_dir_path,
Packit 4f15d5
                                         struct ureport_server_config *config)
Packit 4f15d5
{
Packit 4f15d5
    struct dump_dir *dd = dd_opendir(dump_dir_path, /* flags */ 0);
Packit 4f15d5
    if (!dd)
Packit 4f15d5
        return false;
Packit 4f15d5
Packit 4f15d5
    if (resp->urr_bthash)
Packit 4f15d5
    {
Packit 4f15d5
        {
Packit 4f15d5
            report_result_t rr = { .label = (char *)"uReport" };
Packit 4f15d5
            rr.bthash = resp->urr_bthash;
Packit 4f15d5
            add_reported_to_entry(dd, &rr);
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        {
Packit 4f15d5
            report_result_t rr = { .label = (char *)"ABRT Server" };
Packit 4f15d5
            rr.url = ureport_server_response_get_report_url(resp, config);
Packit 4f15d5
            add_reported_to_entry(dd, &rr);
Packit 4f15d5
            free(rr.url);
Packit 4f15d5
        }
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (resp->urr_reported_to_list)
Packit 4f15d5
    {
Packit 4f15d5
        for (GList *e = resp->urr_reported_to_list; e; e = g_list_next(e))
Packit 4f15d5
            add_reported_to(dd, e->data);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (resp->urr_solution)
Packit 4f15d5
        dd_save_text(dd, FILENAME_NOT_REPORTABLE, resp->urr_solution);
Packit 4f15d5
Packit 4f15d5
    dd_close(dd);
Packit 4f15d5
    return true;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
char *
Packit 4f15d5
ureport_server_response_get_report_url(struct ureport_server_response *resp,
Packit 4f15d5
                                       struct ureport_server_config *config)
Packit 4f15d5
{
Packit 4f15d5
    char *bthash_url = concat_path_file(config->ur_url, BTHASH_URL_SFX);
Packit 4f15d5
    char *report_url = concat_path_file(bthash_url, resp->urr_bthash);
Packit 4f15d5
    free(bthash_url);
Packit 4f15d5
    return report_url;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
static void
Packit 4f15d5
ureport_add_str(struct json_object *ur, const char *key, const char *s)
Packit 4f15d5
{
Packit 4f15d5
    struct json_object *jstring = json_object_new_string(s);
Packit 4f15d5
    if (!jstring)
Packit 4f15d5
        die_out_of_memory();
Packit 4f15d5
Packit 4f15d5
    json_object_object_add(ur, key, jstring);
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
char *
Packit 4f15d5
ureport_from_dump_dir_ext(const char *dump_dir_path, const struct ureport_preferences *preferences)
Packit 4f15d5
{
Packit 4f15d5
    char *error_message;
Packit 4f15d5
    struct sr_report *report = sr_abrt_report_from_dir(dump_dir_path,
Packit 4f15d5
                                                       &error_message);
Packit 4f15d5
Packit 4f15d5
    if (!report)
Packit 4f15d5
    {
Packit 4f15d5
        if (NULL == preferences || !(preferences->urp_flags & UREPORT_PREF_FLAG_RETURN_ON_FAILURE))
Packit 4f15d5
            error_msg_and_die("%s", error_message);
Packit 4f15d5
Packit 4f15d5
        log_notice("%s", error_message);
Packit 4f15d5
        return NULL;
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (preferences != NULL && preferences->urp_auth_items != NULL)
Packit 4f15d5
    {
Packit 4f15d5
        struct dump_dir *dd = dd_opendir(dump_dir_path, DD_OPEN_READONLY);
Packit 4f15d5
        if (!dd)
Packit 4f15d5
            xfunc_die(); /* dd_opendir() already printed an error message */
Packit 4f15d5
Packit 4f15d5
        GList *iter = preferences->urp_auth_items;
Packit 4f15d5
        for ( ; iter != NULL; iter = g_list_next(iter))
Packit 4f15d5
        {
Packit 4f15d5
            const char *key = (const char *)iter->data;
Packit 4f15d5
            char *value = dd_load_text_ext(dd, key,
Packit 4f15d5
                    DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE | DD_FAIL_QUIETLY_ENOENT);
Packit 4f15d5
Packit 4f15d5
            if (value == NULL)
Packit 4f15d5
            {
Packit 4f15d5
                perror_msg("Cannot include '%s' in 'auth'", key);
Packit 4f15d5
                continue;
Packit 4f15d5
            }
Packit 4f15d5
Packit 4f15d5
            sr_report_add_auth(report, key, value);
Packit 4f15d5
            free(value);
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        dd_close(dd);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    char *json_ureport = sr_report_to_json(report);
Packit 4f15d5
    sr_report_free(report);
Packit 4f15d5
Packit 4f15d5
    return json_ureport;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
char *
Packit 4f15d5
ureport_from_dump_dir(const char *dump_dir_path)
Packit 4f15d5
{
Packit 4f15d5
    return ureport_from_dump_dir_ext(dump_dir_path, /*no preferences*/NULL);
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
struct post_state *
Packit 4f15d5
ureport_do_post(const char *json, struct ureport_server_config *config,
Packit 4f15d5
                const char *url_sfx)
Packit 4f15d5
{
Packit 4f15d5
    int flags = POST_WANT_BODY | POST_WANT_ERROR_MSG;
Packit 4f15d5
Packit 4f15d5
    if (config->ur_ssl_verify)
Packit 4f15d5
        flags |= POST_WANT_SSL_VERIFY;
Packit 4f15d5
Packit 4f15d5
    struct post_state *post_state = new_post_state(flags);
Packit 4f15d5
Packit 4f15d5
    if (config->ur_client_cert && config->ur_client_key)
Packit 4f15d5
    {
Packit 4f15d5
        post_state->client_cert_path = config->ur_client_cert;
Packit 4f15d5
        post_state->client_key_path = config->ur_client_key;
Packit 4f15d5
        post_state->cert_authority_cert_path = config->ur_cert_authority_cert;
Packit 4f15d5
    }
Packit 4f15d5
    else if (config->ur_username && config->ur_password)
Packit 4f15d5
    {
Packit 4f15d5
        post_state->username = config->ur_username;
Packit 4f15d5
        post_state->password = config->ur_password;
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    char **headers = xmalloc(sizeof(char *) * (3 + size_map_string(config->ur_http_headers)));
Packit 4f15d5
    headers[0] = (char *)"Accept: application/json";
Packit 4f15d5
    headers[1] = (char *)"Connection: close";
Packit 4f15d5
    headers[2] = NULL;
Packit 4f15d5
Packit 4f15d5
    if (config->ur_http_headers != NULL)
Packit 4f15d5
    {
Packit 4f15d5
        unsigned i = 2;
Packit 4f15d5
        const char *header;
Packit 4f15d5
        const char *value;
Packit 4f15d5
        map_string_iter_t iter;
Packit 4f15d5
        init_map_string_iter(&iter, config->ur_http_headers);
Packit 4f15d5
        while (next_map_string_iter(&iter, &header, &value))
Packit 4f15d5
            headers[i++] = xasprintf("%s: %s", header, value);
Packit 4f15d5
        headers[i] = NULL;
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    char *dest_url = concat_path_file(config->ur_url, url_sfx);
Packit 4f15d5
Packit 4f15d5
    post_string_as_form_data(post_state, dest_url, "application/json",
Packit 4f15d5
                     (const char **)headers, json);
Packit 4f15d5
Packit 4f15d5
    /* Client authentication failed. Try again without client auth.
Packit 4f15d5
     * CURLE_SSL_CONNECT_ERROR - cert not found/server doesnt trust the CA
Packit 4f15d5
     * CURLE_SSL_CERTPROBLEM - malformed certificate/no permission
Packit 4f15d5
     */
Packit 4f15d5
    if ((post_state->curl_result == CURLE_SSL_CONNECT_ERROR
Packit 4f15d5
         || post_state->curl_result == CURLE_SSL_CERTPROBLEM)
Packit 4f15d5
            && config->ur_client_cert && config->ur_client_key)
Packit 4f15d5
    {
Packit 4f15d5
        warn_msg("Authentication failed. Retrying unauthenticated.");
Packit 4f15d5
        free_post_state(post_state);
Packit 4f15d5
        post_state = new_post_state(flags);
Packit 4f15d5
Packit 4f15d5
        post_string_as_form_data(post_state, dest_url, "application/json",
Packit 4f15d5
                         (const char **)headers, json);
Packit 4f15d5
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    free(dest_url);
Packit 4f15d5
Packit 4f15d5
    for (unsigned i = size_map_string(config->ur_http_headers); i != 0; --i)
Packit 4f15d5
        free(headers[i + 1]);
Packit 4f15d5
    free(headers);
Packit 4f15d5
Packit 4f15d5
    return post_state;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
struct ureport_server_response *
Packit 4f15d5
ureport_submit(const char *json, struct ureport_server_config *config)
Packit 4f15d5
{
Packit 4f15d5
    struct post_state *post_state = ureport_do_post(json, config, UREPORT_SUBMIT_ACTION);
Packit 4f15d5
Packit 4f15d5
    if (post_state == NULL)
Packit 4f15d5
    {
Packit 4f15d5
        error_msg(_("Failed on submitting the problem"));
Packit 4f15d5
        return NULL;
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    struct ureport_server_response *resp = ureport_server_response_from_reply(post_state, config);
Packit 4f15d5
    free(post_state);
Packit 4f15d5
Packit 4f15d5
    return resp;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
char *
Packit 4f15d5
ureport_json_attachment_new(const char *bthash, const char *type, const char *data)
Packit 4f15d5
{
Packit 4f15d5
    struct json_object *attachment = json_object_new_object();
Packit 4f15d5
    if (!attachment)
Packit 4f15d5
        die_out_of_memory();
Packit 4f15d5
Packit 4f15d5
    ureport_add_str(attachment, "bthash", bthash);
Packit 4f15d5
    ureport_add_str(attachment, "type", type);
Packit 4f15d5
    ureport_add_str(attachment, "data", data);
Packit 4f15d5
Packit 4f15d5
    char *result = xstrdup(json_object_to_json_string(attachment));
Packit 4f15d5
    json_object_put(attachment);
Packit 4f15d5
Packit 4f15d5
    return result;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
bool
Packit 4f15d5
ureport_attach_string(const char *bthash, const char *type, const char *data,
Packit 4f15d5
               struct ureport_server_config *config)
Packit 4f15d5
{
Packit 4f15d5
    char *json = ureport_json_attachment_new(bthash, type, data);
Packit 4f15d5
    post_state_t *post_state = ureport_do_post(json, config, UREPORT_ATTACH_ACTION);
Packit 4f15d5
    free(json);
Packit 4f15d5
Packit 4f15d5
    struct ureport_server_response *resp =
Packit 4f15d5
        ureport_server_response_from_reply(post_state, config);
Packit 4f15d5
    free_post_state(post_state);
Packit 4f15d5
    /* don't use str_bo_bool() because we require "true" string */
Packit 4f15d5
    const int result = !resp || resp->urr_is_error || strcmp(resp->urr_value, "true") != 0;
Packit 4f15d5
Packit 4f15d5
    if (resp && resp->urr_is_error)
Packit 4f15d5
        error_msg(_("The server at '%s' responded with an error: '%s'"),
Packit 4f15d5
                config->ur_url, resp->urr_value);
Packit 4f15d5
Packit 4f15d5
    ureport_server_response_free(resp);
Packit 4f15d5
Packit 4f15d5
    return result;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
bool
Packit 4f15d5
ureport_attach_int(const char *bthash, const char *type, int data,
Packit 4f15d5
                    struct ureport_server_config *config)
Packit 4f15d5
{
Packit 4f15d5
    char *data_str = xasprintf("%d", data);
Packit 4f15d5
    const bool result = ureport_attach_string(bthash, type, data_str, config);
Packit 4f15d5
    free(data_str);
Packit 4f15d5
Packit 4f15d5
    return result;
Packit 4f15d5
}