/*
    Copyright (C) 2011  ABRT Team
    Copyright (C) 2011  RedHat inc.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "libabrt.h"
#include "abrt-cli-core.h"

/* It is not possible to include polkitagent.h without the following define.
 * Check out the included header file.
 */
#ifdef HAVE_POLKIT
#define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE
#include <polkitagent/polkitagent.h>
#endif

int g_cli_authenticate;

#ifdef HAVE_POLKIT
static PolkitAgentListener *s_local_polkit_agent = NULL;
static gpointer s_local_agent_handle = NULL;
#endif

/* Vector of problems: */
/* problem_data_vector[i] = { "name" = { "content", CD_FLAG_foo_bits } } */

inline problem_data_t *get_problem_data(vector_of_problem_data_t *vector, unsigned i)
{
    return (problem_data_t *)g_ptr_array_index(vector, i);
}

void free_vector_of_problem_data(vector_of_problem_data_t *vector)
{
    if (vector)
        g_ptr_array_free(vector, TRUE);
}

vector_of_problem_data_t *new_vector_of_problem_data(void)
{
    return g_ptr_array_new_with_free_func((void (*)(void*)) &problem_data_free);
}

vector_of_problem_data_t *fetch_crash_infos(void)
{
    GList *problems = get_problems_over_dbus(g_cli_authenticate);
    if (problems == ERR_PTR)
        return NULL;

    vector_of_problem_data_t *vpd = new_vector_of_problem_data();

    for (GList *iter = problems; iter; iter = g_list_next(iter))
    {
        problem_data_t *problem_data = get_full_problem_data_over_dbus((const char *)(iter->data));
        if (problem_data == ERR_PTR)
            continue;

        g_ptr_array_add(vpd, problem_data);
    }

    return vpd;
}


static bool isxdigit_str(const char *str)
{
    do
    {
        if (*str < '0' || *str > '9')
            if ((*str|0x20) < 'a' || (*str|0x20) > 'f')
                return false;
        str++;
    } while (*str);
    return true;
}

char *find_problem_by_hash(const char *hash, GList *problems)
{
    unsigned hash_len = strlen(hash);
    if (!isxdigit_str(hash) || hash_len < 5)
        return NULL;

    char *found_name = NULL;
    for (GList *iter = problems; iter; iter = g_list_next(iter))
    {
        char hash_str[SHA1_RESULT_LEN*2 + 1];
        str_to_sha1str(hash_str, (const char *)(iter->data));
        if (strncasecmp(hash, hash_str, hash_len) == 0)
        {
            if (found_name)
                error_msg_and_die(_("'%s' identifies more than one problem directory"), hash);
            found_name = xstrdup((const char *)(iter->data));
        }
    }

    return found_name;
}

char *hash2dirname(const char *hash)
{
    /* Try loading by dirname hash */
    GList *problems = get_problems_over_dbus(g_cli_authenticate);
    if (problems == ERR_PTR)
        return NULL;

    char *found_name = find_problem_by_hash(hash, problems);

    g_list_free_full(problems, free);

    return found_name;
}

char *hash2dirname_if_necessary(const char *input)
{
    return isxdigit_str(input) ? hash2dirname(input) : xstrdup(input);
}

void initialize_polkit_agent(void)
{
#ifdef HAVE_POLKIT
    GError *error = NULL;
    PolkitSubject *subject = polkit_unix_process_new_for_owner(
                                getpid(),
                                /*start time from /proc*/0,
                                getuid());

    s_local_polkit_agent = polkit_agent_text_listener_new(NULL, &error);
    if (s_local_polkit_agent == NULL)
    {
        error_msg_and_die("polkit_agent_text_listener_new: %s (%s, %d)\n",
                error->message, g_quark_to_string (error->domain), error->code);
    }

    s_local_agent_handle = polkit_agent_listener_register(s_local_polkit_agent,
            POLKIT_AGENT_REGISTER_FLAGS_RUN_IN_THREAD, subject, NULL, NULL, &error);
    if (s_local_agent_handle == NULL)
    {
        error_msg_and_die("polkit_agent_listener_register: %s (%s, %d)\n",
                error->message, g_quark_to_string (error->domain), error->code);
    }

    g_object_unref(subject);
#else
    log_info("Polkit support is currently disabled");
#endif
}

void uninitialize_polkit_agent(void)
{
#ifdef HAVE_POLKIT
    if (s_local_agent_handle != NULL)
        polkit_agent_listener_unregister(s_local_agent_handle);

    if (s_local_polkit_agent != NULL)
        g_object_unref(s_local_polkit_agent);
#endif
}
