// SecurityManager.cpp : Implementation of CSecurityManager
#include "stdafx.h"
#include "VideoSurvDll.h"
#include "SecurityManager.h"
#include "SecurityLoginDlg.h"

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// CSecurityManager
//\_

STDMETHODIMP CSecurityManager::FinalConstruct ()
{
	return S_OK;
}

STDMETHODIMP CSecurityManager::FinalRelease ()
{
	return S_OK;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// ISctructure Item
//\_
STDMETHODIMP CSecurityManager::Init()
{
	// create one user with the full permissions
	CComPtr<ISecurityUser> oUser;
	_ComVerifyRS( CreateAppObject( CLSID_SecurityUser, &oUser, GetApplication(), (IAppObject*) this ) );
	_ComVerifyRS( oUser->put_Login(CComBSTR("Admin")) );
	_ComVerifyRS( oUser->put_Name(CComBSTR("Main system administrator")) );
	_ComVerifyRS( oUser->put_DefaultPermissions(sdlFullAccess) );

	_ComVerifyRS( AddUser(oUser) );
	return IStructureLevelImpl::Init();
}

STDMETHODIMP CSecurityManager::PostInit()
{
	_ComVerifyRS( LogInit( GetApplication(), 1) );
	_ComVerifyRS( DoAdvise( GetApplication(), IID__IApplicationStateEvents ) );
	return IStructureLevelImpl::PostInit();
}

STDMETHODIMP_(void) CSecurityManager::PreClose()
{
	IStructureLevelImpl::PreClose();
}

STDMETHODIMP_(void) CSecurityManager::Close()
{
	IStructureLevelImpl::Close();
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// ISecurity group
//\_
STDMETHODIMP CSecurityManager::get_Users(IEnumSecurityUsers* *pVal)
{
	typedef CComObject< CComEnum< IEnumSecurityUsers, &IID_IEnumSecurityUsers, ISecurityUser*, _CopyInterface< ISecurityUser > > > EnumSecurityUsers;
	if ( !pVal )
		return E_POINTER;
	EnumSecurityUsers*
		pEnum = NULL;
	EnumSecurityUsers::CreateInstance( &pEnum );
	HRESULT
		hr = pEnum->Init( (ISecurityUser**) m_vecUsers.begin(), (ISecurityUser**) m_vecUsers.end(), NULL, AtlFlagCopy );
	if ( SUCCEEDED( hr ) )
		hr = pEnum->QueryInterface( IID_IEnumSecurityUsers, (void**) pVal );
	if ( FAILED( hr ) )
		delete pEnum;
	return hr;
}

// add new user to system
STDMETHODIMP CSecurityManager::AddUser(ISecurityUser *pUser)
{
	if (pUser==NULL) return E_POINTER;

	// all simply, if we do not have any user with same SecurityID we can add it, if not...
	CComBSTR bsSecID;
	_ComVerifyRS( pUser->get_SecurityID(&bsSecID) );

	for (int i=0; i<m_vecUsers.size(); i++)
	{
		CComBSTR bsTemp;
		_ComVerifyRS( m_vecUsers[i]->get_SecurityID(&bsTemp) );
		if (bsSecID==bsTemp) {
			string sError; LoadString(271, &sError);
			return Error(sError.c_str());
		}
	}

	m_vecUsers.push_back(pUser);

	return S_OK;
}

// remove user from group
STDMETHODIMP CSecurityManager::DeleteUser(ISecurityUser *pUser)
{
	if (pUser==NULL) return E_POINTER;

	CComBSTR bsSecID;
	_ComVerifyRS( pUser->get_SecurityID(&bsSecID) );

	std::vector < CComPtr < ISecurityUser > >::iterator pIt;

	for (pIt = m_vecUsers.begin(); pIt<m_vecUsers.end(); pIt++)
	{
		CComBSTR bsTemp;
		_ComVerifyRS( (*pIt)->get_SecurityID(&bsTemp) );
		if (bsSecID==bsTemp)
		{
			m_vecUsers.erase(pIt);
			return S_OK;
		}
	}

	string sError; LoadString(272, &sError);
	return Error(sError.c_str());
}

STDMETHODIMP CSecurityManager::GetUser(BSTR bsSecurityID, ISecurityUser **ppUser)
{
	if (ppUser==NULL) return E_POINTER;

	std::vector < CComPtr < ISecurityUser > >::iterator pIt;

	for (pIt = m_vecUsers.begin(); pIt<m_vecUsers.end(); pIt++)
	{
		// if we find this user in this group, delete it
		CComBSTR bsTemp;
		_ComVerifyRS( (*pIt)->get_SecurityID(&bsTemp) );
		if (bsTemp==bsSecurityID) {
			(*pIt).CopyTo(ppUser);
			return S_OK;
		}
	}

	return S_FALSE;
}

STDMETHODIMP CSecurityManager::get_UserCount(long *pVal)
{
	if (pVal==NULL) return E_POINTER;

	*pVal=m_vecUsers.size();

	return S_OK;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// IPersistsStream
//\_
HRESULT CSecurityManager::LoadSecurityData(IStorage *pStg)
{
	m_vecUsers.clear();

	CComPtr<IStream>
		oUsersStream;

	if ( FAILED( pStg->OpenStream( CComBSTR("Users"), NULL, STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &oUsersStream ) ) )
		return S_FALSE;

	long nUsersCount;
	_ComCallRH( oUsersStream->Read( &nUsersCount, sizeof( nUsersCount ), NULL ) );

	for (int i=0; i<nUsersCount; i++)
	{
		CLSID ClsID;
		_ComCallRH( oUsersStream->Read( &ClsID, sizeof( ClsID ), NULL ) );

		CComPtr<IPersistStream> oPersistStream;

		_ComVerifyRS( CreateAppObject( ClsID, &oPersistStream, GetApplication(), (IAppObject*) this ) );
		_ComCallRH( oPersistStream->Load(oUsersStream) );

		CComQIPtr<ISecurityUser> oUser(oPersistStream);

		if (oUser)
			m_vecUsers.push_back(oUser.p);
	}

	// now group structure
	CComPtr<IStream>
		oGroupsStream;

	if ( FAILED( pStg->OpenStream( CComBSTR("Groups"), NULL, STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &oGroupsStream ) ) )
		return S_FALSE;

	long nGroups;
	_ComCallRH( oGroupsStream->Read(&nGroups,sizeof(nGroups), NULL) );

	for (i = 0; i<nGroups; i++)
	{
		// load group id
		CComBSTR bsTemp;

		_ComCallRH( bsTemp.ReadFromStream(oGroupsStream) );

		// this user must be present
		CComPtr<ISecurityUser> oUser;
		_ComVerifyRS( GetUser(bsTemp,&oUser) );

		CComQIPtr<ISecurityGroup> oGroup(oUser);
		_VerifyRS( oGroup );

		long nUsers;
		_ComCallRH( oGroupsStream->Read(&nUsers,sizeof(nUsers), NULL) );

		// add users to group
		for (int j=0; j<nUsers; j++)
		{
			oUser.Release();
			bsTemp.Empty();
			_ComCallRH( bsTemp.ReadFromStream(oGroupsStream) );

			_ComVerifyRS( GetUser(bsTemp,&oUser) );
			_ComCallRH( oGroup->AddUser(oUser) );
		}
	}

	// now security data
	CComPtr<IStream>
		oSecStream;

	if ( FAILED( pStg->OpenStream( CComBSTR("Security"), NULL, STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &oSecStream ) ) )
		return S_FALSE;

	return LoadSecurityItem(oSecStream);
}

HRESULT CSecurityManager::SaveSecurityData(IStorage *pStg)
{
	// firstly save all users
	CComPtr<IStream>
		oUsersStream;

	_ComCallRH( pStg->CreateStream( CComBSTR("Users"), STGM_CREATE | STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &oUsersStream ) );

	long nUsersCount = m_vecUsers.size();
	_ComCallRH( oUsersStream->Write( &nUsersCount, sizeof( nUsersCount ), NULL ) );

	std::vector < CComPtr < ISecurityUser > >::iterator pIt;

	for (pIt = m_vecUsers.begin(); pIt<m_vecUsers.end(); pIt++)
	{
		CComQIPtr <IPersistStream> oPersistStream(*pIt);
		CLSID ClsID;

		if (oPersistStream) {
		   _ComVerifyRS( oPersistStream->GetClassID( &ClsID ) );
			_ComCallRH( oUsersStream->Write( &ClsID, sizeof( ClsID ), NULL ) );
			_ComCallRH( oPersistStream->Save(oUsersStream,TRUE) );
		}
	}

	// now group structure
	CComPtr<IStream>
		oGroupsStream;

	_ComCallRH( pStg->CreateStream( CComBSTR("Groups"), STGM_CREATE | STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &oGroupsStream ) );

	long nGroups=0;
	_ComCallRH( oGroupsStream->Write(&nGroups,sizeof(nGroups), NULL) );

	for (pIt = m_vecUsers.begin(); pIt<m_vecUsers.end(); pIt++)
	{
		CComQIPtr <IPersistStream> oPersistStream(*pIt);
		CComQIPtr <ISecurityGroup> oSecurityGroup(*pIt);

		if (oPersistStream && oSecurityGroup) {
			CComBSTR bsTemp;
			long nLength;

			// write groupid
			_ComVerifyRS( (*pIt)->get_SecurityID(&bsTemp) );
			_ComCallRH( bsTemp.WriteToStream(oGroupsStream) );

			// write group count
			_ComVerifyRS( oSecurityGroup->get_UserCount(&nLength) );

			_ComCallRH( oGroupsStream->Write(&nLength,sizeof(nLength), NULL) );

			// write group member ids
			CComPtr<IEnumSecurityUsers> oUsers;
			_ComVerifyRS( oSecurityGroup->get_Users(&oUsers) );

			CComPtr<ISecurityUser> oUser;
			ULONG lReaded;

			while ( SUCCEEDED( oUsers->Next( 1, &oUser, &lReaded ) ) && lReaded == 1 )
			{
				bsTemp.Empty();
				_ComVerifyRS( oUser->get_SecurityID(&bsTemp) );

				_ComCallRH( bsTemp.WriteToStream(oGroupsStream) );

				oUser.Release();
			}

			nGroups++;
		}
	}

	// write group count to reserved place
	LARGE_INTEGER lSeek;
	lSeek.QuadPart = 0;

	_ComCallRH( oGroupsStream->Seek(lSeek,STREAM_SEEK_SET,NULL) );
	_ComCallRH( oGroupsStream->Write(&nGroups,sizeof(nGroups), NULL) );

		// now group structure
	CComPtr<IStream>
		oSecStream;

	_ComCallRH( pStg->CreateStream( CComBSTR("Security"), STGM_CREATE | STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &oSecStream ) );

	return SaveSecurityItem(oSecStream);
}

STDMETHODIMP CSecurityManager::LoadItem( IStorage* pIParentStorage, BOOL bGlobalWorkspace )
{
	if ( bGlobalWorkspace )
	{
		CComPtr<IStorage>
			oStorage;
		// try open security storage
		if ( FAILED( pIParentStorage->OpenStorage( L"Security", NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &oStorage ) ) )
			return S_FALSE;

		_ComCallRH( LoadSecurityData( oStorage ) );
	}

	return S_OK;
}

STDMETHODIMP CSecurityManager::SaveItem( IStorage* pIParentStorage, BOOL bGlobalWorkspace )
{
	if ( bGlobalWorkspace )
	{
		CComPtr<IStorage>
			oStorage;
		// try open security storage
		if ( FAILED( pIParentStorage->OpenStorage( L"Security", NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &oStorage ) ) )
			// try create security storage
			_ComCallRH( pIParentStorage->CreateStorage( L"Security", STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &oStorage ) );

		_ComCallRH( SaveSecurityData( oStorage ) );
	}

	return S_OK;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// ISecurityManager
//\_
STDMETHODIMP CSecurityManager::Login(BSTR bsName, BSTR bsPass)
{
	// iterate throught all users to find can we login existing or not
	for (int i=0; i<m_vecUsers.size(); i++)
	{
		CComBSTR bsLogin, bsPassword;
		_ComVerifyRS( m_vecUsers[i]->get_Login(&bsLogin) );
		_ComVerifyRS( m_vecUsers[i]->get_Password(&bsPassword) );

		if ( (bsLogin == bsName) && (bsPassword == bsPass ) )
		{
			// user exist
			m_oLoggedUser = m_vecUsers[i];
			_ComCallRH( AddLogMessage(rctInfo,1000) );
			return S_OK;
		}
	}
	
	// report error
	string sTemp;	LoadString (270, &sTemp);
	_ComCallRH( AddLogMessage(rctWarning,1001) );
	return Error(sTemp.c_str());
}

STDMETHODIMP CSecurityManager::UILogin(HWND hWndParent)
{
	CComPtr <ILogManager> oLoc;
	_ComVerifyRS( GetSertainSubItem<ILogManager>( GetApplication(), CLSID_LogManager, &oLoc) );

	CSecurityLoginDlg Dlg(GetApplication());

	string sCap;
	_ComVerifyRS( LoadString(152,&sCap) );

	HRESULT hr;
	do
	{
		if (Dlg.DoModal(hWndParent)!=IDOK)
			return S_FALSE;

		hr = Login(Dlg.bsName,Dlg.bsPass);
		if (FAILED(hr))
			MessageBox(hWndParent,GetErrorDescription().c_str(),sCap.c_str(),MB_OK|MB_ICONWARNING);
		
	}
	while (hr!=S_OK);

	return S_OK;
}

STDMETHODIMP CSecurityManager::get_LoggedUser(ISecurityUser **pVal)
{
	AUTO_LOCKER;
	return m_oLoggedUser.CopyTo(pVal);
}

STDMETHODIMP CSecurityManager::CheckPermission(IUnknown *pObj, long nPropID, BOOL *bPermit)
{
	if (m_oLoggedUser == NULL) return E_FAIL;

	CComQIPtr <ISecurityItem> oSecItem (pObj);
	_VerifyRS( oSecItem );

	// fristly try to get permission for reegistered user
	CComBSTR bsUserID;
	_ComVerifyRS( m_oLoggedUser->get_SecurityID(&bsUserID) );

	if (S_OK == oSecItem->get_SecProperty(bsUserID, nPropID, bPermit))
		return S_OK;

	// get default user privelege
	SECURITY_DEFAULT_LEVEL DefID;
	_ComVerifyRS( m_oLoggedUser->get_DefaultPermissions(&DefID) );
	return oSecItem->get_DefSecProperty(DefID, nPropID, bPermit);
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// ICloneObject
//\_
HRESULT CSecurityManager::AssignISecurityGroup(IUnknown *pSrc)
{
	HRESULT hr;

	// ISecurityGroup, asign object, property by property
	CComQIPtr <ISecurityGroup> oSecurityGroup(pSrc);
	if (oSecurityGroup==NULL) return E_NOINTERFACE;

	m_vecUsers.clear();

	// get enum for all users
	CComPtr<IEnumSecurityUsers> oUsers;
	_ComVerifyRS( hr = oSecurityGroup->get_Users(&oUsers) );

	CComPtr<ISecurityUser> oUser;	ULONG lReaded;

	while ( SUCCEEDED( oUsers->Next( 1, &oUser, &lReaded ) ) && lReaded == 1 ) {
		m_vecUsers.push_back(oUser);
		oUser.Release();
	}
	return S_OK;
}

STDMETHODIMP CSecurityManager::Assign(IUnknown* pSourseObj)
{
	if (pSourseObj==NULL) return E_POINTER;

	CComQIPtr<ICloneObject> oCloneObject (pSourseObj);

	if (oCloneObject==NULL) return E_NOINTERFACE;

	// check objects id

	CLSID clsid;
	_ComVerifyRS( oCloneObject->get_ObjectCLSID(&clsid) );

	_VerifyRS( InlineIsEqualGUID(clsid,GetObjectCLSID()) );

	_ComCallRH( AssignISecurityItem(pSourseObj) );
	_ComCallRH( AssignIStructureItem(pSourseObj) );
	_ComCallRH( LogInit( GetApplication(), 1) );

	return AssignISecurityGroup(pSourseObj);
}

STDMETHODIMP CSecurityManager::AssignPart(REFIID riid, IUnknown *pSource)
{
	if (pSource == NULL) return E_POINTER;

	if (InlineIsEqualGUID(riid,IID_ISecurityItem))
		return AssignISecurityItem(pSource);
	else if (InlineIsEqualGUID(riid,IID_ISecurityGroup))
		return AssignISecurityGroup(pSource);
	else if (InlineIsEqualGUID(riid,IID_IStructureItem))
		return AssignIStructureItem(pSource);
	else return E_NOTIMPL;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// _IApplicationStateEvents
//\_
STDMETHODIMP CSecurityManager::OnAppLoadComplete()
{
	HWND hwndMain = ::GetLastActivePopup(NULL);

	CComPtr <IAppWindow> oAppWindow;
	_ComVerifyRS( GetApplication()->get_AppWindow(&oAppWindow) );
	_ComVerifyRS( oAppWindow->get_hWnd(&hwndMain) );
	DoUnadvise( IID__IApplicationStateEvents );

	return UILogin( hwndMain ) == S_OK ? S_OK : E_ABORT;
}
