/**********************************************************************/
/**                       Microsoft Windows/NT                       **/
/**                Copyright(c) Microsoft Corporation, 1997 - 1999 **/
/**********************************************************************/

/*
	dialin.cpp
		Implementationof CRasDialin class, this class implements COM
		interfaces IUnknown, IShellExtInit, IShellPropSheetExt to extend
		User object's property sheet.

    FILE HISTORY:

*/

#include "stdafx.h"
#include "Dialin.h"
#include "DlgDial.h"
#include <dsrole.h>
#include <lmserver.h>
#include <localsec.h>
#include <dsgetdc.h>
#include <mmc.h>
#include <sdowrap.h>
#include "sharesdo.h"


#ifdef __cplusplus
extern "C"{
#endif


#ifndef __IID_DEFINED__
#define __IID_DEFINED__

typedef struct _IID
{
    unsigned long x;
    unsigned short s1;
    unsigned short s2;
    unsigned char  c[8];
} IID;

#endif // __IID_DEFINED__

#ifndef CLSID_DEFINED
#define CLSID_DEFINED
typedef IID CLSID;
#endif // CLSID_DEFINED

const IID IID_IRasDialin = {0xB52C1E4F,0x1DD2,0x11D1,{0xBC,0x43,0x00,0xC0,0x4F,0xC3,0x1F,0xD3}};


const IID LIBID_RASDIALLib = {0xB52C1E42,0x1DD2,0x11D1,{0xBC,0x43,0x00,0xC0,0x4F,0xC3,0x1F,0xD3}};


const CLSID CLSID_RasDialin = {0xB52C1E50,0x1DD2,0x11D1,{0xBC,0x43,0x00,0xC0,0x4F,0xC3,0x1F,0xD3}};


#ifdef __cplusplus
}
#endif



static ULONG_PTR g_cfMachineName = 0;
static ULONG_PTR g_cfDisplayName = 0;
LONG	g_lComponentDataSessions = 0;



/////////////////////////////////////////////////////////////////////////////
// CRasDialin
CRasDialin::CRasDialin()
{
	m_pPage = NULL;
	m_pMergedPage = NULL;

	m_pwszObjName = NULL;
	m_pwszClass = NULL;
    ZeroMemory(&m_ObjMedium, sizeof(m_ObjMedium));
	m_ObjMedium.tymed =TYMED_HGLOBAL;
	m_ObjMedium.hGlobal = NULL;
	m_bShowPage = TRUE;
}

CRasDialin::~CRasDialin()
{
	// stgmedia
//	delete m_pPage;
	ReleaseStgMedium(&m_ObjMedium);
}


//===============================================================================
// IShellExtInit::Initialize
//
// information of the user object is passed in via parameter pDataObject
// further processing will be based on the DN of the user object

STDMETHODIMP CRasDialin::Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hRegKey)
{
	TRACE(_T("CRasDialin::Initialize()\r\n"));
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	ASSERT (pDataObj != NULL);

	// get the object name out of the pDataObj
	HRESULT		hr = S_OK;

	TracePrintf(g_dwTraceHandle, _T("RegisterClipboardFormat %s"),CFSTR_DSOBJECTNAMES);
	ULONG_PTR cfDsObjectNames = RegisterClipboardFormat(CFSTR_DSOBJECTNAMES);
	TracePrintf(g_dwTraceHandle, _T(" %x"),cfDsObjectNames);
    FORMATETC fmte = {cfDsObjectNames, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
    LPDSOBJECTNAMES pDsObjectNames;
    // Get the path to the DS object from the data object.
    // Note: This call runs on the caller's main thread. The pages' window
    // procs run on a different thread, so don't reference the data object
    // from a winproc unless it is first marshalled on this thread.
	TracePrintf(g_dwTraceHandle, _T("pDataObj->GetData returns"));
    CHECK_HR(hr = pDataObj->GetData(&fmte, &m_ObjMedium));
	TracePrintf(g_dwTraceHandle, _T(" %x"), hr);
    pDsObjectNames = (LPDSOBJECTNAMES)m_ObjMedium.hGlobal;	
    if (pDsObjectNames->cItems < 1)
    {
		ASSERT (0);
        return E_FAIL;
    }
	m_bShowPage = TRUE;

	if(m_bShowPage)
	{
		// get the name of the object
    	m_pwszObjName = (LPWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[0].offsetName);

		// get the class name of the object
    	m_pwszClass = (LPWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[0].offsetClass);

		TracePrintf(g_dwTraceHandle, _T("UserPath %s"),m_pwszObjName);

		try{
			ASSERT(!m_pMergedPage);
			CString machineName;
		
			m_pMergedPage = new CDlgRASDialinMerge(RASUSER_ENV_DS, NULL, m_pwszObjName);

			TracePrintf(g_dwTraceHandle, _T("new Dialin page object %x"),m_pMergedPage);
			ASSERT(m_pMergedPage);

#ifdef SINGLE_SDO_CONNECTION 	// for share the same sdo connection for multiple users			
			TracePrintf(g_dwTraceHandle, _T("HrGetDCName returns  "));
			CHECK_HR(hr = m_pMergedPage->HrGetDCName(machineName));
			TracePrintf(g_dwTraceHandle, _T("%x"),hr);
			TracePrintf(g_dwTraceHandle, _T("HrGetSharedSdoServer returns  "));

			// ignore return value:
			// reason: marshalling could fail in case this call was made from different thread,
			// before the pointer is used, it will be checked again
			GetSharedSdoServer((LPCTSTR)machineName, NULL, NULL, NULL, m_pMergedPage->GetMarshalSdoServerHolder());
			TracePrintf(g_dwTraceHandle, _T("%x"),hr);
#endif
		}
		catch(CMemoryException&)
		{
			CHECK_HR( hr = E_OUTOFMEMORY );
		}
	}

L_ERR:
	TracePrintf(g_dwTraceHandle, _T("%x"),hr);
    return hr;
}

//
//  FUNCTION: IShellPropSheetExt::AddPages(LPFNADDPROPSHEETPAGE, LPARAM)
//
//  PURPOSE: Called by the shell just before the property sheet is displayed.
//
//  PARAMETERS:
//    lpfnAddPage -  Pointer to the Shell's AddPage function
//    lParam      -  Passed as second parameter to lpfnAddPage
//
//  RETURN VALUE:
//
//    NOERROR in all cases.  If for some reason our pages don't get added,
//    the Shell still needs to bring up the Properties... sheet.
//
//  COMMENTS:
//

STDMETHODIMP CRasDialin::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
{
    TRACE(_T("CRasDialin::AddPages()\r\n"));

	if(!m_bShowPage)	return S_OK;
	
	HRESULT	hr = S_OK;
	
	// param validation
	ASSERT (lpfnAddPage);
    if (lpfnAddPage == NULL)
        return E_UNEXPECTED;

	// make sure our state is fixed up (cause we don't know what context we were called in)
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	ASSERT(m_pMergedPage);
	
    // tell MMC to hook the proc because we are running on a separate,
   	// non MFC thread.
	m_pMergedPage->m_psp.pfnCallback = &CDlgRASDialinMerge::PropSheetPageProc;

	// We also need to save a self-reference so that the static callback
	// function can recover a "this" pointer
	m_pMergedPage->m_psp.lParam = (LONG_PTR)m_pMergedPage;

	MMCPropPageCallback(&m_pMergedPage->m_psp);

	HPROPSHEETPAGE hPage = ::CreatePropertySheetPage(&m_pMergedPage->m_psp);

	ASSERT (hPage);
	if (hPage == NULL)
		return E_UNEXPECTED;
		// add the page
	lpfnAddPage (hPage, lParam);


	m_pPage = NULL;	// since it's just consumed by the dialog, cannot added again

    return S_OK;
}

//
//  FUNCTION: IShellPropSheetExt::ReplacePage(UINT, LPFNADDPROPSHEETPAGE, LPARAM)
//
//  PURPOSE: Called by the shell only for Control Panel property sheet
//           extensions
//
//  PARAMETERS:
//    uPageID         -  ID of page to be replaced
//    lpfnReplaceWith -  Pointer to the Shell's Replace function
//    lParam          -  Passed as second parameter to lpfnReplaceWith
//
//  RETURN VALUE:
//
//    E_FAIL, since we don't support this function.  It should never be
//    called.

//  COMMENTS:
//

STDMETHODIMP CRasDialin::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE lpfnReplaceWith, LPARAM lParam)
{
    TRACE(_T("CRasDialin::ReplacePage()\r\n"));

    return E_FAIL;
}


/*---------------------------------------------------------------------------
    IExtendPropertySheet Implementation
 ---------------------------------------------------------------------------*/

/*!--------------------------------------------------------------------------
    IExtendPropertySheet::QueryPagesFor
        MMC calls this to see if a node has property pages
    Author:
 ---------------------------------------------------------------------------*/
STDMETHODIMP
CRasDialin::QueryPagesFor
(
    LPDATAOBJECT pDataObject
)
{
	return S_OK;
}


/*!--------------------------------------------------------------------------
    TFSComponentData::CreatePropertyPages
        Implementation of IExtendPropertySheet::CreatePropertyPages
        Called for a node to put up property pages
    Author:
 ---------------------------------------------------------------------------*/
STDMETHODIMP
CRasDialin::CreatePropertyPages
(
    LPPROPERTYSHEETCALLBACK lpProvider,
    LONG_PTR                    handle,
    LPDATAOBJECT            pDataObject
)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());


    FORMATETC               fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
    //STGMEDIUM               medium = { TYMED_NULL, NULL, NULL };
	STGMEDIUM               mediumMachine = { TYMED_HGLOBAL, NULL, NULL };
	STGMEDIUM               mediumUser = { TYMED_HGLOBAL, NULL, NULL };

    HGLOBAL 			hMem = GlobalAlloc(GMEM_SHARE, MAX_PATH * sizeof(WCHAR));
    mediumMachine.hGlobal = hMem;

 	hMem = GlobalAlloc(GMEM_SHARE, MAX_PATH * sizeof(WCHAR));
    mediumUser.hGlobal = hMem;

    HRESULT        hr = S_OK;
    DWORD		   dwError;
    LPWSTR		   pMachineName = NULL;
    LPWSTR			pUserName = NULL;
    SERVER_INFO_102*	pServerInfo102 = NULL;
    NET_API_STATUS	netRet = 0;
	DSROLE_PRIMARY_DOMAIN_INFO_BASIC* pdsRole = NULL;

	//==================================================================

	// check if the machine is a standalone NT5 server
    // check if the machine focused on is NT5 machine
    if ( !g_cfMachineName )
        g_cfMachineName = RegisterClipboardFormat(CCF_LOCAL_USER_MANAGER_MACHINE_NAME);

    fmte.cfFormat = g_cfMachineName;

    CHECK_HR( hr = pDataObject->GetDataHere(&fmte, &mediumMachine));
    pMachineName = (LPWSTR)mediumMachine.hGlobal;

    ASSERT(pMachineName);

    if(!pMachineName)	CHECK_HR(hr = E_INVALIDARG);


	// not to show the page if focusing on workstation
	// TODO change to real
/* postpone, till The dialog is being called	
	if(HrIsNTServer(pMachineName) != S_OK)
	{
		m_bShowPage = FALSE;
		goto L_EXIT;
	}
*/
 	if (g_cfDisplayName == 0)
 		g_cfDisplayName = RegisterClipboardFormat(CCF_DISPLAY_NAME);
 		
 	
    fmte.cfFormat = g_cfDisplayName;

    CHECK_HR( hr = pDataObject->GetDataHere(&fmte, &mediumUser));
    pUserName = (LPWSTR)mediumUser.hGlobal;

    ASSERT(pUserName);

	if(!pUserName)
    	CHECK_HR(hr = E_INVALIDARG);

	ASSERT(!m_pMergedPage);
	
	try{
		m_pMergedPage = new CDlgRASDialinMerge(RASUSER_ENV_LOCAL, pMachineName, pUserName);

#ifdef SINGLE_SDO_CONNECTION 	// for share the same sdo connection for multiple users			
		CHECK_HR(hr = GetSharedSdoServer(pMachineName, NULL, NULL, NULL, m_pMergedPage->GetMarshalSdoServerHolder()));
#endif

	    // tell MMC to hook the proc because we are running on a separate,
    	// non MFC thread.
		m_pMergedPage->m_psp.pfnCallback = &CDlgRASDialinMerge::PropSheetPageProc;

		// We also need to save a self-reference so that the static callback
		// function can recover a "this" pointer
		m_pMergedPage->m_psp.lParam = (LONG_PTR)m_pMergedPage;

		MMCPropPageCallback(&m_pMergedPage->m_psp);

		HPROPSHEETPAGE hPage = ::CreatePropertySheetPage(&m_pMergedPage->m_psp);
		if(hPage == NULL)
			return E_UNEXPECTED;

		lpProvider->AddPage(hPage);
	}catch(CMemoryException&){
		delete m_pMergedPage;
		m_pMergedPage = NULL;
		CHECK_HR(hr = E_OUTOFMEMORY);
	}

L_ERR:
	if FAILED(hr)
		ReportError(hr, IDS_ERR_PROPERTYPAGE, NULL);

	if(pUserName)
		GlobalFree(pUserName);

	if(pMachineName)
		GlobalFree(pMachineName);

	if(pServerInfo102)
		NetApiBufferFree(pServerInfo102);

    return hr;
}