/*
 * Copyright (C) 2020 Uniontech Technology Co., Ltd.
 *
 * Author:     gl di <diguoliang@uniontech.com>
 *
 * Maintainer: gl di <diguoliang@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 "dbus.h"
#include <dlfcn.h>
#include <unistd.h>
#include <QDebug>
#include <QtCore/QFile>
#include <QtDBus/QDBusMessage>
#include <QtDBus/QDBusConnectionInterface>
#include <QtDBus/QDBusConnection>
#include <QDir>
#include <QDBusInterface>
#include <QDBusReply>
#include <QTimer>

#include <xcb/xcb.h>

#include "utils/string.h"
#include "prohibitedwindowdecision.h"
#include "config/screenshotconfig.h"

#define DBUS_DEEPIN_WM_SERVICE   "com.deepin.wm"
#define DBUS_DEEPIN_WM_OBJ       "/com/deepin/wm"
#define DBUS_DEEPIN_WM_INTF      "com.deepin.wm"

#define DBUS_KWIN_SERVICE   "org.kde.KWin"
#define DBUS_KWIN_OBJ       "/KWin"
#define DBUS_KWIN_INTF      "org.kde.KWin"

#define DBUS_USEC_SERVICE           "org.deepin.usec1"
#define DBUS_USEC_OBJ               "/org/deepin/usec1/AccessControl"
#define DBUS_USEC_INTF              "org.deepin.usec1.AccessControl"

typedef Bool (*SCREEN_FUNC)(Window);
typedef int (*WINDOWPID_FUNC)(Window);
typedef Bool (*SCREENSHOT_TOOLS_FUNC)(const char *);
typedef void (*FREE_SCREENSHOT_TOOLS_FUNC)();
typedef void (*SCREENSHOT_DISPLAY_FUNC)();
typedef void (*SET_PROHIBITED_FUNC)(const char*, bool);
typedef void (*DTKWMJACK_DISPLAY_FUNC)();

Display *m_pDisplay = nullptr;
SCREEN_FUNC m_screenFunc = NULL;
WINDOWPID_FUNC m_windowFunc = NULL;
SCREENSHOT_TOOLS_FUNC m_screenshotToolsFunc = NULL;
FREE_SCREENSHOT_TOOLS_FUNC m_freeScreenshotToolsFunc = NULL;
SCREENSHOT_DISPLAY_FUNC m_screenshotDisplayFunc = NULL;
SET_PROHIBITED_FUNC m_setProhibitedFunc = NULL;
DTKWMJACK_DISPLAY_FUNC m_dtkwmjackDisplayFunc = NULL;
xcb_connection_t *m_pConnection = nullptr;

void *m_screenshotHandle = NULL;
void *m_dtkwmjackDisplayHandle = NULL;

PolkitProhibitedDBus::PolkitProhibitedDBus(QObject *parent)
    : QObject(parent)
{
    m_pDisplay = XOpenDisplay(NULL);
    if (!m_pDisplay) {
        return;
    }

    m_pConnection = xcb_connect(NULL, NULL);
    if (!m_pConnection) {
        return;
    }

    m_pScreenShotConfig = ScreenShotConfig::instance();
    QTimer* timer = new QTimer(this);
    timer->setInterval(1000);
    connect(timer, &QTimer::timeout, this, &PolkitProhibitedDBus::timerCheckProhibitedWindowPid);
    connect(timer, &QTimer::timeout, this, &PolkitProhibitedDBus::checkNeedProhibitScreenshot);

    QDBusConnection::sessionBus().connect(QString(),QString(),"org.kde.KWin","ProhibitScreenShotChanged",this,SLOT(dealWithSystemdbus(int,bool)));
    QDBusConnection::sessionBus().connect(QString(),QString(),"org.kde.KWin","ClientMinimizeChanged",this,SLOT(toggleActiveMinimize(int,bool)));
    QDBusConnection::sessionBus().connect(QString(),QString(),"org.kde.KWin","DestroyWindowChanged",this,SLOT(destroyWindow(int)));
    QDBusConnection::sessionBus().connect(QString(),QString(),"org.kde.KWin","MultitaskStateChanged",this,SLOT(protectedRootWindow(bool)));
    QDBusConnection::sessionBus().connect(QString(),QString(),"org.kde.KWin","SplitScreenStateChanged",this,SLOT(protectedRootWindow(bool)));
    QDBusConnection::sessionBus().connect(QString(),QString(),"org.kde.KWin","PresentWindowStateChanged",this,SLOT(protectedRootWindow(bool)));
    QDBusConnection::sessionBus().connect(QString(),QString(),"org.kde.KWin","ClientToDesktopStateChanged",this,SLOT(clientToDesktop(int, int)));
    QDBusConnection::sessionBus().connect(QString(),QString(),"com.deepin.wm","WorkspaceSwitched",this,SLOT(workspaceSwitched(int, int)));
    QDBusConnection::sessionBus().connect(QString(),QString(),"org.kde.KWin","ShowingDesktopStateChanged",this,SLOT(showingDesktop(bool)));
    QDBusConnection::sessionBus().connect(QString(),QString(),"org.kde.KWin","tabboxClosed",this,SLOT(tabboxClosed()));
    QDBusConnection::sessionBus().connect(QString(),QString(),"org.kde.KWin","clientMoveResize",this,SLOT(clientMoveResize(bool,int)));

    timer->start();

    isWayland = (qgetenv("XDG_SESSION_TYPE") == "wayland") ? true : false;

    m_screenshotHandle = dlopen("libdtkscreenace.so", RTLD_LAZY | RTLD_LOCAL);
    m_dtkwmjackDisplayHandle = dlopen("libdtkwmjack.so", RTLD_LAZY | RTLD_LOCAL);
    dlerror();
    if (!m_screenshotHandle || !m_dtkwmjackDisplayHandle) {
        return;
    }

    *(void **) (&m_screenshotDisplayFunc) = dlsym(m_screenshotHandle, "InitDtkDisplay");
    *(void **) (&m_dtkwmjackDisplayFunc) = dlsym(m_dtkwmjackDisplayHandle, "InitDtkWmDisplay");

    if (!m_screenshotDisplayFunc || !m_dtkwmjackDisplayFunc) {
        return;
    }

    (*m_screenshotDisplayFunc)();
    (*m_dtkwmjackDisplayFunc)();

    QStringList screenshotToolBlackList = m_pScreenShotConfig->screenshotToolBlackList();
    if (screenshotToolBlackList.count() > 0) {
        for (int i = 0; i < screenshotToolBlackList.count(); i++) {
            QString screenshotTool = screenshotToolBlackList.at(i);
            std::string str = screenshotTool.toStdString();
            const char *name = str.c_str();
            if (name != NULL) {
                *(void **) (&m_screenshotToolsFunc) = dlsym(m_screenshotHandle, "SetScreenShotTools");
                if (!m_screenshotToolsFunc) {
                    return;
                }
                (*m_screenshotToolsFunc)(name);
            }
        }
    }
}

PolkitProhibitedDBus::~PolkitProhibitedDBus()
{
    *(void **) (&m_freeScreenshotToolsFunc) = dlsym(m_screenshotHandle, "FreeScreenShotTools");
    if (m_freeScreenshotToolsFunc) {
        (*m_freeScreenshotToolsFunc)();
    }

    if (m_pDisplay) {
        XCloseDisplay(m_pDisplay);
    }

    if (m_pConnection) {
        xcb_disconnect(m_pConnection);
    }

    *(void **) (&m_screenshotDisplayFunc) = dlsym(m_screenshotHandle, "DestoryDtkDisplay");
    if (m_screenshotDisplayFunc) {
        (*m_screenshotDisplayFunc)();
    }

    *(void **) (&m_dtkwmjackDisplayFunc) = dlsym(m_dtkwmjackDisplayHandle, "DestoryDtkWmDisplay");
    if (m_dtkwmjackDisplayFunc) {
        (*m_dtkwmjackDisplayFunc)();
    }

    dlclose(m_screenshotHandle);
    dlclose(m_dtkwmjackDisplayHandle);
}

int PolkitProhibitedDBus::doProtectedWindow(bool bProhibited, int window)
{
    // window = -1: means current environment is ICBC-wayland
    // In 1060 wayland, window is the real of client's window id.
    if (window == -1 && isWayland) {
        QString whiteLists = m_pScreenShotConfig->accessOnClientClassList().join(",");
        std::string str = whiteLists.toStdString();
        const char *wmClass = str.c_str();
        *(void **) (&m_setProhibitedFunc) = dlsym(m_screenshotHandle, "SetProhibited");
        if (m_setProhibitedFunc) {
            (*m_setProhibitedFunc)(wmClass, bProhibited);
            return 0;
        }
        return -4;
    }

    if (bProhibited) {
        *(void **) (&m_screenFunc) = dlsym(m_screenshotHandle, "SetProtectedWindow");
        if (m_screenFunc) {
            (*m_screenFunc)(window);
        }
    } else {
        *(void **) (&m_screenFunc) = dlsym(m_screenshotHandle, "RemoveProtectedWindow");
        if (m_screenFunc) {
            (*m_screenFunc)(window);
        }
    }
    return 0;
}

int PolkitProhibitedDBus::doGetWindowPid(int window)
{
    if (!m_pDisplay) {
        return 0;
    }

    int nWindowPID = 0;
    *(void **) (&m_windowFunc) = dlsym(m_dtkwmjackDisplayHandle, "GetWindowPID");
    if (m_windowFunc) {
        nWindowPID = (*m_windowFunc)(window);
    }
    return nWindowPID;
}

bool PolkitProhibitedDBus::queryProhibitWindowState(int window)
{
    for (int i = 0; i < windowInfo.keys().count(); i++) {
        int nWindowId = windowInfo.keys().at(i);
        if (nWindowId == window) {
            return true;
        }
    }
    return false;
}

int PolkitProhibitedDBus::setProhibited(bool bProhibited)
{
    //　判断该接口调用是否为外部dbus调用,如果是,需要鉴权,如果不是,直接做开关设置(程序崩溃监测接口内部调用)
    if (calledFromDBus()) {
        QDBusReply<uint> reply = connection().interface()->servicePid(message().service());
        uint pid = reply.value();

        if (!checkSenderWhiteList(pid)) {
            qDebug() << "Failed to check process white list";
            return -2;
        } else { /* 当关闭防截屏时:只要存在其他wps白名单应用打开了防截屏,就不允许关闭防截屏 */
            auto it = m_mapAccessInterfaceProhibitedPidStatus.find(pid);
            if (it == m_mapAccessInterfaceProhibitedPidStatus.end()) {
                m_mapAccessInterfaceProhibitedPidStatus.insert(pid, bProhibited);
            } else {
                it.value() = bProhibited;
            }
            if (!bProhibited && checkIsExistsOtherOpenProhibitedSwitchPid(pid)) {
                qDebug() << "Failed to close switch interface, there are other processes opening the interface switch";
                return -3;
            }
        }
    }

    m_pScreenShotConfig->setProhibited(bProhibited);
    emit ProhibitedChanged(bProhibited);

    if (isWayland) {
        return doProtectedWindow(bProhibited);
    } else {
        return doProtectedWindow(bProhibited, XDefaultRootWindow(m_pDisplay));
    }
}

bool PolkitProhibitedDBus::prohibited()
{
    return m_pScreenShotConfig->prohibited();
}

void PolkitProhibitedDBus::timerCheckProhibitedWindowPid()
{
    // 检查打开开关接口的wps白名单进程的状态,检测文件系统目录/proc/$pid/是否存在,如果不存在,删除禁止截图窗口pid,关闭防截图
    auto refreshProhibitedWindowPidStatus = [](QMap<uint, bool>& prohibitedWindowPidStatusMap) {
        QDir dir;
        for (auto it = prohibitedWindowPidStatusMap.begin(); it != prohibitedWindowPidStatusMap.end();) {
            uint pid = it.key();
            QString pidDir = "/proc/" + QString::number(pid);
            if (!dir.exists(pidDir)) {
                qDebug() << "Delete prohibited window pid: " << pid;
                prohibitedWindowPidStatusMap.erase(it++);
                continue;
            }
            ++it;
        }
    };

    refreshProhibitedWindowPidStatus(this->m_mapAccessInterfaceProhibitedPidStatus);

    // 只存在程序崩溃退出后自动关闭防截图功能,所以需要判断接口开关值
    if (!prohibited()) {
        return;
    }

    if (isNeed) {
        return;
    }

    // 所有调用防截图程序都正常或崩溃退出
    if (m_mapAccessInterfaceProhibitedPidStatus.empty()) {
        setProhibited(false);
    } else {
        if (!checkIsExistsOtherOpenProhibitedSwitchPid(0)) {
            setProhibited(false);
        }
    }
}

bool PolkitProhibitedDBus::checkSenderWhiteList(uint pid)
{
    char buf[1024];
    ssize_t len;
    char proc_exe[64];
    snprintf(proc_exe, 63, "/proc/%d/exe", pid);
    len = readlink(proc_exe, buf, sizeof(buf)-1);
    if (len < 0 || len >= sizeof(buf)) {
        return false;
    }

    buf[len] = '\0';
    std::string strExePath = buf;
    QStringList strAccessOnPathList = m_pScreenShotConfig->accessOnPathList();
    for (int i = 0; i < strAccessOnPathList.count(); i++) {
        QString strAccessOnPath = strAccessOnPathList.at(i);
        if (strAccessOnPath.contains(QString::fromStdString(strExePath))) {
            return true;
        }
    }
    return false;
}

bool PolkitProhibitedDBus::checkIsExistsOtherOpenProhibitedSwitchPid(uint pid)
{
    bool isExistsOtherOpenProhibitedSwitchPid = false;
    for (auto it = m_mapAccessInterfaceProhibitedPidStatus.begin(); it != m_mapAccessInterfaceProhibitedPidStatus.end(); it++) {
        if (it.key() == pid) {
            continue;
        }
        if (it.value()) {
            isExistsOtherOpenProhibitedSwitchPid = true;
            break;
        }
    }

    return isExistsOtherOpenProhibitedSwitchPid;
}

void PolkitProhibitedDBus::checkNeedProhibitScreenshot()
{
    if (!m_pDisplay) {
        return;
    }

    ProhibitedWindowDecision windowDecision;
    if (windowDecision.needProhibitScreenshot(XDefaultRootWindow(m_pDisplay))) {
        m_mapAccessInterfaceProhibitedPidStatus.insert(100000, true);
        if (!prohibited()) {
            setProhibited(true);
            isNeed = true;
        }
    } else {
        m_mapAccessInterfaceProhibitedPidStatus.remove(100000);
        isNeed = false;
        if (!checkIsExistsOtherOpenProhibitedSwitchPid(0)) {
            if (prohibited()) {
                setProhibited(false);
            }
        }
    }
}

void PolkitProhibitedDBus::dealWithSystemdbus(int pid, bool bProhibited)
{
    if (!m_pDisplay) {
        return;
    }

    if (!checkSenderWhiteList(pid)) {
        qDebug() << "Failed to check process white list";
        return;
    } else { /* 当关闭防截屏时：只要存在其他wps白名单应用打开了防截屏,就不允许关闭防截屏 */
        auto it = m_mapAccessInterfaceProhibitedPidStatus.find(pid);
        if (it == m_mapAccessInterfaceProhibitedPidStatus.end()) {
            m_mapAccessInterfaceProhibitedPidStatus.insert(pid, bProhibited);
        } else {
            it.value() = bProhibited;
        }
        if (!bProhibited && checkIsExistsOtherOpenProhibitedSwitchPid(pid)) {
            qDebug() << "Failed to close switch interface, there are other processes opening the interface switch";
            return;
        }
    }

    m_pScreenShotConfig->setProhibited(bProhibited);
    emit ProhibitedChanged(bProhibited);

    if (isWayland) {
        doProtectedWindow(bProhibited);
        return;
    }

    doProtectedWindow(bProhibited, XDefaultRootWindow(m_pDisplay));
    XFlush(m_pDisplay);
}

void PolkitProhibitedDBus::toggleActiveMinimize(int window, bool isMinimize)
{
    for (int i = 0; i < windowInfo.keys().count(); i++) {
        int nWindowId = windowInfo.keys().at(i);
        if (nWindowId == window) {
            isCalledByUs = true;
            if (isMinimize) {
                if (!isWayland) {
                    // delay unregister prohibit
                    QTimer::singleShot(50, this, [this, window]{
                        doProtectedWindow(false, window);
                    });
                }
            } else {
                if (!isWayland) {
                    doProtectedWindow(true, window);
                }
            }
        }
    }
}

void PolkitProhibitedDBus::destroyWindow(int window)
{
    for (int i = 0; i < windowInfo.keys().count(); i++) {
        int nWindowId = windowInfo.keys().at(i);
        if (nWindowId == window) {
            doProtectedWindow(false, window);
            windowInfo.remove(window);
            m_nProtectedWindowCount --;
            break;
        }
    }
}

int PolkitProhibitedDBus::registerProhibitedWindow(int window)
{
    if (checkWindowAttribute(window, "WM_CLIENT_LEADER") || !checkWindowText(window)) {
        sendErrorReply(QDBusError::Failed, QString("window: %1, maybe a LEADER_WINDOW or USER_TIME_WINDOW, please use the real ID of the window!").arg(window));
        return -1;
    }

    if (windowInfo.contains(window))
        return 0; // mean this window has be registered, no longer requires processing.

    if (calledFromDBus()) {
        QDBusReply<uint> reply = connection().interface()->servicePid(message().service());
        uint pid = reply.value();
        int nWindowPID = doGetWindowPid(window);

        if (pid != nWindowPID) {
            sendErrorReply(QDBusError::Failed, QString("window: %1 pid is %2, is no belong to parent pid %3 !").arg(window).arg(nWindowPID).arg(pid));
            return -1; // No permission to set other windows
        }

        QDBusInterface interfaceRequire(DBUS_USEC_SERVICE, DBUS_USEC_OBJ, DBUS_USEC_INTF, QDBusConnection::systemBus());
        QDBusPendingReply<QStringList> pendingReply = interfaceRequire.call("CanAuthorize", (int)pid, "prohibitscreenshot");
        if (pendingReply.isValid()) {
            QStringList list = pendingReply.value();
            if (!list.contains("allow"))
                return -2;
        }

        m_nProtectedWindowCount ++;
        windowInfo[window] = true;
    }

    int ret = doProtectedWindow(true, window);
    if (!ret) {
        doUpdateX11Atom(true, window);
    }
    return ret;
}

int PolkitProhibitedDBus::unRegisterProhibitedWindow(int window)
{
    if (!windowInfo.contains(window)) {
        sendErrorReply(QDBusError::Failed, QString("window: %1 was not a prohibit window!").arg(window));
        return -1;
    }

    if (calledFromDBus()) {
        QDBusReply<uint> reply = connection().interface()->servicePid(message().service());
        uint pid = reply.value();
        int nWindowPID = doGetWindowPid(window);

        if (pid != nWindowPID) {
            sendErrorReply(QDBusError::Failed, QString("window: %1 pid is %2, is no belong to parent pid %3 !").arg(window).arg(nWindowPID).arg(pid));
            return -1; // No permission to set other windows
        }
        m_nProtectedWindowCount --;
        windowInfo.remove(window);
    }

    int ret = doProtectedWindow(false, window);
    if (!ret) {
        doUpdateX11Atom(false, window);
    }
    return ret;
}

void PolkitProhibitedDBus::doUpdateX11Atom(bool isActive, int window)
{
    if (isWayland) {
        return;
    }
  
    Atom atomType = XInternAtom(m_pDisplay, "_DEEPIN_PROHIBIT_SCREENSHOT", False);
    if (isActive) {
        const int isProtected = 1;
        XChangeProperty(m_pDisplay, window, atomType, XA_CARDINAL, 32, PropModeReplace,
                        (unsigned char*)&isProtected, 1);
    } else {
        XDeleteProperty(m_pDisplay, window, atomType);
    }
    XFlush(m_pDisplay);
}

void PolkitProhibitedDBus::protectedRootWindow(bool isActive)
{
    if (!m_pDisplay) {
        return;
    }

    if (!isWayland) {
        if ((isActive && m_nProtectedWindowCount > 0) || !isActive) {
            doProtectedWindow(isActive, XDefaultRootWindow(m_pDisplay));
        }
    }
}

void PolkitProhibitedDBus::clientToDesktop(int window, int desk)
{
    for (int i = 0; i < windowInfo.keys().count(); i++) {
        int nWindowId = windowInfo.keys().at(i);
        if (nWindowId == window) {
            QDBusInterface remoteApp(DBUS_KWIN_SERVICE, DBUS_KWIN_OBJ, DBUS_KWIN_INTF);
            QDBusReply<int> reply = remoteApp.asyncCall("currentDesktop");

            if (reply.value() != desk) {
                doProtectedWindow(false, window);
            } else {
                doProtectedWindow(true, window);
            }
        }
    }
}

void PolkitProhibitedDBus::workspaceSwitched(int from, int to)
{
    for (int i = 0; i < windowInfo.keys().count(); i++) {
        int nWindowId = windowInfo.keys().at(i);

        QDBusInterface remoteApp(DBUS_KWIN_SERVICE, DBUS_KWIN_OBJ, DBUS_KWIN_INTF);
        QDBusReply<bool> reply = remoteApp.asyncCall("isOnCurrentDesktop", nWindowId);

        bool isOnCurrentDesktop = reply.value();
        doProtectedWindow(isOnCurrentDesktop, nWindowId);
    }
}

void PolkitProhibitedDBus::showingDesktop(bool isShowingDesktop)
{
    for (int i = 0; i < windowInfo.keys().count(); i++) {
        int nWindowId = windowInfo.keys().at(i);
        doProtectedWindow(!isShowingDesktop, nWindowId);
    }
}

void PolkitProhibitedDBus::sessionRemoved()
{
    exit(0);
}

QString PolkitProhibitedDBus::getOffClient()
{
    return m_pScreenShotConfig->accessOffClientClassList().join(",");
}

void PolkitProhibitedDBus::tabboxClosed()
{
    if (!isWayland) {
        ProhibitedWindowDecision windowDecision;
        for (int i = 0; i < windowInfo.keys().count(); i++) {
            int nWindowId = windowInfo.keys().at(i);
            if (!windowDecision.isNormalState(nWindowId)) {
                doProtectedWindow(false, nWindowId);
            }
        }
    }
}

void PolkitProhibitedDBus::clientMoveResize(bool moving, int window)
{
    for (int i = 0; i < windowInfo.keys().count(); i++) {
        int nWindowId = windowInfo.keys().at(i);
        if (nWindowId == window) {
            doProtectedWindow(moving, XDefaultRootWindow(m_pDisplay));
            return;
        }
    }
}

bool PolkitProhibitedDBus::checkWindowAttribute(int targetWindow, QString attribute)
{
    if (isWayland) {
        return false;
    }

    if (!m_pDisplay || !m_pConnection) {
        return false;
    }

    bool hasAttribute = false;
    std::string str = attribute.toStdString();
    const char *pAttribute = str.c_str();

    xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(m_pConnection)).data;
    xcb_window_t rootWindow = screen->root;

    xcb_query_tree_cookie_t treeCookie = xcb_query_tree(m_pConnection, rootWindow);
    xcb_query_tree_reply_t *treeReply = xcb_query_tree_reply(m_pConnection, treeCookie, NULL);
    int windowCount = xcb_query_tree_children_length(treeReply);
    xcb_window_t *windowList = xcb_query_tree_children(treeReply);

    xcb_atom_t leaderAtom;
    xcb_intern_atom_cookie_t leaderAtomCookie = xcb_intern_atom(m_pConnection, 0, strlen(pAttribute), pAttribute);
    xcb_intern_atom_reply_t *leaderAtomReply = xcb_intern_atom_reply(m_pConnection, leaderAtomCookie, NULL);
    if (leaderAtomReply) {
        leaderAtom = leaderAtomReply->atom;
    }

    for (int i = 0; i < windowCount; i++) {
        xcb_window_t window = windowList[i];

        xcb_get_property_cookie_t leaderPropCookie = xcb_get_property(m_pConnection, false, window, leaderAtom, XCB_ATOM_WINDOW, 0, 1);
        xcb_get_property_reply_t *leaderPropReply = xcb_get_property_reply(m_pConnection, leaderPropCookie, NULL);

        if (leaderPropReply && leaderPropReply->type == XCB_ATOM_WINDOW && leaderPropReply->format == 32 && leaderPropReply->length > 0) {
            xcb_window_t leaderWindow = *(xcb_window_t*) xcb_get_property_value(leaderPropReply);
            if (targetWindow == leaderWindow) {
                hasAttribute = true;
                break;
            }
        }

        free(leaderPropReply);
    }

    free(treeReply);
    free(leaderAtomReply);

    return hasAttribute;
}

bool PolkitProhibitedDBus::checkWindowText(int nWindow)
{
    if (isWayland) {
        return true;
    }

    if (!m_pDisplay || !m_pConnection) {
        return false;
    }

    bool hasWindowText = false;

    xcb_get_geometry_cookie_t cookie = xcb_get_geometry(m_pConnection, nWindow);
    xcb_get_geometry_reply_t *reply = xcb_get_geometry_reply(m_pConnection, cookie, NULL);

    if (reply == NULL) {
        return false;
    }

    Atom nameAtom = XInternAtom(m_pDisplay, "_NET_WM_NAME", false);
    Atom utf8Atom = XInternAtom(m_pDisplay, "UTF8_STRING", false);
    Atom type;
    int format;
    unsigned long nitems, after;
    unsigned char *data = 0;

    if (Success == XGetWindowProperty(m_pDisplay, nWindow, nameAtom, 0, 65536, false, utf8Atom, &type, &format, &nitems, &after, &data)) {
        if (data == 0) {
            hasWindowText = false;
        } else {
            hasWindowText = true;
        }
    }

    XFree(data);
    free(reply);
    return hasWindowText;
}
