/*
    Copyright (C) 2010  ABRT team
    Copyright (C) 2010  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 <sys/un.h>
#include "internal_libreport.h"

#define SOCKET_FILE  VAR_RUN"/abrt/abrt.socket"

/* connects to abrtd
 * returns: socketfd
 * -1 on error
 */
static int connect_to_abrtd_socket()
{
    int socketfd = xsocket(AF_UNIX, SOCK_STREAM, 0);
    if (socketfd == -1)
        return -1;
    /*close_on_exec_on(socketfd); - not needed, we are closing it soon */
    struct sockaddr_un local;
    memset(&local, 0, sizeof(local));
    local.sun_family = AF_UNIX;
    strcpy(local.sun_path, SOCKET_FILE);
    int r = connect(socketfd, (struct sockaddr*)&local, sizeof(local));
    if (r != 0)
    {
        VERB1 pwarn_msg("Can't connect to '%s'", SOCKET_FILE);
        close(socketfd);
        return -1;
    }

    return socketfd;
}

static int connect_to_abrtd_and_call_DeleteDebugDump(const char *dump_dir_name)
{
    int result = -1; /* error so far */
    int socketfd = connect_to_abrtd_socket();
    if (socketfd != -1)
    {
        full_write(socketfd, "DELETE ", strlen("DELETE "));
        full_write(socketfd, dump_dir_name, strlen(dump_dir_name));
        full_write(socketfd, " HTTP/1.1\r\n\r\n", strlen(" HTTP/1.1\r\n\r\n"));
        shutdown(socketfd, SHUT_WR);

        char response[64];
        int r = full_read(socketfd, response, sizeof(response) - 1);
        if (r >= 0)
        {
            log_notice("Response via socket:'%.*s'", r, response);
            /*  0123456789...  */
            /* "HTTP/1.1 200 " */
            response[5] = '1';
            response[7] = '1';
            if (strncmp(response, "HTTP/1.1 ", strlen("HTTP/1.1 ")) == 0
                && isdigit(response[9])
                && isdigit(response[10])
                && isdigit(response[11])
                && response[12] == ' ')
            {
                result = (response[9] - '0') * 100 + (response[10] - '0') * 10 + (response[11] - '0');
            }
        }
    }

    close(socketfd);

    return result;
}

int problem_data_send_to_abrt(problem_data_t* problem_data)
{
    int result = 1; /* error so far */
    int socketfd = connect_to_abrtd_socket();
    if (socketfd != -1)
    {
        GHashTableIter iter;
        char *name;
        struct problem_item *value;
        g_hash_table_iter_init(&iter, problem_data);

        full_write(socketfd, "PUT / HTTP/1.1\r\n\r\n", strlen("PUT / HTTP/1.1\r\n\r\n"));
        while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
        {
            if (value->flags & CD_FLAG_BIN)
            {
                /* sending files over the socket is not implemented yet */
                log_warning("Skipping binary file %s", name);
                continue;
            }

            /* only files should contain '/' and those are handled earlier */
            if (name[0] == '.' || strchr(name, '/'))
            {
                error_msg("Problem data field name contains disallowed chars: '%s'", name);
                continue;
            }

            char* msg = xasprintf("%s=%s", name, value->content);
            full_write(socketfd, msg, strlen(msg)+1 /* yes, +1 coz we want to send the trailing 0 */);
            free(msg);
        }
        shutdown(socketfd, SHUT_WR);

        char response[64];
        int r = full_read(socketfd, response, sizeof(response) - 1);
        if (r >= 0)
        {
            log_notice("Response via socket:'%.*s'", r, response);
            /*  0123456789...  */
            /* "HTTP/1.1 200 " */
            response[5] = '1';
            response[7] = '1';
            result = strncmp(response, "HTTP/1.1 201 ", strlen("HTTP/1.1 201 "));
        }

        close(socketfd);
    }

    return result;
}

int delete_dump_dir_possibly_using_abrtd(const char *dump_dir_name)
{
    INITIALIZE_LIBREPORT();

#if DUMP_DIR_OWNED_BY_USER == 0
    /* Try to delete it ourselves */
    struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY);
    if (dd)
    {
        if (dd->locked) /* it is not readonly */
            return dd_delete(dd);
        dd_close(dd);
    }
    else
    {
        if (errno == ENOENT || errno == ENOTDIR)
            /* No such dir, no point in trying to talk over socket */
            return 1;
    }

    log_notice("Deleting '%s' via abrtd", dump_dir_name);
    const int res = connect_to_abrtd_and_call_DeleteDebugDump(dump_dir_name);
    if (res != 0)
        error_msg(_("Can't delete: '%s'"), dump_dir_name);

    return res;
#else
    log_notice("Deleting '%s' via abrtd", dump_dir_name);
    const int res = connect_to_abrtd_and_call_DeleteDebugDump(dump_dir_name);
    if (res == 200)
    {
        /*
         * Deleted
         */
        return 0;
    }

    /*
     * An error occurred but we can still try to delete it directly
     */

    /* Using NULL in order to easily detect a buggy error message */
    const char *error_reason = NULL;
    /* Used only for error messages */
    char num_buf[sizeof(int)*3 + 1];

    if (res < 0 || res == 400)
    {
        /*  -1 : an error in communication
         * 400 : bad request or abrtd refused to delete the directory outside of the dump location
         *
         * Try to delete it ourselves
         */
        struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY);
        if (dd)
        {
            if (dd->locked) /* it is not readonly */
                return dd_delete(dd);

            error_reason = _("locked by another process");
            dd_close(dd);
        }
    }
    else
    {
        switch (res)
        {
            case 403:
                error_reason = _("permission denied");
                break;
            case 404:
                error_reason = _("not a problem directory");
                break;
            default:
                snprintf(num_buf, sizeof(num_buf), "%d", res);
                error_reason = num_buf;
                break;
        }
    }

    error_msg(_("Can't delete '%s': %s"), dump_dir_name, error_reason);
    return 1;
#endif
}
