/**
 * Copyright (C), 2024, OpenKylin, Inc
 * @file  : common/KMXauth.h
 * @date  : 2024-07-19
 * @brief : 封装xauth相关操作
 */

#include "KMXauth.h"

#include "KMLogger.h"

#ifdef ENABLE_XAUTH

bool KMXauth::authEq(const char *str, const char *auth, size_t authLen)
{
    return authLen == strlen(str) && memcmp(str, auth, authLen) == 0;
}

bool KMXauth::xauthEntryShouldPropagate(const Xauth *xa, int family, const char *remoteHostname, const char *localHostname, const char *number)
{
    /* ensure entry isn't for a different type of access */
    if (family != FamilyWild && xa->family != family && xa->family != FamilyWild)
    {
        return false;
    }

    /* ensure entry isn't for remote access, except that if remoteHostname
     * is specified, then remote access to that hostname is OK */
    if (xa->family != FamilyWild && xa->family != FamilyLocal && (remoteHostname == nullptr || !authEq(remoteHostname, xa->address, xa->address_length)))
    {
        return false;
    }

    /* ensure entry is for this machine */
    if (xa->family == FamilyLocal && !authEq(localHostname, xa->address, xa->address_length))
    {
        /* OpenSUSE inherits the hostname value from DHCP without updating
         * its X11 authentication cookie. The old hostname value can still
         * be found in the environment variable XAUTHLOCALHOSTNAME.
         * For reference:
         * https://bugzilla.opensuse.org/show_bug.cgi?id=262309
         * For this reason if we have a cookie whose address is equal to the
         * variable XAUTHLOCALHOSTNAME, we still need to propagate it, but
         * we also need to change its address to `unames.nodename`.
         */
        const char *xauthLocalHostname = ::getenv("XAUTHLOCALHOSTNAME");
        if (xauthLocalHostname == nullptr)
        {
            return false;
        }

        if (!authEq((char *)xauthLocalHostname, xa->address, xa->address_length))
        {
            return false;
        }
    }

    /* ensure entry is for this session */
    if (xa->number != nullptr && !authEq(number, xa->number, xa->number_length))
    {
        return false;
    }

    return true;
}

void KMXauth::writeXauth(int family, const char *remoteHostArg, const char *number, FILE *output)
{
    Xauth *xa, localXa;
    char *filename;
    FILE *f;
    struct utsname unames;

    if (::uname(&unames))
    {
        KMWarn("uname failed");
        return;
    }

    filename = XauFileName();
    f = ::fopen(filename, "rb");
    if (f == nullptr)
    {
        return;
    }

    while (true)
    {
        xa = XauReadAuth(f);
        if (xa == nullptr)
        {
            break;
        }

        if (xauthEntryShouldPropagate(xa, family, remoteHostArg, unames.nodename, number))
        {
            localXa = *xa;

            if (localXa.family == FamilyLocal && !authEq(unames.nodename, localXa.address, localXa.address_length))
            {
                /* If we decided to propagate this cookie, but its address
                 * doesn't match `unames.nodename`, we need to change it or
                 * inside the container it will not work.
                 */
                localXa.address = unames.nodename;
                localXa.address_length = strlen(localXa.address);
            }

            if (!XauWriteAuth(output, &localXa))
            {
                KMWarn("xauth write error");
            }
        }

        XauDisposeAuth(xa);
    }

    ::fclose(f);
}

#endif // !ENABLE_XAUTH
