/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: xbSecurity
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbSecurity.h"
#include "xbConfig.h"
#include "xbFileTools.h"

xbSecurity::xbSecurity(xbSecurityType type,LPCSTR pRights)
{
	m_userDefaulted = false;
	m_groupDefaulted = false;
	m_userSet = false;
	m_groupSet = false;
	Initialize(type,pRights);
}

xbSecurity::~xbSecurity()
{
}

void xbSecurity::Initialize(xbSecurityType type,LPCSTR pRights)
{
	m_userDefaulted = false;
	m_groupDefaulted = false;
	m_userSet = false;
	m_groupSet = false;

	m_sd.Clear();
	m_user.Clear();
	m_group.Clear();
	m_dacl.Clear();
	m_sacl.Clear();

	xbString strRights;
	regBehaviour(type, strRights, pRights);

	if (type == xbSecurityDflt)
	{
		if (xbGetOSver()==5 && xbGetOSminorVer()==1)
			type = xbSecurityAll; //Bug XP services / secu fichiers
		else
			type = xbSecurityNone;
	}
	else
	{
		if (type == xbSecurityInherited)
			type = xbSecurityNone;
	}

	if (type == xbSecurityNone) return;

	m_sd.Init(0,sizeof(SECURITY_DESCRIPTOR));

	if (!::InitializeSecurityDescriptor(m_sd, SECURITY_DESCRIPTOR_REVISION))
		xbThrowEsys(xbHR_LAST,"xbSecurity::InitializeSecurityDescriptor() failure");

	if (type == xbSecurityAll)
	{
		// Set the DACL to allow EVERYONE
		if (!::SetSecurityDescriptorDacl(m_sd, TRUE, NULL, FALSE))
			xbThrowEsys(xbHR_LAST,"xbSecurity::SetSecurityDescriptorDacl() failure");
	}

	if (strRights.IsValid())
		rightsStringParse(strRights);
}

void xbSecurity::SetProcessUserAndGrp(bool bDefaulted)
{
	if (m_sd.IsEmpty()) xbThrowEsys("xbSecurity::SetProcessUserAndGrp() : security descriptor is empty");

	xbTdata<SID> pUserSid;
	xbTdata<SID> pGroupSid;

	GetProcessSids(&pUserSid, &pGroupSid);
	SetUser(pUserSid, bDefaulted);
	SetGroup(pGroupSid, bDefaulted);
}

void xbSecurity::SetThreadUserAndGrp(bool bDefaulted, bool bIfNoneUseProcess)
{
	if (m_sd.IsEmpty()) xbThrowEsys("xbSecurity::SetProcessUserAndGrp() : security descriptor is empty");

	xbTdata<SID> pUserSid;
	xbTdata<SID> pGroupSid;
	
	if (!GetThreadSids(&pUserSid, &pGroupSid, bIfNoneUseProcess?xbOp_Optional:xbOp_Required))
		GetProcessSids(&pUserSid, &pGroupSid);
	SetUser(pUserSid, bDefaulted);
	SetGroup(pGroupSid, bDefaulted);
}

void xbSecurity::SetUser(PSID pUserSid, bool bDefaulted)
{
	if (m_sd.IsEmpty()) xbThrowEsys("xbSecurity::SetUser() : security descriptor is empty");

	// Mark the SD as having no owner
	if (!::SetSecurityDescriptorOwner(m_sd, NULL, bDefaulted?TRUE:FALSE))
		xbThrowEsys(xbHR_LAST,"xbSecurity::SetUser() : SetSecurityDescriptorOwner failure");

	m_userDefaulted = bDefaulted;
	m_userSet = false;
	m_user.Clear();

	// If they asked for no owner don't do the copy
	if (pUserSid == NULL)
	{
		m_userSet = true;
		return;
	}

	// Make a copy of the Sid for the return value
	if (!::IsValidSid(pUserSid))
		xbThrowEsys(xbHR_LAST,"xbSecurity::SetUser() : UserSid invalid");

	DWORD dwSize = ::GetLengthSid(pUserSid);

	m_user.Length = dwSize;
	try
	{
		if (!::CopySid(dwSize, m_user, pUserSid))
			xbThrowEsys(xbHR_LAST,"xbSecurity::SetUser() : CopySid failure");

		xbAssert(IsValidSid(m_user));

		if (!::SetSecurityDescriptorOwner(m_sd, m_user, bDefaulted))
			xbThrowEsys(xbHR_LAST,"xbSecurity::SetUser() : SetSecurityDescriptorOwner failure (2)");
	}
	catch(...)
	{
		m_user.Clear();
		throw;
	}
	m_userSet = true;
}

bool xbSecurity::GetUser(xbTdata<SID> *ppUserSid,bool *pbDefaulted,xbOpMode opm) const
{
	if (m_sd.IsEmpty()) 
	{
		if (opm == xbOp_Required)
			xbThrowEsys("xbSecurity::GetUser() : security descriptor is empty");
		return false;
	}

	if (m_userSet) 
	{
		*ppUserSid = m_user;
		if (pbDefaulted)
			*pbDefaulted = m_userDefaulted;
		return true;;
	}

	BOOL bDefaulted = FALSE;
	PSID pUserSid = NULL;
	if (!GetSecurityDescriptorOwner(m_sd.Ptr(), &pUserSid, &bDefaulted))
	{
		if (opm == xbOp_Required)
			xbThrowEsys(xbHR_LAST,"xbSecurity::GetUser() : GetSecurityDescriptorOwner failure");
		return false;
	}

	DWORD dwSize = ::GetLengthSid(pUserSid);
	m_user.Length = dwSize;
	try
	{
		if (!::CopySid(dwSize, m_user, pUserSid))
			xbThrowEsys(xbHR_LAST,"xbSecurity::GetUser() : CopySid failure");

		xbAssert(IsValidSid(m_user));
	}
	catch(...)
	{
		m_user.Clear();
		throw;
	}
	m_userSet = true;
	m_userDefaulted = bDefaulted?true:false;

	*ppUserSid = m_user;
	if (pbDefaulted)
		*pbDefaulted = m_userDefaulted;
	return true;
}

void xbSecurity::SetGroup(PSID pGroupSid, bool bDefaulted)
{
	if (m_sd.IsEmpty()) xbThrowEsys("xbSecurity::SetGroup() : security descriptor is empty");

	// Mark the SD as having no group
	if (!::SetSecurityDescriptorGroup(m_sd, NULL, bDefaulted?TRUE:FALSE))
		xbThrowEsys(xbHR_LAST,"xbSecurity::SetGroup() : SetSecurityDescriptorGroup failure");

	m_groupDefaulted = bDefaulted;
	m_groupSet = false;
	m_group.Clear();

	// If they asked for no owner don't do the copy
	if (pGroupSid == NULL)
	{
		m_groupSet = true;
		return;
	}

	// Make a copy of the Sid for the return value
	if (!::IsValidSid(pGroupSid))
		xbThrowEsys(xbHR_LAST,"xbSecurity::SetGroup() : GroupSid invalid");

	DWORD dwSize = ::GetLengthSid(pGroupSid);
	m_group.Length = dwSize;

	try
	{
		if (!::CopySid(dwSize, m_group, pGroupSid))
			xbThrowEsys(xbHR_LAST,"xbSecurity::SetGroup() : CopySid failure");

		xbAssert(IsValidSid(m_group));

		if (!::SetSecurityDescriptorGroup(m_sd, m_group, bDefaulted))
			xbThrowEsys(xbHR_LAST,"xbSecurity::SetGroup() : SetSecurityDescriptorGroup failure (2)");
	}
	catch(...)
	{
		m_group.Clear();
		throw;
	}
	m_groupSet = true;
}


bool xbSecurity::GetGroup(xbTdata<SID> *ppGroupSid,bool *pbDefaulted,xbOpMode opm) const
{
	if (m_sd.IsEmpty()) 
	{
		if (opm == xbOp_Required)
			xbThrowEsys("xbSecurity::GetGroup() : security descriptor is empty");
		return false;
	}

	if (m_groupSet) 
	{
		*ppGroupSid = m_group;
		if (pbDefaulted)
			*pbDefaulted = m_groupDefaulted;
		return true;
	}
	
	BOOL bDefaulted = FALSE;
	PSID pGroupSid = NULL;
	if (!GetSecurityDescriptorGroup(m_sd.Ptr(), &pGroupSid, &bDefaulted))
	{
		if (opm == xbOp_Required)
			xbThrowEsys(xbHR_LAST,"xbSecurity::GetGroup() : GetSecurityDescriptorGroup failure");
		return false;
	}

	DWORD dwSize = ::GetLengthSid(pGroupSid);
	m_group.Length = dwSize;
	try
	{
		if (!::CopySid(dwSize, m_group, pGroupSid))
			xbThrowEsys(xbHR_LAST,"xbSecurity::GetGroup() : CopySid failure");

		xbAssert(IsValidSid(m_group));
	}
	catch(...)
	{
		m_group.Clear();
		throw;
	}
	m_groupSet = true;
	m_groupDefaulted = bDefaulted?true:false;

	if (pbDefaulted)
		*pbDefaulted = m_groupDefaulted;
	*ppGroupSid = m_group;
	return true;
}

void xbSecurity::Allow(LPCTSTR pszUserOrGrp, DWORD dwAccessMask, DWORD dwInhMask)
{
	if (m_sd.IsEmpty()) xbThrowEsys("xbSecurity::Allow() : security descriptor is empty");

	AddAccessAllowedACEToACL(&m_dacl, pszUserOrGrp, dwAccessMask, dwInhMask);
	if (!SetSecurityDescriptorDacl(m_sd, TRUE, m_dacl, FALSE))
		xbThrowEsys(xbHR_LAST,"xbSecurity::Allow() : SetSecurityDescriptorDacl failure");
}

void xbSecurity::Deny(LPCTSTR pszUserOrGrp, DWORD dwAccessMask, DWORD dwInhMask)
{
	if (m_sd.IsEmpty()) xbThrowEsys("xbSecurity::Deny() : security descriptor is empty");

	AddAccessDeniedACEToACL(&m_dacl, pszUserOrGrp, dwAccessMask, dwInhMask);
	if (!SetSecurityDescriptorDacl(m_sd, TRUE, m_dacl, FALSE))
		xbThrowEsys(xbHR_LAST,"xbSecurity::Deny() : SetSecurityDescriptorDacl failure");
}

int xbSecurity::Remove(LPCTSTR pszUserOrGrp)
{
	if (m_sd.IsEmpty()
		|| m_dacl.IsEmpty()) xbThrowEsys("xbSecurity::Remove() : security descriptor or DACL is empty");

	xbTdata<ACL> dacl(m_dacl);
	int ret = 0;
	if ((ret = RemoveUserOrGrpFromACL(dacl, pszUserOrGrp)) <= 0) return 0;

	if (!SetSecurityDescriptorDacl(m_sd, TRUE, dacl, FALSE))
		xbThrowEsys(xbHR_LAST,"xbSecurity::Remove() : SetSecurityDescriptorDacl failure");
	m_dacl = dacl;
	return ret;
}

bool xbSecurity::GetProcessSids(xbTdata<SID> * ppUserSid, xbTdata<SID>* ppGroupSid,xbOpMode opm)
{
	HANDLE hToken = NULL;
	if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
	{
		if (opm == xbOp_Optional && ::GetLastError() == ERROR_NO_TOKEN)
			return false;
		xbThrowEsys(xbHR_LAST,"xbSecurity::GetProcessSids() : OpenProcessToken failure");
	}
	try
	{
		GetTokenSids(hToken, ppUserSid, ppGroupSid);
	}
	catch(...)
	{
		CloseHandle(hToken);
		throw;
	}
	CloseHandle(hToken);
	return true;
}

bool xbSecurity::GetThreadSids(xbTdata<SID>* ppUserSid, xbTdata<SID>* ppGroupSid, xbOpMode opm, bool bOpenAsSelf)
{
	HANDLE hToken = NULL;
	if (!::OpenThreadToken(::GetCurrentThread(), TOKEN_QUERY, bOpenAsSelf?TRUE:FALSE, &hToken))
	{
		if (opm == xbOp_Optional && ::GetLastError() == ERROR_NO_TOKEN)
			return false;
		xbThrowEsys(xbHR_LAST,"xbSecurity::GetThreadSids() : OpenThreadToken failure");
	}

	try
	{
		GetTokenSids(hToken, ppUserSid, ppGroupSid);
	}
	catch(...)
	{
		CloseHandle(hToken);
		throw;
	}
	CloseHandle(hToken);
	return true;
}

void xbSecurity::GetTokenSids(HANDLE hToken, xbTdata<SID>* ppUserSid, xbTdata<SID>* ppGroupSid)
{
	PTOKEN_PRIMARY_GROUP ptkGroup = NULL;

	if (ppUserSid)
	{
		// Get length required for TokenUser by specifying buffer length of 0
		DWORD dwSize = 0;
		::GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
		if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || dwSize == 0)
			xbThrowEsys(xbHR_LAST,"xbSecurity::GetTokenSids() : GetTokenInformation failure (getting size)");

		xbTdata<TOKEN_USER>	ptkUser;
		ptkUser.Length = dwSize;

		// Get Sid of process token.
		if (!::GetTokenInformation(hToken, TokenUser, ptkUser, dwSize, &dwSize))
			xbThrowEsys(xbHR_LAST,"xbSecurity::GetTokenSids() : GetTokenInformation failure");

		// Make a copy of the Sid for the return value
		dwSize = ::GetLengthSid(ptkUser->User.Sid);

		xbTdata<SID> pSid;
		pSid.Length = dwSize;
		if (!::CopySid(dwSize, pSid, ptkUser->User.Sid))
			xbThrowEsys(xbHR_LAST,"xbSecurity::GetTokenSids() : CopySid failure");

		xbAssert(IsValidSid(pSid));

		ppUserSid->Attach(&pSid);
	}
	if (ppGroupSid)
	{
		// Get length required for TokenPrimaryGroup by specifying buffer length of 0
		DWORD dwSize = 0;
		::GetTokenInformation(hToken, TokenPrimaryGroup, NULL, 0, &dwSize);
		if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || dwSize == 0)
			xbThrowEsys(xbHR_LAST,"xbSecurity::GetTokenSids() : GetTokenInformation failure (getting size)");

		xbTdata<TOKEN_PRIMARY_GROUP>	ptkGroup;
		ptkGroup.Length = dwSize;

		// Get Sid of process token.
		if (!::GetTokenInformation(hToken, TokenPrimaryGroup, ptkGroup, dwSize, &dwSize))
			xbThrowEsys(xbHR_LAST,"xbSecurity::GetTokenSids() : GetTokenInformation failure");

		// Make a copy of the Sid for the return value
		dwSize = ::GetLengthSid(ptkGroup->PrimaryGroup);

		xbTdata<SID> pSid;
		pSid.Length = dwSize;
		if (!CopySid(dwSize, pSid, ptkGroup->PrimaryGroup))
			xbThrowEsys(xbHR_LAST,"xbSecurity::GetTokenSids() : CopySid failure");

		xbAssert(IsValidSid(pSid));

		ppGroupSid->Attach(&pSid);
	}
}


void xbSecurity::GetCurrentUserSID(xbTdata<SID> *ppSid)
{
	GetProcessSids(ppSid,NULL);
}


bool xbSecurity::GetUserOrGrpSID(LPCTSTR pszUserOrGrp, xbTdata<SID> *ppSid,xbString *pDomain,PSID_NAME_USE pSidType,xbOpMode opm)
{
	DWORD dwDomainSize = 0;
	DWORD dwSize = 0;
	SID_NAME_USE snu;

	// Call to get size info for alloc
	LookupAccountName(NULL, pszUserOrGrp, NULL, &dwSize, NULL, &dwDomainSize, &snu);
	if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || dwSize == 0)
	{
		if (opm == xbOp_Required)
			xbThrow xbEapp(xbHR_LAST,"User or group '"+xbString(pszUserOrGrp)+"' unknown (LookupAccountName failed)");
		return false;
	}

	xbTdata<SID>	pSid;
	pSid.Length = dwSize;

	xbString domain;

	if (!LookupAccountName(NULL, pszUserOrGrp, pSid, &dwSize, 
		xbStrExp(&domain,dwDomainSize), &dwDomainSize, &snu))
		xbThrowEsys(xbHR_LAST,"xbSecurity::GetUserOrGrpSID() : LookupAccountName failure");

	ppSid->Attach(&pSid);

	if (pSidType)
		*pSidType = snu;
	if (pDomain)
		*pDomain = domain;

	return true;
}

void xbSecurity::CopyFrom(const SECURITY_DESCRIPTOR *pSelfRelativeSD)
{
	if (!pSelfRelativeSD) 
	{
		Initialize(xbSecurityNone);
		return;
	}

	Initialize(xbSecurityPrivate);

	try
	{
		for(;;)
		{
			PACL    pDACL = NULL;
			BOOL    bDACLPresent = FALSE;
			BOOL    bDefaulted = FALSE;
			// get the existing DACL.
			if (!GetSecurityDescriptorDacl((SECURITY_DESCRIPTOR*)pSelfRelativeSD, &bDACLPresent, &pDACL, &bDefaulted))
				xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFrom(SECURITYDESCRIPTOR*) : GetSecurityDescriptorDacl failure");

			if (!bDACLPresent) break;

			if (pDACL)
			{
				// allocate new DACL.
				m_dacl.Length = pDACL->AclSize;

				// initialize the DACL
				if (!::InitializeAcl(m_dacl, pDACL->AclSize, ACL_REVISION))
					xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFrom(SECURITYDESCRIPTOR*) : InitializeAcl failure (dacl)");

				// copy the ACES
				for (int i = 0; i < pDACL->AceCount; i++)
				{
					ACCESS_ALLOWED_ACE* pACE = NULL; 
					if (!::GetAce(pDACL, i, (void **)&pACE))
						xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFrom(SECURITYDESCRIPTOR*) : GetAce failure (dacl)");

					if (pACE->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
					{
						if (!::AddAccessAllowedAceEx(m_dacl, ACL_REVISION, pACE->Header.AceFlags, pACE->Mask, (PSID)&(pACE->SidStart)))
							xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFrom(SECURITYDESCRIPTOR*) : AddAccessAllowedAceEx failure (dacl)");
						continue;
					}
					if (pACE->Header.AceType == ACCESS_DENIED_ACE_TYPE)
					{
						ACCESS_DENIED_ACE* pDACE = (ACCESS_DENIED_ACE*)pACE; 
						if (!::AddAccessDeniedAceEx(m_dacl, ACL_REVISION, pDACE->Header.AceFlags, pDACE->Mask, (PSID)&(pDACE->SidStart)))
							xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFrom(SECURITYDESCRIPTOR*) : AddAccessDeniedAceEx failure (dacl)");
						continue;
					}
				}

				if (!IsValidAcl(m_dacl))
					xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFrom(SECURITYDESCRIPTOR*) : Making DACL failure");
			}

			// set the DACL
			if (!SetSecurityDescriptorDacl(m_sd, pDACL ? TRUE : FALSE, m_dacl, bDefaulted))
				xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFrom(SECURITYDESCRIPTOR*) : SetSecurityDescriptorDacl failure");
			
			break;
		}
		for(;;)
		{
			PACL    pSACL = NULL;
			BOOL    bSACLPresent = FALSE;
			BOOL    bDefaulted = FALSE;

			// get the existing SACL.
			if (!GetSecurityDescriptorSacl((SECURITY_DESCRIPTOR*)pSelfRelativeSD, &bSACLPresent, &pSACL, &bDefaulted))
				xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFrom(SECURITYDESCRIPTOR*) : GetSecurityDescriptorSacl failure");
			if (!bSACLPresent) break;
			if (pSACL)
			{
				// allocate new SACL.
				m_sacl.Length = pSACL->AclSize;

				// initialize the SACL
				if (!InitializeAcl(m_sacl, pSACL->AclSize, ACL_REVISION))
					xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFrom(SECURITYDESCRIPTOR*) : InitializeAcl failure (sacl)");

				// copy the ACES
				for (int i = 0; i < pSACL->AceCount; i++)
				{
					ACCESS_ALLOWED_ACE* pACE = NULL; 
					if (!::GetAce(pSACL, i, (void **)&pACE))
						xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFrom(SECURITYDESCRIPTOR*) : GetAce failure (sacl)");

					if (pACE->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
					{
						if (!::AddAccessAllowedAceEx(m_dacl, ACL_REVISION, pACE->Header.AceFlags, pACE->Mask, (PSID)&(pACE->SidStart)))
							xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFrom(SECURITYDESCRIPTOR*) : AddAccessAllowedAceEx failure (sacl)");
						continue;
					}
					if (pACE->Header.AceType == ACCESS_DENIED_ACE_TYPE)
					{
						ACCESS_DENIED_ACE* pDACE = (ACCESS_DENIED_ACE*)pACE; 
						if (!::AddAccessDeniedAceEx(m_dacl, ACL_REVISION, pDACE->Header.AceFlags, pDACE->Mask, (PSID)&(pDACE->SidStart)))
							xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFrom(SECURITYDESCRIPTOR*) : AddAccessDeniedAceEx failure (sacl)");
						continue;
					}
				}

				if (!IsValidAcl(m_sacl))
					xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFrom(SECURITYDESCRIPTOR*) : Making SACL failure");
			}

			// set the SACL
			if (!SetSecurityDescriptorSacl(m_sd, pSACL ? TRUE : FALSE, m_sacl, bDefaulted))
				xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFrom(SECURITYDESCRIPTOR*) : SetSecurityDescriptorSacl failure");

			break;
		}

		BOOL bDefaulted;
		PSID pUserSid;
		if (!GetSecurityDescriptorOwner((SECURITY_DESCRIPTOR*)pSelfRelativeSD, &pUserSid, &bDefaulted))
			xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFrom(SECURITYDESCRIPTOR*) : GetSecurityDescriptorOwner failure");

		SetUser(pUserSid, bDefaulted?true:false);

		PSID pGroupSid;
		if (!GetSecurityDescriptorGroup((SECURITY_DESCRIPTOR*)pSelfRelativeSD, &pGroupSid, &bDefaulted))
			xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFrom(SECURITYDESCRIPTOR*) : GetSecurityDescriptorGroup failure");

		SetGroup(pGroupSid, bDefaulted?true:false);
	}
	catch(...)
	{
		Empty();
		throw;
	}
}

bool xbSecurity::Check(xbOpMode opm) const
{
	if (IsEmpty())
	{
		if (opm == xbOp_Required) xbThrowEsys("xbSecurity::Check() : security descriptor is empty");
		return false;
	}
	if (!IsValidSecurityDescriptor(m_sd.Ptr()))
	{
		if (opm == xbOp_Required) xbThrowEsys("xbSecurity::Check() : security descriptor is invalid");
		return false;
	}
	return true;
}

void xbSecurity::CopyFromObject(HANDLE hObject)
{
	xbTdata<SECURITY_DESCRIPTOR> pSD;

	DWORD dwSize = 0;
	::GetKernelObjectSecurity(hObject, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
		DACL_SECURITY_INFORMATION, NULL, 0, &dwSize);
	if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || dwSize == 0)
		xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFromObject(HANDLE hObject) : GetKernelObjectSecurity failure (getting size)");
	pSD.Length = dwSize;

	if (!::GetKernelObjectSecurity(hObject, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
		DACL_SECURITY_INFORMATION, pSD, dwSize, &dwSize))
		xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFromObject(HANDLE hObject) : GetKernelObjectSecurity failure");

	CopyFrom(pSD);
}

void xbSecurity::CopyToObject(HANDLE hObject) const
{
	Check(xbOp_Required);
	const SECURITY_DESCRIPTOR *pSD = m_sd;
	if (!::SetKernelObjectSecurity(hObject,OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
		DACL_SECURITY_INFORMATION,(SECURITY_DESCRIPTOR*)pSD))
		xbThrowEsys(xbHR_LAST,"xbSecurity::CopyToObject(HANDLE hObject) : SetKernelObjectSecurity failure");
}

bool xbSecurity::CopyFromFSobject(LPCSTR pFileName,xbOpMode opm)
{
	xbTdata<SECURITY_DESCRIPTOR> pSD;

	DWORD dwSize = 0;
	::GetFileSecurity(pFileName,OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
		DACL_SECURITY_INFORMATION, NULL, 0, &dwSize);
	if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || dwSize == 0)
	{
		if (opm == xbOp_Required)
			xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFromFSobject("+xbString(pFileName)+") : GetFileSecurity failure (getting size)");
		return false;
	}
	pSD.Length = dwSize;

	if (!::GetFileSecurity(pFileName, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
		DACL_SECURITY_INFORMATION, pSD, dwSize, &dwSize))
		xbThrowEsys(xbHR_LAST,"xbSecurity::CopyFromFSobject("+xbString(pFileName)+") : GetFileSecurity failure");

	CopyFrom(pSD);
	return true;
}

bool xbSecurity::CopyToFSobject(LPCSTR pFileName,xbOpMode opm) const
{
	Check(xbOp_Required);
	const SECURITY_DESCRIPTOR *pSD = m_sd;
	if (!::SetFileSecurity(pFileName,OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
		DACL_SECURITY_INFORMATION,(SECURITY_DESCRIPTOR*)pSD))
	{
		if (opm == xbOp_Required)
			xbThrowEsys(xbHR_LAST,"xbSecurity::CopyToFSobject("+xbString(pFileName)+") : SetFileSecurity failure");
		return false;
	}
	return true;
}

void xbSecurity::CopyACL(PACL pDest, PACL pSrc)
{
	if (pSrc == NULL) return;


	ACL_SIZE_INFORMATION aclSizeInfo;
	if (!GetAclInformation(pSrc, (LPVOID) &aclSizeInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation))
		xbThrowEsys(xbHR_LAST,"xbSecurity::CopyACL() : GetAclInformation failure");

	// Copy all of the ACEs to the new ACL
	for (UINT i = 0; i < aclSizeInfo.AceCount; i++)
	{
		LPVOID pAce = NULL;
		if (!GetAce(pSrc, i, &pAce))
			xbThrowEsys(xbHR_LAST,"xbSecurity::CopyACL() : GetAce failure");

		if (!AddAce(pDest, ACL_REVISION, 0xffffffff, pAce, ((ACE_HEADER *) pAce)->AceSize))
			xbThrowEsys(xbHR_LAST,"xbSecurity::CopyACL() : AddAce failure");
	}
}

void xbSecurity::AddAccessDeniedACEToACL(xbTdata<ACL> *ppAcl, LPCTSTR pszUserOrGrp, DWORD dwAccessMask, DWORD dwInhMask)
{
	xbTdata<SID> guSID;
	GetUserOrGrpSID(pszUserOrGrp, &guSID);

	ACL_SIZE_INFORMATION aclSizeInfo;
	aclSizeInfo.AclBytesInUse = 0;

	if (ppAcl->IsValid()
		&& !::GetAclInformation(*ppAcl, (LPVOID) &aclSizeInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation))
		xbThrowEsys(xbHR_LAST,"xbSecurity::AddAccessDeniedACEToACL() : GetAclInformation failure");

	int aclSize = aclSizeInfo.AclBytesInUse + sizeof(ACL) + sizeof(ACCESS_DENIED_ACE) + GetLengthSid(guSID) - sizeof(DWORD);

	xbTdata<ACL> newAcl;
	newAcl.Length = aclSize;

	if (!::InitializeAcl(newAcl, aclSize, ACL_REVISION))
		xbThrowEsys(xbHR_LAST,"xbSecurity::AddAccessDeniedACEToACL() : InitializeAcl failure");

	if (dwInhMask)
	{
		if (!::AddAccessDeniedAceEx(newAcl, ACL_REVISION2, dwInhMask, dwAccessMask, guSID))
			xbThrowEsys(xbHR_LAST,"xbSecurity::AddAccessDeniedACEToACL() : AddAccessDeniedAce failure");
	}
	else
	{
		if (!::AddAccessDeniedAce(newAcl, ACL_REVISION2, dwAccessMask, guSID))
			xbThrowEsys(xbHR_LAST,"xbSecurity::AddAccessDeniedACEToACL() : AddAccessDeniedAce failure");
	}
	CopyACL(newAcl, *ppAcl);

	ppAcl->Attach(&newAcl);
}


void xbSecurity::AddAccessAllowedACEToACL(xbTdata<ACL> *ppAcl, LPCTSTR pszUserOrGrp, DWORD dwAccessMask, DWORD dwInhMask)
{
	xbTdata<SID> guSID;
	GetUserOrGrpSID(pszUserOrGrp, &guSID);

	ACL_SIZE_INFORMATION aclSizeInfo;
	aclSizeInfo.AclBytesInUse = 0;

	if (ppAcl->IsValid()
		&& !::GetAclInformation(*ppAcl, (LPVOID) &aclSizeInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation))
		xbThrowEsys(xbHR_LAST,"xbSecurity::AddAccessAllowedACEToACL() : GetAclInformation failure");

	int aclSize = aclSizeInfo.AclBytesInUse + sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(guSID) - sizeof(DWORD);

	xbTdata<ACL> newAcl;
	newAcl.Length = aclSize;

	if (!::InitializeAcl(newAcl, aclSize, ACL_REVISION))
		xbThrowEsys(xbHR_LAST,"xbSecurity::AddAccessAllowedACEToACL() : InitializeAcl failure");

	CopyACL(newAcl, *ppAcl);

	if (dwInhMask)
	{
		if (!::AddAccessAllowedAceEx(newAcl, ACL_REVISION2, dwInhMask, dwAccessMask, guSID))
			xbThrowEsys(xbHR_LAST,"xbSecurity::AddAccessAllowedACEToACL() : AddAccessAllowedAceEx failure");
	}
	else
	{
		if (!::AddAccessAllowedAce(newAcl, ACL_REVISION2, dwAccessMask, guSID))
			xbThrowEsys(xbHR_LAST,"xbSecurity::AddAccessAllowedACEToACL() : AddAccessAllowedAce failure");
	}
	ppAcl->Attach(&newAcl);
}


int xbSecurity::RemoveUserOrGrpFromACL(PACL pAcl, LPCTSTR pszUserOrGrp)
{
	if (!pAcl)
		xbThrowEsys(xbHR_LAST,"xbSecurity::RemoveUserOrGrpFromACL() : pAcl is NULL");
	xbTdata<SID> guSID;
	GetUserOrGrpSID(pszUserOrGrp, &guSID);

	ACL_SIZE_INFORMATION aclSizeInfo;
	if (!::GetAclInformation(pAcl, (LPVOID) &aclSizeInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation))
		xbThrowEsys(xbHR_LAST,"xbSecurity::RemoveUserOrGrpFromACL() : GetAclInformation failure");

	int nCnt = 0;
	for (ulong i = 0; i < aclSizeInfo.AceCount; i++)
	{
		LPVOID ace = NULL;
		if (!::GetAce(pAcl, i, &ace))
			xbThrowEsys(xbHR_LAST,"xbSecurity::RemoveUserOrGrpFromACL() : GetAce failure");

		ACE_HEADER	*aceHeader = (ACE_HEADER *) ace;

		switch(aceHeader->AceType)
		{
		case ACCESS_ALLOWED_ACE_TYPE :
			{
				ACCESS_ALLOWED_ACE *accessAllowedAce = (ACCESS_ALLOWED_ACE *) ace;

				if (!::EqualSid(guSID, (PSID) &accessAllowedAce->SidStart)) continue;
				break;
			} 
		case ACCESS_DENIED_ACE_TYPE :
			{
				ACCESS_DENIED_ACE *accessDeniedAce = (ACCESS_DENIED_ACE *) ace;

				if (!::EqualSid(guSID, (PSID) &accessDeniedAce->SidStart)) continue;
				break;
			} 
		case SYSTEM_AUDIT_ACE_TYPE :
			{
				SYSTEM_AUDIT_ACE *systemAuditAce = (SYSTEM_AUDIT_ACE *) ace;

				if (!EqualSid(guSID, (PSID) &systemAuditAce->SidStart)) continue;
				break;
			}
		default:
			continue;
		}
		::DeleteAce(pAcl, i);
		nCnt++;
	}
	return nCnt;
}

void xbSecurity::SetProcessPrivilege(LPCTSTR Privilege, bool bEnable)
{
	HANDLE hToken = NULL;
	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
		xbThrowEsys(xbHR_LAST,"xbSecurity::SetProcessPrivilege() : OpenProcessToken failure");
	try
	{
		SetPrivilege(hToken,Privilege,bEnable);
	}
	catch(...)
	{
		CloseHandle(hToken);
		throw;
	}
}

void xbSecurity::SetThreadPrivilege(LPCTSTR Privilege, bool bEnable, bool bOpenAsSelf)
{
	HANDLE hToken = NULL;
	if (!::OpenThreadToken(::GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, bOpenAsSelf?TRUE:FALSE, &hToken))
		xbThrowEsys(xbHR_LAST,"xbSecurity::SetThreadPrivilege() : OpenThreadToken failure");
	try
	{
		SetPrivilege(hToken,Privilege,bEnable);
	}
	catch(...)
	{
		CloseHandle(hToken);
		throw;
	}
}

void xbSecurity::SetPrivilege(HANDLE hToken, LPCTSTR privilege, bool bEnable)
{
	LUID   luid;
	if (!LookupPrivilegeValue(NULL, privilege, &luid ))
		xbThrowEsys(xbHR_LAST,"xbSecurity::SetPrivilege() : LookupPrivilegeValue failure");

	TOKEN_PRIVILEGES tp;
	tp.PrivilegeCount = 1;
	tp.Privileges[0].Luid = luid;
	tp.Privileges[0].Attributes = 0;

	TOKEN_PRIVILEGES tpPrevious;
	DWORD  cbPrevious = sizeof(TOKEN_PRIVILEGES);
	if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &tpPrevious, &cbPrevious))
		xbThrowEsys(xbHR_LAST,"xbSecurity::SetPrivilege() : AdjustTokenPrivileges failure");

	tpPrevious.PrivilegeCount = 1;
	tpPrevious.Privileges[0].Luid = luid;

	if (bEnable)
		tpPrevious.Privileges[0].Attributes |= SE_PRIVILEGE_ENABLED;
	else
		tpPrevious.Privileges[0].Attributes &= ~(SE_PRIVILEGE_ENABLED);

	if (!AdjustTokenPrivileges(hToken, FALSE, &tpPrevious, cbPrevious, NULL, NULL))
		xbThrowEsys(xbHR_LAST,"xbSecurity::SetPrivilege() : AdjustTokenPrivileges failure (2)");
}

void xbSecurity::regBehaviour(xbSecurityType &type, xbString &strRights,LPCSTR pRights)
{
	if (type == xbSecurityNone)
		return;
	if (pRights)
	{
		rightsStringSecuTypeParse(pRights, type, strRights);
		if (strRights.IsValid()||type == xbSecurityNone)
			return;
	}

	xbString sectype;
	switch(type)
	{
	case xbSecurityPrivate:
		sectype = "private";
		break;
	case xbSecurityAll:
		sectype = "all";
		break;
	case xbSecurityDflt:
		sectype = "default";
		break;
	case xbSecurityInherited:
		sectype = "inherited";
		break;
	case xbSecurityNone:
		return;
	default:
		xbThrow xbEsys("xbSecurity::regBehaviour: invalid xbSecurityType");
	}

	if (g_xbCfgEngine.isInConfigLoad())
		return;
	xbString str(g_xbCfgEngine.GetSettingString("fsrights."+xbGetFileName()+"."+sectype));
	if (str.IsEmpty())
		str=g_xbCfgEngine.GetSettingString("fsrights."+sectype);
	if (str.IsValid())
		rightsStringSecuTypeParse(str, type, strRights);
}

void xbSecurity::rightsStringSecuTypeParse(const xbString &rights,xbSecurityType &type, xbString &strRights)
{
	xbString str,strStaying;
	rights.GetToken(0,";",&str,&strStaying);
	for(;;)
	{
		if (str.IsEmpty())
		{
			if (strStaying.IsValid())
				type = xbSecurityPrivate;
			break;
		}
		if (str.CompareNoCase("none")==0)
		{
			type = xbSecurityNone;
			return;
		}
		if (str.CompareNoCase("all")==0)
		{
			type = xbSecurityAll;
			break;
		}
		if (str.CompareNoCase("default")==0)
		{
			type = xbSecurityDflt;
			break;
		}
		if (str.CompareNoCase("private")==0)
		{
			type = xbSecurityPrivate;
			break;
		}
		if (str.CompareNoCase("inherited")==0)
		{
			type = xbSecurityInherited;
			break;
		}
		type = xbSecurityPrivate;
		strRights = rights;
		return;
	}
	strRights = strStaying;
}
/*
thread[=]
process[=]
user=<name>[=]
group=<name>[=]
+user/group=attrs
-user/group=attrs
-user/group
*/
void xbSecurity::rightsStringParse(const xbString &strRights)
{
	xbString str;
	int pos=0;
	while((pos = strRights.GetToken(pos,";",&str))>0)
	{
		if (str.IsEmpty() || str.First() == '!')
			continue;

		xbString sname,sval;
		str.GetToken(0,"=",&sname,&sval);
		if (sname.IsEmpty())
			xbThrowEarg("Invalid setting ('"+str+"') in file security definition","Definition: '"+strRights+"'");
		if (sval.IsEmpty())
		{
			if (sname.IsValid() && sname.First()=='-')
			{
				Remove(sname);
				continue;
			}
			if (sname.CompareNoCase("thread")==0)
			{
				SetThreadUserAndGrp(false);
				continue;
			}
			if (sname.CompareNoCase("thread=")==0)
			{
				SetThreadUserAndGrp(true);
				continue;
			}
			if (sname.CompareNoCase("process")==0)
			{
				SetProcessUserAndGrp(true);
				continue;
			}
			if (sname.CompareNoCase("process=")==0)
			{
				SetProcessUserAndGrp(true);
				continue;
			}

			xbThrowEarg("Unknown setting ('"+sname+"') in file security definition","Definition: '"+strRights+"'");
		}
		if (sname.First()=='+')
		{
			xbString sattr,sinh;
			sval.GetToken(0,":",&sattr,&sinh);
			Allow(sname.Mid(1),rightsAttrStringParse(sattr),rightsInhStringParse(sinh));
			continue;
		}
		if (sname.First()=='-')
		{
			xbString sattr,sinh;
			sval.GetToken(0,":",&sattr,&sinh);
			Deny(sname.Mid(1),rightsAttrStringParse(sattr),rightsInhStringParse(sinh));
			continue;
		}

		if (sname.CompareNoCase("user")==0)
		{
			bool bDefaulted=false;
			if (sval.Last()=='=')
			{
				bDefaulted=true;
				sval.Length=sval.Length-1;
			}
			xbTdata<SID> userSid;
			GetUserOrGrpSID(sval, &userSid);
			SetUser(userSid,bDefaulted);
			continue;
		}

		if (sname.CompareNoCase("group")==0)
		{
			bool bDefaulted=false;
			if (sval.Last()=='=')
			{
				bDefaulted=true;
				sval.Length=sval.Length-1;
			}
			xbTdata<SID> userSid;
			GetUserOrGrpSID(sval, &userSid);
			SetGroup(userSid,bDefaulted);
			continue;
		}

		xbThrowEarg("Unknown setting ('"+sname+"') in file security definition","Definition: '"+strRights+"'");
	}
}

xbString xbSecurity::GetSIDname(PSID pSid, xbString *pDomain,PSID_NAME_USE pSidType)
{
	DWORD dwNameSize = 0;
	DWORD dwDomainSize = 0;
	SID_NAME_USE snu;

	// Call to get size info for alloc
	LookupAccountSid(NULL, pSid, NULL, &dwNameSize, NULL, &dwDomainSize, &snu);
	if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || dwNameSize == 0)
		xbThrowEsys(xbHR_LAST,"xbSecurity::GetSIDname() : LookupAccountSid failure (getting size)");

	xbString name,domain;

	if (!LookupAccountSid(NULL, pSid, xbStrExp(&name,dwNameSize), &dwNameSize, 
		xbStrExp(&domain,dwDomainSize), &dwDomainSize, &snu))
		xbThrowEsys(xbHR_LAST,"xbSecurity::GetSIDname() : LookupAccountSid failure");

	if (pDomain)
		*pDomain = domain;
	else if (domain.IsValid())
		name = domain+"\\"+name;

	if (pSidType)
		*pSidType = snu;

	return name;
}
xbString xbSecurity::GetUserName(xbOpMode opm) const
{
	xbTdata<SID> sid;
	if (!GetUser(&sid,NULL,opm))
		return xbString();
	return GetSIDname(sid);
}

xbString xbSecurity::GetGroupName(xbOpMode opm) const
{
	xbTdata<SID> sid;
	if (!GetGroup(&sid,NULL,opm))
		return xbString();
	return GetSIDname(sid);
}

xbString xbSecurity::rightStrings() const
{
	xbString ret,tmp;
	if ((tmp = GetUserName(xbOp_Optional)).IsValid())
		ret = "user="+tmp;
	if ((tmp = GetGroupName(xbOp_Optional)).IsValid())
	{
		if (ret.IsValid())
			ret += ";";
		ret += "group="+tmp;
	}
	if ((tmp = rightStringsAcePart()).IsValid())
	{
		if (ret.IsValid())
			ret += ";";
		ret += tmp;
	}
	return ret;
}

xbString xbSecurity::rightStringsAcePart() const
{
	if (m_dacl.IsEmpty())
		return xbString();

	const ACL *pAcl = m_dacl;
	ACL_SIZE_INFORMATION aclSizeInfo;
	if (!::GetAclInformation((PACL)pAcl, (LPVOID) &aclSizeInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation))
		xbThrowEsys(xbHR_LAST,"xbSecurity::rightStringsAcePart() : GetAclInformation failure");

	xbString ret;
	int nCnt = 0;
	for (ulong i = 0; i < aclSizeInfo.AceCount; i++)
	{
		LPVOID ace = NULL;
		if (!::GetAce((PACL)pAcl, i, &ace))
			xbThrowEsys(xbHR_LAST,"xbSecurity::rightStringsAcePart() : GetAce failure");

		ACE_HEADER	*aceHeader = (ACE_HEADER *) ace;

		switch(aceHeader->AceType)
		{
		case ACCESS_ALLOWED_ACE_TYPE :
			{
				ACCESS_ALLOWED_ACE *accessAllowedAce = (ACCESS_ALLOWED_ACE *) ace;
				if (ret.IsValid())
					ret += ";";

				ret += "+"+GetSIDname((PSID) &accessAllowedAce->SidStart)+"="
					+rightsAttr(accessAllowedAce->Mask);
				if (accessAllowedAce->Header.AceFlags)
					ret += ":"+rightsInhType(accessAllowedAce->Header.AceFlags);

				break;
			} 
		case ACCESS_DENIED_ACE_TYPE :
			{
				ACCESS_DENIED_ACE *accessDeniedAce = (ACCESS_DENIED_ACE *) ace;
				if (ret.IsValid())
					ret += ";";

				ret += "-"+GetSIDname((PSID) &accessDeniedAce->SidStart)+"="
					+rightsAttr(accessDeniedAce->Mask);
				if (accessDeniedAce->Header.AceFlags)
					ret += ":"+rightsInhType(accessDeniedAce->Header.AceFlags);
				break;
			} 
		default:
			continue;
		}
	}
	
	return ret;
}

ulong xbSecurity::rightsAttrStringParse(const xbString &strAttrs)
{
	ulong ret=0;
	LPCSTR pPtr = strAttrs;
	LPCSTR pPtrEnd = pPtr + strAttrs.Length;
	while(pPtr < pPtrEnd)
	{
		char ch = *(pPtr++);
/*
		if (ch == 'r')
		{
			ret += GENERIC_READ;
			continue;
		}
		if (ch == 'w')
		{
			ret += GENERIC_WRITE;
			continue;
		}
		if (ch == 'x')
		{
			ret += GENERIC_EXECUTE;
			continue;
		}
		if (ch == 'A')
		{
			ret += GENERIC_ALL;
			continue;
		}
*/
		if (ch == 'A')
		{
			ret |= FILE_ALL_ACCESS;
			continue;
		}
		if (ch == 'R')
		{
			ret |= FILE_GENERIC_READ;
			continue;
		}
		if (ch == 'W')
		{
			ret |= FILE_GENERIC_WRITE;
			continue;
		}
		if (ch == 'X')
		{
			ret |= FILE_GENERIC_EXECUTE;
			continue;
		}
		if (ch == 'r')
		{
			ret |= FILE_READ_DATA;
			continue;
		}
		if (ch == 'w')
		{
			ret |= FILE_WRITE_DATA;
			continue;
		}
		if (ch == 'x')
		{
			ret |= FILE_EXECUTE;
			continue;
		}
		if (ch == 'g')
		{
			ret |= FILE_APPEND_DATA;
			continue;
		}
		if (ch == 'e')
		{
			ret |= FILE_READ_EA;
			continue;
		}
		if (ch == 'E')
		{
			ret |= FILE_WRITE_EA;
			continue;
		}
		if (ch == 't')
		{
			ret |= FILE_READ_ATTRIBUTES;
			continue;
		}
		if (ch == 'T')
		{
			ret |= FILE_WRITE_ATTRIBUTES;
			continue;
		}
		if (ch == 's')
		{
			ret |= DELETE;
			continue;
		}
		if (ch == 'S')
		{
			ret |= FILE_DELETE_CHILD;
			continue;
		}
		if (ch == 'O')
		{
			ret |= WRITE_OWNER;
			continue;
		}
		if (ch == 'p')
		{
			ret |= READ_CONTROL;
			continue;
		}
		if (ch == 'P')
		{
			ret |= WRITE_DAC;
			continue;
		}
		if (ch == 'm')
		{
			ret |= MAXIMUM_ALLOWED;
			continue;
		}
		if (ch == 'z')
		{
			ret |= SYNCHRONIZE;
			continue;
		}
		if (ch == '+')
		{
			ulong nb;
			LPCSTR pStop=NULL;
			if (xbStrToUlong(pPtr,&nb,0,&pStop)<=0 || !pStop)
				xbThrowEarg("Invalid attribute ('"+xbStringPad(1,ch)+"') in file security definition","Definition attributes: '"+strAttrs+"', syntax: +Num, example: +0x2 or +2b");
			pPtr = pStop;
			if (*pPtr == 'b')
			{
				pPtr++;
				if (nb > 31)
					xbThrowEarg("Attribute ('"+xbStringPad(1,ch)+"') with invalid bit number in file security definition","Definition attributes: '"+strAttrs+"', syntax: +Xb where X, a bit number, must be between 0 and 31, example: +31b");
				nb = 1 << nb;
			}

			ret |= nb;
			continue;
		}
		if (ch == '-')
		{
			ulong nb;
			LPCSTR pStop=NULL;
			if (xbStrToUlong(pPtr,&nb,0,&pStop)<=0 || !pStop)
				xbThrowEarg("Invalid attribute ('"+xbStringPad(1,ch)+"') in file security definition","Definition attributes: '"+strAttrs+"', syntax: -Num, example: -0x2 or -2b");
			pPtr = pStop;
			if (*pPtr == 'b')
			{
				pPtr++;
				if (nb > 31)
					xbThrowEarg("Attribute ('"+xbStringPad(1,ch)+"') with invalid bit number in file security definition","Definition attributes: '"+strAttrs+"', syntax: -Xb where X, a bit number, must be between 0 and 31, example: -31b");
				nb = 1 << nb;
			}

			ret &= ~nb;
			continue;
		}
		xbThrowEarg("Unknown attribute ('"+xbStringPad(1,ch)+"') in file security definition","Definition attributes: '"+strAttrs+"'");
	}
	return ret;
}

/*
CONTAINER_INHERIT_ACE The ACE is inherited by container objects. 
INHERIT_ONLY_ACE The ACE does not apply to the object to which the access control list (ACL) is assigned, but it can be inherited by child objects. 
INHERITED_ACE Indicates an inherited ACE. This flag allows operations that change the security on a tree of objects to modify inherited ACEs while not changing ACEs that were directly applied to the object. 
NO_PROPAGATE_INHERIT_ACE The OBJECT_INHERIT_ACE and CONTAINER_INHERIT_ACE bits are not propagated to an inherited ACE. 
OBJECT_INHERIT_ACE 
*/
ulong xbSecurity::rightsInhStringParse(const xbString &strAttrs)
{
	ulong ret=0;
	LPCSTR pPtr = strAttrs;
	LPCSTR pPtrEnd = pPtr + strAttrs.Length;
	while(pPtr < pPtrEnd)
	{
		char ch = *(pPtr++);
		if (ch == 'c')
		{
			ret |= CONTAINER_INHERIT_ACE;
			continue;
		}
		if (ch == 'I')
		{
			ret |= INHERIT_ONLY_ACE;
			continue;
		}
		if (ch == 'i')
		{
			ret |= INHERITED_ACE;
			continue;
		}
		if (ch == 'p')
		{
			ret |= NO_PROPAGATE_INHERIT_ACE;
			continue;
		}
		if (ch == 'o')
		{
			ret |= OBJECT_INHERIT_ACE;
			continue;
		}
		xbThrowEarg("Unknown inherited type ('"+xbStringPad(1,ch)+"') in file security definition","Definition inherited types: '"+strAttrs+"'");
	}
	return ret;
}

/*
#define FILE_READ_DATA            ( 0x0001 )    // file & pipe
#define FILE_LIST_DIRECTORY       ( 0x0001 )    // directory

#define FILE_WRITE_DATA           ( 0x0002 )    // file & pipe
#define FILE_ADD_FILE             ( 0x0002 )    // directory

#define FILE_APPEND_DATA          ( 0x0004 )    // file
#define FILE_ADD_SUBDIRECTORY     ( 0x0004 )    // directory
#define FILE_CREATE_PIPE_INSTANCE ( 0x0004 )    // named pipe


#define FILE_READ_EA              ( 0x0008 )    // file & directory

#define FILE_WRITE_EA             ( 0x0010 )    // file & directory

#define FILE_EXECUTE              ( 0x0020 )    // file
#define FILE_TRAVERSE             ( 0x0020 )    // directory

#define FILE_DELETE_CHILD         ( 0x0040 )    // directory

#define FILE_READ_ATTRIBUTES      ( 0x0080 )    // all

#define FILE_WRITE_ATTRIBUTES     ( 0x0100 )    // all

#define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF)

#define FILE_GENERIC_READ         (STANDARD_RIGHTS_READ     |\
                                   FILE_READ_DATA           |\
                                   FILE_READ_ATTRIBUTES     |\
                                   FILE_READ_EA             |\
                                   SYNCHRONIZE)


#define FILE_GENERIC_WRITE        (STANDARD_RIGHTS_WRITE    |\
                                   FILE_WRITE_DATA          |\
                                   FILE_WRITE_ATTRIBUTES    |\
                                   FILE_WRITE_EA            |\
                                   FILE_APPEND_DATA         |\
                                   SYNCHRONIZE)


#define FILE_GENERIC_EXECUTE      (STANDARD_RIGHTS_EXECUTE  |\
                                   FILE_READ_ATTRIBUTES     |\
                                   FILE_EXECUTE             |\
                                   SYNCHRONIZE)
*/
xbString xbSecurity::rightsAttr(ulong attr)
{
	xbString ret;
	if ((attr & FILE_ALL_ACCESS)==FILE_ALL_ACCESS)
	{
		attr &= ~FILE_ALL_ACCESS;
		ret += "A";
	}
	{
		bool bR=false,bW=false,bX=false;
		if ((attr & FILE_GENERIC_READ)==FILE_GENERIC_READ)
		{
			bR = true;
			ret += "R";
		}
		if ((attr & FILE_GENERIC_WRITE)==FILE_GENERIC_WRITE)
		{
			bW = true;
			ret += "W";
		}
		if ((attr & FILE_GENERIC_EXECUTE)==FILE_GENERIC_EXECUTE)
		{
			bX = true;
			ret += "X";
		}
		if (bR)
			attr &= ~FILE_GENERIC_READ;
		if (bW)
			attr &= ~FILE_GENERIC_WRITE;
		if (bX)
			attr &= ~FILE_GENERIC_EXECUTE;
	}
	if (attr & FILE_READ_DATA)
	{
		attr &= ~FILE_READ_DATA;
		ret += "r";
	}
	if (attr & FILE_WRITE_DATA)
	{
		attr &= ~FILE_WRITE_DATA;
		ret += "w";
	}
	if (attr & FILE_APPEND_DATA)
	{
		attr &= ~FILE_APPEND_DATA;
		ret += "g";
	}
	if (attr & FILE_EXECUTE)
	{
		attr &= ~FILE_EXECUTE;
		ret += "x";
	}
	if (attr & FILE_READ_EA)
	{
		attr &= ~FILE_READ_EA;
		ret += "e";
	}
	if (attr & FILE_WRITE_EA)
	{
		attr &= ~FILE_WRITE_EA;
		ret += "E";
	}
	if (attr & FILE_READ_ATTRIBUTES)
	{
		attr &= ~FILE_READ_ATTRIBUTES;
		ret += "t";
	}
	if (attr & FILE_WRITE_ATTRIBUTES)
	{
		attr &= ~FILE_WRITE_ATTRIBUTES;
		ret += "T";
	}
	/*
	if (attr & GENERIC_READ)
	{
		attr &= ~GENERIC_READ;
		ret += "r";
	}
	if (attr & GENERIC_WRITE)
	{
		attr &= ~GENERIC_WRITE;
		ret += "w";
	}
	if (attr & GENERIC_EXECUTE)
	{
		attr &= ~GENERIC_EXECUTE;
		ret += "x";
	}
	if (attr & GENERIC_ALL)
	{
		attr &= ~GENERIC_ALL;
		ret += "A";
	}
	*/
	if (attr & DELETE)
	{
		attr &= ~DELETE;
		ret += "s";
	}
	if (attr & FILE_DELETE_CHILD)
	{
		attr &= ~FILE_DELETE_CHILD;
		ret += "S";
	}	
	if (attr & WRITE_OWNER)
	{
		attr &= ~WRITE_OWNER;
		ret += "O";
	}
	if (attr & READ_CONTROL)
	{
		attr &= ~READ_CONTROL;
		ret += "p";
	}
	if (attr & WRITE_DAC)
	{
		attr &= ~WRITE_DAC;
		ret += "P";
	}
	if (attr & MAXIMUM_ALLOWED)
	{
		attr &= ~MAXIMUM_ALLOWED;
		ret += "m";
	}
	if (attr & SYNCHRONIZE)
	{
		attr &= ~SYNCHRONIZE;
		ret += "z";
	}
	if (attr)
	{
		for(ulong i=0,bval=1; attr != 0; i++,bval<<=1)
		{
			if (!bval)
				xbThrow xbEsys("xbSecurity::rightsAttr("+xbString(attr,xbStringNum_hexa)+") : internal error");
			if (!(attr & bval))
				continue;
			attr -= bval;
			ret += "+"+xbString(i)+"b";
		}
	}
	return ret;
}

xbString xbSecurity::rightsInhType(ulong attr)
{
	xbString ret;
	if (attr & CONTAINER_INHERIT_ACE)
	{
		attr &= ~CONTAINER_INHERIT_ACE;
		ret += "c";
	}
	if (attr & INHERIT_ONLY_ACE)
	{
		attr &= ~INHERIT_ONLY_ACE;
		ret += "I";
	}
	if (attr & INHERITED_ACE)
	{
		attr &= ~INHERITED_ACE;
		ret += "i";
	}
	if (attr & NO_PROPAGATE_INHERIT_ACE)
	{
		attr &= ~NO_PROPAGATE_INHERIT_ACE;
		ret += "p";
	}
	if (attr & OBJECT_INHERIT_ACE)
	{
		attr &= ~OBJECT_INHERIT_ACE;
		ret += "o";
	}
	return ret;
}

