Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2136 lines
75 KiB

  1. /*---------------------------------------------------------------------------
  2. File: Migrator.cpp
  3. Comments: Implementation of McsMigrationDriver COM object.
  4. This object encapsulates the knowledge of when to call the local engine,
  5. and when to call the dispatcher.
  6. It will also provide a description of the tasks to be performed, for display
  7. on the last page of each migration wizard, and will be responsible for calculating
  8. the actions required to undo an operation (this is not yet implemented).
  9. (c) Copyright 1999, Mission Critical Software, Inc., All Rights Reserved
  10. Proprietary and confidential to Mission Critical Software, Inc.
  11. REVISION LOG ENTRY
  12. Revision By: Christy Boles
  13. ---------------------------------------------------------------------------
  14. */// Migrator.cpp : Implementation of CMcsMigrationDriverApp and DLL registration.
  15. #include "stdafx.h"
  16. #include "MigDrvr.h"
  17. //#import "\bin\McsVarSetMin.tlb" no_namespace, named_guids
  18. //#import "\bin\McsEADCTAgent.tlb" no_namespace, named_guids
  19. //#import "\bin\McsDispatcher.tlb" no_namespace, named_guids
  20. //#import "\bin\DBManager.tlb" no_namespace, named_guids
  21. //#import "\bin\McsDctWorkerObjects.tlb"
  22. //#import "\bin\NetEnum.tlb" no_namespace
  23. #import "VarSet.tlb" no_namespace, named_guids rename("property", "aproperty")
  24. //#import "Engine.tlb" no_namespace, named_guids //#imported via DetDlg.h below
  25. #import "Dispatch.tlb" no_namespace, named_guids
  26. #import "WorkObj.tlb"
  27. #import "NetEnum.tlb" no_namespace
  28. #include <iads.h>
  29. #include <adshlp.h>
  30. #include <dsgetdc.h>
  31. #include <Ntdsapi.h>
  32. #include <lm.h>
  33. #include <Psapi.h>
  34. #include <map>
  35. #pragma comment(lib, "Psapi.lib")
  36. #include "Migrator.h"
  37. #include "TaskChk.h"
  38. #include "ResStr.h"
  39. // dialogs used
  40. #include "DetDlg.h"
  41. #include "MonDlg.h"
  42. #include "SetDlg.h"
  43. #include "MainDlg.h"
  44. #include "Working.h"
  45. #include "ErrDct.hpp"
  46. #include "TReg.hpp"
  47. #include "EaLen.hpp"
  48. #include <MigrationMutex.h>
  49. #include <AdsiHelpers.h>
  50. #include <NtLdap.h>
  51. #include "GetDcName.h"
  52. //#define MAX_DB_FIELD 255
  53. // Opertation flags to be performed on the Account
  54. #define OPS_Create_Account (0x00000001)
  55. #define OPS_Copy_Properties (0x00000002)
  56. #define OPS_Process_Members (0x00000004)
  57. #define OPS_Process_MemberOf (0x00000008)
  58. #define OPS_Call_Extensions (0x00000010)
  59. #define OPS_All OPS_Create_Account | OPS_Copy_Properties | OPS_Process_Members | OPS_Process_MemberOf | OPS_Call_Extensions
  60. #define OPS_Copy OPS_Create_Account | OPS_Copy_Properties
  61. BOOL gbCancelled = FALSE;
  62. bool __stdcall IsAgentOrDispatcherProcessRunning();
  63. DWORD __stdcall SetDomainControllers(IVarSetPtr& spVarSet);
  64. #ifndef IADsPtr
  65. _COM_SMARTPTR_TYPEDEF(IADs, IID_IADs);
  66. #endif
  67. /////////////////////////////////////////////////////////////////////////////
  68. //
  69. BOOL // ret - TRUE if found program directory in the registry
  70. GetProgramDirectory(
  71. WCHAR * filename // out - buffer that will contain path to program directory
  72. )
  73. {
  74. DWORD rc = 0;
  75. BOOL bFound = FALSE;
  76. TRegKey key;
  77. rc = key.OpenRead(GET_STRING(IDS_HKLM_DomainAdmin_Key),HKEY_LOCAL_MACHINE);
  78. if ( ! rc )
  79. {
  80. rc = key.ValueGetStr(L"Directory",filename,MAX_PATH*sizeof(WCHAR));
  81. if ( ! rc )
  82. {
  83. if ( *filename )
  84. bFound = TRUE;
  85. }
  86. }
  87. return bFound;
  88. }
  89. BOOL // ret - TRUE if found program directory in the registry
  90. GetLogLevel(
  91. DWORD * level // out - value that should be used for log level
  92. )
  93. {
  94. DWORD rc = 0;
  95. BOOL bFound = FALSE;
  96. TRegKey key;
  97. rc = key.OpenRead(GET_STRING(IDS_HKLM_DomainAdmin_Key),HKEY_LOCAL_MACHINE);
  98. if ( ! rc )
  99. {
  100. rc = key.ValueGetDWORD(L"TranslationLogLevel",level);
  101. if ( ! rc )
  102. {
  103. bFound = TRUE;
  104. }
  105. }
  106. return bFound;
  107. }
  108. //----------------------------------------------------------------------------
  109. // Function: GetAllowSwitching
  110. //
  111. // Synopsis: Read REG_DWORD value of HKLM\Software\Microsoft\ADMT
  112. // \DisallowFallbackToAddInProfileTranslation. If the value is
  113. // set to 1, *bAllowed is set to FALSE. Otherwise, *bAllow is
  114. // set to TRUE. If there is any error (except for ERROR_FILE_NOT_FOUND)
  115. // when reading this key, the rc value will be returned.
  116. //
  117. // Arguments:
  118. //
  119. // bAllowed Pointer to BOOL
  120. //
  121. // Returns: ERROR_SUCCESS if successful; otherwise an error code
  122. //
  123. // Modifies: None
  124. //
  125. //----------------------------------------------------------------------------
  126. DWORD
  127. GetAllowSwitching(
  128. BOOL *bAllowed
  129. )
  130. {
  131. DWORD rc = ERROR_SUCCESS;
  132. DWORD value;
  133. TRegKey key;
  134. *bAllowed = TRUE;
  135. rc = key.OpenRead(GET_STRING(IDS_HKLM_DomainAdmin_Key),HKEY_LOCAL_MACHINE);
  136. if (rc == ERROR_SUCCESS)
  137. {
  138. rc = key.ValueGetDWORD(L"DisallowFallbackToAddInProfileTranslation", &value);
  139. if (rc == ERROR_SUCCESS)
  140. {
  141. if (value == 1)
  142. *bAllowed = FALSE;
  143. }
  144. else if (rc == ERROR_FILE_NOT_FOUND)
  145. rc = ERROR_SUCCESS;
  146. }
  147. return rc;
  148. }
  149. HRESULT CMigrator::ViewPreviousDispatchResults()
  150. {
  151. _bstr_t logFile;
  152. if ( logFile.length() == 0 )
  153. {
  154. WCHAR path[MAX_PATH];
  155. if (!GetProgramDirectory(path))
  156. {
  157. DWORD rc = GetLastError();
  158. return HRESULT_FROM_WIN32(rc);
  159. }
  160. logFile = path;
  161. logFile += L"Logs\\Dispatcher.csv";
  162. }
  163. // reset the stats, so that we don't see anything left over from the previous run
  164. gData.Initialize();
  165. CPropertySheet mdlg;
  166. CAgentMonitorDlg listDlg;
  167. CMainDlg summaryDlg;
  168. CLogSettingsDlg settingsDlg;
  169. listDlg.m_psp.dwFlags |= PSP_PREMATURE | PSP_HASHELP;
  170. summaryDlg.m_psp.dwFlags |= PSP_PREMATURE | PSP_HASHELP;
  171. settingsDlg.m_psp.dwFlags |= PSP_PREMATURE | PSP_HASHELP;
  172. mdlg.AddPage(&summaryDlg);
  173. mdlg.AddPage(&listDlg);
  174. mdlg.AddPage(&settingsDlg);
  175. settingsDlg.SetImmediateStart(TRUE);
  176. settingsDlg.SetDispatchLog(logFile);
  177. mdlg.SetActivePage(&listDlg);
  178. // UINT nResponse = mdlg.DoModal();
  179. UINT_PTR nResponse = mdlg.DoModal();
  180. return S_OK;
  181. }
  182. // WaitForAgentsToFinish Method
  183. //
  184. // Waits for dispatcher and all dispatched agents to complete
  185. // their tasks.
  186. // Used when ADMT is run from script or command line.
  187. static void WaitForAgentsToFinish(_bstr_t strLogPath)
  188. {
  189. gData.SetLogPath(strLogPath);
  190. CloseHandle(CreateThread(NULL, 0, &ResultMonitorFn, NULL, 0, NULL));
  191. CloseHandle(CreateThread(NULL, 0, &LogReaderFn, NULL, 0, NULL));
  192. LARGE_INTEGER liDueTime;
  193. liDueTime.QuadPart = -50000000; // 5 sec
  194. HANDLE hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
  195. for (int nState = 0; nState < 3;)
  196. {
  197. SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, FALSE);
  198. if (WaitForSingleObject(hTimer, INFINITE) == WAIT_OBJECT_0)
  199. {
  200. BOOL bDone = FALSE;
  201. switch (nState)
  202. {
  203. case 0: // first pass of dispatcher log
  204. {
  205. gData.GetFirstPassDone(&bDone);
  206. break;
  207. }
  208. case 1: // dispatcher finished
  209. {
  210. gData.GetLogDone(&bDone);
  211. break;
  212. }
  213. case 2: // agents finished
  214. {
  215. gData.GetDone(&bDone);
  216. break;
  217. }
  218. }
  219. if (bDone)
  220. {
  221. ++nState;
  222. }
  223. }
  224. else
  225. {
  226. break;
  227. }
  228. }
  229. CloseHandle(hTimer);
  230. }
  231. //----------------------------------------------------------------------------
  232. // Function: LogAgentStatus
  233. //
  234. // Synopsis: Create an agent status summary section which looks like the
  235. // following:
  236. //
  237. // ***** start of agent completion status summary *****
  238. // Monitoring was stopped early so some agents might still be running ...
  239. // Machine Name Completion Status Error Message Log File Path
  240. // .....
  241. // ***** end of agent completion status summary *****
  242. //
  243. // The line "Monitoring was stopped early ..." is added only when
  244. // the user stops the monitoring before all agents have completed.
  245. //
  246. // Arguments:
  247. // teErrLog: the error log pointer to write status summary to
  248. // tslServerList: the list of server nodes
  249. // bForcedToStopMonitoring: the monitoring was forced to stop by the user
  250. //
  251. // Returns:
  252. //
  253. // Modifies: It updates the bstrStatusForLogging, bstrErrorMessageForLogging
  254. // and dwStatusForLogging member variables.
  255. //
  256. //----------------------------------------------------------------------------
  257. void LogAgentStatus(TError& teErrLog, TServerList* tslServerList, BOOL bForcedToStopMonitoring)
  258. {
  259. CString cstrPrelog;
  260. CString cstrForcedToStopMonitoring;
  261. CString cstrEpilog;
  262. CString cstrMachineNameTitle;
  263. CString cstrCompletionStatusTitle;
  264. CString cstrErrorMessageTitle;
  265. CString cstrLogFilePathTitle;
  266. cstrPrelog.LoadString(IDS_CompletionStatusLoggingPrelog);
  267. cstrForcedToStopMonitoring.LoadString(IDS_CompletionStatusLoggingForcedToStopMonitoring);
  268. cstrEpilog.LoadString(IDS_CompletionStatusLoggingEpilog);
  269. cstrMachineNameTitle.LoadString(IDS_CompletionStatusLoggingMachineNameTitle);
  270. cstrCompletionStatusTitle.LoadString(IDS_CompletionStatusLoggingCompletionStatusTitle);
  271. cstrErrorMessageTitle.LoadString(IDS_CompletionStatusLoggingErrorMessageTitle);
  272. cstrLogFilePathTitle.LoadString(IDS_CompletionStatusLoggingLogFilePathTitle);
  273. int maxServerNameLen = wcslen((LPCWSTR)cstrMachineNameTitle);
  274. int maxCompletionStatusLen = wcslen((LPCWSTR)cstrCompletionStatusTitle);
  275. int maxErrorMessageLen = wcslen((LPCWSTR)cstrErrorMessageTitle);
  276. int maxLogFilePathLen = wcslen((LPCWSTR)cstrLogFilePathTitle);
  277. int maxStatus = 0;
  278. // print prelog
  279. teErrLog.MsgWrite(0, (LPCWSTR)cstrPrelog);
  280. if (bForcedToStopMonitoring)
  281. teErrLog.MsgWrite(0, (LPCWSTR)cstrForcedToStopMonitoring);
  282. TNodeListEnum e;
  283. TServerNode* pNode;
  284. typedef std::multimap<DWORD, TServerNode*> CStatusToServerNode;
  285. CStatusToServerNode aMap;
  286. // calculate the maximum length for each column and sort nodes based on the completion status
  287. for (pNode = (TServerNode*)e.OpenFirst(tslServerList); pNode; pNode = (TServerNode*)e.Next())
  288. {
  289. pNode->PrepareForLogging();
  290. int len;
  291. len = wcslen(pNode->GetServer());
  292. if (maxServerNameLen < len)
  293. maxServerNameLen = len;
  294. len = wcslen(pNode->GetStatusForLogging());
  295. if (maxCompletionStatusLen < len)
  296. maxCompletionStatusLen = len;
  297. len = wcslen(pNode->GetErrorMessageForLogging());
  298. if (maxErrorMessageLen < len)
  299. maxErrorMessageLen = len;
  300. len = wcslen(pNode->GetLogPath());
  301. if (maxLogFilePathLen < len)
  302. maxLogFilePathLen = len;
  303. aMap.insert(CStatusToServerNode::value_type(pNode->GetStatusNumberForLogging(), pNode));
  304. }
  305. // determine the table format
  306. WCHAR format[100];
  307. int bufSize = sizeof(format)/sizeof(format[0]);
  308. if (_snwprintf(format,
  309. bufSize,
  310. L"%%-%ds\t%%-%ds\t%%-%ds\t%%-%ds",
  311. maxServerNameLen,
  312. maxCompletionStatusLen,
  313. maxErrorMessageLen,
  314. maxLogFilePathLen) < 0)
  315. format[bufSize - 1] = L'\0';
  316. // print out the column names
  317. teErrLog.MsgWrite(0,
  318. format,
  319. (LPCWSTR)cstrMachineNameTitle,
  320. (LPCWSTR)cstrCompletionStatusTitle,
  321. (LPCWSTR)cstrErrorMessageTitle,
  322. (LPCWSTR)cstrLogFilePathTitle);
  323. // print out the agent completion status information, sorted by completion status
  324. for (CStatusToServerNode::const_iterator it = aMap.begin(); it != aMap.end(); it++)
  325. {
  326. pNode = it->second;
  327. teErrLog.MsgWrite(0,
  328. format,
  329. pNode->GetServer(),
  330. pNode->GetStatusForLogging(),
  331. pNode->GetErrorMessageForLogging(),
  332. pNode->GetLogPath());
  333. }
  334. // print epilog
  335. teErrLog.MsgWrite(0, (LPCWSTR)cstrEpilog);
  336. }
  337. STDMETHODIMP CMigrator::PerformMigrationTask(IUnknown* punkVarSet, LONG_PTR hWnd)
  338. {
  339. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  340. HRESULT hr = S_OK;
  341. IVarSetPtr pVS = punkVarSet;
  342. BSTR jobID = NULL;
  343. CWnd wnd;
  344. long lActionID = -2;
  345. IIManageDBPtr pDb;
  346. _bstr_t wizard = pVS->get(L"Options.Wizard");
  347. _bstr_t undo;
  348. _bstr_t viewPreviousResults = pVS->get(L"MigrationDriver.ViewPreviousResults");
  349. bool bAnyToDispatch = true;
  350. long lAutoCloseHideDialogs = pVS->get(GET_BSTR(DCTVS_Options_AutoCloseHideDialogs));
  351. // if agent or dispatcher process still running...
  352. if (IsAgentOrDispatcherProcessRunning())
  353. {
  354. // return error result
  355. return MIGRATOR_E_PROCESSES_STILL_RUNNING;
  356. }
  357. hr = pDb.CreateInstance(__uuidof(IManageDB));
  358. if (FAILED(hr))
  359. {
  360. return hr;
  361. }
  362. gbCancelled = FALSE;
  363. // This provides an easy way to view the previous dispatch results
  364. if ( !UStrICmp(viewPreviousResults,GET_STRING(IDS_YES)) )
  365. {
  366. return ViewPreviousDispatchResults();
  367. }
  368. if (_bstr_t(pVS->get(GET_BSTR(DCTVS_Options_DontBeginNewLog))) != GET_BSTR(IDS_YES))
  369. {
  370. // begin a new log
  371. TError err;
  372. err.LogOpen(_bstr_t(pVS->get(GET_BSTR(DCTVS_Options_Logfile))), 0, 0, true);
  373. err.LogClose();
  374. }
  375. // get the setting for whether to allow switching from REPLACE to ADD for profile translation
  376. BOOL bAllowed = TRUE;
  377. GetAllowSwitching(&bAllowed);
  378. pVS->put(GET_BSTR(DCTVS_Options_AllowSwitchingFromReplaceToAddInProfileTranslation),
  379. bAllowed ? GET_BSTR(IDS_YES) : GET_BSTR(IDS_No));
  380. // update the log level, if needed
  381. DWORD level = 0;
  382. if( GetLogLevel(&level) )
  383. {
  384. pVS->put(GET_BSTR(DCTVS_Options_LogLevel),(long)level);
  385. }
  386. undo = pVS->get(GET_BSTR(DCTVS_Options_Undo));
  387. if ( !_wcsicmp((WCHAR*) undo, GET_STRING(IDS_YES)) )
  388. {
  389. hr = pDb->raw_GetCurrentActionID(&lActionID);
  390. if ( SUCCEEDED(hr) )
  391. pVS->put(L"UndoAction", lActionID);
  392. hr = pDb->raw_GetNextActionID(&lActionID);
  393. hr = 0;
  394. }
  395. else
  396. {
  397. hr = pDb->raw_GetNextActionID(&lActionID);
  398. if ( SUCCEEDED(hr) )
  399. {
  400. pVS->put(L"ActionID",lActionID);
  401. _bstr_t password2 = pVS->get(GET_BSTR(DCTVS_AccountOptions_SidHistoryCredentials_Password));
  402. pVS->put(GET_BSTR(DCTVS_AccountOptions_SidHistoryCredentials_Password),L"");
  403. hr = pDb->raw_SetActionHistory(lActionID, punkVarSet);
  404. pVS->put(GET_BSTR(DCTVS_AccountOptions_SidHistoryCredentials_Password),password2);
  405. if ( FAILED(hr) )
  406. {
  407. // log a message, but don't abort the whole operation
  408. hr = S_OK;
  409. }
  410. }
  411. }
  412. // This sets up any varset keys needed internally for reports to be generated
  413. PreProcessForReporting(pVS);
  414. wnd.Attach((HWND)hWnd);
  415. // set preferred domain controllers to be used
  416. // by the account replicator and dispatched agents
  417. DWORD dwError = SetDomainControllers(pVS);
  418. if (dwError != ERROR_SUCCESS)
  419. {
  420. return HRESULT_FROM_WIN32(dwError);
  421. }
  422. // Run the local agent first, if needed to copy any accounts
  423. if ( NeedToRunLocalAgent(pVS) )
  424. {
  425. IDCTAgentPtr pAgent;
  426. hr = pAgent.CreateInstance(__uuidof(DCTAgent));
  427. if (SUCCEEDED(hr))
  428. {
  429. hr = pAgent->raw_SubmitJob(punkVarSet,&jobID);
  430. if ( SUCCEEDED(hr) )
  431. {
  432. // since this is a local agent, we should go ahead and signal Ok to shut down
  433. // the reason HRESULT is not checked is that when there is no reference to
  434. // agent COM server, it will be shut down anyway
  435. pAgent->raw_SignalOKToShutDown();
  436. CAgentDetailDlg detailDlg(&wnd);
  437. detailDlg.SetJobID(jobID);
  438. // based on the type of migration, set the format correspondingly
  439. // it used to be set to acct repl always
  440. // since Exchange migration uses local agent as well, we need to single out
  441. // Exchange migration case
  442. _bstr_t text = pVS->get(GET_BSTR(DCTVS_Security_TranslateContainers));
  443. if (text.length())
  444. detailDlg.SetFormat(2);
  445. else
  446. detailDlg.SetFormat(1); // acct repl stats
  447. // if we're only copying a few accounts, default the refresh rate to a lower value, since the
  448. // process may finish before the refresh can happen
  449. long nAccounts = pVS->get(GET_BSTR(DCTVS_Accounts_NumItems));
  450. if ( nAccounts <= 20 )
  451. {
  452. detailDlg.SetRefreshInterval(1);
  453. }
  454. else
  455. {
  456. detailDlg.SetRefreshInterval(5);
  457. }
  458. _bstr_t logfile = pVS->get(GET_BSTR(DCTVS_Options_Logfile));
  459. detailDlg.SetLogFile((WCHAR*)logfile);
  460. detailDlg.SetAutoCloseHide(lAutoCloseHideDialogs);
  461. UINT_PTR nResponse = detailDlg.DoModal();
  462. }
  463. }
  464. }
  465. if ( gbCancelled )
  466. {
  467. // if the local operation was cancelled, don't dispatch the agents
  468. wnd.Detach();
  469. return S_OK;
  470. }
  471. // now run the dispatcher
  472. if ( SUCCEEDED(hr) )
  473. {
  474. // there's no need to dispatch agents to do translation or migration
  475. // if we were not able to copy the accounts
  476. if ( NeedToDispatch(pVS) )
  477. {
  478. IDCTDispatcherPtr pDispatcher;
  479. hr = pDispatcher.CreateInstance(CLSID_DCTDispatcher);
  480. if ( SUCCEEDED(hr) )
  481. {
  482. // Call the dispatch preprocessor.
  483. PreProcessDispatcher(pVS);
  484. // make sure we're not going to lock out any computers by migrating them to a domain where they
  485. // don't have a good computer account
  486. hr = TrimMigratingComputerList(pVS, &bAnyToDispatch);
  487. if (SUCCEEDED(hr) && bAnyToDispatch)
  488. {
  489. CWorking tempDlg(IDS_DISPATCHING);
  490. if (lAutoCloseHideDialogs == 0)
  491. {
  492. tempDlg.Create(IDD_PLEASEWAIT);
  493. tempDlg.ShowWindow(SW_SHOW);
  494. }
  495. // give the dialog a change to process messages
  496. CWnd * wnd = AfxGetMainWnd();
  497. MSG msg;
  498. while ( wnd && PeekMessage( &msg, wnd->m_hWnd, 0, 0, PM_NOREMOVE ) )
  499. {
  500. if ( ! AfxGetApp()->PumpMessage() )
  501. {
  502. break;
  503. }
  504. }
  505. AfxGetApp()->DoWaitCursor(0);
  506. _bstr_t logFile = pVS->get(GET_BSTR(DCTVS_Options_DispatchCSV));
  507. WCHAR path[MAX_PATH] = L"";
  508. DWORD rc;
  509. if (!GetProgramDirectory(path))
  510. {
  511. rc = GetLastError();
  512. hr = HRESULT_FROM_WIN32(rc);
  513. }
  514. if (SUCCEEDED(hr))
  515. {
  516. if ( logFile.length() == 0 )
  517. {
  518. logFile = path;
  519. logFile += L"Logs\\Dispatcher.csv";
  520. pVS->put(GET_BSTR(DCTVS_Options_DispatchCSV),logFile);
  521. }
  522. // clear the CSV log file if it exists, so we will not get old information in it
  523. if ( ! DeleteFile(logFile) )
  524. {
  525. rc = GetLastError();
  526. // it is OK if the file is not there
  527. if (rc == ERROR_FILE_NOT_FOUND || rc == ERROR_PATH_NOT_FOUND)
  528. rc = ERROR_SUCCESS;
  529. hr = HRESULT_FROM_WIN32(rc);
  530. if (FAILED(hr))
  531. {
  532. TErrorDct errLog;
  533. WCHAR errText[LEN_Path];
  534. _bstr_t errMsg = errLog.GetMsgText(DCT_MSG_CANNOT_REMOVE_OLD_INTERNAL_DISPATCH_LOG_S,
  535. errLog.ErrorCodeToText(rc, DIM(errText), errText));
  536. WCHAR* message = (WCHAR*) errMsg;
  537. message[wcslen(message)-1] = L'\0'; // there is a trailing CR
  538. Error(message, GUID_NULL, hr);
  539. }
  540. }
  541. }
  542. if (SUCCEEDED(hr))
  543. {
  544. // set up the location for the agents to write back their results
  545. logFile = path;
  546. logFile += L"Logs\\Agents\\";
  547. _bstr_t logsPath = path;
  548. logsPath += L"Logs";
  549. if (!CreateDirectory(logsPath,NULL))
  550. {
  551. rc = GetLastError();
  552. // it is OK if the directory already exists
  553. if (rc == ERROR_ALREADY_EXISTS)
  554. rc = ERROR_SUCCESS;
  555. hr = HRESULT_FROM_WIN32(rc);
  556. }
  557. }
  558. if (SUCCEEDED(hr))
  559. {
  560. // logFile is "...\\Logs\\Agents\\"
  561. if ( ! CreateDirectory(logFile,NULL) )
  562. {
  563. rc = GetLastError();
  564. // it is OK if the directory already exists
  565. if (rc == ERROR_ALREADY_EXISTS)
  566. rc = ERROR_SUCCESS;
  567. hr = HRESULT_FROM_WIN32(rc);
  568. }
  569. }
  570. if (SUCCEEDED(hr))
  571. {
  572. pVS->put(GET_BSTR(DCTVS_Dispatcher_ResultPath),logFile);
  573. punkVarSet->AddRef();
  574. hr = pDispatcher->raw_DispatchToServers(&punkVarSet);
  575. }
  576. if (lAutoCloseHideDialogs == 0)
  577. {
  578. tempDlg.ShowWindow(SW_HIDE);
  579. }
  580. if ( SUCCEEDED(hr) )
  581. {
  582. // reset the stats, so that we don't see anything left over from the previous run
  583. gData.Initialize();
  584. logFile = pVS->get(GET_BSTR(DCTVS_Options_DispatchCSV));
  585. if (lAutoCloseHideDialogs == 0)
  586. {
  587. CPropertySheet mdlg;
  588. CAgentMonitorDlg listDlg;
  589. CMainDlg summaryDlg;
  590. CLogSettingsDlg settingsDlg;
  591. CString title;
  592. title.LoadString(IDS_MainWindowTitle);
  593. listDlg.m_psp.dwFlags |= PSP_PREMATURE | PSP_HASHELP;
  594. summaryDlg.m_psp.dwFlags |= PSP_PREMATURE | PSP_HASHELP;
  595. settingsDlg.m_psp.dwFlags |= PSP_PREMATURE | PSP_HASHELP;
  596. mdlg.AddPage(&summaryDlg);
  597. mdlg.AddPage(&listDlg);
  598. mdlg.AddPage(&settingsDlg);
  599. settingsDlg.SetImmediateStart(TRUE);
  600. settingsDlg.SetDispatchLog(logFile);
  601. // this determines whether the stats for security translation will be displayed in the agent detail
  602. if ( NeedToUseST(pVS,TRUE) )
  603. {
  604. listDlg.SetSecurityTranslationFlag(TRUE);
  605. }
  606. else
  607. {
  608. listDlg.SetSecurityTranslationFlag(FALSE);
  609. }
  610. if( !UStrICmp(wizard,L"reporting") )
  611. {
  612. listDlg.SetReportingFlag(TRUE);
  613. }
  614. mdlg.SetActivePage(&listDlg);
  615. mdlg.SetTitle(title);
  616. UINT_PTR nResponse = mdlg.DoModal();
  617. }
  618. else
  619. {
  620. WaitForAgentsToFinish(logFile);
  621. }
  622. //
  623. // log the agent completion status into migration log
  624. //
  625. TError err;
  626. // open the migration log
  627. err.LogOpen(_bstr_t(pVS->get(GET_BSTR(DCTVS_Options_Logfile))), 1);
  628. // log the completion status information
  629. gData.Lock();
  630. BOOL bForcedToStopMonitoring = FALSE;
  631. gData.GetForcedToStopMonitoring(&bForcedToStopMonitoring);
  632. LogAgentStatus(err, gData.GetUnsafeServerList(), bForcedToStopMonitoring);
  633. gData.Unlock();
  634. // close the migration log
  635. err.LogClose();
  636. // store results to database
  637. TNodeListEnum e;
  638. TServerNode * pNode;
  639. // if we are retrying an operation, don't save it to the database again!
  640. for ( pNode = (TServerNode*)e.OpenFirst(gData.GetUnsafeServerList()) ; pNode ; pNode = (TServerNode*)e.Next() )
  641. {
  642. if ( UStrICmp(wizard,L"retry") )
  643. {
  644. hr = pDb->raw_AddDistributedAction(SysAllocString(pNode->GetServer()),SysAllocString(pNode->GetJobPath()),pNode->GetStatus(),pNode->GetMessageText());
  645. if ( FAILED(hr) )
  646. {
  647. hr = S_OK;
  648. }
  649. }
  650. else
  651. {
  652. hr = pDb->raw_SetDistActionStatus(-1,pNode->GetJobPath(),pNode->GetStatus(),pNode->GetMessageText());
  653. if ( FAILED(hr) )
  654. {
  655. hr = S_OK;
  656. }
  657. }
  658. }
  659. }
  660. }
  661. // Call the Dispatcher post processor
  662. PostProcessDispatcher(pVS);
  663. }
  664. }
  665. if ( NeedToRunReports(pVS) )
  666. {
  667. RunReports(pVS);
  668. }
  669. }
  670. wnd.Detach();
  671. // Reset the undo flag so that next wizard does not have to deal with it.
  672. //* pVS->put(GET_BSTR(DCTVS_Options_Undo), L"No");
  673. pVS->put(GET_BSTR(DCTVS_Options_Undo), GET_BSTR(IDS_No));
  674. return hr;
  675. }
  676. STDMETHODIMP CMigrator::GetTaskDescription(IUnknown *pVarSet,/*[out]*/BSTR * pDescription)
  677. {
  678. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  679. IVarSetPtr pVS = pVarSet;
  680. CString str;
  681. _bstr_t wizard = pVS->get(L"Options.Wizard");
  682. _bstr_t undo = pVS->get(GET_BSTR(DCTVS_Options_Undo));
  683. //* if ( !_wcsicmp((WCHAR*) undo, L"Yes") )
  684. if ( !_wcsicmp((WCHAR*) undo, GET_STRING(IDS_YES)) )
  685. {
  686. str.FormatMessage(IDS_Undo);
  687. BuildGeneralDesc(pVS, str);
  688. BuildUndoDesc(pVS,str);
  689. }
  690. else if ( !UStrICmp(wizard,L"user") )
  691. {
  692. str.FormatMessage(IDS_UserMigration);
  693. BuildGeneralDesc(pVS,str);
  694. BuildAcctReplDesc(pVS,str);
  695. }
  696. else if ( !UStrICmp(wizard,L"group") )
  697. {
  698. str.FormatMessage(IDS_GroupMigration);
  699. BuildGeneralDesc(pVS,str);
  700. BuildAcctReplDesc(pVS,str);
  701. }
  702. else if ( !UStrICmp(wizard,L"computer") )
  703. {
  704. str.FormatMessage(IDS_ComputerMigration);
  705. BuildGeneralDesc(pVS,str);
  706. BuildAcctReplDesc(pVS,str);
  707. BuildSecTransDesc(pVS,str,TRUE);
  708. BuildDispatchDesc(pVS,str);
  709. }
  710. else if ( !UStrICmp(wizard,L"security") )
  711. {
  712. str.FormatMessage(IDS_SecurityTranslation);
  713. BuildSecTransDesc(pVS,str,TRUE);
  714. BuildDispatchDesc(pVS,str);
  715. }
  716. else if ( !UStrICmp(wizard,L"reporting") )
  717. {
  718. str.FormatMessage(IDS_ReportGeneration);
  719. BuildReportDesc(pVS,str);
  720. }
  721. else if ( !UStrICmp(wizard,L"retry") )
  722. {
  723. str.FormatMessage(IDS_RetryTasks);
  724. }
  725. else if ( ! UStrICmp(wizard,L"service") )
  726. {
  727. str.FormatMessage(IDS_Service);
  728. }
  729. else if ( ! UStrICmp(wizard,L"trust") )
  730. {
  731. str.FormatMessage(IDS_TrustManagement);
  732. }
  733. else if ( !UStrICmp(wizard,L"exchangeDir") )
  734. {
  735. BuildSecTransDesc(pVS,str,TRUE);
  736. }
  737. else if ( !UStrICmp(wizard,L"groupmapping") )
  738. {
  739. BuildGeneralDesc(pVS,str);
  740. BuildAcctReplDesc(pVS,str);
  741. BuildGroupMappingDesc(pVS,str);
  742. }
  743. (*pDescription) = str.AllocSysString();
  744. return S_OK;
  745. }
  746. STDMETHODIMP CMigrator::GetUndoTask(IUnknown * pVarSet,/*[out]*/ IUnknown ** ppVarSetOut)
  747. {
  748. HRESULT hr = S_OK;
  749. IVarSetPtr pVarSetIn = pVarSet;
  750. IVarSetPtr pVarSetOut;
  751. (*ppVarSetOut) = NULL;
  752. hr = pVarSetOut.CreateInstance(CLSID_VarSet);
  753. if ( SUCCEEDED(hr) )
  754. {
  755. hr = ConstructUndoVarSet(pVarSetIn,pVarSetOut);
  756. pVarSetOut->AddRef();
  757. (*ppVarSetOut) = pVarSetOut;
  758. }
  759. return hr;
  760. }
  761. HRESULT CMigrator::ProcessServerListForUndo(IVarSet * pVarSet)
  762. {
  763. HRESULT hr = S_OK;
  764. _bstr_t srcName;
  765. _bstr_t tgtName;
  766. WCHAR keySrc[100];
  767. WCHAR keyTgt[100];
  768. WCHAR keyTmp[100];
  769. long ndx,numItems;
  770. numItems = pVarSet->get(GET_BSTR(DCTVS_Servers_NumItems));
  771. for ( ndx = 0 ; ndx < numItems ; ndx++ )
  772. {
  773. // if the computer was renamed, swap the source and target names
  774. swprintf(keySrc,GET_STRING(DCTVSFmt_Servers_D),ndx);
  775. swprintf(keyTgt,GET_STRING(IDS_DCTVSFmt_Servers_RenameTo_D),ndx);
  776. srcName = pVarSet->get(keySrc);
  777. tgtName = pVarSet->get(keyTgt);
  778. if ( tgtName.length() )
  779. {
  780. if ( ((WCHAR*)tgtName)[0] != L'\\' )
  781. {
  782. // ensure that tgtName has \\ prefix
  783. tgtName = L"\\\\" + tgtName;
  784. }
  785. if ( ((WCHAR*)srcName)[0] == L'\\' )
  786. {
  787. // remove the \\ prefix from the new name
  788. srcName = ((WCHAR*)srcName)+2;
  789. }
  790. pVarSet->put(keySrc,tgtName);
  791. pVarSet->put(keyTgt,srcName);
  792. }
  793. swprintf(keyTmp,GET_STRING(IDS_DCTVSFmt_Servers_ChangeDomain_D),ndx);
  794. pVarSet->put(keyTmp,GET_BSTR(IDS_YES));
  795. swprintf(keyTmp,GET_STRING(IDS_DCTVSFmt_Servers_MigrateOnly_D),ndx);
  796. pVarSet->put(keyTmp,GET_BSTR(IDS_YES));
  797. }
  798. return hr;
  799. }
  800. HRESULT CMigrator::BuildAccountListForUndo(IVarSet * pVarSet,long actionID)
  801. {
  802. HRESULT hr = S_OK;
  803. WCHAR key[200];
  804. long ndx;
  805. _bstr_t srcPath;
  806. IIManageDBPtr pDB;
  807. IVarSetPtr pVarSetTemp(CLSID_VarSet);
  808. IUnknown * pUnk = NULL;
  809. hr = pDB.CreateInstance(CLSID_IManageDB);
  810. if ( SUCCEEDED(hr) )
  811. {
  812. hr = pVarSetTemp.QueryInterface(IID_IUnknown,&pUnk);
  813. if ( SUCCEEDED(hr) )
  814. {
  815. hr = pDB->raw_GetMigratedObjects(actionID,&pUnk);
  816. }
  817. if ( SUCCEEDED(hr) )
  818. {
  819. pUnk->Release();
  820. srcPath = L"Test";
  821. swprintf(key,L"MigratedObjects");
  822. long numMigrated = pVarSetTemp->get(key);
  823. for ( ndx = 0 ; srcPath.length() ; ndx++ )
  824. {
  825. swprintf(key,L"MigratedObjects.%d.%s",ndx,GET_STRING(DB_SourceAdsPath));
  826. srcPath = pVarSetTemp->get(key);
  827. if ( srcPath.length() )
  828. {
  829. // get the object type
  830. swprintf(key,L"MigratedObjects.%d.%s",ndx,GET_STRING(DB_Type));
  831. _bstr_t text = pVarSetTemp->get(key);
  832. swprintf(key,L"Accounts.%ld.Type",ndx);
  833. //work-around a fix that places the sourcepath for an
  834. //NT 4.0 computer migration
  835. if ((text != _bstr_t(L"computer")) || (wcsncmp(L"WinNT://", (WCHAR*)srcPath, 8)))
  836. {
  837. // set the object type in the account list
  838. pVarSet->put(key,text);
  839. // copy the source path to the account list
  840. swprintf(key,L"Accounts.%ld",ndx);
  841. pVarSet->put(key,srcPath);
  842. // set the target path in the account list
  843. swprintf(key,L"MigratedObjects.%d.%s",ndx,GET_STRING(DB_TargetAdsPath));
  844. text = pVarSetTemp->get(key);
  845. swprintf(key,L"Accounts.%ld.TargetName",ndx);
  846. pVarSet->put(key,text);
  847. }
  848. }
  849. }
  850. swprintf(key,GET_STRING(DCTVS_Accounts_NumItems));
  851. pVarSet->put(key,numMigrated);
  852. }
  853. }
  854. return hr;
  855. }
  856. HRESULT CMigrator::ConstructUndoVarSet(IVarSet * pVarSetIn,IVarSet * pVarSetOut)
  857. {
  858. HRESULT hr = S_OK;
  859. IVarSet * pTemp = NULL;
  860. _bstr_t origSource;
  861. _bstr_t origTarget;
  862. _bstr_t origSourceDns;
  863. _bstr_t origTargetDns;
  864. _bstr_t origSourceFlat;
  865. _bstr_t origTargetFlat;
  866. _bstr_t temp;
  867. _bstr_t temp2;
  868. long actionID = pVarSetIn->get(L"ActionID");
  869. // general options
  870. // mark the varset as an undo operation
  871. pVarSetOut->put(GET_BSTR(DCTVS_Options_Undo),GET_BSTR(IDS_YES));
  872. temp = pVarSetIn->get(GET_BSTR(DCTVS_Options_NoChange));
  873. if ( !UStrICmp(temp,GET_STRING(IDS_YES)) )
  874. {
  875. // for a no-change mode operation, there's nothing to undo!
  876. return hr;
  877. }
  878. // swap the source and target domains
  879. origSource = pVarSetIn->get(GET_BSTR(DCTVS_Options_SourceDomain));
  880. origTarget = pVarSetIn->get(GET_BSTR(DCTVS_Options_TargetDomain));
  881. origSourceDns = pVarSetIn->get(GET_BSTR(DCTVS_Options_SourceDomainDns));
  882. origTargetDns = pVarSetIn->get(GET_BSTR(DCTVS_Options_TargetDomainDns));
  883. origSourceFlat = pVarSetIn->get(GET_BSTR(DCTVS_Options_SourceDomainFlat));
  884. origTargetFlat = pVarSetIn->get(GET_BSTR(DCTVS_Options_TargetDomainFlat));
  885. temp = pVarSetIn->get(GET_BSTR(DCTVS_Options_Logfile));
  886. temp2 = pVarSetIn->get(GET_BSTR(DCTVS_Options_DispatchLog));
  887. pVarSetOut->put(GET_BSTR(DCTVS_Options_Logfile),temp);
  888. // For inter-forest, leave the domain names as they were
  889. pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomain),origSource);
  890. pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomain),origTarget);
  891. pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomainDns),origSourceDns);
  892. pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomainDns),origTargetDns);
  893. pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomainFlat),origSourceFlat);
  894. pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomainFlat),origTargetFlat);
  895. // copy the account list
  896. hr = pVarSetIn->raw_getReference(GET_BSTR(DCTVS_Accounts),&pTemp);
  897. if ( SUCCEEDED(hr) )
  898. {
  899. hr = pVarSetOut->raw_ImportSubTree(GET_BSTR(DCTVS_Accounts),pTemp);
  900. if ( SUCCEEDED(hr) )
  901. {
  902. BuildAccountListForUndo(pVarSetOut,actionID);
  903. }
  904. pTemp->Release();
  905. }
  906. hr = pVarSetIn->raw_getReference(SysAllocString(L"AccountOptions"),&pTemp);
  907. if ( SUCCEEDED(hr) )
  908. {
  909. hr = pVarSetOut->raw_ImportSubTree(SysAllocString(L"AccountOptions"),pTemp);
  910. pTemp->Release();
  911. }
  912. // and the server list
  913. hr = pVarSetIn->raw_getReference(GET_BSTR(DCTVS_Servers),&pTemp);
  914. if ( SUCCEEDED(hr) )
  915. {
  916. hr = pVarSetOut->raw_ImportSubTree(GET_BSTR(DCTVS_Servers),pTemp);
  917. if ( SUCCEEDED(hr) )
  918. {
  919. ProcessServerListForUndo(pVarSetOut);
  920. pTemp->Release();
  921. }
  922. }
  923. LONG bSameForest = FALSE;
  924. MCSDCTWORKEROBJECTSLib::IAccessCheckerPtr pAccess;
  925. hr = pAccess.CreateInstance(__uuidof(MCSDCTWORKEROBJECTSLib::AccessChecker));
  926. if ( SUCCEEDED(hr) )
  927. {
  928. hr = pAccess->raw_IsInSameForest(origSourceDns,origTargetDns,&bSameForest);
  929. if ( hr == 8250 )
  930. {
  931. hr = 0;
  932. bSameForest = FALSE;
  933. }
  934. }
  935. if ( SUCCEEDED(hr) )
  936. {
  937. // for account migration, need to check whether we're cloning, or moving accounts
  938. if ( ! bSameForest ) // cloning accounts
  939. {
  940. // Since we cloned the accounts we need to delete the target accounts.
  941. // We will call the account replicator to do this. We will also call
  942. // the undo function on all the registered extensions. This way the extensions
  943. // will have a chance to cleanup after themselves in cases of UNDO.
  944. pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomain),origSource);
  945. pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomain),origTarget);
  946. pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomainDns),origSourceDns);
  947. pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomainDns),origTargetDns);
  948. pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomainFlat),origSourceFlat);
  949. pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomainFlat),origTargetFlat);
  950. }
  951. else // moving, using moveObject
  952. {
  953. // swap the source and target, and move them back, using the same options as before
  954. pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomain),origTarget);
  955. pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomain),origSource);
  956. pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomainDns),origTargetDns);
  957. pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomainDns),origSourceDns);
  958. pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomainFlat),origTargetFlat);
  959. pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomainFlat),origSourceFlat);
  960. }
  961. }
  962. // if migrating computers, swap the source and target domains, and call the dispatcher again to move them back to the source domain
  963. _bstr_t comp = pVarSetIn->get(GET_BSTR(DCTVS_AccountOptions_CopyComputers));
  964. if ( !UStrICmp(comp,GET_STRING(IDS_YES)) )
  965. {
  966. pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomain),origTarget);
  967. pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomain),origSource);
  968. pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomainDns),origTargetDns);
  969. pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomainDns),origSourceDns);
  970. pVarSetOut->put(GET_BSTR(DCTVS_Options_SourceDomainFlat),origTargetFlat);
  971. pVarSetOut->put(GET_BSTR(DCTVS_Options_TargetDomainFlat),origSourceFlat);
  972. pVarSetOut->put(GET_BSTR(DCTVS_Options_DispatchLog),temp2);
  973. pVarSetOut->put(GET_BSTR(DCTVS_Options_Wizard),L"computer");
  974. }
  975. // security translation - don't undo
  976. return S_OK;
  977. }
  978. HRESULT CMigrator::SetReportDataInRegistry(WCHAR const * reportName,WCHAR const * filename)
  979. {
  980. TRegKey hKeyReports;
  981. DWORD rc;
  982. rc = hKeyReports.Open(GET_STRING(IDS_REGKEY_REPORTS));
  983. // if the "Reports" registry key does not already exist, create it
  984. if ( rc == ERROR_FILE_NOT_FOUND )
  985. {
  986. rc = hKeyReports.Create(GET_STRING(IDS_REGKEY_REPORTS));
  987. }
  988. if ( ! rc )
  989. {
  990. rc = hKeyReports.ValueSetStr(reportName,filename);
  991. }
  992. return HRESULT_FROM_WIN32(rc);
  993. }
  994. HRESULT CMigrator::RunReports(IVarSet * pVarSet)
  995. {
  996. HRESULT hr = S_OK;
  997. _bstr_t directory = pVarSet->get(GET_BSTR(DCTVS_Reports_Directory));
  998. _bstr_t srcdm = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain));
  999. _bstr_t tgtdm = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain));
  1000. long lAutoCloseHideDialogs = pVarSet->get(GET_BSTR(DCTVS_Options_AutoCloseHideDialogs));
  1001. IIManageDBPtr pDB;
  1002. int ver;
  1003. BOOL bNT4Dom = FALSE;
  1004. CWorking tempDlg(IDS_NAMECONFLICTS);
  1005. CWnd * wnd = NULL;
  1006. MSG msg;
  1007. if (lAutoCloseHideDialogs == 0)
  1008. {
  1009. tempDlg.Create(IDD_PLEASEWAIT);
  1010. tempDlg.ShowWindow(SW_SHOW);
  1011. tempDlg.m_strMessage.LoadString(IDS_STATUS_GeneratingReports);
  1012. tempDlg.UpdateData(FALSE);
  1013. wnd = AfxGetMainWnd();
  1014. while ( wnd && PeekMessage( &msg, wnd->m_hWnd, 0, 0, PM_NOREMOVE ) )
  1015. {
  1016. if ( ! AfxGetApp()->PumpMessage() )
  1017. {
  1018. break;
  1019. }
  1020. }
  1021. AfxGetApp()->DoWaitCursor(0);
  1022. }
  1023. //get the source domain OS version
  1024. ver = GetOSVersionForDomain(srcdm);
  1025. if (ver < 5)
  1026. bNT4Dom = TRUE;
  1027. hr = pDB.CreateInstance(CLSID_IManageDB);
  1028. if ( SUCCEEDED(hr) )
  1029. {
  1030. // Migrated users and groups report
  1031. _bstr_t text = pVarSet->get(GET_BSTR(DCTVS_Reports_MigratedAccounts));
  1032. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1033. {
  1034. // run the migrated users and groups report
  1035. CString filename;
  1036. filename = (WCHAR*)directory;
  1037. if ( filename[filename.GetLength()-1] != L'\\' )
  1038. filename += L"\\";
  1039. filename += L"MigrAcct.htm";
  1040. hr = pDB->raw_GenerateReport(SysAllocString(L"MigratedAccounts"),filename.AllocSysString(), srcdm, tgtdm, bNT4Dom);
  1041. if ( SUCCEEDED(hr) )
  1042. {
  1043. SetReportDataInRegistry(L"MigratedAccounts",filename);
  1044. }
  1045. }
  1046. // migrated computers report
  1047. text = pVarSet->get(GET_BSTR(DCTVS_Reports_MigratedComputers));
  1048. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1049. {
  1050. // run the migrated computers report
  1051. CString filename;
  1052. filename = (WCHAR*)directory;
  1053. if ( filename[filename.GetLength()-1] != L'\\' )
  1054. filename += L"\\";
  1055. filename += L"MigrComp.htm";
  1056. hr = pDB->raw_GenerateReport(SysAllocString(L"MigratedComputers"),filename.AllocSysString(), srcdm, tgtdm, bNT4Dom);
  1057. if ( SUCCEEDED(hr) )
  1058. {
  1059. SetReportDataInRegistry(L"MigratedComputers",filename);
  1060. }
  1061. }
  1062. // expired computers report
  1063. text = pVarSet->get(GET_BSTR(DCTVS_Reports_ExpiredComputers));
  1064. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1065. {
  1066. // run the expired computers report
  1067. CString filename;
  1068. // clear the extra settings from the varset
  1069. pVarSet->put(GET_BSTR(DCTVS_GatherInformation_ComputerPasswordAge),SysAllocString(L""));
  1070. filename = (WCHAR*)directory;
  1071. if ( filename[filename.GetLength()-1] != L'\\' )
  1072. filename += L"\\";
  1073. filename += L"ExpComp.htm";
  1074. hr = pDB->raw_GenerateReport(SysAllocString(L"ExpiredComputers"),filename.AllocSysString(), srcdm, tgtdm, bNT4Dom);
  1075. if ( SUCCEEDED(hr) )
  1076. {
  1077. SetReportDataInRegistry(L"ExpiredComputers",filename);
  1078. }
  1079. }
  1080. // account references report
  1081. text = pVarSet->get(GET_BSTR(DCTVS_Reports_AccountReferences));
  1082. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1083. {
  1084. // run the account references report
  1085. CString filename;
  1086. filename = (WCHAR*)directory;
  1087. if ( filename[filename.GetLength()-1] != L'\\' )
  1088. filename += L"\\";
  1089. filename += L"AcctRefs.htm";
  1090. hr = pDB->raw_GenerateReport(SysAllocString(L"AccountReferences"),filename.AllocSysString(), srcdm, tgtdm, bNT4Dom);
  1091. if ( SUCCEEDED(hr) )
  1092. {
  1093. SetReportDataInRegistry(L"AccountReferences",filename);
  1094. }
  1095. // clear the extra settings from the varset
  1096. pVarSet->put(GET_BSTR(DCTVS_Security_GatherInformation),GET_BSTR(IDS_No));
  1097. pVarSet->put(GET_BSTR(DCTVS_Security_ReportAccountReferences),GET_BSTR(IDS_No));
  1098. }
  1099. // name conflict report
  1100. text = pVarSet->get(GET_BSTR(DCTVS_Reports_NameConflicts));
  1101. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1102. {
  1103. if (lAutoCloseHideDialogs == 0)
  1104. {
  1105. AfxGetApp()->DoWaitCursor(1);
  1106. // run the name conflicts report
  1107. tempDlg.m_strMessage.LoadString(IDS_STATUS_Gathering_NameConf);
  1108. tempDlg.UpdateData(FALSE);
  1109. while ( wnd && PeekMessage( &msg, wnd->m_hWnd, 0, 0, PM_NOREMOVE ) )
  1110. {
  1111. if ( ! AfxGetApp()->PumpMessage() )
  1112. {
  1113. break;
  1114. }
  1115. }
  1116. }
  1117. //fill the account table in the database
  1118. PopulateDomainDBs(pVarSet, pDB);
  1119. if (lAutoCloseHideDialogs == 0)
  1120. {
  1121. tempDlg.m_strMessage.LoadString(IDS_STATUS_GeneratingReports);
  1122. tempDlg.UpdateData(FALSE);
  1123. while ( wnd && PeekMessage( &msg, wnd->m_hWnd, 0, 0, PM_NOREMOVE ) )
  1124. {
  1125. if ( ! AfxGetApp()->PumpMessage() )
  1126. {
  1127. break;
  1128. }
  1129. }
  1130. AfxGetApp()->DoWaitCursor(0);
  1131. }
  1132. CString filename = (WCHAR*)directory;
  1133. if ( filename[filename.GetLength()-1] != L'\\' )
  1134. filename += L"\\";
  1135. filename += L"NameConf.htm";
  1136. hr = pDB->raw_GenerateReport(SysAllocString(L"NameConflicts"),filename.AllocSysString(), srcdm, tgtdm, bNT4Dom);
  1137. if ( SUCCEEDED(hr) )
  1138. {
  1139. SetReportDataInRegistry(L"NameConflicts",filename);
  1140. }
  1141. }
  1142. if (lAutoCloseHideDialogs == 0)
  1143. {
  1144. tempDlg.ShowWindow(SW_HIDE);
  1145. }
  1146. }
  1147. if (lAutoCloseHideDialogs == 0)
  1148. {
  1149. AfxGetApp()->DoWaitCursor(-1);
  1150. }
  1151. return hr;
  1152. }
  1153. //--------------------------------------------------------------------------
  1154. // PreProcessDispatcher : Pre processor swaps the source and target domains
  1155. // in case of UNDO so that the computers can be
  1156. // joined back to the source domain.
  1157. //--------------------------------------------------------------------------
  1158. void CMigrator::PreProcessDispatcher(IVarSet * pVarSet)
  1159. {
  1160. _bstr_t sUndo = pVarSet->get(L"Options.Wizard");
  1161. // In the service account migration wizard, turn off any security translation tasks
  1162. if ( !_wcsicmp(sUndo,L"service") )
  1163. {
  1164. IVarSet * pVS2 = NULL;
  1165. HRESULT hr = pVarSet->raw_getReference(L"Security",&pVS2);
  1166. if ( SUCCEEDED(hr) )
  1167. {
  1168. pVS2->Clear();
  1169. pVS2->Release();
  1170. }
  1171. }
  1172. }
  1173. //--------------------------------------------------------------------------
  1174. // PostProcessDispatcher : Swaps the source and target domains back. Also sets
  1175. // the Undo option to no.
  1176. //--------------------------------------------------------------------------
  1177. void CMigrator::PostProcessDispatcher(IVarSet * pVarSet)
  1178. {
  1179. _bstr_t sUndo = pVarSet->get(L"Options.Wizard");
  1180. _bstr_t origSource = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain));
  1181. _bstr_t origTarget = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain));
  1182. if ( !_wcsicmp(sUndo, L"undo") )
  1183. {
  1184. pVarSet->put(GET_BSTR(DCTVS_Options_SourceDomain), origTarget);
  1185. pVarSet->put(GET_BSTR(DCTVS_Options_TargetDomain), origSource);
  1186. }
  1187. }
  1188. void CMigrator::PreProcessForReporting(IVarSet * pVarSet)
  1189. {
  1190. _bstr_t text = pVarSet->get(GET_BSTR(DCTVS_Reports_Generate));
  1191. IVarSet * pVs = NULL;
  1192. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1193. {
  1194. // we are generating reports
  1195. // some reports require additional information gathering. We will set up the necessary varset
  1196. // keys to gather the information
  1197. text = pVarSet->get(GET_BSTR(DCTVS_Reports_ExpiredComputers));
  1198. if ( !UStrICmp(text,GET_STRING(IDS_YES)))
  1199. {
  1200. // we need to gather the computer password age from the computers in the domain
  1201. pVarSet->put(GET_BSTR(DCTVS_GatherInformation_ComputerPasswordAge),GET_BSTR(IDS_YES));
  1202. }
  1203. text = pVarSet->get(GET_BSTR(DCTVS_Reports_AccountReferences));
  1204. if ( !UStrICmp(text,GET_STRING(IDS_YES)))
  1205. {
  1206. // clean up all the Security translation flags so that we dont end up doing
  1207. // something that we were not supposed to.
  1208. HRESULT hr = pVarSet->raw_getReference(GET_BSTR(DCTVS_Security), &pVs);
  1209. if ( pVs )
  1210. {
  1211. pVs->Clear();
  1212. pVs->Release();
  1213. }
  1214. // for this one, we need to gather information from the selected computers
  1215. pVarSet->put(GET_BSTR(DCTVS_Security_GatherInformation),GET_BSTR(IDS_YES));
  1216. pVarSet->put(GET_BSTR(DCTVS_Security_ReportAccountReferences),GET_BSTR(IDS_YES));
  1217. pVarSet->put(GET_BSTR(DCTVS_Security_TranslateFiles),GET_BSTR(IDS_YES));
  1218. pVarSet->put(GET_BSTR(DCTVS_Security_TranslateShares),GET_BSTR(IDS_YES));
  1219. pVarSet->put(GET_BSTR(DCTVS_Security_TranslatePrinters),GET_BSTR(IDS_YES));
  1220. pVarSet->put(GET_BSTR(DCTVS_Security_TranslateLocalGroups),GET_BSTR(IDS_YES));
  1221. pVarSet->put(GET_BSTR(DCTVS_Security_TranslateRegistry),GET_BSTR(IDS_YES));
  1222. }
  1223. }
  1224. }
  1225. HRESULT CMigrator::TrimMigratingComputerList(IVarSet * pVarSet, bool* bAnyToDispatch)
  1226. {
  1227. // this functions checks each computer to be migrated, and does not migrate it if the account was not successfully copied
  1228. HRESULT hr = S_OK;
  1229. _bstr_t text;
  1230. WCHAR key[100];
  1231. long val,i;
  1232. IIManageDBPtr pDB;
  1233. _bstr_t srcDomain;
  1234. _bstr_t tgtDomain;
  1235. _bstr_t computer;
  1236. long actionID = pVarSet->get(L"ActionID");
  1237. CString temp;
  1238. _bstr_t origSource = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain));
  1239. _bstr_t origTarget = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain));
  1240. hr = pDB.CreateInstance(CLSID_IManageDB);
  1241. if ( FAILED(hr) )
  1242. {
  1243. return hr;
  1244. }
  1245. //
  1246. // If task is undo then mark computers that were successfully migrated for dispatch.
  1247. //
  1248. *bAnyToDispatch = false;
  1249. text = pVarSet->get(GET_BSTR(DCTVS_Options_Undo));
  1250. if (! UStrICmp(text,GET_STRING(IDS_YES)))
  1251. {
  1252. _bstr_t strYes = GET_BSTR(IDS_YES);
  1253. _bstr_t strNo = GET_BSTR(IDS_No);
  1254. _bstr_t strServersFormat = GET_BSTR(DCTVSFmt_Servers_D);
  1255. _bstr_t strServersDnsFormat = GET_BSTR(IDS_DCTVSFmt_Servers_DnsName_D);
  1256. _bstr_t strServersRenameToFormat = GET_BSTR(IDS_DCTVSFmt_Servers_RenameTo_D);
  1257. _bstr_t strServersSkipDispatchFormat = GET_BSTR(IDS_DCTVSFmt_Servers_SkipDispatch_D);
  1258. //
  1259. // For each server that was acted upon during migration task.
  1260. //
  1261. long cServer = pVarSet->get(GET_BSTR(DCTVS_Servers_NumItems));
  1262. long lActionId = pVarSet->get(L"UndoAction");
  1263. for (long iServer = 0; iServer < cServer; iServer++)
  1264. {
  1265. bool bSucceeded = false;
  1266. //
  1267. // Retrieve original source computer name without leading UNC prefix.
  1268. // If the computer was re-named during the migration the original
  1269. // name is stored in 'Servers.#.TargetName' without a UNC prefix
  1270. // otherwise the name is stored in 'Servers.#' with a UNC prefix.
  1271. //
  1272. _bstr_t strServerName;
  1273. if (_snwprintf(key, sizeof(key) / sizeof(key[0]), strServersRenameToFormat, iServer) < 0)
  1274. {
  1275. return E_FAIL;
  1276. }
  1277. key[sizeof(key) / sizeof(key[0]) - 1] = L'\0';
  1278. strServerName = pVarSet->get(key);
  1279. if (strServerName.length() == 0)
  1280. {
  1281. _snwprintf(key, sizeof(key) / sizeof(key[0]), strServersFormat, iServer);
  1282. key[sizeof(key) / sizeof(key[0]) - 1] = L'\0';
  1283. _bstr_t strServerNamePrefixed = pVarSet->get(key);
  1284. if (strServerNamePrefixed.length() > 2)
  1285. {
  1286. strServerName = (PCWSTR)strServerNamePrefixed + 2;
  1287. }
  1288. }
  1289. //
  1290. // If computer previously had a DNS name then bind to computer object to
  1291. // retrieve current DNS name. This will be used to connec to the computer.
  1292. //
  1293. if (_snwprintf(key, sizeof(key) / sizeof(key[0]), strServersDnsFormat, iServer) < 0)
  1294. {
  1295. return E_FAIL;
  1296. }
  1297. key[sizeof(key) / sizeof(key[0]) - 1] = L'\0';
  1298. _bstr_t strOldServerNameDns = pVarSet->get(key);
  1299. if (strOldServerNameDns.length())
  1300. {
  1301. IVarSetPtr spVarSet(CLSID_VarSet);
  1302. IUnknownPtr spUnknown(spVarSet);
  1303. IUnknown* punk = spUnknown;
  1304. hr = pDB->raw_GetAMigratedObject(strServerName + L"$", origTarget, origSource, &punk);
  1305. if (SUCCEEDED(hr))
  1306. {
  1307. _bstr_t strComputerTargetPath = spVarSet->get(L"MigratedObjects.TargetAdsPath");
  1308. IADsPtr spComputer;
  1309. hr = ADsGetObject(strComputerTargetPath, __uuidof(IADs), (VOID**)&spComputer);
  1310. if (SUCCEEDED(hr))
  1311. {
  1312. VARIANT var;
  1313. VariantInit(&var);
  1314. hr = spComputer->Get(_bstr_t(L"dNSHostName"), &var);
  1315. if (SUCCEEDED(hr))
  1316. {
  1317. _bstr_t strNewServerNameDns = (_variant_t(var, false));
  1318. if (_snwprintf(key, sizeof(key) / sizeof(key[0]), strServersDnsFormat, iServer) < 0)
  1319. {
  1320. return E_FAIL;
  1321. }
  1322. key[sizeof(key) / sizeof(key[0]) - 1] = L'\0';
  1323. pVarSet->put(key, L"\\\\" + strNewServerNameDns);
  1324. strServerName = (LPCTSTR)strOldServerNameDns + 2;
  1325. }
  1326. }
  1327. }
  1328. }
  1329. //
  1330. // If agent successfully completed task on server then mark succeeded.
  1331. //
  1332. long lStatus = pDB->GetDistributedActionStatus(lActionId, strServerName);
  1333. if ((lStatus & Agent_Status_Finished) && !(lStatus & Agent_Status_Failed))
  1334. {
  1335. bSucceeded = true;
  1336. }
  1337. //
  1338. // If the agent succeeded on this computer then mark not to skip dispatch
  1339. // and also indicate that there are computers to dispatch to. If the agent
  1340. // failed on this computer then mark to skip dispatch.
  1341. //
  1342. _snwprintf(key, sizeof(key) / sizeof(key[0]), strServersSkipDispatchFormat, iServer);
  1343. key[sizeof(key) / sizeof(key[0]) - 1] = L'\0';
  1344. if (bSucceeded)
  1345. {
  1346. pVarSet->put(key, strNo);
  1347. *bAnyToDispatch = true;
  1348. }
  1349. else
  1350. {
  1351. pVarSet->put(key, strYes);
  1352. }
  1353. }
  1354. return S_OK;
  1355. }
  1356. text = pVarSet->get(GET_BSTR(DCTVS_Options_NoChange));
  1357. if (! UStrICmp(text,GET_STRING(IDS_YES)))
  1358. {
  1359. // don't need to trim in nochange mode
  1360. *bAnyToDispatch = true; //say yes run dispatcher if Nochange
  1361. return S_OK;
  1362. }
  1363. srcDomain = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain));
  1364. tgtDomain = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain));
  1365. *bAnyToDispatch = false; //indicate that so far no accounts to dispatch
  1366. val = pVarSet->get(GET_BSTR(DCTVS_Servers_NumItems));
  1367. for ( i = 0 ; i < val ; i++ )
  1368. {
  1369. //init the skipDispath flag to "No"
  1370. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_SkipDispatch_D),i);
  1371. pVarSet->put(key,GET_BSTR(IDS_No));
  1372. swprintf(key,GET_STRING(DCTVSFmt_Servers_D),i);
  1373. computer = pVarSet->get(key);
  1374. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_ChangeDomain_D),i);
  1375. text = pVarSet->get(key);
  1376. if (! UStrICmp(text,GET_STRING(IDS_YES)) )
  1377. {
  1378. // we are migrating this computer to a different domain
  1379. // check our database to verify that the computer account has been
  1380. // successfully migrated
  1381. computer += L"$";
  1382. IVarSetPtr pVS(CLSID_VarSet);
  1383. IUnknown * pUnk = NULL;
  1384. hr = pVS->QueryInterface(IID_IUnknown,(void**)&pUnk);
  1385. if ( SUCCEEDED(hr) )
  1386. {
  1387. if ( ((WCHAR*)computer)[0] == L'\\' )
  1388. {
  1389. // leave off the leading \\'s
  1390. hr = pDB->raw_GetAMigratedObject(SysAllocString(((WCHAR*)computer) + 2),srcDomain,tgtDomain,&pUnk);
  1391. }
  1392. else
  1393. {
  1394. hr = pDB->raw_GetAMigratedObject(computer,srcDomain,tgtDomain,&pUnk);
  1395. }
  1396. if ( hr == S_OK )
  1397. {
  1398. // the computer was found in the migrated objects table
  1399. // make sure we are using its correct target name, if it has been renamed
  1400. swprintf(key,L"MigratedObjects.TargetSamName");
  1401. _bstr_t targetName = pVS->get(key);
  1402. swprintf(key,L"MigratedObjects.SourceSamName");
  1403. _bstr_t sourceName = pVS->get(key);
  1404. long id = pVS->get(L"MigratedObjects.ActionID");
  1405. if ( UStrICmp((WCHAR*)sourceName,(WCHAR*)targetName) )
  1406. {
  1407. // the computer is being renamed
  1408. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_RenameTo_D),i);
  1409. // strip off the $ from the end of the target name, if specified
  1410. WCHAR target[LEN_Account];
  1411. safecopy(target,(WCHAR*)targetName);
  1412. if ( target[UStrLen(target)-1] == L'$' )
  1413. {
  1414. target[UStrLen(target)-1] = 0;
  1415. }
  1416. pVarSet->put(key,target);
  1417. }
  1418. if ( id != actionID )
  1419. {
  1420. // the migration failed, but this computer had been migrated before
  1421. // don't migrate the computer because it's account in the target domain, won't be reset
  1422. // and it will therefore be locked out of the domain
  1423. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_ChangeDomain_D),i);
  1424. pVarSet->put(key,GET_BSTR(IDS_No));
  1425. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_Reboot_D),i);
  1426. pVarSet->put(key,GET_BSTR(IDS_No));
  1427. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_SkipDispatch_D),i);
  1428. pVarSet->put(key,GET_BSTR(IDS_YES));
  1429. }
  1430. else
  1431. *bAnyToDispatch = true; //atleast one server for dispatcher
  1432. }
  1433. else
  1434. {
  1435. // the computer migration failed!
  1436. // don't migrate the computer because it won't have it's account in the target domain,
  1437. // and will therefore be locked out of the domain
  1438. pVarSet->put(key,GET_BSTR(IDS_No));
  1439. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_Reboot_D),i);
  1440. pVarSet->put(key,GET_BSTR(IDS_No));
  1441. swprintf(key,GET_STRING(IDS_DCTVSFmt_Servers_SkipDispatch_D),i);
  1442. pVarSet->put(key,GET_BSTR(IDS_YES));
  1443. }
  1444. pUnk->Release();
  1445. }
  1446. }
  1447. else
  1448. *bAnyToDispatch = true; //atleast one server for dispatcher
  1449. }
  1450. return hr;
  1451. }
  1452. HRESULT CMigrator::PopulateAccounts(IVarSetPtr pVs)
  1453. {
  1454. _bstr_t origSource = pVs->get(GET_BSTR(DCTVS_Options_SourceDomain));
  1455. _bstr_t origTarget = pVs->get(GET_BSTR(DCTVS_Options_TargetDomain));
  1456. // Check if the source domain is NT4 or win2k
  1457. // if NT4 then call the NetObjEnum to enumerate the domain.
  1458. return S_OK;
  1459. }
  1460. //----------------------------------------------------------------------------
  1461. // PopulateDomainDBs : This function coordinates the populating of the Access
  1462. // DBs for both the source and target domains with the
  1463. // necessary fields from the AD.
  1464. //----------------------------------------------------------------------------
  1465. bool CMigrator::PopulateDomainDBs(
  1466. IVarSet * pVarSet, //in- varset with domain names.
  1467. IIManageDBPtr pDb //in- an instance of DBManager
  1468. )
  1469. {
  1470. /* local variables */
  1471. _bstr_t srcdomain = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain));
  1472. _bstr_t tgtdomain = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain));
  1473. /* function body */
  1474. //populate the DB for the source domain
  1475. PopulateADomainDB(srcdomain, TRUE, pDb);
  1476. //populate the DB for the target domain
  1477. PopulateADomainDB(tgtdomain, FALSE, pDb);
  1478. return true;
  1479. }
  1480. //----------------------------------------------------------------------------
  1481. // PopulateADomainDB : This function looks up the necessary fields from the AD,
  1482. // using an MCSNetObjectEnum object, for the given domain
  1483. // and populates the corresponding Access DB with that info.
  1484. //----------------------------------------------------------------------------
  1485. bool CMigrator::PopulateADomainDB(
  1486. WCHAR const *domain, // in- name of domain to enumerate
  1487. BOOL bSource, // in- whether the domain is the source domain
  1488. IIManageDBPtr pDb // in- an instance of DBManager
  1489. )
  1490. {
  1491. INetObjEnumeratorPtr pQuery(__uuidof(NetObjEnumerator));
  1492. WCHAR sPath[MAX_PATH];
  1493. WCHAR sQuery[MAX_PATH];
  1494. LPWSTR sData[] = { L"sAMAccountName", L"ADsPath", L"distinguishedName", L"canonicalName", L"objectSid" };
  1495. HRESULT hr;
  1496. long nElt = DIM(sData);
  1497. BSTR HUGEP * pData = NULL;
  1498. SAFEARRAY * pszColNames;
  1499. IEnumVARIANT * pEnum = NULL;
  1500. _variant_t var;
  1501. bool bSuccess = false;
  1502. PCWSTR sType[] = { L"USER", L"GROUP", L"COMPUTER" };
  1503. bool bW2KDom = false;
  1504. CADsPathName apnPathName;
  1505. if ( bSource )
  1506. pDb->raw_ClearTable(L"SourceAccounts");
  1507. else
  1508. pDb->raw_ClearTable(L"TargetAccounts");
  1509. pDb->raw_OpenAccountsTable(bSource);
  1510. if (GetOSVersionForDomain(domain) > 4)
  1511. {
  1512. bW2KDom = true;
  1513. }
  1514. // iterate three times once to get USERS, GROUPS, COMPUTERS (mainly for WinNT)
  1515. for (int i = 0; i < 3; i++)
  1516. {
  1517. //
  1518. // If W2K or later setup for an LDAP query otherwise setup to use Net APIs.
  1519. //
  1520. if (bW2KDom)
  1521. {
  1522. //
  1523. // Generate ADsPath in order to query the entire domain.
  1524. //
  1525. wcscpy(sPath, L"LDAP://");
  1526. wcscat(sPath, domain);
  1527. //
  1528. // Generate LDAP query string for the type of object to query.
  1529. //
  1530. switch (i)
  1531. {
  1532. case 0:
  1533. // Query for user objects.
  1534. // Query only for normal account types which filters out trust accounts for example.
  1535. // Note that SAM_NORMAL_USER_ACCOUNT equals 0x30000000 hexadecimal or 805306368 decimal.
  1536. wcscpy(sQuery,
  1537. L"(&"
  1538. L"(objectCategory=Person)"
  1539. L"(|(objectClass=user)(objectClass=inetOrgPerson))"
  1540. L"(sAMAccountType=805306368)"
  1541. L")"
  1542. );
  1543. break;
  1544. case 1:
  1545. // Query for group objects.
  1546. wcscpy(sQuery, L"(objectCategory=Group)");
  1547. break;
  1548. case 2:
  1549. // Query for computer objects.
  1550. if (bSource)
  1551. {
  1552. // only query workstations and member servers as
  1553. // source domain controllers cannot be migrated
  1554. swprintf(
  1555. sQuery,
  1556. L"(&(objectCategory=Computer)(userAccountControl:%s:=%u))",
  1557. LDAP_MATCHING_RULE_BIT_AND_W,
  1558. static_cast<unsigned>(UF_WORKSTATION_TRUST_ACCOUNT)
  1559. );
  1560. }
  1561. else
  1562. {
  1563. // query workstations, member servers and domain controllers as source
  1564. // computer name may conflict with target domain controller name
  1565. swprintf(sQuery,
  1566. L"(&"
  1567. L"(objectCategory=Computer)"
  1568. L"(|(userAccountControl:%s:=%u)(userAccountControl:%s:=%u))"
  1569. L")",
  1570. LDAP_MATCHING_RULE_BIT_AND_W,
  1571. static_cast<unsigned>(UF_WORKSTATION_TRUST_ACCOUNT),
  1572. LDAP_MATCHING_RULE_BIT_AND_W,
  1573. static_cast<unsigned>(UF_SERVER_TRUST_ACCOUNT)
  1574. );
  1575. }
  1576. break;
  1577. default:
  1578. wcscpy(sQuery, L"");
  1579. break;
  1580. }
  1581. }
  1582. else
  1583. {
  1584. //
  1585. // specify type of object to query for when using net object enumerator on NT4 or earlier domains
  1586. //
  1587. wcscpy(sPath, L"CN=");
  1588. wcscat(sPath, sType[i]);
  1589. wcscat(sPath, L"S");
  1590. wcscpy(sQuery, L"(objectClass=*)");
  1591. }
  1592. // Set the enumerator query
  1593. hr = pQuery->raw_SetQuery(sPath, _bstr_t(domain), sQuery, ADS_SCOPE_SUBTREE, FALSE);
  1594. if (SUCCEEDED(hr))
  1595. {
  1596. // Create a safearray of columns we need from the enumerator.
  1597. SAFEARRAYBOUND bd = { nElt, 0 };
  1598. pszColNames = ::SafeArrayCreate(VT_BSTR, 1, &bd);
  1599. HRESULT hr = ::SafeArrayAccessData(pszColNames, (void HUGEP **)&pData);
  1600. if ( SUCCEEDED(hr) )
  1601. {
  1602. for( long i = 0; i < nElt; i++)
  1603. {
  1604. pData[i] = SysAllocString(sData[i]);
  1605. }
  1606. hr = ::SafeArrayUnaccessData(pszColNames);
  1607. }
  1608. if (SUCCEEDED(hr))
  1609. {
  1610. // Set the columns on the enumerator object.
  1611. hr = pQuery->raw_SetColumns(pszColNames);
  1612. }
  1613. }
  1614. if (SUCCEEDED(hr))
  1615. {
  1616. // Now execute.
  1617. hr = pQuery->raw_Execute(&pEnum);
  1618. }
  1619. //while we have more enumerated objects, get the enumerated fields
  1620. //for that object, save them in local variables, and add them to
  1621. //the appropriate DB
  1622. HRESULT hrEnum = S_OK;
  1623. DWORD dwFetch = 1;
  1624. while (hrEnum == S_OK && dwFetch > 0)
  1625. {
  1626. //get the enumerated fields for this current object
  1627. hrEnum = pEnum->Next(1, &var, &dwFetch);
  1628. if ( dwFetch > 0 && hrEnum == S_OK && ( var.vt & VT_ARRAY) )
  1629. {
  1630. BOOL bSave = TRUE;
  1631. // We now have a Variant containing an array of variants so we access the data
  1632. VARIANT* pVar;
  1633. pszColNames = V_ARRAY(&var);
  1634. SafeArrayAccessData(pszColNames, (void**)&pVar);
  1635. //get the sAMAccountName field
  1636. _bstr_t sSAMName = pVar[0].bstrVal;
  1637. _bstr_t sRDN = L"";
  1638. _bstr_t sCanonicalName = L"";
  1639. if (bW2KDom)
  1640. {
  1641. //
  1642. // Include only user defined objects from the source domain.
  1643. //
  1644. if ((bSource == FALSE) || IsUserRid(_variant_t(pVar[4])))
  1645. {
  1646. // get the relative distinguished name
  1647. _bstr_t sDN = pVar[2].bstrVal;
  1648. apnPathName.Set(sDN, ADS_SETTYPE_DN);
  1649. sRDN = apnPathName.GetElement(0L);
  1650. // get the canonical name
  1651. sCanonicalName = pVar[3].bstrVal;
  1652. }
  1653. else
  1654. {
  1655. bSave = FALSE;
  1656. }
  1657. }
  1658. else
  1659. {
  1660. //create an RDN from the SAM name
  1661. sRDN = L"CN=" + sSAMName;
  1662. //
  1663. // Include only user defined objects from source domain.
  1664. //
  1665. // Retrieve object id and compare against non reserved rids.
  1666. //
  1667. long lRid = _variant_t(pVar[2]);
  1668. if (lRid < MIN_NON_RESERVED_RID)
  1669. {
  1670. bSave = FALSE;
  1671. }
  1672. //
  1673. // Only include workstations and member servers and not domain controllers.
  1674. //
  1675. if (i == 2)
  1676. {
  1677. // retrieve object flags and check for domain controller bit
  1678. long lFlags = _variant_t(pVar[3]);
  1679. if (lFlags & UF_SERVER_TRUST_ACCOUNT)
  1680. {
  1681. bSave = FALSE;
  1682. }
  1683. }
  1684. }
  1685. SafeArrayUnaccessData(pszColNames);
  1686. //use the DBManager Interface to store this object's fields
  1687. //in the appropriate database
  1688. if (bSave)
  1689. {
  1690. pDb->raw_AddSourceObject(_bstr_t(domain), sSAMName, _bstr_t(sType[i]), sRDN, sCanonicalName, bSource);
  1691. }
  1692. var.Clear();
  1693. }
  1694. }
  1695. if ( pEnum ) pEnum->Release();
  1696. } // while
  1697. pDb->raw_CloseAccountsTable();
  1698. return SUCCEEDED(hr);
  1699. }
  1700. DWORD CMigrator::GetOSVersionForDomain(WCHAR const * domain)
  1701. {
  1702. _bstr_t strDc;
  1703. WKSTA_INFO_100 * pInfo = NULL;
  1704. DWORD retVal = 0;
  1705. DWORD rc = GetAnyDcName5(domain, strDc);
  1706. if ( !rc )
  1707. {
  1708. rc = NetWkstaGetInfo(strDc,100,(LPBYTE*)&pInfo);
  1709. if ( ! rc )
  1710. {
  1711. retVal = pInfo->wki100_ver_major;
  1712. NetApiBufferFree(pInfo);
  1713. }
  1714. }
  1715. return retVal;
  1716. }
  1717. BOOL CMigrator::DeleteItemFromList(WCHAR const * aName)
  1718. {
  1719. DATABASE_ENTRY aListItem;
  1720. CString itemName;
  1721. POSITION pos, lastpos;
  1722. BOOL bFound = FALSE;
  1723. pos = mUserList.GetHeadPosition();
  1724. while ((pos != NULL) && (!bFound))
  1725. {
  1726. lastpos = pos;
  1727. aListItem = mUserList.GetNext(pos);
  1728. itemName = (WCHAR*)(aListItem.m_sSAMName);
  1729. if (itemName == aName)
  1730. {
  1731. mUserList.RemoveAt(lastpos);
  1732. bFound = TRUE;
  1733. }
  1734. }
  1735. return bFound;
  1736. }
  1737. // IsAgentOrDispatcherProcessRunning
  1738. bool __stdcall IsAgentOrDispatcherProcessRunning()
  1739. {
  1740. bool bIsRunning = true;
  1741. CMigrationMutex mutexAgent(AGENT_MUTEX);
  1742. CMigrationMutex mutexDispatcher(DISPATCHER_MUTEX);
  1743. if (mutexAgent.ObtainOwnership(30000) && mutexDispatcher.ObtainOwnership(30000))
  1744. {
  1745. bIsRunning = false;
  1746. }
  1747. return bIsRunning;
  1748. }
  1749. // SetDomainControllers
  1750. //
  1751. // Sets preferred domain controllers to be used
  1752. // by the account replicator and dispatched agents
  1753. DWORD __stdcall SetDomainControllers(IVarSetPtr& spVarSet)
  1754. {
  1755. DWORD dwError = ERROR_SUCCESS;
  1756. // set source domain controller
  1757. _bstr_t strSourceServer = spVarSet->get(GET_BSTR(DCTVS_Options_SourceServerOverride));
  1758. _bstr_t strSourceServerDns = spVarSet->get(GET_BSTR(DCTVS_Options_SourceServerOverrideDns));
  1759. if ((strSourceServer.length() == 0) && (strSourceServerDns.length() == 0))
  1760. {
  1761. bool bSourceDns = false;
  1762. _bstr_t strSourceDomain = spVarSet->get(GET_BSTR(DCTVS_Options_SourceDomainDns));
  1763. if (strSourceDomain.length() > 0)
  1764. {
  1765. bSourceDns = true;
  1766. }
  1767. else
  1768. {
  1769. strSourceDomain = spVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain));
  1770. }
  1771. ULONG ulFlags = (bSourceDns ? DS_IS_DNS_NAME : DS_IS_FLAT_NAME) | DS_DIRECTORY_SERVICE_PREFERRED;
  1772. dwError = GetDcName5(strSourceDomain, ulFlags, strSourceServerDns, strSourceServer);
  1773. }
  1774. if (dwError == ERROR_SUCCESS)
  1775. {
  1776. spVarSet->put(
  1777. GET_BSTR(DCTVS_Options_SourceServer),
  1778. strSourceServerDns.length() ? strSourceServerDns : strSourceServer
  1779. );
  1780. spVarSet->put(GET_BSTR(DCTVS_Options_SourceServerDns), strSourceServerDns);
  1781. spVarSet->put(GET_BSTR(DCTVS_Options_SourceServerFlat), strSourceServer);
  1782. }
  1783. if (dwError != ERROR_SUCCESS)
  1784. {
  1785. return dwError;
  1786. }
  1787. // set target domain controller
  1788. _bstr_t strTargetServer = spVarSet->get(GET_BSTR(DCTVS_Options_TargetServerOverride));
  1789. _bstr_t strTargetServerDns = spVarSet->get(GET_BSTR(DCTVS_Options_TargetServerOverrideDns));
  1790. if ((strTargetServer.length() == 0) && (strTargetServerDns.length() == 0))
  1791. {
  1792. bool bTargetDns = false;
  1793. _bstr_t strTargetDomain = spVarSet->get(GET_BSTR(DCTVS_Options_TargetDomainDns));
  1794. if (strTargetDomain.length() > 0)
  1795. {
  1796. bTargetDns = true;
  1797. }
  1798. else
  1799. {
  1800. strTargetDomain = spVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain));
  1801. }
  1802. ULONG ulFlags = (bTargetDns ? DS_IS_DNS_NAME : DS_IS_FLAT_NAME) | DS_DIRECTORY_SERVICE_PREFERRED;
  1803. dwError = GetDcName5(strTargetDomain, ulFlags, strTargetServerDns, strTargetServer);
  1804. }
  1805. if (dwError == ERROR_SUCCESS)
  1806. {
  1807. spVarSet->put(
  1808. GET_BSTR(DCTVS_Options_TargetServer),
  1809. strTargetServerDns.length() ? strTargetServerDns : strTargetServer
  1810. );
  1811. spVarSet->put(GET_BSTR(DCTVS_Options_TargetServerDns), strTargetServerDns);
  1812. spVarSet->put(GET_BSTR(DCTVS_Options_TargetServerFlat), strTargetServer);
  1813. }
  1814. return dwError;
  1815. }