//+--------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1996 - 2001.
//
//  File:       dataobj.cpp
//
//  Contents:   Implementation of data object class
//
//  History:
//
//---------------------------------------------------------------------------


#include "stdafx.h"
#include "cookie.h"
#include "snapmgr.h"
#include "DataObj.h"
#include <sceattch.h>

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


///////////////////////////////////////////////////////////////////////////////
// Snap-in NodeType in both GUID format and string format
UINT CDataObject::m_cfNodeType               = RegisterClipboardFormat(CCF_NODETYPE);
UINT CDataObject::m_cfNodeTypeString         = RegisterClipboardFormat(CCF_SZNODETYPE);
UINT CDataObject::m_cfNodeID                 = RegisterClipboardFormat(CCF_NODEID2);

UINT CDataObject::m_cfDisplayName            = RegisterClipboardFormat(CCF_DISPLAY_NAME);
UINT CDataObject::m_cfSnapinClassID          = RegisterClipboardFormat(CCF_SNAPIN_CLASSID);
UINT CDataObject::m_cfInternal               = RegisterClipboardFormat(SNAPIN_INTERNAL);

UINT CDataObject::m_cfSceSvcAttachment       = RegisterClipboardFormat(CCF_SCESVC_ATTACHMENT);
UINT CDataObject::m_cfSceSvcAttachmentData   = RegisterClipboardFormat(CCF_SCESVC_ATTACHMENT_DATA);
UINT CDataObject::m_cfModeType               = RegisterClipboardFormat(CCF_SCE_MODE_TYPE);
UINT CDataObject::m_cfGPTUnknown             = RegisterClipboardFormat(CCF_SCE_GPT_UNKNOWN);
UINT CDataObject::m_cfRSOPUnknown            = RegisterClipboardFormat(CCF_SCE_RSOP_UNKNOWN);
UINT CDataObject::m_cfMultiSelect            = ::RegisterClipboardFormat(CCF_OBJECT_TYPES_IN_MULTI_SELECT);
/////////////////////////////////////////////////////////////////////////////
// CDataObject implementations


//+--------------------------------------------------------------------------
//
//  Member:     CDataObject::GetDataHere
//
//  Synopsis:   Fill the hGlobal in [lpmedium] with the requested data
//
//  History:
//
//---------------------------------------------------------------------------

STDMETHODIMP
CDataObject::GetDataHere(LPFORMATETC lpFormatetc,  // In
                         LPSTGMEDIUM lpMedium)     // In
{
   HRESULT hr = DV_E_CLIPFORMAT;

   AFX_MANAGE_STATE(AfxGetStaticModuleState());

   if (!lpFormatetc)
      return E_INVALIDARG;
   
   //
   // Based on the CLIPFORMAT, write data to the stream
   //
   const CLIPFORMAT cf = lpFormatetc->cfFormat;

   if (cf == m_cfNodeType)
      hr = CreateNodeTypeData(lpMedium);
   else if (cf == m_cfNodeTypeString)
      hr = CreateNodeTypeStringData(lpMedium);
   else if (cf == m_cfDisplayName)
      hr = CreateDisplayName(lpMedium);
   else if (cf == m_cfSnapinClassID)
      hr = CreateSnapinClassID(lpMedium);
   else if (cf == m_cfInternal)
      hr = CreateInternal(lpMedium);
   else if (cf == m_cfSceSvcAttachment)
      hr = CreateSvcAttachment(lpMedium);
   else if (cf == m_cfSceSvcAttachmentData)
      hr = CreateSvcAttachmentData(lpMedium);
   else if (cf == m_cfModeType)
      hr = CreateModeType(lpMedium);
   else if (cf == m_cfGPTUnknown)
      hr = CreateGPTUnknown(lpMedium);
   else if (cf == m_cfRSOPUnknown)
      hr = CreateRSOPUnknown(lpMedium);

   return hr;
}



//+--------------------------------------------------------------------------
//
//  Member:     CDataObject::GetData
//
//  Synopsis:   Support for mutli select is added.  First return the mutli
//              select GUID information if that is what we are being called for.
//              The else if copies the actual mutli-select information.
//
//              The function will only copy the mutli select information if
//              the FORMATEETC.cfFormat == CDataObject::m_cfInternal and
//              FORMATETC.tymed == TYMED_HGLOBAL.
//
//  History:    1-14-1999 - a-mthoge
//
//---------------------------------------------------------------------------
STDMETHODIMP
CDataObject::GetData(LPFORMATETC lpFormatetcIn,
                     LPSTGMEDIUM lpMedium)
{
   HRESULT hRet = S_OK;

   if (NULL == lpFormatetcIn ||
       NULL == lpMedium) 
   {
      return E_POINTER;
   }

   if(lpFormatetcIn->cfFormat == m_cfMultiSelect &&
      lpFormatetcIn->tymed    == TYMED_HGLOBAL &&
      m_nInternalArray )
   {
      //
      // Need to create a SSMCObjectTypes structure and return this
      // to MMC for mutli select.
      //
      // we only support result items created by SCE.
      //
      lpMedium->hGlobal = GlobalAlloc(GMEM_SHARE, sizeof(DWORD) + sizeof(GUID) );
      if(!lpMedium->hGlobal)
         return E_FAIL;

      //
      // Set count and GUID to 1.
      //
      SMMCObjectTypes *pTypes = (SMMCObjectTypes *)GlobalLock(lpMedium->hGlobal);
      pTypes->count = 1;
      memcpy( &(pTypes->guid), &m_internal.m_clsid, sizeof(GUID));

      GlobalUnlock(lpMedium->hGlobal);
      return S_OK;
   } 
   else if(lpFormatetcIn->cfFormat == m_cfInternal &&
      lpFormatetcIn->tymed    == TYMED_HGLOBAL &&
      m_nInternalArray )
   {
      //
      // Copy the contents of the mutli select to STGMEDIUM
      //
      lpMedium->hGlobal = GlobalAlloc( GMEM_SHARE, sizeof(INTERNAL) * (m_nInternalArray + 1));
      if(!lpMedium->hGlobal)
         return E_FAIL;

      //
      // The first element in the array is set to
      // MMC_MUTLI_SELECT_COOKIE and the type is set the count of items after the
      // first structure.
      //
      INTERNAL *pInternal = (INTERNAL *)GlobalLock( lpMedium->hGlobal );

      pInternal->m_cookie = (MMC_COOKIE)MMC_MULTI_SELECT_COOKIE;
      pInternal->m_type   = (DATA_OBJECT_TYPES)m_nInternalArray;

      //
      // Copy the rest of the INTERNAL structures to this array.
      //
      pInternal++;
      memcpy(pInternal, m_pInternalArray, sizeof(INTERNAL) * m_nInternalArray);
   } 
   else if (lpFormatetcIn->cfFormat == m_cfNodeID &&
              lpFormatetcIn->tymed    == TYMED_HGLOBAL ) 
   {
      return CreateNodeId(lpMedium);
   }
   return hRet;
}


//+--------------------------------------------------------------------------
//
//  Member:     CDataObject::EnumFormatEtc
//
//  Synopsis:   Not implemented
//
//  History:
//
//---------------------------------------------------------------------------
STDMETHODIMP
CDataObject::EnumFormatEtc(DWORD dwDirection,
                           LPENUMFORMATETC*
                           ppEnumFormatEtc)
{
   return E_NOTIMPL;
}

/////////////////////////////////////////////////////////////////////////////
// CDataObject creation members


//+--------------------------------------------------------------------------
//
//  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
//              [lpMedium] - [in,out] where to store the data
//  History:
//
//---------------------------------------------------------------------------
HRESULT
CDataObject::Create(const void* pBuffer,
                    int len,
                    LPSTGMEDIUM lpMedium)
{
   HRESULT hr = DV_E_TYMED;

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

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

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

         //
         // 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;
}

//+--------------------------------------------------------------------------
//
//  Member:     CDataObject::CreateNodeTypeData
//
//  Synopsis:   Fill the hGlobal in [lpMedium] with our node type
//
//  History:
//
//---------------------------------------------------------------------------
HRESULT
CDataObject::CreateNodeTypeData(LPSTGMEDIUM lpMedium)
{
   const GUID *pNodeType;
   //
   // Create the node type object in GUID format
   //

    switch (m_internal.m_foldertype) 
    {
     case LOCALPOL:
        pNodeType = &cNodetypeSceTemplate;
        break;
     
     case PROFILE:
        if ( ::IsEqualGUID(m_internal.m_clsid, CLSID_Snapin) ||
             ::IsEqualGUID(m_internal.m_clsid, CLSID_RSOPSnapin) ) 
        {
           pNodeType = &cNodetypeSceTemplate;
        } 
        else 
        {
           // other areas aren't extensible on this node
           // return our generic node type
           pNodeType = &cSCENodeType;
        }
        break;

     case AREA_SERVICE_ANALYSIS:
        pNodeType = &cNodetypeSceAnalysisServices;
        break;

     case AREA_SERVICE:
        pNodeType = &cNodetypeSceTemplateServices;
        break;

     default:
        if ( ::IsEqualGUID(m_internal.m_clsid, CLSID_Snapin) )
            pNodeType = &cNodeType;
        else if ( ::IsEqualGUID(m_internal.m_clsid, CLSID_RSOPSnapin) )
           pNodeType = &cRSOPNodeType;
        else if ( ::IsEqualGUID(m_internal.m_clsid, CLSID_SAVSnapin) )
            pNodeType = &cSAVNodeType;
        else
            pNodeType = &cSCENodeType;
        break;
    }

   return Create(reinterpret_cast<const void*>(pNodeType), sizeof(GUID), lpMedium);
}


//+--------------------------------------------------------------------------
//
//  Member:     CDataObject::CreateNodeTypeStringData
//
//  Synopsis:   Fill the hGlobal in [lpMedium] with the string representation
//              of our node type
//
//  History:
//
//---------------------------------------------------------------------------
HRESULT CDataObject::CreateNodeTypeStringData(LPSTGMEDIUM lpMedium)
{
   //
   // Create the node type object in GUID string format
   //
   LPCTSTR pszNodeType;

    switch (m_internal.m_foldertype) 
    {
     case AREA_SERVICE_ANALYSIS:
        pszNodeType = lstruuidNodetypeSceAnalysisServices;
        break;

     case AREA_SERVICE:
        pszNodeType = lstruuidNodetypeSceTemplateServices;
        break;

     case LOCALPOL:
        pszNodeType = lstruuidNodetypeSceTemplate;
        break;

     case PROFILE:
       if ( ::IsEqualGUID(m_internal.m_clsid, CLSID_Snapin) )
          pszNodeType = lstruuidNodetypeSceTemplate;
       else if ( ::IsEqualGUID(m_internal.m_clsid, CLSID_RSOPSnapin) )
          pszNodeType = lstruuidNodetypeSceTemplate;
       else 
       {
          // other snapin types do not allow extensions on this level
          // return our generic node type
          pszNodeType = cszSCENodeType;
       }
       break;

     default:
         if ( ::IsEqualGUID(m_internal.m_clsid, CLSID_Snapin) )
             pszNodeType = cszNodeType;
         else if ( ::IsEqualGUID(m_internal.m_clsid, CLSID_RSOPSnapin) )
            pszNodeType = cszRSOPNodeType;
         else if ( ::IsEqualGUID(m_internal.m_clsid, CLSID_SAVSnapin) )
             pszNodeType = cszSAVNodeType;
         else
             pszNodeType = cszSCENodeType;
        break;
    }

   return Create(pszNodeType, ((wcslen(pszNodeType)+1) * sizeof(WCHAR)), lpMedium);
}


//+--------------------------------------------------------------------------
//
//  Member:     CDataObject::CreateNodeID
//
//  Synopsis:   Create an hGlobal in [lpMedium] with our node ID
//
//  History:
//
//---------------------------------------------------------------------------
HRESULT
CDataObject::CreateNodeId(LPSTGMEDIUM lpMedium)
{
   SNodeID2 *nodeID = NULL;
   BYTE *id = NULL;
   DWORD dwIDSize = 0;
   DWORD dwIDNameSize = 0;
   LPTSTR szNodeName = NULL;
   CFolder *pFolder = NULL;
   LPTSTR szMedium = NULL;
   //
   // Create the node type object in GUID format
   //


   switch (m_internal.m_foldertype) 
   {
      case LOCATIONS:
      case PROFILE:
      case REG_OBJECTS:
      case FILE_OBJECTS:
         //
         // There can be many nodes of these types and they will be
         // locked to the system so just use the display name
         //
         if (m_internal.m_cookie) 
         {
            pFolder = reinterpret_cast<CFolder*>(m_internal.m_cookie);
            szNodeName = pFolder->GetName();
            dwIDNameSize = (lstrlen(szNodeName)+1)*sizeof(TCHAR);
            dwIDSize = sizeof(SNodeID2)+dwIDNameSize;
            lpMedium->hGlobal = GlobalAlloc(GMEM_SHARE,dwIDSize);
            if(!lpMedium->hGlobal)
               return STG_E_MEDIUMFULL;
            
            nodeID = (SNodeID2 *)GlobalLock(lpMedium->hGlobal);
            nodeID->dwFlags = 0;
            nodeID->cBytes = dwIDNameSize;

            memcpy(nodeID->id,szNodeName,dwIDNameSize);
         } 
         else
            return E_FAIL;
         break;

      default:
         //
         // Everything else is unique: there's one and only one node
         // of the type per snapin.
         //
         dwIDSize = sizeof(FOLDER_TYPES)+sizeof(SNodeID2);
         lpMedium->hGlobal = GlobalAlloc(GMEM_SHARE,dwIDSize);
         if(!lpMedium->hGlobal)
            return STG_E_MEDIUMFULL;
         
         nodeID = (SNodeID2 *)GlobalLock(lpMedium->hGlobal);
         nodeID->dwFlags = 0;
         nodeID->cBytes = sizeof(FOLDER_TYPES);
         memcpy(nodeID->id,&(m_internal.m_foldertype),sizeof(FOLDER_TYPES));
         GlobalUnlock(lpMedium->hGlobal);
         break;
   }
   return S_OK;
}


//+--------------------------------------------------------------------------
//
//  Member:     CDataObject::CreateNodeTypeData
//
//  Synopsis:   Fill the hGlobal in [lpMedium] with SCE's display name,
//              which will differ depending on where it's being viewed from
//              as reported by the mode bits
//
//  History:
//
//---------------------------------------------------------------------------
HRESULT CDataObject::CreateDisplayName(LPSTGMEDIUM lpMedium)
{
   //
   // This is the display named used in the scope pane and snap-in manager
   //

   //
   // Load the name from resource
   // Note - if this is not provided, the console will used the snap-in name
   //

   CString szDispName;

   if ( ::IsEqualGUID(m_internal.m_clsid, CLSID_SAVSnapin) )
      szDispName.LoadString(IDS_ANALYSIS_VIEWER_NAME);
   else if ( ::IsEqualGUID(m_internal.m_clsid, CLSID_SCESnapin) )
      szDispName.LoadString(IDS_TEMPLATE_EDITOR_NAME);
   else if ( ::IsEqualGUID(m_internal.m_clsid, CLSID_LSSnapin) )
      szDispName.LoadString(IDS_LOCAL_SECURITY_NAME);
   else if ( ::IsEqualGUID(m_internal.m_clsid, CLSID_Snapin) )
      szDispName.LoadString(IDS_EXTENSION_NAME);
   else if ( ::IsEqualGUID(m_internal.m_clsid, CLSID_RSOPSnapin) )
      szDispName.LoadString(IDS_EXTENSION_NAME);
   else
      szDispName.LoadString(IDS_NODENAME);

/*  // can't depend on m_ModeBits because it's not set yet
   if (m_ModeBits & MB_ANALYSIS_VIEWER) {
      szDispName.LoadString(IDS_ANALYSIS_VIEWER_NAME);
   } else if (m_ModeBits & MB_TEMPLATE_EDITOR) {
      szDispName.LoadString(IDS_TEMPLATE_EDITOR_NAME);
   } else if ( (m_ModeBits & MB_NO_NATIVE_NODES) ||
              (m_ModeBits & MB_SINGLE_TEMPLATE_ONLY) ) {
      szDispName.LoadString(IDS_EXTENSION_NAME);
   } else {
      szDispName.LoadString(IDS_NODENAME);
   }
*/
   return Create(szDispName, ((szDispName.GetLength()+1) * sizeof(WCHAR)), lpMedium);
}


//+--------------------------------------------------------------------------
//
//  Member:     CDataObject::CreateSnapinClassID
//
//  Synopsis:   Fill the hGlobal in [lpMedium] with SCE's class ID
//
//  History:
//
//---------------------------------------------------------------------------
HRESULT CDataObject::CreateSnapinClassID(LPSTGMEDIUM lpMedium)
{
   //
   // Create the snapin classid in CLSID format
   //
   return Create(reinterpret_cast<const void*>(&m_internal.m_clsid), sizeof(CLSID), lpMedium);
}


//+--------------------------------------------------------------------------
//
//  Member:     CDataObject::CreateInternal
//
//  Synopsis:   Fill the hGlobal in [lpMedium] with SCE's internal data type
//
//  History:
//
//---------------------------------------------------------------------------
HRESULT CDataObject::CreateInternal(LPSTGMEDIUM lpMedium)
{
   return Create(&m_internal, sizeof(INTERNAL), lpMedium);
}

//+--------------------------------------------------------------------------
//
//  Member:     CDataObject::AddInternal
//
//  Synopsis:   Adds an INTERNAL object to the array of internal objects.
//
//  History:    1-14-1999  a-mthoge
//
//---------------------------------------------------------------------------
void CDataObject::AddInternal( MMC_COOKIE cookie, DATA_OBJECT_TYPES  type)
{
   //
   // Allocate memory for one more internal array.
   INTERNAL *hNew = (INTERNAL *)LocalAlloc( 0, sizeof(INTERNAL) * (m_nInternalArray + 1) );
   if(!hNew)
      return;
   
   m_nInternalArray++;

   //
   // Copy other internal array information.
   //
   if( m_pInternalArray )
   {
      memcpy(hNew, m_pInternalArray, sizeof(INTERNAL) * (m_nInternalArray - 1) );
      LocalFree( m_pInternalArray );
   }

   //
   // Set the new internal array members.
   //
   hNew[ m_nInternalArray - 1].m_cookie = cookie;
   hNew[ m_nInternalArray - 1].m_type   = type;

   //
   // Set the CObjectData internal array pointer.
   //
   m_pInternalArray = hNew;
}

//+--------------------------------------------------------------------------
//
//  Member:     CDataObject::CreateSvcAttachment
//
//  Synopsis:   Fill the hGlobal in [lpMedium] with the name of the inf
//              template a service attachment should modify or with an empty
//              string for the inf-templateless analysis section
//
//  History:
//
//---------------------------------------------------------------------------
HRESULT CDataObject::CreateSvcAttachment(LPSTGMEDIUM lpMedium)
{
   LPCTSTR sz = 0;

   if ((AREA_SERVICE == m_internal.m_foldertype) ||
       (AREA_SERVICE_ANALYSIS == m_internal.m_foldertype)) 
   {
      CFolder *pFolder = reinterpret_cast<CFolder *>(m_internal.m_cookie);
      if (pFolder) 
      {
         sz = pFolder->GetInfFile();
         if (sz) 
            return Create(sz,(lstrlen(sz)+1)*sizeof(TCHAR),lpMedium);
         else
            return E_FAIL;
      } 
      else
         return E_FAIL;
   }

   //
   // This shouldn't be asked for except in the SERVICE areas
   //
   return E_UNEXPECTED;
}

//+--------------------------------------------------------------------------
//
//  Member:     CDataObject::CreateSvcAttachmentData
//
//  Synopsis:   Fill the hGlobal in [lpMedium] with a pointer to the
//              ISceSvcAttachmentData interface that an attachment should use
//              to communicate with SCE
//
//  History:
//
//---------------------------------------------------------------------------
HRESULT CDataObject::CreateSvcAttachmentData(LPSTGMEDIUM lpMedium)
{
   if ((AREA_SERVICE == m_internal.m_foldertype) ||
       (AREA_SERVICE_ANALYSIS == m_internal.m_foldertype)) 
   {
      return Create(&m_pSceSvcAttachmentData,sizeof(m_pSceSvcAttachmentData),lpMedium);
   }

   //
   // This shouldn't be asked for except in the SERVICE areas
   //
   return E_UNEXPECTED;
}

//+--------------------------------------------------------------------------
//
//  Member:     CDataObject::CreateModeType
//
//  Synopsis:   Fill the hGlobal in [lpMedium] with the Mode that SCE was
//              started in
//
//  History:
//
//---------------------------------------------------------------------------
HRESULT CDataObject::CreateModeType(LPSTGMEDIUM lpMedium)
{
   DWORD mode = m_Mode;
   if (mode == SCE_MODE_DOMAIN_COMPUTER_ERROR)
      mode = SCE_MODE_DOMAIN_COMPUTER;
   
   return Create(&mode,sizeof(DWORD),lpMedium);
}


//+--------------------------------------------------------------------------
//
//  Member:     CDataObject::CreateGPTUnknown
//
//  Synopsis:   Fill the hGlobal in [lpMedium] with a pointer to GPT's
//              IUnknown interface.  The object requesting this will be
//              responsible for Releasing the interface
//
//  History:
//
//---------------------------------------------------------------------------
HRESULT CDataObject::CreateGPTUnknown(LPSTGMEDIUM lpMedium)
{
   LPUNKNOWN pUnk = 0;

   if (!m_pGPTInfo) 
   {
      //
      // If we don't have a pointer to a GPT interface then we must not
      // be in a mode where we're extending GPT and we can't provide a
      // pointer to its IUnknown
      //
      return E_UNEXPECTED;
   }

   HRESULT hr = m_pGPTInfo->QueryInterface(IID_IUnknown,
                                   reinterpret_cast<void **>(&pUnk));
   if (SUCCEEDED(hr))
      return Create(&pUnk,sizeof(pUnk),lpMedium);
   else
      return hr;
}


//+--------------------------------------------------------------------------
//
//  Member:     CDataObject::CreateRSOPUnknown
//
//  Synopsis:   Fill the hGlobal in [lpMedium] with a pointer to RSOP's
//              IUnknown interface.  The object requesting this will be
//              responsible for Releasing the interface
//
//  History:
//
//---------------------------------------------------------------------------
HRESULT CDataObject::CreateRSOPUnknown(LPSTGMEDIUM lpMedium)
{
   HRESULT hr = E_FAIL;
   LPUNKNOWN pUnk = NULL;

   if (!m_pRSOPInfo) 
   {
      //
      // If we don't have a pointer to a RSOP interface then we must not
      // be in a mode where we're extending RSOP and we can't provide a
      // pointer to its IUnknown
      //
      return E_UNEXPECTED;
   }

   hr = m_pRSOPInfo->QueryInterface(IID_IUnknown,
                                   reinterpret_cast<void **>(&pUnk));
   if (SUCCEEDED(hr))
      return Create(&pUnk,sizeof(pUnk),lpMedium);
   else
      return hr;
}