/*++

   Copyright    (c)    1994-1999    Microsoft Corporation

   Module  Name :

        setperm.cpp

   Abstract:

        IIS Security Wizard helper file

   Author:

        Sergei Antonov (sergeia)

   Project:

        Internet Services Manager

   Revision History:
		7/12/99		created
--*/

//
// Include Files
//
#include "stdafx.h"
#include "comprop.h"
#include <aclapi.h>
#include <ntseapi.h>
#include <shlwapi.h>

static HRESULT
SetSecurityDeep(
   LPTSTR *ppszBuffer,
   UINT * pcchBuffer,
   DWORD dwAttributes,
   SECURITY_INFORMATION si,
   PACL pDacl,
   PACL pSacl
   );

static BOOL 
PathIsDotOrDotDot(LPCTSTR pszPath)
{
   if (TEXT('.') == *pszPath++)
   {
      if (TEXT('\0') == *pszPath || (TEXT('.') == *pszPath && TEXT('\0') == *(pszPath + 1)))
         return TRUE;
   }
   return FALSE;
}

HRESULT
CPWSummary::SetPermToChildren(
	IN CString& FileName,
   IN SECURITY_INFORMATION si,
	IN PACL pDacl,
   IN PACL pSacl
	)
{
   HRESULT hr = S_OK;
   LPTSTR pszBuffer = NULL;
   UINT cchBuffer = 0, cchFolder;
   HANDLE hFind;
   WIN32_FIND_DATA fd;

   pszBuffer = (LPTSTR)LocalAlloc(LMEM_FIXED, 2 * MAX_PATH * sizeof(TCHAR));
   if (pszBuffer == NULL)
      return E_OUTOFMEMORY;
   cchBuffer = (UINT)LocalSize(pszBuffer) / sizeof(TCHAR);
   lstrcpy(pszBuffer, FileName);
   cchFolder = lstrlen(pszBuffer);
   // Append a backslash if it's not there already
   if (pszBuffer[cchFolder-1] != TEXT('\\'))
   {
      pszBuffer[cchFolder] = TEXT('\\');
      cchFolder++;
   }

   // Append the '*' wildcard
   pszBuffer[cchFolder] = TEXT('*');
   pszBuffer[cchFolder+1] = TEXT('\0');

   if (INVALID_HANDLE_VALUE != (hFind = FindFirstFile(pszBuffer, &fd)))
   {
      do
      {
         if (PathIsDotOrDotDot(fd.cFileName))
            continue;
         //
         // Build full path name and recurse
         //
         lstrcpyn(pszBuffer + cchFolder, fd.cFileName, cchBuffer - cchFolder);
         if (fd.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
         {
            hr = SetSecurityDeep(&pszBuffer,
                     &cchBuffer,
                     fd.dwFileAttributes,
                     si,
                     pDacl,
                     pSacl);
         }
         else
         {
            hr = HRESULT_FROM_WIN32(SetNamedSecurityInfo(
                                 pszBuffer,
                                 SE_FILE_OBJECT,
                                 si,
                                 NULL,
                                 NULL,
                                 pDacl,
                                 pSacl));
         }
      }
      while (S_OK == hr && FindNextFile(hFind, &fd));
      FindClose(hFind);
   }
   if (pszBuffer != NULL)
      LocalFree(pszBuffer);
   return hr;
}

static HRESULT
SetSecurityDeep(
   LPTSTR *ppszBuffer,
   UINT * pcchBuffer,
   DWORD dwAttributes,
   SECURITY_INFORMATION si,
   PACL pDacl,
   PACL pSacl
   )
{
   HRESULT hr = S_OK;
   DWORD dwErr = NOERROR;
   LPTSTR pszBuffer;
   BOOL bWriteDone = FALSE;

   pszBuffer = *ppszBuffer;
   //
   // Recursively apply the new SD to subfolders
   //
   if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
   {
      HANDLE hFind;
      WIN32_FIND_DATA fd;
      UINT cchFolder;
      UINT cchSizeRequired;

      cchFolder = lstrlen(pszBuffer);

      //
      // If the buffer is getting tight, realloc
      //
      cchSizeRequired = cchFolder + 1 + sizeof(fd.cFileName) / sizeof(TCHAR); // 1 for backslash
      if (cchSizeRequired > *pcchBuffer)
      {
         cchSizeRequired += MAX_PATH;  // so we don't realloc as often
         pszBuffer = (LPTSTR)LocalReAlloc(*ppszBuffer, cchSizeRequired * sizeof(TCHAR), LMEM_MOVEABLE);
         if (pszBuffer)
         {
            *ppszBuffer = pszBuffer;
            *pcchBuffer = cchSizeRequired;
         }
         else
         {
            // fd.cFileName typically has some empty space, so we
            // may be able to continue
            pszBuffer = *ppszBuffer;
            if (*pcchBuffer < cchFolder + 3) // backslash, '*', and NULL
               return E_OUTOFMEMORY;
         }
      }

      // Append a backslash if it's not there already
      if (pszBuffer[cchFolder-1] != TEXT('\\'))
      {
         pszBuffer[cchFolder] = TEXT('\\');
         cchFolder++;
      }

      // Append the '*' wildcard
      pszBuffer[cchFolder] = TEXT('*');
      pszBuffer[cchFolder+1] = TEXT('\0');

      //
      // Enumerate the folder contents
      //
      hFind = FindFirstFile(pszBuffer, &fd);

      if (INVALID_HANDLE_VALUE == hFind)
      {
         dwErr = GetLastError();

         if (ERROR_ACCESS_DENIED == dwErr)
         {
            // Remove the '*' wildcard
            pszBuffer[cchFolder-1] = TEXT('\0');

            if (si & DACL_SECURITY_INFORMATION)
            {
               //
               // The user may be granting themselves access, so call
               // WriteObjectSecurity and retry FindFirstFile.
               //
               // Don't blindly call WriteObjectSecurity before FindFirstFile
               // since it's possible the user has access now but is removing
               // their own access.
               //
               bWriteDone = TRUE;
               hr = HRESULT_FROM_WIN32(SetNamedSecurityInfo(
                                 pszBuffer,
                                 SE_FILE_OBJECT,
                                 si,
                                 NULL,
                                 NULL,
                                 pDacl,
                                 pSacl));
               if (SUCCEEDED(hr))
               {
                  // Retry FindFirstFile
                  pszBuffer[cchFolder-1] = TEXT('\\');
                  hFind = FindFirstFile(pszBuffer, &fd);
               }
            }
         }
      }

      if (hFind != INVALID_HANDLE_VALUE)
      {
         do
         {
            if (PathIsDotOrDotDot(fd.cFileName))
               continue;

            //
            // Build full path name and recurse
            //
            lstrcpyn(pszBuffer + cchFolder, fd.cFileName, *pcchBuffer - cchFolder);
            hr = SetSecurityDeep(ppszBuffer,
                     pcchBuffer,
                     fd.dwFileAttributes,
                     si,
                     pDacl,
                     pSacl);

            // In case the buffer was reallocated
            pszBuffer = *ppszBuffer;
         }
         while (S_OK == hr && FindNextFile(hFind, &fd));

         FindClose(hFind);
      }
      else if (NOERROR != dwErr)
      {
         hr = S_FALSE;   // abort
      }

      // Truncate the path back to the original length (sans backslash)
      pszBuffer[cchFolder-1] = TEXT('\0');
   }

   //
   // Finally, write out the new security descriptor
   //
   if (!bWriteDone)
   {
      hr = HRESULT_FROM_WIN32(SetNamedSecurityInfo(
                                 pszBuffer,
                                 SE_FILE_OBJECT,
                                 si,
                                 NULL,
                                 NULL,
                                 pDacl,
                                 pSacl));
   }
   if (SUCCEEDED(hr))
   {
      //
      // Notify the shell if we change permissions on a folder (48220)
      //
      if (  (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) 
         && (si & DACL_SECURITY_INFORMATION)
         )
      {
         SHChangeNotify(
            SHCNE_UPDATEDIR,
            SHCNF_PATH | SHCNF_FLUSH | SHCNF_FLUSHNOWAIT,
            pszBuffer,
            NULL);
      }
   }
   else
   {
      hr = S_FALSE;   // abort
   }
   return hr;
}