/*--------------------------------------------------------------------------- File: Migrator.cpp Comments: Implementation of McsMigrationDriver COM object. This object encapsulates the knowledge of when to call the local engine, and when to call the dispatcher. It will also provide a description of the tasks to be performed, for display on the last page of each migration wizard, and will be responsible for calculating the actions required to undo an operation (this is not yet implemented). (c) Copyright 1999, Mission Critical Software, Inc., All Rights Reserved Proprietary and confidential to Mission Critical Software, Inc. REVISION LOG ENTRY Revision By: Christy Boles --------------------------------------------------------------------------- */// Migrator.cpp : Implementation of CMcsMigrationDriverApp and DLL registration. #include "stdafx.h" #include "MigDrvr.h" //#import "\bin\McsVarSetMin.tlb" no_namespace, named_guids //#import "\bin\McsEADCTAgent.tlb" no_namespace, named_guids //#import "\bin\McsDispatcher.tlb" no_namespace, named_guids //#import "\bin\DBManager.tlb" no_namespace, named_guids //#import "\bin\McsDctWorkerObjects.tlb" //#import "\bin\NetEnum.tlb" no_namespace #import "VarSet.tlb" no_namespace, named_guids rename("property", "aproperty") //#import "Engine.tlb" no_namespace, named_guids //#imported via DetDlg.h below #import "Dispatch.tlb" no_namespace, named_guids #import "WorkObj.tlb" #import "NetEnum.tlb" no_namespace #include #include #include #include #include #include #pragma comment(lib, "Psapi.lib") #include "Migrator.h" #include "TaskChk.h" #include "ResStr.h" // dialogs used #include "DetDlg.h" #include "MonDlg.h" #include "SetDlg.h" #include "MainDlg.h" #include "Working.h" #include "ErrDct.hpp" #include "TReg.hpp" #include "EaLen.hpp" #include //#define MAX_DB_FIELD 255 typedef HRESULT (CALLBACK * DSGETDCNAME)(LPWSTR, LPWSTR, GUID*, LPWSTR, DWORD, PDOMAIN_CONTROLLER_INFO*); // Opertation flags to be performed on the Account #define OPS_Create_Account (0x00000001) #define OPS_Copy_Properties (0x00000002) #define OPS_Process_Members (0x00000004) #define OPS_Process_MemberOf (0x00000008) #define OPS_Call_Extensions (0x00000010) #define OPS_All OPS_Create_Account | OPS_Copy_Properties | OPS_Process_Members | OPS_Process_MemberOf | OPS_Call_Extensions #define OPS_Copy OPS_Create_Account | OPS_Copy_Properties BOOL gbCancelled = FALSE; bool __stdcall IsAgentOrDispatcherProcessRunning(); void __stdcall SetDomainControllers(IVarSetPtr& spVarSet); ///////////////////////////////////////////////////////////////////////////// // BOOL // ret - TRUE if found program directory in the registry GetProgramDirectory( WCHAR * filename // out - buffer that will contain path to program directory ) { DWORD rc = 0; BOOL bFound = FALSE; TRegKey key; rc = key.OpenRead(GET_STRING(IDS_HKLM_DomainAdmin_Key),HKEY_LOCAL_MACHINE); if ( ! rc ) { rc = key.ValueGetStr(L"Directory",filename,MAX_PATH); if ( ! rc ) { if ( *filename ) bFound = TRUE; } } if ( ! bFound ) { UStrCpy(filename,L"C:\\"); // if all else fails, default to the C: drive } return bFound; } BOOL // ret - TRUE if found program directory in the registry GetLogLevel( DWORD * level // out - value that should be used for log level ) { DWORD rc = 0; BOOL bFound = FALSE; TRegKey key; rc = key.OpenRead(GET_STRING(IDS_HKLM_DomainAdmin_Key),HKEY_LOCAL_MACHINE); if ( ! rc ) { rc = key.ValueGetDWORD(L"TranslationLogLevel",level); if ( ! rc ) { bFound = TRUE; } } return bFound; } HRESULT CMigrator::ViewPreviousDispatchResults() { _bstr_t logFile; if ( logFile.length() == 0 ) { WCHAR path[MAX_PATH]; GetProgramDirectory(path); logFile = path; logFile += L"Logs\\Dispatcher.csv"; } // reset the stats, so that we don't see anything left over from the previous run gData.Initialize(); CPropertySheet mdlg; CAgentMonitorDlg listDlg; CMainDlg summaryDlg; CLogSettingsDlg settingsDlg; listDlg.m_psp.dwFlags |= PSP_PREMATURE | PSP_HASHELP; summaryDlg.m_psp.dwFlags |= PSP_PREMATURE | PSP_HASHELP; settingsDlg.m_psp.dwFlags |= PSP_PREMATURE | PSP_HASHELP; mdlg.AddPage(&summaryDlg); mdlg.AddPage(&listDlg); mdlg.AddPage(&settingsDlg); settingsDlg.SetImmediateStart(TRUE); settingsDlg.SetDispatchLog(logFile); mdlg.SetActivePage(&listDlg); // UINT nResponse = mdlg.DoModal(); UINT_PTR nResponse = mdlg.DoModal(); return S_OK; } // WaitForAgentsToFinish Method // // Waits for dispatcher and all dispatched agents to complete // their tasks. // Used when ADMT is run from script or command line. static void WaitForAgentsToFinish(_bstr_t strLogPath) { gData.SetLogPath(strLogPath); CloseHandle(CreateThread(NULL, 0, &ResultMonitorFn, NULL, 0, NULL)); CloseHandle(CreateThread(NULL, 0, &LogReaderFn, NULL, 0, NULL)); CloseHandle(CreateThread(NULL, 0, &MonitorRunningAgents, NULL, 0, NULL)); LARGE_INTEGER liDueTime; liDueTime.QuadPart = -50000000; // 5 sec HANDLE hTimer = CreateWaitableTimer(NULL, TRUE, NULL); for (int nState = 0; nState < 3;) { SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, FALSE); if (WaitForSingleObject(hTimer, INFINITE) == WAIT_OBJECT_0) { BOOL bDone = FALSE; switch (nState) { case 0: // first pass of dispatcher log { gData.GetFirstPassDone(&bDone); break; } case 1: // dispatcher finished { gData.GetLogDone(&bDone); break; } case 2: // agents finished { ComputerStats stats; gData.GetComputerStats(&stats); if (stats.numRunning == 0) { bDone = TRUE; } break; } } if (bDone) { ++nState; } } else { break; } } CloseHandle(hTimer); gData.SetDone(TRUE); } STDMETHODIMP CMigrator::PerformMigrationTask(IUnknown* punkVarSet, LONG_PTR hWnd) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); HRESULT hr = S_OK; IVarSetPtr pVS = punkVarSet; BSTR jobID = NULL; CWnd wnd; long lActionID = -2; IIManageDBPtr pDb; _bstr_t wizard = pVS->get(L"Options.Wizard"); _bstr_t undo; _bstr_t viewPreviousResults = pVS->get(L"MigrationDriver.ViewPreviousResults"); bool bAnyToDispatch = true; long lAutoCloseHideDialogs = pVS->get(GET_BSTR(DCTVS_Options_AutoCloseHideDialogs)); // if agent or dispatcher process still running... if (IsAgentOrDispatcherProcessRunning()) { // return error result return MIGRATOR_E_PROCESSES_STILL_RUNNING; } hr = pDb.CreateInstance(__uuidof(IManageDB)); if (FAILED(hr)) { return hr; } gbCancelled = FALSE; // This provides an easy way to view the previous dispatch results if ( !UStrICmp(viewPreviousResults,GET_STRING(IDS_YES)) ) { ViewPreviousDispatchResults(); return S_OK; } if (_bstr_t(pVS->get(GET_BSTR(DCTVS_Options_DontBeginNewLog))) != GET_BSTR(IDS_YES)) { // begin a new log TError err; err.LogOpen(_bstr_t(pVS->get(GET_BSTR(DCTVS_Options_Logfile))), 0, 0, true); err.LogClose(); } // update the log level, if needed DWORD level = 0; if( GetLogLevel(&level) ) { pVS->put(GET_BSTR(DCTVS_Options_LogLevel),(long)level); } undo = pVS->get(GET_BSTR(DCTVS_Options_Undo)); //* if ( !_wcsicmp((WCHAR*) undo, L"Yes") ) if ( !_wcsicmp((WCHAR*) undo, GET_STRING(IDS_YES)) ) { hr = pDb->raw_GetCurrentActionID(&lActionID); if ( SUCCEEDED(hr) ) pVS->put(L"UndoAction", lActionID); hr = pDb->raw_GetNextActionID(&lActionID); hr = 0; } else { hr = pDb->raw_GetNextActionID(&lActionID); if ( SUCCEEDED(hr) ) { pVS->put(L"ActionID",lActionID); _bstr_t password1 = pVS->get(GET_BSTR(DCTVS_Options_Credentials_Password)); _bstr_t password2 = pVS->get(GET_BSTR(DCTVS_AccountOptions_SidHistoryCredentials_Password)); pVS->put(GET_BSTR(DCTVS_Options_Credentials_Password),L""); pVS->put(GET_BSTR(DCTVS_AccountOptions_SidHistoryCredentials_Password),L""); hr = pDb->raw_SetActionHistory(lActionID, punkVarSet); pVS->put(GET_BSTR(DCTVS_Options_Credentials_Password),password1); pVS->put(GET_BSTR(DCTVS_AccountOptions_SidHistoryCredentials_Password),password2); if ( FAILED(hr) ) { // log a message, but don't abort the whole operation hr = S_OK; //return hr; } } } // This sets up any varset keys needed internally for reports to be generated PreProcessForReporting(pVS); wnd.Attach((HWND)hWnd); //for scripting, we need to make sure we have the source domain sid in the varset RetrieveSrcDomainSid(pVS, pDb); // set preferred domain controllers to be used // by the account replicator and dispatched agents SetDomainControllers(pVS); // Run the local agent first, if needed to copy any accounts if ( NeedToRunLocalAgent(pVS) ) { IDCTAgentPtr pAgent; hr = pAgent.CreateInstance(__uuidof(DCTAgent)); if (SUCCEEDED(hr)) { hr = pAgent->raw_SubmitJob(punkVarSet,&jobID); if ( SUCCEEDED(hr) ) { CAgentDetailDlg detailDlg(&wnd); detailDlg.SetJobID(jobID); detailDlg.SetFormat(1); // acct repl stats // if we're only copying a few accounts, default the refresh rate to a lower value, since the // process may finish before the refresh can happen long nAccounts = pVS->get(GET_BSTR(DCTVS_Accounts_NumItems)); if ( nAccounts <= 20 ) { detailDlg.SetRefreshInterval(1); } else { detailDlg.SetRefreshInterval(5); } _bstr_t logfile = pVS->get(GET_BSTR(DCTVS_Options_Logfile)); detailDlg.SetLogFile((WCHAR*)logfile); detailDlg.SetAutoCloseHide(lAutoCloseHideDialogs); UINT_PTR nResponse = detailDlg.DoModal(); } } } if ( gbCancelled ) { // if the local operation was cancelled, don't dispatch the agents wnd.Detach(); return S_OK; } // now run the dispatcher if ( SUCCEEDED(hr) ) { // there's no need to dispatch agents to do translation or migration // if we were not able to copy the accounts if ( NeedToDispatch(pVS) ) { IDCTDispatcherPtr pDispatcher; hr = pDispatcher.CreateInstance(CLSID_DCTDispatcher); if ( SUCCEEDED(hr) ) { // Call the dispatch preprocessor. PreProcessDispatcher(pVS); // make sure we're not going to lock out any computers by migrating them to a domain where they // don't have a good computer account TrimMigratingComputerList(pVS, &bAnyToDispatch); if (bAnyToDispatch) { CWorking tempDlg(IDS_DISPATCHING); if (lAutoCloseHideDialogs == 0) { tempDlg.Create(IDD_PLEASEWAIT); tempDlg.ShowWindow(SW_SHOW); } // give the dialog a change to process messages CWnd * wnd = AfxGetMainWnd(); MSG msg; // Call the dispatch preprocessor. // PreProcessDispatcher(pVS); while ( wnd && PeekMessage( &msg, wnd->m_hWnd, 0, 0, PM_NOREMOVE ) ) { if ( ! AfxGetApp()->PumpMessage() ) { break; } } AfxGetApp()->DoWaitCursor(0); _bstr_t logFile = pVS->get(GET_BSTR(DCTVS_Options_DispatchCSV)); WCHAR path[MAX_PATH] = L""; GetProgramDirectory(path); if ( logFile.length() == 0 ) { logFile = path; logFile += L"Logs\\Dispatcher.csv"; pVS->put(GET_BSTR(DCTVS_Options_DispatchCSV),logFile); } // clear the CSV log file if it exists, so we will not get old information in it if ( ! DeleteFile(logFile) ) { hr = GetLastError(); } // set up the location for the agents to write back their results logFile = path; logFile += L"Logs\\Agents"; _bstr_t logsPath = path; logsPath += L"Logs"; CreateDirectory(logsPath,NULL); if ( ! CreateDirectory(logFile,NULL) ) { DWORD rc = GetLastError(); } pVS->put(GET_BSTR(DCTVS_Dispatcher_ResultPath),logFile); // if (bAnyToDispatch) // { punkVarSet->AddRef(); hr = pDispatcher->raw_DispatchToServers(&punkVarSet); if (lAutoCloseHideDialogs == 0) { tempDlg.ShowWindow(SW_HIDE); } if ( SUCCEEDED(hr) ) { // reset the stats, so that we don't see anything left over from the previous run gData.Initialize(); logFile = pVS->get(GET_BSTR(DCTVS_Options_DispatchCSV)); if (lAutoCloseHideDialogs == 0) { CPropertySheet mdlg; CAgentMonitorDlg listDlg; CMainDlg summaryDlg; CLogSettingsDlg settingsDlg; CString title; title.LoadString(IDS_MainWindowTitle); listDlg.m_psp.dwFlags |= PSP_PREMATURE | PSP_HASHELP; summaryDlg.m_psp.dwFlags |= PSP_PREMATURE | PSP_HASHELP; settingsDlg.m_psp.dwFlags |= PSP_PREMATURE | PSP_HASHELP; mdlg.AddPage(&summaryDlg); mdlg.AddPage(&listDlg); mdlg.AddPage(&settingsDlg); settingsDlg.SetImmediateStart(TRUE); settingsDlg.SetDispatchLog(logFile); // this determines whether the stats for security translation will be displayed in the agent detail if ( NeedToUseST(pVS,TRUE) ) { listDlg.SetSecurityTranslationFlag(TRUE); } else { listDlg.SetSecurityTranslationFlag(FALSE); } if( !UStrICmp(wizard,L"reporting") ) { listDlg.SetReportingFlag(TRUE); } mdlg.SetActivePage(&listDlg); mdlg.SetTitle(title); UINT_PTR nResponse = mdlg.DoModal(); } else { WaitForAgentsToFinish(logFile); } // store results to database TNodeListEnum e; TServerNode * pNode; // if we are retrying an operation, don't save it to the database again! for ( pNode = (TServerNode*)e.OpenFirst(gData.GetUnsafeServerList()) ; pNode ; pNode = (TServerNode*)e.Next() ) { if ( UStrICmp(wizard,L"retry") ) { hr = pDb->raw_AddDistributedAction(SysAllocString(pNode->GetServer()),SysAllocString(pNode->GetJobPath()),pNode->GetStatus(),pNode->GetMessageText()); if ( FAILED(hr) ) { hr = S_OK; } } else { hr = pDb->raw_SetDistActionStatus(-1,pNode->GetJobPath(),pNode->GetStatus(),pNode->GetMessageText()); if ( FAILED(hr) ) { hr = S_OK; } } } } } // Call the Dispatcher post processor PostProcessDispatcher(pVS); } } if ( NeedToRunReports(pVS) ) { RunReports(pVS); } } wnd.Detach(); // Reset the undo flag so that next wizard does not have to deal with it. //* pVS->put(GET_BSTR(DCTVS_Options_Undo), L"No"); pVS->put(GET_BSTR(DCTVS_Options_Undo), GET_BSTR(IDS_No)); return hr; } STDMETHODIMP CMigrator::GetTaskDescription(IUnknown *pVarSet,/*[out]*/BSTR * pDescription) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); IVarSetPtr pVS = pVarSet; CString str; _bstr_t wizard = pVS->get(L"Options.Wizard"); _bstr_t undo = pVS->get(GET_BSTR(DCTVS_Options_Undo)); //* if ( !_wcsicmp((WCHAR*) undo, L"Yes") ) if ( !_wcsicmp((WCHAR*) undo, GET_STRING(IDS_YES)) ) { str.FormatMessage(IDS_Undo); BuildGeneralDesc(pVS, str); BuildUndoDesc(pVS,str); } else if ( !UStrICmp(wizard,L"user") ) { str.FormatMessage(IDS_UserMigration); BuildGeneralDesc(pVS,str); BuildAcctReplDesc(pVS,str); } else if ( !UStrICmp(wizard,L"group") ) { str.FormatMessage(IDS_GroupMigration); BuildGeneralDesc(pVS,str); BuildAcctReplDesc(pVS,str); } else if ( !UStrICmp(wizard,L"computer") ) { str.FormatMessage(IDS_ComputerMigration); BuildGeneralDesc(pVS,str); BuildAcctReplDesc(pVS,str); BuildSecTransDesc(pVS,str,TRUE); BuildDispatchDesc(pVS,str); } else if ( !UStrICmp(wizard,L"security") ) { str.FormatMessage(IDS_SecurityTranslation); BuildSecTransDesc(pVS,str,TRUE); BuildDispatchDesc(pVS,str); } else if ( !UStrICmp(wizard,L"reporting") ) { str.FormatMessage(IDS_ReportGeneration); BuildReportDesc(pVS,str); } else if ( !UStrICmp(wizard,L"retry") ) { str.FormatMessage(IDS_RetryTasks); } else if ( ! UStrICmp(wizard,L"service") ) { str.FormatMessage(IDS_Service); } else if ( ! UStrICmp(wizard,L"trust") ) { str.FormatMessage(IDS_TrustManagement); } else if ( !UStrICmp(wizard,L"exchangeDir") ) { BuildSecTransDesc(pVS,str,TRUE); } else if ( !UStrICmp(wizard,L"groupmapping") ) { BuildGeneralDesc(pVS,str); BuildAcctReplDesc(pVS,str); BuildGroupMappingDesc(pVS,str); } (*pDescription) = str.AllocSysString(); return S_OK; } STDMETHODIMP CMigrator::GetUndoTask(IUnknown * pVarSet,/*[out]*/ IUnknown ** ppVarSetOut) { HRESULT hr = S_OK; IVarSetPtr pVarSetIn = pVarSet; IVarSetPtr pVarSetOut; (*ppVarSetOut) = NULL; hr = pVarSetOut.CreateInstance(CLSID_VarSet); if ( SUCCEEDED(hr) ) { hr = ConstructUndoVarSet(pVarSetIn,pVarSetOut); pVarSetOut->AddRef(); (*ppVarSetOut) = pVarSetOut; } return hr; } HRESULT CMigrator::ProcessServerListForUndo(IVarSet * pVarSet) { HRESULT hr = S_OK; _bstr_t srcName; _bstr_t tgtName; WCHAR keySrc[100]; WCHAR keyTgt[100]; WCHAR keyTmp[100]; long ndx,numItems; numItems = pVarSet->get(GET_BSTR(DCTVS_Servers_NumItems)); for ( ndx = 0 ; ndx < numItems ; ndx++ ) { // if the computer was renamed, swap the source and target names swprintf(keySrc,GET_STRING(DCTVSFmt_Servers_D),ndx); swprintf(keyTgt,GET_STRING(IDS_DCTVSFmt_Servers_RenameTo_D),ndx); srcName = pVarSet->get(keySrc); tgtName = pVarSet->get(keyTgt); if ( tgtName.length() ) { if ( ((WCHAR*)tgtName)[0] != L'\\' ) { // ensure that tgtName has \\ prefix tgtName = L"\\\\" + tgtName; } if ( ((WCHAR*)srcName)[0] == L'\\' ) { // remove the \\ prefix from the new name srcName = ((WCHAR*)srcName)+2; } pVarSet->put(keySrc,tgtName); pVarSet->put(keyTgt,srcName); } swprintf(keyTmp,GET_STRING(IDS_DCTVSFmt_Servers_ChangeDomain_D),ndx); pVarSet->put(keyTmp,GET_BSTR(IDS_YES)); swprintf(keyTmp,GET_STRING(IDS_DCTVSFmt_Servers_MigrateOnly_D),ndx); pVarSet->put(keyTmp,GET_BSTR(IDS_YES)); } return hr; } HRESULT CMigrator::BuildAccountListForUndo(IVarSet * pVarSet,long actionID) { HRESULT hr = S_OK; WCHAR key[200]; long ndx; _bstr_t srcPath; IIManageDBPtr pDB; IVarSetPtr pVarSetTemp(CLSID_VarSet); IUnknown * pUnk = NULL; hr = pDB.CreateInstance(CLSID_IManageDB); if ( SUCCEEDED(hr) ) { hr = pVarSetTemp.QueryInterface(IID_IUnknown,&pUnk); if ( SUCCEEDED(hr) ) { hr = pDB->raw_GetMigratedObjects(actionID,&pUnk); } if ( SUCCEEDED(hr) ) { pUnk->Release(); srcPath = L"Test"; swprintf(key,L"MigratedObjects"); long numMigrated = pVarSetTemp->get(key); for ( ndx = 0 ; srcPath.length() ; ndx++ ) { swprintf(key,L"MigratedObjects.%d.%s",ndx,GET_STRING(DB_SourceAdsPath)); srcPath = pVarSetTemp->get(key); if ( srcPath.length() ) { // get the object type swprintf(key,L"MigratedObjects.%d.%s",ndx,GET_STRING(DB_Type)); _bstr_t text = pVarSetTemp->get(key); swprintf(key,L"Accounts.%ld.Type",ndx); //work-around a fix that places the sourcepath for an //NT 4.0 computer migration if ((text != _bstr_t(L"computer")) || (wcsncmp(L"WinNT://", (WCHAR*)srcPath, 8))) { // set the object type in the account list pVarSet->put(key,text); // copy the source path to the account list swprintf(key,L"Accounts.%ld",ndx); pVarSet->put(key,srcPath); // set the target path in the account list swprintf(key,L"MigratedObjects.%d.%s",ndx,GET_STRING(DB_TargetAdsPath)); text = pVarSetTemp->get(key); swprintf(key,L"Accounts.%ld.TargetName",ndx); pVarSet->put(key,text); } } } swprintf(key,GET_STRING(DCTVS_Accounts_NumItems)); pVarSet->put(key,numMigrated); } } return hr; } HRESULT CMigrator::ConstructUndoVarSet(IVarSet * pVarSetIn,IVarSet * pVarSetOut) { HRESULT hr = S_OK; IVarSet * pTemp = NULL; _bstr_t origSource; _bstr_t origTarget; _bstr_t origSourceDns; _bstr_t origTargetDns; _bstr_t temp; _bstr_t temp2; long actionID = pVarSetIn->get(L"ActionID"); // general options // mark the varset as an undo operation pVarSetOut->put(GET_BSTR(DCTVS_Options_Undo),GET_BSTR(IDS_YES)); temp = pVarSetIn->get(GET_BSTR(DCTVS_Options_NoChange)); if ( !UStrICmp(temp,GET_STRING(IDS_YES)) ) { // for a no-change mode operation, there's nothing to undo! return hr; } // swap the source and target domains origSource = pVarSetIn->get(GET_BSTR(DCTVS_Options_SourceDomain)); origTarget = pVarSetIn->get(GET_BSTR(DCTVS_Options_TargetDomain)); origSourceDns = pVarSetIn->get(GET_BSTR(DCTVS_Options_SourceDomainDns)); origTargetDns = pVarSetIn->get(GET_BSTR(DCTVS_Options_TargetDomainDns)); temp = pVarSetIn->get(GET_BSTR(DCTVS_Options_Logfile)); temp2 = pVarSetIn->get(GET_BSTR(DCTVS_Options_DispatchLog)); pVarSetOut->put(GET_BSTR(DCTVS_Options_Logfile),temp); // For inter-forest, leave the domain names as they were pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomain),origSource); pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomain),origTarget); pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomainDns),origSourceDns); pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomainDns),origTargetDns); // copy the account list hr = pVarSetIn->raw_getReference(GET_BSTR(DCTVS_Accounts),&pTemp); if ( SUCCEEDED(hr) ) { hr = pVarSetOut->raw_ImportSubTree(GET_BSTR(DCTVS_Accounts),pTemp); if ( SUCCEEDED(hr) ) { BuildAccountListForUndo(pVarSetOut,actionID); } pTemp->Release(); } hr = pVarSetIn->raw_getReference(SysAllocString(L"AccountOptions"),&pTemp); if ( SUCCEEDED(hr) ) { hr = pVarSetOut->raw_ImportSubTree(SysAllocString(L"AccountOptions"),pTemp); pTemp->Release(); } // and the server list hr = pVarSetIn->raw_getReference(GET_BSTR(DCTVS_Servers),&pTemp); if ( SUCCEEDED(hr) ) { hr = pVarSetOut->raw_ImportSubTree(GET_BSTR(DCTVS_Servers),pTemp); if ( SUCCEEDED(hr) ) { ProcessServerListForUndo(pVarSetOut); pTemp->Release(); } } LONG bSameForest = FALSE; MCSDCTWORKEROBJECTSLib::IAccessCheckerPtr pAccess; hr = pAccess.CreateInstance(__uuidof(MCSDCTWORKEROBJECTSLib::AccessChecker)); if ( SUCCEEDED(hr) ) { hr = pAccess->raw_IsInSameForest(origSourceDns,origTargetDns,&bSameForest); if ( hr == 8250 ) { hr = 0; bSameForest = FALSE; } } if ( SUCCEEDED(hr) ) { // for account migration, need to check whether we're cloning, or moving accounts if ( ! bSameForest ) // cloning accounts { // Since we cloned the accounts we need to delete the target accounts. // We will call the account replicator to do this. We will also call // the undo function on all the registered extensions. This way the extensions // will have a chance to cleanup after themselves in cases of UNDO. pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomain),origSource); pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomain),origTarget); } else // moving, using moveObject { // swap the source and target, and move them back, using the same options as before pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomain),origTarget); pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomain),origSource); pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomainDns),origTargetDns); pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomainDns),origSourceDns); } } // if migrating computers, swap the source and target domains, and call the dispatcher again to move them back to the source domain _bstr_t comp = pVarSetIn->get(GET_BSTR(DCTVS_AccountOptions_CopyComputers)); if ( !UStrICmp(comp,GET_STRING(IDS_YES)) ) { pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomain),origTarget); pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomain),origSource); pVarSetOut->put(GET_BSTR(DCTVS_Options_DispatchLog),temp2); pVarSetOut->put(GET_BSTR(DCTVS_Options_Wizard),L"computer"); } // security translation - don't undo return S_OK; } HRESULT CMigrator::SetReportDataInRegistry(WCHAR const * reportName,WCHAR const * filename) { TRegKey hKeyReports; DWORD rc; rc = hKeyReports.Open(GET_STRING(IDS_REGKEY_REPORTS)); // if the "Reports" registry key does not already exist, create it if ( rc == ERROR_FILE_NOT_FOUND ) { rc = hKeyReports.Create(GET_STRING(IDS_REGKEY_REPORTS)); } if ( ! rc ) { rc = hKeyReports.ValueSetStr(reportName,filename); } return HRESULT_FROM_WIN32(rc); } HRESULT CMigrator::RunReports(IVarSet * pVarSet) { HRESULT hr = S_OK; _bstr_t directory = pVarSet->get(GET_BSTR(DCTVS_Reports_Directory)); _bstr_t srcdm = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain)); _bstr_t tgtdm = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain)); _bstr_t srcdomainDNS = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomainDns)); long lAutoCloseHideDialogs = pVarSet->get(GET_BSTR(DCTVS_Options_AutoCloseHideDialogs)); IIManageDBPtr pDB; int ver; BOOL bNT4Dom = FALSE; CWorking tempDlg(IDS_NAMECONFLICTS); CWnd * wnd = NULL; MSG msg; if (lAutoCloseHideDialogs == 0) { tempDlg.Create(IDD_PLEASEWAIT); tempDlg.ShowWindow(SW_SHOW); tempDlg.m_strMessage.LoadString(IDS_STATUS_GeneratingReports); tempDlg.UpdateData(FALSE); wnd = AfxGetMainWnd(); while ( wnd && PeekMessage( &msg, wnd->m_hWnd, 0, 0, PM_NOREMOVE ) ) { if ( ! AfxGetApp()->PumpMessage() ) { break; } } AfxGetApp()->DoWaitCursor(0); } //get the source domain OS version ver = GetOSVersionForDomain(srcdomainDNS); if (ver < 5) bNT4Dom = TRUE; hr = pDB.CreateInstance(CLSID_IManageDB); if ( SUCCEEDED(hr) ) { // Migrated users and groups report _bstr_t text = pVarSet->get(GET_BSTR(DCTVS_Reports_MigratedAccounts)); if ( !UStrICmp(text,GET_STRING(IDS_YES)) ) { // run the migrated users and groups report CString filename; filename = (WCHAR*)directory; if ( filename[filename.GetLength()-1] != L'\\' ) filename += L"\\"; filename += L"MigrAcct.htm"; hr = pDB->raw_GenerateReport(SysAllocString(L"MigratedAccounts"),filename.AllocSysString(), srcdm, tgtdm, bNT4Dom); if ( SUCCEEDED(hr) ) { SetReportDataInRegistry(L"MigratedAccounts",filename); } } // migrated computers report text = pVarSet->get(GET_BSTR(DCTVS_Reports_MigratedComputers)); if ( !UStrICmp(text,GET_STRING(IDS_YES)) ) { // run the migrated computers report CString filename; filename = (WCHAR*)directory; if ( filename[filename.GetLength()-1] != L'\\' ) filename += L"\\"; filename += L"MigrComp.htm"; hr = pDB->raw_GenerateReport(SysAllocString(L"MigratedComputers"),filename.AllocSysString(), srcdm, tgtdm, bNT4Dom); if ( SUCCEEDED(hr) ) { SetReportDataInRegistry(L"MigratedComputers",filename); } } // expired computers report text = pVarSet->get(GET_BSTR(DCTVS_Reports_ExpiredComputers)); if ( !UStrICmp(text,GET_STRING(IDS_YES)) ) { // run the expired computers report CString filename; // clear the extra settings from the varset pVarSet->put(GET_BSTR(DCTVS_GatherInformation_ComputerPasswordAge),SysAllocString(L"")); filename = (WCHAR*)directory; if ( filename[filename.GetLength()-1] != L'\\' ) filename += L"\\"; filename += L"ExpComp.htm"; hr = pDB->raw_GenerateReport(SysAllocString(L"ExpiredComputers"),filename.AllocSysString(), srcdm, tgtdm, bNT4Dom); if ( SUCCEEDED(hr) ) { SetReportDataInRegistry(L"ExpiredComputers",filename); } } // account references report text = pVarSet->get(GET_BSTR(DCTVS_Reports_AccountReferences)); if ( !UStrICmp(text,GET_STRING(IDS_YES)) ) { // run the account references report CString filename; filename = (WCHAR*)directory; if ( filename[filename.GetLength()-1] != L'\\' ) filename += L"\\"; filename += L"AcctRefs.htm"; hr = pDB->raw_GenerateReport(SysAllocString(L"AccountReferences"),filename.AllocSysString(), srcdm, tgtdm, bNT4Dom); if ( SUCCEEDED(hr) ) { SetReportDataInRegistry(L"AccountReferences",filename); } // clear the extra settings from the varset pVarSet->put(GET_BSTR(DCTVS_Security_GatherInformation),GET_BSTR(IDS_No)); pVarSet->put(GET_BSTR(DCTVS_Security_ReportAccountReferences),GET_BSTR(IDS_No)); } // name conflict report text = pVarSet->get(GET_BSTR(DCTVS_Reports_NameConflicts)); if ( !UStrICmp(text,GET_STRING(IDS_YES)) ) { if (lAutoCloseHideDialogs == 0) { AfxGetApp()->DoWaitCursor(1); // run the name conflicts report tempDlg.m_strMessage.LoadString(IDS_STATUS_Gathering_NameConf); tempDlg.UpdateData(FALSE); while ( wnd && PeekMessage( &msg, wnd->m_hWnd, 0, 0, PM_NOREMOVE ) ) { if ( ! AfxGetApp()->PumpMessage() ) { break; } } } //fill the account table in the database PopulateDomainDBs(pVarSet); if (lAutoCloseHideDialogs == 0) { tempDlg.m_strMessage.LoadString(IDS_STATUS_GeneratingReports); tempDlg.UpdateData(FALSE); while ( wnd && PeekMessage( &msg, wnd->m_hWnd, 0, 0, PM_NOREMOVE ) ) { if ( ! AfxGetApp()->PumpMessage() ) { break; } } AfxGetApp()->DoWaitCursor(0); } CString filename = (WCHAR*)directory; if ( filename[filename.GetLength()-1] != L'\\' ) filename += L"\\"; filename += L"NameConf.htm"; hr = pDB->raw_GenerateReport(SysAllocString(L"NameConflicts"),filename.AllocSysString(), srcdm, tgtdm, bNT4Dom); if ( SUCCEEDED(hr) ) { SetReportDataInRegistry(L"NameConflicts",filename); } } if (lAutoCloseHideDialogs == 0) { tempDlg.ShowWindow(SW_HIDE); } } if (lAutoCloseHideDialogs == 0) { AfxGetApp()->DoWaitCursor(-1); } return hr; } //-------------------------------------------------------------------------- // PreProcessDispatcher : Pre processor swaps the source and target domains // in case of UNDO so that the computers can be // joined back to the source domain. //-------------------------------------------------------------------------- void CMigrator::PreProcessDispatcher(IVarSet * pVarSet) { _bstr_t sUndo = pVarSet->get(L"Options.Wizard"); // In the service account migration wizard, turn off any security translation tasks if ( !_wcsicmp(sUndo,L"service") ) { IVarSet * pVS2 = NULL; HRESULT hr = pVarSet->raw_getReference(L"Security",&pVS2); if ( SUCCEEDED(hr) ) { pVS2->Clear(); pVS2->Release(); } } } //-------------------------------------------------------------------------- // PostProcessDispatcher : Swaps the source and target domains back. Also sets // the Undo option to no. //-------------------------------------------------------------------------- void CMigrator::PostProcessDispatcher(IVarSet * pVarSet) { _bstr_t sUndo = pVarSet->get(L"Options.Wizard"); _bstr_t origSource = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain)); _bstr_t origTarget = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain)); if ( !_wcsicmp(sUndo, L"undo") ) { pVarSet->put(GET_BSTR(DCTVS_Options_SourceDomain), origTarget); pVarSet->put(GET_BSTR(DCTVS_Options_TargetDomain), origSource); } } void CMigrator::PreProcessForReporting(IVarSet * pVarSet) { _bstr_t text = pVarSet->get(GET_BSTR(DCTVS_Reports_Generate)); IVarSet * pVs = NULL; if ( !UStrICmp(text,GET_STRING(IDS_YES)) ) { // we are generating reports // some reports require additional information gathering. We will set up the necessary varset // keys to gather the information text = pVarSet->get(GET_BSTR(DCTVS_Reports_ExpiredComputers)); if ( !UStrICmp(text,GET_STRING(IDS_YES))) { // we need to gather the computer password age from the computers in the domain pVarSet->put(GET_BSTR(DCTVS_GatherInformation_ComputerPasswordAge),GET_BSTR(IDS_YES)); } text = pVarSet->get(GET_BSTR(DCTVS_Reports_AccountReferences)); if ( !UStrICmp(text,GET_STRING(IDS_YES))) { // clean up all the Security translation flags so that we dont end up doing // something that we were not supposed to. HRESULT hr = pVarSet->raw_getReference(GET_BSTR(DCTVS_Security), &pVs); if ( pVs ) { pVs->Clear(); pVs->Release(); } // for this one, we need to gather information from the selected computers pVarSet->put(GET_BSTR(DCTVS_Security_GatherInformation),GET_BSTR(IDS_YES)); pVarSet->put(GET_BSTR(DCTVS_Security_ReportAccountReferences),GET_BSTR(IDS_YES)); pVarSet->put(GET_BSTR(DCTVS_Security_TranslateFiles),GET_BSTR(IDS_YES)); pVarSet->put(GET_BSTR(DCTVS_Security_TranslateShares),GET_BSTR(IDS_YES)); pVarSet->put(GET_BSTR(DCTVS_Security_TranslatePrinters),GET_BSTR(IDS_YES)); pVarSet->put(GET_BSTR(DCTVS_Security_TranslateLocalGroups),GET_BSTR(IDS_YES)); pVarSet->put(GET_BSTR(DCTVS_Security_TranslateRegistry),GET_BSTR(IDS_YES)); } } } HRESULT CMigrator::TrimMigratingComputerList(IVarSet * pVarSet, bool* bAnyToDispatch) { // this functions checks each computer to be migrated, and does not migrate it if the account was not successfully copied HRESULT hr = S_OK; _bstr_t text; WCHAR key[100]; long val,i; IIManageDBPtr pDB; _bstr_t srcDomain; _bstr_t tgtDomain; _bstr_t computer; long actionID = pVarSet->get(L"ActionID"); CString temp; _bstr_t origSource = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain)); _bstr_t origTarget = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain)); *bAnyToDispatch = false; text = pVarSet->get(GET_BSTR(DCTVS_Options_Undo)); if (! UStrICmp(text,GET_STRING(IDS_YES))) { // don't trim on undo's, just reverse the domain affiliations // return S_OK; bool bFound = false; long numServers = pVarSet->get(GET_BSTR(DCTVS_Servers_NumItems)); //for each server, see if it is in the account list (only successfully //migrated computers are listed in the account list) for (i = 0; i < numServers; i++) { bFound = false; //get this servers name swprintf(key,GET_STRING(DCTVSFmt_Servers_D),i); text = pVarSet->get(key); //get rid of leading "\\" temp = (WCHAR*)text; wcscpy((WCHAR*)text, temp.Right(temp.GetLength()-2)); long numAccts = pVarSet->get(GET_BSTR(DCTVS_Accounts_NumItems)); //see if that name is in the accounts list _bstr_t text2; for (val = 0; ((val < numAccts) && (!bFound)); val++) { swprintf(key,GET_STRING(DCTVSFmt_Accounts_D),val); text2 = pVarSet->get(key); //W2K machine accounts are LDAP paths, so check for the actual name //in that path and compare with it if ( !wcsncmp(L"LDAP://", text2, 7)) { CString sAccount = (WCHAR*)text2; int nIndex; if ((nIndex = sAccount.Find(L"/CN=")) != -1)//if found { sAccount = sAccount.Right(sAccount.GetLength() - nIndex - wcslen(L"/CN=")); if ((nIndex = sAccount.Find(L',')) != -1) text2 = sAccount.Left(nIndex); } } if (! UStrICmp((WCHAR*)text,(WCHAR*)text2)) bFound = true; } swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_SkipDispatch_D),i); if (!bFound) pVarSet->put(key,GET_BSTR(IDS_YES)); else { *bAnyToDispatch = true; pVarSet->put(key,GET_BSTR(IDS_No)); } } return S_OK; } text = pVarSet->get(GET_BSTR(DCTVS_Options_NoChange)); if (! UStrICmp(text,GET_STRING(IDS_YES))) { // don't need to trim in nochange mode *bAnyToDispatch = true; //say yes run dispatcher if Nochange return S_OK; } hr = pDB.CreateInstance(CLSID_IManageDB); if ( FAILED(hr) ) { return hr; } srcDomain = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain)); tgtDomain = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain)); *bAnyToDispatch = false; //indicate that so far no accounts to dispatch val = pVarSet->get(GET_BSTR(DCTVS_Servers_NumItems)); for ( i = 0 ; i < val ; i++ ) { //init the skipDispath flag to "No" swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_SkipDispatch_D),i); pVarSet->put(key,GET_BSTR(IDS_No)); swprintf(key,GET_STRING(DCTVSFmt_Servers_D),i); computer = pVarSet->get(key); swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_ChangeDomain_D),i); text = pVarSet->get(key); if (! UStrICmp(text,GET_STRING(IDS_YES)) ) { // we are migrating this computer to a different domain // check our database to verify that the computer account has been // successfully migrated computer += L"$"; IVarSetPtr pVS(CLSID_VarSet); IUnknown * pUnk = NULL; hr = pVS->QueryInterface(IID_IUnknown,(void**)&pUnk); if ( SUCCEEDED(hr) ) { if ( ((WCHAR*)computer)[0] == L'\\' ) { // leave off the leading \\'s hr = pDB->raw_GetAMigratedObject(SysAllocString(((WCHAR*)computer) + 2),srcDomain,tgtDomain,&pUnk); } else { hr = pDB->raw_GetAMigratedObject(computer,srcDomain,tgtDomain,&pUnk); } if ( hr == S_OK ) { // the computer was found in the migrated objects table // make sure we are using its correct target name, if it has been renamed swprintf(key,L"MigratedObjects.TargetSamName"); _bstr_t targetName = pVS->get(key); swprintf(key,L"MigratedObjects.SourceSamName"); _bstr_t sourceName = pVS->get(key); long id = pVS->get(L"MigratedObjects.ActionID"); if ( UStrICmp((WCHAR*)sourceName,(WCHAR*)targetName) ) { // the computer is being renamed swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RenameTo_D),i); // strip off the $ from the end of the target name, if specified WCHAR target[LEN_Account]; safecopy(target,(WCHAR*)targetName); if ( target[UStrLen(target)-1] == L'$' ) { target[UStrLen(target)-1] = 0; } pVarSet->put(key,target); } if ( id != actionID ) { // the migration failed, but this computer had been migrated before // don't migrate the computer because it's account in the target domain, won't be reset // and it will therefore be locked out of the domain swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_ChangeDomain_D),i); pVarSet->put(key,GET_BSTR(IDS_No)); swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_Reboot_D),i); pVarSet->put(key,GET_BSTR(IDS_No)); swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_SkipDispatch_D),i); pVarSet->put(key,GET_BSTR(IDS_YES)); } else *bAnyToDispatch = true; //atleast one server for dispatcher } else { // the computer migration failed! // don't migrate the computer because it won't have it's account in the target domain, // and will therefore be locked out of the domain pVarSet->put(key,GET_BSTR(IDS_No)); swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_Reboot_D),i); pVarSet->put(key,GET_BSTR(IDS_No)); swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_SkipDispatch_D),i); pVarSet->put(key,GET_BSTR(IDS_YES)); } pUnk->Release(); } } else *bAnyToDispatch = true; //atleast one server for dispatcher } return hr; } HRESULT CMigrator::PopulateAccounts(IVarSetPtr pVs) { _bstr_t origSource = pVs->get(GET_BSTR(DCTVS_Options_SourceDomain)); _bstr_t origTarget = pVs->get(GET_BSTR(DCTVS_Options_TargetDomain)); // Check if the source domain is NT4 or win2k // if NT4 then call the NetObjEnum to enumerate the domain. return S_OK; } //---------------------------------------------------------------------------- // PopulateDomainDBs : This function coordinates the populating of the Access // DBs for both the source and target domains with the // necessary fields from the AD. //---------------------------------------------------------------------------- bool CMigrator::PopulateDomainDBs( IVarSet * pVarSet //in- varset with domain names. ) { /* local variables */ _bstr_t srcdomain = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain)); _bstr_t tgtdomain = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain)); _bstr_t srcdomainDNS = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomainDns)); _bstr_t tgtdomainDNS = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomainDns)); /* function body */ //populate the DB for the source domain PopulateADomainDB(srcdomain, srcdomainDNS, TRUE); //populate the DB for the target domain PopulateADomainDB(tgtdomain, tgtdomainDNS, FALSE); return true; } //---------------------------------------------------------------------------- // PopulateADomainDB : This function looks up the necessary fields from the AD, // using an MCSNetObjectEnum object, for the given domain // and populates the corresponding Access DB with that info. //---------------------------------------------------------------------------- bool CMigrator::PopulateADomainDB( WCHAR const *domain, // in- NetBIOS name of domain to enumerate WCHAR const *domainDNS, // in- DSN name of domain to enumerate BOOL bSource ) { INetObjEnumeratorPtr pQuery(__uuidof(NetObjEnumerator)); IIManageDBPtr pDb; WCHAR sPath[MAX_PATH]; WCHAR sQuery[MAX_PATH]; LPWSTR sData[] = { L"sAMAccountName", L"ADsPath" }; HRESULT hr; long nElt = DIM(sData); // int nobjectType; BSTR HUGEP * pData = NULL; SAFEARRAY * pszColNames; IEnumVARIANT * pEnum = NULL; _variant_t var; DWORD dwFetch = 1; bool bSuccess = false; BSTR iterator[] = { L"USER", L"GROUP", L"COMPUTER" }; int i = 0; int ver = 0; bool bW2KDom = false; // create instance of database manager hr = pDb.CreateInstance(__uuidof(IManageDB)); if (FAILED(hr)) return false; if ( bSource ) pDb->raw_ClearTable(L"SourceAccounts"); else pDb->raw_ClearTable(L"TargetAccounts"); pDb->raw_OpenAccountsTable(bSource); ver = GetOSVersionForDomain(domainDNS); // iterate three times once to get USERS, GROUPS, COMPUTERS (mainly for WinNT) while ( i < 3 ) { // Set the LDAP path to the whole domain and then the query everything if ( ver > 4 ) { wsprintf(sPath, L"LDAP://%s", domainDNS); wsprintf(sQuery, L"(objectClass=%s)", (WCHAR*) iterator[i]); bW2KDom = true; } else { wsprintf(sPath, L"CN=%sS", (WCHAR*)iterator[i]); wcscpy(sQuery, L"(objectClass=*)"); bW2KDom = false; } // Set the enumerator query hr = pQuery->raw_SetQuery(sPath, const_cast(domainDNS), sQuery, ADS_SCOPE_SUBTREE, FALSE); if (SUCCEEDED(hr)) { // Create a safearray of columns we need from the enumerator. SAFEARRAYBOUND bd = { nElt, 0 }; pszColNames = ::SafeArrayCreate(VT_BSTR, 1, &bd); HRESULT hr = ::SafeArrayAccessData(pszColNames, (void HUGEP **)&pData); if ( SUCCEEDED(hr) ) { for( long i = 0; i < nElt; i++) { pData[i] = SysAllocString(sData[i]); } hr = ::SafeArrayUnaccessData(pszColNames); } if (SUCCEEDED(hr)) { // Set the columns on the enumerator object. hr = pQuery->raw_SetColumns(pszColNames); } } if (SUCCEEDED(hr)) { // Now execute. hr = pQuery->raw_Execute(&pEnum); } //while we have more enumerated objects, get the enumerated fields //for that object, save them in local variables, and add them to //the appropriate DB HRESULT hrEnum = S_OK; while (hrEnum == S_OK && dwFetch > 0) { //get the enumerated fields for this current object hrEnum = pEnum->Next(1, &var, &dwFetch); if ( dwFetch > 0 && hrEnum == S_OK && ( var.vt & VT_ARRAY) ) { _bstr_t sAdsPath; _bstr_t sDN = L""; _bstr_t sSAMName; _bstr_t sRDN = L""; _bstr_t sCanonicalName = L""; _bstr_t sObjectClass; BOOL bSave = TRUE; // We now have a Variant containing an array of variants so we access the data _variant_t * pVar; pszColNames = V_ARRAY(&var); SafeArrayAccessData(pszColNames, (void HUGEP **)&pVar); //get the sAMAccountName field sSAMName = pVar[0].bstrVal; //create an RDN from the SAM name, will be replaced below with real RDN //if not NT4.0 source domain sRDN = L"CN=" + sSAMName; //get the ADsPath field sAdsPath = pVar[1].bstrVal; //get the objectClass field sObjectClass = (WCHAR*) iterator[i]; SafeArrayUnaccessData(pszColNames); //for W2K domains, computers get enumerated twice, once as a computer //and once as a user, so for any item of type computer we will not add //it to the database, place the computer account name in a string list, //and when we see that name again change it's type from user to computer. //this code assumes the enumeration above enumerates users prior to computers if (bW2KDom) { /* for W2K domain, get the RDN and Canonical Name of this object */ //connect to the object IADs * pAds = NULL; hr = ADsGetObject(sAdsPath, IID_IADs, (void**)&pAds); if ( SUCCEEDED(hr) ) { //get the CN BSTR sName = NULL; hr = pAds->get_Name(&sName); if (SUCCEEDED(hr)) sRDN = _bstr_t(sName, false); //get the DN _variant_t varDN; hr = pAds->Get(L"distinguishedName", &varDN); if (SUCCEEDED(hr)) sDN = varDN.bstrVal; pAds->Release(); } //convert from DN to canonical name if (sDN.length()) { HANDLE hDs = NULL; //bind to the domain hr = DsBind(NULL, domainDNS, &hDs); if (SUCCEEDED(hr)) { PDS_NAME_RESULT pNamesOut = NULL; WCHAR * pNamesIn[1]; pNamesIn[0] = (WCHAR *)sDN; //convert the name hr = DsCrackNames(hDs, DS_NAME_FLAG_SYNTACTICAL_ONLY, DS_FQDN_1779_NAME, DS_CANONICAL_NAME, 1, pNamesIn, &pNamesOut); DsUnBind(&hDs); //unbind if (SUCCEEDED(hr)) { //if name converted, get it if (pNamesOut->rItems[0].status == DS_NAME_NO_ERROR) sCanonicalName = pNamesOut->rItems[0].pName; DsFreeNameResult(pNamesOut); //release the buffer } }//end if Bound }//end if got DN /* adjust for computer issue */ //if computer, delete from user list so it will not be created again later if (!_wcsicmp((WCHAR*)sObjectClass, L"computer")) DeleteItemFromList(sSAMName); //if user see if the name is in the computer list, if so, //don't store in database if ((!_wcsicmp((WCHAR*)sObjectClass, L"user")) && (((WCHAR*)sSAMName)[sSAMName.length() - 1] == L'$')) { //store fields for database entry in struct for later inclusion DATABASE_ENTRY aListItem; aListItem.m_domain = domain; aListItem.m_sSAMName = sSAMName; aListItem.m_sObjectClass = sObjectClass; aListItem.m_sRDN = sRDN; aListItem.m_sCanonicalName = sCanonicalName; aListItem.m_bSource = bSource; mUserList.AddTail(aListItem); //add item to user list bSave = FALSE; } } //use the DBManager Interface to store this object's fields //in the appropriate database if (bSave) { hr = pDb->raw_AddSourceObject(const_cast(domain), sSAMName, sObjectClass, sRDN, sCanonicalName, bSource); if ( FAILED(hr) ) int x = 0; } VariantInit(&var); } } if ( pEnum ) pEnum->Release(); i++; } // while //add each user in the user list to the database since it was not //matched with a computer name already entered POSITION pos = mUserList.GetHeadPosition(); DATABASE_ENTRY aListItem; while (pos != NULL) { aListItem = mUserList.GetNext(pos); hr = pDb->raw_AddSourceObject(aListItem.m_domain, aListItem.m_sSAMName, aListItem.m_sObjectClass, aListItem.m_sRDN, aListItem.m_sCanonicalName, aListItem.m_bSource); if ( FAILED(hr) ) int x = 0; } mUserList.RemoveAll(); pDb->raw_CloseAccountsTable(); return SUCCEEDED(hr); } DWORD CMigrator::GetOSVersionForDomain(WCHAR const * domain) { // Load DsGetDcName dynamically DOMAIN_CONTROLLER_INFO * pSrcDomCtrlInfo = NULL; WKSTA_INFO_100 * pInfo = NULL; DWORD retVal = 0; DSGETDCNAME DsGetDcName = NULL; HMODULE hPro = LoadLibrary(L"NetApi32.dll"); if ( hPro ) { DsGetDcName = (DSGETDCNAME)GetProcAddress(hPro, "DsGetDcNameW"); if ( DsGetDcName ) { DWORD rc = DsGetDcName( NULL ,// LPCTSTR ComputerName ? const_cast(domain) ,// LPCTSTR DomainName NULL ,// GUID *DomainGuid ? NULL ,// LPCTSTR SiteName ? 0 ,// ULONG Flags ? &pSrcDomCtrlInfo // PDOMAIN_CONTROLLER_INFO *DomainControllerInfo ); if ( !rc ) { rc = NetWkstaGetInfo(pSrcDomCtrlInfo->DomainControllerName,100,(LPBYTE*)&pInfo); if ( ! rc ) { retVal = pInfo->wki100_ver_major; NetApiBufferFree(pInfo); } NetApiBufferFree(pSrcDomCtrlInfo); } } FreeLibrary(hPro); } return retVal; } BOOL CMigrator::DeleteItemFromList(WCHAR const * aName) { DATABASE_ENTRY aListItem; CString itemName; POSITION pos, lastpos; BOOL bFound = FALSE; pos = mUserList.GetHeadPosition(); while ((pos != NULL) && (!bFound)) { lastpos = pos; aListItem = mUserList.GetNext(pos); itemName = (WCHAR*)(aListItem.m_sSAMName); if (itemName == aName) { mUserList.RemoveAt(lastpos); bFound = TRUE; } } return bFound; } /********************************************************************* * * * Written by: Paul Thompson * * Date: 11 OCT 2000 * * * * This function is responsible for opening the sid mapping file * * whether it is anm ANSI or UNICODE file and return the file * * pointer. * * * *********************************************************************/ //BEGIN RetrieveSrcDomainSid void CMigrator::RetrieveSrcDomainSid( IVarSet * pVarSet, //in- varset with domain names. IIManageDBPtr pDb //in - pointer to the DB manager class ) { /* local variables */ IVarSetPtr pVarSetTemp(CLSID_VarSet); IUnknown * pUnk = NULL; HRESULT hr; WCHAR key[200]; _bstr_t srcdomain = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain)); _bstr_t sSrcDomain; /* function body */ hr = pVarSetTemp.QueryInterface(IID_IUnknown,&pUnk); if ( SUCCEEDED(hr) ) { hr = pDb->raw_GetMigratedObjects(-1,&pUnk); } if ( SUCCEEDED(hr) ) { pUnk->Release(); long lCnt = pVarSetTemp->get("MigratedObjects"); bool bFound = false; for ( long l = 0; (l < lCnt) && (!bFound); l++) { //get the source domain swprintf(key,L"MigratedObjects.%ld.%s",l,GET_STRING(DB_SourceDomain)); sSrcDomain = pVarSetTemp->get(key); //if source domain matches, see if the sid was stored if (!UStrICmp((WCHAR*)srcdomain, (WCHAR*)sSrcDomain)) { //get the source domain Sid swprintf(key,L"MigratedObjects.%ld.%s",l,GET_STRING(DB_SourceDomainSid)); _variant_t varSid = pVarSetTemp->get(key); if (varSid.vt != VT_NULL) { _bstr_t txtSid = pVarSetTemp->get(key); pVarSet->put(GET_BSTR(DCTVS_Options_SourceDomainSid),txtSid); bFound = true; } } } } }//END RetrieveSrcDomainSid // IsAgentOrDispatcherProcessRunning bool __stdcall IsAgentOrDispatcherProcessRunning() { bool bIsRunning = true; CMigrationMutex mutexAgent(AGENT_MUTEX); CMigrationMutex mutexDispatcher(DISPATCHER_MUTEX); if (mutexAgent.ObtainOwnership(30000) && mutexDispatcher.ObtainOwnership(30000)) { bIsRunning = false; } return bIsRunning; } // SetDomainControllers // // Sets preferred domain controllers to be used // by the account replicator and dispatched agents void __stdcall SetDomainControllers(IVarSetPtr& spVarSet) { // set source domain controller _bstr_t strSourceServer = spVarSet->get(GET_BSTR(DCTVS_Options_SourceServerOverride)); if (strSourceServer.length() == 0) { _bstr_t strSourceDomain = spVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain)); PDOMAIN_CONTROLLER_INFO pdci; DWORD dwError = DsGetDcName( NULL, strSourceDomain, NULL, NULL, DS_IS_FLAT_NAME|DS_RETURN_FLAT_NAME, &pdci ); if (dwError == ERROR_SUCCESS) { strSourceServer = pdci->DomainControllerName; NetApiBufferFree(pdci); } } spVarSet->put(GET_BSTR(DCTVS_Options_SourceServer), strSourceServer); // set target domain controller _bstr_t strTargetServer = spVarSet->get(GET_BSTR(DCTVS_Options_TargetServerOverride)); if (strTargetServer.length() == 0) { _bstr_t strTargetDomain = spVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain)); PDOMAIN_CONTROLLER_INFO pdci; DWORD dwError = DsGetDcName( NULL, strTargetDomain, NULL, NULL, DS_IS_FLAT_NAME|DS_RETURN_FLAT_NAME, &pdci ); if (dwError == ERROR_SUCCESS) { strTargetServer = pdci->DomainControllerName; NetApiBufferFree(pdci); } } spVarSet->put(GET_BSTR(DCTVS_Options_TargetServer), strTargetServer); }