#!/usr/bin/env python3
# This file is part of Xpra.
# Copyright (c) 2017-2024 Antoine Martin <antoine@xpra.org>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

# @PydevCodeAnalysisIgnore

from ctypes import (
    WinDLL,  # @UnresolvedImport
    POINTER, Structure, Union, c_void_p, c_ubyte, addressof,
)
from ctypes.wintypes import DWORD, ULONG, HANDLE, BOOL, INT, BYTE, WORD, LPCSTR

from xpra.platform.win32.constants import WAIT_ABANDONED, WAIT_OBJECT_0, WAIT_TIMEOUT, WAIT_FAILED
from xpra.platform.win32.common import LPSECURITY_ATTRIBUTES

PDWORD = POINTER(DWORD)
PHANDLE = POINTER(HANDLE)
LPDWORD = POINTER(DWORD)
PVOID = c_void_p
LPCVOID = c_void_p
LPVOID = c_void_p

WAIT_STR = {
    WAIT_ABANDONED: "ABANDONED",
    WAIT_OBJECT_0: "OBJECT_0",
    WAIT_TIMEOUT: "TIMEOUT",
    WAIT_FAILED: "FAILED",
}

INFINITE = 65535
INVALID_HANDLE_VALUE = HANDLE(-1).value

INVALID_HANDLE = HANDLE(INVALID_HANDLE_VALUE)


class _inner_struct(Structure):
    _fields_ = [
        ('Offset', DWORD),
        ('OffsetHigh', DWORD),
    ]


class _inner_union(Union):
    _fields_ = [
        ('anon_struct', _inner_struct),
        ('Pointer', c_void_p),
    ]


class OVERLAPPED(Structure):
    _fields_ = [
        ('Internal', POINTER(ULONG)),
        ('InternalHigh', POINTER(ULONG)),
        ('union', _inner_union),
        ('hEvent', HANDLE),
    ]


LPOVERLAPPED = POINTER(OVERLAPPED)

kernel32 = WinDLL("kernel32", use_last_error=True)
WaitForSingleObject = kernel32.WaitForSingleObject
WaitForSingleObject.argtypes = [HANDLE, DWORD]
WaitForSingleObject.restype = DWORD
CreateEventA = kernel32.CreateEventA
CreateEventA.restype = HANDLE
SetEvent = kernel32.SetEvent
SetEvent.argtypes = [HANDLE]
SetEvent.restype = BOOL
ReadFile = kernel32.ReadFile
ReadFile.argtypes = [HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED]
ReadFile.restype = BOOL
WriteFile = kernel32.WriteFile
WriteFile.argtypes = [HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED]
WriteFile.restype = BOOL
CreateFileA = kernel32.CreateFileA
CreateFileA.argtypes = [LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE]
CreateFileA.restype = HANDLE
WaitNamedPipeA = kernel32.WaitNamedPipeA
SetNamedPipeHandleState = kernel32.SetNamedPipeHandleState
SetNamedPipeHandleState.argtypes = [HANDLE, LPDWORD, LPDWORD, LPDWORD]
SetNamedPipeHandleState.restype = INT
GetOverlappedResult = kernel32.GetOverlappedResult
GetOverlappedResult.argtypes = [HANDLE, LPOVERLAPPED, LPDWORD, BOOL]
GetOverlappedResult.restype = BOOL
CreateNamedPipeA = kernel32.CreateNamedPipeA
CreateNamedPipeA.argtypes = [LPCSTR, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, LPSECURITY_ATTRIBUTES]
CreateNamedPipeA.restype = HANDLE
ConnectNamedPipe = kernel32.ConnectNamedPipe
ConnectNamedPipe.argtypes = [HANDLE, LPOVERLAPPED]
ConnectNamedPipe.restype = BOOL
DisconnectNamedPipe = kernel32.DisconnectNamedPipe
DisconnectNamedPipe.argtypes = [HANDLE]
DisconnectNamedPipe.restype = BOOL
FlushFileBuffers = kernel32.FlushFileBuffers
FlushFileBuffers.argtypes = [HANDLE]
FlushFileBuffers.restype = BOOL
GetLastError = kernel32.GetLastError
GetLastError.argtypes = []
GetLastError.restype = DWORD

advapi32 = WinDLL("advapi32", use_last_error=True)
OpenProcessToken = advapi32.OpenProcessToken
OpenProcessToken.argtypes = [HANDLE, DWORD, PHANDLE]
OpenProcessToken.restype = BOOL
GetTokenInformation = advapi32.GetTokenInformation
# GetTokenInformation.argtypes = [HANDLE, TOKEN_INFORMATION_CLASS, LPVOID, DWORD, PDWORD]
GetTokenInformation.restype = BOOL


class SID_IDENTIFIER_AUTHORITY(Structure):
    # noinspection PyTypeChecker
    _fields_ = [
        ('Value', BYTE * 6),
    ]

    def __repr__(self):
        return "<SID_IDENTIFIER_AUTHORITY: %s" % (":".join(str(v) for v in self.Value))


PSID_IDENTIFIER_AUTHORITY = POINTER(SID_IDENTIFIER_AUTHORITY)


class SID(Structure):
    # noinspection PyTypeChecker
    _fields_ = [
        ('Revision', BYTE),
        ('SubAuthorityCount', BYTE),
        ('IdentifierAuthority', SID_IDENTIFIER_AUTHORITY),
        ('SubAuthority', DWORD * 16),
    ]

    def __repr__(self):
        subs = []
        for i in range(self.SubAuthorityCount):
            subs.append(self.SubAuthority[i])
        return "<SID: Revision:%i, SubAuthorityCount:%i, IdentifierAuthority:%s, SubAuthority:%s>" % (
            self.Revision, self.SubAuthorityCount, self.IdentifierAuthority, subs,
        )


PSID = POINTER(SID)


class ACL(Structure):
    _fields_ = [
        ('AclRevision', BYTE),
        ('Sbz1', BYTE),
        ('AclSize', WORD),
        ('AceCount', WORD),
        ('Sbz2', WORD),
    ]


PACL = POINTER(ACL)


class SECURITY_DESCRIPTOR(Structure):
    SECURITY_DESCRIPTOR_CONTROL = WORD
    _fields_ = [
        ('Revision', c_ubyte),
        ('Sbz1', c_ubyte),
        ('Control', SECURITY_DESCRIPTOR_CONTROL),
        ('Owner', PSID),
        ('Group', PSID),
        ('Sacl', PACL),
        ('Dacl', PACL),
    ]

    def __repr__(self):
        def c(v):
            return v.contents if v else 0

        return "<SECURITY_DESCRIPTOR at %#x: Revision:%i, Sbz1:%i, Control:%s, Owner:%s, Group:%s, Sacl=%s, Dacl=%s>" % (
            addressof(self), self.Revision, self.Sbz1, self.Control, c(self.Owner), c(self.Group), c(self.Sacl),
            c(self.Dacl))


PSECURITY_DESCRIPTOR = POINTER(SECURITY_DESCRIPTOR)

InitializeSecurityDescriptor = advapi32.InitializeSecurityDescriptor
SetSecurityDescriptorOwner = advapi32.SetSecurityDescriptorOwner
# don't set this argtypes, or you will get mysterious segfaults:
# SetSecurityDescriptorOwner.argtypes = [SECURITY_DESCRIPTOR, PSID, BOOL]
SetSecurityDescriptorOwner.restype = BOOL
SetSecurityDescriptorGroup = advapi32.SetSecurityDescriptorGroup
# don't set this argtypes, or you will get mysterious segfaults:
# SetSecurityDescriptorGroup.argtypes = [SECURITY_DESCRIPTOR, PSID, BOOL]
SetSecurityDescriptorGroup.restype = BOOL
SetSecurityDescriptorDacl = advapi32.SetSecurityDescriptorDacl
SetSecurityDescriptorDacl.argtypes = [PSECURITY_DESCRIPTOR, BOOL, PACL, BOOL]
SetSecurityDescriptorDacl.restype = BOOL
SetSecurityDescriptorSacl = advapi32.SetSecurityDescriptorSacl
SetSecurityDescriptorSacl.argtypes = [PSECURITY_DESCRIPTOR, BOOL, PACL, BOOL]
SetSecurityDescriptorSacl.restype = BOOL

AllocateAndInitializeSid = advapi32.AllocateAndInitializeSid
AllocateAndInitializeSid.argtypes = [PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD,
                                     DWORD, PSID]
AllocateAndInitializeSid.restype = BOOL
FreeSid = advapi32.FreeSid
FreeSid.argtypes = [PSID]
FreeSid.restype = PVOID
InitializeAcl = advapi32.InitializeAcl
InitializeAcl.argtypes = [PACL, DWORD, DWORD]
InitializeAcl.restype = BOOL
GetLengthSid = advapi32.GetLengthSid
GetLengthSid.argtypes = [PSID]
GetLengthSid.restype = DWORD
CreateWellKnownSid = advapi32.CreateWellKnownSid
CreateWellKnownSid.argtypes = [WORD, PSID, PSID, PDWORD]
CreateWellKnownSid.restype = BOOL


class ACE_HEADER(Structure):
    _fields_ = [
        ('AceType', BYTE),
        ('AceFlags', BYTE),
        ('SidStart', WORD),
    ]


class ACCESS_ALLOWED_ACE(Structure):
    _fields_ = [
        ('Header', ACE_HEADER),
        ('Mask', DWORD),
        ('SidStart', DWORD),
    ]


AddAccessAllowedAce = advapi32.AddAccessAllowedAce
# AddAccessAllowedAce.argtypes = [PACL, DWORD, DWORD, PSID]
AddAccessAllowedAce.restype = BOOL


class TOKEN_USER(Structure):
    _fields_ = [
        ('SID', PSID),
        ('ATTRIBUTES', DWORD),
    ]


class TOKEN_PRIMARY_GROUP(Structure):
    _fields_ = [
        ('PrimaryGroup', PSID),
    ]
