// Created by: PLOTNIKOV Eugeny
// Copyright (c) 1996-1999 Matra Datavision
// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#ifdef _WIN32

  /******************************************************************************/
  /* File:      OSD_WNT.cxx                                                     */
  /* Purpose:   Security management routines ( more convenient than WIN32       */
  /*            ones ) and other convrnient functions.                          */
  /******************************************************************************/
  /***/
  #include <OSD_WNT.hxx>

  #include <strsafe.h>
  #include <wchar.h>
  #include <stdlib.h>

  #include <Standard_Macro.hxx>

  /***/
  #ifndef OCCT_UWP
static void Init(void);

/***/
class Init_OSD_WNT
{ // provides initialization

public:
  Init_OSD_WNT() { Init(); }

}; // end Init_OSD_WNT

static Init_OSD_WNT initOsdWnt;
  #endif
/***/
static BOOL   fInit = FALSE;
static PSID*  predefinedSIDs;
static HANDLE hHeap;
/***/
static MOVE_DIR_PROC     _move_dir_proc;
static COPY_DIR_PROC     _copy_dir_proc;
static RESPONSE_DIR_PROC _response_dir_proc;
  /***/
  #define PREDEFINED_SIDs_COUNT 9
  #define UNIVERSAL_PREDEFINED_SIDs_COUNT 5
  /***/
  #define SID_INTERACTIVE 0
  #define SID_NETWORK 1
  #define SID_LOCAL 2
  #define SID_DIALUP 3
  #define SID_BATCH 4
  #define SID_CREATOR_OWNER 5
  #define SID_ADMIN 6
  #define SID_WORLD 7
  #define SID_NULL 8
  /***/
  #ifndef OCCT_UWP
// None of the existing security APIs are supported in a UWP applications
/******************************************************************************/
/* Function : AllocSD                                                       */
/* Purpose  : Allocates and initializes security identifier                 */
/* Returns  : Pointer to allocated SID on success, NULL otherwise           */
/* Warning  : Allocated SID must be deallocated by 'FreeSD' function        */
/******************************************************************************/
/***/
PSECURITY_DESCRIPTOR AllocSD(void)
{

  PSECURITY_DESCRIPTOR retVal =
    (PSECURITY_DESCRIPTOR)HeapAlloc(hHeap, 0, sizeof(SECURITY_DESCRIPTOR));

  if (retVal != NULL && !InitializeSecurityDescriptor(retVal, SECURITY_DESCRIPTOR_REVISION))
  {

    HeapFree(hHeap, 0, (PVOID)retVal);
    retVal = NULL;

  } /* end if */

  return retVal;

} /* end AllocSD */

/***/
/******************************************************************************/
/* Function : FreeSD                                                        */
/* Purpose  : Deallocates security identifier which was allocated by the    */
/*            'AllocSD' function                                            */
/******************************************************************************/
/***/
void FreeSD(PSECURITY_DESCRIPTOR pSD)
{

  BOOL fPresent;
  BOOL fDaclDefaulted;
  PACL pACL;

  if (GetSecurityDescriptorDacl(pSD, &fPresent, &pACL, &fDaclDefaulted) && fPresent)

    HeapFree(hHeap, 0, (PVOID)pACL);

  HeapFree(hHeap, 0, (PVOID)pSD);

} /* end FreeSD */

  /***/
  /******************************************************************************/
  /* Function : GetTokenInformationEx                                         */
  /* Purpose  : Allocates and fills out access token                          */
  /* Returns  : Pointer to the access token on success, NULL otherwise        */
  /* Warning  : Allocated access token  must be deallocated by                */
  /*            'FreeTokenInformation' function                               */
  /******************************************************************************/
  /***/

    #if defined(__CYGWIN32__) || defined(__MINGW32__)
      #define __try
      #define __finally
      #define __leave return buffer
    #endif

LPVOID GetTokenInformationEx(HANDLE hToken, TOKEN_INFORMATION_CLASS tic)
{

  DWORD  errVal;
  DWORD  dwSize;
  DWORD  dwSizeNeeded = 0;
  LPVOID buffer       = NULL;
  BOOL   fOK          = FALSE;

  __try
  {

    do
    {

      dwSize = dwSizeNeeded;
      errVal = ERROR_SUCCESS;

      if (!GetTokenInformation(hToken, tic, buffer, dwSize, &dwSizeNeeded))
      {

        if ((errVal = GetLastError()) != ERROR_INSUFFICIENT_BUFFER)

          __leave;

        if ((buffer = HeapAlloc(hHeap, 0, dwSizeNeeded)) == NULL)

          __leave;

      } /* end if */

    } while (errVal != ERROR_SUCCESS);

    fOK = TRUE;

  } /* end __try */

  __finally
  {

    if (!fOK && buffer != NULL)
    {

      HeapFree(hHeap, 0, buffer);
      buffer = NULL;

    } /* end if */

  } /* end __finally */

    #ifdef VAC
leave:; // added for VisualAge
    #endif

  return buffer;

} /* end GetTokenInformationEx */

    #if defined(__CYGWIN32__) || defined(__MINGW32__)
      #undef __try
      #undef __finally
      #undef __leave
    #endif

/***/
/******************************************************************************/
/* Function : FreeTokenInformation                                          */
/* Purpose  : Deallocates access token which was allocated by the           */
/*            'GetTokenInformationEx' function                              */
/******************************************************************************/
/***/
void FreeTokenInformation(LPVOID lpvTkInfo)
{

  HeapFree(hHeap, 0, lpvTkInfo);

} /* end FreeTokenInformation */

/***/
/******************************************************************************/
/* Function : Init                                                          */
/* Purpose  : Allocates and initializes predefined security identifiers     */
/* Warning  : Generates 'STATUS_NO_MEMORY' software exception if there are  */
/*            insufficient of memory. This exception can be caught by using */
/*            software exception handling ( SEH ) mechanism                 */
/*            ( __try / __except )                                          */
/******************************************************************************/
/***/
static void Init(void)
{

  SID_IDENTIFIER_AUTHORITY sidIDAnull    = SECURITY_NULL_SID_AUTHORITY;
  SID_IDENTIFIER_AUTHORITY sidIDAworld   = SECURITY_WORLD_SID_AUTHORITY;
  SID_IDENTIFIER_AUTHORITY sidIDANT      = SECURITY_NT_AUTHORITY;
  SID_IDENTIFIER_AUTHORITY sidIDAlocal   = SECURITY_LOCAL_SID_AUTHORITY;
  SID_IDENTIFIER_AUTHORITY sidIDAcreator = SECURITY_CREATOR_SID_AUTHORITY;

  if (!fInit)
  {

    predefinedSIDs = (PSID*)HeapAlloc(hHeap = GetProcessHeap(),
                                      HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY,
                                      PREDEFINED_SIDs_COUNT * sizeof(PSID*));

    AllocateAndInitializeSid(&sidIDANT,
                             2,
                             SECURITY_BUILTIN_DOMAIN_RID,
                             DOMAIN_ALIAS_RID_ADMINS,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             &predefinedSIDs[SID_ADMIN]);

    AllocateAndInitializeSid(&sidIDAworld,
                             1,
                             SECURITY_WORLD_RID,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             &predefinedSIDs[SID_WORLD]);

    AllocateAndInitializeSid(&sidIDANT,
                             1,
                             SECURITY_INTERACTIVE_RID,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             &predefinedSIDs[SID_INTERACTIVE]);

    AllocateAndInitializeSid(&sidIDANT,
                             1,
                             SECURITY_NETWORK_RID,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             &predefinedSIDs[SID_NETWORK]);

    AllocateAndInitializeSid(&sidIDAlocal,
                             1,
                             SECURITY_LOCAL_RID,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             &predefinedSIDs[SID_LOCAL]);

    AllocateAndInitializeSid(&sidIDANT,
                             1,
                             SECURITY_DIALUP_RID,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             &predefinedSIDs[SID_DIALUP]);

    AllocateAndInitializeSid(&sidIDANT,
                             1,
                             SECURITY_BATCH_RID,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             &predefinedSIDs[SID_BATCH]);

    AllocateAndInitializeSid(&sidIDAcreator,
                             1,
                             SECURITY_CREATOR_OWNER_RID,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             &predefinedSIDs[SID_CREATOR_OWNER]);

    AllocateAndInitializeSid(&sidIDAnull,
                             1,
                             SECURITY_NULL_RID,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             0,
                             &predefinedSIDs[SID_NULL]);

    fInit = TRUE;

  } /* end if */

} /* end init */

/***/
/******************************************************************************/
/* Function : PredefinedSid                                                 */
/* Purpose  : Checks whether specified SID predefined or not                */
/* Returns  : TRUE if specified SID is predefined, FALSE otherwise          */
/******************************************************************************/
/***/
BOOL PredefinedSid(PSID pSID)
{

  int i;

  for (i = 0; i < PREDEFINED_SIDs_COUNT; ++i)

    if (EqualSid(pSID, predefinedSIDs[i]))

      return TRUE;

  return FALSE;

} /* end PredefinedSid */

/***/
/******************************************************************************/
/* Function : NtPredefinedSid                                               */
/* Purpose  : Checks whether specified SID universal or not                 */
/* Returns  : TRUE if specified SID is NOT universal, FALSE otherwise       */
/******************************************************************************/
/***/
BOOL NtPredefinedSid(PSID pSID)
{

  int                       i;
  PSID_IDENTIFIER_AUTHORITY pTestIDA;
  SID_IDENTIFIER_AUTHORITY  ntIDA = SECURITY_NT_AUTHORITY;
  PDWORD                    pdwTestSA;

  for (i = 0; i < UNIVERSAL_PREDEFINED_SIDs_COUNT; ++i)

    if (EqualSid(pSID, predefinedSIDs[i]))

      return TRUE;

  pTestIDA = GetSidIdentifierAuthority(pSID);

  if (memcmp(pTestIDA, &ntIDA, sizeof(SID_IDENTIFIER_AUTHORITY)) == 0)
  {

    pdwTestSA = GetSidSubAuthority(pSID, 0);

    if (*pdwTestSA == SECURITY_LOGON_IDS_RID)

      return TRUE;

  } /* end if */

  return FALSE;

} /* end NtPredefinedSid */

/***/
/******************************************************************************/
/* Function : AdminSid                                                      */
/* Purpose  : Returns SID of the administrative user account                */
/******************************************************************************/
/***/
PSID AdminSid(void)
{

  return predefinedSIDs[SID_ADMIN];

} /* end AdminSid */

/***/
/******************************************************************************/
/* Function : WorldSid                                                      */
/* Purpose  : Returns SID of group that includes all users                  */
/******************************************************************************/
/***/
PSID WorldSid(void)
{

  return predefinedSIDs[SID_WORLD];

} /* end WorldSid */

/***/
/******************************************************************************/
/* Function : InteractiveSid                                                */
/* Purpose  : Returns SID of group that includes all users logged on for    */
/*            interactive operation                                         */
/******************************************************************************/
/***/
PSID InteractiveSid(void)
{

  return predefinedSIDs[SID_INTERACTIVE];

} /* end InteractiveSID */

/***/
/******************************************************************************/
/* Function : NetworkSid                                                    */
/* Purpose  : Returns SID of group that includes all users logged on across */
/*            a network                                                     */
/******************************************************************************/
/***/
PSID NetworkSid(void)
{

  return predefinedSIDs[SID_NETWORK];

} /* end NetworkSid */

/***/
/******************************************************************************/
/* Function : LocalSid                                                      */
/* Purpose  : Returns SID of group that includes all users logged on locally*/
/******************************************************************************/
/***/
PSID LocalSid(void)
{

  return predefinedSIDs[SID_LOCAL];

} /* end LocalSid */

/***/
/******************************************************************************/
/* Function : DialupSid                                                     */
/* Purpose  : Returns SID of group that includes all users logged on to     */
/*            terminals using a dialup modem                                */
/******************************************************************************/
/***/
PSID DialupSid(void)
{

  return predefinedSIDs[SID_DIALUP];

} /* end DialupSid */

/***/
/******************************************************************************/
/* Function : BatchSid                                                      */
/* Purpose  : Returns SID of group that includes all users logged on using  */
/*            a batch queue facility                                        */
/******************************************************************************/
/***/
PSID BatchSid(void)
{

  return predefinedSIDs[SID_BATCH];

} /* end BatchSid */

/***/
/******************************************************************************/
/* Function : CreatorOwnerSid                                               */
/* Purpose  : Returns SID of 'CREATOR OWNER' special group                  */
/******************************************************************************/
/***/
PSID CreatorOwnerSid(void)
{

  return predefinedSIDs[SID_CREATOR_OWNER];

} /* end CreatorOwnerSid */

/***/
/******************************************************************************/
/* Function : NullSid                                                       */
/* Purpose  : Returns null SID                                              */
/******************************************************************************/
/***/
PSID NullSid(void)
{

  return predefinedSIDs[SID_NULL];

} /* end NullSid */

  /***/
  /******************************************************************************/
  /* Function : GetFileSecurityEx                                             */
  /* Purpose  : Allocates a security descriptor and fills it out by security  */
  /*            information which belongs to the specified file               */
  /* Returns  : Pointer to the allocated security descriptor on success       */
  /*            NULL otherwise                                                */
  /* Warning  : Allocated security descriptor must be deallocated by          */
  /*            'FreeFileSecurity' function                                   */
  /******************************************************************************/
  /***/

    #if defined(__CYGWIN32__) || defined(__MINGW32__)
      #define __try
      #define __finally
      #define __leave return retVal
    #endif

PSECURITY_DESCRIPTOR GetFileSecurityEx(LPCWSTR fileName, SECURITY_INFORMATION si)
{

  DWORD                errVal;
  DWORD                dwSize;
  DWORD                dwSizeNeeded = 0;
  PSECURITY_DESCRIPTOR retVal       = NULL;
  BOOL                 fOK          = FALSE;

  __try
  {

    do
    {

      dwSize = dwSizeNeeded;
      errVal = ERROR_SUCCESS;

      if (!GetFileSecurityW(fileName, si, retVal, dwSize, &dwSizeNeeded))
      {

        if ((errVal = GetLastError()) != ERROR_INSUFFICIENT_BUFFER)
          __leave;

        if ((retVal = (PSECURITY_DESCRIPTOR)HeapAlloc(hHeap, 0, dwSizeNeeded)) == NULL)
          __leave;

      } /* end if */

    } while (errVal != ERROR_SUCCESS);

    fOK = TRUE;

  } /* end __try */

  __finally
  {

    if (!fOK && retVal != NULL)
    {

      HeapFree(hHeap, 0, retVal);
      retVal = NULL;

    } /* end if */

  } /* end __finally */

    #ifdef VAC
leave:; // added for VisualAge
    #endif

  return retVal;

} /* end GetFileSecurityEx */

    #if defined(__CYGWIN32__) || defined(__MINGW32__)
      #undef __try
      #undef __finally
      #undef __leave
    #endif

/***/
/******************************************************************************/
/* Function : FreeFileSecurity                                              */
/* Purpose  : Deallocates security descriptor which was allocated by the    */
/*            'GetFileSecurityEx' function                                  */
/******************************************************************************/
/***/
void FreeFileSecurity(PSECURITY_DESCRIPTOR pSD)
{

  HeapFree(hHeap, 0, (LPVOID)pSD);

} /* end FreeFileSecurity */

/******************************************************************************/
/* Function : CreateAcl                                                     */
/* Purpose  : Allocates and initializes access-control list                 */
/* Returns  : Pointer to the allocated and initialized ACL on success,      */
/*            NULL otherwise                                                */
/* Warning  : Allocated ACL must be deallocated by 'FreeAcl' function       */
/******************************************************************************/
/***/
PACL CreateAcl(DWORD dwAclSize)
{

  PACL retVal;

  retVal = (PACL)HeapAlloc(hHeap, 0, dwAclSize);

  if (retVal != NULL)

    InitializeAcl(retVal, dwAclSize, ACL_REVISION);

  return retVal;

} /* end CreateAcl */

/***/
/******************************************************************************/
/* Function : FreeAcl                                                       */
/* Purpose  : Deallocates access-control list which was allocated by the    */
/*            'CreateAcl' function                                          */
/******************************************************************************/
/***/
void FreeAcl(PACL pACL)
{

  HeapFree(hHeap, 0, (PVOID)pACL);

} /* end FreeAcl */

/******************************************************************************/
/* Function : AllocAccessAllowedAce                                         */
/* Purpose  : Allocates and initializes access-control entry                */
/* Returns  : Pointer to the ACE on success, NULL othrwise                  */
/* Warning  : Allocated ACE must be deallocated by the 'FreeAce' function   */
/******************************************************************************/
/***/
PVOID AllocAccessAllowedAce(DWORD dwMask, BYTE flags, PSID pSID)
{

  PFILE_ACE retVal;
  WORD      wSize;

  wSize = (WORD)(sizeof(ACE_HEADER) + sizeof(DWORD) + GetLengthSid(pSID));

  retVal = (PFILE_ACE)HeapAlloc(hHeap, 0, wSize);

  if (retVal != NULL)
  {

    retVal->header.AceType  = ACCESS_ALLOWED_ACE_TYPE;
    retVal->header.AceFlags = flags;
    retVal->header.AceSize  = wSize;

    retVal->dwMask = dwMask;

    CopySid(GetLengthSid(pSID), &retVal->pSID, pSID);

  } /* end if */

  return retVal;

} /* end AllocAccessAllowedAce */

/***/
/******************************************************************************/
/* Function : FreeAce                                                       */
/* Purpose  : Deallocates an ACE which was allocated by the                 */
/*            'AllocAccessAllowedAce ' function                             */
/******************************************************************************/
/***/
void FreeAce(PVOID pACE)
{

  HeapFree(hHeap, 0, pACE);

} /* end FreeAce */
  #endif
  #define WILD_CARD L"/*.*"
  #define WILD_CARD_LEN (sizeof(WILD_CARD))

/***/
/******************************************************************************/
/* Function : MoveDirectory                                                 */
/* Purpose  : Moves specified directory tree to the new location            */
/* Returns  : TRUE on success, FALSE otherwise                              */
/******************************************************************************/
/***/
static BOOL MoveDirectory(const wchar_t* oldDir, const wchar_t* newDir, DWORD& theRecurseLevel)
{
  wchar_t* driveSrc = NULL;
  wchar_t* driveDst = NULL;
  wchar_t* pathSrc  = NULL;
  wchar_t* pathDst  = NULL;
  BOOL     retVal   = FALSE;
  if (theRecurseLevel == 0)
  {
    ++theRecurseLevel;
    BOOL fFind = FALSE;
    if ((driveSrc = (wchar_t*)HeapAlloc(hHeap, 0, _MAX_DRIVE * sizeof(wchar_t))) != NULL
        && (driveDst = (wchar_t*)HeapAlloc(hHeap, 0, _MAX_DRIVE * sizeof(wchar_t))) != NULL
        && (pathSrc = (wchar_t*)HeapAlloc(hHeap, 0, _MAX_DIR * sizeof(wchar_t))) != NULL
        && (pathDst = (wchar_t*)HeapAlloc(hHeap, 0, _MAX_DIR * sizeof(wchar_t))) != NULL)
    {
      _wsplitpath(oldDir, driveSrc, pathSrc, NULL, NULL);
      _wsplitpath(newDir, driveDst, pathDst, NULL, NULL);
      if (wcscmp(driveSrc, driveDst) == 0 && wcscmp(pathSrc, pathDst) == 0)
      {
      retry:
        retVal = MoveFileExW(oldDir, newDir, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED);
        fFind  = TRUE;
        if (!retVal)
        {
          if (_response_dir_proc != NULL)
          {
            const DIR_RESPONSE response = _response_dir_proc(oldDir);
            if (response == DIR_RETRY)
            {
              goto retry;
            }
            else if (response == DIR_IGNORE)
            {
              retVal = TRUE;
            }
          }
        }
        else if (_move_dir_proc != NULL)
        {
          _move_dir_proc(oldDir, newDir);
        }
      }
    }

    if (pathDst != NULL)
    {
      HeapFree(hHeap, 0, pathDst);
    }
    if (pathSrc != NULL)
    {
      HeapFree(hHeap, 0, pathSrc);
    }
    if (driveDst != NULL)
    {
      HeapFree(hHeap, 0, driveDst);
    }
    if (driveSrc != NULL)
    {
      HeapFree(hHeap, 0, driveSrc);
    }

    if (fFind)
    {
      --theRecurseLevel;
      return retVal;
    }
  }
  else
  {
    ++theRecurseLevel;
  }

  WIN32_FIND_DATAW* pFD          = NULL;
  wchar_t*          pName        = NULL;
  wchar_t*          pFullNameSrc = NULL;
  wchar_t*          pFullNameDst = NULL;
  HANDLE            hFindFile    = INVALID_HANDLE_VALUE;
  retVal                         = CreateDirectoryW(newDir, NULL);
  if (retVal || (!retVal && GetLastError() == ERROR_ALREADY_EXISTS))
  {
    size_t anOldDirLength;
    StringCchLengthW(oldDir, MAX_PATH, &anOldDirLength);
    const size_t aNameLength = anOldDirLength + WILD_CARD_LEN + sizeof(L'\x00');
    if ((pFD = (WIN32_FIND_DATAW*)HeapAlloc(hHeap, 0, sizeof(WIN32_FIND_DATAW))) != NULL
        && (pName = (wchar_t*)HeapAlloc(hHeap, 0, aNameLength)) != NULL)
    {
      StringCchCopyW(pName, aNameLength, oldDir);
      StringCchCatW(pName, aNameLength, WILD_CARD);
      retVal    = TRUE;
      hFindFile = FindFirstFileExW(pName, FindExInfoStandard, pFD, FindExSearchNameMatch, NULL, 0);
      for (BOOL fFind = hFindFile != INVALID_HANDLE_VALUE; fFind;
           fFind      = FindNextFileW(hFindFile, pFD))
      {
        if ((pFD->cFileName[0] == L'.' && pFD->cFileName[1] == L'\0')
            || (pFD->cFileName[0] == L'.' && pFD->cFileName[1] == L'.'
                && pFD->cFileName[2] == L'\0'))
        {
          continue;
        }

        size_t aNewDirLength = 0, aFileNameLength = 0;
        StringCchLengthW(newDir, MAX_PATH, &aNewDirLength);
        StringCchLengthW(pFD->cFileName,
                         sizeof(pFD->cFileName) / sizeof(pFD->cFileName[0]),
                         &aFileNameLength);
        const size_t aFullNameSrcLength =
          anOldDirLength + aFileNameLength + sizeof(L'/') + sizeof(L'\x00');
        const size_t aFullNameDstLength =
          aNewDirLength + aFileNameLength + sizeof(L'/') + sizeof(L'\x00');
        if ((pFullNameSrc = (wchar_t*)HeapAlloc(hHeap, 0, aFullNameSrcLength)) == NULL
            || (pFullNameDst = (wchar_t*)HeapAlloc(hHeap, 0, aFullNameDstLength)) == NULL)
        {
          break;
        }

        StringCchCopyW(pFullNameSrc, aFullNameSrcLength, oldDir);
        StringCchCatW(pFullNameSrc, aFullNameSrcLength, L"/");
        StringCchCatW(pFullNameSrc, aFullNameSrcLength, pFD->cFileName);

        StringCchCopyW(pFullNameDst, aFullNameDstLength, newDir);
        StringCchCatW(pFullNameDst, aFullNameDstLength, L"/");
        StringCchCatW(pFullNameDst, aFullNameDstLength, pFD->cFileName);

        if ((pFD->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
        {
          retVal = MoveDirectory(pFullNameSrc, pFullNameDst, theRecurseLevel);
          if (!retVal)
          {
            break;
          }
        }
        else
        {
        retry_1:
          retVal = MoveFileExW(pFullNameSrc,
                               pFullNameDst,
                               MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED);
          if (!retVal)
          {
            if (_response_dir_proc != NULL)
            {
              const DIR_RESPONSE response = _response_dir_proc(pFullNameSrc);
              if (response == DIR_ABORT)
              {
                break;
              }
              else if (response == DIR_RETRY)
              {
                goto retry_1;
              }
              else if (response == DIR_IGNORE)
              {
                retVal = TRUE;
              }
              else
              {
                break;
              }
            }
          }
          else if (_move_dir_proc != NULL)
          {
            _move_dir_proc(pFullNameSrc, pFullNameDst);
          }
        }

        HeapFree(hHeap, 0, pFullNameDst);
        HeapFree(hHeap, 0, pFullNameSrc);
        pFullNameSrc = pFullNameDst = NULL;
      }
    }
  }

  if (hFindFile != INVALID_HANDLE_VALUE)
  {
    FindClose(hFindFile);
  }

  if (pFullNameSrc != NULL)
  {
    HeapFree(hHeap, 0, pFullNameSrc);
  }
  if (pFullNameDst != NULL)
  {
    HeapFree(hHeap, 0, pFullNameDst);
  }
  if (pName != NULL)
  {
    HeapFree(hHeap, 0, pName);
  }
  if (pFD != NULL)
  {
    HeapFree(hHeap, 0, pFD);
  }

  if (retVal)
  {
  retry_2:
    retVal = RemoveDirectoryW(oldDir);
    if (!retVal)
    {
      if (_response_dir_proc != NULL)
      {
        const DIR_RESPONSE response = _response_dir_proc(oldDir);
        if (response == DIR_RETRY)
        {
          goto retry_2;
        }
        else if (response == DIR_IGNORE)
        {
          retVal = TRUE;
        }
      }
    }
  }

  --theRecurseLevel;
  return retVal;
}

BOOL MoveDirectory(const wchar_t* oldDir, const wchar_t* newDir)
{
  DWORD aRecurseLevel = 0;
  return MoveDirectory(oldDir, newDir, aRecurseLevel);
}

/***/
/******************************************************************************/
/* Function : CopyDirectory                                                 */
/* Purpose  : Copies specified directory tree to the new location           */
/* Returns  : TRUE on success, FALSE otherwise                              */
/******************************************************************************/
/***/
BOOL CopyDirectory(const wchar_t* dirSrc, const wchar_t* dirDst)
{
  WIN32_FIND_DATAW* pFD          = NULL;
  wchar_t*          pName        = NULL;
  wchar_t*          pFullNameSrc = NULL;
  wchar_t*          pFullNameDst = NULL;
  HANDLE            hFindFile    = INVALID_HANDLE_VALUE;

  BOOL retVal = CreateDirectoryW(dirDst, NULL);
  if (retVal || (!retVal && GetLastError() == ERROR_ALREADY_EXISTS))
  {
    size_t aDirSrcLength = 0;
    StringCchLengthW(dirSrc, MAX_PATH, &aDirSrcLength);
    const size_t aNameLength = aDirSrcLength + WILD_CARD_LEN + sizeof(L'\x00');
    if ((pFD = (WIN32_FIND_DATAW*)HeapAlloc(hHeap, 0, sizeof(WIN32_FIND_DATAW))) != NULL
        && (pName = (wchar_t*)HeapAlloc(hHeap, 0, aNameLength)) != NULL)
    {
      StringCchCopyW(pName, aNameLength, dirSrc);
      StringCchCatW(pName, aNameLength, WILD_CARD);

      retVal    = TRUE;
      hFindFile = FindFirstFileExW(pName, FindExInfoStandard, pFD, FindExSearchNameMatch, NULL, 0);
      for (BOOL fFind = hFindFile != INVALID_HANDLE_VALUE; fFind;
           fFind      = FindNextFileW(hFindFile, pFD))
      {
        if ((pFD->cFileName[0] == L'.' && pFD->cFileName[1] == L'\0')
            || (pFD->cFileName[0] == L'.' && pFD->cFileName[1] == L'.'
                && pFD->cFileName[2] == L'\0'))
        {
          continue;
        }

        size_t aDirDstLength = 0, aFileNameLength = 0;
        StringCchLengthW(dirDst, MAX_PATH, &aDirDstLength);
        StringCchLengthW(pFD->cFileName,
                         sizeof(pFD->cFileName) / sizeof(pFD->cFileName[0]),
                         &aFileNameLength);
        const size_t aFullNameSrcLength =
          aDirSrcLength + aFileNameLength + sizeof(L'/') + sizeof(L'\x00');
        const size_t aFullNameDstLength =
          aDirDstLength + aFileNameLength + sizeof(L'/') + sizeof(L'\x00');
        if ((pFullNameSrc = (wchar_t*)HeapAlloc(hHeap, 0, aFullNameSrcLength)) == NULL
            || (pFullNameDst = (wchar_t*)HeapAlloc(hHeap, 0, aFullNameDstLength)) == NULL)
        {
          break;
        }

        StringCchCopyW(pFullNameSrc, aFullNameSrcLength, dirSrc);
        StringCchCatW(pFullNameSrc, aFullNameSrcLength, L"/");
        StringCchCatW(pFullNameSrc, aFullNameSrcLength, pFD->cFileName);

        StringCchCopyW(pFullNameDst, aFullNameDstLength, dirDst);
        StringCchCatW(pFullNameDst, aFullNameDstLength, L"/");
        StringCchCatW(pFullNameDst, aFullNameDstLength, pFD->cFileName);
        if ((pFD->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
        {
          retVal = CopyDirectory(pFullNameSrc, pFullNameDst);
          if (!retVal)
          {
            break;
          }
        }
        else
        {
        retry:
  #ifndef OCCT_UWP
          retVal = CopyFileW(pFullNameSrc, pFullNameDst, FALSE);
  #else
          retVal = (CopyFile2(pFullNameSrc, pFullNameDst, FALSE) == S_OK) ? TRUE : FALSE;
  #endif
          if (!retVal)
          {
            if (_response_dir_proc != NULL)
            {
              const DIR_RESPONSE response = _response_dir_proc(pFullNameSrc);
              if (response == DIR_ABORT)
              {
                break;
              }
              else if (response == DIR_RETRY)
              {
                goto retry;
              }
              else if (response == DIR_IGNORE)
              {
                retVal = TRUE;
              }
              else
              {
                break;
              }
            }
          }
          else if (_copy_dir_proc != NULL)
          {
            _copy_dir_proc(pFullNameSrc, pFullNameDst);
          }
        }

        HeapFree(hHeap, 0, pFullNameDst);
        HeapFree(hHeap, 0, pFullNameSrc);
        pFullNameSrc = pFullNameDst = NULL;
      }
    }
  }

  if (hFindFile != INVALID_HANDLE_VALUE)
  {
    FindClose(hFindFile);
  }

  if (pFullNameSrc != NULL)
  {
    HeapFree(hHeap, 0, pFullNameSrc);
  }
  if (pFullNameDst != NULL)
  {
    HeapFree(hHeap, 0, pFullNameDst);
  }
  if (pName != NULL)
  {
    HeapFree(hHeap, 0, pName);
  }
  if (pFD != NULL)
  {
    HeapFree(hHeap, 0, pFD);
  }

  return retVal;
} /* end CopyDirectory */

/***/
/******************************************************************************/
/* Function : SetMoveDirectoryProc                                          */
/* Purpose  : Sets callback procedure which is calling by the               */
/*            'MoveDirectory' after moving of each item in the              */
/*            directory. To unregister this callback function supply NULL   */
/*            pointer                                                       */
/******************************************************************************/
/***/
void SetMoveDirectoryProc(MOVE_DIR_PROC proc)
{

  _move_dir_proc = proc;

} /* end SetMoveDirectoryProc */

/***/
/******************************************************************************/
/* Function : SetCopyDirectoryProc                                          */
/* Purpose  : Sets callback procedure which is calling by the               */
/*            'CopyDirectory' after copying of each item in the             */
/*            directory. To unregister this callback function supply NULL   */
/*            pointer                                                       */
/******************************************************************************/
/***/
void SetCopyDirectoryProc(COPY_DIR_PROC proc)
{

  _copy_dir_proc = proc;

} /* end SetCopyDirectoryProc */

/***/
/******************************************************************************/
/* Function : SetResponseDirectoryProc                                      */
/* Purpose  : Sets callback procedure which is calling by the               */
/*            directory processing function if an error was occur.          */
/*            The return value of that callback procedure determines        */
/*            behaviour of directory processing functions in case of error. */
/*            To unregister this callback function supply NULL pointer      */
/******************************************************************************/
/***/
void SetResponseDirectoryProc(RESPONSE_DIR_PROC proc)
{

  _response_dir_proc = proc;

} /* end SetResponseDirectoryProc */

/***/
/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
#endif
