namespace axl::sys::win

namespace win {

// typedefs

typedef sl::Handle<HANDLE, CloseHandle> Handle;
typedef sl::Handle<HKEY, CloseRegKey, sl::MinusOne<HKEY>> RegKeyHandle;

typedef NTSTATUS NTAPI NtQueryDirectoryFileFunc(
    IN HANDLE FileHandle,
    IN HANDLE Event,
    IN PVOID ApcRoutine,
    IN PVOID ApcContext,
    OUT PIO_STATUS_BLOCK IoStatusBlock,
    OUT PVOID FileInformation,
    IN ULONG Length,
    IN INT FileInformationClass,
    IN BOOLEAN ReturnSingleEntry,
    IN PVOID FileName,
    IN BOOLEAN RestartScan
    );

typedef NTSTATUS NTAPI NtOpenDirectoryObjectFunc(
    OUT PHANDLE DirectoryHandle,
    IN ACCESS_MASK DesiredAccess,
    IN POBJECT_ATTRIBUTES ObjectAttributes
    );

typedef NTSTATUS NTAPI NtQueryDirectoryObjectFunc(
    IN HANDLE DirectoryHandle,
    OUT PVOID Buffer,
    IN ULONG Length,
    IN BOOLEAN ReturnSingleEntry,
    IN BOOLEAN RestartScan,
    IN PULONG Context,
    OUT PULONG ReturnLength
    );

typedef NTSTATUS NTAPI NtOpenSymbolicLinkObjectFunc(
    OUT PHANDLE LinkHandle,
    IN ACCESS_MASK DesiredAccess,
    IN POBJECT_ATTRIBUTES ObjectAttributes
    );

typedef NTSTATUS NTAPI NtQuerySymbolicLinkObjectFunc(
    IN HANDLE LinkHandle,
    OUT PUNICODE_STRING LinkTarget,
    OUT PULONG ReturnedLength
    );

typedef NTSTATUS NTAPI NtQueryInformationFileFunc(
    HANDLE FileHandle,
    PIO_STATUS_BLOCK IoStatusBlock,
    PVOID FileInformation,
    ULONG Length,
    FILE_INFORMATION_CLASS FileInformationClass
    );

typedef NTSTATUS NTAPI NtQueryInformationProcessFunc(
    HANDLE ProcessHandle,
    PROCESSINFOCLASS ProcessInformationClass,
    PVOID ProcessInformation,
    ULONG ProcessInformationLength,
    PULONG ReturnLength
    );

typedef NTSTATUS NTAPI NtQueryObjectFunc(
    IN HANDLE FileHandle,
    IN OBJECT_INFORMATION_CLASS InformationClass,
    OUT PVOID Buffer,
    IN ULONG BufferSize,
    OUT PULONG ReturnedLength
    );

typedef sl::Handle<SC_HANDLE, CloseServiceHandle> ServiceHandle;

// enums

enum
{
    STATUS_NO_MORE_FILES          = 0x80000006,
    STATUS_NO_MORE_ENTRIES        = 0x8000001a,
    STATUS_INFO_LENGTH_MISMATCH   = 0xc0000004,
    STATUS_BUFFER_TOO_SMALL       = 0xc0000023,
    DIRECTORY_QUERY               = 0x0001,
    DIRECTORY_TRAVERSE            = 0x0002,
    DIRECTORY_CREATE_OBJECT       = 0x0004,
    DIRECTORY_CREATE_SUBDIRECTORY = 0x0008,
};

enum FILE_INFORMATION_CLASS;
enum OBJECT_INFORMATION_CLASS;
enum PROCESSINFOCLASS;
enum WaitResult;

// structs

struct FILE_BASIC_INFORMATION;
struct FILE_DIRECTORY_INFORMATION;
struct OBJECT_DIRECTORY_INFORMATION;
struct OBJECT_NAME_INFORMATION;

// classes

class AccessToken;
class Bstr;
class CertStore;
class Certificate;
class CloseCertStore;
class CloseCryptMsg;
class CloseHandle;
class CloseRegKey;
class CloseServiceHandle;
class CriticalSection;
class CryptMsg;
class DestroyDeviceInfoSet;
class DeviceInfo;
class DeviceInfoSet;
class Event;
class FreeBstr;
class FreeCertContext;
class FreeLibrary;
class JobObject;
class Library;
class NtStatus;
class NtStatusProvider;
class PerfCounter;
class Process;
class Semaphore;
class Service;
class ServiceMgr;
class Sid;
class SizeOfSid;
class Thread;
class VirtualMemory;
class WaitableHandle;
class WaitableTimer;
class WinError;
class WinErrorProvider;

// global variables

AXL_SELECT_ANY SP_DEVINFO_DATA g_nullDevInfoData = { sizeof(SP_DEVINFO_DATA) };
AXL_SELECT_ANY NtQueryDirectoryFileFunc* ntQueryDirectoryFile = NULL;
AXL_SELECT_ANY NtOpenDirectoryObjectFunc* ntOpenDirectoryObject = NULL;
AXL_SELECT_ANY NtQueryDirectoryObjectFunc* ntQueryDirectoryObject = NULL;
AXL_SELECT_ANY NtOpenSymbolicLinkObjectFunc* ntOpenSymbolicLinkObject = NULL;
AXL_SELECT_ANY NtQuerySymbolicLinkObjectFunc* ntQuerySymbolicLinkObject = NULL;
AXL_SELECT_ANY NtQueryInformationFileFunc* ntQueryInformationFile = NULL;
AXL_SELECT_ANY NtQueryInformationProcessFunc* ntQueryInformationProcess = NULL;
AXL_SELECT_ANY NtQueryObjectFunc* ntQueryObject = NULL;
AXL_SELECT_ANY SID_IDENTIFIER_AUTHORITY g_nullSidAuthority = SECURITY_NULL_SID_AUTHORITY;
AXL_SELECT_ANY SID_IDENTIFIER_AUTHORITY g_worldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
AXL_SELECT_ANY SID_IDENTIFIER_AUTHORITY g_localSidAuthority = SECURITY_LOCAL_SID_AUTHORITY;
AXL_SELECT_ANY SID_IDENTIFIER_AUTHORITY g_creatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
AXL_SELECT_ANY SID_IDENTIFIER_AUTHORITY g_nonUniqueSidAuthority = SECURITY_NON_UNIQUE_AUTHORITY;
AXL_SELECT_ANY SID_IDENTIFIER_AUTHORITY g_resMgrSidAuthority = SECURITY_RESOURCE_MANAGER_AUTHORITY;
AXL_SELECT_ANY SID_IDENTIFIER_AUTHORITY g_ntSidAuthority = SECURITY_NT_AUTHORITY;
AXL_SELECT_ANY SID g_worldSid = { SID_REVISION, 1, SECURITY_WORLD_SID_AUTHORITY, SECURITY_WORLD_RID };
AXL_SELECT_ANY SID g_localSid = { SID_REVISION, 1, SECURITY_LOCAL_SID_AUTHORITY, SECURITY_LOCAL_RID };
AXL_SELECT_ANY SID g_creatorOwnerSid = { SID_REVISION, 1, SECURITY_CREATOR_SID_AUTHORITY, SECURITY_CREATOR_OWNER_RID };
AXL_SELECT_ANY SID g_creatorGroupSid = { SID_REVISION, 1, SECURITY_CREATOR_SID_AUTHORITY, SECURITY_CREATOR_GROUP_RID };
AXL_SELECT_ANY SID g_dialupSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_DIALUP_RID };
AXL_SELECT_ANY SID g_networkSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_NETWORK_RID };
AXL_SELECT_ANY SID g_batchSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_BATCH_RID };
AXL_SELECT_ANY SID g_interactiveSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_INTERACTIVE_RID };
AXL_SELECT_ANY SID g_serviceSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_SERVICE_RID };
AXL_SELECT_ANY SID g_anonymousSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_ANONYMOUS_LOGON_RID };
AXL_SELECT_ANY SID g_proxySid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_PROXY_RID };
AXL_SELECT_ANY SID g_enterpriseSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_ENTERPRISE_CONTROLLERS_RID };
AXL_SELECT_ANY SID g_selfSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_PRINCIPAL_SELF_RID };
AXL_SELECT_ANY SID g_authenticatedSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_AUTHENTICATED_USER_RID };
AXL_SELECT_ANY SID g_terminalServerSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_TERMINAL_SERVER_RID };
AXL_SELECT_ANY SID g_systemSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID };
AXL_SELECT_ANY SID g_localServiceSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SERVICE_RID };
AXL_SELECT_ANY SID g_networkServiceSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_NETWORK_SERVICE_RID };

// global functions

bool
verifyAuthenticodeSignature(
    const sl::StringRef_w& fileName,
    sl::String_w* programName,
    sl::String_w* subjectName,
    sl::String_w* issuerName,
    sl::Array<char>* serialNumber,
    uint64_t* timestamp
    );

template <typename T>
HRESULT
createBstrFromString(
    BSTR* p,
    const sl::StringRefBase<T>& string
    );

size_t
findCryptAttr(
    const CRYPT_ATTRIBUTES* attributes,
    const char* oid
    );

size_t
getCryptMsgSignerInfoProgramName(
    sl::String_w* programName,
    const CMSG_SIGNER_INFO* signerInfo
    );

sl::String_w
getCryptMsgSignerInfoProgramName(const CMSG_SIGNER_INFO* signerInfo);

bool
getCryptMsgSignerInfoTimestamp(
    uint64_t* timestamp,
    const CMSG_SIGNER_INFO* signerInfo
    );

uint64_t
getCryptMsgSignerInfoTimestamp(const CMSG_SIGNER_INFO* signerInfo);

void
initNtDllFunctions();

AXL_SL_DEFINE_GUID(
    g_ntStatusGuid,
    0x81443347,
    0x3bc9,
    0x4d5e,
    0x8b,
    0x23,
    0x32,
    0xd7,
    0x80,
    0xed,
    0xb5,
    0x2b
    );

size_t
setNtStatus(long status);

bool
syncExec(
    const sl::StringRef_w& cmdLine,
    sl::Array<char>* output,
    dword_t* exitCode = NULL
    );

sl::String_w
getProcessImageName(dword_t pid);

bool
cryptQueryObjectFile(
    const sl::StringRef_w& fileName,
    dword_t expectedContentTypeFlags,
    dword_t expectedFormatTypeFlags,
    dword_t flags,
    dword_t* encodingType,
    dword_t* contentType,
    dword_t* formatType,
    HCERTSTORE* certStore,
    HCRYPTMSG* cryptMsg,
    const void** context
    );

size_t
cryptDecodeObject(
    sl::Array<char>* buffer,
    dword_t encodingType,
    const char* oid,
    const void* p,
    size_t size,
    dword_t flags = 0
    );

size_t
cryptDecodeObject(
    void* buffer,
    size_t bufferSize,
    dword_t encodingType,
    const char* oid,
    const void* p,
    size_t size,
    dword_t flags = 0
    );

AXL_SL_DEFINE_GUID(
    g_winErrorGuid,
    0x54e100e8,
    0x2137,
    0x40b8,
    0xbc,
    0xd8,
    0x0,
    0xac,
    0x1d,
    0xb,
    0xaa,
    0x16
    );

size_t
setWinError(dword_t code);

bool
verifyTrustFile(const sl::StringRef_w& fileName);

bool
verifyTrustFile(const sl::StringRef& fileName);

} // namespace win