//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1997-2002.
//
//  File:       DataObj.cpp
//
//  Contents:   Implementation of data object classes: CCertTemplatesDataObject
//
//----------------------------------------------------------------------------

#include "stdafx.h"

USE_HANDLE_MACROS("CERTTMPL(dataobj.cpp)")

#include "compdata.h"
#include "dataobj.h"

#include "uuids.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


#include "stddtobj.cpp"

// IDataObject interface implementation

CCertTemplatesDataObject::CCertTemplatesDataObject()
		: m_pCookie (0),
		m_objecttype (CERTTMPL_SNAPIN),
		m_dataobjecttype (CCT_UNINITIALIZED),
        m_pbMultiSelData(NULL),
        m_cbMultiSelData(0),
		m_bMultiSelDobj(false),
		m_iCurr(0)
{
}

HRESULT CCertTemplatesDataObject::GetDataHere(
	FORMATETC __RPC_FAR *pFormatEtcIn,
	STGMEDIUM __RPC_FAR *pMedium)
{
	const CLIPFORMAT cf=pFormatEtcIn->cfFormat;
	if (cf == m_CFNodeType)
	{
		if ( IsValidObjectType (m_pCookie->m_objecttype) )
		{
			const GUID* pguid = GetObjectTypeGUID( m_pCookie->m_objecttype );
			stream_ptr s(pMedium);
			return s.Write(pguid, sizeof(GUID));
		}
		else
			return E_UNEXPECTED;
	}
	else if (cf == m_CFSnapInCLSID)
	{
		stream_ptr s(pMedium);
		return s.Write(&m_SnapInCLSID, sizeof(GUID));
	}
	else if (cf == m_CFNodeTypeString)
	{
		if ( IsValidObjectType (m_pCookie->m_objecttype) )
		{
			const BSTR strGUID = GetObjectTypeString( m_pCookie->m_objecttype );
			stream_ptr s(pMedium);
			return s.Write(strGUID);
		}
		else
			return E_UNEXPECTED;
	}
	else if (cf == m_CFDisplayName)
	{
		return PutDisplayName(pMedium);
	}
	else if (cf == m_CFDataObjectType)
	{
		stream_ptr s(pMedium);
		return s.Write(&m_dataobjecttype, sizeof(m_dataobjecttype));
	}
	else if (cf == m_CFRawCookie)
	{
		stream_ptr s(pMedium);


		if ( m_pCookie )
		{
			// CODEWORK This cast ensures that the data format is
			// always a CCookie*, even for derived subclasses
			if ( ((CCertTmplCookie*) MMC_MULTI_SELECT_COOKIE) == m_pCookie ||
					IsValidObjectType (m_pCookie->m_objecttype) )
			{
				CCookie* pcookie = (CCookie*) m_pCookie;
				return s.Write(reinterpret_cast<PBYTE>(&pcookie), sizeof(m_pCookie));
			}
			else
				return E_UNEXPECTED;
		}
	}
	else if ( cf == m_CFMultiSel )
	{
		return CreateMultiSelectObject (pMedium);
	}
	else if (cf == m_CFSnapinPreloads)
	{
		stream_ptr s(pMedium);
		// If this is TRUE, then the next time this snapin is loaded, it will
		// be preloaded to give us the opportunity to change the root node
		// name before the user sees it.
		BOOL	x = 1;

		return s.Write (reinterpret_cast<PBYTE>(&x), sizeof (BOOL));
	}

	return DV_E_FORMATETC;
}

HRESULT CCertTemplatesDataObject::Initialize(
	CCertTmplCookie*			pcookie,
	DATA_OBJECT_TYPES		type,
	CCertTmplComponentData&	refComponentData)
{
	if ( !pcookie || m_pCookie )
	{
		ASSERT(FALSE);
		return S_OK;	// Initialize must not fail
	}

	m_dataobjecttype = type;
	m_pCookie = pcookie;

	if ( ((CCertTmplCookie*) MMC_MULTI_SELECT_COOKIE) != m_pCookie )
		((CRefcountedObject*)m_pCookie)->AddRef();
	VERIFY( SUCCEEDED(refComponentData.GetClassID(&m_SnapInCLSID)) );
	return S_OK;
}


CCertTemplatesDataObject::~CCertTemplatesDataObject()
{
	if ( ((CCertTmplCookie*) MMC_MULTI_SELECT_COOKIE) != m_pCookie &&
			m_pCookie && IsValidObjectType (m_pCookie->m_objecttype) )
	{
		((CRefcountedObject*)m_pCookie)->Release();
	}
    if (m_pbMultiSelData)
        delete m_pbMultiSelData;

    for (int i=0; i < m_rgCookies.GetSize(); ++i)
    {
        m_rgCookies[i]->Release();
        m_rgCookies[i] = 0;
    }
}

void CCertTemplatesDataObject::AddCookie(CCertTmplCookie* pCookie)
{
    m_rgCookies.Add(pCookie);
    pCookie->AddRef();
}

HRESULT CCertTemplatesDataObject::PutDisplayName(STGMEDIUM* pMedium)
	// Writes the "friendly name" to the provided storage medium
	// Returns the result of the write operation
{
	AFX_MANAGE_STATE (AfxGetStaticModuleState ());
	CString strDomainName = m_pCookie->GetManagedDomainDNSName();

	stream_ptr s (pMedium);
	CString		snapinName;
    // security review 2/21/2002 BryanWal ok
	snapinName.FormatMessage (IDS_CERTTMPL_ROOT_NODE_NAME, strDomainName);
	return s.Write ((PCWSTR) snapinName);
}

// Register the clipboard formats
CLIPFORMAT CCertTemplatesDataObject::m_CFDisplayName =
								(CLIPFORMAT)RegisterClipboardFormat(CCF_DISPLAY_NAME);
CLIPFORMAT CDataObject::m_CFRawCookie =
								(CLIPFORMAT)RegisterClipboardFormat(L"CERTTMPL_SNAPIN_RAW_COOKIE");
CLIPFORMAT CCertTemplatesDataObject::m_CFMultiSel =
								(CLIPFORMAT)RegisterClipboardFormat(CCF_OBJECT_TYPES_IN_MULTI_SELECT);
CLIPFORMAT CCertTemplatesDataObject::m_CFMultiSelDobj =
								(CLIPFORMAT)RegisterClipboardFormat(CCF_MMC_MULTISELECT_DATAOBJECT);
CLIPFORMAT CCertTemplatesDataObject::m_CFMultiSelDataObjs =
							    (CLIPFORMAT)RegisterClipboardFormat(CCF_MULTI_SELECT_SNAPINS);
CLIPFORMAT CCertTemplatesDataObject::m_CFDsObjectNames =
                                (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES);


void CCertTemplatesDataObject::SetMultiSelData(BYTE* pbMultiSelData, UINT cbMultiSelData)
{
    m_pbMultiSelData = pbMultiSelData;
    m_cbMultiSelData = cbMultiSelData;
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CCertTmplComponentData::QueryDataObject (
		MMC_COOKIE cookie,
		DATA_OBJECT_TYPES type,
		LPDATAOBJECT* ppDataObject)
{
	if ( MMC_MULTI_SELECT_COOKIE == cookie )
	{
		return QueryMultiSelectDataObject (cookie, type, ppDataObject);
	}
	CCertTmplCookie* pUseThisCookie =
			(CCertTmplCookie*) ActiveBaseCookie (
			reinterpret_cast<CCookie*> (cookie));

	CComObject<CCertTemplatesDataObject>* pDataObject = 0;
	HRESULT hRes = CComObject<CCertTemplatesDataObject>::CreateInstance(&pDataObject);
	if ( FAILED(hRes) )
		return hRes;

	HRESULT hr = pDataObject->Initialize (
			pUseThisCookie,
			type,
			*this);
	if ( FAILED(hr) )
	{
		delete pDataObject;
		return hr;
	}

    pDataObject->AddRef();
	*ppDataObject = pDataObject;
	return hr;
}

typedef CArray<GUID, const GUID&> CGUIDArray;

void GuidArray_Add(CGUIDArray& rgGuids, const GUID& guid)
{
    for (INT_PTR i=rgGuids.GetUpperBound(); i >= 0; --i)
    {
        if (rgGuids[i] == guid)
            break;
    }

    if (i < 0)
        rgGuids.Add(guid);
}

HRESULT CCertTmplComponentData::QueryMultiSelectDataObject(MMC_COOKIE cookie, DATA_OBJECT_TYPES type,
                                            LPDATAOBJECT* ppDataObject)
{
    ASSERT(ppDataObject != NULL);
    if (ppDataObject == NULL)
        return E_POINTER;

	HRESULT		hr = S_OK;
    CGUIDArray	rgGuids;

    // Determine the items selected
    ASSERT(m_pResultData != NULL);
    RESULTDATAITEM rdi;
    // security review 2/21/2002 BryanWal ok
    ZeroMemory(&rdi, sizeof(rdi));
    rdi.mask = RDI_STATE;
    rdi.nIndex = -1;
    rdi.nState = TVIS_SELECTED;

	CCookiePtrArray	rgCookiesSelected;
    while (m_pResultData->GetNextItem (&rdi) == S_OK)
    {
        const GUID* pguid;
        CCertTmplCookie* pCookie = reinterpret_cast <CCertTmplCookie*> (rdi.lParam);
        if ( pCookie )
        {
			rgCookiesSelected.Add (pCookie);
			switch (pCookie->m_objecttype)
			{
            case CERTTMPL_CERT_TEMPLATE:
                pguid = &NODEID_CertTmpl_CERT_TEMPLATE;
                break;

			default:
				ASSERT (0);
				continue;
			}
        }
        else
        {
			hr = E_INVALIDARG;
			break;
        }

        GuidArray_Add(rgGuids, *pguid);
    }

    if ( SUCCEEDED (hr) )
    {
        CComObject<CCertTemplatesDataObject>* pObject;
        hr = CComObject<CCertTemplatesDataObject>::CreateInstance(&pObject);
        ASSERT(SUCCEEDED (hr) && pObject != NULL);
        if ( SUCCEEDED (hr) )
        {
            if ( pObject )
            {
                // Save cookie and type for delayed rendering
	            pObject->Initialize ((CCertTmplCookie*) cookie,
				            type,
				            *this);
                pObject->SetMultiSelDobj();



                // Store the coclass with the data object
                UINT cb = (UINT)(rgGuids.GetSize() * sizeof(GUID));
                GUID* pGuid = new GUID[(UINT)rgGuids.GetSize()];
                if ( pGuid )
                {
                    // security review 2/21/2002 BryanWal ok
                    CopyMemory(pGuid, rgGuids.GetData(), cb);
                    pObject->SetMultiSelData((BYTE*)pGuid, cb);
	                for (int i=0; i < rgCookiesSelected.GetSize(); ++i)
	                {
		                pObject->AddCookie(rgCookiesSelected[i]);
	                }

                    hr = pObject->QueryInterface(
			                IID_PPV_ARG (IDataObject, ppDataObject));
                }
                else
                    hr = E_OUTOFMEMORY;
            }
            else
                hr = E_FAIL;
        }
    }

    return hr;
}



//+--------------------------------------------------------------------------
//
//  Member:     CDataObject::Create
//
//  Synopsis:   Fill the hGlobal in [lpmedium] with the data in pBuffer
//
//  Arguments:  [pBuffer]  - [in] the data to be written
//              [len]      - [in] the length of that data
//              [pMedium] - [in,out] where to store the data
//  History:
//
//---------------------------------------------------------------------------
HRESULT CCertTemplatesDataObject::Create (const void* pBuffer, int len, LPSTGMEDIUM pMedium)
{
   HRESULT hr = DV_E_TYMED;

   //
   // Do some simple validation
   //
   if (pBuffer == NULL || pMedium == NULL)
      return E_POINTER;

   //
   // Make sure the type medium is HGLOBAL
   //
   if (pMedium->tymed == TYMED_HGLOBAL) {
      //
      // Create the stream on the hGlobal passed in
      //
      LPSTREAM lpStream = 0;
      hr = CreateStreamOnHGlobal(pMedium->hGlobal, FALSE, &lpStream);

	  ASSERT (SUCCEEDED (hr));
      if (SUCCEEDED(hr))
	  {
         //
         // Write to the stream the number of bytes
         //
         ULONG written = 0;
         hr = lpStream->Write(pBuffer, len, &written);
		 ASSERT (SUCCEEDED (hr));

         //
         // Because we told CreateStreamOnHGlobal with 'FALSE',
         // only the stream is released here.
         // Note - the caller (i.e. snap-in, object) will free the HGLOBAL
         // at the correct time.  This is according to the IDataObject specification.
         //
         lpStream->Release();
      }
   }

   return hr;
}


//+----------------------------------------------------------------------------
//
//  Method:     CCertTemplatesDataObject::CreateMultiSelectObject
//
//  Synopsis:   this is to create the list of types selected
//
//-----------------------------------------------------------------------------

HRESULT CCertTemplatesDataObject::CreateMultiSelectObject(LPSTGMEDIUM pMedium)
{
    ASSERT(m_pbMultiSelData != 0);
    ASSERT(m_cbMultiSelData != 0);

    pMedium->tymed = TYMED_HGLOBAL;
    pMedium->hGlobal = ::GlobalAlloc(GMEM_SHARE|GMEM_MOVEABLE,
                                      (m_cbMultiSelData + sizeof(DWORD)));
    if (pMedium->hGlobal == NULL)
        return STG_E_MEDIUMFULL;

    BYTE* pb = reinterpret_cast<BYTE*>(::GlobalLock(pMedium->hGlobal));
    *((DWORD*)pb) = m_cbMultiSelData / sizeof(GUID);
    pb += sizeof(DWORD);
    // security review 2/21/2002 BryanWal ok
    CopyMemory(pb, m_pbMultiSelData, m_cbMultiSelData);

    ::GlobalUnlock(pMedium->hGlobal);

	return S_OK;
}




LPDATAOBJECT ExtractMultiSelect (LPDATAOBJECT lpDataObject)
{
	if (lpDataObject == NULL)
		return NULL;

	SMMCDataObjects * pDO = NULL;

	STGMEDIUM stgmedium = { TYMED_HGLOBAL, NULL };
	FORMATETC formatetc = { CCertTemplatesDataObject::m_CFMultiSelDataObjs, NULL,
                           DVASPECT_CONTENT, -1, TYMED_HGLOBAL};

	if ( FAILED (lpDataObject->GetData (&formatetc, &stgmedium)) )
	{
		return NULL;
	}
	else
	{
		pDO = reinterpret_cast<SMMCDataObjects*>(stgmedium.hGlobal);
		return pDO->lpDataObject[0]; //assume that ours is the 1st
	}
}

STDMETHODIMP CCertTemplatesDataObject::GetData(LPFORMATETC lpFormatetc, LPSTGMEDIUM pMedium)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    HRESULT hr = DV_E_CLIPFORMAT;

    if (lpFormatetc->cfFormat == m_CFMultiSel)
    {
        ASSERT(((CCertTmplCookie*) MMC_MULTI_SELECT_COOKIE) == m_pCookie);
        if ( ((CCertTmplCookie*) MMC_MULTI_SELECT_COOKIE) != m_pCookie )
            return E_FAIL;

        hr = CreateMultiSelectObject (pMedium);
    }
	else if ( lpFormatetc->cfFormat == m_CFDsObjectNames )
	{
		switch (m_pCookie->m_objecttype)
		{
        case CERTTMPL_CERT_TEMPLATE:
			{
				CCertTemplate* pCertTemplate = dynamic_cast <CCertTemplate*> (m_pCookie);
				ASSERT (pCertTemplate);
				if ( pCertTemplate )
				{
					// figure out how much storage we need
                    CString adsiPath;
                    adsiPath = pCertTemplate->GetLDAPPath ();
					int cbPath = sizeof (WCHAR) * (adsiPath.GetLength() + 1);
					int cbClass = sizeof (WCHAR) * (pCertTemplate->GetClass ().GetLength() + 1);;
					int cbStruct = sizeof(DSOBJECTNAMES); //contains already a DSOBJECT embedded struct

					LPDSOBJECTNAMES pDSObj = 0;

					pDSObj = (LPDSOBJECTNAMES)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
							cbStruct + cbPath + cbClass);

					if ( pDSObj )
					{
						// write the info
						pDSObj->clsidNamespace = CLSID_CertTemplatesSnapin;
						pDSObj->cItems = 1;

						pDSObj->aObjects[0].dwFlags = 0;
						pDSObj->aObjects[0].dwProviderFlags = 0;

						pDSObj->aObjects[0].offsetName = cbStruct;
						pDSObj->aObjects[0].offsetClass = cbStruct + cbPath;

                        // security review 2/21/2002 BryanWal ok
						wcsncpy((LPWSTR)((BYTE *)pDSObj + (pDSObj->aObjects[0].offsetName)),
								(LPCWSTR) adsiPath, cbPath);

                        // security review 2/21/2002 BryanWal ok
						wcsncpy((LPWSTR)((BYTE *)pDSObj + (pDSObj->aObjects[0].offsetClass)),
								(LPCWSTR) pCertTemplate->GetClass (), cbClass);

						pMedium->hGlobal = (HGLOBAL)pDSObj;
						pMedium->tymed = TYMED_HGLOBAL;
						pMedium->pUnkForRelease = NULL;
						hr = S_OK;
					}
					else
						hr = STG_E_MEDIUMFULL;
				}
			}
			break;

		default:
			break;
		}
	}

    return hr;
}

STDMETHODIMP CCertTemplatesDataObject::Next(ULONG celt, MMC_COOKIE* rgelt, ULONG *pceltFetched)
{
    HRESULT hr = S_OK;

    if ((rgelt == NULL) ||
        ((celt > 1) && (pceltFetched == NULL)))
    {
        hr = E_INVALIDARG;
        return hr;
    }

    ULONG celtTemp = (ULONG)(m_rgCookies.GetSize() - m_iCurr);
    celtTemp = (celt < celtTemp) ? celt : celtTemp;

    if (pceltFetched)
        *pceltFetched = celtTemp;

    if (celtTemp == 0)
        return S_FALSE;

    for (ULONG i=0; i < celtTemp; ++i)
    {
        rgelt[i] = reinterpret_cast<MMC_COOKIE>(m_rgCookies[m_iCurr++]);
    }

    return (celtTemp < celt) ? S_FALSE : S_OK;
}

STDMETHODIMP CCertTemplatesDataObject::Skip(ULONG celt)
{
    ULONG celtTemp = (ULONG)(m_rgCookies.GetSize() - m_iCurr);
    celtTemp = (celt < celtTemp) ? celt : celtTemp;

    m_iCurr += celtTemp;

    return (celtTemp < celt) ? S_FALSE : S_OK;
}


STDMETHODIMP CCertTemplatesDataObject::Reset(void)
{
    m_iCurr = 0;
    return S_OK;
}