/*
 * Copyright (C) 2020 Uniontech Technology Co., Ltd.
 *
 * Author:     xinbo wang <wangxinbo@uniontech.com>
 *
 * Maintainer: xinbo wang <wangxinbo@uniontech.com>
 *
 * 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 3 of the License, or
 * 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, see <http://www.gnu.org/licenses/>.
 */

#include "x11_dnd.h"
#include "log.h"
#include "uaceExt.h"

#include<sys/types.h>
#include<sys/stat.h>
#include <fcntl.h>

#define FIFO_READ "/tmp/dtkdisplay_ace_read"
#define FIFO_WRITE "/tmp/dtkdisplay_ace_write"

typedef struct _CheckPermission{
    int type;
    int sourcePID;
    int targetPID;    
}SelectionPermissionCheck;

Bool isQuitThread = false;

int createNamedPipe(char *pipe){
    int res = 0;
    if (access(pipe, F_OK) == -1)
    {
        res = mkfifo(pipe, 0666);
        return res;
    }
    return 0;
}

void *threadPermissionCheck(void *arg)
{
    int ret;
    X11DndBackend * pX11 = (X11DndBackend *)arg;
    if (!pX11 )
        return NULL;

    if (!pX11->display)
        return NULL;

    pX11->fd_write_to_server = open(FIFO_READ, O_WRONLY);
    pX11->fd_read_from_server = open(FIFO_WRITE, O_RDONLY);

    SelectionPermissionCheck * readbuf = (SelectionPermissionCheck *)malloc(sizeof(SelectionPermissionCheck));
    char quitChar;

    while (pX11->lock) {
        if (isQuitThread)
            break;

        ret = read(pX11->fd_read_from_server, readbuf, sizeof(SelectionPermissionCheck));
        if (ret < 0)
            continue;

        log_info("Get server uace request, type: %d, source: %d, target: %d\n", readbuf->type, readbuf->sourcePID, readbuf->targetPID);

        int check = pDndSec->DoSecurityVerify(NULL, 0, readbuf->sourcePID, readbuf->targetPID);

        write(pX11->fd_write_to_server, &check, sizeof(check));

        log_info("Permission check result: %d\n", check);
    }

    free(readbuf);
    close(pX11->fd_write_to_client);
    close(pX11->fd_read_from_server);

    return NULL;
}

int initX11Dnd()
{
    if (!pDndSec) {
        log_error("need init X11 dtkdisplay content");
        return -1;
    }

    X11DndBackendPtr pX11 = (X11DndBackendPtr)malloc(sizeof(X11DndBackend));

    if (!pX11) {
        log_error("malloc dnd security wayland backend failed \n");
        return -1;
    }
    memset(pX11, 0, sizeof(X11DndBackend));

    pDndSec->backend = pX11;
    pX11->lock = true;

    // create named pipe 
    if(createNamedPipe(FIFO_READ) == -1) {
        log_error("create pipe error \n");
        return -2;
    }
    if(createNamedPipe(FIFO_WRITE) == -1) {
        log_error("create pipe error \n");
        return -3;
    }

    pX11->display = XOpenDisplay(NULL);
    if (pX11->display == NULL) {
        log_error("failed to create X11 display");
        return -1;
    }

    pthread_mutex_init(&pX11->cond_lock, NULL);
    pthread_create(&pX11->dispatch, NULL, threadPermissionCheck, pX11);

    XUaceExtRegisterSelectionSpy(pX11->display, pX11->fd_read_client, pX11->fd_write_to_client);

    return 0;
}

void destoryX11Dnd()
{
    if (!pDndSec->backend) {
        log_error("X11 backend has been destroyed \n");
        return;
    }

    X11DndBackendPtr pX11 = ((X11DndBackend*)pDndSec->backend);

    pthread_mutex_lock(&pX11->cond_lock);
    pX11->lock = false;
    pthread_mutex_unlock(&pX11->cond_lock);

    //cancel pthread
    isQuitThread = true;
    int write_to_quit = open(FIFO_WRITE, O_WRONLY);
    SelectionPermissionCheck quit_message;
    quit_message.type = quit_message.sourcePID = quit_message.targetPID = 0;
    write(write_to_quit, &quit_message, sizeof(SelectionPermissionCheck));
    close(write_to_quit);
    pthread_join(pX11->dispatch, 0);

    // display disconnect
    XCloseDisplay(pX11->display);

    free(pDndSec->backend);
}

int xGetSecuritySession(SessionType types)
{
    log_warn("XSecuritySession is not available, session type: %d. \n", types);
    return -1;
}

int xDestroySecuritySession(int session)
{
    log_warn("XdestroySecuritySession is not available, session: %d. \n", session);
    return -1;
}

int xDoSecurityVerify(int client, int target)
{
    log_warn("XdoSecurityVerify is not available, client: %d target: %d. \n", client, target);
    return -1;
}

void xReportSecurityVerified(int session, Permission result)
{
    log_warn("XReportSecurityVerified is not available, session: %d, result: %d. \n", session, result);
}

struct dtk_array* xGetSecurityClients()
{
    log_warn("xGetSecurityClients is not available. \n");
    return NULL;
}
