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.

11877 lines
483 KiB

  1. /*---------------------------------------------------------------------------
  2. File: AcctRepl.cpp
  3. Comments: Implementation of Account Replicator COM object.
  4. This COM object handles the copying or moving of directory objects.
  5. Win2K to Win2K migration is implemented in this file.
  6. NT -> Win2K migration is implemented in UserCopy.cpp
  7. (c) Copyright 1999, Mission Critical Software, Inc., All Rights Reserved
  8. Proprietary and confidential to Mission Critical Software, Inc.
  9. REVISION LOG ENTRY
  10. Revision By: Christy Boles
  11. Revised on 02/12/99 10:08:44
  12. Revision By: Sham Chauthani
  13. Revised on 07/02/99 12:40:00
  14. ---------------------------------------------------------------------------
  15. */
  16. // AcctRepl.cpp : Implementation of CAcctRepl
  17. #include "stdafx.h"
  18. #include "WorkObj.h"
  19. #include "AcctRepl.h"
  20. #include "BkupRstr.hpp"
  21. #include "StrHelp.h"
  22. /////////////////////////////////////////////////////////////////////////////
  23. // CAcctRepl
  24. #include "Err.hpp"
  25. #include "ErrDct.hpp"
  26. #include "EaLen.hpp"
  27. #include <dsgetdc.h>
  28. #include "UserRts.h"
  29. #include "BkupRstr.hpp"
  30. #include "DCTStat.h"
  31. #include "ResStr.h"
  32. #include "LSAUtils.h"
  33. #include "ARUtil.hpp"
  34. #include "Names.hpp"
  35. #include <lm.h>
  36. #include <iads.h>
  37. #include "RegTrans.h"
  38. #include "TEvent.hpp"
  39. #include "RecNode.hpp"
  40. #include "ntdsapi.h"
  41. #include "TxtSid.h"
  42. #include "ExLDAP.h"
  43. #include "GetDcName.h"
  44. #include "Array.h"
  45. #include "TReg.hpp"
  46. #import "AdsProp.tlb" no_namespace
  47. #import "NetEnum.tlb" no_namespace
  48. #ifndef IADsPtr
  49. _COM_SMARTPTR_TYPEDEF(IADs, IID_IADs);
  50. #endif
  51. #ifndef IADsContainerPtr
  52. _COM_SMARTPTR_TYPEDEF(IADsContainer, IID_IADsContainer);
  53. #endif
  54. #ifndef IADsGroupPtr
  55. _COM_SMARTPTR_TYPEDEF(IADsGroup, IID_IADsGroup);
  56. #endif
  57. #ifndef IADsPropertyPtr
  58. _COM_SMARTPTR_TYPEDEF(IADsProperty, IID_IADsProperty);
  59. #endif
  60. #ifndef IDirectorySearchPtr
  61. _COM_SMARTPTR_TYPEDEF(IDirectorySearch, IID_IDirectorySearch);
  62. #endif
  63. #ifndef IADsPathnamePtr
  64. _COM_SMARTPTR_TYPEDEF(IADsPathname, IID_IADsPathname);
  65. #endif
  66. #ifndef IADsMembersPtr
  67. _COM_SMARTPTR_TYPEDEF(IADsMembers, IID_IADsMembers);
  68. #endif
  69. #ifndef tstring
  70. typedef std::basic_string<_TCHAR> tstring;
  71. #endif
  72. extern tstring __stdcall GetEscapedFilterValue(PCTSTR pszValue);
  73. using namespace _com_util;
  74. #ifdef _DEBUG
  75. #define new DEBUG_NEW
  76. #undef THIS_FILE
  77. static char THIS_FILE[] = __FILE__;
  78. #endif
  79. IVarSet * g_pVarSet = NULL;
  80. TErrorDct err;
  81. TErrorDct errAlt; // this is used for logging errors that occur after dispatcher is launched; use migration.log
  82. bool useErrAlt;
  83. TError & errCommon = err;
  84. extern bool g_bAddSidWorks;
  85. DWORD g_dwOpMask = OPS_All; // Global OpSeq by default all ops
  86. bool bAbortMessageWritten = false;
  87. static WCHAR s_ClassUser[] = L"user";
  88. static WCHAR s_ClassInetOrgPerson[] = L"inetOrgPerson";
  89. BOOL BuiltinRid(DWORD rid);
  90. ADSGETOBJECT ADsGetObject;
  91. typedef BOOL (CALLBACK * TConvertStringSidToSid)(LPCWSTR StringSid,PSID *Sid);
  92. TConvertStringSidToSid ConvertStringSidToSid;
  93. bool firstTime = true;
  94. typedef struct _Lookup {
  95. WCHAR * pName;
  96. WCHAR * pType;
  97. } Lookup;
  98. //Function to sort by account sam name only
  99. int TNodeCompareNameOnly(TNode const * t1,TNode const * t2)
  100. {
  101. // Sort function to sort by Type(dec) and Name(asc)
  102. TAcctReplNode const * n1 = (TAcctReplNode *)t1;
  103. TAcctReplNode const * n2 = (TAcctReplNode *)t2;
  104. return UStrICmp(n1->GetSourceSam(), n2->GetTargetSam());
  105. }
  106. // Function to do a find on the Account list that is sorted with TNodeCompareNameOnly function.
  107. int TNodeFindByNameOnly(TNode const * t1, void const * pVoid)
  108. {
  109. TAcctReplNode const * n1 = (TAcctReplNode *) t1;
  110. WCHAR * pLookup = (WCHAR *) pVoid;
  111. return UStrICmp(n1->GetTargetSam(), pLookup);
  112. }
  113. int TNodeCompareAccountType(TNode const * t1,TNode const * t2)
  114. {
  115. // Sort function to sort by Type(dec) and Name(asc)
  116. TAcctReplNode const * n1 = (TAcctReplNode *)t1;
  117. TAcctReplNode const * n2 = (TAcctReplNode *)t2;
  118. // Compare types
  119. int retVal = UStrICmp(n2->GetType(), n1->GetType());
  120. if ( retVal == 0 )
  121. {
  122. // If same type then compare names.
  123. return UStrICmp(n1->GetName(), n2->GetName());
  124. }
  125. else
  126. return retVal;
  127. }
  128. int TNodeCompareAccountTypeAndRDN(TNode const * t1,TNode const * t2)
  129. {
  130. // Sort function to sort by Type(dec) and RDN(asc)
  131. TAcctReplNode const * n1 = (TAcctReplNode *)t1;
  132. TAcctReplNode const * n2 = (TAcctReplNode *)t2;
  133. // Compare types
  134. int retVal = UStrICmp(n2->GetType(), n1->GetType());
  135. if ( retVal == 0 )
  136. {
  137. // If same type then compare RDNs in the source paths.
  138. //get the RDNs from the source paths
  139. WCHAR* sN1RDN = wcschr(n1->GetSourcePath() + wcslen(L"WinNT://"), L'/');
  140. WCHAR* sN2RDN = wcschr(n2->GetSourcePath() + wcslen(L"WinNT://"), L'/');
  141. //if got RDNs
  142. if ((sN1RDN && *sN1RDN) && (sN2RDN && *sN2RDN))
  143. return UStrICmp(sN1RDN, sN2RDN);
  144. else //else, compare the whole source paths
  145. return UStrICmp(n1->GetSourcePath(), n2->GetSourcePath());;
  146. }
  147. else
  148. return retVal;
  149. }
  150. // Function to sort by Account type and then by SamAccountName
  151. int TNodeCompareAccountSam(TNode const * t1,TNode const * t2)
  152. {
  153. // Sort function to sort by Type(dec) and Name(asc)
  154. TAcctReplNode const * n1 = (TAcctReplNode *)t1;
  155. TAcctReplNode const * n2 = (TAcctReplNode *)t2;
  156. // Compare types Sort in decending order
  157. int retVal = UStrICmp(n2->GetType(), n1->GetType());
  158. if ( retVal == 0 )
  159. {
  160. // If same type then compare Sam Account names.
  161. return UStrICmp(n1->GetSourceSam(), n2->GetSourceSam());
  162. }
  163. else
  164. return retVal;
  165. }
  166. // Function to do a find on the Account list that is sorted with TNodeCompareAccountType function.
  167. int TNodeFindAccountName(TNode const * t1, void const * pVoid)
  168. {
  169. TAcctReplNode const * n1 = (TAcctReplNode *) t1;
  170. Lookup * pLookup = (Lookup *) pVoid;
  171. int retVal = UStrICmp(pLookup->pType, n1->GetType());
  172. if ( retVal == 0 )
  173. {
  174. return UStrICmp(n1->GetSourceSam(), pLookup->pName);
  175. }
  176. else
  177. return retVal;
  178. }
  179. // Function to do a find on the Account list that is sorted with TNodeCompareAccountTypeAndRDN
  180. // function by using the RDN in a given path.
  181. int TNodeFindAccountRDN(TNode const * t1, void const * pVoid)
  182. {
  183. TAcctReplNode const * n1 = (TAcctReplNode *) t1;
  184. Lookup * pLookup = (Lookup *) pVoid;
  185. int retVal = UStrICmp(pLookup->pType, n1->GetType());
  186. if ( retVal == 0 )
  187. {
  188. //get and compare the RDNs in these paths
  189. WCHAR* sNodeRDN = wcschr(n1->GetSourcePath() + wcslen(L"LDAP://"), L'/');
  190. WCHAR* sLookupRDN = wcschr(pLookup->pName + wcslen(L"LDAP://"), L'/');
  191. //if got the RDNs, compare them
  192. if ((sNodeRDN && *sNodeRDN) && (sLookupRDN && *sLookupRDN))
  193. return UStrICmp(sNodeRDN, sLookupRDN);
  194. else //else, compare the whole source path
  195. return UStrICmp(n1->GetSourcePath(), pLookup->pName);;
  196. }
  197. else
  198. return retVal;
  199. }
  200. int TNodeCompareMember(TNode const * t1, TNode const * t2)
  201. {
  202. TRecordNode const * n1 = (TRecordNode *) t1;
  203. TRecordNode const * n2 = (TRecordNode *) t2;
  204. if ( n1->GetARNode() < n2->GetARNode() )
  205. return -1;
  206. if ( n1->GetARNode() > n2->GetARNode() )
  207. return 1;
  208. return UStrICmp(n1->GetMember(), n2->GetMember());
  209. }
  210. int TNodeCompareMemberName(TNode const * t1, TNode const * t2)
  211. {
  212. TRecordNode const * n1 = (TRecordNode *) t1;
  213. TRecordNode const * n2 = (TRecordNode *) t2;
  214. return UStrICmp(n1->GetMember(), n2->GetMember());
  215. }
  216. int TNodeCompareMemberDN(TNode const * t1, TNode const * t2)
  217. {
  218. TRecordNode const * n1 = (TRecordNode *) t1;
  219. TRecordNode const * n2 = (TRecordNode *) t2;
  220. return UStrICmp(n1->GetDN(), n2->GetDN());
  221. }
  222. int TNodeCompareMemberItem(TNode const * t1, void const * t2)
  223. {
  224. TRecordNode const * n1 = (TRecordNode *) t1;
  225. WCHAR const * n2 = (WCHAR const *) t2;
  226. return UStrICmp(n1->GetDN(),n2);
  227. }
  228. int TNodeCompareAcctNode(TNode const * t1, TNode const * t2)
  229. {
  230. TRecordNode const * n1 = (TRecordNode *) t1;
  231. TRecordNode const * n2 = (TRecordNode *) t2;
  232. if ( n1->GetARNode() < n2->GetARNode() )
  233. return -1;
  234. if ( n1->GetARNode() > n2->GetARNode() )
  235. return 1;
  236. return 0;
  237. }
  238. // Checks to see if the account is from the BUILTIN domain.
  239. BOOL IsBuiltinAccount(Options * pOptions, WCHAR * sAcctName)
  240. {
  241. BOOL ret = FALSE;
  242. PSID sid = new BYTE[35];
  243. SID_NAME_USE use;
  244. WCHAR sDomain[LEN_Path];
  245. DWORD dwDom, dwsid;
  246. if (!sid)
  247. return TRUE;
  248. dwDom = DIM(sDomain);
  249. dwsid = 35;
  250. if ( LookupAccountName(pOptions->srcComp, sAcctName, sid, &dwsid, sDomain, &dwDom, &use) )
  251. {
  252. ret = !_wcsicmp(sDomain, L"BUILTIN");
  253. }
  254. if (sid)
  255. delete [] sid;
  256. return ret;
  257. }
  258. // global counters defined in usercopy.cpp
  259. extern AccountStats warnings;
  260. extern AccountStats errors;
  261. extern AccountStats created;
  262. extern AccountStats replaced;
  263. extern AccountStats processed;
  264. // updates progress indicator
  265. // this updates the stats entries in the VarSet
  266. // this information will be returned to clients who call DCTAgent::QueryJobStatus
  267. // while the job is running.
  268. void
  269. Progress(
  270. WCHAR const * mesg // in - progress message
  271. )
  272. {
  273. if ( g_pVarSet )
  274. {
  275. g_pVarSet->put(GET_WSTR(DCTVS_CurrentPath),mesg);
  276. g_pVarSet->put(GET_WSTR(DCTVS_Stats_Users_Examined),processed.users);
  277. g_pVarSet->put(GET_WSTR(DCTVS_Stats_Users_Created),created.users);
  278. g_pVarSet->put(GET_WSTR(DCTVS_Stats_Users_Replaced),replaced.users);
  279. g_pVarSet->put(GET_WSTR(DCTVS_Stats_Users_Warnings),warnings.users);
  280. g_pVarSet->put(GET_WSTR(DCTVS_Stats_Users_Errors),errors.users);
  281. g_pVarSet->put(GET_WSTR(DCTVS_Stats_GlobalGroups_Examined),processed.globals);
  282. g_pVarSet->put(GET_WSTR(DCTVS_Stats_GlobalGroups_Created),created.globals);
  283. g_pVarSet->put(GET_WSTR(DCTVS_Stats_GlobalGroups_Replaced),replaced.globals);
  284. g_pVarSet->put(GET_WSTR(DCTVS_Stats_GlobalGroups_Warnings),warnings.globals);
  285. g_pVarSet->put(GET_WSTR(DCTVS_Stats_GlobalGroups_Errors),errors.globals);
  286. g_pVarSet->put(GET_WSTR(DCTVS_Stats_LocalGroups_Examined),processed.locals);
  287. g_pVarSet->put(GET_WSTR(DCTVS_Stats_LocalGroups_Created),created.locals);
  288. g_pVarSet->put(GET_WSTR(DCTVS_Stats_LocalGroups_Replaced),replaced.locals);
  289. g_pVarSet->put(GET_WSTR(DCTVS_Stats_LocalGroups_Warnings),warnings.locals);
  290. g_pVarSet->put(GET_WSTR(DCTVS_Stats_LocalGroups_Errors),errors.locals);
  291. g_pVarSet->put(GET_WSTR(DCTVS_Stats_Computers_Examined),processed.computers);
  292. g_pVarSet->put(GET_WSTR(DCTVS_Stats_Computers_Created),created.computers);
  293. g_pVarSet->put(GET_WSTR(DCTVS_Stats_Computers_Replaced),replaced.computers);
  294. g_pVarSet->put(GET_WSTR(DCTVS_Stats_Computers_Warnings),warnings.computers);
  295. g_pVarSet->put(GET_WSTR(DCTVS_Stats_Computers_Errors),errors.computers);
  296. g_pVarSet->put(GET_WSTR(DCTVS_Stats_Generic_Examined),processed.generic);
  297. g_pVarSet->put(GET_WSTR(DCTVS_Stats_Generic_Created),created.generic);
  298. g_pVarSet->put(GET_WSTR(DCTVS_Stats_Generic_Replaced),replaced.generic);
  299. g_pVarSet->put(GET_WSTR(DCTVS_Stats_Generic_Warnings),warnings.generic);
  300. g_pVarSet->put(GET_WSTR(DCTVS_Stats_Generic_Errors),errors.generic);
  301. }
  302. }
  303. // Gets the domain sid for the specified domain
  304. BOOL // ret- TRUE if successful
  305. GetSidForDomain(
  306. LPWSTR DomainName, // in - name of domain to get SID for
  307. PSID * pDomainSid // out- SID for domain, free with FreeSid
  308. )
  309. {
  310. PSID pSid = NULL;
  311. DWORD rc = 0;
  312. _bstr_t domctrl;
  313. if (DomainName == NULL || DomainName[0] != L'\\' )
  314. {
  315. rc = GetAnyDcName4(DomainName, domctrl);
  316. }
  317. if ( ! rc )
  318. {
  319. rc = GetDomainSid(domctrl,&pSid);
  320. }
  321. (*pDomainSid) = pSid;
  322. return ( pSid != NULL);
  323. }
  324. //---------------------------------------------------------------------------
  325. // CADsPathName Class
  326. //
  327. // Note that this class was copied from AdsiHelpers.h but I have had trouble
  328. // including it in this file. Therefore using copy which should be removed
  329. // once AdsiHelpers.h is included.
  330. //---------------------------------------------------------------------------
  331. #ifndef CADsPathName
  332. class CADsPathName
  333. {
  334. // ADS_DISPLAY_ENUM
  335. // ADS_DISPLAY_FULL = 1
  336. // ADS_DISPLAY_VALUE_ONLY = 2
  337. // ADS_FORMAT_ENUM
  338. // ADS_FORMAT_WINDOWS = 1
  339. // ADS_FORMAT_WINDOWS_NO_SERVER = 2
  340. // ADS_FORMAT_WINDOWS_DN = 3
  341. // ADS_FORMAT_WINDOWS_PARENT = 4
  342. // ADS_FORMAT_X500 = 5
  343. // ADS_FORMAT_X500_NO_SERVER = 6
  344. // ADS_FORMAT_X500_DN = 7
  345. // ADS_FORMAT_X500_PARENT = 8
  346. // ADS_FORMAT_SERVER = 9
  347. // ADS_FORMAT_PROVIDER = 10
  348. // ADS_FORMAT_LEAF = 11
  349. // ADS_SETTYPE_ENUM
  350. // ADS_SETTYPE_FULL = 1
  351. // ADS_SETTYPE_PROVIDER = 2
  352. // ADS_SETTYPE_SERVER = 3
  353. // ADS_SETTYPE_DN = 4
  354. public:
  355. CADsPathName(_bstr_t strPath = _bstr_t(), long lSetType = ADS_SETTYPE_FULL) :
  356. m_sp(CLSID_Pathname)
  357. {
  358. if (strPath.length() > 0)
  359. {
  360. CheckResult(m_sp->Set(strPath, lSetType));
  361. }
  362. }
  363. void Set(_bstr_t strADsPath, long lSetType)
  364. {
  365. CheckResult(m_sp->Set(strADsPath, lSetType));
  366. }
  367. void SetDisplayType(long lDisplayType)
  368. {
  369. CheckResult(m_sp->SetDisplayType(lDisplayType));
  370. }
  371. _bstr_t Retrieve(long lFormatType)
  372. {
  373. BSTR bstr;
  374. CheckResult(m_sp->Retrieve(lFormatType, &bstr));
  375. return _bstr_t(bstr, false);
  376. }
  377. long GetNumElements()
  378. {
  379. long l;
  380. CheckResult(m_sp->GetNumElements(&l));
  381. return l;
  382. }
  383. _bstr_t GetElement(long lElementIndex)
  384. {
  385. BSTR bstr;
  386. CheckResult(m_sp->GetElement(lElementIndex, &bstr));
  387. return _bstr_t(bstr, false);
  388. }
  389. void AddLeafElement(_bstr_t strLeafElement)
  390. {
  391. CheckResult(m_sp->AddLeafElement(strLeafElement));
  392. }
  393. void RemoveLeafElement()
  394. {
  395. CheckResult(m_sp->RemoveLeafElement());
  396. }
  397. CADsPathName CopyPath()
  398. {
  399. IDispatch* pdisp;
  400. CheckResult(m_sp->CopyPath(&pdisp));
  401. return CADsPathName(IADsPathnamePtr(IDispatchPtr(pdisp, false)));
  402. }
  403. _bstr_t GetEscapedElement(long lReserved, _bstr_t strInStr)
  404. {
  405. BSTR bstr;
  406. CheckResult(m_sp->GetEscapedElement(lReserved, strInStr, &bstr));
  407. return _bstr_t(bstr, false);
  408. }
  409. long GetEscapedMode()
  410. {
  411. long l;
  412. CheckResult(m_sp->get_EscapedMode(&l));
  413. return l;
  414. }
  415. void PutEscapedMode(long l)
  416. {
  417. CheckResult(m_sp->put_EscapedMode(l));
  418. }
  419. protected:
  420. CADsPathName(const CADsPathName& r) :
  421. m_sp(r.m_sp)
  422. {
  423. }
  424. CADsPathName(IADsPathnamePtr& r) :
  425. m_sp(r)
  426. {
  427. }
  428. void CheckResult(HRESULT hr)
  429. {
  430. if (FAILED(hr))
  431. {
  432. _com_issue_errorex(hr, IUnknownPtr(m_sp), IID_IADsPathname);
  433. }
  434. }
  435. protected:
  436. IADsPathnamePtr m_sp;
  437. };
  438. #endif
  439. STDMETHODIMP
  440. CAcctRepl::Process(
  441. IUnknown * pWorkItemIn // in - VarSet defining account replication job
  442. )
  443. {
  444. HRESULT hr = S_OK;
  445. try
  446. {
  447. IVarSetPtr pVarSet = pWorkItemIn;
  448. MCSDCTWORKEROBJECTSLib::IStatusObjPtr pStatus;
  449. BOOL bSameForest = FALSE;
  450. HMODULE hMod = LoadLibrary(L"activeds.dll");
  451. if ( hMod == NULL )
  452. {
  453. DWORD eNum = GetLastError();
  454. err.SysMsgWrite(ErrE, eNum, DCT_MSG_LOAD_LIBRARY_FAILED_SD, L"activeds.dll", eNum);
  455. Mark(L"errors",L"generic");
  456. return HRESULT_FROM_WIN32(eNum);
  457. }
  458. ADsGetObject = (ADSGETOBJECT)GetProcAddress(hMod, "ADsGetObject");
  459. g_pVarSet = pVarSet;
  460. try{
  461. pStatus = pVarSet->get(GET_BSTR(DCTVS_StatusObject));
  462. opt.pStatus = pStatus;
  463. }
  464. catch (...)
  465. {
  466. // Oh well, keep going
  467. }
  468. // Load the options specified by the user including the account information
  469. WCHAR mesg[LEN_Path];
  470. wcscpy(mesg, GET_STRING(IDS_BUILDING_ACCOUNT_LIST));
  471. Progress(mesg);
  472. LoadOptionsFromVarSet(pVarSet);
  473. MCSDCTWORKEROBJECTSLib::IAccessCheckerPtr pAccess(__uuidof(MCSDCTWORKEROBJECTSLib::AccessChecker));
  474. if ( BothWin2K(&opt) )
  475. {
  476. hr = pAccess->raw_IsInSameForest(opt.srcDomainDns,opt.tgtDomainDns, (long*)&bSameForest);
  477. }
  478. if ( SUCCEEDED(hr) )
  479. {
  480. opt.bSameForest = bSameForest;
  481. }
  482. // We are going to initialize the Extension objects
  483. m_pExt = new CProcessExtensions(pVarSet);
  484. //
  485. // If updating of user rights is specified then create
  486. // instance of user rights component and set the test mode option.
  487. //
  488. if (m_UpdateUserRights)
  489. {
  490. CheckError(CoCreateInstance(CLSID_UserRights, NULL,CLSCTX_ALL, IID_IUserRights, (void**)&m_pUserRights));
  491. m_pUserRights->put_NoChange(opt.nochange);
  492. }
  493. TNodeListSortable newList;
  494. if ( opt.expandMemberOf && ! opt.bUndo ) // always expand the member-of property, since we want to update the member-of property for migrated accounts
  495. {
  496. // Expand the containers and the membership
  497. wcscpy(mesg, GET_STRING(IDS_EXPANDING_MEMBERSHIP));
  498. Progress(mesg);
  499. // Expand the list to include all the groups that the accounts in this list are members of
  500. newList.CompareSet(&TNodeCompareAccountTypeAndRDN);
  501. if ( newList.IsTree() ) newList.ToSorted();
  502. ExpandMembership( &acctList, &opt, &newList, Progress, FALSE);
  503. }
  504. if ( opt.expandContainers && !opt.bUndo)
  505. {
  506. // Expand the containers and the membership
  507. wcscpy(mesg, GET_STRING(IDS_EXPANDING_CONTAINERS));
  508. Progress(mesg);
  509. // Expand the list to include all the members of the containers.
  510. acctList.CompareSet(&TNodeCompareAccountTypeAndRDN);
  511. ExpandContainers(&acctList, &opt, Progress);
  512. }
  513. // Add the newly created list ( if one was created )
  514. if ( opt.expandMemberOf && !opt.bUndo )
  515. {
  516. wcscpy(mesg, GET_STRING(IDS_MERGING_EXPANDED_LISTS));
  517. Progress(mesg);
  518. // add the new and the old list
  519. acctList.CompareSet(&TNodeCompareAccountTypeAndRDN);
  520. for ( TNode * acct = newList.Head(); acct; )
  521. {
  522. TNode * temp = acct->Next();
  523. if ( ! acctList.InsertIfNew(acct) )
  524. delete acct;
  525. acct = temp;
  526. }
  527. Progress(L"");
  528. }
  529. do { // once
  530. // Copy the NT accounts for users, groups and/or computers
  531. if ( pStatus!= NULL && (pStatus->Status & DCT_STATUS_ABORTING) )
  532. break;
  533. int res;
  534. if ( opt.bUndo )
  535. res = UndoCopy(&opt,&acctList,&Progress, err,(IStatusObj *)((MCSDCTWORKEROBJECTSLib::IStatusObj *)pStatus),NULL);
  536. else
  537. res = CopyObj( &opt,&acctList,&Progress, err,(IStatusObj *)((MCSDCTWORKEROBJECTSLib::IStatusObj *)pStatus),NULL);
  538. // Close the password log
  539. if ( opt.passwordLog.IsOpen() )
  540. {
  541. opt.passwordLog.LogClose();
  542. }
  543. if ( pStatus != NULL && (pStatus->Status & DCT_STATUS_ABORTING) )
  544. break;
  545. // Update Rights for user and group accounts
  546. if ( m_UpdateUserRights )
  547. {
  548. UpdateUserRights((IStatusObj *)((MCSDCTWORKEROBJECTSLib::IStatusObj *)pStatus));
  549. }
  550. if ( pStatus != NULL && (pStatus->Status & DCT_STATUS_ABORTING) )
  551. break;
  552. // Change of Domain affiliation on computers and optional reboot will be done by local agent
  553. } while (false);
  554. LoadResultsToVarSet(pVarSet);
  555. // Cleanup the account list
  556. if ( acctList.IsTree() )
  557. {
  558. acctList.ToSorted();
  559. }
  560. TNodeListEnum e;
  561. TAcctReplNode * tnode;
  562. TAcctReplNode * tnext;
  563. for ( tnode = (TAcctReplNode *)e.OpenFirst(&acctList) ; tnode ; tnode = tnext )
  564. {
  565. tnext = (TAcctReplNode*)e.Next();
  566. acctList.Remove(tnode);
  567. delete tnode;
  568. }
  569. e.Close();
  570. err.LogClose();
  571. Progress(L"");
  572. if (m_pExt)
  573. delete m_pExt;
  574. g_pVarSet = NULL;
  575. if ( hMod )
  576. FreeLibrary(hMod);
  577. }
  578. catch (_com_error& ce)
  579. {
  580. hr = ce.Error();
  581. err.SysMsgWrite(ErrS, hr, DCT_MSG_ACCOUNT_REPLICATOR_UNABLE_TO_CONTINUE);
  582. }
  583. catch (...)
  584. {
  585. hr = E_UNEXPECTED;
  586. err.SysMsgWrite(ErrS, hr, DCT_MSG_ACCOUNT_REPLICATOR_UNABLE_TO_CONTINUE);
  587. }
  588. return hr;
  589. }
  590. //------------------------------------------------------------------------------
  591. // CopyObj: When source and target domains are both Win2k this function calls
  592. // The 2kobject functions. Other wise it calls the User copy functions.
  593. //------------------------------------------------------------------------------
  594. int CAcctRepl::CopyObj(
  595. Options * options, // in -options
  596. TNodeListSortable * acctlist, // in -list of accounts to process
  597. ProgressFn * progress, // in -window to write progress messages to
  598. TError & error, // in -window to write error messages to
  599. IStatusObj * pStatus, // in -status object to support cancellation
  600. void WindowUpdate (void ) // in - window update function
  601. )
  602. {
  603. BOOL bSameForest = FALSE;
  604. long rc;
  605. HRESULT hr = S_OK;
  606. // if the Source/Target domain is NT4 then use the UserCopy Function. If both domains are Win2K then use
  607. // the CopyObj2K function to do so.
  608. if ( BothWin2K( options ) )
  609. {
  610. // Since these are Win2k domains we need to process it with Win2k code.
  611. MCSDCTWORKEROBJECTSLib::IAccessCheckerPtr pAccess(__uuidof(MCSDCTWORKEROBJECTSLib::AccessChecker));
  612. // First of all we need to find out if they are in the same forest.
  613. HRESULT hr = pAccess->raw_IsInSameForest(options->srcDomainDns,options->tgtDomainDns, (long*)&bSameForest);
  614. if ( SUCCEEDED(hr) )
  615. {
  616. options->bSameForest = bSameForest;
  617. if ( !bSameForest || (options->flags & F_COMPUTERS) ) // always copy the computer accounts
  618. {
  619. // Different forest we need to copy.
  620. rc = CopyObj2K(options, acctlist, progress, pStatus);
  621. if (opt.fixMembership)
  622. {
  623. // Update the group memberships
  624. rc = UpdateGroupMembership(options, acctlist, progress, pStatus);
  625. if ( !options->expandMemberOf )
  626. {
  627. hr = UpdateMemberToGroups(acctlist, options, FALSE);
  628. rc = HRESULT_CODE(hr);
  629. }
  630. else //if groups migrated, still expand but only for groups
  631. {
  632. hr = UpdateMemberToGroups(acctlist, options, TRUE);
  633. rc = HRESULT_CODE(hr);
  634. }
  635. }
  636. //for user or group, migrate the manager\directReports or
  637. //managedBy\managedObjects properties respectively
  638. if ((options->flags & F_USERS) || (options->flags & F_GROUP))
  639. UpdateManagement(acctlist, options);
  640. }
  641. else
  642. {
  643. // Within a forest we can move the object around.
  644. rc = MoveObj2K(options, acctlist, progress, pStatus);
  645. }
  646. if ( progress )
  647. progress(L"");
  648. }
  649. else
  650. {
  651. rc = -1;
  652. err.SysMsgWrite(ErrE, hr, DCT_MSG_ACCESS_CHECKER_FAILED_D, hr);
  653. Mark(L"errors",L"generic");
  654. }
  655. }
  656. else
  657. {
  658. // Create the object.
  659. rc = CopyObj2K(options, acctlist, progress, pStatus);
  660. if (opt.fixMembership)
  661. {
  662. rc = UpdateGroupMembership(options, acctlist, progress, pStatus);
  663. if ( !options->expandMemberOf )
  664. {
  665. hr = UpdateMemberToGroups(acctlist, options, FALSE);
  666. rc = HRESULT_CODE(hr);
  667. }
  668. else //if groups migrated, still expand but only for groups
  669. {
  670. hr = UpdateMemberToGroups(acctlist, options, TRUE);
  671. rc = HRESULT_CODE(hr);
  672. }
  673. }
  674. // Call NT4 Code to update the group memberships
  675. //UpdateNT4GroupMembership(options, acctlist, progress, pStatus, WindowUpdate);
  676. }
  677. return rc;
  678. }
  679. //------------------------------------------------------------------------------
  680. // BothWin2k: Checks to see if Source and Target domains are both Win2k.
  681. //------------------------------------------------------------------------------
  682. bool CAcctRepl::BothWin2K( // True if both domains are win2k
  683. Options * pOptions //in- options
  684. )
  685. {
  686. // This function checks for the version on the Source and Target domain. If either one is
  687. // a non Win2K domain then it returns false
  688. bool retVal = true;
  689. if ( (pOptions->srcDomainVer > -1) && (pOptions->tgtDomainVer > -1) )
  690. return ((pOptions->srcDomainVer > 4) && (pOptions->tgtDomainVer > 4));
  691. MCSDCTWORKEROBJECTSLib::IAccessCheckerPtr pAccess(__uuidof(MCSDCTWORKEROBJECTSLib::AccessChecker));
  692. HRESULT hr;
  693. DWORD verMaj, verMin, sp;
  694. hr = pAccess->raw_GetOsVersion(pOptions->srcComp, &verMaj, &verMin, &sp);
  695. if ( FAILED(hr) )
  696. {
  697. err.SysMsgWrite(ErrE,hr, DCT_MSG_GET_OS_VER_FAILED_SD, pOptions->srcDomain, hr);
  698. Mark(L"errors", L"generic");
  699. retVal = false;
  700. }
  701. else
  702. {
  703. pOptions->srcDomainVer = verMaj;
  704. pOptions->srcDomainVerMinor = verMin;
  705. if (verMaj < 5)
  706. retVal = false;
  707. }
  708. hr = pAccess->raw_GetOsVersion(pOptions->tgtComp, &verMaj, &verMin, &sp);
  709. if ( FAILED(hr) )
  710. {
  711. err.SysMsgWrite(ErrE, hr,DCT_MSG_GET_OS_VER_FAILED_SD, pOptions->tgtDomain , hr);
  712. Mark(L"errors", L"generic");
  713. retVal = false;
  714. }
  715. else
  716. {
  717. pOptions->tgtDomainVer = verMaj;
  718. pOptions->tgtDomainVerMinor = verMin;
  719. if (verMaj < 5)
  720. retVal = false;
  721. }
  722. return retVal;
  723. }
  724. int CAcctRepl::CopyObj2K(
  725. Options * pOptions, //in -Options that we recieved from the user
  726. TNodeListSortable * acctlist, //in -AcctList of accounts to be copied.
  727. ProgressFn * progress, //in -Progress Function to display messages
  728. IStatusObj * pStatus // in -status object to support cancellation
  729. )
  730. {
  731. // This function copies the object from Win2K domain to another Win2K domain.
  732. TNodeTreeEnum tenum;
  733. TAcctReplNode * acct;
  734. IObjPropBuilderPtr pObjProp(__uuidof(ObjPropBuilder));
  735. IVarSetPtr pVarset(__uuidof(VarSet));
  736. IUnknown * pUnk;
  737. HRESULT hr;
  738. _bstr_t currentType = L"";
  739. // TNodeListSortable pMemberOf;
  740. // sort the account list by Source Type\Source Name
  741. acctlist->CompareSet(&TNodeCompareAccountType);
  742. if ( acctlist->IsTree() ) acctlist->ToSorted();
  743. acctlist->SortedToScrambledTree();
  744. acctlist->Sort(&TNodeCompareAccountType);
  745. acctlist->Balance();
  746. if ( pOptions->flags & F_AddSidHistory )
  747. {
  748. //Need to Add Sid history on the target account. So lets bind it and go from there
  749. g_bAddSidWorks = BindToDS( pOptions );
  750. }
  751. if ( pOptions->flags & F_TranslateProfiles )
  752. {
  753. GetBkupRstrPriv((WCHAR*)NULL);
  754. GetPrivilege((WCHAR*)NULL,SE_SECURITY_NAME);
  755. }
  756. // Get the defaultNamingContext for the source domain
  757. _variant_t var;
  758. // Get an IUnknown pointer to the Varset for passing it around.
  759. hr = pVarset->QueryInterface(IID_IUnknown, (void**)&pUnk);
  760. CTargetPathSet setTargetPath;
  761. for ( acct = (TAcctReplNode *)tenum.OpenFirst(acctlist) ; acct ; acct = (TAcctReplNode *)tenum.Next() )
  762. {
  763. if (m_pExt && acct->CallExt())
  764. {
  765. hr = m_pExt->Process(acct, pOptions->tgtDomain, pOptions,TRUE);
  766. }
  767. // We will process accounts only if the corresponding check boxes (for object types to copy) are checked.
  768. if ( !NeedToProcessAccount( acct, pOptions ) )
  769. continue;
  770. // If we are told not to copy the object then we will obey
  771. if ( !acct->CreateAccount() )
  772. continue;
  773. //if the UPN name conflicted, then the UPNUpdate extension set the hr to
  774. //ERROR_OBJECT_ALREADY_EXISTS. If so, set flag for "no change" mode
  775. if (acct->GetHr() == ERROR_OBJECT_ALREADY_EXISTS)
  776. {
  777. acct->bUPNConflicted = TRUE;
  778. acct->SetHr(S_OK);
  779. }
  780. // Mark processed object count and update the status display
  781. Mark(L"processed", acct->GetType());
  782. if ( pStatus )
  783. {
  784. LONG status = 0;
  785. HRESULT hr = pStatus->get_Status(&status);
  786. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  787. {
  788. if ( !bAbortMessageWritten )
  789. {
  790. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  791. bAbortMessageWritten = true;
  792. }
  793. break;
  794. }
  795. }
  796. // Create the target object
  797. WCHAR mesg[LEN_Path];
  798. wsprintf(mesg, GET_STRING(IDS_CREATING_S), acct->GetName());
  799. if ( progress )
  800. progress(mesg);
  801. HRESULT hrCreate = Create2KObj(acct, pOptions, setTargetPath);
  802. acct->SetHr(hrCreate);
  803. if ( SUCCEEDED(hrCreate) )
  804. {
  805. err.MsgWrite(0, DCT_MSG_ACCOUNT_CREATED_S, acct->GetTargetName());
  806. }
  807. else
  808. {
  809. if ((HRESULT_CODE(hrCreate) == ERROR_OBJECT_ALREADY_EXISTS) )
  810. {
  811. ;
  812. }
  813. else
  814. {
  815. if ( acct->IsCritical() )
  816. {
  817. err.SysMsgWrite(ErrE,ERROR_SPECIAL_ACCOUNT,DCT_MSG_REPLACE_FAILED_SD,acct->GetName(),ERROR_SPECIAL_ACCOUNT);
  818. Mark(L"errors", acct->GetType());
  819. }
  820. else
  821. {
  822. if ( HRESULT_CODE(hrCreate) == ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH )
  823. {
  824. err.MsgWrite(ErrE, DCT_MSG_CANT_REPLACE_DIFFERENT_TYPE_SS, acct->GetTargetPath(), acct->GetSourcePath() );
  825. Mark(L"errors", acct->GetType());
  826. }
  827. else
  828. {
  829. err.SysMsgWrite(ErrE, hrCreate, DCT_MSG_CREATE_FAILED_SSD, acct->GetName(), pOptions->tgtDomain, hrCreate);
  830. Mark(L"errors", acct->GetType());
  831. }
  832. }
  833. }
  834. }
  835. if ( acct->WasCreated() )
  836. {
  837. // Do we need to add sid history
  838. if ( pOptions->flags & F_AddSidHistory )
  839. {
  840. // Global flag tells us if we should try the AddSidHistory because
  841. // for some special cases if it does not work once it will not work
  842. // see the AddSidHistory function for more details.
  843. if ( g_bAddSidWorks )
  844. {
  845. WCHAR mesg[LEN_Path];
  846. wsprintf(mesg, GET_STRING(IDS_ADDING_SIDHISTORY_S), acct->GetName());
  847. if ( progress )
  848. progress(mesg);
  849. if (! AddSidHistory( pOptions, acct->GetSourceSam(), acct->GetTargetSam(), pStatus ) )
  850. {
  851. Mark(L"errors", acct->GetType());
  852. }
  853. // CopySidHistoryProperty(pOptions, acct, pStatus);
  854. }
  855. }
  856. }
  857. }
  858. tenum.Close();
  859. // free memory as set no longer needed
  860. setTargetPath.clear();
  861. bool bWin2k = BothWin2K(pOptions);
  862. //
  863. // Generate a mapping between the source object's distinguished name and the target object's
  864. // distinguished name. This is used to translate distinguished name attributes during copying
  865. // of properties.
  866. //
  867. IVarSetPtr spSourceToTargetDnMap = GenerateSourceToTargetDnMap(acctlist);
  868. for ( acct = (TAcctReplNode *)tenum.OpenFirst(acctlist) ; acct ; acct = (TAcctReplNode *)tenum.Next() )
  869. {
  870. if ( pStatus )
  871. {
  872. LONG status = 0;
  873. HRESULT hr = pStatus->get_Status(&status);
  874. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  875. {
  876. if ( !bAbortMessageWritten )
  877. {
  878. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  879. bAbortMessageWritten = true;
  880. }
  881. break;
  882. }
  883. }
  884. // We are told not to copy the properties to the account so we ignore it.
  885. if ( acct->CopyProps() )
  886. {
  887. // If the object type is different from the one that was processed prior to this then we need to map properties
  888. if ((!pOptions->nochange) && (_wcsicmp(acct->GetType(),currentType) != 0))
  889. {
  890. WCHAR mesg[LEN_Path];
  891. wsprintf(mesg, GET_STRING(IDS_MAPPING_PROPS_S), acct->GetType());
  892. if ( progress )
  893. progress(mesg);
  894. // Set the current type
  895. currentType = acct->GetType();
  896. // Clear the current mapping
  897. pVarset->Clear();
  898. // Get a new mapping
  899. if ( BothWin2K(pOptions) )
  900. {
  901. hr = pObjProp->raw_MapProperties(currentType, pOptions->srcDomain, pOptions->srcDomainVer, currentType, pOptions->tgtDomain, pOptions->tgtDomainVer, 0, &pUnk);
  902. if (hr == DCT_MSG_PROPERTIES_NOT_MAPPED)
  903. {
  904. err.MsgWrite(ErrW,DCT_MSG_PROPERTIES_NOT_MAPPED, acct->GetType());
  905. hr = S_OK;
  906. }
  907. }
  908. else
  909. hr = S_OK;
  910. if ( FAILED( hr ) )
  911. {
  912. err.SysMsgWrite(ErrE, hr, DCT_MSG_PROPERTY_MAPPING_FAILED_SD, (WCHAR*)currentType, hr);
  913. Mark(L"errors", currentType);
  914. // No properties should be set if mapping fails
  915. pVarset->Clear();
  916. }
  917. }
  918. // We update the properties if the object was created or it already existed and the replce flag is set.
  919. BOOL bExists = FALSE;
  920. if (HRESULT_CODE(acct->GetHr()) == ERROR_OBJECT_ALREADY_EXISTS)
  921. bExists = TRUE;
  922. if ( ((SUCCEEDED(acct->GetHr()) && (!bExists)) || ((bExists) && (pOptions->flags & F_REPLACE))) )
  923. {
  924. WCHAR mesg[LEN_Path];
  925. wsprintf(mesg, GET_STRING(IDS_UPDATING_PROPS_S), acct->GetName());
  926. if ( progress )
  927. progress(mesg);
  928. // Create the AccountList object and update the list variable
  929. if ( !pOptions->nochange )
  930. {
  931. _bstr_t sExcList;
  932. if (pOptions->bExcludeProps)
  933. {
  934. if (!_wcsicmp(acct->GetType(), s_ClassUser))
  935. sExcList = pOptions->sExcUserProps;
  936. else if (!_wcsicmp(acct->GetType(), s_ClassInetOrgPerson))
  937. sExcList = pOptions->sExcInetOrgPersonProps;
  938. else if (!_wcsicmp(acct->GetType(), L"group"))
  939. sExcList = pOptions->sExcGroupProps;
  940. else if (!_wcsicmp(acct->GetType(), L"computer"))
  941. sExcList = pOptions->sExcCmpProps;
  942. }
  943. //
  944. // If asterisk character is not specified then copy properties using specified
  945. // exclusion list otherwise exclude all properties by not copying any properties.
  946. //
  947. if ((sExcList.length() == 0) || (IsStringInDelimitedString(sExcList, L"*", L',') == FALSE))
  948. {
  949. //exclude user's profile path if translate roaming profiles and sIDHistory
  950. //are both not selected
  951. if (((!_wcsicmp(acct->GetType(), s_ClassUser) || !_wcsicmp(acct->GetType(), s_ClassInetOrgPerson))) &&
  952. (!(pOptions->flags & F_TranslateProfiles)) && (!(pOptions->flags & F_AddSidHistory)))
  953. {
  954. //if already excluding properties, just add profiles to the list
  955. if (pOptions->bExcludeProps)
  956. {
  957. //if we already have a list add a , to the end
  958. if (sExcList.length())
  959. sExcList += L",";
  960. //add the profile path to the exclude list
  961. sExcList += L"profilePath";
  962. }
  963. else //else turn on the flag and add just profile path to the exclude list
  964. {
  965. //set the flag to indicate we want to exclude something
  966. pOptions->bExcludeProps = TRUE;
  967. //add the profile path only to the exclude list
  968. sExcList = L"profilePath";
  969. }
  970. }//end if to exclude profile path
  971. // add system exclude attributes
  972. if (pOptions->sExcSystemProps.length())
  973. {
  974. if (sExcList.length())
  975. {
  976. sExcList += L",";
  977. }
  978. sExcList += pOptions->sExcSystemProps;
  979. pOptions->bExcludeProps = TRUE;
  980. }
  981. if ( bWin2k )
  982. {
  983. //if ask to, exclude any properties desired by the user and create a new varset
  984. if (pOptions->bExcludeProps)
  985. {
  986. IVarSetPtr pVarsetTemp(__uuidof(VarSet));
  987. IUnknown * pUnkTemp;
  988. hr = pVarsetTemp->QueryInterface(IID_IUnknown, (void**)&pUnkTemp);
  989. if (SUCCEEDED(hr))
  990. {
  991. hr = pObjProp->raw_ExcludeProperties(sExcList, pUnk, &pUnkTemp);
  992. }
  993. //
  994. // Only copy properties if exclusion list was successfully created.
  995. // This prevents possibly updating attributes that should not be
  996. // updated. For example, if some attributes used by Exchange were
  997. // updated this could break Exchange functionality.
  998. //
  999. if (SUCCEEDED(hr))
  1000. {
  1001. // Call the win 2k code to copy all but excluded props
  1002. hr = pObjProp->raw_CopyProperties(
  1003. const_cast<WCHAR*>(acct->GetSourcePath()),
  1004. pOptions->srcDomain,
  1005. const_cast<WCHAR*>(acct->GetTargetPath()),
  1006. pOptions->tgtDomain,
  1007. pUnkTemp,
  1008. pOptions->pDb,
  1009. IUnknownPtr(spSourceToTargetDnMap)
  1010. );
  1011. }
  1012. pUnkTemp->Release();
  1013. }//end if asked to exclude
  1014. else
  1015. {
  1016. // Call the win 2k code to copy all props
  1017. hr = pObjProp->raw_CopyProperties(
  1018. const_cast<WCHAR*>(acct->GetSourcePath()),
  1019. pOptions->srcDomain,
  1020. const_cast<WCHAR*>(acct->GetTargetPath()),
  1021. pOptions->tgtDomain,
  1022. pUnk,
  1023. pOptions->pDb,
  1024. IUnknownPtr(spSourceToTargetDnMap)
  1025. );
  1026. }
  1027. }
  1028. else
  1029. {
  1030. // Otherwise let the Net APIs do their thing.
  1031. hr = pObjProp->raw_CopyNT4Props(const_cast<WCHAR*>(acct->GetSourceSam()),
  1032. const_cast<WCHAR*>(acct->GetTargetSam()),
  1033. pOptions->srcComp, pOptions->tgtComp,
  1034. const_cast<WCHAR*>(acct->GetType()),
  1035. acct->GetGroupType(),
  1036. sExcList);
  1037. }
  1038. }
  1039. }
  1040. else
  1041. // we are going to assume that copy properties would work
  1042. hr = S_OK;
  1043. if ( FAILED(hr) )
  1044. {
  1045. if ( (acct->GetStatus() & AR_Status_Special) )
  1046. {
  1047. err.MsgWrite(ErrE,DCT_MSG_FAILED_TO_REPLACE_SPECIAL_ACCT_S,acct->GetTargetSam());
  1048. }
  1049. else
  1050. {
  1051. err.SysMsgWrite(ErrE, HRESULT_CODE(hr), DCT_MSG_COPY_PROPS_FAILED_SD, acct->GetTargetName(), hr);
  1052. }
  1053. acct->MarkError();
  1054. Mark(L"errors", acct->GetType());
  1055. }
  1056. else
  1057. {
  1058. if (HRESULT_CODE(acct->GetHr()) == ERROR_OBJECT_ALREADY_EXISTS)
  1059. {
  1060. acct->MarkAlreadyThere();
  1061. acct->MarkReplaced();
  1062. Mark(L"replaced",acct->GetType());
  1063. err.MsgWrite(0, DCT_MSG_ACCOUNT_REPLACED_S, acct->GetTargetName());
  1064. }
  1065. }
  1066. }
  1067. }
  1068. // do we need to call extensions. Only if Extension flag is set and the object is copied.
  1069. if ((!pOptions->nochange) && (acct->CallExt()) && (acct->WasCreated() || acct->WasReplaced()))
  1070. {
  1071. // Let the Extension objects do their thing.
  1072. WCHAR mesg[LEN_Path];
  1073. wsprintf(mesg,GET_STRING(IDS_RUNNING_EXTS_S), acct->GetName());
  1074. if ( progress )
  1075. progress(mesg);
  1076. // Close the log file if it is open
  1077. WCHAR filename[LEN_Path];
  1078. err.LogClose();
  1079. if (m_pExt)
  1080. hr = m_pExt->Process(acct, pOptions->tgtDomain, pOptions,FALSE);
  1081. safecopy (filename,opt.logFile);
  1082. err.LogOpen(filename,1 /*append*/);
  1083. }
  1084. // only do these updates for account's we're copying
  1085. // and only do updates if the account was actually created
  1086. // .. or if the account was replaced,
  1087. // or if we intentionally didn't replace the account (as in the group merge case)
  1088. if ( acct->CreateAccount()
  1089. && ( acct->WasCreated()
  1090. || ( acct->WasReplaced()
  1091. || !acct->CopyProps()
  1092. )
  1093. )
  1094. )
  1095. {
  1096. WCHAR mesg[LEN_Path];
  1097. wsprintf(mesg, GET_STRING(IDS_TRANSLATE_ROAMING_PROFILE_S), acct->GetName());
  1098. if ( progress )
  1099. progress(mesg);
  1100. //Set the new profile if needed
  1101. if ( pOptions->flags & F_TranslateProfiles && ((_wcsicmp(acct->GetType(), s_ClassUser) == 0) || (_wcsicmp(acct->GetType(), s_ClassInetOrgPerson) == 0)))
  1102. {
  1103. WCHAR tgtProfilePath[MAX_PATH];
  1104. GetBkupRstrPriv((WCHAR*)NULL);
  1105. GetPrivilege((WCHAR*)NULL,SE_SECURITY_NAME);
  1106. if ( wcslen(acct->GetSourceProfile()) > 0 )
  1107. {
  1108. DWORD ret = TranslateRemoteProfile( acct->GetSourceProfile(),
  1109. tgtProfilePath,
  1110. acct->GetSourceSam(),
  1111. acct->GetTargetSam(),
  1112. pOptions->srcDomain,
  1113. pOptions->tgtDomain,
  1114. pOptions->pDb,
  1115. pOptions->lActionID,
  1116. NULL,
  1117. pOptions->nochange);
  1118. if ( !ret )
  1119. {
  1120. WCHAR tgtuser[LEN_Path];
  1121. USER_INFO_3 * tgtinfo;
  1122. DWORD nParmErr;
  1123. wcscpy(tgtuser, acct->GetTargetSam());
  1124. // Get information for the target account
  1125. long rc = NetUserGetInfo(const_cast<WCHAR *>(pOptions->tgtComp),
  1126. tgtuser,
  1127. 3,
  1128. (LPBYTE *) &tgtinfo);
  1129. if (!pOptions->nochange)
  1130. {
  1131. // Set the new profile path
  1132. tgtinfo->usri3_profile = tgtProfilePath;
  1133. // Set the information back for the account.
  1134. rc = NetUserSetInfo(const_cast<WCHAR *>(pOptions->tgtComp),
  1135. tgtuser,
  1136. 3,
  1137. (LPBYTE)tgtinfo,
  1138. &nParmErr);
  1139. NetApiBufferFree((LPVOID) tgtinfo);
  1140. if (rc)
  1141. {
  1142. err.MsgWrite(ErrE, DCT_MSG_SETINFO_FAIL_SD, tgtuser, rc);
  1143. Mark(L"errors", acct->GetType());
  1144. }
  1145. }
  1146. }
  1147. }
  1148. }
  1149. if ( acct->WasReplaced() )
  1150. {
  1151. // Do we need to add sid history
  1152. if ( pOptions->flags & F_AddSidHistory )
  1153. {
  1154. WCHAR mesg[LEN_Path];
  1155. wsprintf(mesg, GET_STRING(IDS_ADDING_SIDHISTORY_S), acct->GetName());
  1156. if ( progress )
  1157. progress(mesg);
  1158. // Global flag tells us if we should try the AddSidHistory because
  1159. // for some special cases if it does not work once it will not work
  1160. // see the AddSidHistory function for more details.
  1161. if ( g_bAddSidWorks )
  1162. {
  1163. if (! AddSidHistory( pOptions, acct->GetSourceSam(), acct->GetTargetSam(), pStatus ) )
  1164. {
  1165. Mark(L"errors", acct->GetType());
  1166. }
  1167. // CopySidHistoryProperty(pOptions, acct, pStatus);
  1168. }
  1169. }
  1170. }
  1171. wsprintf(mesg, L"", acct->GetName());
  1172. if ( progress )
  1173. progress(mesg);
  1174. }
  1175. }
  1176. // Cleanup
  1177. pUnk->Release();
  1178. tenum.Close();
  1179. return 0;
  1180. }
  1181. void CAcctRepl::LoadOptionsFromVarSet(IVarSet * pVarSet)
  1182. {
  1183. _bstr_t text;
  1184. DWORD rc;
  1185. MCSDCTWORKEROBJECTSLib::IAccessCheckerPtr pAccess(__uuidof(MCSDCTWORKEROBJECTSLib::AccessChecker));
  1186. //store the name of the wizard being run
  1187. opt.sWizard = pVarSet->get(GET_BSTR(DCTVS_Options_Wizard));
  1188. //
  1189. // If group mapping and merging is the task then allow distinguished
  1190. // name conflicts as the main reason for this task is to merge
  1191. // several source groups into one target group.
  1192. //
  1193. if (opt.sWizard.length() && (_wcsicmp((wchar_t*)opt.sWizard, L"groupmapping") == 0))
  1194. {
  1195. m_bIgnorePathConflict = true;
  1196. }
  1197. // Read General Options
  1198. // open log file first, so we'll be sure to get any errors!
  1199. text = pVarSet->get(GET_BSTR(DCTVS_Options_Logfile));
  1200. safecopy(opt.logFile,(WCHAR*)text);
  1201. WCHAR filename[MAX_PATH];
  1202. safecopy (filename,opt.logFile);
  1203. err.LogOpen(filename,1 /*append*/);
  1204. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_SidHistoryCredentials_Domain));
  1205. safecopy(opt.authDomain ,(WCHAR*)text);
  1206. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_SidHistoryCredentials_UserName));
  1207. safecopy(opt.authUser ,(WCHAR*)text);
  1208. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_SidHistoryCredentials_Password));
  1209. safecopy(opt.authPassword,(WCHAR*)text);
  1210. text = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain));
  1211. safecopy(opt.srcDomain,(WCHAR*)text);
  1212. text = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain));
  1213. safecopy(opt.tgtDomain,(WCHAR*)text);
  1214. text = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomainDns));
  1215. safecopy(opt.srcDomainDns,(WCHAR*)text);
  1216. text = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomainDns));
  1217. safecopy(opt.tgtDomainDns,(WCHAR*)text);
  1218. text = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomainFlat));
  1219. safecopy(opt.srcDomainFlat,(WCHAR*)text);
  1220. text = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomainFlat));
  1221. safecopy(opt.tgtDomainFlat,(WCHAR*)text);
  1222. text = pVarSet->get(GET_BSTR(DCTVS_Options_SourceServer));
  1223. safecopy(opt.srcComp, (WCHAR*)text);
  1224. text = pVarSet->get(GET_BSTR(DCTVS_Options_SourceServerDns));
  1225. safecopy(opt.srcCompDns, (WCHAR*)text);
  1226. text = pVarSet->get(GET_BSTR(DCTVS_Options_SourceServerFlat));
  1227. safecopy(opt.srcCompFlat, (WCHAR*)text);
  1228. text = pVarSet->get(GET_BSTR(DCTVS_Options_TargetServer));
  1229. safecopy(opt.tgtComp, (WCHAR*)text);
  1230. text = pVarSet->get(GET_BSTR(DCTVS_Options_TargetServerDns));
  1231. safecopy(opt.tgtCompDns, (WCHAR*)text);
  1232. text = pVarSet->get(GET_BSTR(DCTVS_Options_TargetServerFlat));
  1233. safecopy(opt.tgtCompFlat, (WCHAR*)text);
  1234. text = pVarSet->get(GET_BSTR(DCTVS_Options_NoChange));
  1235. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1236. {
  1237. opt.nochange = TRUE;
  1238. }
  1239. else
  1240. {
  1241. opt.nochange = FALSE;
  1242. }
  1243. // Read Account Replicator Options
  1244. // initialize
  1245. safecopy(opt.prefix, L"");
  1246. safecopy(opt.suffix, L"");
  1247. safecopy(opt.globalPrefix, L"");
  1248. safecopy(opt.globalSuffix, L"");
  1249. DWORD flags = 0;
  1250. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_ReplaceExistingAccounts));
  1251. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1252. {
  1253. flags |= F_REPLACE;
  1254. }
  1255. else
  1256. {
  1257. // Prefix/Suffix only apply if the Replace flag is not set.
  1258. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_Prefix));
  1259. safecopy(opt.prefix,(WCHAR*)text);
  1260. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_Suffix));
  1261. safecopy(opt.suffix,(WCHAR*)text);
  1262. }
  1263. // Global flags apply no matter what
  1264. text = pVarSet->get(GET_BSTR(DCTVS_Options_Prefix));
  1265. safecopy(opt.globalPrefix,(WCHAR*)text);
  1266. text = pVarSet->get(GET_BSTR(DCTVS_Options_Suffix));
  1267. safecopy(opt.globalSuffix,(WCHAR*)text);
  1268. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_CopyContainerContents));
  1269. if ( text == _bstr_t(GET_BSTR(IDS_YES)) )
  1270. opt.expandContainers = TRUE;
  1271. else
  1272. opt.expandContainers = FALSE;
  1273. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_CopyMemberOf));
  1274. if ( text == _bstr_t(GET_BSTR(IDS_YES)) )
  1275. opt.expandMemberOf = TRUE;
  1276. else
  1277. opt.expandMemberOf = FALSE;
  1278. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_FixMembership));
  1279. if ( text == _bstr_t(GET_BSTR(IDS_YES)) )
  1280. opt.fixMembership = TRUE;
  1281. else
  1282. opt.fixMembership = FALSE;
  1283. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_AddToGroup));
  1284. safecopy(opt.addToGroup,(WCHAR*)text);
  1285. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_AddToGroupOnSourceDomain));
  1286. safecopy(opt.addToGroupSource,(WCHAR*)text);
  1287. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_TranslateRoamingProfiles));
  1288. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1289. flags |= F_TranslateProfiles;
  1290. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_CopyUsers));
  1291. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1292. flags |= F_USERS;
  1293. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_CopyGlobalGroups));
  1294. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1295. flags |= F_GROUP;
  1296. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_CopyComputers));
  1297. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1298. flags |= F_COMPUTERS;
  1299. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_CopyOUs));
  1300. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1301. flags |= F_OUS;
  1302. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_CopyContainerContents));
  1303. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1304. flags |= F_COPY_CONT_CONTENT;
  1305. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_IncludeMigratedAccts));
  1306. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1307. flags |= F_COPY_MIGRATED_ACCT;
  1308. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_CopyLocalGroups));
  1309. if (! UStrICmp(text,GET_STRING(IDS_YES)) )
  1310. flags |= F_LGROUP;
  1311. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_DisableCopiedAccounts));
  1312. if (! UStrICmp(text,GET_STRING(IDS_All)) )
  1313. flags |= F_DISABLE_ALL;
  1314. else if (! UStrICmp(text,GET_STRING(IDS_Special)) )
  1315. flags |= F_DISABLE_SPECIAL;
  1316. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_DisableSourceAccounts));
  1317. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1318. flags |= F_DISABLESOURCE;
  1319. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_GenerateStrongPasswords));
  1320. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1321. flags |= F_STRONGPW_ALL;
  1322. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_PasswordFile));
  1323. if ( text.length() )
  1324. {
  1325. // don't need this anymore, since it is handled by a plug-in
  1326. // opt.passwordLog.LogOpen(text,TRUE);
  1327. }
  1328. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_UpdateUserRights));
  1329. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1330. {
  1331. m_UpdateUserRights = TRUE;
  1332. }
  1333. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_ReplaceExistingGroupMembers));
  1334. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1335. flags |= F_REMOVE_OLD_MEMBERS;
  1336. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_RemoveExistingUserRights));
  1337. if ( ! UStrICmp(text,GET_STRING(IDS_YES)) )
  1338. flags |= F_RevokeOldRights;
  1339. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_MoveReplacedAccounts));
  1340. if ( ! UStrICmp(text,GET_STRING(IDS_YES)) )
  1341. flags |= F_MOVE_REPLACED_ACCT;
  1342. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_CopyComputers));
  1343. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1344. {
  1345. flags |= F_MACHINE;
  1346. }
  1347. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_AddSidHistory));
  1348. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1349. {
  1350. flags |= F_AddSidHistory;
  1351. }
  1352. opt.flags = flags;
  1353. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_RenameOnly));
  1354. if (! UStrICmp(text,GET_STRING(IDS_YES)) )
  1355. {
  1356. m_RenameOnly = TRUE;
  1357. }
  1358. text = pVarSet->get(GET_BSTR(DCTVS_Options_Undo));
  1359. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1360. {
  1361. // this is an undo operation
  1362. opt.bUndo = TRUE;
  1363. }
  1364. else
  1365. {
  1366. opt.bUndo = FALSE;
  1367. }
  1368. // What undo action are we performing.
  1369. if ( opt.bUndo )
  1370. {
  1371. _variant_t var = pVarSet->get(L"UndoAction");
  1372. if (var.vt == VT_I4)
  1373. opt.lUndoActionID = var.lVal;
  1374. else
  1375. opt.lUndoActionID = -2;
  1376. }
  1377. else
  1378. {
  1379. _variant_t var = pVarSet->get(L"ActionID");
  1380. if (var.vt == VT_I4)
  1381. opt.lActionID = var.lVal;
  1382. else
  1383. opt.lActionID = -1;
  1384. }
  1385. // Read the password policy from the varset
  1386. // We used to get the strong password policy from the target EA Server, so we can generate strong passwords
  1387. // that meet the policy.
  1388. // we don't do that anymore, since we have removed all depenedencies on EA.
  1389. LONG len = 10;
  1390. // set the password settings to default values
  1391. opt.policyInfo.bEnforce = TRUE;
  1392. opt.policyInfo.maxConsecutiveAlpha = (LONG)pVarSet->get(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MaxConsecutiveAlpha));
  1393. opt.policyInfo.minDigits = (LONG)pVarSet->get(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MinDigit));
  1394. opt.policyInfo.minLower = (LONG)pVarSet->get(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MinLower));
  1395. opt.policyInfo.minUpper = (LONG)pVarSet->get(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MinUpper));
  1396. opt.policyInfo.minSpecial = (LONG)pVarSet->get(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MinSpecial));
  1397. HRESULT hrAccess = pAccess->raw_GetPasswordPolicy(opt.tgtDomain,&len);
  1398. if ( SUCCEEDED(hrAccess) )
  1399. {
  1400. opt.minPwdLength = len;
  1401. pVarSet->put(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MinLength),len);
  1402. }
  1403. WriteOptionsToLog();
  1404. // Build List of Accounts to Copy
  1405. // Clear the account list first though
  1406. TNodeListEnum e;
  1407. TAcctReplNode * acct;
  1408. for ( acct = (TAcctReplNode *)e.OpenFirst(&acctList) ; acct ; acct = (TAcctReplNode *)e.Next() )
  1409. {
  1410. acctList.Remove((TNode*)acct);
  1411. }
  1412. BothWin2K(&opt);
  1413. // See if a global operation mask specified.
  1414. _variant_t vdwOpMask = pVarSet->get(GET_BSTR(DCTVS_Options_GlobalOperationMask));
  1415. if ( vdwOpMask.vt == VT_I4 )
  1416. g_dwOpMask = (DWORD)vdwOpMask.lVal;
  1417. // Then build the new list expect a list of accounts to copy in the VarSet
  1418. if ( ! opt.bUndo )
  1419. {
  1420. rc = PopulateAccountListFromVarSet(pVarSet);
  1421. if ( rc )
  1422. {
  1423. _com_issue_error(HRESULT_FROM_WIN32(rc));
  1424. }
  1425. }
  1426. // If we have an NT5 source domain then we need to fillin the path info
  1427. DWORD maj, min, sp;
  1428. HRESULT hr = pAccess->raw_GetOsVersion(opt.srcComp, &maj, &min, &sp);
  1429. if (SUCCEEDED(hr))
  1430. {
  1431. // Ask the auxiliarry function to fill in the the Path for the source object if the AcctNode is not filled
  1432. for ( acct = (TAcctReplNode *)e.OpenFirst(&acctList) ; acct ; acct = (TAcctReplNode *)e.Next() )
  1433. {
  1434. if ((!acct->IsFilled) && (maj > 4))
  1435. {
  1436. FillPathInfo(acct, &opt);
  1437. AddPrefixSuffix(acct, &opt);
  1438. }
  1439. else if ((maj == 4) && (!_wcsicmp(acct->GetType(),L"computer")))
  1440. FillPathInfo(acct, &opt);
  1441. }
  1442. }
  1443. // Check for incompatible options!
  1444. if ( (flags & F_RevokeOldRights) && !m_UpdateUserRights )
  1445. {
  1446. err.MsgWrite(ErrW,DCT_MSG_RIGHTS_INCOMPATIBLE_FLAGS);
  1447. Mark(L"warnings", "generic");
  1448. }
  1449. text = pVarSet->get(GET_BSTR(DCTVS_Options_OuPath));
  1450. if ( text.length() )
  1451. {
  1452. wcscpy(opt.tgtOUPath, text);
  1453. }
  1454. // intialize the system exclude attributes option
  1455. // if not in passed in VarSet then retrieve from database
  1456. _variant_t vntSystemExclude = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_ExcludedSystemProps));
  1457. if (V_VT(&vntSystemExclude) == VT_EMPTY)
  1458. {
  1459. IVarSetPtr spVarSet(__uuidof(VarSet));
  1460. IUnknownPtr spUnknown(spVarSet);
  1461. IUnknown* punk = spUnknown;
  1462. opt.pDb->GetSettings(&punk);
  1463. vntSystemExclude = spVarSet->get(GET_BSTR(DCTVS_AccountOptions_ExcludedSystemProps));
  1464. }
  1465. opt.sExcSystemProps = vntSystemExclude;
  1466. //store the object property exclusion lists in the options structure
  1467. opt.sExcUserProps = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_ExcludedUserProps));
  1468. opt.sExcInetOrgPersonProps = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_ExcludedInetOrgPersonProps));
  1469. opt.sExcGroupProps = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_ExcludedGroupProps));
  1470. opt.sExcCmpProps = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_ExcludedComputerProps));
  1471. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_ExcludeProps));
  1472. if ( !UStrICmp(text,GET_STRING(IDS_YES)) )
  1473. opt.bExcludeProps = TRUE;
  1474. else
  1475. opt.bExcludeProps = FALSE;
  1476. }
  1477. DWORD
  1478. CAcctRepl::PopulateAccountListFromVarSet(
  1479. IVarSet * pVarSet // in - varset containing account list
  1480. )
  1481. {
  1482. _bstr_t val;
  1483. long numAccounts;
  1484. _bstr_t text;
  1485. DWORD maj, min, sp;
  1486. PSID pSrcSid = NULL;
  1487. WCHAR txtSid[200] = L"";
  1488. DWORD lenTxt = DIM(txtSid);
  1489. MCSDCTWORKEROBJECTSLib::IAccessCheckerPtr pAccess(__uuidof(MCSDCTWORKEROBJECTSLib::AccessChecker));
  1490. numAccounts = pVarSet->get(GET_BSTR(DCTVS_Accounts_NumItems));
  1491. // Set up the account list functionality
  1492. acctList.CompareSet(&TNodeCompareNameOnly);
  1493. if ( acctList.IsTree() ) acctList.ToSorted();
  1494. //get the source domain's Sid so we can store it as part of the node
  1495. _bstr_t source = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain));
  1496. GetSidForDomain((WCHAR*)source,&pSrcSid);
  1497. for ( int i = 0 ; i < numAccounts ; i++ )
  1498. {
  1499. WCHAR key[LEN_Path];
  1500. UCHAR acctName[LEN_Account];
  1501. TAcctReplNode * curr = new TAcctReplNode;
  1502. if (!curr)
  1503. return ERROR_NOT_ENOUGH_MEMORY;
  1504. if ( opt.pStatus )
  1505. {
  1506. LONG status = 0;
  1507. HRESULT hr = opt.pStatus->get_Status(&status);
  1508. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  1509. {
  1510. if ( !bAbortMessageWritten )
  1511. {
  1512. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  1513. bAbortMessageWritten = true;
  1514. }
  1515. break;
  1516. }
  1517. }
  1518. // The object type must be specified
  1519. swprintf(key,GET_STRING(DCTVSFmt_Accounts_Type_D),i);
  1520. val = pVarSet->get(key);
  1521. curr->SetType(val);
  1522. swprintf(key,GET_STRING(DCTVSFmt_Accounts_D),i);
  1523. text = pVarSet->get(key);
  1524. if ( ! text.length() )
  1525. {
  1526. // oops, no name specified
  1527. // skip this entry and try the next one
  1528. err.MsgWrite(ErrW,DCT_MSG_NO_NAME_IN_VARSET_S,key);
  1529. Mark(L"warnings",L"generic");
  1530. delete curr;
  1531. continue;
  1532. }
  1533. //set the source domain's sid
  1534. curr->SetSourceSid(pSrcSid);
  1535. // Set the operation to the global mask then check if we need to overwrite with the individual setting.
  1536. curr->operations = g_dwOpMask;
  1537. swprintf(key, GET_STRING(DCTVS_Accounts_D_OperationMask), i);
  1538. _variant_t vOpMask = pVarSet->get(key);
  1539. if ( vOpMask.vt == VT_I4 )
  1540. curr->operations = (DWORD)vOpMask.lVal;
  1541. // Get the rest of the info from the VarSet.
  1542. if ( ( (text.length() > 7 ) && (_wcsnicmp((WCHAR*) text, L"LDAP://",UStrLen(L"LDAP://")) == 0) )
  1543. || ( (text.length() > 8 ) && (_wcsnicmp((WCHAR*)text, L"WinNT://",UStrLen(L"WinNT://")) == 0)) )
  1544. {
  1545. //hmmmm... They are giving use ADsPath. Lets get all the info we can from the object then.
  1546. curr->SetSourcePath((WCHAR*) text);
  1547. HRESULT hr = FillNodeFromPath(curr, &opt, &acctList);
  1548. if (SUCCEEDED(hr))
  1549. {
  1550. // Get the target name if one is specified.
  1551. swprintf(key,GET_STRING(DCTVSFmt_Accounts_TargetName_D),i);
  1552. text = pVarSet->get(key);
  1553. if ( text.length() )
  1554. {
  1555. // if target name is specified then use that.
  1556. curr->SetTargetName((WCHAR*) text);
  1557. curr->SetTargetSam((WCHAR*) text);
  1558. }
  1559. curr->IsFilled = true;
  1560. }
  1561. }
  1562. else
  1563. {
  1564. FillNamingContext(&opt);
  1565. // if this is a computer account, make sure the trailing $ is included in the name
  1566. curr->SetName(text);
  1567. curr->SetTargetName(text);
  1568. if ( !UStrICmp(val,L"computer") )
  1569. {
  1570. // if ( ((WCHAR*)text)[text.length() - 1] != L'$' ) //comment out to fix 89513.
  1571. text += L"$";
  1572. }
  1573. curr->SetSourceSam(text);
  1574. curr->SetTargetSam(text);
  1575. safecopy(acctName,(WCHAR*)text);
  1576. // optional target name
  1577. swprintf(key,GET_STRING(DCTVSFmt_Accounts_TargetName_D),i);
  1578. text = pVarSet->get(key);
  1579. if ( text.length() )
  1580. curr->SetTargetName(text);
  1581. // HRESULT hr = pAccess->raw_GetOsVersion(opt.srcComp, &maj, &min, &sp);
  1582. pAccess->raw_GetOsVersion(opt.srcComp, &maj, &min, &sp);
  1583. if ( maj < 5 )
  1584. AddPrefixSuffix(curr,&opt);
  1585. // if this is a computer account, make sure the trailing $ is included in the name
  1586. if ( !UStrICmp(val,L"computer") )
  1587. {
  1588. if ( text.length() && ((WCHAR*)text)[text.length() - 1] != L'$' )
  1589. text += L"$";
  1590. }
  1591. if ( text.length() )
  1592. {
  1593. if ( ((WCHAR*)text)[text.length() - 1] != L'$' )
  1594. text += L"$";
  1595. curr->SetTargetSam(text);
  1596. }
  1597. curr->IsFilled = false;
  1598. }
  1599. if ( _wcsicmp(val, L"") != 0 )
  1600. {
  1601. acctList.InsertBottom((TNode*)curr);
  1602. }
  1603. else
  1604. {
  1605. err.MsgWrite(ErrW,DCT_MSG_BAD_ACCOUNT_TYPE_SD,curr->GetName(),val);
  1606. Mark(L"warnings",L"generic");
  1607. delete curr;
  1608. }
  1609. }
  1610. return 0;
  1611. }
  1612. HRESULT
  1613. CAcctRepl::UpdateUserRights(
  1614. IStatusObj * pStatus // in - status object
  1615. )
  1616. {
  1617. HRESULT hr = S_OK;
  1618. if (!opt.bSameForest && !opt.bUndo)
  1619. {
  1620. hr = m_pUserRights->OpenSourceServer(_bstr_t(opt.srcComp));
  1621. if (SUCCEEDED(hr))
  1622. {
  1623. hr = m_pUserRights->OpenTargetServer(_bstr_t(opt.tgtComp));
  1624. }
  1625. if (SUCCEEDED(hr))
  1626. {
  1627. m_pUserRights->put_RemoveOldRightsFromTargetAccounts((opt.flags & F_RevokeOldRights) != 0);
  1628. if (acctList.IsTree())
  1629. {
  1630. acctList.ToSorted();
  1631. }
  1632. TNodeListEnum e;
  1633. for (TAcctReplNode* acct = (TAcctReplNode*)e.OpenFirst(&acctList) ; acct; acct = (TAcctReplNode *)e.Next())
  1634. {
  1635. if ( pStatus )
  1636. {
  1637. LONG status = 0;
  1638. HRESULT hr = pStatus->get_Status(&status);
  1639. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  1640. {
  1641. if ( !bAbortMessageWritten )
  1642. {
  1643. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  1644. bAbortMessageWritten = true;
  1645. }
  1646. break;
  1647. }
  1648. }
  1649. if (_wcsicmp(acct->GetType(), L"computer") != 0) // only update rights for users and groups, not computer accounts
  1650. {
  1651. // if the account wasn't created or replaced, don't bother
  1652. if (acct->GetStatus() & (AR_Status_Created | AR_Status_Replaced))
  1653. {
  1654. if ( acct->GetSourceRid() && acct->GetTargetRid() )
  1655. {
  1656. _bstr_t strSourceSid = GenerateSidAsString(&opt, FALSE, acct->GetSourceRid());
  1657. _bstr_t strTargetSid = GenerateSidAsString(&opt, TRUE, acct->GetTargetRid());
  1658. hr = m_pUserRights->CopyUserRightsWithSids(
  1659. _bstr_t(acct->GetSourceSam()),
  1660. strSourceSid,
  1661. _bstr_t(acct->GetTargetSam()),
  1662. strTargetSid
  1663. );
  1664. }
  1665. else
  1666. {
  1667. hr = m_pUserRights->CopyUserRights(
  1668. _bstr_t(acct->GetSourceSam()),
  1669. _bstr_t(acct->GetTargetSam())
  1670. );
  1671. }
  1672. if (SUCCEEDED(hr))
  1673. {
  1674. err.MsgWrite(0, DCT_MSG_UPDATED_RIGHTS_S, acct->GetTargetName() );
  1675. acct->MarkRightsUpdated();
  1676. }
  1677. else
  1678. {
  1679. err.SysMsgWrite(ErrE, hr, DCT_MSG_UPDATE_RIGHTS_FAILED_SD, acct->GetTargetName(), hr);
  1680. acct->MarkError();
  1681. Mark(L"errors", acct->GetType());
  1682. }
  1683. }
  1684. }
  1685. }
  1686. e.Close();
  1687. }
  1688. Progress(L"");
  1689. }
  1690. return hr;
  1691. }
  1692. //---------------------------------------------------------------------------
  1693. // EnumerateAccountRights Method
  1694. //
  1695. // Synopsis
  1696. // Enumerate account rights for specified account. The rights are stored in
  1697. // the account node object.
  1698. //
  1699. // Arguments
  1700. // IN bTarget - specifies whether to use the target or source account
  1701. // IN pAcct - a pointer to an account node object
  1702. //
  1703. // Return
  1704. // Returns an HRESULT where S_OK indicates success anything else an error.
  1705. //---------------------------------------------------------------------------
  1706. HRESULT CAcctRepl::EnumerateAccountRights(BOOL bTarget, TAcctReplNode* pAcct)
  1707. {
  1708. HRESULT hr;
  1709. //
  1710. // Retrieve the target or source server name and the target or source
  1711. // account RID and generate the account SID as a string.
  1712. //
  1713. _bstr_t strServer = bTarget ? opt.tgtComp : opt.srcComp;
  1714. DWORD dwRid = bTarget ? pAcct->GetTargetRid() : pAcct->GetSourceRid();
  1715. _bstr_t strSid = GenerateSidAsString(&opt, bTarget, dwRid);
  1716. if ((PCWSTR)strServer && (PCWSTR)strSid)
  1717. {
  1718. //
  1719. // If array of user rights currently exists
  1720. // in account node object then destroy array.
  1721. //
  1722. if (pAcct->psaUserRights)
  1723. {
  1724. SafeArrayDestroy(pAcct->psaUserRights);
  1725. pAcct->psaUserRights = NULL;
  1726. }
  1727. //
  1728. // Retrieve array of user rights and save them in account node object.
  1729. //
  1730. hr = m_pUserRights->GetRightsOfUser(strServer, strSid, &pAcct->psaUserRights);
  1731. }
  1732. else
  1733. {
  1734. hr = E_OUTOFMEMORY;
  1735. }
  1736. return hr;
  1737. }
  1738. //---------------------------------------------------------------------------
  1739. // AddAccountRights Method
  1740. //
  1741. // Synopsis
  1742. // Add account rights to specified account.
  1743. //
  1744. // Arguments
  1745. // IN bTarget - specifies whether to use the target or source account
  1746. // IN pAcct - a pointer to an account node object
  1747. //
  1748. // Return
  1749. // Returns an HRESULT where S_OK indicates success anything else an error.
  1750. //---------------------------------------------------------------------------
  1751. HRESULT CAcctRepl::AddAccountRights(BOOL bTarget, TAcctReplNode* pAcct)
  1752. {
  1753. HRESULT hr = S_OK;
  1754. if (pAcct->psaUserRights)
  1755. {
  1756. //
  1757. // Retrieve the target or source server name, the target or source account name
  1758. // and the target or source account RID and generate the account SID as a string.
  1759. //
  1760. _bstr_t strServer = bTarget ? opt.tgtComp : opt.srcComp;
  1761. _bstr_t strName = bTarget ? pAcct->GetTargetName() : pAcct->GetName();
  1762. DWORD dwRid = bTarget ? pAcct->GetTargetRid() : pAcct->GetSourceRid();
  1763. _bstr_t strSid = GenerateSidAsString(&opt, bTarget, dwRid);
  1764. if ((PCWSTR)strServer && (PCWSTR)strSid)
  1765. {
  1766. //
  1767. // Add rights to account.
  1768. //
  1769. hr = m_pUserRights->AddUserRights(strServer, strSid, pAcct->psaUserRights);
  1770. }
  1771. else
  1772. {
  1773. hr = E_OUTOFMEMORY;
  1774. }
  1775. //
  1776. // If rights added successfully then generate messages specifying
  1777. // rights granted and that rights were updated for specified account
  1778. // otherwise generate message stating failure of rights update.
  1779. //
  1780. if (SUCCEEDED(hr))
  1781. {
  1782. SAFEARRAY* psaUserRights = pAcct->psaUserRights;
  1783. BSTR* pbstrRight;
  1784. hr = SafeArrayAccessData(psaUserRights, (void**)&pbstrRight);
  1785. if (SUCCEEDED(hr))
  1786. {
  1787. ULONG ulCount = psaUserRights->rgsabound[0].cElements;
  1788. for (ULONG ulIndex = 0; ulIndex < ulCount; ulIndex++)
  1789. {
  1790. BSTR bstrRight = pbstrRight[ulIndex];
  1791. err.MsgWrite(0, DCT_MSG_RIGHT_GRANTED_SS, bstrRight, (PCWSTR)strName);
  1792. }
  1793. SafeArrayUnaccessData(psaUserRights);
  1794. }
  1795. err.MsgWrite(0, DCT_MSG_UPDATED_RIGHTS_S, (PCWSTR)strName);
  1796. pAcct->MarkRightsUpdated();
  1797. }
  1798. else
  1799. {
  1800. err.SysMsgWrite(ErrE, hr, DCT_MSG_UPDATE_RIGHTS_FAILED_SD, (PCWSTR)strName, hr);
  1801. pAcct->MarkError();
  1802. Mark(L"errors", pAcct->GetType());
  1803. }
  1804. }
  1805. return hr;
  1806. }
  1807. //---------------------------------------------------------------------------
  1808. // RemoveAccountRights Method
  1809. //
  1810. // Synopsis
  1811. // Remove account rights from specified account.
  1812. //
  1813. // Arguments
  1814. // IN bTarget - specifies whether to use the target or source account
  1815. // IN pAcct - a pointer to an account node object
  1816. //
  1817. // Return
  1818. // Returns an HRESULT where S_OK indicates success anything else an error.
  1819. //---------------------------------------------------------------------------
  1820. HRESULT CAcctRepl::RemoveAccountRights(BOOL bTarget, TAcctReplNode* pAcct)
  1821. {
  1822. HRESULT hr = S_OK;
  1823. if (pAcct->psaUserRights)
  1824. {
  1825. //
  1826. // Retrieve the target or source server name, the target or source account name
  1827. // and the target or source account RID and generate the account SID as a string.
  1828. //
  1829. _bstr_t strServer = bTarget ? opt.tgtComp : opt.srcComp;
  1830. DWORD dwRid = bTarget ? pAcct->GetTargetRid() : pAcct->GetSourceRid();
  1831. _bstr_t strSid = GenerateSidAsString(&opt, bTarget, dwRid);
  1832. if ((LPCTSTR)strServer && (LPCTSTR)strSid)
  1833. {
  1834. //
  1835. // Remove rights from account.
  1836. //
  1837. hr = m_pUserRights->RemoveUserRights(strServer, strSid, pAcct->psaUserRights);
  1838. }
  1839. else
  1840. {
  1841. hr = E_OUTOFMEMORY;
  1842. }
  1843. }
  1844. return hr;
  1845. }
  1846. void
  1847. CAcctRepl::WriteOptionsToLog()
  1848. {
  1849. // This will make it easier to tell if arguments are ignored because they
  1850. // were specified in the wrong format, or misspelled, etc.
  1851. WCHAR cmdline[1000];
  1852. UStrCpy(cmdline ,GET_STRING(IDS_AccountMigration));
  1853. if ( opt.nochange )
  1854. {
  1855. UStrCpy(cmdline + UStrLen(cmdline),GET_STRING(IDS_WriteChanges_No));
  1856. }
  1857. UStrCpy(cmdline + UStrLen(cmdline),L" ");
  1858. UStrCpy(cmdline + UStrLen(cmdline),opt.srcDomainFlat);
  1859. UStrCpy(cmdline + UStrLen(cmdline),L" ");
  1860. UStrCpy(cmdline + UStrLen(cmdline),opt.tgtDomainFlat);
  1861. UStrCpy(cmdline + UStrLen(cmdline),L" ");
  1862. if ( opt.flags & F_USERS )
  1863. {
  1864. UStrCpy(cmdline + UStrLen(cmdline),GET_STRING(IDS_CopyUsers_Yes));
  1865. }
  1866. else
  1867. {
  1868. UStrCpy(cmdline + UStrLen(cmdline), GET_STRING(IDS_CopyUsers_No));
  1869. }
  1870. UStrCpy(cmdline + UStrLen(cmdline),L" ");
  1871. if ( opt.flags & F_GROUP )
  1872. {
  1873. UStrCpy(cmdline + UStrLen(cmdline),GET_STRING(IDS_CopyGlobalGroups_Yes));
  1874. }
  1875. else
  1876. {
  1877. UStrCpy(cmdline + UStrLen(cmdline),GET_STRING(IDS_CopyGlobalGroups_No));
  1878. }
  1879. UStrCpy(cmdline + UStrLen(cmdline),L" ");
  1880. if ( opt.flags & F_LGROUP )
  1881. {
  1882. UStrCpy(cmdline + UStrLen(cmdline),GET_STRING(IDS_CopyLocalGroups_Yes));
  1883. }
  1884. else
  1885. {
  1886. UStrCpy(cmdline + UStrLen(cmdline),GET_STRING(IDS_CopyLocalGroups_No));
  1887. }
  1888. UStrCpy(cmdline + UStrLen(cmdline),L" ");
  1889. if ( opt.flags & F_MACHINE )
  1890. {
  1891. UStrCpy(cmdline + UStrLen(cmdline),GET_STRING(IDS_CopyComputers_Yes));
  1892. }
  1893. else
  1894. {
  1895. UStrCpy(cmdline + UStrLen(cmdline),GET_STRING(IDS_CopyComputers_No));
  1896. }
  1897. UStrCpy(cmdline + UStrLen(cmdline),L" ");
  1898. if ( opt.flags & F_REPLACE )
  1899. {
  1900. UStrCpy(cmdline + UStrLen(cmdline),GET_STRING(IDS_ReplaceExisting_Yes));
  1901. UStrCpy(cmdline + UStrLen(cmdline),L" ");
  1902. }
  1903. if ( opt.flags & F_DISABLE_ALL )
  1904. {
  1905. UStrCpy(cmdline + UStrLen(cmdline),GET_STRING(IDS_DisableAll_Yes));
  1906. UStrCpy(cmdline + UStrLen(cmdline),L" ");
  1907. }
  1908. else if ( opt.flags & F_DISABLE_SPECIAL )
  1909. {
  1910. UStrCpy(cmdline + UStrLen(cmdline),GET_STRING(IDS_DisableSpecial_Yes));
  1911. UStrCpy(cmdline + UStrLen(cmdline),L" ");
  1912. }
  1913. if ( opt.flags & F_DISABLESOURCE )
  1914. {
  1915. UStrCpy(cmdline + UStrLen(cmdline),GET_STRING(IDS_DisableSourceAccounts_Yes));
  1916. UStrCpy(cmdline + UStrLen(cmdline),L" ");
  1917. }
  1918. if ( opt.flags & F_STRONGPW_ALL )
  1919. {
  1920. UStrCpy(cmdline + UStrLen(cmdline),GET_STRING(IDS_StrongPwd_All));
  1921. UStrCpy(cmdline + UStrLen(cmdline),L" ");
  1922. }
  1923. else if ( opt.flags & F_STRONGPW_SPECIAL )
  1924. {
  1925. UStrCpy(cmdline + UStrLen(cmdline),GET_STRING(IDS_StrongPwd_Special));
  1926. UStrCpy(cmdline + UStrLen(cmdline),L" ");
  1927. }
  1928. if ( *opt.addToGroup )
  1929. {
  1930. UStrCpy(cmdline + UStrLen(cmdline),GET_STRING(IDS_AddToGroup));
  1931. UStrCpy(cmdline + UStrLen(cmdline),opt.addToGroup);
  1932. UStrCpy(cmdline + UStrLen(cmdline),L" ");
  1933. }
  1934. err.MsgWrite(0,DCT_MSG_GENERIC_S,cmdline);
  1935. }
  1936. void
  1937. CAcctRepl::LoadResultsToVarSet(
  1938. IVarSet * pVarSet // i/o - VarSet
  1939. )
  1940. {
  1941. _bstr_t text;
  1942. text = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_CSVResultFile));
  1943. if ( text.length() )
  1944. {
  1945. CommaDelimitedLog results;
  1946. if ( results.LogOpen((WCHAR*)text,FALSE) )
  1947. {
  1948. // Write the results to a comma-separated file
  1949. // as SrcName,TgtName,AccountType,Status, srcRid, tgtRid
  1950. // This file can be used by ST as input.
  1951. TNodeListEnum e;
  1952. TAcctReplNode * tnode;
  1953. if ( acctList.IsTree() )
  1954. {
  1955. acctList.ToSorted();
  1956. }
  1957. for ( tnode = (TAcctReplNode *)e.OpenFirst(&acctList) ; tnode ; tnode = (TAcctReplNode *)e.Next() )
  1958. {
  1959. results.MsgWrite(L"%s,%s,%lx,%lx,%lx,%lx",tnode->GetName(),tnode->GetTargetSam(), tnode->GetType(),tnode->GetStatus(),tnode->GetSourceRid(),tnode->GetTargetRid());
  1960. }
  1961. e.Close();
  1962. results.LogClose();
  1963. }
  1964. else
  1965. {
  1966. err.MsgWrite(ErrE,DCT_MSG_FAILED_TO_WRITE_ACCOUNT_STATS_S,text);
  1967. Mark(L"errors", "generic");
  1968. }
  1969. }
  1970. long level = pVarSet->get(GET_BSTR(DCTVS_Results_ErrorLevel));
  1971. if ( level < err.GetMaxSeverityLevel() )
  1972. {
  1973. pVarSet->put(GET_BSTR(DCTVS_Results_ErrorLevel),(LONG)err.GetMaxSeverityLevel());
  1974. }
  1975. }
  1976. IADsGroup * GetWellKnownTargetGroup(long groupID,Options * pOptions)
  1977. {
  1978. IADsGroup * pGroup = NULL;
  1979. HRESULT hr;
  1980. PSID pSid;
  1981. WCHAR strSid[LEN_Path];
  1982. WCHAR sPath[LEN_Path];
  1983. CLdapConnection c;
  1984. // Get the SID for the Domain Computers group
  1985. pSid = GetWellKnownSid(groupID,pOptions,TRUE);
  1986. if ( pSid )
  1987. {
  1988. c.BytesToString((LPBYTE)pSid,strSid,GetLengthSid(pSid));
  1989. swprintf(sPath,L"LDAP://%ls/<SID=%ls>",pOptions->tgtDomain,strSid);
  1990. hr = ADsGetObject(sPath,IID_IADsGroup,(void**)&pGroup);
  1991. FreeSid(pSid);
  1992. }
  1993. return pGroup;
  1994. }
  1995. void PadCnName(WCHAR * sTarget)
  1996. {
  1997. if (sTarget == NULL)
  1998. _com_issue_error(E_INVALIDARG);
  1999. // escape character
  2000. const WCHAR ESCAPE_CHARACTER = L'\\';
  2001. // characters that need escaping for RDN format
  2002. static WCHAR SPECIAL_CHARACTERS[] = L"\"#+,;<=>\\";
  2003. // copy old name
  2004. WCHAR szOldName[LEN_Path];
  2005. DWORD dwArraySizeOfszOldName = sizeof(szOldName)/sizeof(szOldName[0]);
  2006. if (wcslen(sTarget) >= dwArraySizeOfszOldName)
  2007. _com_issue_error(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
  2008. wcscpy(szOldName, sTarget);
  2009. WCHAR* pchNew = sTarget;
  2010. // for each character in old name...
  2011. for (WCHAR* pchOld = szOldName; *pchOld; pchOld++)
  2012. {
  2013. // if special character...
  2014. if (wcschr(SPECIAL_CHARACTERS, *pchOld))
  2015. {
  2016. // then add escape character
  2017. *pchNew++ = ESCAPE_CHARACTER;
  2018. }
  2019. // add character
  2020. *pchNew++ = *pchOld;
  2021. }
  2022. // null terminate new name
  2023. *pchNew = L'\0';
  2024. }
  2025. //------------------------------------------------------------------------------
  2026. // Create2KObj: Creates a Win2K object. This code uses LDAP to create a new
  2027. // object of specified type in the specified container.
  2028. // If any information is incorrect or If there are any access
  2029. // problems then it simply returns the Failed HRESULT.
  2030. //------------------------------------------------------------------------------
  2031. HRESULT CAcctRepl::Create2KObj(
  2032. TAcctReplNode * pAcct, //in -TNode with account information
  2033. Options * pOptions, //in -Options set by the user.
  2034. CTargetPathSet& setTargetPath
  2035. )
  2036. {
  2037. // This function creates a Win2K object.
  2038. IADsPtr pAdsSrc;
  2039. IADsPtr pAds;
  2040. c_array<WCHAR> achAdsPath(LEN_Path);
  2041. DWORD nPathLen = LEN_Path;
  2042. c_array<WCHAR> achSubPath(LEN_Path);
  2043. _bstr_t strClass;
  2044. HRESULT hr;
  2045. c_array<WCHAR> achTarget(LEN_Path);
  2046. _variant_t varT;
  2047. _bstr_t strName;
  2048. IADsContainerPtr pCont;
  2049. IDispatchPtr pDisp;
  2050. c_array<WCHAR> achPref(LEN_Path);
  2051. c_array<WCHAR> achSuf(LEN_Path);
  2052. // Get the name of the class for the source object so we can use that to create the new object.
  2053. strClass = pAcct->GetType();
  2054. if (!strClass)
  2055. {
  2056. return E_FAIL;
  2057. }
  2058. // check if the sourceAdsPath, for LDAP paths only, is correct before creating this object on the target. If not fail now.
  2059. if (!wcsncmp(L"LDAP://", pAcct->GetSourcePath(), 7))
  2060. {
  2061. wcsncpy(achAdsPath, pAcct->GetSourcePath(), nPathLen-1);
  2062. hr = ADsGetObject(achAdsPath, IID_IADs, (void**)&pAdsSrc);
  2063. if (FAILED(hr))
  2064. {
  2065. err.SysMsgWrite(ErrE,hr, DCT_MSG_LDAP_CALL_FAILED_SD, (WCHAR*)achAdsPath, hr);
  2066. Mark(L"errors", pAcct->GetType());
  2067. return hr;
  2068. }
  2069. }
  2070. // Now that we have the classname we can go ahead and create an object in the target domain.
  2071. // First we need to get IAdsContainer * to the domain.
  2072. wcscpy(achSubPath, pOptions->tgtOUPath);
  2073. if ( !wcsncmp(L"LDAP://", achSubPath, 7) )
  2074. StuffComputerNameinLdapPath(achAdsPath, nPathLen, achSubPath, pOptions);
  2075. else
  2076. MakeFullyQualifiedAdsPath(achAdsPath, nPathLen, achSubPath, pOptions->tgtComp, pOptions->tgtNamingContext);
  2077. hr = ADsGetObject(achAdsPath, IID_IADsContainer, (void**)&pCont);
  2078. if ( FAILED(hr) )
  2079. {
  2080. if ( firstTime )
  2081. {
  2082. err.SysMsgWrite(ErrE,hr,DCT_MSG_CONTAINER_NOT_FOUND_SSD, pOptions->tgtOUPath, pOptions->tgtDomain, hr);
  2083. firstTime = false;
  2084. Mark(L"errors", pAcct->GetType());
  2085. }
  2086. if ( _wcsicmp(strClass, L"computer") == 0 )
  2087. {
  2088. MakeFullyQualifiedAdsPath(achAdsPath, nPathLen, L"CN=Computers", pOptions->tgtDomain, pOptions->tgtNamingContext);
  2089. hr = ADsGetObject(achAdsPath, IID_IADsContainer, (void**)&pCont);
  2090. }
  2091. else
  2092. {
  2093. MakeFullyQualifiedAdsPath(achAdsPath, nPathLen, L"CN=Users", pOptions->tgtDomain, pOptions->tgtNamingContext);
  2094. hr = ADsGetObject(achAdsPath, IID_IADsContainer, (void**)&pCont);
  2095. }
  2096. if ( FAILED(hr) )
  2097. {
  2098. err.SysMsgWrite(ErrE, hr, DCT_MSG_DEFAULT_CONTAINER_NOT_FOUND_SD, (WCHAR*)achAdsPath, hr);
  2099. Mark(L"errors", pAcct->GetType());
  2100. return (hr);
  2101. }
  2102. }
  2103. // Call the create method on the container.
  2104. wcscpy(achTarget, pAcct->GetTargetName());
  2105. // In case of the NT4 source domain the source and the target name have no CN= so we need
  2106. // to add this to the target name. The target name from the group mapping wizard also needs a "CN="
  2107. // added to the target name.
  2108. if ((pOptions->srcDomainVer < 5) || (!_wcsicmp(strClass, L"computer")) || (!_wcsicmp((WCHAR*)pOptions->sWizard, L"groupmapping")))
  2109. {
  2110. c_array<WCHAR> achTemp(LEN_Path);
  2111. wcscpy(achTemp, pAcct->GetTargetName());
  2112. PadCnName(achTemp);
  2113. // if the CN part is not there add it.
  2114. if ( _wcsicmp(strClass, L"organizationalUnit") == 0 )
  2115. wsprintf(achTarget, L"OU=%s", (WCHAR*)achTemp);
  2116. else
  2117. wsprintf(achTarget, L"CN=%s", (WCHAR*)achTemp);
  2118. pAcct->SetTargetName(achTarget);
  2119. }
  2120. // we need to truncate CN name to less that 64 characters
  2121. for ( DWORD z = 0; z < wcslen(achTarget); z++ )
  2122. {
  2123. if ( achTarget[z] == L'=' ) break;
  2124. }
  2125. if ( z < wcslen(achTarget) )
  2126. {
  2127. // Get the prefix part ex.CN=
  2128. wcsncpy(achPref, achTarget, z+1);
  2129. achPref[z+1] = 0;
  2130. wcscpy(achSuf, achTarget+z+1);
  2131. }
  2132. // The CN for the account could be greater than 64 we need to truncate it.
  2133. c_array<WCHAR> achTempCn(LEN_Path);
  2134. // if class is inetOrgPerson...
  2135. if (_wcsicmp(strClass, s_ClassInetOrgPerson) == 0)
  2136. {
  2137. // retrieve naming attribute
  2138. SNamingAttribute naNamingAttribute;
  2139. if (SUCCEEDED(GetNamingAttribute(pOptions->tgtDomainDns, s_ClassInetOrgPerson, naNamingAttribute)))
  2140. {
  2141. if (long(wcslen(achSuf)) > naNamingAttribute.nMaxRange)
  2142. {
  2143. err.MsgWrite(ErrE, DCT_MSG_RDN_LENGTH_GREATER_THAN_MAX_RANGE_S, pAcct->GetTargetName());
  2144. Mark(L"errors", pAcct->GetType());
  2145. return HRESULT_FROM_WIN32(ERROR_DS_CONSTRAINT_VIOLATION);
  2146. }
  2147. }
  2148. wcscpy(achTempCn, achSuf);
  2149. }
  2150. else if ( wcslen(achSuf) > 64 )
  2151. {
  2152. if ( wcslen(pOptions->globalSuffix) )
  2153. {
  2154. // in case of a global suffix we need to remove the suffix and then truncate the account and then readd the suffix.
  2155. achSuf[wcslen(achSuf) - wcslen(pOptions->globalSuffix)] = L'\0';
  2156. }
  2157. int truncate = 64 - wcslen(pOptions->globalSuffix);
  2158. wcsncpy(achTempCn, achSuf, truncate);
  2159. achTempCn[truncate] = L'\0';
  2160. if (wcslen(pOptions->globalSuffix))
  2161. wcscat(achTempCn, pOptions->globalSuffix);
  2162. err.MsgWrite(1, DCT_MSG_TRUNCATE_CN_SS, pAcct->GetTargetName(), (WCHAR*)achTempCn);
  2163. }
  2164. else
  2165. wcscpy(achTempCn, achSuf);
  2166. wsprintf(achTarget, L"%s%s", (WCHAR*)achPref, (WCHAR*)achTempCn);
  2167. pAcct->SetTargetName(achTarget);
  2168. // even for a local group the object type of the group has to be a local group
  2169. if ( !_wcsicmp(strClass, L"lgroup") )
  2170. {
  2171. strClass = L"group";
  2172. }
  2173. // Call the create method on the container.
  2174. wcscpy(achTarget, pAcct->GetTargetName());
  2175. hr = pCont->Create(strClass, _bstr_t(achTarget), &pDisp);
  2176. if ( FAILED(hr) )
  2177. {
  2178. err.SysMsgWrite(ErrE, hr,DCT_MSG_CREATE_FAILED_SSD, pAcct->GetTargetName(), pOptions->tgtOUPath, hr);
  2179. Mark(L"errors", pAcct->GetType());
  2180. return hr;
  2181. }
  2182. // Get the IADs interface to get the path to newly created object.
  2183. pAds = pDisp;
  2184. if ( pAds == NULL )
  2185. {
  2186. err.SysMsgWrite(ErrE, hr, DCT_MSG_GET_IADS_FAILED_SSD, pAcct->GetTargetName(), pOptions->tgtOUPath, E_NOINTERFACE);
  2187. Mark(L"errors", pAcct->GetType());
  2188. return hr;
  2189. }
  2190. // if object class is inetOrgPerson and the naming attribute is not cn then...
  2191. if ((_wcsicmp(strClass, s_ClassInetOrgPerson) == 0) && (_wcsicmp(achPref, L"cn=") != 0))
  2192. {
  2193. // retrieve the source cn attribute and set the target cn attribute
  2194. // the cn attribute is a mandatory attribute and therefore
  2195. // must be set before attempting to create the object
  2196. _bstr_t strCN(L"cn");
  2197. VARIANT var;
  2198. VariantInit(&var);
  2199. hr = pAdsSrc->Get(strCN, &var);
  2200. if (SUCCEEDED(hr))
  2201. {
  2202. pAds->Put(strCN, var);
  2203. VariantClear(&var);
  2204. }
  2205. }
  2206. // Set the Target Account Sam name if not an OU.
  2207. wstring strTargetSam = pAcct->GetTargetSam();
  2208. // check if the $ is at the end of the SAM name for computer accounts.
  2209. if ( !_wcsicmp(strClass, L"computer") )
  2210. {
  2211. // also make sure the target SAM name is not too long
  2212. if ( strTargetSam.length() > MAX_COMPUTERNAME_LENGTH + 1 )
  2213. {
  2214. strTargetSam[MAX_COMPUTERNAME_LENGTH] = 0;
  2215. }
  2216. if (strTargetSam[strTargetSam.length()-1] != L'$')
  2217. {
  2218. strTargetSam += L"$";
  2219. pAcct->SetTargetSam(strTargetSam.c_str());
  2220. }
  2221. }
  2222. varT = strTargetSam.c_str();
  2223. if ( _wcsicmp(strClass, L"organizationalUnit") != 0)
  2224. // organizational unit has no sam account name
  2225. hr = pAds->Put(L"sAMAccountName", varT);
  2226. if ( _wcsicmp(strClass, L"group") == 0 )
  2227. {
  2228. varT = _variant_t(pAcct->GetGroupType());
  2229. if ( pOptions->srcDomainVer < 5 )
  2230. {
  2231. // all NT4 accounts are security accounts but they tell us that they are Dist accts so lets set them straight.
  2232. varT.lVal |= 0x80000000;
  2233. }
  2234. hr = pAds->Put(L"groupType", varT);
  2235. }
  2236. else if ((_wcsicmp(strClass, s_ClassUser) == 0) || (_wcsicmp(strClass, s_ClassInetOrgPerson) == 0))
  2237. {
  2238. if (pAdsSrc == NULL)
  2239. {
  2240. ADsGetObject(const_cast<WCHAR*>(pAcct->GetSourcePath()), IID_IADs, (void**)&pAdsSrc);
  2241. }
  2242. if (pAdsSrc)
  2243. {
  2244. // Get the source profile path and store it in the path
  2245. _variant_t var;
  2246. // Don't know why it is different for WinNT to ADSI
  2247. if ( pOptions->srcDomainVer > 4 )
  2248. hr = pAdsSrc->Get(L"profilePath", &var);
  2249. else
  2250. hr = pAdsSrc->Get(L"profile", &var);
  2251. if ( SUCCEEDED(hr))
  2252. {
  2253. pAcct->SetSourceProfile((WCHAR*) V_BSTR(&var));
  2254. }
  2255. }
  2256. }
  2257. // In no change mode we do not call the set info.
  2258. if ( !pOptions->nochange )
  2259. {
  2260. hr = pAds->SetInfo();
  2261. if ( FAILED(hr) )
  2262. {
  2263. if (HRESULT_CODE(hr) == ERROR_OBJECT_ALREADY_EXISTS)
  2264. {
  2265. //
  2266. // check if object DN is conflicting with
  2267. // another object that is currently being migrated
  2268. //
  2269. BSTR bstrPath = 0;
  2270. if (SUCCEEDED(pAds->get_ADsPath(&bstrPath)))
  2271. {
  2272. pAcct->SetTargetPath(_bstr_t(bstrPath, false));
  2273. if (DoTargetPathConflict(setTargetPath, pAcct))
  2274. {
  2275. return hr;
  2276. }
  2277. }
  2278. if ( wcslen(pOptions->prefix) > 0 )
  2279. {
  2280. c_array<WCHAR> achTgt(LEN_Path);
  2281. c_array<WCHAR> achTempSam(LEN_Path);
  2282. _variant_t varStr;
  2283. // Here I am adding a prefix and then lets see if we can setinfo that way
  2284. // find the '=' sign
  2285. wcscpy(achTgt, pAcct->GetTargetName());
  2286. for ( DWORD z = 0; z < wcslen(achTgt); z++ )
  2287. {
  2288. if ( achTgt[z] == L'=' ) break;
  2289. }
  2290. if ( z < wcslen(achTgt) )
  2291. {
  2292. // Get the prefix part ex.CN=
  2293. wcsncpy(achPref, achTgt, z+1);
  2294. achPref[z+1] = 0;
  2295. wcscpy(achSuf, achTgt+z+1);
  2296. }
  2297. // The CN for the account could be greater than 64 we need to truncate it.
  2298. // if class is inetOrgPerson...
  2299. if (_wcsicmp(strClass, s_ClassInetOrgPerson) == 0)
  2300. {
  2301. SNamingAttribute naNamingAttribute;
  2302. if (SUCCEEDED(GetNamingAttribute(pOptions->tgtDomainDns, s_ClassInetOrgPerson, naNamingAttribute)))
  2303. {
  2304. if (long(wcslen(achSuf) + wcslen(pOptions->prefix)) > naNamingAttribute.nMaxRange)
  2305. {
  2306. wsprintf(achTgt, L"%s%s%s", (WCHAR*)achPref, pOptions->prefix, (WCHAR*)achSuf);
  2307. err.MsgWrite(ErrE, DCT_MSG_RDN_LENGTH_GREATER_THAN_MAX_RANGE_S, (WCHAR*)achTgt);
  2308. Mark(L"errors", pAcct->GetType());
  2309. return HRESULT_FROM_WIN32(ERROR_DS_CONSTRAINT_VIOLATION);
  2310. }
  2311. }
  2312. wcscpy(achTempCn, achSuf);
  2313. }
  2314. else if ( wcslen(achSuf) + wcslen(pOptions->prefix) > 64 )
  2315. {
  2316. int truncate = 64 - wcslen(pOptions->prefix);
  2317. wcsncpy(achTempCn, achSuf, truncate);
  2318. achTempCn[truncate] = L'\0';
  2319. err.MsgWrite(1, DCT_MSG_TRUNCATE_CN_SS, pAcct->GetTargetName(), (WCHAR*)achTempCn);
  2320. }
  2321. else
  2322. wcscpy(achTempCn, achSuf);
  2323. // Remove the \ if it is escaping the space
  2324. if ( achTempCn[0] == L'\\' && achTempCn[1] == L' ' )
  2325. {
  2326. wstring str = achTempCn + 1;
  2327. wcscpy(achTempCn, str.c_str());
  2328. }
  2329. // Build the target string with the Prefix
  2330. wsprintf(achTgt, L"%s%s%s", (WCHAR*)achPref, pOptions->prefix, (WCHAR*)achTempCn);
  2331. pAcct->SetTargetName(achTgt);
  2332. // Create the object in the container
  2333. hr = pCont->Create(strClass, _bstr_t(achTgt), &pDisp);
  2334. if ( FAILED(hr) )
  2335. {
  2336. err.SysMsgWrite(ErrE, hr,DCT_MSG_CREATE_FAILED_SSD, pAcct->GetTargetName(), pOptions->tgtOUPath, hr);
  2337. Mark(L"errors", pAcct->GetType());
  2338. return hr;
  2339. }
  2340. // Get the IADs interface to get the path to newly created object.
  2341. hr = pDisp->QueryInterface(IID_IADs, (void**)&pAds);
  2342. if ( FAILED(hr) )
  2343. {
  2344. err.SysMsgWrite(ErrE, hr, DCT_MSG_GET_IADS_FAILED_SSD, pAcct->GetTargetName(), pOptions->tgtOUPath, hr);
  2345. Mark(L"errors", pAcct->GetType());
  2346. return hr;
  2347. }
  2348. // if object class is inetOrgPerson and the naming attribute is not cn then...
  2349. if ((_wcsicmp(strClass, s_ClassInetOrgPerson) == 0) && (_wcsicmp(achPref, L"cn=") != 0))
  2350. {
  2351. // retrieve the source cn attribute and set the target cn attribute
  2352. // the cn attribute is a mandatory attribute and therefore
  2353. // must be set before attempting to create the object
  2354. _bstr_t strCN(L"cn");
  2355. VARIANT var;
  2356. VariantInit(&var);
  2357. hr = pAdsSrc->Get(strCN, &var);
  2358. if (SUCCEEDED(hr))
  2359. {
  2360. pAds->Put(strCN, var);
  2361. VariantClear(&var);
  2362. }
  2363. }
  2364. // truncate to allow prefix/suffix to fit in 20 characters.
  2365. int resLen = wcslen(pOptions->prefix) + wcslen(pAcct->GetTargetSam());
  2366. if ( !_wcsicmp(pAcct->GetType(), L"computer") )
  2367. {
  2368. // Computer name can be only 15 characters long + $
  2369. if ( resLen > MAX_COMPUTERNAME_LENGTH + 1 )
  2370. {
  2371. c_array<WCHAR> achTruncatedSam(LEN_Path);
  2372. wcscpy(achTruncatedSam, pAcct->GetTargetSam());
  2373. if ( wcslen( pOptions->globalSuffix ) )
  2374. {
  2375. // We must remove the global suffix if we had one.
  2376. achTruncatedSam[wcslen(achTruncatedSam) - wcslen(pOptions->globalSuffix)] = L'\0';
  2377. }
  2378. int truncate = MAX_COMPUTERNAME_LENGTH + 1 - wcslen(pOptions->prefix) - wcslen(pOptions->globalSuffix);
  2379. if ( truncate < 1 ) truncate = 1;
  2380. wcsncpy(achTempSam, achTruncatedSam, truncate - 1);
  2381. achTempSam[truncate-1] = L'\0'; // Dont forget the $ sign and terminate string.
  2382. wcscat(achTempSam, pOptions->globalSuffix);
  2383. wcscat(achTempSam, L"$");
  2384. }
  2385. else
  2386. wcscpy(achTempSam, pAcct->GetTargetSam());
  2387. // Add the prefix
  2388. wsprintf(achTgt, L"%s%s", pOptions->prefix,(WCHAR*)achTempSam);
  2389. }
  2390. else if ((_wcsicmp(pAcct->GetType(), s_ClassUser) == 0) || (_wcsicmp(pAcct->GetType(), s_ClassInetOrgPerson) == 0))
  2391. {
  2392. if ( resLen > 20 )
  2393. {
  2394. c_array<WCHAR> achTruncatedSam(LEN_Path);
  2395. wcscpy(achTruncatedSam, pAcct->GetTargetSam());
  2396. if ( wcslen( pOptions->globalSuffix ) )
  2397. {
  2398. // We must remove the global suffix if we had one.
  2399. achTruncatedSam[wcslen(achTruncatedSam) - wcslen(pOptions->globalSuffix)] = L'\0';
  2400. }
  2401. int truncate = 20 - wcslen(pOptions->prefix) - wcslen(pOptions->globalSuffix);
  2402. if ( truncate < 0 ) truncate = 0;
  2403. wcsncpy(achTempSam, achTruncatedSam, truncate);
  2404. achTempSam[truncate] = L'\0';
  2405. wcscat(achTempSam, pOptions->globalSuffix);
  2406. }
  2407. else
  2408. wcscpy(achTempSam, pAcct->GetTargetSam());
  2409. // Add the prefix
  2410. wsprintf(achTgt, L"%s%s", pOptions->prefix, (WCHAR*)achTempSam);
  2411. }
  2412. else
  2413. wsprintf(achTgt, L"%s%s", pOptions->prefix,pAcct->GetTargetSam());
  2414. pAcct->SetTargetSam(achTgt);
  2415. varStr = achTgt;
  2416. pAds->Put(L"sAMAccountName", varStr);
  2417. if ( _wcsicmp(strClass, L"group") == 0 )
  2418. {
  2419. varT = _variant_t(pAcct->GetGroupType());
  2420. if ( pOptions->srcDomainVer < 5 )
  2421. {
  2422. // all NT4 accounts are security accounts but they tell us that they are Dist accts so lets set them straight.
  2423. varT.lVal |= 0x80000000;
  2424. }
  2425. hr = pAds->Put(L"groupType", varT);
  2426. }
  2427. hr = pAds->SetInfo();
  2428. if ( SUCCEEDED(hr) )
  2429. {
  2430. Mark(L"created", strClass);
  2431. pAcct->MarkCreated();
  2432. BSTR sTgtPath = 0;
  2433. HRESULT temphr = pAds->get_ADsPath(&sTgtPath);
  2434. if ( SUCCEEDED(temphr) )
  2435. {
  2436. pAcct->SetTargetPath(_bstr_t(sTgtPath, false));
  2437. setTargetPath.insert(pAcct);
  2438. }
  2439. else
  2440. pAcct->SetTargetPath(L"");
  2441. }
  2442. else if ( HRESULT_CODE(hr) == ERROR_OBJECT_ALREADY_EXISTS )
  2443. {
  2444. //
  2445. // check if object DN is conflicting with
  2446. // another object that is currently being migrated
  2447. //
  2448. BSTR bstrPath = 0;
  2449. if (SUCCEEDED(pAds->get_ADsPath(&bstrPath)))
  2450. {
  2451. pAcct->SetTargetPath(_bstr_t(bstrPath, false));
  2452. if (DoTargetPathConflict(setTargetPath, pAcct))
  2453. {
  2454. return hr;
  2455. }
  2456. }
  2457. pAcct->MarkAlreadyThere();
  2458. err.MsgWrite(ErrE, DCT_MSG_PREF_ACCOUNT_EXISTS_S, pAcct->GetTargetSam());
  2459. Mark(L"errors",pAcct->GetType());
  2460. }
  2461. else
  2462. {
  2463. pAcct->MarkError();
  2464. err.SysMsgWrite(ErrE, hr, DCT_MSG_CREATE_FAILED_SSD, pAcct->GetTargetSam(), pOptions->tgtOUPath, hr);
  2465. Mark(L"errors",pAcct->GetType());
  2466. }
  2467. }
  2468. else if ( wcslen(pOptions->suffix) > 0 )
  2469. {
  2470. c_array<WCHAR> achTgt(LEN_Path);
  2471. c_array<WCHAR> achTempSam(LEN_Path);
  2472. _variant_t varStr;
  2473. wcscpy(achTgt, pAcct->GetTargetName());
  2474. for ( DWORD z = 0; z < wcslen(achTgt); z++ )
  2475. {
  2476. if ( achTgt[z] == L'=' ) break;
  2477. }
  2478. if ( z < wcslen(achTgt) )
  2479. {
  2480. // Get the prefix part ex.CN=
  2481. wcsncpy(achPref, achTgt, z+1);
  2482. achPref[z+1] = 0;
  2483. wcscpy(achSuf, achTgt+z+1);
  2484. }
  2485. // The CN for the account could be greater than 64 we need to truncate it.
  2486. // if class is inetOrgPerson...
  2487. if (_wcsicmp(strClass, s_ClassInetOrgPerson) == 0)
  2488. {
  2489. SNamingAttribute naNamingAttribute;
  2490. if (SUCCEEDED(GetNamingAttribute(pOptions->tgtDomainDns, s_ClassInetOrgPerson, naNamingAttribute)))
  2491. {
  2492. if (long(wcslen(achSuf) + wcslen(pOptions->suffix)) > naNamingAttribute.nMaxRange)
  2493. {
  2494. wsprintf(achTgt, L"%s%s%s", (WCHAR*)achPref, (WCHAR*)achSuf, pOptions->suffix);
  2495. err.MsgWrite(ErrE, DCT_MSG_RDN_LENGTH_GREATER_THAN_MAX_RANGE_S, (WCHAR*)achTgt);
  2496. Mark(L"errors", pAcct->GetType());
  2497. return HRESULT_FROM_WIN32(ERROR_DS_CONSTRAINT_VIOLATION);
  2498. }
  2499. }
  2500. wcscpy(achTempCn, achSuf);
  2501. }
  2502. else if ( wcslen(achSuf) + wcslen(pOptions->suffix) + wcslen(pOptions->globalSuffix) > 64 )
  2503. {
  2504. if ( wcslen(pOptions->globalSuffix) )
  2505. {
  2506. // in case of a global suffix we need to remove the suffix and then truncate the account and then readd the suffix.
  2507. achSuf[wcslen(achSuf) - wcslen(pOptions->globalSuffix)] = L'\0';
  2508. }
  2509. int truncate = 64 - wcslen(pOptions->suffix) - wcslen(pOptions->globalSuffix);
  2510. wcsncpy(achTempCn, achSuf, truncate);
  2511. achTempCn[truncate] = L'\0';
  2512. wcscat(achTempCn, pOptions->globalSuffix);
  2513. err.MsgWrite(1, DCT_MSG_TRUNCATE_CN_SS, pAcct->GetTargetName(), (WCHAR*)achSuf);
  2514. }
  2515. else
  2516. wcscpy(achTempCn, achSuf);
  2517. // Remove the trailing space \ escape sequence
  2518. wcscpy(achTgt, achTempCn);
  2519. for ( int i = wcslen(achTgt)-1; i >= 0; i-- )
  2520. {
  2521. if ( achTgt[i] != L' ' )
  2522. break;
  2523. }
  2524. if ( achTgt[i] == L'\\' )
  2525. {
  2526. WCHAR * pTemp = &achTgt[i];
  2527. *pTemp = 0;
  2528. wcscat(achPref, achTgt);
  2529. wcscpy(achSuf, pTemp+1);
  2530. }
  2531. else
  2532. {
  2533. wcscat(achPref, achTgt);
  2534. wcscpy(achSuf, L"");
  2535. }
  2536. wsprintf(achTgt, L"%s%s%s", (WCHAR*)achPref, (WCHAR*)achSuf, pOptions->suffix);
  2537. pAcct->SetTargetName(achTgt);
  2538. // Create the object in the container
  2539. hr = pCont->Create(strClass, _bstr_t(achTgt), &pDisp);
  2540. if ( FAILED(hr) )
  2541. {
  2542. err.SysMsgWrite(ErrE, hr,DCT_MSG_CREATE_FAILED_SSD, pAcct->GetTargetName(), pOptions->tgtOUPath, hr);
  2543. Mark(L"errors", pAcct->GetType());
  2544. return hr;
  2545. }
  2546. // Get the IADs interface to get the path to newly created object.
  2547. hr = pDisp->QueryInterface(IID_IADs, (void**)&pAds);
  2548. if ( FAILED(hr) )
  2549. {
  2550. err.SysMsgWrite(ErrE, hr, DCT_MSG_GET_IADS_FAILED_SSD, pAcct->GetTargetName(), pOptions->tgtOUPath, hr);
  2551. Mark(L"errors", pAcct->GetType());
  2552. return hr;
  2553. }
  2554. // if object class is inetOrgPerson and the naming attribute is not cn then...
  2555. if ((_wcsicmp(strClass, s_ClassInetOrgPerson) == 0) && (_wcsicmp(achPref, L"cn=") != 0))
  2556. {
  2557. // retrieve the source cn attribute and set the target cn attribute
  2558. // the cn attribute is a mandatory attribute and therefore
  2559. // must be set before attempting to create the object
  2560. _bstr_t strCN(L"cn");
  2561. VARIANT var;
  2562. VariantInit(&var);
  2563. hr = pAdsSrc->Get(strCN, &var);
  2564. if (SUCCEEDED(hr))
  2565. {
  2566. pAds->Put(strCN, var);
  2567. VariantClear(&var);
  2568. }
  2569. }
  2570. // truncate to allow prefix/suffix to fit in valid length
  2571. int resLen = wcslen(pOptions->suffix) + wcslen(pAcct->GetTargetSam());
  2572. if ( !_wcsicmp(pAcct->GetType(), L"computer") )
  2573. {
  2574. // Computer name can be only 15 characters long + $
  2575. if ( resLen > MAX_COMPUTERNAME_LENGTH + 1 )
  2576. {
  2577. c_array<WCHAR> achTruncatedSam(LEN_Path);
  2578. wcscpy(achTruncatedSam, pAcct->GetTargetSam());
  2579. if ( wcslen( pOptions->globalSuffix ) )
  2580. {
  2581. // We must remove the global suffix if we had one.
  2582. achTruncatedSam[wcslen(achTruncatedSam) - wcslen(pOptions->globalSuffix)] = L'\0';
  2583. }
  2584. int truncate = MAX_COMPUTERNAME_LENGTH + 1 - wcslen(pOptions->suffix) - wcslen(pOptions->globalSuffix);
  2585. if ( truncate < 1 ) truncate = 1;
  2586. wcsncpy(achTempSam, achTruncatedSam, truncate - 1);
  2587. achTempSam[truncate-1] = L'\0';
  2588. // Re add the global suffix after the truncation.
  2589. wcscat(achTempSam, pOptions->globalSuffix);
  2590. wcscat(achTempSam, L"$");
  2591. }
  2592. else
  2593. wcscpy(achTempSam, pAcct->GetTargetSam());
  2594. // Add the suffix taking into account the $ sign
  2595. if ( achTempSam[wcslen(achTempSam) - 1] == L'$' )
  2596. achTempSam[wcslen(achTempSam) - 1] = L'\0';
  2597. wsprintf(achTgt, L"%s%s$", (WCHAR*)achTempSam, pOptions->suffix);
  2598. }
  2599. else if ((_wcsicmp(pAcct->GetType(), s_ClassUser) == 0) || (_wcsicmp(pAcct->GetType(), s_ClassInetOrgPerson) == 0))
  2600. {
  2601. if ( resLen > 20 )
  2602. {
  2603. c_array<WCHAR> achTruncatedSam(LEN_Path);
  2604. wcscpy(achTruncatedSam, pAcct->GetTargetSam());
  2605. if ( wcslen( pOptions->globalSuffix ) )
  2606. {
  2607. // We must remove the global suffix if we had one.
  2608. achTruncatedSam[wcslen(achTruncatedSam) - wcslen(pOptions->globalSuffix)] = L'\0';
  2609. }
  2610. int truncate = 20 - wcslen(pOptions->suffix) - wcslen(pOptions->globalSuffix);
  2611. if ( truncate < 0 ) truncate = 0;
  2612. wcsncpy(achTempSam, achTruncatedSam, truncate);
  2613. achTempSam[truncate] = L'\0';
  2614. wcscat(achTempSam, pOptions->globalSuffix);
  2615. }
  2616. else
  2617. wcscpy(achTempSam, pAcct->GetTargetSam());
  2618. // Add the suffix.
  2619. wsprintf(achTgt, L"%s%s", (WCHAR*)achTempSam, pOptions->suffix);
  2620. }
  2621. else
  2622. wsprintf(achTgt, L"%s%s", pAcct->GetTargetSam(), pOptions->suffix);
  2623. pAcct->SetTargetSam(achTgt);
  2624. varStr = achTgt;
  2625. pAds->Put(L"sAMAccountName", varStr);
  2626. if ( _wcsicmp(strClass, L"group") == 0 )
  2627. {
  2628. varT = _variant_t(pAcct->GetGroupType());
  2629. if ( pOptions->srcDomainVer < 5 )
  2630. {
  2631. // all NT4 accounts are security accounts but they tell us that they are Dist accts so lets set them straight.
  2632. varT.lVal |= 0x80000000;
  2633. }
  2634. hr = pAds->Put(L"groupType", varT);
  2635. }
  2636. hr = pAds->SetInfo();
  2637. if ( SUCCEEDED(hr) )
  2638. {
  2639. Mark(L"created", strClass);
  2640. pAcct->MarkCreated();
  2641. BSTR sTgtPath = 0;
  2642. HRESULT temphr = pAds->get_ADsPath(&sTgtPath);
  2643. if ( SUCCEEDED(temphr) )
  2644. {
  2645. pAcct->SetTargetPath(_bstr_t(sTgtPath, false));
  2646. setTargetPath.insert(pAcct);
  2647. }
  2648. else
  2649. pAcct->SetTargetPath(L"");
  2650. }
  2651. else if ( HRESULT_CODE(hr) == ERROR_OBJECT_ALREADY_EXISTS )
  2652. {
  2653. //
  2654. // check if object DN is conflicting with
  2655. // another object that is currently being migrated
  2656. //
  2657. BSTR bstrPath = 0;
  2658. if (SUCCEEDED(pAds->get_ADsPath(&bstrPath)))
  2659. {
  2660. pAcct->SetTargetPath(_bstr_t(bstrPath, false));
  2661. if (DoTargetPathConflict(setTargetPath, pAcct))
  2662. {
  2663. return hr;
  2664. }
  2665. }
  2666. pAcct->MarkAlreadyThere();
  2667. err.MsgWrite(ErrE, DCT_MSG_PREF_ACCOUNT_EXISTS_S, pAcct->GetTargetSam());
  2668. Mark(L"errors",pAcct->GetType());
  2669. }
  2670. else
  2671. {
  2672. pAcct->MarkError();
  2673. err.SysMsgWrite(ErrE, hr, DCT_MSG_CREATE_FAILED_SSD, pAcct->GetTargetSam(), pOptions->tgtOUPath, hr);
  2674. Mark(L"errors",pAcct->GetType());
  2675. }
  2676. }
  2677. else
  2678. {
  2679. if (pOptions->flags & F_REPLACE)
  2680. {
  2681. c_array<WCHAR> achPath9(LEN_Path);
  2682. SAFEARRAY * pszColNames = NULL;
  2683. BSTR HUGEP * pData;
  2684. LPWSTR sData[] = { L"ADsPath", L"profilePath", L"objectClass" };
  2685. INetObjEnumeratorPtr pQuery(__uuidof(NetObjEnumerator));
  2686. IEnumVARIANT * pEnumMem = NULL;
  2687. _variant_t var;
  2688. DWORD dwFetch;
  2689. HRESULT temphr;
  2690. int nElt = DIM(sData);
  2691. SAFEARRAYBOUND bd = { nElt, 0 };
  2692. BOOL bIsCritical = FALSE;
  2693. BOOL bIsDifferentType = FALSE;
  2694. // Since the object already exists we need to get the ADsPath to the object and update the acct structure
  2695. // Set up the query and the path
  2696. wstring strPath = L"LDAP://";
  2697. strPath += pOptions->tgtComp+2;
  2698. strPath += L"/";
  2699. strPath += pOptions->tgtNamingContext;
  2700. wstring sTempSamName = pAcct->GetTargetSam();
  2701. if ( sTempSamName[0] == L' ' )
  2702. {
  2703. sTempSamName = L"\\20" + sTempSamName.substr(1);
  2704. }
  2705. wstring sQuery = L"(sAMAccountName=" + sTempSamName + L")";
  2706. temphr = pQuery->raw_SetQuery(_bstr_t(strPath.c_str()), _bstr_t(pOptions->tgtDomainDns), _bstr_t(sQuery.c_str()), ADS_SCOPE_SUBTREE, FALSE);
  2707. if ( FAILED(temphr) )
  2708. {
  2709. return temphr;
  2710. }
  2711. // Set up the columns so we get the ADsPath of the object.
  2712. pszColNames = ::SafeArrayCreate(VT_BSTR, 1, &bd);
  2713. temphr = ::SafeArrayAccessData(pszColNames, (void HUGEP **)&pData);
  2714. if ( FAILED(temphr) )
  2715. {
  2716. SafeArrayDestroy(pszColNames);
  2717. return temphr;
  2718. }
  2719. for ( long i = 0; i < nElt; i++ )
  2720. {
  2721. pData[i] = SysAllocString(sData[i]);
  2722. }
  2723. temphr = ::SafeArrayUnaccessData(pszColNames);
  2724. if ( FAILED(temphr) )
  2725. {
  2726. ::SafeArrayDestroy(pszColNames);
  2727. return temphr;
  2728. }
  2729. temphr = pQuery->raw_SetColumns(pszColNames);
  2730. if ( FAILED(temphr) )
  2731. {
  2732. ::SafeArrayDestroy(pszColNames);
  2733. return temphr;
  2734. }
  2735. // Time to execute the plan.
  2736. temphr = pQuery->raw_Execute(&pEnumMem);
  2737. if ( FAILED(temphr) )
  2738. {
  2739. ::SafeArrayDestroy(pszColNames);
  2740. return temphr;
  2741. }
  2742. ::SafeArrayDestroy(pszColNames);
  2743. temphr = pEnumMem->Next(1, &var, &dwFetch);
  2744. if ( temphr == S_OK )
  2745. {
  2746. // This would only happen if the member existed in the target domain.
  2747. // We now have a Variant containing an array of variants so we access the data
  2748. _variant_t * pVar;
  2749. _bstr_t sConfName = pAcct->GetTargetName();
  2750. _bstr_t sOldCont;
  2751. pszColNames = V_ARRAY(&var);
  2752. SafeArrayAccessData(pszColNames, (void HUGEP **)&pVar);
  2753. wcscpy(achAdsPath, (WCHAR*)pVar[0].bstrVal);
  2754. pAcct->SetTargetPath(achAdsPath);
  2755. // Check if the object we are about to replace is of the same type.
  2756. if ( _wcsicmp(pAcct->GetType(), (WCHAR*) pVar[2].bstrVal) )
  2757. bIsDifferentType = TRUE;
  2758. SafeArrayUnaccessData(pszColNames);
  2759. IADsPtr pAdsNew;
  2760. temphr = ADsGetObject(const_cast<WCHAR*>(pAcct->GetTargetPath()), IID_IADs, (void**)&pAdsNew);
  2761. if ( SUCCEEDED(temphr) )
  2762. {
  2763. //see if critical
  2764. _variant_t varCritical;
  2765. temphr = pAdsNew->Get(L"isCriticalSystemObject", &varCritical);
  2766. if (SUCCEEDED(temphr))
  2767. {
  2768. bIsCritical = V_BOOL(&varCritical) == -1 ? TRUE : FALSE;
  2769. }
  2770. //get the name
  2771. BSTR sTgtName = NULL;
  2772. temphr = pAdsNew->get_Name(&sTgtName);
  2773. if ( SUCCEEDED(temphr) )
  2774. sConfName = _bstr_t(sTgtName, false);
  2775. //get the parent container of the conflicting object
  2776. BSTR sTgtCont = NULL;
  2777. temphr = pAdsNew->get_Parent(&sTgtCont);
  2778. if ( SUCCEEDED(temphr) )
  2779. sOldCont = _bstr_t(sTgtCont, false);
  2780. }
  2781. if ( bIsDifferentType )
  2782. {
  2783. // Since the source and target accounts are of different types we do not want to replace these.
  2784. hr = HRESULT_FROM_WIN32(ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH);
  2785. }
  2786. //else if not critical then move the account
  2787. else if ( !bIsCritical )
  2788. {
  2789. //if user selected to move that account into the user-specified OU, then move it
  2790. if (pOptions->flags & F_MOVE_REPLACED_ACCT)
  2791. {
  2792. temphr = pCont->MoveHere(const_cast<WCHAR*>(pAcct->GetTargetPath()), const_cast<WCHAR*>(pAcct->GetTargetName()), &pDisp);
  2793. //if move failed due to CN conflict, do not migrate
  2794. if ( FAILED(temphr) )
  2795. {
  2796. // Retrieve distinguished names of object and container from path.
  2797. CADsPathName pathname;
  2798. pathname.Set(pAcct->GetTargetPath(), ADS_SETTYPE_FULL);
  2799. _bstr_t strObjectPath = pathname.Retrieve(ADS_FORMAT_X500_DN);
  2800. pathname.Set(pOptions->tgtOUPath, ADS_SETTYPE_FULL);
  2801. _bstr_t strContainerPath = pathname.Retrieve(ADS_FORMAT_X500_DN);
  2802. // Log error and mark account so that no further operations are
  2803. // performed for this account.
  2804. err.SysMsgWrite(ErrE, hr, DCT_MSG_MOVE_FAILED_RDN_CONFLICT_SS, (LPCTSTR)strObjectPath, (LPCTSTR)strContainerPath);
  2805. Mark(L"errors", pAcct->GetType());
  2806. pAcct->operations = 0;
  2807. pAcct->MarkError();
  2808. return temphr;
  2809. }
  2810. }
  2811. else //else try to rename the CN of the object (I'll use the same MoveHere API)
  2812. {
  2813. IADsContainerPtr pOldCont;
  2814. temphr = ADsGetObject(sOldCont, IID_IADsContainer, (void**)&pOldCont);
  2815. if (SUCCEEDED(temphr))
  2816. {
  2817. temphr = pOldCont->MoveHere(const_cast<WCHAR*>(pAcct->GetTargetPath()), const_cast<WCHAR*>(pAcct->GetTargetName()), &pDisp);
  2818. }
  2819. //if failed to rename the CN, do not migrate
  2820. if ( FAILED(temphr) )
  2821. {
  2822. // The CN rename failed due to conflicting CN in this container
  2823. err.MsgWrite(ErrE, DCT_MSG_CN_RENAME_CONFLICT_SSS, (WCHAR*)sConfName, pAcct->GetTargetName(), (WCHAR*)sOldCont);
  2824. Mark(L"errors", pAcct->GetType());
  2825. //if we couldn't rename the CN, change the error code so we don't continue migrating this user
  2826. if ((HRESULT_CODE(temphr) == ERROR_OBJECT_ALREADY_EXISTS))
  2827. temphr = HRESULT_FROM_WIN32(ERROR_DS_INVALID_DN_SYNTAX);
  2828. return temphr;
  2829. }
  2830. }
  2831. // Get the new location of the object.
  2832. BSTR sNewPath;
  2833. temphr = pDisp->QueryInterface(IID_IADs, (void**)&pAdsNew);
  2834. if ( FAILED(temphr) )
  2835. {
  2836. return temphr;
  2837. }
  2838. temphr = pAdsNew->get_ADsPath(&sNewPath);
  2839. if ( FAILED(temphr) )
  2840. {
  2841. return temphr;
  2842. }
  2843. // And store that in the target path
  2844. pAcct->SetTargetPath((WCHAR*) sNewPath);
  2845. SysFreeString(sNewPath);
  2846. setTargetPath.insert(pAcct);
  2847. // If the account is a group account and the Replace Existing members flag is set then we need to
  2848. // remove all the members of this group.
  2849. if ( (_wcsicmp(L"group", pAcct->GetType()) == 0 ) && (pOptions->flags & F_REMOVE_OLD_MEMBERS) )
  2850. RemoveMembers(pAcct, pOptions);
  2851. pAcct->MarkAlreadyThere();
  2852. pAcct->MarkReplaced();
  2853. }
  2854. else
  2855. {
  2856. //if this is a special account that we need to mark as such
  2857. if (bIsCritical)
  2858. {
  2859. pAcct->MarkCritical();
  2860. hr = HRESULT_FROM_WIN32(ERROR_SPECIAL_ACCOUNT);
  2861. }
  2862. }
  2863. }
  2864. else
  2865. {
  2866. // Sam Account name is not in the target domain and we have a conflict see if it is a CN conf
  2867. DWORD nPathLen = LEN_Path;
  2868. c_array<WCHAR> achPath(LEN_Path);
  2869. IADs * pAdsNew = NULL;
  2870. // Build the path to the target object
  2871. MakeFullyQualifiedAdsPath(achPath9, nPathLen, pOptions->tgtOUPath, pOptions->tgtDomain, pOptions->tgtNamingContext);
  2872. WCHAR * pRelativeTgtOUPath = wcschr(achPath9 + UStrLen(L"LDAP://") + 2,L'/');
  2873. if ( pRelativeTgtOUPath )
  2874. {
  2875. *pRelativeTgtOUPath = 0;
  2876. swprintf(achPath,L"%ls/%ls,%ls",(WCHAR*)achPath9,pAcct->GetTargetName(),pRelativeTgtOUPath+1);
  2877. }
  2878. temphr = ADsGetObject(achPath, IID_IADs, (void**) &pAdsNew);
  2879. if ( SUCCEEDED(temphr) )
  2880. {
  2881. // Object with that CN exists so we use it
  2882. BSTR sTgtPath;
  2883. HRESULT temphr = pAdsNew->get_ADsPath(&sTgtPath);
  2884. if (SUCCEEDED(temphr))
  2885. pAcct->SetTargetPath(sTgtPath);
  2886. else
  2887. pAcct->SetTargetPath(L"");
  2888. // Check if the object we are about to replace is of the same type.
  2889. BSTR sClass;
  2890. temphr = pAdsNew->get_Class(&sClass);
  2891. if ((SUCCEEDED(temphr)) && (!_wcsicmp(pAcct->GetType(), (WCHAR*)sClass)))
  2892. bIsDifferentType = FALSE;
  2893. else
  2894. bIsDifferentType = TRUE;
  2895. _variant_t varCritical;
  2896. temphr = pAdsNew->Get(L"isCriticalSystemObject", &varCritical);
  2897. if (SUCCEEDED(temphr))
  2898. bIsCritical = V_BOOL(&varCritical) == -1 ? TRUE : FALSE;
  2899. //if the source and target accounts are of different types we do not want to replace these.
  2900. if (bIsDifferentType)
  2901. {
  2902. hr = HRESULT_FROM_WIN32(ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH);
  2903. }
  2904. //else if not critical then fix the SAM name and other related chores
  2905. else if ( !bIsCritical )
  2906. {
  2907. //get the old Target Account Sam name
  2908. _variant_t varOldSAM = pAcct->GetTargetSam();
  2909. temphr = pAdsNew->Get(L"sAMAccountName", &varOldSAM);
  2910. // Set the Target Account Sam name
  2911. _variant_t varSAM = pAcct->GetTargetSam();
  2912. temphr = pAdsNew->Put(L"sAMAccountName", varSAM);
  2913. if (SUCCEEDED(temphr))
  2914. temphr = pAdsNew->SetInfo();
  2915. if ( FAILED(temphr) )
  2916. {
  2917. // The SAM rename failed due to conflicting SAM, do not migrate
  2918. err.MsgWrite(ErrE, DCT_MSG_SAM_RENAME_CONFLICT_SS, (WCHAR*)(varOldSAM.bstrVal), pAcct->GetTargetSam());
  2919. Mark(L"errors", pAcct->GetType());
  2920. return temphr;
  2921. }
  2922. setTargetPath.insert(pAcct);
  2923. // If the account is a group account and the Replace Existing members flag is set then we need to
  2924. // remove all the members of this group.
  2925. if ( (_wcsicmp(L"group", pAcct->GetType()) == 0 ) && (pOptions->flags & F_REMOVE_OLD_MEMBERS) )
  2926. RemoveMembers(pAcct, pOptions);
  2927. pAcct->MarkAlreadyThere();
  2928. pAcct->MarkReplaced();
  2929. }
  2930. else
  2931. {
  2932. //if this is a special account that we need to mark as such
  2933. if (bIsCritical)
  2934. {
  2935. pAcct->MarkCritical();
  2936. hr = HRESULT_FROM_WIN32(ERROR_SPECIAL_ACCOUNT);
  2937. }
  2938. }
  2939. }
  2940. else
  2941. {
  2942. // This should only happen if the replace fails because the object that already has
  2943. // this SAM Account Name is a special Win2K builtin object or container
  2944. // One example of this problem is "Service".
  2945. pAcct->SetStatus(pAcct->GetStatus()|AR_Status_Special);
  2946. err.SysMsgWrite(ErrE,ERROR_SPECIAL_ACCOUNT,DCT_MSG_REPLACE_FAILED_SD,pAcct->GetName(),ERROR_SPECIAL_ACCOUNT);
  2947. Mark(L"errors", pAcct->GetType());
  2948. }
  2949. }
  2950. pEnumMem->Release();
  2951. }
  2952. else
  2953. {
  2954. pAcct->MarkAlreadyThere();
  2955. err.MsgWrite(ErrW,DCT_MSG_ACCOUNT_EXISTS_S, pAcct->GetTargetSam());
  2956. Mark(L"warnings",pAcct->GetType());
  2957. // retrieve target path for account
  2958. // fix group membership expects target path to be set
  2959. // even for conflicting objects that were not replaced
  2960. HRESULT hrPath = pCont->GetObject(strClass, _bstr_t(achTarget), &pDisp);
  2961. if (SUCCEEDED(hrPath))
  2962. {
  2963. BSTR bstr;
  2964. IADsPtr spObject(pDisp);
  2965. hrPath = spObject->get_ADsPath(&bstr);
  2966. if (SUCCEEDED(hrPath))
  2967. {
  2968. pAcct->SetTargetPath(_bstr_t(bstr, false));
  2969. setTargetPath.insert(pAcct);
  2970. }
  2971. }
  2972. }
  2973. }
  2974. }
  2975. }
  2976. else
  2977. {
  2978. Mark(L"created", pAcct->GetType());
  2979. pAcct->MarkCreated();
  2980. BSTR sTgtPath = NULL;
  2981. HRESULT temphr = pAds->get_ADsPath(&sTgtPath);
  2982. if ( SUCCEEDED(temphr) )
  2983. {
  2984. pAcct->SetTargetPath(sTgtPath);
  2985. SysFreeString(sTgtPath);
  2986. setTargetPath.insert(pAcct);
  2987. }
  2988. else
  2989. pAcct->SetTargetPath(L"");
  2990. // Add computers to
  2991. if ( !_wcsicmp(strClass,L"computer") )
  2992. {
  2993. IADsGroupPtr pGroup = GetWellKnownTargetGroup(DOMAIN_COMPUTERS,pOptions);
  2994. if ( pGroup )
  2995. {
  2996. temphr = pGroup->Add(SysAllocString(pAcct->GetTargetPath()));
  2997. if ( SUCCEEDED(temphr) )
  2998. {
  2999. // if we successfully added the computer to Domain computers, now set Domain Computers as
  3000. // the primary group
  3001. temphr = pAds->Put(L"primaryGroupID",_variant_t(LONG(515)));
  3002. if ( SUCCEEDED(temphr) )
  3003. {
  3004. temphr = pAds->SetInfo();
  3005. }
  3006. if ( SUCCEEDED(hr) )
  3007. {
  3008. // if this worked, now we can remove the computer from Domain Users
  3009. pGroup = GetWellKnownTargetGroup(DOMAIN_USERS,pOptions);
  3010. if ( pGroup )
  3011. {
  3012. temphr = pGroup->Remove(SysAllocString(pAcct->GetTargetPath()));
  3013. }
  3014. }
  3015. }
  3016. }
  3017. }
  3018. }
  3019. }
  3020. else
  3021. {
  3022. // This is the No change mode. All we need to do here is to see if there might be a collision.
  3023. c_array<WCHAR> achPath(LEN_Path);
  3024. c_array<WCHAR> achPath9(LEN_Path);
  3025. DWORD nPathLen = LEN_Path;
  3026. c_array<WCHAR> achPathTmp(LEN_Path);
  3027. IADsPtr pAdsNew;
  3028. BOOL bConflict = FALSE;
  3029. /* see if the CN conflicts */
  3030. // Build the path to the target object
  3031. MakeFullyQualifiedAdsPath(achPath9, nPathLen, pOptions->tgtOUPath, pOptions->tgtDomain, pOptions->tgtNamingContext);
  3032. WCHAR * pRelativeTgtOUPath = wcschr(achPath9 + UStrLen(L"LDAP://") + 2,L'/');
  3033. if ( pRelativeTgtOUPath )
  3034. {
  3035. *pRelativeTgtOUPath = 0;
  3036. swprintf(achPathTmp,L"%ls/%ls,%ls",(WCHAR*)achPath9,pAcct->GetTargetName(),pRelativeTgtOUPath+1);
  3037. }
  3038. //
  3039. // check if object DN is conflicting with
  3040. // another object that is currently being migrated
  3041. //
  3042. pAcct->SetTargetPath(achPathTmp);
  3043. if (DoTargetPathConflict(setTargetPath, pAcct))
  3044. {
  3045. return HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS);
  3046. }
  3047. HRESULT temphr = ADsGetObject(achPathTmp, IID_IADs, (void**) &pAdsNew);
  3048. if (SUCCEEDED(temphr))
  3049. {
  3050. bConflict = TRUE;
  3051. }
  3052. /* if no CN conflict, see if the SAM conflicts */
  3053. if (!bConflict)
  3054. {
  3055. hr = LookupAccountInTarget(pOptions, const_cast<WCHAR*>(pAcct->GetTargetSam()), achPath);
  3056. if ( hr == S_OK )
  3057. bConflict = TRUE;
  3058. }
  3059. if (!bConflict)
  3060. {
  3061. // There is no such account on the target. We can go ahead and assume that it would have worked.
  3062. hr = S_OK;
  3063. Mark(L"created", pAcct->GetType());
  3064. pAcct->MarkCreated();
  3065. //if the UPN conflicted, post a message
  3066. if (pAcct->bUPNConflicted)
  3067. err.MsgWrite(ErrE, DCT_MSG_UPN_CONF, pAcct->GetTargetSam());
  3068. }
  3069. else
  3070. {
  3071. bConflict = FALSE; //reset the conflict flag
  3072. // there is a conflict. See if we need to add prefix or suffix. Or simply replace the account.
  3073. if ( wcslen(pOptions->prefix) > 0 )
  3074. {
  3075. // Prefix was specified so we need to try that.
  3076. c_array<WCHAR> achTgt(LEN_Path);
  3077. _variant_t varStr;
  3078. // Here I am adding a prefix and then lets see if we can setinfo that way
  3079. // find the '=' sign
  3080. wcscpy(achTgt, pAcct->GetTargetName());
  3081. for ( DWORD z = 0; z < wcslen(achTgt); z++ )
  3082. {
  3083. if ( achTgt[z] == L'=' ) break;
  3084. }
  3085. if ( z < wcslen(achTgt) )
  3086. {
  3087. // Get the prefix part ex.CN=
  3088. wcsncpy(achPref, achTgt, z+1);
  3089. achPref[z+1] = 0;
  3090. wcscpy(achSuf, achTgt+z+1);
  3091. }
  3092. // Build the target string with the Prefix
  3093. wsprintf(achTgt, L"%s%s%s", (WCHAR*)achPref, pOptions->prefix, (WCHAR*)achSuf);
  3094. pAcct->SetTargetName(achTgt);
  3095. // Build the target SAM name with the prefix.
  3096. wsprintf(achTgt, L"%s%s", pOptions->prefix, pAcct->GetTargetSam());
  3097. pAcct->SetTargetSam(achTgt);
  3098. //see if the CN still conflicts
  3099. swprintf(achPathTmp,L"%ls/%ls,%ls",(WCHAR*)achPath9,pAcct->GetTargetName(),pRelativeTgtOUPath+1);
  3100. //
  3101. // check if object DN is conflicting with
  3102. // another object that is currently being migrated
  3103. //
  3104. pAcct->SetTargetPath(achPathTmp);
  3105. if (DoTargetPathConflict(setTargetPath, pAcct))
  3106. {
  3107. return HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS);
  3108. }
  3109. temphr = ADsGetObject(achPathTmp, IID_IADs, (void**) &pAdsNew);
  3110. if (SUCCEEDED(temphr))
  3111. {
  3112. bConflict = TRUE;
  3113. }
  3114. //if no CN conflict, see if the SAM name conflicts
  3115. if (!bConflict)
  3116. {
  3117. hr = LookupAccountInTarget(pOptions, const_cast<WCHAR*>(pAcct->GetTargetSam()), achPath);
  3118. if ( hr == S_OK )
  3119. bConflict = TRUE;
  3120. }
  3121. if (!bConflict)
  3122. {
  3123. hr = 0;
  3124. Mark(L"created", strClass);
  3125. pAcct->MarkCreated();
  3126. }
  3127. else
  3128. {
  3129. hr = HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS);
  3130. pAcct->MarkAlreadyThere();
  3131. err.MsgWrite(ErrE, DCT_MSG_ACCOUNT_EXISTS_S, pAcct->GetTargetSam());
  3132. Mark(L"errors",pAcct->GetType());
  3133. }
  3134. //if the UPN conflicted, post a message
  3135. if (pAcct->bUPNConflicted)
  3136. err.MsgWrite(ErrE, DCT_MSG_UPN_CONF, pAcct->GetTargetSam());
  3137. }
  3138. else if ( wcslen(pOptions->suffix) > 0 )
  3139. {
  3140. // Suffix was specified so we will try that.
  3141. c_array<WCHAR> achTgt(LEN_Path);
  3142. _variant_t varStr;
  3143. // Here I am adding a prefix and then lets see if we can setinfo that way
  3144. wsprintf(achTgt, L"%s%s", pAcct->GetTargetName(), pOptions->suffix);
  3145. // Build the target SAM name with the prefix.
  3146. wsprintf(achTgt, L"%s%s", pAcct->GetTargetSam(), pOptions->suffix);
  3147. pAcct->SetTargetSam(achTgt);
  3148. //see if the CN still conflicts
  3149. swprintf(achPathTmp,L"%ls/%ls,%ls",(WCHAR*)achPath9,pAcct->GetTargetName(),pRelativeTgtOUPath+1);
  3150. //
  3151. // check if object DN is conflicting with
  3152. // another object that is currently being migrated
  3153. //
  3154. pAcct->SetTargetPath(achPathTmp);
  3155. if (DoTargetPathConflict(setTargetPath, pAcct))
  3156. {
  3157. return HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS);
  3158. }
  3159. temphr = ADsGetObject(achPathTmp, IID_IADs, (void**) &pAdsNew);
  3160. if (SUCCEEDED(temphr))
  3161. {
  3162. bConflict = TRUE;
  3163. }
  3164. //if no CN conflict, see if the SAM name conflicts
  3165. if (!bConflict)
  3166. {
  3167. hr = LookupAccountInTarget(pOptions, const_cast<WCHAR*>(pAcct->GetTargetSam()), achPath);
  3168. if ( hr == S_OK )
  3169. bConflict = TRUE;
  3170. }
  3171. if (!bConflict)
  3172. {
  3173. hr = 0;
  3174. Mark(L"created", strClass);
  3175. pAcct->MarkCreated();
  3176. }
  3177. else
  3178. {
  3179. hr = HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS);
  3180. pAcct->MarkAlreadyThere();
  3181. err.MsgWrite(ErrE, DCT_MSG_ACCOUNT_EXISTS_S, pAcct->GetTargetSam());
  3182. Mark(L"errors",pAcct->GetType());
  3183. }
  3184. //if the UPN conflicted, post a message
  3185. if (pAcct->bUPNConflicted)
  3186. err.MsgWrite(ErrE, DCT_MSG_UPN_CONF, pAcct->GetTargetSam());
  3187. }
  3188. else if (pOptions->flags & F_REPLACE)
  3189. {
  3190. // Replace the account.
  3191. hr = HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS);
  3192. }
  3193. else
  3194. {
  3195. // The account is already there and we really cant do anything about it. So tell the user.
  3196. hr = HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS);
  3197. pAcct->MarkAlreadyThere();
  3198. err.MsgWrite(ErrE, DCT_MSG_ACCOUNT_EXISTS_S, pAcct->GetTargetSam());
  3199. Mark(L"errors",pAcct->GetType());
  3200. }
  3201. }
  3202. pAcct->SetTargetPath(achPathTmp);
  3203. setTargetPath.insert(pAcct);
  3204. }
  3205. return hr;
  3206. }
  3207. //----------------------------------------------------------------------------
  3208. // DoTargetPathConflict
  3209. //
  3210. // Checks if object's target distinguished name conflicts with another object
  3211. // that is currently being migrated and has already been processed.
  3212. //
  3213. // If a conflict is detected the account node's operations bits are cleared to
  3214. // prevent any further processing of this object and an error message is
  3215. // logged.
  3216. //----------------------------------------------------------------------------
  3217. bool CAcctRepl::DoTargetPathConflict(CTargetPathSet& setTargetPath, TAcctReplNode* pAcct)
  3218. {
  3219. bool bConflict = false;
  3220. //
  3221. // If path conflicts are not to be ignored then check for a path conflict.
  3222. //
  3223. if (m_bIgnorePathConflict == false)
  3224. {
  3225. CTargetPathSet::iterator it = setTargetPath.find(pAcct);
  3226. if (it != setTargetPath.end())
  3227. {
  3228. pAcct->operations = 0;
  3229. err.MsgWrite(
  3230. ErrW,
  3231. DCT_MSG_OBJECT_RDN_CONFLICT_WITH_OTHER_CURRENT_OBJECT_SSS,
  3232. pAcct->GetSourcePath(),
  3233. pAcct->GetTargetName(),
  3234. (*it)->GetSourcePath()
  3235. );
  3236. Mark(L"errors", pAcct->GetType());
  3237. bConflict = true;
  3238. }
  3239. }
  3240. return bConflict;
  3241. }
  3242. // GetNamingAttribute Method
  3243. HRESULT CAcctRepl::GetNamingAttribute(LPCTSTR pszServer, LPCTSTR pszClass, SNamingAttribute& rNamingAttribute)
  3244. {
  3245. HRESULT hr = S_OK;
  3246. try
  3247. {
  3248. if (pszServer == NULL)
  3249. _com_issue_error(E_INVALIDARG);
  3250. wstring strClass = pszClass;
  3251. CNamingAttributeMap::iterator it = m_mapNamingAttribute.find(strClass);
  3252. if (it != m_mapNamingAttribute.end())
  3253. {
  3254. rNamingAttribute = it->second;
  3255. }
  3256. else
  3257. {
  3258. WCHAR szADsPath[LEN_Path];
  3259. DWORD dwArraySizeOfszADsPath = sizeof(szADsPath)/sizeof(szADsPath[0]);
  3260. // bind to rootDSE
  3261. IADsPtr spRootDSE;
  3262. if (wcslen(L"LDAP://") + wcslen(pszServer) + wcslen(L"/rootDSE") >= dwArraySizeOfszADsPath)
  3263. _com_issue_error(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
  3264. wcscpy(szADsPath, L"LDAP://");
  3265. wcscat(szADsPath, pszServer);
  3266. wcscat(szADsPath, L"/rootDSE");
  3267. CheckError(ADsGetObject(szADsPath, __uuidof(IADs), (VOID**)&spRootDSE));
  3268. // get schema naming context
  3269. VARIANT var;
  3270. CheckError(spRootDSE->Get(_bstr_t(L"schemaNamingContext"), &var));
  3271. _bstr_t strSchemaNamingContext = _variant_t(var, false);
  3272. // bind to schema's directory search interface
  3273. IDirectorySearchPtr spSearch;
  3274. wcscpy(szADsPath, L"LDAP://");
  3275. wcscat(szADsPath, strSchemaNamingContext);
  3276. CheckError(ADsGetObject(szADsPath, __uuidof(IDirectorySearch), (VOID**)&spSearch));
  3277. // search for inetOrgPerson class and retrieve rDNAttID attribute
  3278. ADS_SEARCH_HANDLE ashSearch = NULL;
  3279. LPWSTR pszAttributes[] = { L"rDNAttID" };
  3280. CheckError(spSearch->ExecuteSearch(
  3281. L"(&(objectClass=classSchema)(lDAPDisplayName=inetOrgPerson)(!isDefunct=TRUE))",
  3282. pszAttributes,
  3283. 1,
  3284. &ashSearch
  3285. ));
  3286. if (ashSearch)
  3287. {
  3288. wstring strAttribute;
  3289. hr = spSearch->GetFirstRow(ashSearch);
  3290. if (SUCCEEDED(hr))
  3291. {
  3292. ADS_SEARCH_COLUMN ascColumn;
  3293. hr = spSearch->GetColumn(ashSearch, L"rDNAttID", &ascColumn);
  3294. if (SUCCEEDED(hr))
  3295. {
  3296. if ((ascColumn.dwADsType == ADSTYPE_CASE_IGNORE_STRING) && (ascColumn.dwNumValues == 1))
  3297. {
  3298. strAttribute = ascColumn.pADsValues->CaseIgnoreString;
  3299. }
  3300. spSearch->FreeColumn(&ascColumn);
  3301. }
  3302. }
  3303. spSearch->CloseSearchHandle(ashSearch);
  3304. if (strAttribute.empty() == false)
  3305. {
  3306. // get attribute's minimum and maximum range values
  3307. wcscpy(szADsPath, L"LDAP://");
  3308. wcscat(szADsPath, pszServer);
  3309. wcscat(szADsPath, L"/schema/");
  3310. wcscat(szADsPath, strAttribute.c_str());
  3311. IADsPropertyPtr spProperty;
  3312. CheckError(ADsGetObject(szADsPath, __uuidof(IADsProperty), (VOID**)&spProperty));
  3313. long lMinRange;
  3314. long lMaxRange;
  3315. CheckError(spProperty->get_MinRange(&lMinRange));
  3316. CheckError(spProperty->get_MaxRange(&lMaxRange));
  3317. // set naming attribute info
  3318. rNamingAttribute.nMinRange = lMinRange;
  3319. rNamingAttribute.nMaxRange = lMaxRange;
  3320. rNamingAttribute.strName = strAttribute;
  3321. // save naming attribute and range values in cache
  3322. m_mapNamingAttribute.insert(CNamingAttributeMap::value_type(strClass, SNamingAttribute(lMinRange, lMaxRange, strAttribute)));
  3323. }
  3324. else
  3325. {
  3326. hr = E_FAIL;
  3327. }
  3328. }
  3329. else
  3330. {
  3331. hr = E_FAIL;
  3332. }
  3333. }
  3334. }
  3335. catch (_com_error& ce)
  3336. {
  3337. hr = ce.Error();
  3338. }
  3339. return hr;
  3340. }
  3341. void VariantSidToString(_variant_t & varSid)
  3342. {
  3343. if ( varSid.vt == VT_BSTR )
  3344. {
  3345. return;
  3346. }
  3347. else if ( varSid.vt == ( VT_ARRAY | VT_UI1) )
  3348. {
  3349. // convert the array of bits to a string
  3350. CLdapConnection c;
  3351. LPBYTE pByte = NULL;
  3352. WCHAR str[LEN_Path];
  3353. SafeArrayAccessData(varSid.parray,(void**)&pByte);
  3354. c.BytesToString(pByte,str,GetLengthSid(pByte));
  3355. SafeArrayUnaccessData(varSid.parray);
  3356. varSid = SysAllocString(str);
  3357. }
  3358. else
  3359. {
  3360. varSid.ChangeType(VT_BSTR);
  3361. }
  3362. }
  3363. HRESULT CAcctRepl::UpdateGroupMembership(
  3364. Options * pOptions, //in -Options set by the user
  3365. TNodeListSortable * acctlist, //in -List of all accounts being copied
  3366. ProgressFn * progress, //in -Progress update
  3367. IStatusObj * pStatus //in -Status update
  3368. )
  3369. {
  3370. IVarSetPtr pVs(__uuidof(VarSet));
  3371. MCSDCTWORKEROBJECTSLib::IAccessCheckerPtr pAccess(__uuidof(MCSDCTWORKEROBJECTSLib::AccessChecker));
  3372. WCHAR sTgtPath[LEN_Path];
  3373. IIManageDBPtr pDB = pOptions->pDb;
  3374. IUnknown* pUnk = NULL;
  3375. TNodeTreeEnum tenum;
  3376. HRESULT hr = S_OK;
  3377. DWORD ret = 0;
  3378. bool bFoundGroups = false;
  3379. WCHAR sDomain[LEN_Path];
  3380. DWORD grpType = 0;
  3381. IUnknownPtr spUnknown(pVs);
  3382. pUnk = spUnknown;
  3383. // sort the account list by Source Type\Source Sam Name
  3384. if ( acctlist->IsTree() ) acctlist->ToSorted();
  3385. acctlist->SortedToScrambledTree();
  3386. acctlist->Sort(&TNodeCompareAccountSam);
  3387. acctlist->Balance();
  3388. for (TAcctReplNode* acct = (TAcctReplNode *)tenum.OpenFirst(acctlist) ; acct ; acct = (TAcctReplNode *)tenum.Next())
  3389. {
  3390. if ( !acct->ProcessMem() )
  3391. continue;
  3392. if ( pStatus )
  3393. {
  3394. LONG status = 0;
  3395. HRESULT hr = pStatus->get_Status(&status);
  3396. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  3397. {
  3398. if ( !bAbortMessageWritten )
  3399. {
  3400. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  3401. bAbortMessageWritten = true;
  3402. }
  3403. break;
  3404. }
  3405. }
  3406. // Since the list is sorted by account type we can continue to ignore everything till we get to the
  3407. // group type and once it is found and processed the rest of types can be ignored
  3408. if ( _wcsicmp(acct->GetType(), L"group") != 0 )
  3409. {
  3410. if ( !bFoundGroups )
  3411. continue;
  3412. else
  3413. break;
  3414. }
  3415. else
  3416. {
  3417. bFoundGroups = true;
  3418. }
  3419. // If we are here this must be a group type so tell the progrss function what we are doing
  3420. WCHAR mesg[LEN_Path];
  3421. bool bGotPrimaryGroups = false;
  3422. wsprintf(mesg, GET_STRING(IDS_UPDATING_GROUP_MEMBERSHIPS_S), acct->GetName());
  3423. if ( progress )
  3424. progress(mesg);
  3425. if ( acct->CreateAccount() && (!acct->WasCreated() && !acct->WasReplaced()) )
  3426. // if the account was not copied then why should we even process it?
  3427. // Bad idea. We need to process the account membership because the group may have been previously copied and
  3428. // in this run we simply need to update the membership. Changing the expansion code to mark the account as created.
  3429. // that should fix the problem.
  3430. continue;
  3431. if ( !_wcsicmp(acct->GetType(), L"group") && *acct->GetTargetPath() )
  3432. {
  3433. IADsGroupPtr spSourceGroup;
  3434. IADsGroupPtr spTargetGroup;
  3435. IADsMembersPtr spMembers;
  3436. IEnumVARIANTPtr spEnum;
  3437. err.MsgWrite(0, DCT_MSG_PROCESSING_GROUP_MEMBER_S, (WCHAR*) acct->GetTargetName());
  3438. if ( !pOptions->nochange )
  3439. {
  3440. hr = ADsGetObject(const_cast<WCHAR*>(acct->GetTargetPath()), IID_IADsGroup, (void**) &spTargetGroup);
  3441. if (FAILED(hr))
  3442. {
  3443. err.SysMsgWrite(ErrE, hr, DCT_MSG_OBJECT_NOT_FOUND_SSD, acct->GetTargetPath(), pOptions->tgtDomain, hr );
  3444. Mark(L"errors", acct->GetType());
  3445. continue; // we cant possibly do any thing without the source group
  3446. }
  3447. }
  3448. else
  3449. hr = S_OK;
  3450. if ( spTargetGroup )
  3451. {
  3452. VARIANT var;
  3453. VariantInit(&var);
  3454. hr = spTargetGroup->Get(L"groupType", &var);
  3455. if (SUCCEEDED(hr))
  3456. grpType = long(_variant_t(var, false));
  3457. }
  3458. hr = ADsGetObject(const_cast<WCHAR*>(acct->GetSourcePath()), IID_IADsGroup, (void**) &spSourceGroup);
  3459. if (FAILED(hr))
  3460. {
  3461. err.SysMsgWrite(ErrE, 0, DCT_MSG_OBJECT_NOT_FOUND_SSD, acct->GetSourcePath(), pOptions->srcDomain, hr );
  3462. Mark(L"errors", acct->GetType());
  3463. continue; // we cant possibly do any thing for this group without the target group
  3464. }
  3465. // Now we get the members interface.
  3466. hr = spSourceGroup->Members(&spMembers);
  3467. // Ask for an enumeration of the members
  3468. if ( SUCCEEDED(hr) )
  3469. hr = spMembers->get__NewEnum((IUnknown **)&spEnum);
  3470. // If unable to retrieve enumerator then generate error message.
  3471. if (FAILED(hr))
  3472. {
  3473. err.SysMsgWrite(ErrE, hr, DCT_MSG_UNABLE_TO_ENUM_MEMBERS_S, acct->GetSourcePath());
  3474. Mark(L"errors", acct->GetType());
  3475. continue;
  3476. }
  3477. VARIANT varMembers;
  3478. VariantInit(&varMembers);
  3479. // Now enumerate through all the objects in the Group
  3480. while ( SUCCEEDED(spEnum->Next(1, &varMembers, &ret)) )
  3481. {
  3482. _variant_t vntMembers(varMembers, false);
  3483. IADsPtr spADs;
  3484. _bstr_t strClass;
  3485. _bstr_t strPath;
  3486. _bstr_t strSam;
  3487. PSID pSid = NULL;
  3488. // Check if user wants to abort the operation
  3489. if ( pOptions->pStatus )
  3490. {
  3491. LONG status = 0;
  3492. HRESULT hr = pOptions->pStatus->get_Status(&status);
  3493. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  3494. {
  3495. if ( !bAbortMessageWritten )
  3496. {
  3497. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  3498. bAbortMessageWritten = true;
  3499. }
  3500. break;
  3501. }
  3502. }
  3503. // If no values are returned that means we are done with all members
  3504. if ( ret == 0 || vntMembers.vt == VT_EMPTY)
  3505. {
  3506. if ( bGotPrimaryGroups )
  3507. break;
  3508. else
  3509. {
  3510. // Go through and add all the users that have this group as their primary group.
  3511. bGotPrimaryGroups = true;
  3512. //
  3513. // It is only necessary to query objects that have their primary group ID equal to the
  3514. // current group for W2K and later as NT4 required that the account be a member of the
  3515. // global group in order to set the primary group ID equal to the group. As the members
  3516. // of the group have already been queried it would be redundant to query for objects
  3517. // that have their primary group ID equal to the current group.
  3518. //
  3519. if (pOptions->srcDomainVer >= 5)
  3520. {
  3521. hr = GetThePrimaryGroupMembers(pOptions, const_cast<WCHAR*>(acct->GetSourceSam()), &spEnum);
  3522. if (SUCCEEDED(hr))
  3523. continue;
  3524. else
  3525. break;
  3526. }
  3527. else
  3528. {
  3529. break;
  3530. }
  3531. }
  3532. }
  3533. // Depending on what we are looking at we get two variant types. In case of members we get
  3534. // IDispatch pointer in a variant. In case of primary group members we get variant(bstr) array
  3535. // So we need to branch here depending on what we get
  3536. if ( bGotPrimaryGroups )
  3537. {
  3538. // first element is the ADsPath of the object so use that to get the object and continue
  3539. if ( vntMembers.vt == (VT_ARRAY | VT_VARIANT) )
  3540. {
  3541. SAFEARRAY * pArray = vntMembers.parray;
  3542. VARIANT * pDt;
  3543. hr = SafeArrayAccessData(pArray, (void**) &pDt);
  3544. if (SUCCEEDED(hr))
  3545. {
  3546. if ( pDt[0].vt == VT_BSTR )
  3547. hr = ADsGetObject((WCHAR*)pDt[0].bstrVal, IID_IADs, (void**) &spADs);
  3548. else
  3549. hr = E_FAIL;
  3550. SafeArrayUnaccessData(pArray);
  3551. }
  3552. vntMembers.Clear();
  3553. }
  3554. else
  3555. hr = E_FAIL;
  3556. }
  3557. else
  3558. {
  3559. // We have a dispatch pointer in the VARIANT so we will get the IADs pointer to it and
  3560. // then get the ADs path to that object and then remove it from the group
  3561. spADs = IDispatchPtr(vntMembers);
  3562. if (spADs)
  3563. {
  3564. hr = S_OK;
  3565. }
  3566. else
  3567. {
  3568. hr = E_FAIL;
  3569. }
  3570. }
  3571. if ( SUCCEEDED(hr) )
  3572. {
  3573. BSTR bstr = NULL;
  3574. hr = spADs->get_ADsPath(&bstr);
  3575. if ( SUCCEEDED(hr) )
  3576. {
  3577. strPath = _bstr_t(bstr, false);
  3578. // Parse out the domain name from the WinNT path.
  3579. if ( !wcsncmp(L"WinNT://", (WCHAR*)strPath, 8) )
  3580. {
  3581. //Grab the domain name from the WinNT path.
  3582. WCHAR sTemp[LEN_Path];
  3583. WCHAR * p = strPath;
  3584. wcscpy(sTemp, p+8);
  3585. p = wcschr(sTemp, L'/');
  3586. if ( p )
  3587. *p = L'\0';
  3588. else
  3589. {
  3590. //we have the path in this format "WinNT://S-1-5....."
  3591. // in this case we need to get the SID and then try and get its domain and account name
  3592. PSID pSid = NULL;
  3593. WCHAR sName[255];
  3594. DWORD rc = 1;
  3595. pSid = SidFromString(sTemp);
  3596. if ( pSid )
  3597. {
  3598. rc = GetName(pSid, sName, sTemp);
  3599. if ( !rc )
  3600. {
  3601. // Give it a winnt path. This way we get the path that we can use
  3602. strPath = _bstr_t(L"WinNT://") + sTemp + _bstr_t(L"/") + sName;
  3603. }
  3604. FreeSid(pSid);
  3605. }
  3606. if ( rc )
  3607. {
  3608. // Log a message that we cant resolve this guy
  3609. err.SysMsgWrite(ErrE, rc, DCT_MSG_PATH_NOT_RESOLVED_SD, sTemp, rc);
  3610. Mark("errors", acct->GetType());
  3611. continue;
  3612. }
  3613. }
  3614. wcscpy(sDomain, sTemp);
  3615. }
  3616. else
  3617. {
  3618. // Get the domain name from the LDAP path. Convert domain name to the NETBIOS name.
  3619. _bstr_t strDomainDns = GetDomainDNSFromPath(strPath);
  3620. if (strDomainDns.length())
  3621. {
  3622. safecopy(sDomain, (WCHAR*)strDomainDns);
  3623. }
  3624. else
  3625. {
  3626. hr = E_FAIL;
  3627. }
  3628. }
  3629. }
  3630. if ( SUCCEEDED(hr) )
  3631. {
  3632. if ( !(acct->GetGroupType() & ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP) )
  3633. {
  3634. // Global/Universal groups are easy all we have to do is use the path we got back and get the info from that object
  3635. BSTR bstr = NULL;
  3636. hr = spADs->get_Class(&bstr);
  3637. if (SUCCEEDED(hr))
  3638. strClass = _bstr_t(bstr, false);
  3639. else
  3640. strClass = L"";
  3641. VARIANT var;
  3642. VariantInit(&var);
  3643. hr = spADs->Get(L"samAccountName", &var);
  3644. if ( SUCCEEDED(hr) )
  3645. strSam = _variant_t(var, false);
  3646. else
  3647. {
  3648. // make sure it is a WinNT:// path
  3649. if ( !wcsncmp((WCHAR*)strPath, L"WinNT://", 8) )
  3650. {
  3651. BSTR bstr = NULL;
  3652. hr = spADs->get_Name(&bstr);
  3653. if (SUCCEEDED(hr))
  3654. strSam = _bstr_t(bstr, false);
  3655. }
  3656. }
  3657. //if universal group change domain if foreign security principal
  3658. if ((acct->GetGroupType() & ADS_GROUP_TYPE_UNIVERSAL_GROUP))
  3659. {
  3660. _bstr_t sTempDomain = GetDomainOfMigratedForeignSecPrincipal(spADs, strPath);
  3661. if (sTempDomain.length())
  3662. wcscpy(sDomain, sTempDomain);
  3663. }
  3664. }
  3665. else
  3666. {
  3667. // Local group we need to get the SID LDAP path and then use that to add the account to the group.
  3668. WCHAR sSidDomain[LEN_Path];
  3669. WCHAR sSidPath[LEN_Path];
  3670. WCHAR sSamName[LEN_Path];
  3671. if ( pSid )
  3672. {
  3673. FreeSid(pSid);
  3674. pSid = NULL;
  3675. }
  3676. hr = BuildSidPath(spADs, sSidPath, sSamName, sSidDomain, pOptions,&pSid);
  3677. if (SUCCEEDED(hr))
  3678. {
  3679. _bstr_t sTempDomain = GetDomainOfMigratedForeignSecPrincipal(spADs, strPath);
  3680. if (sTempDomain.length())
  3681. wcscpy(sDomain, sTempDomain);
  3682. strPath = sSidPath;
  3683. strSam = sSamName;
  3684. }
  3685. else
  3686. {
  3687. err.SysMsgWrite(ErrW, hr, DCT_MSG_FAILED_TO_ADD_TO_GROUP_SSD, (WCHAR*)strPath, acct->GetTargetName(), hr);
  3688. Mark(L"warnings", acct->GetType());
  3689. }
  3690. }
  3691. }
  3692. }
  3693. if ( SUCCEEDED(hr) )
  3694. {
  3695. // Now that we have the SamAccountname and the path we can lookup the info from the DB
  3696. hr = pDB->GetAMigratedObject(strSam, sDomain, pOptions->tgtDomain, &pUnk);
  3697. if ( pOptions->nochange )
  3698. {
  3699. WCHAR targetPath[LEN_Path];
  3700. // in this case the account was not really copied so we need to make sure that we
  3701. // we include the accounts that would have been added if this was a true migration.
  3702. Lookup p;
  3703. p.pName = strSam;
  3704. p.pType = strClass;
  3705. TAcctReplNode * pNode = (TAcctReplNode *) acctlist->Find(&TNodeFindAccountName, &p);
  3706. if (pNode)
  3707. {
  3708. pVs->put(L"MigratedObjects.TargetSamName", _variant_t(pNode->GetTargetSam()));
  3709. BuildTargetPath(pNode->GetTargetName(), pOptions->tgtOUPath, targetPath);
  3710. pVs->put(L"MigratedObjects.TargetAdsPath", _variant_t(targetPath));
  3711. hr = S_OK;
  3712. }
  3713. }
  3714. if ( hr == S_OK )
  3715. {
  3716. VerifyAndUpdateMigratedTarget(pOptions, pVs);
  3717. // Since we have previously copied the account we can simply add the one that we copied.
  3718. _bstr_t strTargetPath = pVs->get(L"MigratedObjects.TargetAdsPath");
  3719. if ( strTargetPath.length() )
  3720. {
  3721. if ( !pOptions->nochange )
  3722. hr = spTargetGroup->Add(strTargetPath);
  3723. else
  3724. hr = S_OK;
  3725. if ( SUCCEEDED(hr) )
  3726. {
  3727. err.MsgWrite(0, DCT_MSG_ADDED_TO_GROUP_S, (WCHAR*)strTargetPath);
  3728. //if this is not a global group, remove the source account from the group, if there
  3729. if (!pOptions->nochange && !(acct->GetGroupType() & ADS_GROUP_TYPE_GLOBAL_GROUP))
  3730. RemoveSourceAccountFromGroup(spTargetGroup, pVs, pOptions);
  3731. }
  3732. else
  3733. {
  3734. hr = BetterHR(hr);
  3735. switch ( HRESULT_CODE(hr) )
  3736. {
  3737. case NERR_UserNotFound:
  3738. case 0x5000:
  3739. err.SysMsgWrite(0, hr, DCT_MSG_MEMBER_NONEXIST_SS, (WCHAR *)strTargetPath, acct->GetTargetName(), hr);
  3740. break;
  3741. default:
  3742. {
  3743. err.SysMsgWrite(ErrW, hr, DCT_MSG_FAILED_TO_ADD_TO_GROUP_SSD, (WCHAR *)strTargetPath, acct->GetTargetName(), hr);
  3744. Mark(L"warnings", acct->GetType());
  3745. }
  3746. }
  3747. }
  3748. }
  3749. }
  3750. else
  3751. {
  3752. // We have not migrated the accounts from source domain to the target domain.
  3753. // so we now have to branch for different group types.
  3754. WCHAR domain[LEN_Path];
  3755. DWORD cbDomain = DIM(domain);
  3756. SID_NAME_USE use;
  3757. if ( grpType & ADS_GROUP_TYPE_GLOBAL_GROUP )
  3758. {
  3759. // For the global groups we simply say that account has not been migrated.
  3760. err.MsgWrite(0, DCT_MSG_MEMBER_NONEXIST_SS, (WCHAR*)strSam, acct->GetTargetName());
  3761. }
  3762. else
  3763. {
  3764. //Process local/universal groups ( can add objects from non-target domains )
  3765. // 1. See if we have migrated this account to some other domain.
  3766. // 2. Is the Source accounts SID valid here (trust) if so add that.
  3767. // 3. See if we can find an account with the same name in the target.
  3768. // if any of these operations yield a valid account then just add it.
  3769. // we are going to lookup migrated objects table to find migration of this object
  3770. // from source domain to any other domain.
  3771. hr = pDB->raw_GetAMigratedObjectToAnyDomain(strSam, sDomain, &pUnk);
  3772. if ( hr == S_OK )
  3773. {
  3774. // we have migrated the object to some other domain. So we will get the path to that object and try to add it to the group
  3775. // it may fail if there is no trust/forest membership of the target domain and the domain that this object resides in.
  3776. _bstr_t strTargetPath = pVs->get(L"MigratedObjects.TargetAdsPath");
  3777. if ( strTargetPath.length() )
  3778. {
  3779. // Since the object is in a different domain, we will have to get the SID of the object,
  3780. // and use that for the Add
  3781. IADsPtr spAds;
  3782. _variant_t varSid;
  3783. hr = ADsGetObject(strTargetPath,IID_IADs,(void**)&spAds);
  3784. if ( SUCCEEDED(hr) )
  3785. {
  3786. VARIANT var;
  3787. VariantInit(&var);
  3788. hr = spAds->Get(L"objectSid",&var);
  3789. if (SUCCEEDED(hr))
  3790. varSid = _variant_t(var, false);
  3791. spAds.Release();
  3792. }
  3793. if ( SUCCEEDED(hr) )
  3794. {
  3795. // Make sure the SID we got was in string format
  3796. VariantSidToString(varSid);
  3797. UStrCpy(sTgtPath,L"LDAP://<SID=");
  3798. UStrCpy(sTgtPath + UStrLen(sTgtPath),varSid.bstrVal);
  3799. UStrCpy(sTgtPath + UStrLen(sTgtPath),L">");
  3800. if ( !pOptions->nochange )
  3801. hr = spTargetGroup->Add(sTgtPath);
  3802. else
  3803. hr = S_OK;
  3804. }
  3805. if ( SUCCEEDED(hr) )
  3806. {
  3807. err.MsgWrite(0, DCT_MSG_ADDED_TO_GROUP_S, (WCHAR*)strTargetPath);
  3808. //remove the source account from the group, if there
  3809. if (!pOptions->nochange)
  3810. {
  3811. RemoveSourceAccountFromGroup(spTargetGroup, pVs, pOptions);
  3812. }
  3813. }
  3814. else
  3815. {
  3816. hr = BetterHR(hr);
  3817. if ( HRESULT_CODE(hr) == NERR_UserExists )
  3818. {
  3819. err.MsgWrite(0,DCT_MSG_USER_IN_GROUP_SS,(WCHAR*)strTargetPath,acct->GetTargetName());
  3820. }
  3821. else if ( HRESULT_CODE(hr) == NERR_UserNotFound )
  3822. {
  3823. err.SysMsgWrite(0, hr, DCT_MSG_MEMBER_NONEXIST_SS, (WCHAR*)strTargetPath, acct->GetTargetName(), hr);
  3824. }
  3825. else
  3826. {
  3827. // message for the generic failure case
  3828. err.SysMsgWrite(ErrW, hr, DCT_MSG_FAILED_TO_ADD_TO_GROUP_SSD, (WCHAR*)strTargetPath, acct->GetTargetName(), hr);
  3829. Mark(L"warnings", acct->GetType());
  3830. }
  3831. }
  3832. }
  3833. }
  3834. else
  3835. {
  3836. // we have never migrated this account. So we will try to add the original account to the target domain.
  3837. // This would work if the target domain and the domain where this object is satisfy the requirements of
  3838. // forest membership/ trusts imposed by Universal/Local groups respectively.
  3839. // Get the sid of the source account
  3840. _variant_t varSid;
  3841. // check whether the target domain knows this sid
  3842. // Before we try to add, make sure the target domain knows this account
  3843. WCHAR name[LEN_Path];
  3844. DWORD lenName = DIM(name);
  3845. cbDomain = DIM(domain);
  3846. if ( grpType & ADS_GROUP_TYPE_UNIVERSAL_GROUP )
  3847. {
  3848. // in case of the Universal group we need to make sure that domains are in
  3849. // the same forest. We will use access checker for this
  3850. BOOL bIsSame = FALSE;
  3851. _bstr_t sSrcDomainDNS = GetDomainDNSFromPath(strPath);
  3852. hr = pAccess->raw_IsInSameForest(pOptions->tgtDomainDns, sSrcDomainDNS, (long*)&bIsSame);
  3853. if ( SUCCEEDED(hr) && bIsSame )
  3854. {
  3855. // We have accounts that are in same forest so we can simply add the account.
  3856. if ( !pOptions->nochange )
  3857. hr = spTargetGroup->Add(strPath);
  3858. else
  3859. hr = S_OK;
  3860. if ( SUCCEEDED(hr) )
  3861. {
  3862. WCHAR sWholeName[LEN_Path];
  3863. wcscpy(sWholeName, sSrcDomainDNS);
  3864. wcscat(sWholeName, L"\\");
  3865. wcscat(sWholeName, !strSam ? L"" : strSam);
  3866. err.MsgWrite(0, DCT_MSG_ADDED_TO_GROUP_S, sWholeName);
  3867. }
  3868. else
  3869. {
  3870. hr = BetterHR(hr);
  3871. err.SysMsgWrite(ErrW, hr, DCT_MSG_FAILED_TO_ADD_TO_GROUP_SSD, (WCHAR*) strSam, acct->GetTargetName(), hr);
  3872. Mark(L"warnings", acct->GetType());
  3873. }
  3874. }
  3875. else
  3876. {
  3877. err.MsgWrite(ErrW, DCT_MSG_CANNOT_ADD_OBJECTS_NOT_IN_FOREST_TO_GROUP_SS, (WCHAR*)strSam, acct->GetTargetName());
  3878. Mark(L"warnings", acct->GetType());
  3879. }
  3880. }
  3881. else
  3882. {
  3883. if ( !pOptions->nochange )
  3884. hr = spTargetGroup->Add(strPath);
  3885. else
  3886. hr = S_OK;
  3887. // In case of local groups If we know the SID in the target domain then we can simply
  3888. // add that account to the target group
  3889. if ( LookupAccountSid(pOptions->tgtComp,pSid,name,&lenName,domain,&cbDomain,&use) )
  3890. {
  3891. WCHAR sWholeName[LEN_Path];
  3892. wcscpy(sWholeName, domain);
  3893. wcscat(sWholeName, L"\\");
  3894. wcscat(sWholeName, !strSam ? L"" : strSam);
  3895. err.MsgWrite(0, DCT_MSG_ADDED_TO_GROUP_S, sWholeName);
  3896. }
  3897. else
  3898. {
  3899. // log the fact that the SID could not be resolved in the target domain
  3900. // this will happen when the target domain does not trust the source domain
  3901. WCHAR sWholeName[LEN_Path];
  3902. wcscpy(sWholeName, sDomain);
  3903. wcscat(sWholeName, L"\\");
  3904. wcscat(sWholeName, !strSam ? L"" : strSam);
  3905. err.MsgWrite(0, DCT_MSG_CANNOT_RESOLVE_SID_IN_TARGET_SS, sWholeName, acct->GetTargetName(), HRESULT_FROM_WIN32(GetLastError()));
  3906. }
  3907. }
  3908. }
  3909. } // if group type
  3910. } // if not migrated to the target domain.
  3911. } // if can get to the member.
  3912. if( pSid )
  3913. FreeSid(pSid);
  3914. } //while
  3915. }
  3916. }
  3917. return hr;
  3918. }
  3919. HRESULT CAcctRepl::LookupAccountInTarget(Options * pOptions, WCHAR * sSam, WCHAR * sPath)
  3920. {
  3921. if ( pOptions->tgtDomainVer < 5 )
  3922. {
  3923. // for NT4 we can just build the path and send it back.
  3924. wsprintf(sPath, L"WinNT://%s/%s", pOptions->tgtDomain, sSam);
  3925. return S_OK;
  3926. }
  3927. // Use the net object enumerator to lookup the account in the target domain.
  3928. INetObjEnumeratorPtr pQuery(__uuidof(NetObjEnumerator));
  3929. IEnumVARIANT * pEnum = NULL;
  3930. SAFEARRAYBOUND bd = { 1, 0 };
  3931. SAFEARRAY * pszColNames;
  3932. BSTR HUGEP * pData = NULL;
  3933. LPWSTR sData[] = { L"aDSPath" };
  3934. WCHAR sQuery[LEN_Path];
  3935. WCHAR sDomPath[LEN_Path];
  3936. DWORD ret = 0;
  3937. _variant_t var, varVal;
  3938. HRESULT hr = S_OK;
  3939. wsprintf(sDomPath, L"LDAP://%s/%s", pOptions->tgtDomainDns, pOptions->tgtNamingContext);
  3940. WCHAR sTempSamName[LEN_Path];
  3941. wcscpy(sTempSamName, sSam);
  3942. if ( sTempSamName[0] == L' ' )
  3943. {
  3944. WCHAR sTemp[LEN_Path];
  3945. wsprintf(sTemp, L"\\20%s", sTempSamName + 1);
  3946. wcscpy(sTempSamName, sTemp);
  3947. }
  3948. wsprintf(sQuery, L"(sAMAccountName=%s)", sTempSamName);
  3949. hr = pQuery->raw_SetQuery(sDomPath, pOptions->tgtDomain, sQuery, ADS_SCOPE_SUBTREE, FALSE);
  3950. // Set up the columns that we want back from the query ( in this case we need SAM accountname )
  3951. pszColNames = ::SafeArrayCreate(VT_BSTR, 1, &bd);
  3952. hr = ::SafeArrayAccessData(pszColNames, (void HUGEP **)&pData);
  3953. if ( SUCCEEDED(hr) )
  3954. pData[0] = SysAllocString(sData[0]);
  3955. if ( SUCCEEDED(hr) )
  3956. hr = ::SafeArrayUnaccessData(pszColNames);
  3957. if ( SUCCEEDED(hr) )
  3958. hr = pQuery->raw_SetColumns(pszColNames);
  3959. // Time to execute the plan.
  3960. if ( SUCCEEDED(hr) )
  3961. hr = pQuery->raw_Execute(&pEnum);
  3962. if ( SUCCEEDED(hr) )
  3963. {
  3964. // if this worked that means we can only have one thing in the result.
  3965. if ( (pEnum->Next(1, &var, &ret) == S_OK) && ( ret > 0 ) )
  3966. {
  3967. SAFEARRAY * pArray = var.parray;
  3968. long ndx = 0;
  3969. hr = SafeArrayGetElement(pArray,&ndx,&varVal);
  3970. if ( SUCCEEDED(hr) )
  3971. wcscpy(sPath, (WCHAR*)varVal.bstrVal);
  3972. else
  3973. hr = HRESULT_FROM_WIN32(NERR_UserNotFound);
  3974. }
  3975. else
  3976. hr = HRESULT_FROM_WIN32(NERR_UserNotFound);
  3977. }
  3978. if ( pEnum ) pEnum->Release();
  3979. return hr;
  3980. }
  3981. //----------------------------------------------------------------------------
  3982. // RemoveMembers : This function enumerates through all the members of the
  3983. // given group and removes them one at a time.
  3984. //----------------------------------------------------------------------------
  3985. HRESULT CAcctRepl::RemoveMembers(
  3986. TAcctReplNode * pAcct, //in- AccountReplicator Node with the Account info
  3987. Options * pOptions //in- Options set by the user.
  3988. )
  3989. {
  3990. IADsMembers * pMem = NULL;
  3991. IADs * pAds = NULL;
  3992. IADsGroup * pGrp = NULL;
  3993. // IUnknown * pUnk;
  3994. IEnumVARIANT * pVar = NULL;
  3995. IDispatch * pDisp = NULL;
  3996. DWORD ret = 0;
  3997. _variant_t var;
  3998. WCHAR * sPath;
  3999. // First we make sure that this is really a group otherwise we ignore it.
  4000. if (_wcsicmp((WCHAR*)pAcct->GetType(),L"group"))
  4001. return S_OK;
  4002. // Lets get a IADsGroup * to the group object.
  4003. HRESULT hr = ADsGetObject(const_cast<WCHAR*>(pAcct->GetTargetPath()), IID_IADsGroup, (void **) &pGrp);
  4004. // Now we get the members interface.
  4005. if ( SUCCEEDED(hr) )
  4006. hr = pGrp->Members(&pMem);
  4007. // Ask for an enumeration of the members
  4008. if ( SUCCEEDED(hr) )
  4009. hr = pMem->get__NewEnum((IUnknown **)&pVar);
  4010. // Now enumerate through all the objects in the Group and for each one remove it from the group
  4011. while ( SUCCEEDED(pVar->Next(1, &var, &ret)) )
  4012. {
  4013. // If no values are returned that means we are done with all members so break out of this loop
  4014. if ( ret == 0 )
  4015. break;
  4016. // We hace a dispatch pointer in the VARIANT so we will get the IADs pointer to it and
  4017. // then get the ADs path to that object and then remove it from the group
  4018. pDisp = V_DISPATCH(&var);
  4019. hr = pDisp->QueryInterface(IID_IADs, (void**) &pAds);
  4020. if ( SUCCEEDED(hr) )
  4021. hr = pAds->get_ADsPath(&sPath);
  4022. if ( pAds ) pAds->Release();
  4023. if ( SUCCEEDED(hr) )
  4024. {
  4025. _bstr_t bstrPath(sPath);
  4026. if ( !pOptions->nochange )
  4027. hr = pGrp->Remove(bstrPath);
  4028. }
  4029. var.Clear();
  4030. }
  4031. if ( pMem ) pMem->Release();
  4032. if ( pGrp ) pGrp->Release();
  4033. if ( pVar ) pVar->Release();
  4034. return hr;
  4035. }
  4036. //----------------------------------------------------------------------------
  4037. // FillPathInfo : This function looks up the ADs path from the source domain
  4038. // for a given SAMAccountName
  4039. //----------------------------------------------------------------------------
  4040. bool CAcctRepl::FillPathInfo(
  4041. TAcctReplNode * pAcct, //in- AccountReplicator Node with the Account info
  4042. Options * pOptions //in- Options set by the user.
  4043. )
  4044. {
  4045. wstring sPath;
  4046. _bstr_t sTgtPath;
  4047. // Fill the naming context for the domains. If the Naming context does not work then it is not a Win2kDomain
  4048. // so we need to stop right here.
  4049. if ( wcslen(pOptions->srcNamingContext) == 0 )
  4050. FillNamingContext(pOptions);
  4051. if ( wcslen(pOptions->srcNamingContext) == 0 )
  4052. {
  4053. // this is probably an NT 4 source domain
  4054. // construct the source path
  4055. if ( ! *pAcct->GetSourcePath() )
  4056. {
  4057. sPath = L"WinNT://";
  4058. sPath += pOptions->srcDomain;
  4059. sPath += L"/";
  4060. sPath += pAcct->GetName();
  4061. pAcct->SetSourcePath(sPath.c_str());
  4062. }
  4063. return true;
  4064. }
  4065. WCHAR strName[LEN_Path];
  4066. wcscpy(strName, pAcct->GetName());
  4067. // Check if the Name field is a LDAP sub path or not. If we have LDAP subpath then we
  4068. // call the AcctReplFullPath function to fillup the path information.
  4069. if ( (wcslen(strName) > 3) && (strName[2] == (L'=')) )
  4070. {
  4071. AcctReplFullPath(pAcct, pOptions);
  4072. return true;
  4073. }
  4074. INetObjEnumeratorPtr pQuery(__uuidof(NetObjEnumerator));
  4075. HRESULT hr;
  4076. LPWSTR sData[] = { L"ADsPath", L"distinguishedName", L"name", L"profilePath", L"groupType" };
  4077. long nElt = DIM(sData);
  4078. BSTR * pData;
  4079. SAFEARRAY * psaColNames;
  4080. IEnumVARIANTPtr pEnum;
  4081. _variant_t var;
  4082. DWORD dwFetch;
  4083. // We are going to update all fields that we know about
  4084. // Set the LDAP path to the whole domain and then the query to the SAMAccountname
  4085. sPath = L"LDAP://";
  4086. sPath += pOptions->srcDomain;
  4087. sPath += L"/";
  4088. sPath += pOptions->srcNamingContext;
  4089. wstring sTempSamName = pAcct->GetSourceSam();
  4090. if (sTempSamName[0] == L' ')
  4091. {
  4092. sTempSamName = L"\\20" + sTempSamName.substr(1);
  4093. }
  4094. wstring strQuery = L"(sAMAccountName=" + sTempSamName + L")";
  4095. // Set the enumerator query
  4096. hr = pQuery->raw_SetQuery(
  4097. _bstr_t(sPath.c_str()),
  4098. _bstr_t(pOptions->srcDomain),
  4099. _bstr_t(strQuery.c_str()),
  4100. ADS_SCOPE_SUBTREE,
  4101. FALSE
  4102. );
  4103. if (SUCCEEDED(hr))
  4104. {
  4105. // Create a safearray of columns we need from the enumerator.
  4106. SAFEARRAYBOUND bd = { nElt, 0 };
  4107. psaColNames = ::SafeArrayCreate(VT_BSTR, 1, &bd);
  4108. if (psaColNames)
  4109. {
  4110. hr = ::SafeArrayAccessData(psaColNames, (void**)&pData);
  4111. }
  4112. else
  4113. {
  4114. hr = E_OUTOFMEMORY;
  4115. }
  4116. if ( SUCCEEDED(hr) )
  4117. {
  4118. for( long i = 0; i < nElt; i++)
  4119. {
  4120. pData[i] = SysAllocString(sData[i]);
  4121. }
  4122. hr = ::SafeArrayUnaccessData(psaColNames);
  4123. }
  4124. if (SUCCEEDED(hr))
  4125. {
  4126. // Set the columns on the enumerator object.
  4127. hr = pQuery->raw_SetColumns(psaColNames);
  4128. }
  4129. if (psaColNames)
  4130. {
  4131. SafeArrayDestroy(psaColNames);
  4132. }
  4133. }
  4134. if (SUCCEEDED(hr))
  4135. {
  4136. // Now execute.
  4137. hr = pQuery->raw_Execute(&pEnum);
  4138. }
  4139. if (SUCCEEDED(hr))
  4140. {
  4141. // We should have recieved only one value. So we will get the value and set it into the Node.
  4142. VARIANT varTemp;
  4143. VariantInit(&varTemp);
  4144. hr = pEnum->Next(1, &varTemp, &dwFetch);
  4145. var = _variant_t(varTemp, false);
  4146. }
  4147. if ( SUCCEEDED(hr) && ( var.vt & VT_ARRAY) )
  4148. {
  4149. // This would only happen if the member existed in the target domain.
  4150. // We now have a Variant containing an array of variants so we access the data
  4151. SAFEARRAY* psa = V_ARRAY(&var);
  4152. VARIANT* pVar;
  4153. SafeArrayAccessData(psa, (void**)&pVar);
  4154. // Get the AdsPath first
  4155. sTgtPath = pVar[0].bstrVal;
  4156. if (sTgtPath.length() > 0)
  4157. {
  4158. // Set the source Path in the Account node
  4159. pAcct->SetSourcePath(sTgtPath);
  4160. // Then we get the distinguishedName to get the prefix string
  4161. sTgtPath = V_BSTR(&pVar[1]);
  4162. // We also get the name value to set the target name
  4163. if (V_BSTR(&pVar[2]))
  4164. {
  4165. pAcct->SetName(V_BSTR(&pVar[2]));
  4166. pAcct->SetTargetName(V_BSTR(&pVar[2]));
  4167. }
  4168. // We also get the profile path so we can translate it
  4169. if (V_BSTR(&pVar[3]))
  4170. {
  4171. pAcct->SetTargetProfile(V_BSTR(&pVar[3]));
  4172. }
  4173. if ( pVar[4].vt == VT_I4 )
  4174. {
  4175. // We have the object type property so lets set it.
  4176. pAcct->SetGroupType(pVar[4].lVal);
  4177. }
  4178. SafeArrayUnaccessData(psa);
  4179. return true;
  4180. }
  4181. else
  4182. {
  4183. //There is no account with this SAM name in this domain
  4184. err.SysMsgWrite(ErrE, 2, DCT_MSG_PATH_NOT_FOUND_SS, pAcct->GetName(), pOptions->tgtDomain);
  4185. Mark(L"errors", pAcct->GetType());
  4186. SafeArrayUnaccessData(psa);
  4187. }
  4188. }
  4189. return false;
  4190. }
  4191. //--------------------------------------------------------------------------
  4192. // AcctReplFullPath : Fills up Account node when the account information
  4193. // coming in is a LDAP sub path.
  4194. //--------------------------------------------------------------------------
  4195. bool CAcctRepl::AcctReplFullPath(
  4196. TAcctReplNode * pAcct, //in- AccountReplicator Node with the Account info
  4197. Options * pOptions //in- Options set by the user.
  4198. )
  4199. {
  4200. WCHAR sName[LEN_Path];
  4201. WCHAR sPath[LEN_Path];
  4202. IADs * pAds;
  4203. _variant_t var;
  4204. // Build a full path and save it to the Account node
  4205. wsprintf(sPath, L"LDAP://%s/%s,%s", pOptions->srcDomain, pAcct->GetName(), pOptions->srcNamingContext);
  4206. pAcct->SetSourcePath(sPath);
  4207. // Do the same for Target account.
  4208. wcscpy(sName, pAcct->GetTargetName());
  4209. if ( !wcslen(sName) )
  4210. {
  4211. // Since Target name not specified we will go ahead and use the source name as the target name,
  4212. wcscpy(sName, pAcct->GetName());
  4213. pAcct->SetTargetName(sName);
  4214. }
  4215. // Build a full path from the sub path
  4216. /* wsprintf(sPath, L"LDAP://%s/%s,%s", pOptions->tgtDomain, sName, pOptions->tgtNamingContext);
  4217. pAcct->SetTargetPath(sPath);
  4218. */
  4219. // Lets try and get the SAM name for the source account
  4220. HRESULT hr = ADsGetObject(const_cast<WCHAR*>(pAcct->GetSourcePath()), IID_IADs, (void**) &pAds);
  4221. if ( FAILED(hr)) return false;
  4222. hr = pAds->Get(L"sAMAccountName", &var);
  4223. pAds->Release();
  4224. if ( SUCCEEDED(hr) )
  4225. pAcct->SetSourceSam((WCHAR*)var.bstrVal);
  4226. // SAM account name for the target account
  4227. // Since we are here we have a LDAP sub path. So we can copy string from 3rd character to end of line or
  4228. // till the first ','
  4229. wcscpy(sName, pAcct->GetTargetName());
  4230. WCHAR * p = wcschr(sName, L',');
  4231. int ndx = wcslen(sName);
  4232. if ( p )
  4233. {
  4234. // There is a , So we can find how many characters that is by subtracting two pointers
  4235. ndx = (int)(p - sName);
  4236. }
  4237. ndx -= 3; // We are going to ignore the first three characters
  4238. // Copy from third character on to the , or End of line this is going to be the SAM name for target
  4239. wcsncpy(sPath, sName + 3, ndx);
  4240. sPath[ndx] = 0; // Truncate it.
  4241. pAcct->SetTargetSam(sPath);
  4242. return true;
  4243. }
  4244. //--------------------------------------------------------------------------
  4245. // NeedToProcessAccount : This function tells us if the user has set the
  4246. // options to copy certain types of accounts.
  4247. //--------------------------------------------------------------------------
  4248. BOOL CAcctRepl::NeedToProcessAccount(
  4249. TAcctReplNode * pAcct, //in- AccountReplicator Node with the Account info
  4250. Options * pOptions //in- Options set by the user.
  4251. )
  4252. {
  4253. if ((_wcsicmp(pAcct->GetType(), s_ClassUser) == 0) || (_wcsicmp(pAcct->GetType(), s_ClassInetOrgPerson) == 0))
  4254. return (pOptions->flags & F_USERS);
  4255. else if ( _wcsicmp(pAcct->GetType(), L"group") == 0)
  4256. return ((pOptions->flags & F_GROUP) || (pOptions->flags & F_LGROUP));
  4257. else if ( _wcsicmp(pAcct->GetType(), L"computer") == 0)
  4258. return pOptions->flags & F_COMPUTERS;
  4259. else if ( _wcsicmp(pAcct->GetType(), L"organizationalUnit") == 0)
  4260. return pOptions->flags & F_OUS;
  4261. else
  4262. {
  4263. err.MsgWrite(0,DCT_MSG_SKIPPING_OBJECT_TYPE_SS,pAcct->GetName(),pAcct->GetType());
  4264. return false;
  4265. }
  4266. }
  4267. // Compares the DC=...,DC=com part of two ads paths to determine if the objects
  4268. // are in the same domain.
  4269. BOOL CompareDCPath(WCHAR const * sPath, WCHAR const * sPath2)
  4270. {
  4271. WCHAR * p1 = NULL, * p2 = NULL;
  4272. p1 = wcsstr(sPath, L"DC=");
  4273. p2 = wcsstr(sPath2, L"DC=");
  4274. if ( p1 && p2 )
  4275. return !_wcsicmp(p1, p2);
  4276. else
  4277. return FALSE;
  4278. }
  4279. _bstr_t PadDN(_bstr_t sDN)
  4280. {
  4281. _bstr_t retVal = sDN;
  4282. int offset = 0;
  4283. WCHAR sLine[LEN_Path];
  4284. WCHAR sOut[LEN_Path];
  4285. safecopy(sLine, (WCHAR*) sDN);
  4286. for ( DWORD i = 0; i < wcslen(sLine); i++ )
  4287. {
  4288. if ( sLine[i] == L'/' )
  4289. {
  4290. sOut[i + offset] = L'\\';
  4291. offset++;
  4292. }
  4293. sOut[i + offset] = sLine[i];
  4294. }
  4295. sOut[i+offset] = 0;
  4296. retVal = sOut;
  4297. return retVal;
  4298. }
  4299. //--------------------------------------------------------------------------
  4300. // ExpandContainers : Adds all the members of a container/group to the
  4301. // account list recursively.
  4302. //--------------------------------------------------------------------------
  4303. BOOL CAcctRepl::ExpandContainers(
  4304. TNodeListSortable *acctlist, //in- Accounts being processed
  4305. Options *pOptions, //in- Options specified by the user
  4306. ProgressFn *progress //in- Show status
  4307. )
  4308. {
  4309. TAcctReplNode * pAcct;
  4310. IEnumVARIANT * pEnum;
  4311. HRESULT hr;
  4312. _variant_t var;
  4313. DWORD dwf;
  4314. INetObjEnumeratorPtr pQuery(__uuidof(NetObjEnumerator));
  4315. LPWSTR sCols[] = { L"member" };
  4316. LPWSTR sCols1[] = { L"ADsPath" };
  4317. int nElt = DIM(sCols);
  4318. SAFEARRAY * cols;
  4319. SAFEARRAY * vals;
  4320. SAFEARRAY * multiVals;
  4321. SAFEARRAYBOUND bd = { nElt, 0 };
  4322. BSTR HUGEP * pData = NULL;
  4323. // _bstr_t * pBstr = NULL;
  4324. _variant_t * pDt = NULL;
  4325. _variant_t * pVar = NULL;
  4326. _variant_t vx;
  4327. _bstr_t sCont, sQuery;
  4328. _bstr_t sPath;
  4329. _bstr_t sSam;
  4330. _bstr_t sType;
  4331. _bstr_t sName;
  4332. _bstr_t sTgtName;
  4333. DWORD dwMaj, dwMin, dwSP;
  4334. // IIManageDBPtr pDb(__uuidof(IManageDB));
  4335. IVarSetPtr pVs(__uuidof(VarSet));
  4336. IUnknown * pUnk;
  4337. long lgrpType;
  4338. WCHAR sAcctType[LEN_Path];
  4339. WCHAR mesg[LEN_Path];
  4340. WCHAR sSourcePath[LEN_Path];
  4341. bool bExpanded = true;
  4342. pVs->QueryInterface(IID_IUnknown, (void **) &pUnk);
  4343. MCSDCTWORKEROBJECTSLib::IAccessCheckerPtr pAccess(__uuidof(MCSDCTWORKEROBJECTSLib::AccessChecker));
  4344. // Change from a tree to a sorted list
  4345. if ( acctlist->IsTree() ) acctlist->ToSorted();
  4346. // Check the domain type for the source domain.
  4347. hr = pAccess->raw_GetOsVersion(pOptions->srcComp, &dwMaj, &dwMin, &dwSP);
  4348. if (FAILED(hr)) return FALSE;
  4349. if ( dwMaj < 5 )
  4350. {
  4351. while ( bExpanded )
  4352. {
  4353. bExpanded = false;
  4354. pAcct = (TAcctReplNode *)acctlist->Head();
  4355. while (pAcct)
  4356. {
  4357. if ( pOptions->pStatus )
  4358. {
  4359. LONG status = 0;
  4360. HRESULT hr = pOptions->pStatus->get_Status(&status);
  4361. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  4362. {
  4363. if ( !bAbortMessageWritten )
  4364. {
  4365. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  4366. bAbortMessageWritten = true;
  4367. }
  4368. break;
  4369. }
  4370. }
  4371. // If we have already expanded the account then we dont need to process it again.
  4372. if ( pAcct->bExpanded )
  4373. {
  4374. pAcct = (TAcctReplNode *) pAcct->Next();
  4375. continue;
  4376. }
  4377. //Set the flag to say that we expanded something.
  4378. bExpanded = true;
  4379. pAcct->bExpanded = true;
  4380. if ( UStrICmp(pAcct->GetType(), L"group") || UStrICmp(pAcct->GetType(), L"lgroup") )
  4381. {
  4382. // Build the column array
  4383. cols = SafeArrayCreate(VT_BSTR, 1, &bd);
  4384. SafeArrayAccessData(cols, (void HUGEP **) &pData);
  4385. for ( int i = 0; i < nElt; i++)
  4386. pData[i] = SysAllocString(sCols1[i]);
  4387. SafeArrayUnaccessData(cols);
  4388. // Build the NT4 recognizable container name
  4389. sCont = _bstr_t(pAcct->GetName()) + L",CN=GROUPS";
  4390. sQuery = L""; // ignored.
  4391. // Query the information
  4392. hr = pQuery->raw_SetQuery(sCont, pOptions->srcDomain, sQuery, ADS_SCOPE_SUBTREE, TRUE);
  4393. if (FAILED(hr)) return FALSE;
  4394. hr = pQuery->raw_SetColumns(cols);
  4395. if (FAILED(hr)) return FALSE;
  4396. hr = pQuery->raw_Execute(&pEnum);
  4397. if (FAILED(hr)) return FALSE;
  4398. while (pEnum->Next(1, &var, &dwf) == S_OK)
  4399. {
  4400. if ( pOptions->pStatus )
  4401. {
  4402. LONG status = 0;
  4403. HRESULT hr = pOptions->pStatus->get_Status(&status);
  4404. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  4405. {
  4406. if ( !bAbortMessageWritten )
  4407. {
  4408. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  4409. bAbortMessageWritten = true;
  4410. }
  4411. break;
  4412. }
  4413. }
  4414. vals = var.parray;
  4415. // Get the first column which is the name of the object.
  4416. SafeArrayAccessData(vals, (void HUGEP**) &pDt);
  4417. sPath = pDt[0];
  4418. SafeArrayUnaccessData(vals);
  4419. // Enumerator returns empty strings which we need to ignore.
  4420. if ( sPath.length() > 0 )
  4421. {
  4422. // Look if we have migrated the group
  4423. if ( pOptions->flags & F_COPY_MIGRATED_ACCT )
  4424. // We want to copy it again even if it was already copied.
  4425. hr = S_FALSE;
  4426. else
  4427. hr = pOptions->pDb->raw_GetAMigratedObject(sPath, pOptions->srcDomain, pOptions->tgtDomain, &pUnk);
  4428. if ( hr != S_OK )
  4429. {
  4430. if ( !IsBuiltinAccount(pOptions, (WCHAR*)sPath) )
  4431. {
  4432. // We don't care about the objects that we have migrated because they will be picked up automatically
  4433. // Find the type of this account.
  4434. if ( GetNt4Type(pOptions->srcComp, (WCHAR*) sPath, sAcctType) )
  4435. {
  4436. // Expand the containers and the membership
  4437. wsprintf(mesg, GET_STRING(IDS_EXPANDING_ADDING_SS) , pAcct->GetName(), (WCHAR*) sPath);
  4438. Progress(mesg);
  4439. TAcctReplNode * pNode = new TAcctReplNode();
  4440. if (!pNode)
  4441. return FALSE;
  4442. pNode->SetName((WCHAR*)sPath);
  4443. pNode->SetTargetName((WCHAR*)sPath);
  4444. pNode->SetSourceSam((WCHAR*)sPath);
  4445. pNode->SetTargetSam((WCHAR*)sPath);
  4446. pNode->SetType(sAcctType);
  4447. if ( !UStrICmp(sAcctType,L"group") )
  4448. {
  4449. // in NT4, only global groups can be members of other groups
  4450. pNode->SetGroupType(2);
  4451. }
  4452. //Get the source domain sid from the user
  4453. pNode->SetSourceSid(pAcct->GetSourceSid());
  4454. // build a source WinNT path
  4455. wsprintf(sSourcePath, L"WinNT://%s/%s", pOptions->srcDomain, (WCHAR*)sPath);
  4456. pNode->SetSourcePath(sSourcePath);
  4457. if (acctlist->InsertIfNew(pNode))
  4458. {
  4459. WCHAR szSam[LEN_Path];
  4460. wcscpy(szSam, pNode->GetTargetSam());
  4461. TruncateSam(szSam, pNode, pOptions, acctlist);
  4462. pNode->SetTargetSam(szSam);
  4463. AddPrefixSuffix(pNode, pOptions);
  4464. }
  4465. else
  4466. {
  4467. delete pNode;
  4468. }
  4469. }
  4470. else
  4471. {
  4472. wsprintf(mesg,GET_STRING(IDS_EXPANDING_IGNORING_SS), pAcct->GetName(), (WCHAR*) sPath);
  4473. Progress(mesg);
  4474. }
  4475. }
  4476. else
  4477. {
  4478. err.MsgWrite(ErrW, DCT_MSG_IGNORING_BUILTIN_S, (WCHAR*)sPath);
  4479. Mark("warnings", pAcct->GetType());
  4480. }
  4481. }
  4482. else
  4483. {
  4484. wsprintf(mesg, GET_STRING(IDS_EXPANDING_IGNORING_SS), pAcct->GetName(), (WCHAR*) sPath);
  4485. Progress(mesg);
  4486. }
  4487. }
  4488. }
  4489. pEnum->Release();
  4490. var.Clear();
  4491. }
  4492. pAcct = (TAcctReplNode *) pAcct->Next();
  4493. }
  4494. }
  4495. pUnk->Release();
  4496. return TRUE;
  4497. }
  4498. // If we are here that means that we are dealing with Win2k
  4499. while ( bExpanded )
  4500. {
  4501. bExpanded = false;
  4502. // Go through the list of accounts and expand them one at a time
  4503. pAcct = (TAcctReplNode *)acctlist->Head();
  4504. while (pAcct)
  4505. {
  4506. // If we have already expanded the account then we dont need to process it again.
  4507. if ( pAcct->bExpanded )
  4508. {
  4509. pAcct = (TAcctReplNode *) pAcct->Next();
  4510. continue;
  4511. }
  4512. //Set the flag to say that we expanded something.
  4513. bExpanded = true;
  4514. pAcct->bExpanded = true;
  4515. if ( pOptions->pStatus )
  4516. {
  4517. LONG status = 0;
  4518. HRESULT hr = pOptions->pStatus->get_Status(&status);
  4519. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  4520. {
  4521. if ( !bAbortMessageWritten )
  4522. {
  4523. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  4524. bAbortMessageWritten = true;
  4525. }
  4526. break;
  4527. }
  4528. }
  4529. DWORD scope = 0;
  4530. sCont = pAcct->GetSourcePath();
  4531. sQuery = L"(objectClass=*)";
  4532. if ( wcslen(pAcct->GetSourceSam()) == 0 )
  4533. {
  4534. scope = ADS_SCOPE_SUBTREE;
  4535. // Build the column array
  4536. cols = SafeArrayCreate(VT_BSTR, 1, &bd);
  4537. SafeArrayAccessData(cols, (void HUGEP **) &pData);
  4538. for ( int i = 0; i < nElt; i++)
  4539. pData[i] = SysAllocString(sCols1[i]);
  4540. SafeArrayUnaccessData(cols);
  4541. }
  4542. else
  4543. {
  4544. scope = ADS_SCOPE_BASE;
  4545. // Build the column array
  4546. cols = SafeArrayCreate(VT_BSTR, 1, &bd);
  4547. SafeArrayAccessData(cols, (void HUGEP **) &pData);
  4548. for ( int i = 0; i < nElt; i++)
  4549. pData[i] = SysAllocString(sCols[i]);
  4550. SafeArrayUnaccessData(cols);
  4551. }
  4552. hr = pQuery->raw_SetQuery(sCont, pOptions->srcDomain, sQuery, scope, TRUE);
  4553. if (FAILED(hr)) return FALSE;
  4554. hr = pQuery->raw_SetColumns(cols);
  4555. if (FAILED(hr)) return FALSE;
  4556. hr = pQuery->raw_Execute(&pEnum);
  4557. if (FAILED(hr)) return FALSE;
  4558. while (pEnum->Next(1, &var, &dwf) == S_OK)
  4559. {
  4560. if ( pOptions->pStatus )
  4561. {
  4562. LONG status = 0;
  4563. HRESULT hr = pOptions->pStatus->get_Status(&status);
  4564. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  4565. {
  4566. if ( !bAbortMessageWritten )
  4567. {
  4568. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  4569. bAbortMessageWritten = true;
  4570. }
  4571. break;
  4572. }
  4573. }
  4574. vals = var.parray;
  4575. // Get the VARIANT Array out
  4576. SafeArrayAccessData(vals, (void HUGEP**) &pDt);
  4577. vx = pDt[0];
  4578. SafeArrayUnaccessData(vals);
  4579. if ( vx.vt == VT_BSTR )
  4580. {
  4581. // We got back a BSTR which could be the value that we are looking for
  4582. sPath = V_BSTR(&vx);
  4583. // Enumerator returns empty strings which we need to ignore.
  4584. if ( sPath.length() > 0 )
  4585. {
  4586. if ( GetSamFromPath(sPath, sSam, sType, sName, sTgtName, lgrpType, pOptions) && CompareDCPath((WCHAR*)sPath, pAcct->GetSourcePath()))
  4587. {
  4588. if ( pOptions->flags & F_COPY_MIGRATED_ACCT )
  4589. hr = S_FALSE;
  4590. else
  4591. hr = pOptions->pDb->raw_GetAMigratedObject(sSam, pOptions->srcDomain, pOptions->tgtDomain, &pUnk);
  4592. if ( hr != S_OK )
  4593. {
  4594. // We don't care about the objects that we have migrated because they will be picked up automatically
  4595. if ( _wcsicmp((WCHAR*) sType, L"computer") != 0 )
  4596. {
  4597. TAcctReplNode * pNode = new TAcctReplNode();
  4598. if (!pNode)
  4599. return FALSE;
  4600. pNode->SetSourceSam((WCHAR*)sSam);
  4601. pNode->SetTargetSam((WCHAR*)sSam);
  4602. pNode->SetName((WCHAR*)sName);
  4603. pNode->SetTargetName((WCHAR*)sTgtName);
  4604. pNode->SetType((WCHAR*)sType);
  4605. pNode->SetSourcePath((WCHAR*)sPath);
  4606. pNode->SetGroupType(lgrpType);
  4607. //Get the source domain sid from the user
  4608. pNode->SetSourceSid(pAcct->GetSourceSid());
  4609. if (acctlist->InsertIfNew(pNode))
  4610. {
  4611. WCHAR szSam[LEN_Path];
  4612. TruncateSam(szSam, pNode, pOptions, acctlist);
  4613. pNode->SetTargetSam(szSam);
  4614. AddPrefixSuffix(pNode, pOptions);
  4615. }
  4616. else
  4617. {
  4618. delete pNode;
  4619. }
  4620. wsprintf(mesg, GET_STRING(IDS_EXPANDING_ADDING_SS), pAcct->GetName(), (WCHAR*) sSam);
  4621. Progress(mesg);
  4622. }
  4623. else
  4624. {
  4625. wsprintf(mesg, GET_STRING(IDS_EXPANDING_IGNORING_SS), pAcct->GetName(), (WCHAR*) sSam);
  4626. Progress(mesg);
  4627. }
  4628. }
  4629. else
  4630. {
  4631. wsprintf(mesg, GET_STRING(IDS_EXPANDING_IGNORING_SS), pAcct->GetName(), (WCHAR*) sSam);
  4632. Progress(mesg);
  4633. }
  4634. }
  4635. }
  4636. // continue;
  4637. }
  4638. // if (! ( vx.vt & VT_ARRAY ) )
  4639. // continue;
  4640. if ( vx.vt & VT_ARRAY )
  4641. // We must have got an Array of multivalued properties
  4642. multiVals = vx.parray;
  4643. else
  4644. {
  4645. // We need to also process the accounts that have this group as its primary group.
  4646. SAFEARRAYBOUND bd = { 0, 0 };
  4647. multiVals = SafeArrayCreate(VT_VARIANT, 1, &bd);
  4648. }
  4649. AddPrimaryGroupMembers(pOptions, multiVals, const_cast<WCHAR*>(pAcct->GetTargetSam()));
  4650. // Access the BSTR elements of this variant array
  4651. SafeArrayAccessData(multiVals, (void HUGEP **) &pVar);
  4652. for ( DWORD dw = 0; dw < multiVals->rgsabound->cElements; dw++ )
  4653. {
  4654. if ( pOptions->pStatus )
  4655. {
  4656. LONG status = 0;
  4657. HRESULT hr = pOptions->pStatus->get_Status(&status);
  4658. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  4659. {
  4660. if ( !bAbortMessageWritten )
  4661. {
  4662. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  4663. bAbortMessageWritten = true;
  4664. }
  4665. break;
  4666. }
  4667. }
  4668. _bstr_t sDN = _bstr_t(pVar[dw]);
  4669. sDN = PadDN(sDN);
  4670. sPath = _bstr_t(L"LDAP://") + _bstr_t(pOptions->srcDomain) + _bstr_t(L"/") + sDN;
  4671. if ( GetSamFromPath(sPath, sSam, sType, sName, sTgtName, lgrpType, pOptions) && CompareDCPath((WCHAR*)sPath, pAcct->GetSourcePath()))
  4672. {
  4673. if ( pOptions->flags & F_COPY_MIGRATED_ACCT )
  4674. hr = S_FALSE;
  4675. else
  4676. hr = pOptions->pDb->raw_GetAMigratedObject(sSam, pOptions->srcDomain, pOptions->tgtDomain, &pUnk);
  4677. if ( hr != S_OK )
  4678. {
  4679. // We don't care about the objects that we have migrated because they will be picked up automatically
  4680. if ( _wcsicmp((WCHAR*) sType, L"computer") != 0 )
  4681. {
  4682. TAcctReplNode * pNode = new TAcctReplNode();
  4683. if (!pNode)
  4684. return FALSE;
  4685. pNode->SetSourceSam((WCHAR*)sSam);
  4686. pNode->SetTargetSam((WCHAR*)sSam);
  4687. pNode->SetName((WCHAR*)sName);
  4688. pNode->SetTargetName((WCHAR*)sTgtName);
  4689. pNode->SetType((WCHAR*)sType);
  4690. pNode->SetSourcePath((WCHAR*)sPath);
  4691. pNode->SetGroupType(lgrpType);
  4692. //Get the source domain sid from the user
  4693. pNode->SetSourceSid(pAcct->GetSourceSid());
  4694. if (acctlist->InsertIfNew(pNode))
  4695. {
  4696. WCHAR szSam[LEN_Path];
  4697. wcscpy(szSam, sSam);
  4698. TruncateSam(szSam, pNode, pOptions, acctlist);
  4699. pNode->SetTargetSam(szSam);
  4700. AddPrefixSuffix(pNode, pOptions);
  4701. }
  4702. else
  4703. {
  4704. delete pNode;
  4705. }
  4706. wsprintf(mesg, GET_STRING(IDS_EXPANDING_ADDING_SS), pAcct->GetName(), (WCHAR*) sSam);
  4707. Progress(mesg);
  4708. }
  4709. else
  4710. {
  4711. wsprintf(mesg, GET_STRING(IDS_EXPANDING_IGNORING_SS), pAcct->GetName(), (WCHAR*) sSam);
  4712. Progress(mesg);
  4713. }
  4714. }
  4715. else
  4716. {
  4717. wsprintf(mesg, GET_STRING(IDS_EXPANDING_IGNORING_SS), pAcct->GetName(), (WCHAR*) sSam);
  4718. Progress(mesg);
  4719. }
  4720. }
  4721. }
  4722. SafeArrayUnaccessData(multiVals);
  4723. }
  4724. pEnum->Release();
  4725. var.Clear();
  4726. pAcct = (TAcctReplNode*)pAcct->Next();
  4727. }
  4728. }
  4729. pUnk->Release();
  4730. return TRUE;
  4731. }
  4732. //--------------------------------------------------------------------------
  4733. // IsContainer : Checks if the account in question is a container type
  4734. // if it is then it returns a IADsContainer * to it.
  4735. //--------------------------------------------------------------------------
  4736. BOOL CAcctRepl::IsContainer(TAcctReplNode *pNode, IADsContainer **ppCont)
  4737. {
  4738. HRESULT hr;
  4739. hr = ADsGetObject(const_cast<WCHAR*>(pNode->GetSourcePath()), IID_IADsContainer, (void**)ppCont);
  4740. return SUCCEEDED(hr);
  4741. }
  4742. BOOL CAcctRepl::GetSamFromPath(_bstr_t sPath, _bstr_t& sSam, _bstr_t& sType, _bstr_t& sSrcName, _bstr_t& sTgtName, long& grpType, Options * pOptions)
  4743. {
  4744. HRESULT hr;
  4745. IADsPtr pAds;
  4746. BOOL bIsCritical = FALSE;
  4747. BOOL rc = TRUE;
  4748. sSam = L"";
  4749. // Get the object so we can ask the questions from it.
  4750. hr = ADsGetObject((WCHAR*)sPath, IID_IADs, (void**)&pAds);
  4751. if ( FAILED(hr) ) return FALSE;
  4752. if ( SUCCEEDED(hr))
  4753. {
  4754. VARIANT var;
  4755. VariantInit(&var);
  4756. hr = pAds->Get(L"isCriticalSystemObject", &var);
  4757. if ( SUCCEEDED(hr) )
  4758. {
  4759. // This will only succeed for the Win2k objects.
  4760. bIsCritical = (V_BOOL(&var) == VARIANT_TRUE) ? TRUE : FALSE;
  4761. VariantClear(&var);
  4762. }
  4763. else
  4764. {
  4765. // This must be a NT4 account. We need to get the SID and check if
  4766. // it's RID belongs to the BUILTIN rids.
  4767. hr = pAds->Get(L"objectSID", &var);
  4768. if ( SUCCEEDED(hr) )
  4769. {
  4770. SAFEARRAY * pArray = V_ARRAY(&var);
  4771. PSID pSid;
  4772. hr = SafeArrayAccessData(pArray, (void**)&pSid);
  4773. if ( SUCCEEDED(hr) )
  4774. {
  4775. PUCHAR ucCnt = GetSidSubAuthorityCount(pSid);
  4776. DWORD * rid = (DWORD *) GetSidSubAuthority(pSid, (*ucCnt)-1);
  4777. bIsCritical = BuiltinRid(*rid);
  4778. if ( bIsCritical )
  4779. {
  4780. BSTR sName;
  4781. hr = pAds->get_Name(&sName);
  4782. bIsCritical = CheckBuiltInWithNTApi(pSid, (WCHAR*)sName, pOptions);
  4783. SysFreeString(sName);
  4784. }
  4785. hr = SafeArrayUnaccessData(pArray);
  4786. }
  4787. VariantClear(&var);
  4788. }
  4789. }
  4790. }
  4791. // Get the class from the object. If it is a container/ou class then it will not have a SAM name so put the CN= or OU= into the list
  4792. BSTR bstr = 0;
  4793. hr = pAds->get_Class(&bstr);
  4794. if ( FAILED(hr) ) rc = FALSE;
  4795. if ( rc )
  4796. {
  4797. sType = _bstr_t(bstr, false);
  4798. if (UStrICmp((WCHAR*) sType, L"organizationalUnit") == 0)
  4799. {
  4800. bstr = 0;
  4801. hr = pAds->get_Name(&bstr);
  4802. sSrcName = _bstr_t(L"OU=") + _bstr_t(bstr, false);
  4803. sTgtName = sSrcName;
  4804. sSam = L"";
  4805. if ( FAILED(hr) ) rc = FALSE;
  4806. }
  4807. else if (UStrICmp((WCHAR*) sType, L"container") == 0)
  4808. {
  4809. bstr = 0;
  4810. hr = pAds->get_Name(&bstr);
  4811. sSrcName = _bstr_t(L"CN=") + _bstr_t(bstr, false);
  4812. sTgtName = sSrcName;
  4813. sSam = L"";
  4814. if ( FAILED(hr) ) rc = FALSE;
  4815. }
  4816. else
  4817. {
  4818. bstr = 0;
  4819. hr = pAds->get_Name(&bstr);
  4820. sSrcName = _bstr_t(bstr, false);
  4821. //if the name includes a '/', then we have to get the escaped version from the path
  4822. //due to a bug in W2K.
  4823. if (wcschr((WCHAR*)sSrcName, L'/'))
  4824. {
  4825. _bstr_t sCNName = GetCNFromPath(sPath);
  4826. if (sCNName.length() != 0)
  4827. sSrcName = sCNName;
  4828. }
  4829. // if inter-forest migration and source object is an InetOrgPerson then...
  4830. if ((pOptions->bSameForest == FALSE) && (_wcsicmp(sType, s_ClassInetOrgPerson) == 0))
  4831. {
  4832. //
  4833. // must use the naming attribute of the target forest
  4834. //
  4835. // retrieve naming attribute for this class in target forest
  4836. SNamingAttribute naNamingAttribute;
  4837. if (FAILED(GetNamingAttribute(pOptions->tgtDomainDns, s_ClassInetOrgPerson, naNamingAttribute)))
  4838. {
  4839. err.MsgWrite(ErrE, DCT_MSG_CANNOT_GET_NAMING_ATTRIBUTE_SS, s_ClassInetOrgPerson, pOptions->tgtDomainDns);
  4840. Mark(L"errors", sType);
  4841. return FALSE;
  4842. }
  4843. _bstr_t strNamingAttribute(naNamingAttribute.strName.c_str());
  4844. // retrieve source attribute value
  4845. VARIANT var;
  4846. VariantInit(&var);
  4847. if (FAILED(pAds->Get(strNamingAttribute, &var)))
  4848. {
  4849. err.MsgWrite(ErrE, DCT_MSG_CANNOT_GET_SOURCE_ATTRIBUTE_REQUIRED_FOR_NAMING_SSS, naNamingAttribute.strName.c_str(), sPath, pOptions->tgtDomainDns);
  4850. Mark(L"errors", sType);
  4851. return FALSE;
  4852. }
  4853. // set target naming attribute value from source attribute value
  4854. sTgtName = strNamingAttribute + L"=" + _bstr_t(_variant_t(var, false));
  4855. }
  4856. else
  4857. {
  4858. // else set target name equal to source name
  4859. sTgtName = sSrcName;
  4860. }
  4861. VARIANT var;
  4862. VariantInit(&var);
  4863. hr = pAds->Get(L"sAMAccountName", &var);
  4864. if ( FAILED(hr)) rc = FALSE;
  4865. sSam = _variant_t(var, false);
  4866. if ( UStrICmp((WCHAR*) sType, L"group") == 0)
  4867. {
  4868. // we need to get and set the group type
  4869. pAds->Get(L"groupType", &var);
  4870. if ( SUCCEEDED(hr))
  4871. grpType = _variant_t(var, false);
  4872. }
  4873. }
  4874. if ( bIsCritical )
  4875. {
  4876. // Builtin account so we are going to ignore this account.
  4877. //Don't log this message in IntraForest because we do mess with it
  4878. // Also if it is a Domain Users group we add the migrated objects to it by default.
  4879. if ( !pOptions->bSameForest && _wcsicmp((WCHAR*) sSam, pOptions->sDomUsers))
  4880. {
  4881. err.MsgWrite(ErrW, DCT_MSG_IGNORING_BUILTIN_S, (WCHAR*)sPath);
  4882. Mark(L"warnings", (WCHAR*) sType);
  4883. }
  4884. rc = FALSE;
  4885. }
  4886. }
  4887. return rc;
  4888. }
  4889. //-----------------------------------------------------------------------------
  4890. // ExpandMembership : This method expands the account list to encorporate the
  4891. // groups that contain the members in the account list.
  4892. //-----------------------------------------------------------------------------
  4893. BOOL CAcctRepl::ExpandMembership(
  4894. TNodeListSortable *acctlist, //in- Accounts being processed
  4895. Options *pOptions, //in- Options specified by the user
  4896. TNodeListSortable *pNewAccts, //out-The newly Added accounts.
  4897. ProgressFn *progress, //in- Show status
  4898. BOOL bGrpsOnly, //in- Expand for groups only
  4899. BOOL bAnySourceDomain //in- include groups from any domain (fix group membership)
  4900. )
  4901. {
  4902. TAcctReplNode * pAcct;
  4903. HRESULT hr = S_OK;
  4904. _variant_t var;
  4905. MCSDCTWORKEROBJECTSLib::IAccessCheckerPtr pAccess(__uuidof(MCSDCTWORKEROBJECTSLib::AccessChecker));
  4906. DWORD dwMaj, dwMin, dwSP;
  4907. IVarSetPtr pVs(__uuidof(VarSet));
  4908. PSID pSid = NULL;
  4909. SID_NAME_USE use;
  4910. DWORD dwNameLen = LEN_Path;
  4911. DWORD dwDomName = LEN_Path;
  4912. c_array<WCHAR> achDomain(LEN_Path);
  4913. c_array<WCHAR> achDomUsers(LEN_Path);
  4914. BOOL rc = FALSE;
  4915. long lgrpType;
  4916. c_array<WCHAR> achMesg(LEN_Path);
  4917. IUnknownPtr spUnknown(pVs);
  4918. IUnknown* pUnk = spUnknown;
  4919. // Change from a tree to a sorted list
  4920. if ( acctlist->IsTree() ) acctlist->ToSorted();
  4921. // Get the Domain Users group name
  4922. pSid = GetWellKnownSid(DOMAIN_USERS, pOptions,FALSE);
  4923. if ( pSid )
  4924. {
  4925. // since we have the well known SID now we can get its name
  4926. if ( ! LookupAccountSid(pOptions->srcComp, pSid, achDomUsers, &dwNameLen, achDomain, &dwDomName, &use) )
  4927. hr = HRESULT_FROM_WIN32(GetLastError());
  4928. else
  4929. wcscpy(pOptions->sDomUsers, achDomUsers);
  4930. FreeSid(pSid);
  4931. }
  4932. // Check the domain type for the source domain.
  4933. if ( SUCCEEDED(hr) )
  4934. hr = pAccess->raw_GetOsVersion(pOptions->srcComp, &dwMaj, &dwMin, &dwSP);
  4935. if ( SUCCEEDED(hr))
  4936. {
  4937. if ( dwMaj < 5 )
  4938. {
  4939. // NT4 objects we need to use NT API to get the groups that this account is a member of.
  4940. LPGROUP_USERS_INFO_0 pBuf = NULL;
  4941. DWORD dwLevel = 0;
  4942. DWORD dwPrefMaxLen = 0xFFFFFFFF;
  4943. DWORD dwEntriesRead = 0;
  4944. DWORD dwTotalEntries = 0;
  4945. NET_API_STATUS nStatus;
  4946. c_array<WCHAR> achGrpName(LEN_Path);
  4947. _bstr_t strType;
  4948. BOOL bBuiltin;
  4949. long numGroups = 0;
  4950. //get a varset of previously migrated groups (we will need this if any accounts being migrated are groups
  4951. hr = pOptions->pDb->raw_GetMigratedObjectByType(-1, _bstr_t(L""), _bstr_t(L"group"), &pUnk);
  4952. if ( SUCCEEDED(hr) )
  4953. {
  4954. //get the num of objects in the varset
  4955. numGroups = pVs->get(L"MigratedObjects");
  4956. }
  4957. m_IgnoredGrpMap.clear(); //clear the ignored group map used to optimize group fixup
  4958. //for each account, enumerate its membership in any previously migrated groups
  4959. for ( pAcct = (TAcctReplNode*)acctlist->Head(); pAcct; pAcct = (TAcctReplNode*)pAcct->Next())
  4960. {
  4961. if ( pOptions->pStatus )
  4962. {
  4963. LONG status = 0;
  4964. HRESULT hr = pOptions->pStatus->get_Status(&status);
  4965. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  4966. {
  4967. if ( !bAbortMessageWritten )
  4968. {
  4969. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  4970. bAbortMessageWritten = true;
  4971. }
  4972. break;
  4973. }
  4974. }
  4975. //if user
  4976. if (!_wcsicmp(pAcct->GetType(), s_ClassUser) || !_wcsicmp(pAcct->GetType(), s_ClassInetOrgPerson))
  4977. {
  4978. //User object
  4979. nStatus = NetUserGetGroups(pOptions->srcComp, pAcct->GetName(), 0, (LPBYTE*)&pBuf, dwPrefMaxLen, &dwEntriesRead, &dwTotalEntries );
  4980. if (nStatus == NERR_Success)
  4981. {
  4982. for ( DWORD i = 0; i < dwEntriesRead; i++ )
  4983. {
  4984. if ( pOptions->pStatus )
  4985. {
  4986. LONG status = 0;
  4987. HRESULT hr = pOptions->pStatus->get_Status(&status);
  4988. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  4989. {
  4990. if ( !bAbortMessageWritten )
  4991. {
  4992. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  4993. bAbortMessageWritten = true;
  4994. }
  4995. break;
  4996. }
  4997. }
  4998. //see if this group is in the acctlist and successfully migrated. If so, then we
  4999. //should not need to add this to the list.
  5000. Lookup p;
  5001. p.pName = pBuf[i].grui0_name;
  5002. strType = L"group";
  5003. p.pType = strType;
  5004. TAcctReplNode * pFindNode = (TAcctReplNode *) acctlist->Find(&TNodeFindAccountName, &p);
  5005. if (pFindNode && (pFindNode->WasCreated() || pFindNode->WasReplaced()) && (bGrpsOnly))
  5006. continue;
  5007. //if we are doing group membership fixup, see if this group
  5008. //is already in the new list we are creating. If so, just add this member to the
  5009. //member map for this group node. This will save us the waste of
  5010. //recalculating all the fields and save on memory.
  5011. pFindNode = NULL;
  5012. if ((!pOptions->expandMemberOf) || ((pOptions->expandMemberOf) && (bGrpsOnly)))
  5013. {
  5014. pFindNode = (TAcctReplNode *) pNewAccts->Find(&TNodeFindAccountName, &p);
  5015. if (pFindNode)
  5016. {
  5017. pFindNode->mapGrpMember.insert(CGroupMemberMap::value_type(pAcct->GetSourceSam(), pAcct->GetType()));
  5018. continue;
  5019. }
  5020. }
  5021. //we also want to avoid the slow code below if we are expanding users' groups for inclusion
  5022. //in the migration and that group has already been added to the new list by another user
  5023. pFindNode = NULL;
  5024. if ((pOptions->expandMemberOf) && (!bGrpsOnly))
  5025. {
  5026. //if already included by another user, move on to the next group for this user
  5027. pFindNode = (TAcctReplNode *) pNewAccts->Find(&TNodeFindAccountName, &p);
  5028. if (pFindNode)
  5029. continue;
  5030. }
  5031. //if this group has already been placed in the ignore map, continue on to
  5032. //the next group
  5033. CGroupNameMap::iterator itGroupNameMap;
  5034. itGroupNameMap = m_IgnoredGrpMap.find(pBuf[i].grui0_name);
  5035. //if found, continue with the next group
  5036. if (itGroupNameMap != m_IgnoredGrpMap.end())
  5037. continue;
  5038. bBuiltin = IsBuiltinAccount(pOptions, pBuf[i].grui0_name);
  5039. // Ignore the Domain users group.
  5040. if ( (_wcsicmp(pBuf[i].grui0_name, achDomUsers) != 0) && !bBuiltin)
  5041. {
  5042. wsprintf(achMesg, GET_STRING(IDS_EXPANDING_GROUP_ADDING_SS), pAcct->GetName(), pBuf[i].grui0_name);
  5043. Progress(achMesg);
  5044. // This is the global group type by default
  5045. strType = L"group";
  5046. // Get the name of the group and add it to the list if it does not already exist in the list.
  5047. wcscpy(achGrpName, pBuf[i].grui0_name);
  5048. TAcctReplNode * pNode = new TAcctReplNode();
  5049. if (!pNode)
  5050. return FALSE;
  5051. // Source name stays as is
  5052. pNode->SetName(achGrpName);
  5053. pNode->SetSourceSam(achGrpName);
  5054. pNode->SetType(strType);
  5055. pNode->SetGroupType(2);
  5056. pNode->SetTargetName(achGrpName);
  5057. //Get the source domain sid from the user
  5058. pNode->SetSourceSid(pAcct->GetSourceSid());
  5059. // Look if we have migrated the group
  5060. hr = pOptions->pDb->raw_GetAMigratedObject(achGrpName, pOptions->srcDomain, pOptions->tgtDomain, &pUnk);
  5061. if ( hr == S_OK )
  5062. {
  5063. VerifyAndUpdateMigratedTarget(pOptions, pVs);
  5064. var = pVs->get(L"MigratedObjects.SourceAdsPath");
  5065. pNode->SetSourcePath(var.bstrVal);
  5066. //Get the target name and assign that to the node
  5067. var = pVs->get(L"MigratedObjects.TargetSamName");
  5068. pNode->SetTargetSam(V_BSTR(&var));
  5069. pNode->SetTargetName(V_BSTR(&var));
  5070. // Get the path too
  5071. var = pVs->get(L"MigratedObjects.TargetAdsPath");
  5072. pNode->SetTargetPath(V_BSTR(&var));
  5073. // Get the type too
  5074. var = pVs->get(L"MigratedObjects.Type");
  5075. strType = V_BSTR(&var);
  5076. pNode->SetType(strType);
  5077. //if they dont want to copy migrated objects, or they do but it was .
  5078. if (!(pOptions->flags & F_COPY_MIGRATED_ACCT))
  5079. {
  5080. pNode->operations = 0;
  5081. pNode->operations |= OPS_Process_Members;
  5082. // Since the account has already been created we should go ahead and mark it created
  5083. // so that the processing of group membership can continue.
  5084. pNode->MarkCreated();
  5085. }
  5086. //else if already migrated, mark already there so that we fix group membership whether we migrate the group or not
  5087. else
  5088. {
  5089. if (pOptions->flags & F_REPLACE)
  5090. pNode->operations |= OPS_Process_Members;
  5091. else
  5092. pNode->operations = OPS_Create_Account | OPS_Process_Members | OPS_Copy_Properties;
  5093. // We need to add the account to the list with the member map set so that we can add the
  5094. // member to the migrated group
  5095. pNode->mapGrpMember.insert(CGroupMemberMap::value_type(pAcct->GetSourceSam(), pAcct->GetType()));
  5096. pNode->MarkAlreadyThere();
  5097. }
  5098. if ( !pOptions->expandMemberOf )
  5099. {
  5100. // We need to add the account to the list with the member map set so that we can add the
  5101. // member to the migrated group
  5102. pNode->mapGrpMember.insert(CGroupMemberMap::value_type(pAcct->GetSourceSam(), pAcct->GetType()));
  5103. pNewAccts->Insert((TNode *) pNode);
  5104. }
  5105. }
  5106. else
  5107. {
  5108. // account has not been previously copied so we will set it up
  5109. if ( pOptions->expandMemberOf )
  5110. {
  5111. TruncateSam(achGrpName, pNode, pOptions, acctlist);
  5112. pNode->SetTargetSam(achGrpName);
  5113. FillPathInfo(pNode,pOptions);
  5114. AddPrefixSuffix(pNode, pOptions);
  5115. }
  5116. else
  5117. {
  5118. //if containing group has not been migrated, and was not to be migrated in this operation
  5119. //then we should add it to the ignore map in case it contains other objects currently
  5120. //being migrated.
  5121. m_IgnoredGrpMap.insert(CGroupNameMap::value_type((WCHAR*)achGrpName, strType));
  5122. delete pNode;
  5123. }
  5124. }
  5125. if ( pOptions->expandMemberOf )
  5126. {
  5127. if ( ! pNewAccts->InsertIfNew((TNode*) pNode) )
  5128. delete pNode;
  5129. }
  5130. }
  5131. if (bBuiltin)
  5132. {
  5133. // BUILTIN account error message
  5134. err.MsgWrite(ErrW, DCT_MSG_IGNORING_BUILTIN_S, pBuf[i].grui0_name);
  5135. Mark(L"warnings", pAcct->GetType());
  5136. }
  5137. }//end for each group
  5138. }//if got groups
  5139. if (pBuf != NULL)
  5140. NetApiBufferFree(pBuf);
  5141. // Process local groups
  5142. pBuf = NULL;
  5143. dwLevel = 0;
  5144. dwPrefMaxLen = 0xFFFFFFFF;
  5145. dwEntriesRead = 0;
  5146. dwTotalEntries = 0;
  5147. DWORD dwFlags = 0 ;
  5148. nStatus = NetUserGetLocalGroups(pOptions->srcComp, pAcct->GetName(), 0, dwFlags, (LPBYTE*)&pBuf, dwPrefMaxLen, &dwEntriesRead, &dwTotalEntries );
  5149. if (nStatus == NERR_Success)
  5150. {
  5151. for ( DWORD i = 0; i < dwEntriesRead; i++ )
  5152. {
  5153. if ( pOptions->pStatus )
  5154. {
  5155. LONG status = 0;
  5156. HRESULT hr = pOptions->pStatus->get_Status(&status);
  5157. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  5158. {
  5159. if ( !bAbortMessageWritten )
  5160. {
  5161. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  5162. bAbortMessageWritten = true;
  5163. }
  5164. break;
  5165. }
  5166. }
  5167. //see if this group is in the acctlist and successfully migrated. If so, then we
  5168. //should not need to add this to the list.
  5169. Lookup p;
  5170. p.pName = pBuf[i].grui0_name;
  5171. strType = L"group";
  5172. p.pType = strType;
  5173. TAcctReplNode * pFindNode = (TAcctReplNode *) acctlist->Find(&TNodeFindAccountName, &p);
  5174. if (pFindNode && (pFindNode->WasCreated() || pFindNode->WasReplaced()) && (bGrpsOnly))
  5175. continue;
  5176. //if we are doing group membership fixup, see if this group
  5177. //is already in the new list we are creating. If so, just add this member to the
  5178. //member map for this group node. This will save us the waste of
  5179. //recalculating all the fields and save on memory.
  5180. pFindNode = NULL;
  5181. if ((!pOptions->expandMemberOf) || ((pOptions->expandMemberOf) && (bGrpsOnly)))
  5182. {
  5183. pFindNode = (TAcctReplNode *) pNewAccts->Find(&TNodeFindAccountName, &p);
  5184. if (pFindNode)
  5185. {
  5186. pFindNode->mapGrpMember.insert(CGroupMemberMap::value_type(pAcct->GetSourceSam(), pAcct->GetType()));
  5187. continue;
  5188. }
  5189. }
  5190. //we also want to avoid the slow code below if we are expanding users' groups for inclusion
  5191. //in the migration and that group has already been added to the new list by another user
  5192. pFindNode = NULL;
  5193. if ((pOptions->expandMemberOf) && (!bGrpsOnly))
  5194. {
  5195. //if already included by another user, move on to the next group for this user
  5196. pFindNode = (TAcctReplNode *) pNewAccts->Find(&TNodeFindAccountName, &p);
  5197. if (pFindNode)
  5198. continue;
  5199. }
  5200. //if this group has already been placed in the ignore map, continue on to
  5201. //the next group
  5202. CGroupNameMap::iterator itGroupNameMap;
  5203. itGroupNameMap = m_IgnoredGrpMap.find(pBuf[i].grui0_name);
  5204. //if found, continue with the next group
  5205. if (itGroupNameMap != m_IgnoredGrpMap.end())
  5206. continue;
  5207. if (!IsBuiltinAccount(pOptions, pBuf[i].grui0_name))
  5208. {
  5209. strType = L"group";
  5210. // Get the name of the group and add it to the list if it does not already exist in the list.
  5211. wcscpy(achGrpName, pBuf[i].grui0_name);
  5212. wsprintf(achMesg, GET_STRING(IDS_EXPANDING_GROUP_ADDING_SS), pAcct->GetName(), (WCHAR*)achGrpName);
  5213. Progress(achMesg);
  5214. TAcctReplNode * pNode = new TAcctReplNode();
  5215. if (!pNode)
  5216. return FALSE;
  5217. pNode->SetName(achGrpName);
  5218. pNode->SetSourceSam(achGrpName);
  5219. pNode->SetType(strType);
  5220. pNode->SetGroupType(4);
  5221. pNode->SetTargetName(achGrpName);
  5222. //Get the source domain sid from the user
  5223. pNode->SetSourceSid(pAcct->GetSourceSid());
  5224. // Look if we have migrated the group
  5225. hr = pOptions->pDb->raw_GetAMigratedObject(achGrpName, pOptions->srcDomain, pOptions->tgtDomain, &pUnk);
  5226. if ( hr == S_OK )
  5227. {
  5228. VerifyAndUpdateMigratedTarget(pOptions, pVs);
  5229. var = pVs->get(L"MigratedObjects.SourceAdsPath");
  5230. pNode->SetSourcePath(var.bstrVal);
  5231. //Get the target name and assign that to the node
  5232. var = pVs->get(L"MigratedObjects.TargetSamName");
  5233. pNode->SetTargetName(V_BSTR(&var));
  5234. pNode->SetTargetSam(V_BSTR(&var));
  5235. // Get the path too
  5236. var = pVs->get(L"MigratedObjects.TargetAdsPath");
  5237. pNode->SetTargetPath(V_BSTR(&var));
  5238. // Get the type too
  5239. var = pVs->get(L"MigratedObjects.Type");
  5240. strType = V_BSTR(&var);
  5241. //if they dont want to copy migrated objects, or they do but it was .
  5242. if (!(pOptions->flags & F_COPY_MIGRATED_ACCT))
  5243. {
  5244. pNode->operations = 0;
  5245. pNode->operations |= OPS_Process_Members;
  5246. // Since the account has already been created we should go ahead and mark it created
  5247. // so that the processing of group membership can continue.
  5248. pNode->MarkCreated();
  5249. }
  5250. //else if already migrated, mark already there so that we fix group membership whether we migrate the group or not
  5251. else
  5252. {
  5253. if (pOptions->flags & F_REPLACE)
  5254. pNode->operations |= OPS_Process_Members;
  5255. else
  5256. pNode->operations = OPS_Create_Account | OPS_Process_Members | OPS_Copy_Properties;
  5257. // We need to add the account to the list with the member map set so that we can add the
  5258. // member to the migrated group
  5259. pNode->mapGrpMember.insert(CGroupMemberMap::value_type(pAcct->GetSourceSam(), pAcct->GetType()));
  5260. pNode->MarkAlreadyThere();
  5261. }
  5262. pNode->SetType(strType);
  5263. pNode->SetGroupType(4);
  5264. if ( !pOptions->expandMemberOf )
  5265. {
  5266. // We need to add the account to the list with the member map set so that we can add the
  5267. // member to the migrated group
  5268. pNode->mapGrpMember.insert(CGroupMemberMap::value_type(pAcct->GetSourceSam(), pAcct->GetType()));
  5269. pNewAccts->Insert((TNode *) pNode);
  5270. }
  5271. }//if migrated
  5272. else
  5273. {
  5274. // account has not been previously copied so we will set it up
  5275. if ( pOptions->expandMemberOf )
  5276. {
  5277. TruncateSam(achGrpName, pNode, pOptions, acctlist);
  5278. pNode->SetTargetSam(achGrpName);
  5279. FillPathInfo(pNode,pOptions);
  5280. AddPrefixSuffix(pNode, pOptions);
  5281. }
  5282. else
  5283. {
  5284. //if containing group has not been migrated, and was not to be migrated in this operation
  5285. //then we should add it to the ignore map in case it contains other objects currently
  5286. //being migrated.
  5287. m_IgnoredGrpMap.insert(CGroupNameMap::value_type((WCHAR*)achGrpName, strType));
  5288. delete pNode;
  5289. }
  5290. }
  5291. if ( pOptions->expandMemberOf )
  5292. {
  5293. if ( ! pNewAccts->InsertIfNew((TNode*) pNode) )
  5294. {
  5295. delete pNode;
  5296. }
  5297. }
  5298. }//end if not built-in
  5299. else
  5300. {
  5301. // BUILTIN account error message
  5302. err.MsgWrite(ErrW, DCT_MSG_IGNORING_BUILTIN_S, pBuf[i].grui0_name);
  5303. Mark(L"warnings", pAcct->GetType());
  5304. }
  5305. }//for each local group
  5306. }//if any local groups
  5307. if (pBuf != NULL)
  5308. NetApiBufferFree(pBuf);
  5309. }//end if user and should expand
  5310. //if global group, expand membership of previously migrated groups (don't need
  5311. //to enumerate groups which local groups are members of since they cannot be
  5312. //placed in another group)
  5313. if ((!_wcsicmp(pAcct->GetType(), L"group")) && (pAcct->GetGroupType() & 2))
  5314. {
  5315. //for each previously migrated group, check for account as member
  5316. for (long ndx = 0; ndx < numGroups; ndx++)
  5317. {
  5318. _bstr_t tgtAdsPath = L"";
  5319. WCHAR text[MAX_PATH];
  5320. IADsGroupPtr pGrp;
  5321. VARIANT_BOOL bIsMem = VARIANT_FALSE;
  5322. _variant_t var;
  5323. //check for abort
  5324. if ( pOptions->pStatus )
  5325. {
  5326. LONG status = 0;
  5327. HRESULT hr = pOptions->pStatus->get_Status(&status);
  5328. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  5329. {
  5330. if ( !bAbortMessageWritten )
  5331. {
  5332. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  5333. bAbortMessageWritten = true;
  5334. }
  5335. break;
  5336. }
  5337. }
  5338. /* since global groups cannot contain other groups on NT4, universal
  5339. groups did not exist on NT4.0, and both cannot contain members outside
  5340. the forest, we can ignore them */
  5341. //get this previously migrated group's type
  5342. swprintf(text,L"MigratedObjects.%ld.%s",ndx,GET_STRING(DB_Type));
  5343. _bstr_t sMOTGrpType = pVs->get(text);
  5344. if ((!wcscmp((WCHAR*)sMOTGrpType, L"ggroup")) || (!wcscmp((WCHAR*)sMOTGrpType, L"ugroup")))
  5345. continue;
  5346. //get this previously migrated group's target ADSPath
  5347. swprintf(text,L"MigratedObjects.%ld.%s",ndx,GET_STRING(DB_TargetAdsPath));
  5348. tgtAdsPath = pVs->get(text);
  5349. if (!tgtAdsPath.length())
  5350. break;
  5351. //connect to the previously migrated target group
  5352. hr = ADsGetObject(tgtAdsPath, IID_IADsGroup, (void**)&pGrp);
  5353. if (FAILED(hr))
  5354. continue;
  5355. //get that group's type
  5356. hr = pGrp->Get(L"groupType", &var);
  5357. //if that previously migrated group is a local group, see if this
  5358. //account is a member
  5359. if ((SUCCEEDED(hr)) && (var.lVal & 4))
  5360. {
  5361. //get the source object's sid from the migrate objects table
  5362. //(source AdsPath will not work)
  5363. WCHAR strSid[MAX_PATH];
  5364. WCHAR strRid[MAX_PATH];
  5365. DWORD lenStrSid = DIM(strSid);
  5366. GetTextualSid(pAcct->GetSourceSid(), strSid, &lenStrSid);
  5367. _bstr_t sSrcDmSid = strSid;
  5368. _ltow((long)(pAcct->GetSourceRid()), strRid, 10);
  5369. _bstr_t sSrcRid = strRid;
  5370. if ((!sSrcDmSid.length()) || (!sSrcRid.length()))
  5371. continue;
  5372. //build an LDAP path to the src object in the group
  5373. _bstr_t sSrcSid = sSrcDmSid + _bstr_t(L"-") + sSrcRid;
  5374. _bstr_t sSrcLDAPPath = L"LDAP://";
  5375. sSrcLDAPPath += _bstr_t(pOptions->tgtComp + 2);
  5376. sSrcLDAPPath += L"/CN=";
  5377. sSrcLDAPPath += sSrcSid;
  5378. sSrcLDAPPath += L",CN=ForeignSecurityPrincipals,";
  5379. sSrcLDAPPath += pOptions->tgtNamingContext;
  5380. //got the source LDAP path, now see if that account is in the group
  5381. hr = pGrp->IsMember(sSrcLDAPPath, &bIsMem);
  5382. //if it is a member, then add this groups to the list
  5383. if (SUCCEEDED(hr) && bIsMem)
  5384. {
  5385. _bstr_t sTemp;
  5386. //create a new node to add to the list
  5387. swprintf(text,L"MigratedObjects.%ld.%s",ndx,GET_STRING(DB_SourceSamName));
  5388. sTemp = pVs->get(text);
  5389. wsprintf(achMesg, GET_STRING(IDS_EXPANDING_GROUP_ADDING_SS), pAcct->GetName(), (WCHAR*)sTemp);
  5390. Progress(achMesg);
  5391. TAcctReplNode * pNode = new TAcctReplNode();
  5392. if (!pNode)
  5393. return FALSE;
  5394. pNode->SetName(sTemp);
  5395. pNode->SetSourceSam(sTemp);
  5396. pNode->SetTargetName(sTemp);
  5397. pNode->SetGroupType(4);
  5398. pNode->SetTargetPath(tgtAdsPath);
  5399. swprintf(text,L"MigratedObjects.%ld.%s",ndx,GET_STRING(DB_TargetSamName));
  5400. sTemp = pVs->get(text);
  5401. pNode->SetTargetSam(sTemp);
  5402. swprintf(text,L"MigratedObjects.%ld.%s",ndx,GET_STRING(DB_SourceDomainSid));
  5403. sTemp = pVs->get(text);
  5404. pNode->SetSourceSid(SidFromString((WCHAR*)sTemp));
  5405. swprintf(text,L"MigratedObjects.%ld.%s",ndx,GET_STRING(DB_SourceAdsPath));
  5406. sTemp = pVs->get(text);
  5407. pNode->SetSourcePath(sTemp);
  5408. swprintf(text,L"MigratedObjects.%ld.%s",ndx,GET_STRING(DB_Type));
  5409. sTemp = pVs->get(text);
  5410. pNode->SetType(sTemp);
  5411. if ( !(pOptions->flags & F_COPY_MIGRATED_ACCT))
  5412. {
  5413. // Since the account already exists we can tell it just to update group memberships
  5414. pNode->operations = 0;
  5415. pNode->operations |= OPS_Process_Members;
  5416. // Since the account has already been created we should go ahead and mark it created
  5417. // so that the processing of group membership can continue.
  5418. pNode->MarkCreated();
  5419. }
  5420. // We need to add the account to the list with the member map set so that we can add the
  5421. // member to the migrated group
  5422. pNode->mapGrpMember.insert(CGroupMemberMap::value_type(pAcct->GetSourceSam(), pAcct->GetType()));
  5423. pNewAccts->Insert((TNode *) pNode);
  5424. }//end if local group has as member
  5425. }//end if local group
  5426. }//end for each group
  5427. }//end if global group
  5428. }//for each account in the list
  5429. m_IgnoredGrpMap.clear(); //clear the ignored group map used to optimize group fixup
  5430. }//end if NT 4.0 objects
  5431. else
  5432. {
  5433. // Win2k objects so we need to go to active directory and query the memberOf field of each of these objects and update the
  5434. // list.
  5435. INetObjEnumeratorPtr pQuery(__uuidof(NetObjEnumerator));
  5436. LPWSTR sCols[] = { L"memberOf" };
  5437. int nCols = DIM(sCols);
  5438. SAFEARRAYBOUND bd = { nCols, 0 };
  5439. wstring strQuery;
  5440. DWORD dwf = 0;
  5441. //
  5442. // In order to determine if an object is a member of a universal group outside of the source domain
  5443. // it is necessary to query the memberOf attribute of the member in the global catalog. Querying
  5444. // this attribute in the source domain only returns universal groups that are in the source domain.
  5445. //
  5446. // Only if universal groups have been migrated is it necessary to query the global catalog therefore
  5447. // will query the migrated objects table to determine if any universal groups have been migrated. If
  5448. // universal groups have been migrated then set query global catalog to true.
  5449. //
  5450. bool bQueryGlobalCatalog = false;
  5451. _bstr_t strGlobalCatalogServer;
  5452. IVarSetPtr spUniversalGroups(__uuidof(VarSet));
  5453. IUnknownPtr spunkUniversalGroups(spUniversalGroups);
  5454. IUnknown* punkUniversalGroups = spunkUniversalGroups;
  5455. HRESULT hrUniversalGroups = pOptions->pDb->raw_GetMigratedObjectByType(
  5456. -1L, _bstr_t(L""), _bstr_t(L"ugroup"), &punkUniversalGroups
  5457. );
  5458. if (SUCCEEDED(hrUniversalGroups))
  5459. {
  5460. long lCount = spUniversalGroups->get(L"MigratedObjects");
  5461. if (lCount > 0)
  5462. {
  5463. //
  5464. // If able to retrieve name of global catalog server in source forest
  5465. // then set query global catalog to true otherwise log error message
  5466. // as ADMT will be unable to fix-up group memberships for universal
  5467. // groups that are outside of the source domain.
  5468. //
  5469. DWORD dwError = GetGlobalCatalogServer4(pOptions->srcDomain, strGlobalCatalogServer);
  5470. if ((dwError == ERROR_SUCCESS) && (strGlobalCatalogServer.length() > 0))
  5471. {
  5472. bQueryGlobalCatalog = true;
  5473. }
  5474. else
  5475. {
  5476. err.SysMsgWrite(ErrW, HRESULT_FROM_WIN32(dwError), DCT_MSG_UNABLE_TO_QUERY_GROUPS_IN_GLOBAL_CATALOG_SERVER_S, pOptions->srcDomain);
  5477. }
  5478. }
  5479. }
  5480. spunkUniversalGroups.Release();
  5481. spUniversalGroups.Release();
  5482. m_IgnoredGrpMap.clear(); //clear the ignored group map used to optimize group fixup
  5483. for ( pAcct = (TAcctReplNode*)acctlist->Head(); pAcct; pAcct = (TAcctReplNode*)pAcct->Next())
  5484. {
  5485. if ( pOptions->pStatus )
  5486. {
  5487. LONG status = 0;
  5488. HRESULT hr = pOptions->pStatus->get_Status(&status);
  5489. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  5490. {
  5491. if ( !bAbortMessageWritten )
  5492. {
  5493. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  5494. bAbortMessageWritten = true;
  5495. }
  5496. break;
  5497. }
  5498. }
  5499. // Get the Accounts Primary group. This is not in the memberOf property for some reason.(Per Richard Ault in Firstwave NewsGroup)
  5500. IADsPtr spADs;
  5501. _variant_t varRid;
  5502. _bstr_t sPath;
  5503. _bstr_t sSam;
  5504. _bstr_t sType;
  5505. _bstr_t sName;
  5506. _bstr_t sTgtName;
  5507. hr = ADsGetObject(const_cast<WCHAR*>(pAcct->GetSourcePath()), IID_IADs, (void**)&spADs);
  5508. if ( SUCCEEDED(hr))
  5509. {
  5510. VARIANT var;
  5511. VariantInit(&var);
  5512. hr = spADs->Get(L"primaryGroupID", &var);
  5513. varRid = _variant_t(var, false);
  5514. spADs.Release();
  5515. }
  5516. if ( SUCCEEDED(hr) )
  5517. {
  5518. c_array<WCHAR> achSam(LEN_Path);
  5519. c_array<WCHAR> achAcctName(LEN_Path);
  5520. DWORD cbName = LEN_Path;
  5521. SID_NAME_USE sidUse;
  5522. // Get the SID from the RID
  5523. PSID sid = GetWellKnownSid(varRid.lVal, pOptions);
  5524. // Lookup the sAMAccountNAme from the SID
  5525. if ( LookupAccountSid(pOptions->srcComp, sid, achAcctName, &cbName, achDomain, &dwDomName, &sidUse) )
  5526. {
  5527. //see if this group was not migrated due to a conflict, if so then
  5528. //we need to fix up this membership
  5529. bool bInclude = true;
  5530. Lookup p;
  5531. p.pName = (WCHAR*)sSam;
  5532. p.pType = (WCHAR*)sType;
  5533. TAcctReplNode * pFindNode = (TAcctReplNode *) acctlist->Find(&TNodeFindAccountName, &p);
  5534. if (pFindNode && (pFindNode->WasCreated() || pFindNode->WasReplaced()) && (bGrpsOnly))
  5535. bInclude = false;
  5536. // We have the SAM Account name for the Primary group so lets Fill the node and add it to the list.
  5537. // Ignore in case of the Domain Users group.
  5538. if ( varRid.lVal != DOMAIN_GROUP_RID_USERS)
  5539. {
  5540. TAcctReplNode * pNode = new TAcctReplNode();
  5541. if (!pNode)
  5542. return FALSE;
  5543. pNode->SetName(achAcctName);
  5544. pNode->SetTargetName(achAcctName);
  5545. pNode->SetSourceSam(achAcctName);
  5546. wcscpy(achSam, achAcctName);
  5547. TruncateSam(achSam, pNode, pOptions, acctlist);
  5548. pNode->SetTargetSam(achSam);
  5549. pNode->SetType(L"group");
  5550. //Get the source domain sid from the user
  5551. pNode->SetSourceSid(pAcct->GetSourceSid());
  5552. AddPrefixSuffix(pNode, pOptions);
  5553. FillPathInfo(pNode, pOptions);
  5554. // See if the object is migrated
  5555. hr = pOptions->pDb->raw_GetAMigratedObject(achAcctName, pOptions->srcDomain, pOptions->tgtDomain, &pUnk);
  5556. if ( hr == S_OK )
  5557. {
  5558. if ((!(pOptions->expandMemberOf) || ((pOptions->expandMemberOf) && ((!(pOptions->flags & F_COPY_MIGRATED_ACCT)) || (bInclude)))) ||
  5559. (!_wcsicmp(pAcct->GetType(), L"group")))
  5560. {
  5561. VerifyAndUpdateMigratedTarget(pOptions, pVs);
  5562. // Get the target name
  5563. sSam = pVs->get(L"MigratedObjects.TargetSamName");
  5564. pNode->SetTargetSam(sSam);
  5565. // Also Get the Ads path
  5566. sPath = pVs->get(L"MigratedObjects.TargetAdsPath");
  5567. pNode->SetTargetPath(sPath);
  5568. //set the target name based on the target adspath
  5569. pNode->SetTargetName(GetCNFromPath(sPath));
  5570. // Since the account is already copied we only want it to update its Group memberships
  5571. if (!(pOptions->flags & F_COPY_MIGRATED_ACCT))
  5572. {
  5573. pNode->operations = 0;
  5574. pNode->operations |= OPS_Process_Members;
  5575. // Since the account has already been created we should go ahead and mark it created
  5576. // so that the processing of group membership can continue.
  5577. pNode->MarkCreated();
  5578. }
  5579. else if (bInclude)//else if already migrated, mark already there so that we fix group membership whether we migrate the group or not
  5580. {
  5581. if (pOptions->flags & F_REPLACE)
  5582. pNode->operations |= OPS_Process_Members;
  5583. else
  5584. pNode->operations = OPS_Create_Account | OPS_Process_Members | OPS_Copy_Properties;
  5585. pNode->MarkAlreadyThere();
  5586. }
  5587. if ((!pOptions->expandMemberOf) || (!_wcsicmp(pAcct->GetType(), L"group")) || (bInclude))
  5588. {
  5589. // We need to add the account to the list with the member map set so that we can add the
  5590. // member to the migrated group
  5591. pNode->mapGrpMember.insert(CGroupMemberMap::value_type(pAcct->GetSourceSam(), pAcct->GetType()));
  5592. pNewAccts->Insert((TNode *) pNode);
  5593. }
  5594. }
  5595. }
  5596. else if ( !pOptions->expandMemberOf )
  5597. {
  5598. delete pNode;
  5599. }
  5600. if (( pOptions->expandMemberOf ) && (_wcsicmp(pAcct->GetType(), L"group")))
  5601. {
  5602. if ( ! pNewAccts->InsertIfNew(pNode) )
  5603. delete pNode;
  5604. }
  5605. }
  5606. }
  5607. if ( sid )
  5608. FreeSid(sid);
  5609. }
  5610. //
  5611. // If the global catalog needs to be queried then two queries will be performed otherwise
  5612. // only one query will be performed in the source domain.
  5613. //
  5614. // The first iteration queries the memberOf attribute of the object in the source domain
  5615. // to retrieve the local, global and universal groups in the source domain that the object
  5616. // is a member of.
  5617. //
  5618. // The second iteration queries the memberOf attribute of the object in the global catalog
  5619. // to retrieve all of the universal groups in the forest that the object is a member of.
  5620. //
  5621. // Note that if the global catalog is in the source domain that the second query will retrieve
  5622. // all of the groups in the source domain again. Also the second query will always return
  5623. // universal groups from the source domain that have already been retrieved during the first
  5624. // query. Duplicate groups are not added to the list.
  5625. //
  5626. int cQuery = bQueryGlobalCatalog ? 2 : 1;
  5627. for (int nQuery = 0; nQuery < cQuery; nQuery++)
  5628. {
  5629. IEnumVARIANTPtr spEnum;
  5630. // Build query stuff
  5631. strQuery = L"(&(sAMAccountName=";
  5632. strQuery += pAcct->GetSourceSam();
  5633. if (!_wcsicmp(pAcct->GetType(), s_ClassUser))
  5634. strQuery += L")(objectCategory=Person)(objectClass=user))";
  5635. else if (!_wcsicmp(pAcct->GetType(), s_ClassInetOrgPerson))
  5636. strQuery += L")(objectCategory=Person)(objectClass=inetOrgPerson))";
  5637. else
  5638. strQuery += L")(objectCategory=Group))";
  5639. SAFEARRAY* psaCols = SafeArrayCreate(VT_BSTR, 1, &bd);
  5640. BSTR* pData;
  5641. SafeArrayAccessData(psaCols, (void**)&pData);
  5642. for ( int i = 0; i < nCols; i++ )
  5643. pData[i] = SysAllocString(sCols[i]);
  5644. SafeArrayUnaccessData(psaCols);
  5645. //
  5646. // Query the source domain on the first iteration then
  5647. // query the global catalog on the second iteration.
  5648. //
  5649. _bstr_t strContainer;
  5650. if (nQuery == 0)
  5651. {
  5652. strContainer = pAcct->GetSourcePath();
  5653. }
  5654. else
  5655. {
  5656. //
  5657. // Constuct an ADsPath from the source object's ADsPath by specifying
  5658. // the GC provider instead of the LDAP provider and specifying the
  5659. // forest DNS name for the server instead of the source domain name.
  5660. //
  5661. // The forest DNS name must be specified so that the entire forest
  5662. // may be queried otherwise only the specified domain is queried.
  5663. //
  5664. BSTR bstr = NULL;
  5665. IADsPathnamePtr spOldPathname(CLSID_Pathname);
  5666. spOldPathname->Set(_bstr_t(pAcct->GetSourcePath()), ADS_SETTYPE_FULL);
  5667. IADsPathnamePtr spNewPathname(CLSID_Pathname);
  5668. // specify global catalog
  5669. spNewPathname->Set(_bstr_t(L"GC"), ADS_SETTYPE_PROVIDER);
  5670. // specify the source global catalog server
  5671. spNewPathname->Set(strGlobalCatalogServer, ADS_SETTYPE_SERVER);
  5672. // specify source object DN
  5673. spOldPathname->Retrieve(ADS_FORMAT_X500_DN, &bstr);
  5674. spNewPathname->Set(_bstr_t(bstr, false), ADS_SETTYPE_DN);
  5675. // retrieve ADsPath to source object in global catalog
  5676. spNewPathname->Retrieve(ADS_FORMAT_X500, &bstr);
  5677. strContainer = _bstr_t(bstr, false);
  5678. }
  5679. // Tell the object to run the query and report back to us
  5680. hr = pQuery->raw_SetQuery(strContainer, _bstr_t(pOptions->srcDomain), _bstr_t(strQuery.c_str()), ADS_SCOPE_BASE, TRUE);
  5681. if (FAILED(hr)) return FALSE;
  5682. hr = pQuery->raw_SetColumns(psaCols);
  5683. if (FAILED(hr)) return FALSE;
  5684. hr = pQuery->raw_Execute(&spEnum);
  5685. if (FAILED(hr)) return FALSE;
  5686. SafeArrayDestroy(psaCols);
  5687. VARIANT var;
  5688. VariantInit(&var);
  5689. while (spEnum->Next(1, &var, &dwf) == S_OK)
  5690. {
  5691. _variant_t varMain(var, false);
  5692. if ( pOptions->pStatus )
  5693. {
  5694. LONG status = 0;
  5695. HRESULT hr = pOptions->pStatus->get_Status(&status);
  5696. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  5697. {
  5698. if ( !bAbortMessageWritten )
  5699. {
  5700. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  5701. bAbortMessageWritten = true;
  5702. }
  5703. break;
  5704. }
  5705. }
  5706. SAFEARRAY * vals = V_ARRAY(&varMain);
  5707. // Get the VARIANT Array out
  5708. VARIANT* pDt;
  5709. SafeArrayAccessData(vals, (void**) &pDt);
  5710. _variant_t vx = pDt[0];
  5711. SafeArrayUnaccessData(vals);
  5712. if ( vx.vt & VT_ARRAY )
  5713. {
  5714. // We must have got an Array of multivalued properties
  5715. // Access the BSTR elements of this variant array
  5716. SAFEARRAY * multiVals = vx.parray;
  5717. VARIANT* pVar;
  5718. SafeArrayAccessData(multiVals, (void**) &pVar);
  5719. for ( DWORD dw = 0; dw < multiVals->rgsabound->cElements; dw++ )
  5720. {
  5721. if ( pOptions->pStatus )
  5722. {
  5723. LONG status = 0;
  5724. HRESULT hr = pOptions->pStatus->get_Status(&status);
  5725. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  5726. {
  5727. if ( !bAbortMessageWritten )
  5728. {
  5729. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  5730. bAbortMessageWritten = true;
  5731. }
  5732. break;
  5733. }
  5734. }
  5735. _bstr_t sDN = _bstr_t(V_BSTR(&pVar[dw]));
  5736. sDN = PadDN(sDN);
  5737. sPath = _bstr_t(L"LDAP://") + _bstr_t(pOptions->srcDomainDns) + _bstr_t(L"/") + sDN;
  5738. //see if the RDN of this group is in the acctlist. If so, then we should not need
  5739. //to add this to the list. I will do a find based on path and not name even though the
  5740. //list is sorted by name. This should be fine since the list is not in tree form.
  5741. Lookup p;
  5742. p.pName = (WCHAR*)sPath;
  5743. sType = L"group";
  5744. p.pType = (WCHAR*)sType;
  5745. TAcctReplNode * pFindNode = (TAcctReplNode *) acctlist->Find(&TNodeFindAccountRDN, &p);
  5746. if (pFindNode && (pFindNode->WasCreated() || pFindNode->WasReplaced()) && ((bGrpsOnly) || (pOptions->expandContainers)))
  5747. continue;
  5748. //this group has already been placed in the ignore map, continue on to
  5749. //the next group
  5750. CGroupNameMap::iterator itGroupNameMap;
  5751. itGroupNameMap = m_IgnoredGrpMap.find(sPath);
  5752. //if found, continue with the next group
  5753. if (itGroupNameMap != m_IgnoredGrpMap.end())
  5754. continue;
  5755. //if we are doing group membership fixup, see if the RDN of this group
  5756. //is already in the new list we are creating. If so, just add this member to the
  5757. //member map for this group node. This will save us the waste of
  5758. //recalculating all the fields and save on memory. (The compare is done based on the RDN which should
  5759. //be fine since this list is a tree sorted based on type and RDN)
  5760. pFindNode = NULL;
  5761. if ((!pOptions->expandMemberOf) || ((pOptions->expandMemberOf) && (bGrpsOnly)))
  5762. {
  5763. pFindNode = (TAcctReplNode *) pNewAccts->Find(&TNodeFindAccountRDN, &p);
  5764. if (pFindNode)
  5765. {
  5766. pFindNode->mapGrpMember.insert(CGroupMemberMap::value_type(pAcct->GetSourceSam(), pAcct->GetType()));
  5767. continue;
  5768. }
  5769. }
  5770. //we also want to avoid the slow code below if we are expanding users' groups for inclusion
  5771. //in the migration and that group has already been added to the new list by another user
  5772. pFindNode = NULL;
  5773. if ((pOptions->expandMemberOf) && (!bGrpsOnly))
  5774. {
  5775. //if already included by another user, move on to the next group for this user
  5776. pFindNode = (TAcctReplNode *) pNewAccts->Find(&TNodeFindAccountRDN, &p);
  5777. if (pFindNode)
  5778. continue;
  5779. }
  5780. if ((bAnySourceDomain || CompareDCPath(sPath, pAcct->GetSourcePath())) && GetSamFromPath(sPath, sSam, sType, sName, sTgtName, lgrpType, pOptions))
  5781. {
  5782. _bstr_t strSourceDomain;
  5783. if (bAnySourceDomain)
  5784. {
  5785. if (CompareDCPath(sPath, pAcct->GetSourcePath()))
  5786. {
  5787. strSourceDomain = pOptions->srcDomain;
  5788. }
  5789. else
  5790. {
  5791. strSourceDomain = GetDomainDNSFromPath(sPath);
  5792. }
  5793. }
  5794. else
  5795. {
  5796. strSourceDomain = pOptions->srcDomain;
  5797. }
  5798. //see if this group was not migrated due to a conflict, if so then
  5799. //we need to fix up this membership
  5800. bool bInclude = true;
  5801. p.pName = (WCHAR*)sSam;
  5802. p.pType = (WCHAR*)sType;
  5803. TAcctReplNode * pFindNode = (TAcctReplNode *) acctlist->Find(&TNodeFindAccountName, &p);
  5804. if (pFindNode && (pFindNode->WasCreated() || pFindNode->WasReplaced()) && (bGrpsOnly))
  5805. bInclude = false;
  5806. // Ignore the Domain users group and group already being migrated
  5807. if ((_wcsicmp(sSam, achDomUsers) != 0) && (bInclude))
  5808. {
  5809. wsprintf(achMesg, GET_STRING(IDS_EXPANDING_GROUP_ADDING_SS), pAcct->GetName(), (WCHAR*) sSam);
  5810. Progress(achMesg);
  5811. TAcctReplNode * pNode = new TAcctReplNode();
  5812. if (!pNode)
  5813. return FALSE;
  5814. pNode->SetName(sName);
  5815. pNode->SetTargetName(sTgtName);
  5816. pNode->SetType(sType);
  5817. pNode->SetSourcePath(sPath);
  5818. pNode->SetSourceSam(sSam);
  5819. c_array<WCHAR> achSam(LEN_Path);
  5820. wcscpy(achSam, sSam);
  5821. TruncateSam(achSam, pNode, pOptions, acctlist);
  5822. pNode->SetTargetSam(achSam);
  5823. //Get the source domain sid from the user
  5824. pNode->SetSourceSid(pAcct->GetSourceSid());
  5825. AddPrefixSuffix(pNode, pOptions);
  5826. pNode->SetGroupType(lgrpType);
  5827. // See if the object is migrated
  5828. hr = pOptions->pDb->raw_GetAMigratedObject((WCHAR*)sSam, strSourceDomain, pOptions->tgtDomain, &pUnk);
  5829. if ( hr == S_OK )
  5830. {
  5831. if ((!(pOptions->expandMemberOf) || ((pOptions->expandMemberOf) && ((!(pOptions->flags & F_COPY_MIGRATED_ACCT)) || (bInclude)))) ||
  5832. (!_wcsicmp(pAcct->GetType(), L"group")))
  5833. {
  5834. VerifyAndUpdateMigratedTarget(pOptions, pVs);
  5835. // Get the target name
  5836. sSam = pVs->get(L"MigratedObjects.TargetSamName");
  5837. pNode->SetTargetSam(sSam);
  5838. // Also Get the Ads path
  5839. sPath = pVs->get(L"MigratedObjects.TargetAdsPath");
  5840. pNode->SetTargetPath(sPath);
  5841. //set the target name based on the target adspath
  5842. pNode->SetTargetName(GetCNFromPath(sPath));
  5843. // Since the account is already copied we only want it to update its Group memberships
  5844. if (!(pOptions->flags & F_COPY_MIGRATED_ACCT))
  5845. {
  5846. pNode->operations = 0;
  5847. pNode->operations |= OPS_Process_Members;
  5848. // Since the account has already been created we should go ahead and mark it created
  5849. // so that the processing of group membership can continue.
  5850. pNode->MarkCreated();
  5851. }
  5852. else if (bInclude)//else if already migrated, mark already there so that we fix group membership whether we migrate the group or not
  5853. {
  5854. if (pOptions->flags & F_REPLACE)
  5855. pNode->operations |= OPS_Process_Members;
  5856. else
  5857. pNode->operations = OPS_Create_Account | OPS_Process_Members | OPS_Copy_Properties;
  5858. pNode->MarkAlreadyThere();
  5859. }
  5860. if ((!pOptions->expandMemberOf) || (!_wcsicmp(pAcct->GetType(), L"group")) || (bInclude))
  5861. {
  5862. // We need to add the account to the list with the member map set so that we can add the
  5863. // member to the migrated group
  5864. pNode->mapGrpMember.insert(CGroupMemberMap::value_type(pAcct->GetSourceSam(), pAcct->GetType()));
  5865. pNewAccts->Insert((TNode *) pNode);
  5866. pNode = NULL;
  5867. }
  5868. }
  5869. }
  5870. else if ( ! pOptions->expandMemberOf )
  5871. {
  5872. //if containing group has not been migrated, and was not to be migrated in this operation
  5873. //then we should add it to the ignore map in case it contains other objects currently
  5874. //being migrated. Store the path as the key so we don't have to call GetSamFromPath to
  5875. //see if we should ignore.
  5876. m_IgnoredGrpMap.insert(CGroupNameMap::value_type(sPath, sSam));
  5877. delete pNode;
  5878. pNode = NULL;
  5879. }
  5880. if (pNode)
  5881. {
  5882. if (( pOptions->expandMemberOf ) && (_wcsicmp(pAcct->GetType(), L"group")))
  5883. {
  5884. if (! pNewAccts->InsertIfNew(pNode) )
  5885. delete pNode;
  5886. }
  5887. else
  5888. {
  5889. delete pNode;
  5890. }
  5891. }
  5892. }
  5893. }
  5894. }
  5895. SafeArrayUnaccessData(multiVals);
  5896. }
  5897. }
  5898. }
  5899. }//for each object being migrated
  5900. m_IgnoredGrpMap.clear(); //clear the ignored group map used to optimize group fixup
  5901. }
  5902. rc = TRUE;
  5903. }
  5904. return rc;
  5905. }
  5906. HRESULT CAcctRepl::BuildSidPath(
  5907. IADs * pAds, //in- pointer to the object whose sid we are retrieving.
  5908. WCHAR * sSidPath, //out-path to the LDAP://<SID=###> object
  5909. WCHAR * sSam, //out-Sam name of the object
  5910. WCHAR * sDomain, //out-Domain name where this object resides.
  5911. Options * pOptions, //in- Options
  5912. PSID * ppSid //out- pointer to the binary SID
  5913. )
  5914. {
  5915. HRESULT hr = S_OK;
  5916. DWORD cbName = LEN_Path, cbDomain = LEN_Path;
  5917. PSID sid = NULL;
  5918. SID_NAME_USE use;
  5919. _variant_t var;
  5920. if (!pAds)
  5921. return E_POINTER;
  5922. // Get the object's SID
  5923. hr = pAds->Get(_bstr_t(L"objectSid"), &var);
  5924. if ( SUCCEEDED(hr) )
  5925. {
  5926. sid = SafeCopySid((PSID)var.parray->pvData);
  5927. if (sid)
  5928. {
  5929. if (LookupAccountSid(pOptions->srcComp, sid, sSam, &cbName, sDomain, &cbDomain, &use))
  5930. {
  5931. //
  5932. // If SID type is domain then the object has the same name as the domain. There is
  5933. // a known issue with the WinNT provider where the ObjectSid attribute is returned
  5934. // incorrectly for objects that have the same name as the the domain. The WinNT
  5935. // provider code passes only the account name to LookupAccountName and not the
  5936. // complete NT4 format name which includes the domain. Therefore LookupAccountName
  5937. // correctly returns the SID for the domain and not the account.
  5938. //
  5939. // This situation is detected by looking at the SID type which will be domain in
  5940. // this case. If this is the case then retrieve the correct account SID by using the
  5941. // complete NT4 account name format. The SAM name is filled in from the path.
  5942. //
  5943. if (use == SidTypeDomain)
  5944. {
  5945. //
  5946. // Retrieve path of object.
  5947. //
  5948. BSTR bstr = NULL;
  5949. hr = pAds->get_ADsPath(&bstr);
  5950. if (SUCCEEDED(hr))
  5951. {
  5952. //
  5953. // Retrieve only name component of path.
  5954. //
  5955. CADsPathName pnPathName(_bstr_t(bstr, false), ADS_SETTYPE_FULL);
  5956. _bstr_t strName = pnPathName.Retrieve(ADS_FORMAT_LEAF);
  5957. if ((PCWSTR)strName)
  5958. {
  5959. //
  5960. // The name component is the SAM name.
  5961. //
  5962. wcsncpy(sSam, strName, LEN_Path);
  5963. sSam[LEN_Path - 1] = L'\0';
  5964. //
  5965. // Construct the complete NT4 name.
  5966. //
  5967. _bstr_t strNT4Name = sDomain;
  5968. strNT4Name += _T("\\");
  5969. strNT4Name += strName;
  5970. //
  5971. // Get size of buffer required for SID.
  5972. //
  5973. DWORD cbSid = 0;
  5974. cbDomain = LEN_Path;
  5975. LookupAccountName(
  5976. pOptions->srcComp,
  5977. strNT4Name,
  5978. NULL,
  5979. &cbSid,
  5980. sDomain,
  5981. &cbDomain,
  5982. &use
  5983. );
  5984. //
  5985. // The last error should be insufficient buffer size.
  5986. //
  5987. DWORD dwError = GetLastError();
  5988. if (dwError == ERROR_INSUFFICIENT_BUFFER)
  5989. {
  5990. // Create buffer for SID.
  5991. var.Clear();
  5992. var.parray = SafeArrayCreateVector(VT_UI1, 0, cbSid);
  5993. if (var.parray)
  5994. {
  5995. var.vt = VT_ARRAY|VT_UI1;
  5996. cbDomain = LEN_Path;
  5997. //
  5998. // Retrieve correct account SID.
  5999. //
  6000. BOOL bLookup = LookupAccountName(
  6001. pOptions->srcComp,
  6002. strNT4Name,
  6003. var.parray->pvData,
  6004. &cbSid,
  6005. sDomain,
  6006. &cbDomain,
  6007. &use
  6008. );
  6009. if (bLookup)
  6010. {
  6011. FreeSid(sid);
  6012. sid = SafeCopySid((PSID)var.parray->pvData);
  6013. }
  6014. else
  6015. {
  6016. DWORD dwError = GetLastError();
  6017. hr = HRESULT_FROM_WIN32(dwError);
  6018. }
  6019. }
  6020. else
  6021. {
  6022. hr = E_OUTOFMEMORY;
  6023. }
  6024. }
  6025. else
  6026. {
  6027. hr = HRESULT_FROM_WIN32(dwError);
  6028. }
  6029. }
  6030. else
  6031. {
  6032. hr = E_FAIL;
  6033. }
  6034. }
  6035. }
  6036. if (SUCCEEDED(hr))
  6037. {
  6038. //
  6039. // Construct SID path string.
  6040. //
  6041. VariantSidToString(var);
  6042. _bstr_t strSid = var;
  6043. if ((PCWSTR)strSid)
  6044. {
  6045. wcscpy(sSidPath, L"LDAP://<SID=");
  6046. wcscat(sSidPath, (PCWSTR)strSid);
  6047. wcscat(sSidPath, L">");
  6048. }
  6049. else
  6050. {
  6051. hr = E_FAIL;
  6052. }
  6053. }
  6054. }
  6055. else
  6056. {
  6057. DWORD dwError = GetLastError();
  6058. hr = HRESULT_FROM_WIN32(dwError);
  6059. }
  6060. }
  6061. else
  6062. {
  6063. DWORD dwError = GetLastError();
  6064. hr = HRESULT_FROM_WIN32(dwError);
  6065. }
  6066. }
  6067. if ( SUCCEEDED(hr) )
  6068. {
  6069. (*ppSid) = sid;
  6070. }
  6071. else
  6072. {
  6073. if (sid)
  6074. {
  6075. FreeSid(sid);
  6076. }
  6077. (*ppSid) = NULL;
  6078. }
  6079. return hr;
  6080. }
  6081. BOOL
  6082. CAcctRepl::CanMoveInMixedMode(TAcctReplNode *pAcct,TNodeListSortable * acctlist, Options * pOptions)
  6083. {
  6084. HRESULT hr = S_OK;
  6085. BOOL ret = TRUE;
  6086. IADsGroup * pGroup = NULL;
  6087. IADsMembers * pMembers = NULL;
  6088. IEnumVARIANT * pEnum = NULL;
  6089. IDispatch * pDisp = NULL;
  6090. IADs * pAds = NULL;
  6091. _bstr_t sSam;
  6092. _variant_t vSam;
  6093. BSTR sClass;
  6094. IVarSetPtr pVs(__uuidof(VarSet));
  6095. IUnknown * pUnk = NULL;
  6096. pVs->QueryInterface(IID_IUnknown, (void**)&pUnk);
  6097. // in case of a global group we need to check if we have/are migrating all the members. If we
  6098. // are then we can move it and if not then we need to use the parallel group theory.
  6099. if ( pAcct->GetGroupType() & 2 )
  6100. {
  6101. // This is a global group. What we need to do now is to see if we have/will migrate all its members.
  6102. // First enumerate the members.
  6103. hr = ADsGetObject(const_cast<WCHAR*>(pAcct->GetSourcePath()), IID_IADsGroup, (void**)&pGroup);
  6104. if ( SUCCEEDED(hr) )
  6105. hr = pGroup->Members(&pMembers);
  6106. if (SUCCEEDED(hr))
  6107. hr = pMembers->get__NewEnum((IUnknown**)&pEnum);
  6108. if ( SUCCEEDED(hr) )
  6109. {
  6110. _variant_t var;
  6111. DWORD fetch = 0;
  6112. while ( pEnum->Next(1, &var, &fetch) == S_OK )
  6113. {
  6114. // Get the sAMAccount name from the object so we can do the lookups
  6115. pDisp = V_DISPATCH(&var);
  6116. hr = pDisp->QueryInterface(IID_IADs, (void**)&pAds);
  6117. if (SUCCEEDED(hr))
  6118. hr = pAds->Get(L"sAMAccountName", &vSam);
  6119. if (SUCCEEDED(hr))
  6120. hr = pAds->get_Class(&sClass);
  6121. if ( SUCCEEDED(hr))
  6122. {
  6123. sSam = vSam;
  6124. // To see if we will migrate all its members check the account list.
  6125. Lookup lup;
  6126. lup.pName = (WCHAR*) sSam;
  6127. lup.pType = (WCHAR*) sClass;
  6128. TAcctReplNode * pNode = (TAcctReplNode*)acctlist->Find(&TNodeFindAccountName, &lup);
  6129. if ( !pNode )
  6130. {
  6131. // This member is not in the account list therefore cannot move this group.
  6132. ret = FALSE;
  6133. err.MsgWrite(0,DCT_MSG_CANNOT_MOVE_GG_FROM_MIXED_MODE_SS,pAcct->GetSourceSam(),(WCHAR*)sSam);
  6134. break;
  6135. }
  6136. }
  6137. }
  6138. if ( pEnum ) pEnum->Release();
  6139. if ( pAds ) pAds->Release();
  6140. if ( pGroup ) pGroup->Release();
  6141. if ( pMembers ) pMembers->Release();
  6142. }
  6143. }
  6144. else
  6145. // Local groups can be moved, if all of their members are removed first
  6146. ret = TRUE;
  6147. return ret;
  6148. }
  6149. HRESULT
  6150. CAcctRepl::CheckClosedSetGroups(
  6151. Options * pOptions, // in - options for the migration
  6152. TNodeListSortable * pAcctList, // in - list of accounts to migrate
  6153. ProgressFn * progress, // in - progress function to display progress messages
  6154. IStatusObj * pStatus // in - status object to support cancellation
  6155. )
  6156. {
  6157. HRESULT hr = S_OK;
  6158. TNodeListEnum e;
  6159. TAcctReplNode* pAcct;
  6160. if ( pAcctList->IsTree() )
  6161. pAcctList->ToSorted();
  6162. for ( pAcct = (TAcctReplNode*)e.OpenFirst(pAcctList) ; pAcct ; pAcct = (TAcctReplNode*)e.Next() )
  6163. {
  6164. if ( (pAcct->operations & OPS_Create_Account ) == 0 )
  6165. continue;
  6166. if ( !UStrICmp(pAcct->GetType(),s_ClassUser) || !UStrICmp(pAcct->GetType(),s_ClassInetOrgPerson) )
  6167. {
  6168. // users, we will always move
  6169. err.MsgWrite(0,DCT_MSG_USER_WILL_BE_MOVED_S,pAcct->GetName());
  6170. pAcct->operations = OPS_Move_Object | OPS_Call_Extensions;
  6171. }
  6172. else if (! UStrICmp(pAcct->GetType(),L"group") )
  6173. {
  6174. if ( CanMoveInMixedMode(pAcct,pAcctList,pOptions) )
  6175. {
  6176. pAcct->operations = OPS_Move_Object | OPS_Call_Extensions;
  6177. err.MsgWrite(0,DCT_MSG_GROUP_WILL_BE_MOVED_S,pAcct->GetName());
  6178. }
  6179. else
  6180. {
  6181. hr = S_FALSE;
  6182. }
  6183. }
  6184. else
  6185. {
  6186. err.MsgWrite(0,DCT_MSG_CANT_MOVE_UNKNOWN_TYPE_SS,pAcct->GetName(), pAcct->GetType());
  6187. }
  6188. }
  6189. e.Close();
  6190. pAcctList->SortedToTree();
  6191. return hr;
  6192. }
  6193. void LoadNecessaryFunctions()
  6194. {
  6195. HMODULE hPro = LoadLibrary(L"advapi32.dll");
  6196. if ( hPro )
  6197. ConvertStringSidToSid = (TConvertStringSidToSid)GetProcAddress(hPro, "ConvertStringSidToSidW");
  6198. else
  6199. {
  6200. err.SysMsgWrite(ErrE, GetLastError(), DCT_MSG_LOAD_LIBRARY_FAILED_SD, L"advapi32.dll");
  6201. Mark(L"errors", L"generic");
  6202. }
  6203. }
  6204. //---------------------------------------------------------------------------------------------------------
  6205. // MoveObj2k - This function moves objects within a forest.
  6206. //---------------------------------------------------------------------------------------------------------
  6207. int CAcctRepl::MoveObj2K(
  6208. Options * pOptions, //in -Options that we recieved from the user
  6209. TNodeListSortable * acctlist, //in -AcctList of accounts to be copied.
  6210. ProgressFn * progress, //in -Progress Function to display messages
  6211. IStatusObj * pStatus // in -status object to support cancellation
  6212. )
  6213. {
  6214. HRESULT hr = S_OK;
  6215. MCSDCTWORKEROBJECTSLib::IAccessCheckerPtr pAccess(__uuidof(MCSDCTWORKEROBJECTSLib::AccessChecker));
  6216. IObjPropBuilderPtr pClass(__uuidof(ObjPropBuilder));
  6217. TNodeListSortable pMemberOf, pMember;
  6218. c_array<WCHAR> achMesg(LEN_Path);
  6219. LoadNecessaryFunctions();
  6220. FillNamingContext(pOptions);
  6221. // Make sure we are connecting to the DC that has RID Pool Allocator FSMO role.
  6222. hr = GetRidPoolAllocator(pOptions);
  6223. if (FAILED(hr))
  6224. {
  6225. return hr;
  6226. }
  6227. // Since we are in the same forest we need to turn off the AddSidHistory functionality.
  6228. // because it is always going to fail.
  6229. pOptions->flags &= ~F_AddSidHistory;
  6230. BOOL bSrcNative = false;
  6231. BOOL bTgtNative = false;
  6232. _variant_t var;
  6233. _bstr_t sTargetDomain = pOptions->tgtDomain;
  6234. pAccess->raw_IsNativeMode(pOptions->srcDomain, (long*)&bSrcNative);
  6235. pAccess->raw_IsNativeMode(pOptions->tgtDomain, (long*)&bTgtNative);
  6236. IMoverPtr pMover(__uuidof(Mover));
  6237. TNodeTreeEnum e;
  6238. // build the source and target DSA names
  6239. _bstr_t sourceDSA;
  6240. _bstr_t targetDSA;
  6241. TAcctReplNode * pAcct = NULL;
  6242. sourceDSA = pOptions->srcCompDns;
  6243. targetDSA = pOptions->tgtCompDns;
  6244. err.LogClose();
  6245. // In this call the fourth parameter is the log file name. We are piggy backing this value
  6246. // so that we will not have to change the interface for the IMover object.
  6247. hr = pMover->raw_Connect(sourceDSA, targetDSA, pOptions->authDomain,
  6248. pOptions->authUser, pOptions->authPassword, pOptions->logFile, L"", L"");
  6249. err.LogOpen(pOptions->logFile, 1);
  6250. if ( SUCCEEDED(hr) )
  6251. {
  6252. // make sure the account list is in the proper format
  6253. if (acctlist->IsTree()) acctlist->ToSorted();
  6254. acctlist->CompareSet(&TNodeCompareAccountType);
  6255. // sort the account list by Source Type\Source Name
  6256. if ( acctlist->IsTree() ) acctlist->ToSorted();
  6257. acctlist->CompareSet(&TNodeCompareAccountType);
  6258. acctlist->SortedToScrambledTree();
  6259. acctlist->Sort(&TNodeCompareAccountType);
  6260. acctlist->Balance();
  6261. pMemberOf.CompareSet(&TNodeCompareMember);
  6262. pMember.CompareSet(&TNodeCompareMember);
  6263. /* The account list is sorted in descending order by type, then in ascending order by object name
  6264. this means that the user accounts will be moved first.
  6265. Here are the steps we will perform for native mode MoveObject.
  6266. 1. For each object to be copied, Remove (and record) the group memberships
  6267. 2. If the object is a group, convert it to universal (to avoid having to remove any members that are not
  6268. being migrated.
  6269. 3. Move the object.
  6270. 4. For each migrated group that was converted to a universal group, change it back to its original
  6271. type, if possible.
  6272. 5. Restore the group memberships for all objects.
  6273. Here are the steps we will perform for mixed mode MoveObject
  6274. 1. If closed set is not achieved, copy the groups, rather than moving them
  6275. 2. For each object to be copied, Remove (and record) the group memberships
  6276. 3. If the object is a group, remove all of its members
  6277. 4. Move the object.
  6278. 5. For each migrated group try to add all of its members back
  6279. 6. Restore the group memberships for all objects.
  6280. */
  6281. if ( ! bSrcNative )
  6282. {
  6283. //
  6284. // If a closed-set is not achieved.
  6285. //
  6286. if (CheckClosedSetGroups(pOptions, acctlist, progress, pStatus) != S_OK)
  6287. {
  6288. bool bAllow = false;
  6289. //
  6290. // Check whether user has enabled non closed-set moves.
  6291. //
  6292. TRegKey key;
  6293. if (key.Open(REGKEY_ADMT) == ERROR_SUCCESS)
  6294. {
  6295. DWORD dwAllow = 0;
  6296. if (key.ValueGetDWORD(REGVAL_ALLOW_NON_CLOSEDSET_MOVE, &dwAllow) == ERROR_SUCCESS)
  6297. {
  6298. if (dwAllow)
  6299. {
  6300. bAllow = true;
  6301. }
  6302. }
  6303. }
  6304. //
  6305. // If user has allowed non closed-set moves generate warning message as a reminder
  6306. // that non closed-set moves are currently allowed otherwise generate error message
  6307. // and throw exception to stop migration task.
  6308. //
  6309. if (bAllow)
  6310. {
  6311. err.MsgWrite(ErrW, DCT_MSG_MOVE_NON_CLOSEDSET);
  6312. }
  6313. else
  6314. {
  6315. Mark(L"errors", L"generic");
  6316. err.MsgWrite(ErrE, DCT_MSG_CANNOT_MOVE_NON_CLOSEDSET);
  6317. _com_issue_error(HRESULT_FROM_WIN32(ERROR_DS_CROSS_DOM_MOVE_ERROR));
  6318. }
  6319. }
  6320. // this will copy any groups that cannot be moved from the source domain
  6321. // if groups are copied in this fashion, SIDHistory cannot be used, and reACLing must be performed
  6322. CopyObj2K(pOptions,acctlist,progress,pStatus);
  6323. }
  6324. // This is the start of the Move loop
  6325. try {
  6326. for ( pAcct = (TAcctReplNode *)e.OpenFirst(acctlist);
  6327. pAcct;
  6328. pAcct = (TAcctReplNode *)e.Next() )
  6329. {
  6330. if ( m_pExt )
  6331. {
  6332. if ( pAcct->operations & OPS_Call_Extensions )
  6333. {
  6334. m_pExt->Process(pAcct,sTargetDomain,pOptions,TRUE);
  6335. }
  6336. }
  6337. // Do we need to abort ?
  6338. if ( pStatus )
  6339. {
  6340. LONG status = 0;
  6341. HRESULT hr = pStatus->get_Status(&status);
  6342. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  6343. {
  6344. if ( !bAbortMessageWritten )
  6345. {
  6346. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  6347. bAbortMessageWritten = true;
  6348. }
  6349. break;
  6350. }
  6351. }
  6352. // in the mixed-mode case, skip any accounts that we've already copied
  6353. if ( ! bSrcNative && ((pAcct->operations & OPS_Move_Object)==0 ) )
  6354. continue;
  6355. if ( bSrcNative &&
  6356. ( (pAcct->operations & OPS_Create_Account)==0 )
  6357. )
  6358. continue;
  6359. //if the UPN name conflicted, then the UPNUpdate extension set the hr to
  6360. //ERROR_OBJECT_ALREADY_EXISTS. If so, set flag for "no change" mode
  6361. if (pAcct->GetHr() == ERROR_OBJECT_ALREADY_EXISTS)
  6362. {
  6363. pAcct->bUPNConflicted = TRUE;
  6364. pAcct->SetHr(S_OK);
  6365. }
  6366. Mark(L"processed", pAcct->GetType());
  6367. c_array<WCHAR> achMesg(LEN_Path);
  6368. achMesg[0] = 0;
  6369. if ( progress )
  6370. progress(achMesg);
  6371. //
  6372. // If updating of user rights is specified then retrieve list of rights
  6373. // for source account before the object is moved as object deletion from
  6374. // a domain will automatically remove rights in the domain if the domain
  6375. // is .NET or later.
  6376. //
  6377. if (m_UpdateUserRights)
  6378. {
  6379. HRESULT hrRights = EnumerateAccountRights(FALSE, pAcct);
  6380. }
  6381. // We need to remove this object from any global groups so that it can be moved.
  6382. if ( ! pOptions->nochange )
  6383. {
  6384. wsprintf(achMesg, (WCHAR*)GET_STRING(DCT_MSG_RECORD_REMOVE_MEMBEROF_S), pAcct->GetName());
  6385. Progress(achMesg);
  6386. RecordAndRemoveMemberOf( pOptions, pAcct, &pMemberOf );
  6387. }
  6388. if ( _wcsicmp(pAcct->GetType(), L"group") == 0 || _wcsicmp(pAcct->GetType(), L"lgroup") == 0 )
  6389. {
  6390. // First, record the group type, so we can change it back later if needed
  6391. IADsGroup * pGroup = NULL;
  6392. VARIANT var;
  6393. VariantInit(&var);
  6394. // get the group type
  6395. hr = ADsGetObject( const_cast<WCHAR*>(pAcct->GetSourcePath()), IID_IADsGroup, (void**) &pGroup);
  6396. if (SUCCEEDED(hr) )
  6397. {
  6398. hr = pGroup->Get(L"groupType", &var);
  6399. pGroup->Release();
  6400. }
  6401. if ( SUCCEEDED(hr) )
  6402. {
  6403. pAcct->SetGroupType(var.lVal);
  6404. }
  6405. else
  6406. {
  6407. pAcct->SetGroupType(0);
  6408. }
  6409. // make sure it is native and group is a global group
  6410. if ( bSrcNative && bTgtNative )
  6411. {
  6412. if ( pAcct->GetGroupType() & 2)
  6413. {
  6414. // We are going to convert the group type to universal groups so we can easily move them
  6415. wsprintf(achMesg, GET_STRING(DCT_MSG_CHANGE_GROUP_TYPE_S), pAcct->GetName());
  6416. Progress(achMesg);
  6417. // Convert global groups to universal, so we can move them without de-populating
  6418. if ( ! pOptions->nochange )
  6419. {
  6420. c_array<WCHAR> achPath(LEN_Path);
  6421. DWORD nPathLen = LEN_Path;
  6422. StuffComputerNameinLdapPath(achPath, nPathLen, const_cast<WCHAR*>(pAcct->GetSourcePath()), pOptions, FALSE);
  6423. hr = pClass->raw_ChangeGroupType(achPath, 8);
  6424. if (SUCCEEDED(hr))
  6425. {
  6426. pAcct->MarkGroupScopeChanged();
  6427. }
  6428. }
  6429. else
  6430. {
  6431. hr = S_OK;
  6432. }
  6433. if (FAILED(hr))
  6434. {
  6435. err.SysMsgWrite(ErrE,hr,DCT_MSG_FAILED_TO_CONVERT_GROUP_TO_UNIVERSAL_SD, pAcct->GetSourceSam(), hr);
  6436. pAcct->MarkError();
  6437. Mark(L"errors", pAcct->GetType());
  6438. continue; // skip any further processing of this group.
  6439. }
  6440. }
  6441. else if ( ! ( pAcct->GetGroupType() & 8 ) ) // don't need to depopulate universal groups
  6442. {
  6443. // For local groups we are going to depopulate the group and move it and then repopulate it.
  6444. // In mixed mode, there are no universal groups, so we must depopulate all of the groups
  6445. // before we can move them out to the new domain. We will RecordAndRemove members of all Group type
  6446. // move them to the target domain and then change their type to Universal and then add all of its
  6447. // members back to it.
  6448. wsprintf(achMesg, GET_STRING(DCT_MSG_RECORD_REMOVE_MEMBER_S), pAcct->GetName());
  6449. Progress(achMesg);
  6450. RecordAndRemoveMember(pOptions, pAcct, &pMember);
  6451. }
  6452. }
  6453. else
  6454. {
  6455. // for mixed mode source domain, we must depopulate all of the groups
  6456. wsprintf(achMesg, GET_STRING(DCT_MSG_RECORD_REMOVE_MEMBER_S), pAcct->GetName());
  6457. if ( progress )
  6458. progress(achMesg);
  6459. RecordAndRemoveMember(pOptions, pAcct, &pMember);
  6460. }
  6461. }
  6462. BOOL bObjectExists = DoesTargetObjectAlreadyExist(pAcct, pOptions);
  6463. if ( bObjectExists )
  6464. {
  6465. // The object exists, see if we need to rename
  6466. if ( wcslen(pOptions->prefix) > 0 )
  6467. {
  6468. // Add a prefix to the account name
  6469. c_array<WCHAR> achTgt(LEN_Path);
  6470. c_array<WCHAR> achPref(LEN_Path);
  6471. c_array<WCHAR> achSuf(LEN_Path);
  6472. c_array<WCHAR> achTempSam(LEN_Path);
  6473. _variant_t varStr;
  6474. // find the '=' sign
  6475. wcscpy(achTgt, pAcct->GetTargetName());
  6476. for ( DWORD z = 0; z < wcslen(achTgt); z++ )
  6477. {
  6478. if ( achTgt[z] == L'=' ) break;
  6479. }
  6480. if ( z < wcslen(achTgt) )
  6481. {
  6482. // Get the prefix part ex.CN=
  6483. wcsncpy(achPref, achTgt, z+1);
  6484. achPref[z+1] = 0;
  6485. wcscpy(achSuf, achTgt+z+1);
  6486. }
  6487. // Build the target string with the Prefix
  6488. wsprintf(achTgt, L"%s%s%s", (WCHAR*)achPref, pOptions->prefix, (WCHAR*)achSuf);
  6489. pAcct->SetTargetName(achTgt);
  6490. // truncate to allow prefix/suffix to fit in 20 characters.
  6491. int resLen = wcslen(pOptions->prefix) + wcslen(pAcct->GetTargetSam());
  6492. if ( !_wcsicmp(pAcct->GetType(), L"computer") )
  6493. {
  6494. // Computer name can be only 15 characters long + $
  6495. if ( resLen > MAX_COMPUTERNAME_LENGTH + 1 )
  6496. {
  6497. c_array<WCHAR> achTruncatedSam(LEN_Path);
  6498. wcscpy(achTruncatedSam, pAcct->GetTargetSam());
  6499. if ( wcslen( pOptions->globalSuffix ) )
  6500. {
  6501. // We must remove the global suffix if we had one.
  6502. achTruncatedSam[wcslen(achTruncatedSam) - wcslen(pOptions->globalSuffix)] = L'\0';
  6503. }
  6504. int truncate = MAX_COMPUTERNAME_LENGTH + 1 - wcslen(pOptions->prefix) - wcslen(pOptions->globalSuffix);
  6505. if ( truncate < 1 ) truncate = 1;
  6506. wcsncpy(achTempSam, achTruncatedSam, truncate - 1);
  6507. achTempSam[truncate - 1] = L'\0';
  6508. wcscat(achTempSam, pOptions->globalSuffix);
  6509. wcscat(achTempSam, L"$");
  6510. }
  6511. else
  6512. wcscpy(achTempSam, pAcct->GetTargetSam());
  6513. // Add the prefix
  6514. wsprintf(achTgt, L"%s%s", pOptions->prefix,(WCHAR*)achTempSam);
  6515. }
  6516. else if ( !_wcsicmp(pAcct->GetType(), L"group") )
  6517. {
  6518. if ( resLen > 64 )
  6519. {
  6520. int truncate = 64 - wcslen(pOptions->prefix);
  6521. if ( truncate < 0 ) truncate = 0;
  6522. wcsncpy(achTempSam, pAcct->GetTargetSam(), truncate);
  6523. achTempSam[truncate] = L'\0';
  6524. }
  6525. else
  6526. wcscpy(achTempSam, pAcct->GetTargetSam());
  6527. // Add the prefix
  6528. wsprintf(achTgt, L"%s%s", pOptions->prefix,(WCHAR*)achTempSam);
  6529. }
  6530. else
  6531. {
  6532. if ( resLen > 20 )
  6533. {
  6534. c_array<WCHAR> achTruncatedSam(LEN_Path);
  6535. wcscpy(achTruncatedSam, pAcct->GetTargetSam());
  6536. if ( wcslen( pOptions->globalSuffix ) )
  6537. {
  6538. // We must remove the global suffix if we had one.
  6539. achTruncatedSam[wcslen(achTruncatedSam) - wcslen(pOptions->globalSuffix)] = L'\0';
  6540. }
  6541. int truncate = 20 - wcslen(pOptions->prefix) - wcslen(pOptions->globalSuffix);
  6542. if ( truncate < 0 ) truncate = 0;
  6543. wcsncpy(achTempSam, achTruncatedSam, truncate);
  6544. achTempSam[truncate] = L'\0';
  6545. wcscat(achTempSam, pOptions->globalSuffix);
  6546. }
  6547. else
  6548. wcscpy(achTempSam, pAcct->GetTargetSam());
  6549. // Add the prefix
  6550. wsprintf(achTgt, L"%s%s", pOptions->prefix,(WCHAR*)achTempSam);
  6551. }
  6552. pAcct->SetTargetSam(achTgt);
  6553. if ( DoesTargetObjectAlreadyExist(pAcct, pOptions) )
  6554. {
  6555. // Double collision lets log a message and forget about this account
  6556. pAcct->MarkAlreadyThere();
  6557. err.MsgWrite(ErrE, DCT_MSG_PREF_ACCOUNT_EXISTS_S, pAcct->GetTargetSam());
  6558. Mark(L"errors",pAcct->GetType());
  6559. continue;
  6560. }
  6561. }
  6562. else if ( wcslen(pOptions->suffix) > 0 )
  6563. {
  6564. // Add a suffix to the account name
  6565. c_array<WCHAR> achTgt(LEN_Path);
  6566. c_array<WCHAR> achTempSam(LEN_Path);
  6567. wsprintf(achTgt, L"%s%s", pAcct->GetTargetName(), pOptions->suffix);
  6568. pAcct->SetTargetName(achTgt);
  6569. //Update the sam account name
  6570. // truncate to allow prefix/suffix to fit in valid length
  6571. int resLen = wcslen(pOptions->suffix) + wcslen(pAcct->GetTargetSam());
  6572. if ( !_wcsicmp(pAcct->GetType(), L"computer") )
  6573. {
  6574. // Computer name can be only 15 characters long + $
  6575. if ( resLen > MAX_COMPUTERNAME_LENGTH + 1 )
  6576. {
  6577. c_array<WCHAR> achTruncatedSam(LEN_Path);
  6578. wcscpy(achTruncatedSam, pAcct->GetTargetSam());
  6579. if ( wcslen( pOptions->globalSuffix ) )
  6580. {
  6581. // We must remove the global suffix if we had one.
  6582. achTruncatedSam[wcslen(achTruncatedSam) - wcslen(pOptions->globalSuffix)] = L'\0';
  6583. }
  6584. int truncate = MAX_COMPUTERNAME_LENGTH + 1 - wcslen(pOptions->suffix) - wcslen(pOptions->globalSuffix);
  6585. if ( truncate < 1 ) truncate = 1;
  6586. wcsncpy(achTempSam, achTruncatedSam, truncate - 1);
  6587. achTempSam[truncate - 1] = L'\0';
  6588. wcscat(achTempSam, pOptions->globalSuffix);
  6589. wcscat(achTempSam, L"$");
  6590. }
  6591. else
  6592. wcscpy(achTempSam, pAcct->GetTargetSam());
  6593. // Add the suffix taking into account the $ sign
  6594. if ( achTempSam[wcslen(achTempSam) - 1] == L'$' )
  6595. achTempSam[wcslen(achTempSam) - 1] = L'\0';
  6596. wsprintf(achTgt, L"%s%s$", (WCHAR*)achTempSam, pOptions->suffix);
  6597. }
  6598. else if ( !_wcsicmp(pAcct->GetType(), L"group") )
  6599. {
  6600. if ( resLen > 64 )
  6601. {
  6602. int truncate = 64 - wcslen(pOptions->suffix);
  6603. if ( truncate < 0 ) truncate = 0;
  6604. wcsncpy(achTempSam, pAcct->GetTargetSam(), truncate);
  6605. achTempSam[truncate] = L'\0';
  6606. }
  6607. else
  6608. wcscpy(achTempSam, pAcct->GetTargetSam());
  6609. // Add the suffix.
  6610. wsprintf(achTgt, L"%s%s", (WCHAR*)achTempSam, pOptions->suffix);
  6611. }
  6612. else
  6613. {
  6614. if ( resLen > 20 )
  6615. {
  6616. c_array<WCHAR> achTruncatedSam(LEN_Path);
  6617. wcscpy(achTruncatedSam, pAcct->GetTargetSam());
  6618. if ( wcslen( pOptions->globalSuffix ) )
  6619. {
  6620. // We must remove the global suffix if we had one.
  6621. achTruncatedSam[wcslen(achTruncatedSam) - wcslen(pOptions->globalSuffix)] = L'\0';
  6622. }
  6623. int truncate = 20 - wcslen(pOptions->suffix) - wcslen(pOptions->globalSuffix);
  6624. if ( truncate < 0 ) truncate = 0;
  6625. wcsncpy(achTempSam, achTruncatedSam, truncate);
  6626. achTempSam[truncate] = L'\0';
  6627. wcscat(achTempSam, pOptions->globalSuffix);
  6628. }
  6629. else
  6630. wcscpy(achTempSam, pAcct->GetTargetSam());
  6631. // Add the suffix.
  6632. wsprintf(achTgt, L"%s%s", (WCHAR*)achTempSam, pOptions->suffix);
  6633. }
  6634. pAcct->SetTargetSam(achTgt);
  6635. if ( DoesTargetObjectAlreadyExist(pAcct, pOptions) )
  6636. {
  6637. // Double collision lets log a message and forget about this account
  6638. pAcct->MarkAlreadyThere();
  6639. err.MsgWrite(ErrE, DCT_MSG_PREF_ACCOUNT_EXISTS_S, pAcct->GetTargetSam());
  6640. Mark(L"errors",pAcct->GetType());
  6641. continue;
  6642. }
  6643. }
  6644. else
  6645. {
  6646. // if the skip existing option is specified, and the object exists in the target domain,
  6647. // we just skip it
  6648. err.MsgWrite(0, DCT_MSG_ACCOUNT_EXISTS_S, pAcct->GetTargetSam());
  6649. continue;
  6650. }
  6651. }
  6652. // If a prefix/suffix is added to the target sam name then we need to rename the account.
  6653. // on the source domain and then move it to the target domain.
  6654. if ( bObjectExists || (_wcsicmp(pAcct->GetSourceSam(), pAcct->GetTargetSam()) && !pOptions->bUndo ))
  6655. {
  6656. // we need to rename the account to the target SAM name before we try to move it
  6657. // Get an ADs pointer to the account
  6658. IADs * pADs = NULL;
  6659. c_array<WCHAR> achPaths(LEN_Path);
  6660. DWORD nPathLen = LEN_Path;
  6661. StuffComputerNameinLdapPath(achPaths, nPathLen, const_cast<WCHAR*>(pAcct->GetSourcePath()), pOptions, FALSE);
  6662. hr = ADsGetObject(achPaths,IID_IADs,(void**)&pADs);
  6663. if ( SUCCEEDED(hr) )
  6664. {
  6665. hr = pADs->Put(_bstr_t(L"sAMAccountName"),_variant_t(pAcct->GetTargetSam()));
  6666. if ( SUCCEEDED(hr) && !pOptions->nochange )
  6667. {
  6668. hr = pADs->SetInfo();
  6669. if ( SUCCEEDED(hr) )
  6670. err.MsgWrite(0,DCT_MSG_ACCOUNT_RENAMED_SS,pAcct->GetSourceSam(),pAcct->GetTargetSam());
  6671. }
  6672. if ( FAILED(hr) )
  6673. {
  6674. err.SysMsgWrite(ErrE,hr,DCT_MSG_RENAME_FAILED_SSD,pAcct->GetSourceSam(),pAcct->GetTargetSam(), hr);
  6675. Mark(L"errors",pAcct->GetType());
  6676. }
  6677. pADs->Release();
  6678. }
  6679. }
  6680. WCHAR sName[MAX_PATH];
  6681. DWORD cbDomain = MAX_PATH, cbSid = MAX_PATH;
  6682. PSID pSrcSid = new BYTE[MAX_PATH];
  6683. WCHAR sDomain[MAX_PATH];
  6684. SID_NAME_USE use;
  6685. if (!pSrcSid)
  6686. return ERROR_NOT_ENOUGH_MEMORY;
  6687. // Get the source account's rid
  6688. wsprintf(sName, L"%s\\%s", pOptions->srcDomain, pAcct->GetSourceSam());
  6689. if (LookupAccountName(pOptions->srcComp, sName, pSrcSid, &cbSid, sDomain, &cbDomain, &use))
  6690. {
  6691. pAcct->SetSourceSid(pSrcSid);
  6692. }
  6693. // Now we move it
  6694. hr = MoveObject( pAcct, pOptions, pMover );
  6695. // don't bother with this in nochange mode
  6696. if ( pOptions->nochange )
  6697. {
  6698. // we haven't modified the accounts in any way, so nothing else needs to be done for nochange mode
  6699. continue;
  6700. }
  6701. // Now, we have attempted to move the object - we need to put back the memberships
  6702. // UNDO --
  6703. if ( _wcsicmp(pAcct->GetSourceSam(), pAcct->GetTargetSam()) && pAcct->WasReplaced() && pOptions->bUndo )
  6704. {
  6705. // Since we undid a prior migration that renamed the account we need
  6706. // to rename the account back to its original name.
  6707. // Get an ADs pointer to the account
  6708. IADs * pADs = NULL;
  6709. c_array<WCHAR> achPaths(LEN_Path);
  6710. DWORD nPathLen = LEN_Path;
  6711. StuffComputerNameinLdapPath(achPaths, nPathLen, const_cast<WCHAR*>(pAcct->GetTargetPath()), pOptions, TRUE);
  6712. hr = ADsGetObject(achPaths,IID_IADs,(void**)&pADs);
  6713. if ( SUCCEEDED(hr) )
  6714. {
  6715. hr = pADs->Put(_bstr_t(L"sAMAccountName"),_variant_t(pAcct->GetTargetSam()));
  6716. if ( SUCCEEDED(hr) && !pOptions->nochange )
  6717. {
  6718. hr = pADs->SetInfo();
  6719. if ( SUCCEEDED(hr) )
  6720. err.MsgWrite(0,DCT_MSG_ACCOUNT_RENAMED_SS,pAcct->GetSourceSam(),pAcct->GetTargetSam());
  6721. }
  6722. if ( FAILED(hr) )
  6723. {
  6724. err.SysMsgWrite(ErrE,hr,DCT_MSG_RENAME_FAILED_SSD,pAcct->GetSourceSam(),pAcct->GetTargetSam(), hr);
  6725. Mark(L"errors",pAcct->GetType());
  6726. }
  6727. pADs->Release();
  6728. }
  6729. }
  6730. // -- UNDO
  6731. // FAILED Move ----
  6732. if ( (bObjectExists || _wcsicmp(pAcct->GetSourceSam(), pAcct->GetTargetSam())) && ! pAcct->WasReplaced() )
  6733. {
  6734. // if we changed the SAM account name, and the move still failed, we need to change it back now
  6735. IADs * pADs = NULL;
  6736. c_array<WCHAR> achPaths(LEN_Path);
  6737. DWORD nPathLen = LEN_Path;
  6738. StuffComputerNameinLdapPath(achPaths, nPathLen, const_cast<WCHAR*>(pAcct->GetSourcePath()), pOptions, FALSE);
  6739. hr = ADsGetObject(achPaths,IID_IADs,(void**)&pADs);
  6740. if ( SUCCEEDED(hr) )
  6741. {
  6742. hr = pADs->Put(_bstr_t(L"sAMAccountName"),_variant_t(pAcct->GetSourceSam()));
  6743. if ( SUCCEEDED(hr) )
  6744. {
  6745. hr = pADs->SetInfo();
  6746. if ( SUCCEEDED(hr) )
  6747. err.MsgWrite(0,DCT_MSG_ACCOUNT_RENAMED_SS,pAcct->GetTargetSam(),pAcct->GetSourceSam());
  6748. }
  6749. if ( FAILED(hr) )
  6750. {
  6751. err.SysMsgWrite(ErrE,hr,DCT_MSG_RENAME_FAILED_SSD,pAcct->GetTargetSam(),pAcct->GetSourceSam(), hr);
  6752. Mark(L"errors",pAcct->GetType());
  6753. }
  6754. pADs->Release();
  6755. }
  6756. }// --- Failed Move
  6757. } // end of Move-Loop
  6758. e.Close();
  6759. }
  6760. catch ( ... )
  6761. {
  6762. err.MsgWrite(ErrE,DCT_MSG_MOVE_EXCEPTION);
  6763. Mark(L"errors", L"generic");
  6764. }
  6765. try { // if we've moved any of the members, update the member records to use the target names
  6766. Progress(GET_STRING(DCT_MSG_UPDATE_MEMBER_LIST_S));
  6767. UpdateMemberList(&pMember,acctlist);
  6768. UpdateMemberList(&pMemberOf,acctlist);
  6769. }
  6770. catch (... )
  6771. {
  6772. err.MsgWrite(ErrE,DCT_MSG_RESET_MEMBER_EXCEPTION);
  6773. Mark(L"errors", L"generic");
  6774. }
  6775. for ( pAcct = (TAcctReplNode *)e.OpenFirst(acctlist);
  6776. pAcct;
  6777. pAcct = (TAcctReplNode *)e.Next() )
  6778. {
  6779. if ( m_pExt && !pOptions->nochange )
  6780. {
  6781. if ( pAcct->WasReplaced() && (pAcct->operations & OPS_Call_Extensions) )
  6782. {
  6783. m_pExt->Process(pAcct,sTargetDomain,pOptions,FALSE);
  6784. }
  6785. }
  6786. //
  6787. // If updating of rights is specified then add rights for target object. If
  6788. // the source domain is W2K then explicitly remove rights for source object
  6789. // as W2K does not automatically remove rights when an object is removed from
  6790. // the domain as of SP 2. This behavior most likely will not change for W2K.
  6791. //
  6792. if (m_UpdateUserRights)
  6793. {
  6794. HRESULT hrRights = AddAccountRights(TRUE, pAcct);
  6795. if (SUCCEEDED(hrRights) && (pOptions->srcDomainVer == 5) && (pOptions->srcDomainVerMinor == 0))
  6796. {
  6797. RemoveAccountRights(FALSE, pAcct);
  6798. }
  6799. }
  6800. //translate the roaming profile if requested
  6801. if ( pOptions->flags & F_TranslateProfiles && ((_wcsicmp(pAcct->GetType(), s_ClassUser) == 0) || (_wcsicmp(pAcct->GetType(), s_ClassInetOrgPerson) == 0)))
  6802. {
  6803. wsprintf(achMesg, GET_STRING(IDS_TRANSLATE_ROAMING_PROFILE_S), pAcct->GetName());
  6804. if ( progress )
  6805. progress(achMesg);
  6806. WCHAR tgtProfilePath[MAX_PATH];
  6807. GetBkupRstrPriv((WCHAR*)NULL);
  6808. GetPrivilege((WCHAR*)NULL,SE_SECURITY_NAME);
  6809. if ( wcslen(pAcct->GetSourceProfile()) > 0 )
  6810. {
  6811. DWORD ret = TranslateRemoteProfile(pAcct->GetSourceProfile(),
  6812. tgtProfilePath,
  6813. pAcct->GetSourceSam(),
  6814. pAcct->GetTargetSam(),
  6815. pOptions->srcDomain,
  6816. pOptions->tgtDomain,
  6817. pOptions->pDb,
  6818. pOptions->lActionID,
  6819. pAcct->GetSourceSid(),
  6820. pOptions->nochange);
  6821. }
  6822. }
  6823. }
  6824. e.Close();
  6825. for ( pAcct = (TAcctReplNode *)e.OpenFirst(acctlist);
  6826. pAcct;
  6827. pAcct = (TAcctReplNode *)e.Next() )
  6828. {
  6829. try
  6830. {
  6831. if (bSrcNative && bTgtNative)
  6832. {
  6833. if ( _wcsicmp(pAcct->GetType(), L"group") == 0 || _wcsicmp(pAcct->GetType(), L"lgroup") == 0 )
  6834. {
  6835. wsprintf(achMesg, GET_STRING(IDS_UPDATING_GROUP_MEMBERSHIPS_S), pAcct->GetName());
  6836. if ( progress )
  6837. progress(achMesg);
  6838. if ( pAcct->GetGroupType() & 4 )
  6839. {
  6840. wsprintf(achMesg, GET_STRING(DCT_MSG_RESET_GROUP_MEMBERS_S), pAcct->GetName());
  6841. Progress(achMesg);
  6842. ResetGroupsMembers(pOptions, pAcct, &pMember, pOptions->pDb);
  6843. }
  6844. else
  6845. {
  6846. // we need to update the members of these Universal/Global groups to
  6847. // point members to the target domain if those members have been migrated
  6848. // in previous runs.
  6849. ResetMembersForUnivGlobGroups(pOptions, pAcct);
  6850. }
  6851. }
  6852. }
  6853. else
  6854. {
  6855. if ( _wcsicmp(pAcct->GetType(), L"group") == 0 || _wcsicmp(pAcct->GetType(), L"lgroup") == 0 )
  6856. {
  6857. wsprintf(achMesg, GET_STRING(DCT_MSG_RESET_GROUP_MEMBERS_S), pAcct->GetName());
  6858. if ( progress )
  6859. progress(achMesg);
  6860. ResetGroupsMembers(pOptions, pAcct, &pMember, pOptions->pDb);
  6861. }
  6862. }
  6863. }
  6864. catch (... )
  6865. {
  6866. err.MsgWrite(ErrE,DCT_MSG_GROUP_MEMBERSHIPS_EXCEPTION);
  6867. Mark(L"errors", pAcct->GetType());
  6868. }
  6869. }
  6870. bool bChangedAny = true; // Have to go through it atleast once.
  6871. while ( bChangedAny )
  6872. {
  6873. bChangedAny = false;
  6874. for ( pAcct = (TAcctReplNode *)e.OpenFirst(acctlist);
  6875. pAcct;
  6876. pAcct = (TAcctReplNode *)e.Next() )
  6877. {
  6878. if ( pOptions->nochange )
  6879. continue;
  6880. if ( bSrcNative && bTgtNative )
  6881. {
  6882. // We have changed the migrated global groups to universal groups
  6883. // now we need to change them back to their original types, if possible
  6884. if ( _wcsicmp(pAcct->GetType(), L"group") == 0 || _wcsicmp(pAcct->GetType(), L"lgroup") == 0 )
  6885. {
  6886. if ( pAcct->GetGroupType() & 2 )
  6887. {
  6888. if ( pAcct->bChangedType )
  6889. continue;
  6890. // attempt to change it back to its original type
  6891. if ( pAcct->WasReplaced() )
  6892. {
  6893. // the account was moved, use the target name
  6894. hr = pClass->raw_ChangeGroupType(const_cast<WCHAR*>(pAcct->GetTargetPath()), pAcct->GetGroupType());
  6895. }
  6896. else
  6897. {
  6898. // we failed to move the account, use the source name
  6899. hr = pClass->raw_ChangeGroupType(const_cast<WCHAR*>(pAcct->GetSourcePath()), pAcct->GetGroupType());
  6900. }
  6901. pAcct->SetHr(hr);
  6902. if ( SUCCEEDED(hr) )
  6903. {
  6904. pAcct->bChangedType = true;
  6905. bChangedAny = true;
  6906. }
  6907. }
  6908. }
  6909. }
  6910. else
  6911. {
  6912. // for mixed->native mode migration we can change the group type and add all the members back
  6913. if ( _wcsicmp(pAcct->GetType(), L"group") == 0 || _wcsicmp(pAcct->GetType(), L"lgroup") == 0 )
  6914. {
  6915. if ( !(pAcct->GetGroupType() & 4) && !pAcct->bChangedType )
  6916. {
  6917. if ( pAcct->WasReplaced() )
  6918. {
  6919. hr = pClass->raw_ChangeGroupType(const_cast<WCHAR*>(pAcct->GetTargetPath()), pAcct->GetGroupType());
  6920. }
  6921. else
  6922. {
  6923. hr = pClass->raw_ChangeGroupType(const_cast<WCHAR*>(pAcct->GetSourcePath()), pAcct->GetGroupType());
  6924. }
  6925. pAcct->SetHr(hr);
  6926. if ( SUCCEEDED(hr) )
  6927. {
  6928. pAcct->bChangedType = true;
  6929. bChangedAny = true;
  6930. }
  6931. }
  6932. } // if group
  6933. } // Native/Mixed
  6934. } //for
  6935. }
  6936. // Log a message for all the groups that we were not able to change back to original type
  6937. for ( pAcct = (TAcctReplNode *)e.OpenFirst(acctlist);
  6938. pAcct;
  6939. pAcct = (TAcctReplNode *)e.Next() )
  6940. {
  6941. if ( pOptions->nochange )
  6942. continue;
  6943. if ( bSrcNative && bTgtNative )
  6944. {
  6945. // We have changed the migrated global groups to universal groups
  6946. // now we need to change them back to their original types, if possible
  6947. if ( _wcsicmp(pAcct->GetType(), L"group") == 0 || _wcsicmp(pAcct->GetType(), L"lgroup") == 0 )
  6948. {
  6949. if ( pAcct->GetGroupType() & 2 )
  6950. {
  6951. if (FAILED(pAcct->GetHr()))
  6952. {
  6953. err.SysMsgWrite(ErrE,hr,DCT_MSG_GROUP_CHANGETYPE_FAILED_SD, pAcct->GetTargetPath(), hr);
  6954. Mark(L"errors", pAcct->GetType());
  6955. }
  6956. }
  6957. }
  6958. }
  6959. else
  6960. {
  6961. // for mixed->native mode migration we can change the group type and add all the members back
  6962. if ( _wcsicmp(pAcct->GetType(), L"group") == 0 || _wcsicmp(pAcct->GetType(), L"lgroup") == 0 )
  6963. {
  6964. if ( !(pAcct->GetGroupType() & 4) )
  6965. {
  6966. if (FAILED(pAcct->GetHr()))
  6967. {
  6968. err.SysMsgWrite(ErrE,hr,DCT_MSG_GROUP_CHANGETYPE_FAILED_SD, pAcct->GetTargetPath(), hr);
  6969. Mark(L"errors", pAcct->GetType());
  6970. }
  6971. }
  6972. } // if group
  6973. } // Native/Mixed
  6974. } //for
  6975. Progress(GET_STRING(DCT_MSG_RESET_MEMBERSHIP_S));
  6976. ResetObjectsMembership( pOptions,&pMemberOf, pOptions->pDb );
  6977. ResetTypeOfPreviouslyMigratedGroups(pOptions);
  6978. }
  6979. else
  6980. {
  6981. // Connection failed.
  6982. err.SysMsgWrite(ErrE,hr,DCT_MSG_MOVEOBJECT_CONNECT_FAILED_D,hr);
  6983. Mark(L"errors", ((TAcctReplNode*)acctlist->Head())->GetType());
  6984. }
  6985. if ( progress )
  6986. progress(L"");
  6987. pMover->Close();
  6988. return 0;
  6989. }
  6990. //---------------------------------------------------------------------------------------------------------
  6991. // MoveObject - This method does the actual move on the object calling the Mover object.
  6992. //---------------------------------------------------------------------------------------------------------
  6993. HRESULT CAcctRepl::MoveObject(
  6994. TAcctReplNode * pAcct,
  6995. Options * pOptions,
  6996. IMoverPtr pMover
  6997. )
  6998. {
  6999. HRESULT hr = S_OK;
  7000. WCHAR sourcePath[LEN_Path];
  7001. WCHAR targetPath[LEN_Path];
  7002. DWORD nPathLen = LEN_Path;
  7003. WCHAR * pRelativeTgtOUPath = NULL;
  7004. safecopy(sourcePath,pAcct->GetSourcePath());
  7005. WCHAR mesg[LEN_Path];
  7006. wsprintf(mesg, (WCHAR*)GET_STRING(DCT_MSG_MOVING_S), pAcct->GetName());
  7007. Progress(mesg);
  7008. if ( ! pOptions->bUndo )
  7009. {
  7010. MakeFullyQualifiedAdsPath(targetPath, nPathLen, pOptions->tgtOUPath, pOptions->tgtDomain, pOptions->tgtNamingContext);
  7011. }
  7012. else
  7013. {
  7014. swprintf(targetPath,L"LDAP://%ls/%ls",pOptions->tgtDomain,pOptions->tgtOUPath);
  7015. }
  7016. //make sourcePath and targetPath all lowercase to avoid a W2K bug in ntdsa.dll
  7017. _wcslwr(targetPath);
  7018. _wcslwr(sourcePath);
  7019. //due to lowercase force above, we have to replace "ldap://" with "LDAP://" in
  7020. //order for subsequent ADsGetObjects calls to succeed
  7021. if ( !_wcsnicmp(L"LDAP://", targetPath, 7) )
  7022. {
  7023. WCHAR aNewPath[LEN_Path] = L"LDAP";
  7024. UStrCpy(aNewPath+UStrLen(aNewPath), targetPath+UStrLen(aNewPath));
  7025. wcscpy(targetPath, aNewPath);
  7026. }
  7027. if ( !_wcsnicmp(L"LDAP://", sourcePath, 7) )
  7028. {
  7029. WCHAR aNewPath[LEN_Path] = L"LDAP";
  7030. UStrCpy(aNewPath+UStrLen(aNewPath), sourcePath+UStrLen(aNewPath));
  7031. wcscpy(sourcePath, aNewPath);
  7032. }
  7033. WCHAR sTargetRDN[LEN_Path];
  7034. wcscpy(sTargetRDN, pAcct->GetTargetName());
  7035. if ( ! pOptions->nochange )
  7036. {
  7037. hr = pMover->raw_MoveObject(sourcePath,sTargetRDN,targetPath);
  7038. //if the Move operation failed due to a W2K bug for CNs which
  7039. //include a '/', un-escape the '/' and try again
  7040. if ((hr == E_INVALIDARG) && (wcschr(sTargetRDN, L'/')))
  7041. {
  7042. _bstr_t strName = GetUnEscapedNameWithFwdSlash(_bstr_t(sTargetRDN)); //remove any escape characters added
  7043. hr = pMover->raw_MoveObject(sourcePath,(WCHAR*)strName,targetPath);
  7044. }
  7045. }
  7046. else
  7047. {
  7048. hr = pMover->raw_CheckMove(sourcePath,sTargetRDN,targetPath);
  7049. //if the Check Move operation failed due to a W2K bug for CNs which
  7050. //include a '/', un-escape the '/' and try again
  7051. if ((hr == E_INVALIDARG) && (wcschr(sTargetRDN, L'/')))
  7052. {
  7053. _bstr_t strName = GetUnEscapedNameWithFwdSlash(_bstr_t(sTargetRDN)); //remove any escape characters added
  7054. hr = pMover->raw_CheckMove(sourcePath,(WCHAR*)strName,targetPath);
  7055. }
  7056. if ( HRESULT_CODE(hr) == ERROR_DS_CANT_MOVE_ACCOUNT_GROUP
  7057. || HRESULT_CODE(hr) == ERROR_DS_CANT_MOVE_RESOURCE_GROUP
  7058. || HRESULT_CODE(hr) == ERROR_DS_CANT_WITH_ACCT_GROUP_MEMBERSHPS
  7059. || HRESULT_CODE(hr) == ERROR_USER_EXISTS )
  7060. {
  7061. hr = 0;
  7062. }
  7063. }
  7064. if ( SUCCEEDED(hr) )
  7065. {
  7066. WCHAR path[LEN_Path];
  7067. DWORD nPathLen = LEN_Path;
  7068. pAcct->MarkReplaced();
  7069. Mark(L"created", pAcct->GetType());
  7070. // set the target path
  7071. UStrCpy(path,pAcct->GetTargetName());
  7072. if ( *pOptions->tgtOUPath )
  7073. {
  7074. wcscat(path, L",");
  7075. wcscat(path, pOptions->tgtOUPath);
  7076. }
  7077. pRelativeTgtOUPath = wcschr(targetPath + wcslen(L"LDAP://") + 2, L'/');
  7078. if ( pRelativeTgtOUPath )
  7079. {
  7080. *pRelativeTgtOUPath = 0;
  7081. swprintf(path,L"%ls/%ls,%ls",targetPath,pAcct->GetTargetName(),pRelativeTgtOUPath+1);
  7082. }
  7083. else
  7084. {
  7085. MakeFullyQualifiedAdsPath(path, nPathLen, pOptions->tgtOUPath, pOptions->tgtComp, pOptions->tgtNamingContext);
  7086. }
  7087. pAcct->SetTargetPath(path);
  7088. err.MsgWrite(0,DCT_MSG_OBJECT_MOVED_SS,pAcct->GetSourcePath(),pAcct->GetTargetPath());
  7089. }
  7090. else
  7091. {
  7092. pAcct->MarkError();
  7093. Mark(L"errors", pAcct->GetType());
  7094. if ( hr == 8524 )
  7095. {
  7096. err.MsgWrite(ErrE,DCT_MSG_MOVEOBJECT_FAILED_S8524,pAcct->GetName(),hr);
  7097. }
  7098. else if
  7099. (
  7100. (hr == HRESULT_FROM_WIN32(ERROR_DS_INAPPROPRIATE_AUTH)) ||
  7101. (hr == SEC_E_UNKNOWN_CREDENTIALS) ||
  7102. (hr == SEC_E_NO_CREDENTIALS)
  7103. )
  7104. {
  7105. err.SysMsgWrite(ErrE,hr,DCT_MSG_MOVEOBJECT_FAILED_DELEGATION_SD,pAcct->GetName(),hr);
  7106. }
  7107. else
  7108. {
  7109. err.SysMsgWrite(ErrE,hr,DCT_MSG_MOVEOBJECT_FAILED_SD,pAcct->GetName(),hr);
  7110. }
  7111. }
  7112. return hr;
  7113. }
  7114. //---------------------------------------------------------------------------------------------------------
  7115. // RecordAndRemoveMemberOf : This method removes all values in the memberOf property and then records these
  7116. // memberships. These memberships are later updated.
  7117. //---------------------------------------------------------------------------------------------------------
  7118. HRESULT CAcctRepl::RecordAndRemoveMemberOf (
  7119. Options * pOptions, //in- Options specified by the user
  7120. TAcctReplNode * pAcct, //in- Account being migrated.
  7121. TNodeListSortable * pMember //out-List containing the MemberOf values.
  7122. )
  7123. {
  7124. // First Enumerate all the objects in the member of property
  7125. INetObjEnumeratorPtr pQuery(__uuidof(NetObjEnumerator));
  7126. IEnumVARIANT * pEnum;
  7127. LPWSTR sCols[] = { L"memberOf" };
  7128. SAFEARRAY * pSa;
  7129. BSTR * pData;
  7130. SAFEARRAYBOUND bd = { 1, 0 };
  7131. IADs * pAds;
  7132. _bstr_t sObjDN;
  7133. _bstr_t sGrpName;
  7134. _variant_t var;
  7135. _variant_t * pDt;
  7136. _variant_t vx;
  7137. DWORD ulFetch;
  7138. _bstr_t sDN;
  7139. WCHAR sPath[LEN_Path];
  7140. IADsGroup * pGroup;
  7141. _variant_t * pVar;
  7142. if ( pMember->IsTree() ) pMember->ToSorted();
  7143. WCHAR sPathSource[LEN_Path];
  7144. DWORD nPathLen = LEN_Path;
  7145. StuffComputerNameinLdapPath(sPathSource, nPathLen, const_cast<WCHAR*>(pAcct->GetSourcePath()), pOptions, FALSE);
  7146. err.MsgWrite(0,DCT_STRIPPING_GROUP_MEMBERSHIPS_SS,pAcct->GetName(),sPathSource);
  7147. // Get this users distinguished name.
  7148. HRESULT hr = ADsGetObject(sPathSource, IID_IADs, (void**) &pAds);
  7149. if ( FAILED(hr) )
  7150. {
  7151. err.SysMsgWrite(ErrE, hr, DCT_MSG_SOURCE_ACCOUNT_NOT_FOUND_SSD, pAcct->GetName(), pOptions->srcDomain, hr);
  7152. Mark(L"errors", pAcct->GetType());
  7153. return hr;
  7154. }
  7155. hr = pAds->Get(L"distinguishedName", &var);
  7156. pAds->Release();
  7157. if ( FAILED(hr))
  7158. return hr;
  7159. sObjDN = V_BSTR(&var);
  7160. // Set up the column array
  7161. pSa = SafeArrayCreate(VT_BSTR, 1, &bd);
  7162. SafeArrayAccessData(pSa, (void HUGEP **) &pData);
  7163. pData[0] = SysAllocString(sCols[0]);
  7164. SafeArrayUnaccessData(pSa);
  7165. // hr = pQuery->raw_SetQuery(const_cast<WCHAR*>(pAcct->GetSourcePath()), pOptions->srcDomain, L"(objectClass=*)", ADS_SCOPE_BASE, TRUE);
  7166. hr = pQuery->raw_SetQuery(sPathSource, pOptions->srcDomain, L"(objectClass=*)", ADS_SCOPE_BASE, TRUE);
  7167. hr = pQuery->raw_SetColumns(pSa);
  7168. hr = pQuery->raw_Execute(&pEnum);
  7169. if ( FAILED(hr))
  7170. return hr;
  7171. while ( pEnum->Next(1, &var, &ulFetch) == S_OK )
  7172. {
  7173. SAFEARRAY * vals = var.parray;
  7174. // Get the VARIANT Array out
  7175. SafeArrayAccessData(vals, (void HUGEP**) &pDt);
  7176. vx = pDt[0];
  7177. SafeArrayUnaccessData(vals);
  7178. // Single value in the property. Good enough for me though
  7179. if ( vx.vt == VT_BSTR )
  7180. {
  7181. sDN = V_BSTR(&vx);
  7182. sDN = PadDN(sDN);
  7183. if ( sDN.length() > 0 )
  7184. {
  7185. WCHAR mesg[LEN_Path];
  7186. wsprintf(mesg, (WCHAR*)GET_STRING(DCT_MSG_RECORD_REMOVE_MEMBEROF_SS), pAcct->GetName(), (WCHAR*) sDN);
  7187. Progress(mesg);
  7188. SimpleADsPathFromDN(pOptions, sDN, sPath);
  7189. WCHAR sSourcePath[LEN_Path];
  7190. WCHAR sPaths[LEN_Path];
  7191. DWORD nPathLen = LEN_Path;
  7192. wcscpy(sSourcePath, (WCHAR*) sPath);
  7193. if ( !wcsncmp(L"LDAP://", sSourcePath, 7) )
  7194. StuffComputerNameinLdapPath(sPaths, nPathLen, sSourcePath, pOptions, FALSE);
  7195. // Get the IADsGroup pointer to each of the objects in member of and remove this object from the group
  7196. hr = ADsGetObject(sPaths, IID_IADsGroup, (void**) &pGroup);
  7197. if ( FAILED(hr) )
  7198. continue;
  7199. pGroup->Get(L"sAMAccountName", &var);
  7200. sGrpName = V_BSTR(&var);
  7201. hr = pGroup->Get(L"groupType",&var);
  7202. if ( SUCCEEDED(hr) )
  7203. {
  7204. if ( var.lVal & 2 )
  7205. {
  7206. // this is a global group
  7207. if ( !pOptions->nochange )
  7208. hr = pGroup->Remove(sPathSource);
  7209. else
  7210. hr = S_OK;
  7211. if ( SUCCEEDED(hr) )
  7212. {
  7213. // err.MsgWrite(0,DCT_MSG_REMOVED_MEMBER_FROM_GROUP_SS,sPath2,(WCHAR*)sGrpName);
  7214. err.MsgWrite(0,DCT_MSG_REMOVED_MEMBER_FROM_GROUP_SS,sPathSource,(WCHAR*)sPaths);
  7215. }
  7216. else
  7217. {
  7218. err.SysMsgWrite(ErrE,hr,DCT_MSG_REMOVE_MEMBER_FAILED_SSD,sPathSource,sPaths,hr);
  7219. Mark(L"errors", pAcct->GetType());
  7220. }
  7221. }
  7222. else
  7223. {
  7224. err.MsgWrite(0,DCT_MSG_NOT_REMOVING_MEMBER_FROM_GROUP_SS,sPathSource,sPaths);
  7225. pGroup->Release();
  7226. continue;
  7227. }
  7228. }
  7229. pGroup->Release();
  7230. if (FAILED(hr))
  7231. continue;
  7232. // Record this path into the list
  7233. TRecordNode * pNode = new TRecordNode();
  7234. if (!pNode)
  7235. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  7236. pNode->SetMember((WCHAR*)sPath);
  7237. pNode->SetMemberSam((WCHAR*)sGrpName);
  7238. pNode->SetDN((WCHAR*)sDN);
  7239. pNode->SetARNode(pAcct);
  7240. if (! pMember->InsertIfNew((TNode*) pNode) )
  7241. delete pNode;
  7242. }
  7243. }
  7244. else if ( vx.vt & VT_ARRAY )
  7245. {
  7246. // We must have got an Array of multivalued properties
  7247. // Access the BSTR elements of this variant array
  7248. SAFEARRAY * multiVals = vx.parray;
  7249. SafeArrayAccessData(multiVals, (void HUGEP **) &pVar);
  7250. for ( DWORD dw = 0; dw < multiVals->rgsabound->cElements; dw++ )
  7251. {
  7252. sDN = _bstr_t(pVar[dw]);
  7253. sDN = PadDN(sDN);
  7254. SimpleADsPathFromDN(pOptions, sDN, sPath);
  7255. WCHAR mesg[LEN_Path];
  7256. wsprintf(mesg, (WCHAR*)GET_STRING(DCT_MSG_RECORD_REMOVE_MEMBEROF_SS), pAcct->GetName(), (WCHAR*) sDN);
  7257. Progress(mesg);
  7258. WCHAR sSourcePath[LEN_Path];
  7259. WCHAR sPaths[LEN_Path];
  7260. DWORD nPathLen = LEN_Path;
  7261. wcscpy(sSourcePath, (WCHAR*) sPath);
  7262. if ( !wcsncmp(L"LDAP://", sSourcePath, 7) )
  7263. StuffComputerNameinLdapPath(sPaths, nPathLen, sSourcePath, pOptions, FALSE);
  7264. // Get the IADsGroup pointer to each of the objects in member of and remove this object from the group
  7265. hr = ADsGetObject(sPaths, IID_IADsGroup, (void**) &pGroup);
  7266. if ( FAILED(hr) )
  7267. continue;
  7268. pGroup->Get(L"sAMAccountName", &var);
  7269. sGrpName = V_BSTR(&var);
  7270. hr = pGroup->Get(L"groupType",&var);
  7271. if ( SUCCEEDED(hr) )
  7272. {
  7273. if ( var.lVal & 2 )
  7274. {
  7275. // This is a global group
  7276. if ( !pOptions->nochange )
  7277. hr = pGroup->Remove(sPathSource);
  7278. else
  7279. hr = S_OK;
  7280. if ( SUCCEEDED(hr) )
  7281. {
  7282. err.MsgWrite(0,DCT_MSG_REMOVED_MEMBER_FROM_GROUP_SS,sPathSource,sPaths);
  7283. }
  7284. else
  7285. {
  7286. err.SysMsgWrite(ErrE,hr,DCT_MSG_REMOVE_MEMBER_FAILED_SSD,sPathSource,sPaths);
  7287. Mark(L"errors", pAcct->GetType());
  7288. }
  7289. }
  7290. else
  7291. {
  7292. err.MsgWrite(0,DCT_MSG_NOT_REMOVING_MEMBER_FROM_GROUP_SS,sPathSource,sPaths);
  7293. pGroup->Release();
  7294. continue;
  7295. }
  7296. }
  7297. pGroup->Release();
  7298. if (FAILED(hr))
  7299. continue;
  7300. // Record this path into the list
  7301. TRecordNode * pNode = new TRecordNode();
  7302. if (!pNode)
  7303. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  7304. pNode->SetMember(sPath);
  7305. pNode->SetMemberSam((WCHAR*)sGrpName);
  7306. pNode->SetDN((WCHAR*)sDN);
  7307. pNode->SetARNode(pAcct);
  7308. if (! pMember->InsertIfNew((TNode*) pNode) )
  7309. delete pNode;
  7310. }
  7311. SafeArrayUnaccessData(multiVals);
  7312. }
  7313. }
  7314. pEnum->Release();
  7315. var.Clear();
  7316. return S_OK;
  7317. }
  7318. //---------------------------------------------------------------------------------------------------------
  7319. // ResetObjectsMembership : This method restores the memberOf property of the object being migrated. It uses
  7320. // the information that was stored by RecordAndRemoveMemberOf function.
  7321. //---------------------------------------------------------------------------------------------------------
  7322. HRESULT CAcctRepl::ResetObjectsMembership(
  7323. Options * pOptions, //in- Options set by the user.
  7324. TNodeListSortable * pMember, //in- The member list that is used to restore the values
  7325. IIManageDBPtr pDb //in- Database object to lookup migrated accounts.
  7326. )
  7327. {
  7328. IVarSetPtr pVs(__uuidof(VarSet));
  7329. _bstr_t sMember;
  7330. IADs * pAds;
  7331. IUnknown * pUnk;
  7332. _bstr_t sPath;
  7333. _variant_t var;
  7334. _bstr_t sMyDN;
  7335. IADsGroup * pGroup = NULL;
  7336. TAcctReplNode * pAcct = NULL;
  7337. HRESULT hr;
  7338. WCHAR sPaths[LEN_Path];
  7339. DWORD nPathLen = LEN_Path;
  7340. // Sort the member list by the account nodes
  7341. pMember->Sort(&TNodeCompareAcctNode);
  7342. // For all the items in the member list lets add the member to the group.
  7343. // First check in the migrated objects table to see if it has been migrated.
  7344. for ( TRecordNode * pNode = (TRecordNode *)pMember->Head(); pNode; pNode = (TRecordNode *)pNode->Next())
  7345. {
  7346. pVs->QueryInterface(IID_IUnknown, (void**) &pUnk);
  7347. // get the needed information from the account node
  7348. if ( pAcct != pNode->GetARNode() )
  7349. {
  7350. if ( pNode->GetARNode()->WasReplaced() )
  7351. {
  7352. // the account was moved successfully - add the target account to all of its old groups
  7353. StuffComputerNameinLdapPath(sPaths, nPathLen, const_cast<WCHAR*>(pNode->GetARNode()->GetTargetPath()), pOptions);
  7354. hr = ADsGetObject(sPaths, IID_IADs, (void**) &pAds);
  7355. }
  7356. else
  7357. {
  7358. // the move failed, add the source account back to its groups
  7359. StuffComputerNameinLdapPath(sPaths, nPathLen, const_cast<WCHAR*>(pNode->GetARNode()->GetSourcePath()), pOptions, FALSE);
  7360. hr = ADsGetObject(sPaths, IID_IADs, (void**) &pAds);
  7361. }
  7362. if ( SUCCEEDED(hr) )
  7363. {
  7364. pAds->Get(L"distinguishedName", &var);
  7365. pAds->Release();
  7366. sMyDN = V_BSTR(&var);
  7367. }
  7368. else
  7369. {
  7370. continue;
  7371. }
  7372. pAcct = pNode->GetARNode();
  7373. if ( pAcct->WasReplaced() )
  7374. {
  7375. err.MsgWrite(0,DCT_READDING_GROUP_MEMBERS_SS,pAcct->GetTargetName(),sPaths);
  7376. }
  7377. else
  7378. {
  7379. err.MsgWrite(0,DCT_READDING_GROUP_MEMBERS_SS,pAcct->GetName(),sPaths);
  7380. }
  7381. }
  7382. sMember = pNode->GetMemberSam();
  7383. if ( pAcct->WasReplaced() )
  7384. {
  7385. pVs->Clear();
  7386. hr = pDb->raw_GetAMigratedObject((WCHAR*)sMember,pOptions->srcDomain, pOptions->tgtDomain, &pUnk);
  7387. pUnk->Release();
  7388. if ( hr == S_OK )
  7389. {
  7390. // Since we have already migrated this object lets use the target objects information.
  7391. VerifyAndUpdateMigratedTarget(pOptions, pVs);
  7392. sPath = pVs->get(L"MigratedObjects.TargetAdsPath");
  7393. }
  7394. else
  7395. // Other wise use the source objects path to add.
  7396. sPath = pNode->GetMember();
  7397. }
  7398. else
  7399. {
  7400. sPath = pNode->GetMember();
  7401. }
  7402. // We have a path to the object lets get the group interface and add this object as a member
  7403. WCHAR sPath2[LEN_Path];
  7404. DWORD nPathLen = LEN_Path;
  7405. if ( SUCCEEDED(hr) )
  7406. {
  7407. StuffComputerNameinLdapPath(sPath2, nPathLen, (WCHAR*) sPath, pOptions, TRUE);
  7408. hr = ADsGetObject(sPath2, IID_IADsGroup, (void**) &pGroup);
  7409. }
  7410. if ( SUCCEEDED(hr) )
  7411. {
  7412. if ( pAcct->WasReplaced() )
  7413. {
  7414. if ( ! pOptions->nochange )
  7415. hr = pGroup->Add(sPaths);
  7416. else
  7417. hr = 0;
  7418. if ( SUCCEEDED(hr) )
  7419. {
  7420. err.MsgWrite(0,DCT_MSG_READDED_MEMBER_SS,pAcct->GetTargetPath(),sPath2);
  7421. }
  7422. else
  7423. {
  7424. //hr = BetterHR(hr);
  7425. if ( HRESULT_CODE(hr) == ERROR_DS_UNWILLING_TO_PERFORM )
  7426. {
  7427. err.MsgWrite(0,DCT_MSG_READD_MEMBER_FAILED_CONSTRAINTS_SS,pAcct->GetTargetPath(),sPath2);
  7428. }
  7429. else
  7430. {
  7431. err.SysMsgWrite(ErrE,hr,DCT_MSG_READD_TARGET_MEMBER_FAILED_SSD,pAcct->GetTargetPath(),(WCHAR*)sPath2,hr);
  7432. }
  7433. Mark(L"errors", pAcct->GetType());
  7434. }
  7435. }
  7436. else
  7437. {
  7438. WCHAR mesg[LEN_Path];
  7439. wsprintf(mesg, (WCHAR*)GET_STRING(DCT_MSG_RESET_OBJECT_MEMBERSHIP_SS), (WCHAR*) sPath2, pAcct->GetTargetName());
  7440. Progress(mesg);
  7441. if ( ! pOptions->nochange )
  7442. hr = pGroup->Add(sPaths);
  7443. else
  7444. hr = 0;
  7445. if ( SUCCEEDED(hr) )
  7446. {
  7447. err.MsgWrite(0,DCT_MSG_READDED_MEMBER_SS,pAcct->GetSourcePath(),(WCHAR*)sPath2);
  7448. }
  7449. else
  7450. {
  7451. //hr = BetterHR(hr);
  7452. if ( HRESULT_CODE(hr) == ERROR_DS_UNWILLING_TO_PERFORM )
  7453. {
  7454. err.MsgWrite(0,DCT_MSG_READD_MEMBER_FAILED_CONSTRAINTS_SS,pAcct->GetTargetPath(),(WCHAR*)sPath2);
  7455. }
  7456. else
  7457. {
  7458. err.SysMsgWrite(ErrE,hr,DCT_MSG_READD_SOURCE_MEMBER_FAILED_SSD,pAcct->GetSourcePath(),(WCHAR*)sPath2,hr);
  7459. }
  7460. Mark(L"errors", pAcct->GetType());
  7461. }
  7462. }
  7463. }
  7464. else
  7465. {
  7466. // the member could not be added to the group
  7467. hr = BetterHR(hr);
  7468. err.SysMsgWrite(ErrW,hr,DCT_MSG_FAILED_TO_GET_OBJECT_SD,(WCHAR*)sPath2,hr);
  7469. Mark(L"warnings", pAcct->GetType());
  7470. }
  7471. if ( pGroup )
  7472. {
  7473. pGroup->Release();
  7474. pGroup = NULL;
  7475. }
  7476. }
  7477. return hr;
  7478. }
  7479. //---------------------------------------------------------------------------------------------------------
  7480. // ResetTypeOfPreviouslyMigratedGroups
  7481. //
  7482. // Attempts to change group scope back to global for global groups that were previously migrated but were
  7483. // unable to have their scope changed back to global due to having members outside of the domain.
  7484. //---------------------------------------------------------------------------------------------------------
  7485. void CAcctRepl::ResetTypeOfPreviouslyMigratedGroups(Options* pOptions)
  7486. {
  7487. // retrieve list of global groups that have been migrated from source domain
  7488. IVarSetPtr spVarSet(__uuidof(VarSet));
  7489. IUnknownPtr spUnknown(spVarSet);
  7490. IUnknown* punk = spUnknown;
  7491. HRESULT hr = pOptions->pDb->GetMigratedObjectByType(-1, _bstr_t(pOptions->srcDomain), _bstr_t(L"ggroup"), &punk);
  7492. if (SUCCEEDED(hr))
  7493. {
  7494. // for each global group
  7495. long lCount = spVarSet->get(_bstr_t(L"MigratedObjects"));
  7496. for (long lIndex = 0; lIndex < lCount; lIndex++)
  7497. {
  7498. WCHAR szKey[256];
  7499. // if marked as having been global group
  7500. // which means it was converted to universal group
  7501. swprintf(szKey, L"MigratedObjects.%ld.status", lIndex);
  7502. long lStatus = spVarSet->get(_bstr_t(szKey));
  7503. if (lStatus & AR_Status_GroupScopeChanged)
  7504. {
  7505. // bind to group in target domain
  7506. swprintf(szKey, L"MigratedObjects.%ld.TargetAdsPath", lIndex);
  7507. _bstr_t strADsPath = spVarSet->get(_bstr_t(szKey));
  7508. if (strADsPath.length())
  7509. {
  7510. IADsGroupPtr spGroup;
  7511. hr = ADsGetObject(strADsPath, IID_IADsGroup, (void**)&spGroup);
  7512. if (SUCCEEDED(hr))
  7513. {
  7514. // if group is currently a universal group
  7515. _bstr_t strPropertyName(L"groupType");
  7516. VARIANT var;
  7517. VariantInit(&var);
  7518. hr = spGroup->Get(strPropertyName, &var);
  7519. if (SUCCEEDED(hr))
  7520. {
  7521. long lGroupType = _variant_t(var, false);
  7522. if (lGroupType & ADS_GROUP_TYPE_UNIVERSAL_GROUP)
  7523. {
  7524. // change it's type back to global group
  7525. spGroup->Put(strPropertyName, _variant_t(long(unsigned long(ADS_GROUP_TYPE_GLOBAL_GROUP|ADS_GROUP_TYPE_SECURITY_ENABLED))));
  7526. hr = spGroup->SetInfo();
  7527. if (SUCCEEDED(hr))
  7528. {
  7529. // log successful change
  7530. err.MsgWrite(ErrI, DCT_MSG_CHANGE_GLOBAL_GROUP_SCOPE_BACK_S, (LPCTSTR)strADsPath);
  7531. }
  7532. }
  7533. if (SUCCEEDED(hr))
  7534. {
  7535. // clear status flag in database
  7536. swprintf(szKey, L"MigratedObjects.%ld.GUID", lIndex);
  7537. _bstr_t strGUID = spVarSet->get(_bstr_t(szKey));
  7538. pOptions->pDb->UpdateMigratedObjectStatus(strGUID, lStatus & ~AR_Status_GroupScopeChanged);
  7539. }
  7540. }
  7541. }
  7542. }
  7543. }
  7544. }
  7545. }
  7546. }
  7547. //---------------------------------------------------------------------------------------------------------
  7548. // RecordAndRemoveMember : Records and removes the objects in the member property of the object(group) being
  7549. // migrated. The recorded information is later used to restore membership.
  7550. //---------------------------------------------------------------------------------------------------------
  7551. HRESULT CAcctRepl::RecordAndRemoveMember (
  7552. Options * pOptions, //in- Options set by the user.
  7553. TAcctReplNode * pAcct, //in- Account being copied.
  7554. TNodeListSortable * pMember //out-Membership list to be used later to restore membership
  7555. )
  7556. {
  7557. HRESULT hr;
  7558. INetObjEnumeratorPtr pQuery(__uuidof(NetObjEnumerator));
  7559. IEnumVARIANT * pEnum;
  7560. LPWSTR sCols[] = { L"member" };
  7561. SAFEARRAY * pSa;
  7562. SAFEARRAYBOUND bd = { 1, 0 };
  7563. WCHAR sPath[LEN_Path];
  7564. DWORD nPathLen = LEN_Path;
  7565. _bstr_t sDN;
  7566. IADsGroupPtr pGroup;
  7567. _variant_t var;
  7568. DWORD ulFetch=0;
  7569. IADsPtr pAds;
  7570. _bstr_t sGrpName;
  7571. _variant_t * pDt;
  7572. _variant_t vx;
  7573. BSTR HUGEP * pData;
  7574. WCHAR sSourcePath[LEN_Path];
  7575. WCHAR sAdsPath[LEN_Path];
  7576. wcscpy(sSourcePath, pAcct->GetSourcePath());
  7577. if ( !wcsncmp(L"LDAP://", sSourcePath, 7) )
  7578. StuffComputerNameinLdapPath(sAdsPath, nPathLen, sSourcePath, pOptions, FALSE);
  7579. hr = ADsGetObject(sAdsPath, IID_IADsGroup, (void**) &pGroup);
  7580. if ( FAILED(hr) ) return hr;
  7581. pSa = SafeArrayCreate(VT_BSTR, 1, &bd);
  7582. hr = SafeArrayAccessData(pSa, (void HUGEP **)&pData);
  7583. pData[0] = SysAllocString(sCols[0]);
  7584. hr = SafeArrayUnaccessData(pSa);
  7585. hr = pQuery->raw_SetQuery(sAdsPath, pOptions->srcDomain, L"(objectClass=*)", ADS_SCOPE_BASE, TRUE);
  7586. hr = pQuery->raw_SetColumns(pSa);
  7587. hr = pQuery->raw_Execute(&pEnum);
  7588. if ( FAILED(hr) ) return hr;
  7589. err.MsgWrite(0,DCT_STRIPPING_GROUP_MEMBERS_SS,pAcct->GetName(),sAdsPath);
  7590. while ( pEnum->Next(1, &var, &ulFetch) == S_OK )
  7591. {
  7592. SAFEARRAY * vals = var.parray;
  7593. // Get the VARIANT Array out
  7594. SafeArrayAccessData(vals, (void HUGEP**) &pDt);
  7595. vx = pDt[0];
  7596. SafeArrayUnaccessData(vals);
  7597. // Single value in the property. Good enough for me though
  7598. if ( vx.vt == VT_BSTR )
  7599. {
  7600. sDN = V_BSTR(&vx);
  7601. sDN = PadDN(sDN);
  7602. if ( sDN.length() )
  7603. {
  7604. WCHAR mesg[LEN_Path];
  7605. wsprintf(mesg, (WCHAR*)GET_STRING(DCT_MSG_RECORD_REMOVE_MEMBER_SS), pAcct->GetName(), (WCHAR*) sDN);
  7606. Progress(mesg);
  7607. hr = ADsPathFromDN(pOptions, sDN, sPath);
  7608. if (SUCCEEDED(hr))
  7609. {
  7610. // Get the IADs pointer to each of the objects in member and remove the object from the group
  7611. hr = ADsGetObject((WCHAR*)sPath, IID_IADs, (void**) &pAds);
  7612. if ( SUCCEEDED(hr) )
  7613. {
  7614. pAds->Get(L"sAMAccountName", &var);
  7615. sGrpName = V_BSTR(&var);
  7616. pAds->Get(L"distinguishedName", &var);
  7617. sDN = V_BSTR(&var);
  7618. }
  7619. if ( !pOptions->nochange )
  7620. hr = pGroup->Remove((WCHAR*) sPath);
  7621. else
  7622. hr = S_OK;
  7623. if (FAILED(hr))
  7624. {
  7625. err.SysMsgWrite(ErrE,hr,DCT_MSG_REMOVE_MEMBER_FAILED_SSD,sAdsPath,(WCHAR*)sPath,hr);
  7626. Mark(L"errors", pAcct->GetType());
  7627. continue;
  7628. }
  7629. else
  7630. {
  7631. err.MsgWrite(0,DCT_MSG_REMOVED_MEMBER_FROM_GROUP_SS,(WCHAR*)sPath,sAdsPath);
  7632. }
  7633. }
  7634. else
  7635. {
  7636. err.SysMsgWrite(ErrE,hr,DCT_MSG_REMOVE_MEMBER_FAILED_SSD,sAdsPath,(WCHAR*)sDN,hr);
  7637. Mark(L"errors", pAcct->GetType());
  7638. continue;
  7639. }
  7640. // Record this path into the list
  7641. TRecordNode * pNode = new TRecordNode();
  7642. if (!pNode)
  7643. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  7644. pNode->SetMember((WCHAR*)sPath);
  7645. pNode->SetMemberSam((WCHAR*)sGrpName);
  7646. pNode->SetDN((WCHAR*)sDN);
  7647. pNode->SetARNode(pAcct);
  7648. if (! pMember->InsertIfNew((TNode*) pNode) )
  7649. delete pNode;
  7650. }
  7651. }
  7652. else if ( vx.vt & VT_ARRAY )
  7653. {
  7654. // We must have got an Array of multivalued properties
  7655. // Access the BSTR elements of this variant array
  7656. _variant_t * pVar;
  7657. SAFEARRAY * multiVals = vx.parray;
  7658. SafeArrayAccessData(multiVals, (void HUGEP **) &pVar);
  7659. for ( DWORD dw = 0; dw < multiVals->rgsabound->cElements; dw++ )
  7660. {
  7661. sDN = _bstr_t(pVar[dw]);
  7662. _bstr_t sPadDN = PadDN(sDN);
  7663. WCHAR mesg[LEN_Path];
  7664. wsprintf(mesg, (WCHAR*)GET_STRING(DCT_MSG_RECORD_REMOVE_MEMBER_SS), pAcct->GetName(), (WCHAR*) sPadDN);
  7665. Progress(mesg);
  7666. hr = ADsPathFromDN(pOptions, sPadDN, sPath);
  7667. if (SUCCEEDED(hr))
  7668. {
  7669. WCHAR tempPath[LEN_Path];
  7670. wcscpy(tempPath, sPath);
  7671. if ( !wcsncmp(L"LDAP://", tempPath, 7) )
  7672. StuffComputerNameinLdapPath(sPath, nPathLen, tempPath, pOptions, FALSE);
  7673. // Get the IADsGroup pointer to each of the objects in member of and remove this object from the group
  7674. hr = ADsGetObject((WCHAR*)sPath, IID_IADs, (void**) &pAds);
  7675. if ( SUCCEEDED(hr) )
  7676. {
  7677. hr = pAds->Get(L"sAMAccountName", &var);
  7678. if ( SUCCEEDED(hr) )
  7679. {
  7680. sGrpName = V_BSTR(&var);
  7681. }
  7682. hr = pAds->Get(L"distinguishedName", &var);
  7683. if ( SUCCEEDED(hr) )
  7684. {
  7685. sDN = V_BSTR(&var);
  7686. }
  7687. if ( !pOptions->nochange )
  7688. hr = pGroup->Remove((WCHAR*)sPath);
  7689. else
  7690. hr = S_OK;
  7691. if ( SUCCEEDED(hr) )
  7692. {
  7693. err.MsgWrite(0,DCT_MSG_REMOVED_MEMBER_FROM_GROUP_SS,(WCHAR*)sPath,sAdsPath);
  7694. }
  7695. else
  7696. {
  7697. err.SysMsgWrite(ErrE,hr,DCT_MSG_REMOVE_MEMBER_FAILED_SSD,sAdsPath,(WCHAR*)sPath,hr);
  7698. Mark(L"errors", pAcct->GetType());
  7699. }
  7700. if ( SUCCEEDED(hr) )
  7701. {
  7702. // Record this path into the list
  7703. TRecordNode * pNode = new TRecordNode();
  7704. if (!pNode)
  7705. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  7706. pNode->SetMember((WCHAR*)sPath);
  7707. pNode->SetMemberSam((WCHAR*)sGrpName);
  7708. pNode->SetDN((WCHAR*)sDN);
  7709. pNode->SetARNode(pAcct);
  7710. if ( ! pMember->InsertIfNew((TNode*) pNode) )
  7711. delete pNode;
  7712. }
  7713. }
  7714. else
  7715. {
  7716. // Since we were not able to get this user. it has probably been migrated to another domain.
  7717. // we should use the DN to find where the object is migrated to and then use the migrated object
  7718. // to establish the membership instead.
  7719. // Remove the rogue member
  7720. if ( !pOptions->nochange )
  7721. hr = pGroup->Remove((WCHAR*)sPath);
  7722. else
  7723. hr = S_OK;
  7724. if ( SUCCEEDED(hr) )
  7725. {
  7726. err.MsgWrite(0,DCT_MSG_REMOVED_MEMBER_FROM_GROUP_SS,(WCHAR*)sPath,sAdsPath);
  7727. }
  7728. else
  7729. {
  7730. err.SysMsgWrite(ErrE,hr,DCT_MSG_REMOVE_MEMBER_FAILED_SSD,sAdsPath,(WCHAR*)sPath,hr);
  7731. Mark(L"errors", pAcct->GetType());
  7732. }
  7733. // Check in the DB to see where this object may have been migrated
  7734. IUnknown * pUnk = NULL;
  7735. IVarSetPtr pVsMigObj(__uuidof(VarSet));
  7736. hr = pVsMigObj->QueryInterface(IID_IUnknown, (void**)&pUnk);
  7737. if ( SUCCEEDED(hr) )
  7738. hr = pOptions->pDb->raw_GetMigratedObjectBySourceDN(sPadDN, &pUnk);
  7739. if (pUnk) pUnk->Release();
  7740. if ( hr == S_OK )
  7741. {
  7742. // Record this path into the list
  7743. TRecordNode * pNode = new TRecordNode();
  7744. if (!pNode)
  7745. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  7746. WCHAR sKey[500];
  7747. wsprintf(sKey, L"MigratedObjects.%s", GET_STRING(DB_TargetAdsPath));
  7748. pNode->SetMember((WCHAR*)pVsMigObj->get(sKey).bstrVal);
  7749. wsprintf(sKey, L"MigratedObjects.%s", GET_STRING(DB_TargetSamName));
  7750. pNode->SetMemberSam((WCHAR*)pVsMigObj->get(sKey).bstrVal);
  7751. pNode->SetDN((WCHAR*)sDN);
  7752. pNode->SetARNode(pAcct);
  7753. if ( ! pMember->InsertIfNew((TNode*) pNode) )
  7754. delete pNode;
  7755. }
  7756. else
  7757. {
  7758. //Log a message saying we can not find this object and the membership will not be updated on the other side.
  7759. err.MsgWrite(ErrE,DCT_MSG_MEMBER_NOT_FOUND_SS, pAcct->GetName(), (WCHAR*)sDN);
  7760. }
  7761. }
  7762. }
  7763. else
  7764. {
  7765. err.SysMsgWrite(ErrE,hr,DCT_MSG_REMOVE_MEMBER_FAILED_SSD,sAdsPath,(WCHAR*)sPadDN,hr);
  7766. Mark(L"errors", pAcct->GetType());
  7767. }
  7768. }
  7769. SafeArrayUnaccessData(multiVals);
  7770. }
  7771. }
  7772. pEnum->Release();
  7773. var.Clear();
  7774. return S_OK;
  7775. }
  7776. void
  7777. CAcctRepl::UpdateMemberList(TNodeListSortable * pMemberList,TNodeListSortable * acctlist)
  7778. {
  7779. // for each moved object in the account list, look it up in the member list
  7780. TNodeTreeEnum e;
  7781. TAcctReplNode * pAcct;
  7782. TRecordNode * pRec;
  7783. // HRESULT hr = S_OK;
  7784. WCHAR dn[LEN_Path];
  7785. WCHAR const * slash;
  7786. pMemberList->Sort(TNodeCompareMemberDN);
  7787. for ( pAcct = (TAcctReplNode *)e.OpenFirst(acctlist) ; pAcct ; pAcct = (TAcctReplNode *)e.Next())
  7788. {
  7789. if ( pAcct->WasReplaced() )
  7790. {
  7791. //err.DbgMsgWrite(0,L"UpdateMemberList:: %ls was replaced",pAcct->GetSourcePath());
  7792. slash = wcschr(pAcct->GetSourcePath()+8,L'/');
  7793. if ( slash )
  7794. {
  7795. safecopy(dn,slash+1);
  7796. // err.DbgMsgWrite(0,L"Searching the member list for %ls",dn);
  7797. // if the account was replaced, find any instances of it in the member list, and update them
  7798. pRec = (TRecordNode *)pMemberList->Find(&TNodeCompareMemberItem,dn);
  7799. while ( pRec )
  7800. {
  7801. // err.DbgMsgWrite(0,L"Found record: Member=%ls, changing it to %ls",pRec->GetMember(),pAcct->GetTargetPath());
  7802. // change the member data to refer to the new location of the account
  7803. pRec->SetMember(pAcct->GetTargetPath());
  7804. pRec->SetMemberSam(pAcct->GetTargetSam());
  7805. pRec->SetMemberMoved();
  7806. pRec = (TRecordNode*)pRec->Next();
  7807. if ( pRec && UStrICmp(pRec->GetDN(),dn) )
  7808. {
  7809. // the next record is for a different node
  7810. pRec = NULL;
  7811. }
  7812. }
  7813. }
  7814. }
  7815. // else
  7816. // err.DbgMsgWrite(0,L"UpdateMemberList:: %ls was not replaced",pAcct->GetSourcePath());
  7817. }
  7818. e.Close();
  7819. // put the list back like it was before
  7820. pMemberList->Sort(TNodeCompareMember);
  7821. }
  7822. void
  7823. CAcctRepl::SimpleADsPathFromDN(
  7824. Options * pOptions,
  7825. WCHAR const * sDN,
  7826. WCHAR * sPath
  7827. )
  7828. {
  7829. WCHAR const * pDcPart = wcsstr(sDN,L",DC=");
  7830. UStrCpy(sPath,L"LDAP://");
  7831. if ( pDcPart )
  7832. {
  7833. WCHAR const * curr; // pointer to DN
  7834. WCHAR * sPathCurr; // pointer to domain name part of the path
  7835. for ( sPathCurr = sPath+UStrLen(sPath), curr = pDcPart + 4; *curr ; sPathCurr++ )
  7836. {
  7837. // replace each occurrence of ,DC= in the DN with '.' in this part of the domain
  7838. if ( !UStrICmp(curr,L",DC=",4) )
  7839. {
  7840. (*sPathCurr) = L'.';
  7841. curr+=4;
  7842. }
  7843. else
  7844. {
  7845. (*sPathCurr) = (*curr);
  7846. curr++;
  7847. }
  7848. }
  7849. // null-terminate the string
  7850. (*sPathCurr) = 0;
  7851. }
  7852. else
  7853. {
  7854. // if we can't figure it out from the path for some reason, default to the source domain
  7855. UStrCpy(sPath+UStrLen(sPath),pOptions->srcDomain);
  7856. }
  7857. UStrCpy(sPath+UStrLen(sPath),L"/");
  7858. UStrCpy(sPath + UStrLen(sPath),sDN);
  7859. }
  7860. BOOL GetSidString(PSID sid, WCHAR* sSid)
  7861. {
  7862. BOOL ret = false;
  7863. SAFEARRAY * pSa = NULL;
  7864. SAFEARRAYBOUND bd;
  7865. HRESULT hr = S_OK;
  7866. LPBYTE pByte = NULL;
  7867. _variant_t var;
  7868. if (IsValidSid(sid))
  7869. {
  7870. DWORD len = GetLengthSid(sid);
  7871. bd.cElements = len;
  7872. bd.lLbound = 0;
  7873. pSa = SafeArrayCreate(VT_UI1, 1, &bd);
  7874. if ( pSa )
  7875. hr = SafeArrayAccessData(pSa, (void**)&pByte);
  7876. if ( SUCCEEDED(hr) )
  7877. {
  7878. for ( DWORD x = 0; x < len; x++)
  7879. pByte[x] = ((LPBYTE)sid)[x];
  7880. hr = SafeArrayUnaccessData(pSa);
  7881. }
  7882. if ( SUCCEEDED(hr) )
  7883. {
  7884. var.vt = VT_UI1 | VT_ARRAY;
  7885. var.parray = pSa;
  7886. VariantSidToString(var);
  7887. wcscpy(sSid, (WCHAR*) var.bstrVal);
  7888. ret = true;
  7889. }
  7890. }
  7891. return ret;
  7892. }
  7893. //---------------------------------------------------------------------------------------------------------
  7894. // ADsPathFromDN : Constructs the AdsPath from distinguished name by looking up the Global Catalog.
  7895. //---------------------------------------------------------------------------------------------------------
  7896. HRESULT CAcctRepl::ADsPathFromDN(
  7897. Options * pOptions, //in -Options as set by the user
  7898. _bstr_t sDN, //in -Distinguished name to be converted
  7899. WCHAR * sPath, //out-The ads path of object referenced by the DN
  7900. bool bWantLDAP //in - Flag telling us if they want LDAP path or GC path.
  7901. )
  7902. {
  7903. HRESULT hr;
  7904. INetObjEnumeratorPtr pQuery(__uuidof(NetObjEnumerator));
  7905. WCHAR sCont[LEN_Path];
  7906. IEnumVARIANT * pEnum;
  7907. WCHAR sQuery[LEN_Path];
  7908. LPWSTR sCols[] = { L"ADsPath" };
  7909. _variant_t var;
  7910. DWORD pFetch = 0;
  7911. BSTR * pDt;
  7912. _variant_t * pvar;
  7913. _variant_t vx;
  7914. SAFEARRAY * pSa;
  7915. SAFEARRAYBOUND bd = { 1, 0 };
  7916. long rc;
  7917. pSa = SafeArrayCreate(VT_BSTR, 1, &bd);
  7918. SafeArrayAccessData( pSa, (void HUGEP **) &pDt);
  7919. pDt[0] = SysAllocString(sCols[0]);
  7920. SafeArrayUnaccessData(pSa);
  7921. //
  7922. // Attempt to query the global catalog for specified distinguished name. If unable to
  7923. // obtain name of global catalog server than query domain controller in source domain.
  7924. //
  7925. _bstr_t strGlobalCatalogServer;
  7926. DWORD dwError = GetGlobalCatalogServer4(pOptions->srcDomain, strGlobalCatalogServer);
  7927. if (dwError == ERROR_SUCCESS)
  7928. {
  7929. if ((PWSTR)strGlobalCatalogServer)
  7930. {
  7931. wsprintf(sCont, L"GC://%s", (PWSTR)strGlobalCatalogServer);
  7932. }
  7933. else
  7934. {
  7935. return E_OUTOFMEMORY;
  7936. }
  7937. }
  7938. else
  7939. {
  7940. wsprintf(sCont, L"LDAP://%s", pOptions->srcDomain);
  7941. }
  7942. wsprintf(sQuery, L"(distinguishedName=%s)", GetEscapedFilterValue(sDN).c_str());
  7943. hr = pQuery->raw_SetQuery(sCont, pOptions->srcDomain, sQuery, ADS_SCOPE_SUBTREE, TRUE);
  7944. if ( FAILED(hr) )
  7945. return hr;
  7946. hr = pQuery->raw_SetColumns(pSa);
  7947. if ( FAILED(hr) )
  7948. return hr;
  7949. hr = pQuery->raw_Execute(&pEnum);
  7950. if ( FAILED(hr) )
  7951. return hr;
  7952. hr = pEnum->Next(1, &var, &pFetch);
  7953. if ( SUCCEEDED(hr) && pFetch > 0 && (var.vt & VT_ARRAY) )
  7954. {
  7955. SAFEARRAY * vals = var.parray;
  7956. // Get the VARIANT Array out
  7957. rc = SafeArrayAccessData(vals, (void HUGEP**) &pvar);
  7958. vx = pvar[0];
  7959. rc = SafeArrayUnaccessData(vals);
  7960. wcscpy(sPath, (WCHAR*)V_BSTR(&vx));
  7961. if (bWantLDAP)
  7962. {
  7963. WCHAR sTemp[LEN_Path];
  7964. wsprintf(sTemp, L"LDAP%s", sPath + 2);
  7965. wcscpy(sPath, sTemp);
  7966. }
  7967. hr = S_OK;
  7968. }
  7969. else
  7970. {
  7971. // This must not be from this forest so we need to use the LDAP://<SID=##> format
  7972. wsprintf(sPath, L"LDAP://%s/%s", pOptions->srcDomain, (WCHAR*) sDN);
  7973. hr = S_OK;
  7974. }
  7975. pEnum->Release();
  7976. return hr;
  7977. }
  7978. //---------------------------------------------------------------------------------------------------------
  7979. // FillNamingContext : Gets the naming context for both domains if they are Win2k
  7980. //---------------------------------------------------------------------------------------------------------
  7981. BOOL CAcctRepl::FillNamingContext(
  7982. Options * pOptions //in,out-Options as set by the user
  7983. )
  7984. {
  7985. // Get the defaultNamingContext for the source domain
  7986. IADs * pAds = NULL;
  7987. WCHAR sAdsPath[LEN_Path];
  7988. VARIANT var;
  7989. BOOL rc = TRUE;
  7990. HRESULT hr;
  7991. VariantInit(&var);
  7992. // we should always be able to get the naming context for the target domain,
  7993. // since the target domain will always be Win2K
  7994. if ( ! *pOptions->tgtNamingContext )
  7995. {
  7996. wcscpy(sAdsPath, L"LDAP://");
  7997. wcscat(sAdsPath, pOptions->srcDomain);
  7998. wcscat(sAdsPath, L"/rootDSE");
  7999. hr = ADsGetObject(sAdsPath, IID_IADs, (void**)&pAds);
  8000. if ( FAILED(hr))
  8001. rc = FALSE;
  8002. if ( SUCCEEDED (hr) )
  8003. {
  8004. hr = pAds->Get(L"defaultNamingContext",&var);
  8005. if ( SUCCEEDED( hr) )
  8006. wcscpy(pOptions->srcNamingContext, var.bstrVal);
  8007. VariantClear(&var);
  8008. }
  8009. if ( pAds )
  8010. {
  8011. pAds->Release();
  8012. pAds = NULL;
  8013. }
  8014. wcscpy(sAdsPath, L"LDAP://");
  8015. wcscat(sAdsPath, pOptions->tgtDomain);
  8016. wcscat(sAdsPath, L"/rootDSE");
  8017. hr = ADsGetObject(sAdsPath, IID_IADs, (void**)&pAds);
  8018. if ( FAILED(hr))
  8019. rc = FALSE;
  8020. if ( SUCCEEDED (hr) )
  8021. {
  8022. hr = pAds->Get(L"defaultNamingContext",&var);
  8023. if ( SUCCEEDED( hr) )
  8024. wcscpy(pOptions->tgtNamingContext, var.bstrVal);
  8025. VariantClear(&var);
  8026. }
  8027. if ( pAds )
  8028. pAds->Release();
  8029. }
  8030. return rc;
  8031. }
  8032. //---------------------------------------------------------------------------------------------------------
  8033. // ResetGroupsMembers : This method re-adds the objects in the pMember list to the group account. This
  8034. // resets the group to its original form. ( as before the migration ). It also
  8035. // takes into account the MigratedObjects table which in turn allows to add the target
  8036. // information of newly migrated accounts to the group instead of the source account.
  8037. //---------------------------------------------------------------------------------------------------------
  8038. HRESULT CAcctRepl::ResetGroupsMembers(
  8039. Options * pOptions, //in- Options as set by the user
  8040. TAcctReplNode * pAcct, //in- Account being copied
  8041. TNodeListSortable * pMember, //in- Membership list to restore
  8042. IIManageDBPtr pDb //in- DB object to look up migrated objects.
  8043. )
  8044. {
  8045. // Add all the members back to the group.
  8046. IADsGroup * pGroup;
  8047. HRESULT hr;
  8048. _bstr_t sMember;
  8049. _bstr_t sPath;
  8050. IVarSetPtr pVs(__uuidof(VarSet));
  8051. IUnknown * pUnk;
  8052. DWORD groupType = 0;
  8053. _variant_t var;
  8054. WCHAR sMemPath[LEN_Path];
  8055. WCHAR sPaths[LEN_Path];
  8056. DWORD nPathLen = LEN_Path;
  8057. WCHAR subPath[LEN_Path];
  8058. *sMemPath = L'\0';
  8059. if ( pAcct->WasReplaced() )
  8060. {
  8061. wcscpy(subPath, pAcct->GetTargetPath());
  8062. StuffComputerNameinLdapPath(sPaths, nPathLen, subPath, pOptions, TRUE);
  8063. hr = ADsGetObject(sPaths, IID_IADsGroup, (void**) &pGroup);
  8064. err.MsgWrite(0, DCT_READDING_MEMBERS_TO_GROUP_SS, pAcct->GetTargetName(), sPaths);
  8065. }
  8066. else
  8067. {
  8068. wcscpy(subPath, pAcct->GetSourcePath());
  8069. StuffComputerNameinLdapPath(sPaths, nPathLen, subPath, pOptions, FALSE);
  8070. hr = ADsGetObject(sPaths, IID_IADsGroup, (void**) &pGroup);
  8071. err.MsgWrite(0, DCT_READDING_MEMBERS_TO_GROUP_SS, pAcct->GetName(), sPaths);
  8072. }
  8073. if ( FAILED(hr) ) return hr;
  8074. hr = pGroup->Get(L"groupType", &var);
  8075. if ( SUCCEEDED(hr) )
  8076. {
  8077. groupType = var.lVal;
  8078. }
  8079. for ( TRecordNode * pNode = (TRecordNode*)pMember->Head(); pNode; pNode = (TRecordNode*)pNode->Next())
  8080. {
  8081. if ( pNode->GetARNode() != pAcct )
  8082. continue;
  8083. pVs->QueryInterface(IID_IUnknown, (void**)&pUnk);
  8084. sMember = pNode->GetMemberSam();
  8085. if ( pAcct->WasReplaced() && sMember.length() && !pNode->IsMemberMoved() )
  8086. {
  8087. hr = pDb->raw_GetAMigratedObject(sMember,pOptions->srcDomain, pOptions->tgtDomain, &pUnk);
  8088. }
  8089. else
  8090. {
  8091. hr = S_FALSE; // if we don't have the sam name, don't bother trying to look this one up
  8092. }
  8093. pUnk->Release();
  8094. if ( hr == S_OK )
  8095. {
  8096. // Since we have already migrated this object lets use the target objects information.
  8097. VerifyAndUpdateMigratedTarget(pOptions, pVs);
  8098. sPath = pVs->get(L"MigratedObjects.TargetAdsPath");
  8099. }
  8100. else
  8101. // Other wise use the source objects path to add.
  8102. sPath = pNode->GetMember();
  8103. if ( groupType & 4 )
  8104. {
  8105. // To add local group members we need to change the LDAP path to the SID type path
  8106. IADs * pAds = NULL;
  8107. hr = ADsGetObject((WCHAR*) sPath, IID_IADs, (void**) &pAds);
  8108. if ( SUCCEEDED(hr) )
  8109. hr = pAds->Get(L"objectSid", &var);
  8110. if ( SUCCEEDED(hr) )
  8111. {
  8112. // Make sure the SID we got was in string format
  8113. VariantSidToString(var);
  8114. UStrCpy(sMemPath,L"LDAP://<SID=");
  8115. UStrCpy(sMemPath + UStrLen(sMemPath),var.bstrVal);
  8116. UStrCpy(sMemPath + UStrLen(sMemPath),L">");
  8117. }
  8118. }
  8119. else
  8120. wcscpy(sMemPath, (WCHAR*) sPath);
  8121. WCHAR mesg[LEN_Path];
  8122. wsprintf(mesg, GET_STRING(DCT_MSG_RESET_GROUP_MEMBERS_SS), pAcct->GetName(), (WCHAR*) sMemPath);
  8123. Progress(mesg);
  8124. if ( !pOptions->nochange )
  8125. hr = pGroup->Add(sMemPath);
  8126. else
  8127. hr = S_OK;
  8128. // Try again with LDAP path if SID path failed.
  8129. if ( FAILED(hr) && ( groupType & 4 ) )
  8130. hr = pGroup->Add((WCHAR*) sPath);
  8131. if ( FAILED(hr) )
  8132. {
  8133. hr = BetterHR(hr);
  8134. err.SysMsgWrite(ErrE, hr, DCT_MSG_FAILED_TO_READD_TO_GROUP_SSD,(WCHAR*)sPath, pAcct->GetName(),hr);
  8135. Mark(L"errors", pAcct->GetType());
  8136. }
  8137. else
  8138. {
  8139. err.MsgWrite(0, DCT_MSG_READD_MEMBER_TO_GROUP_SS, (WCHAR*) sPath, pAcct->GetName());
  8140. }
  8141. }
  8142. pGroup->Release();
  8143. return hr;
  8144. }
  8145. BOOL CAcctRepl::TruncateSam(WCHAR * tgtname, TAcctReplNode * acct, Options * options, TNodeListSortable * acctList)
  8146. {
  8147. // SInce we can not copy accounts with lenght more than 20 characters we will truncate
  8148. // it and then add sequence numbers (0-99) in case there are duplicates.
  8149. // we are also going to take into account the global prefix and suffix length while truncating
  8150. // the account.
  8151. BOOL ret = TRUE;
  8152. int lenPref = wcslen(options->globalPrefix);
  8153. int lenSuff = wcslen(options->globalSuffix);
  8154. int lenOrig = wcslen(tgtname);
  8155. int maxLen = 20;
  8156. if ( !_wcsicmp(acct->GetType(), L"group") )
  8157. maxLen = 255;
  8158. else
  8159. maxLen = 20;
  8160. int lenTruncate = maxLen - ( 2 + lenPref + lenSuff );
  8161. // we can not truncate accounts if prefix and suffix are > 20 characters themselves
  8162. if ( lenPref + lenSuff > (maxLen - 2) ) return FALSE;
  8163. WCHAR sTemp[LEN_Path];
  8164. wcscpy(sTemp, tgtname);
  8165. StripSamName(tgtname);
  8166. bool bReplaced = wcscmp(tgtname, sTemp) != 0;
  8167. bool bTruncate = lenPref + lenSuff + lenOrig > maxLen;
  8168. if (bReplaced || bTruncate)
  8169. {
  8170. bool bGenerate = true;
  8171. // if the account was previously migrated
  8172. if (IsAccountMigrated(acct, options, options->pDb, sTemp))
  8173. {
  8174. //if (CheckifAccountExists(options, sTemp))
  8175. //{
  8176. // if prefix matches
  8177. if ((lenPref == 0) || (_wcsnicmp(sTemp, options->globalPrefix, lenPref) == 0))
  8178. {
  8179. // if suffix matches
  8180. if ((lenSuff == 0) || (_wcsnicmp(&sTemp[wcslen(sTemp) - lenSuff], options->globalSuffix, lenSuff) == 0))
  8181. {
  8182. // and portion of name without sequence number matches
  8183. int cchName = wcslen(sTemp) - 2 - lenSuff - lenPref;
  8184. if ((_wcsnicmp(&sTemp[lenPref], tgtname, cchName) == 0))
  8185. {
  8186. // then use previously truncated name
  8187. cchName += 2;
  8188. wcsncpy(tgtname, &sTemp[lenPref], cchName);
  8189. tgtname[cchName] = 0;
  8190. bGenerate = false;
  8191. }
  8192. }
  8193. }
  8194. //}
  8195. }
  8196. // generate truncated name without sequence number
  8197. // if name has not been previously generated and
  8198. // the account does not exist then use generated name
  8199. if (bGenerate)
  8200. {
  8201. // Note: using swprintf instead of wsprintf because
  8202. // swprintf supports supplying length argument for precision
  8203. swprintf(
  8204. sTemp,
  8205. L"%s%.*s%s",
  8206. lenPref ? options->globalPrefix : L"",
  8207. lenTruncate + 2,
  8208. tgtname,
  8209. lenSuff ? options->globalSuffix : L""
  8210. );
  8211. if (acctList->Find(&TNodeFindByNameOnly, sTemp) == NULL)
  8212. {
  8213. if (CheckifAccountExists(options, sTemp) == false)
  8214. {
  8215. tgtname[lenTruncate + 2] = 0;
  8216. if (bTruncate)
  8217. {
  8218. err.MsgWrite(0, DCT_MSG_TRUNCATED_ACCOUNT_NAME_SSD, acct->GetName(), tgtname, maxLen);
  8219. }
  8220. bGenerate = false;
  8221. }
  8222. }
  8223. }
  8224. // generate truncated name with sequence number
  8225. if (bGenerate)
  8226. {
  8227. wcsncpy(sTemp, tgtname, lenTruncate);
  8228. sTemp[lenTruncate] = 0;
  8229. int cnt = 0;
  8230. bool cont = true;
  8231. while (cont)
  8232. {
  8233. wsprintf(
  8234. tgtname,
  8235. L"%s%s%02d%s",
  8236. lenPref ? options->globalPrefix : L"",
  8237. sTemp,
  8238. cnt,
  8239. lenSuff ? options->globalSuffix : L""
  8240. );
  8241. if (acctList->Find(&TNodeFindByNameOnly, tgtname) || CheckifAccountExists(options, tgtname))
  8242. {
  8243. cnt++;
  8244. }
  8245. else
  8246. {
  8247. wsprintf(tgtname, L"%s%02d", sTemp, cnt);
  8248. cont = false;
  8249. // Account is truncated so log a message.
  8250. if (bTruncate)
  8251. {
  8252. err.MsgWrite(0, DCT_MSG_TRUNCATED_ACCOUNT_NAME_SSD, acct->GetName(), tgtname, maxLen);
  8253. }
  8254. }
  8255. if (cnt > 99)
  8256. {
  8257. // We only have 2 digits for numbers so any more than this we can not handle.
  8258. if (bTruncate)
  8259. {
  8260. err.MsgWrite(ErrW,DCT_MSG_FAILED_TO_TRUNCATE_S, acct->GetTargetName());
  8261. }
  8262. Mark(L"warnings",acct->GetType());
  8263. UStrCpy(tgtname, acct->GetTargetName());
  8264. ret = FALSE;
  8265. break;
  8266. }
  8267. }
  8268. }
  8269. }
  8270. return ret;
  8271. }
  8272. //---------------------------------------------------------------------------------------------------------
  8273. // FillNodeFromPath : We will take the LDAP path that is provided to us and from that fill
  8274. // in all the information that is required in AcctRepl node.
  8275. //---------------------------------------------------------------------------------------------------------
  8276. HRESULT CAcctRepl::FillNodeFromPath(
  8277. TAcctReplNode *pAcct, // in-Account node to fillin
  8278. Options * pOptions, //in - Options set by the users
  8279. TNodeListSortable * acctList
  8280. )
  8281. {
  8282. HRESULT hr = S_OK;
  8283. IADsPtr pAds;
  8284. VARIANT var;
  8285. BSTR sText;
  8286. WCHAR text[LEN_Account];
  8287. BOOL bBuiltIn = FALSE;
  8288. WCHAR sSam[LEN_Path];
  8289. VariantInit(&var);
  8290. FillNamingContext(pOptions);
  8291. hr = ADsGetObject(const_cast<WCHAR*>(pAcct->GetSourcePath()), IID_IADs, (void**)&pAds);
  8292. if ( SUCCEEDED(hr) )
  8293. {
  8294. // Check if this is a BuiltIn account.
  8295. hr = pAds->Get(L"isCriticalSystemObject", &var);
  8296. if ( SUCCEEDED(hr) )
  8297. {
  8298. bBuiltIn = V_BOOL(&var) == -1 ? TRUE : FALSE;
  8299. }
  8300. else
  8301. {
  8302. // This must be a NT4 account. We need to get the SID and check if
  8303. // it's RID belongs to the BUILTIN rids.
  8304. hr = pAds->Get(L"objectSID", &var);
  8305. if ( SUCCEEDED(hr) )
  8306. {
  8307. SAFEARRAY * pArray = V_ARRAY(&var);
  8308. PSID pSid;
  8309. hr = SafeArrayAccessData(pArray, (void**)&pSid);
  8310. if ( SUCCEEDED(hr) )
  8311. {
  8312. PUCHAR ucCnt = GetSidSubAuthorityCount(pSid);
  8313. DWORD * rid = (DWORD *) GetSidSubAuthority(pSid, (*ucCnt)-1);
  8314. bBuiltIn = BuiltinRid(*rid);
  8315. if ( bBuiltIn )
  8316. {
  8317. hr = pAds->get_Name(&sText);
  8318. if (SUCCEEDED(hr))
  8319. {
  8320. bBuiltIn = CheckBuiltInWithNTApi(pSid, (WCHAR*) sText, pOptions);
  8321. }
  8322. SysFreeString(sText);
  8323. sText = NULL;
  8324. }
  8325. hr = SafeArrayUnaccessData(pArray);
  8326. }
  8327. VariantClear(&var);
  8328. }
  8329. }
  8330. hr = pAds->get_Class(&sText);
  8331. if ( SUCCEEDED(hr) )
  8332. {
  8333. pAcct->SetType((WCHAR*) sText);
  8334. }
  8335. else
  8336. {
  8337. err.MsgWrite(ErrE, DCT_MSG_GET_REQUIRED_ATTRIBUTE_FAILED, L"objectClass", pAcct->GetSourcePath(), hr);
  8338. Mark(L"errors", (wcslen(pAcct->GetType()) > 0) ? pAcct->GetType() : L"generic");
  8339. pAcct->operations = 0;
  8340. return hr;
  8341. }
  8342. // check if it is a group. If it is then get the group type and store it in the node.
  8343. if ( _wcsicmp((WCHAR*) sText, L"group") == 0 )
  8344. {
  8345. hr = pAds->Get(L"groupType", &var);
  8346. if ( SUCCEEDED(hr) )
  8347. {
  8348. pAcct->SetGroupType((long) V_INT(&var));
  8349. }
  8350. }
  8351. SysFreeString(sText);
  8352. sText = NULL;
  8353. hr = pAds->get_Name(&sText);
  8354. if (SUCCEEDED(hr))
  8355. {
  8356. safecopy(text,(WCHAR*)sText);
  8357. pAcct->SetName(text);
  8358. }
  8359. else
  8360. {
  8361. err.MsgWrite(ErrE, DCT_MSG_GET_REQUIRED_ATTRIBUTE_FAILED, L"distinguishedName", pAcct->GetSourcePath(), hr);
  8362. Mark(L"errors", pAcct->GetType());
  8363. pAcct->operations = 0;
  8364. return hr;
  8365. }
  8366. //if the name includes a '/', then we have to get the escaped version from the path
  8367. //due to a bug in W2K.
  8368. if (wcschr((WCHAR*)sText, L'/'))
  8369. {
  8370. _bstr_t sCNName = GetCNFromPath(_bstr_t(pAcct->GetSourcePath()));
  8371. if (sCNName.length() != 0)
  8372. {
  8373. pAcct->SetName((WCHAR*)sCNName);
  8374. }
  8375. }
  8376. // if inter-forest migration and source object is an InetOrgPerson then...
  8377. if ((pOptions->bSameForest == FALSE) && (_wcsicmp(pAcct->GetType(), s_ClassInetOrgPerson) == 0))
  8378. {
  8379. //
  8380. // must use the naming attribute of the target forest
  8381. //
  8382. // retrieve naming attribute for this class in target forest
  8383. SNamingAttribute naNamingAttribute;
  8384. hr = GetNamingAttribute(pOptions->tgtDomainDns, s_ClassInetOrgPerson, naNamingAttribute);
  8385. if (FAILED(hr))
  8386. {
  8387. err.MsgWrite(ErrE, DCT_MSG_CANNOT_GET_NAMING_ATTRIBUTE_SS, s_ClassInetOrgPerson, pOptions->tgtDomainDns);
  8388. Mark(L"errors", pAcct->GetType());
  8389. return hr;
  8390. }
  8391. _bstr_t strNamingAttribute(naNamingAttribute.strName.c_str());
  8392. // retrieve source attribute value
  8393. VARIANT var;
  8394. VariantInit(&var);
  8395. hr = pAds->Get(strNamingAttribute, &var);
  8396. if (FAILED(hr))
  8397. {
  8398. err.MsgWrite(ErrE, DCT_MSG_CANNOT_GET_SOURCE_ATTRIBUTE_REQUIRED_FOR_NAMING_SSS, naNamingAttribute.strName.c_str(), pAcct->GetSourcePath(), pOptions->tgtDomainDns);
  8399. Mark(L"errors", pAcct->GetType());
  8400. return hr;
  8401. }
  8402. // set target naming attribute value from source attribute value
  8403. pAcct->SetTargetName(strNamingAttribute + L"=" + _bstr_t(_variant_t(var, false)));
  8404. }
  8405. else
  8406. {
  8407. // else set target name equal to source name
  8408. pAcct->SetTargetName(pAcct->GetName());
  8409. }
  8410. hr = pAds->Get(L"sAMAccountName", &var);
  8411. if ( SUCCEEDED(hr))
  8412. {
  8413. // Add the prefix or the suffix as it is needed
  8414. wcscpy(sSam, (WCHAR*)V_BSTR(&var));
  8415. pAcct->SetSourceSam(sSam);
  8416. TruncateSam(sSam, pAcct, pOptions, acctList);
  8417. pAcct->SetTargetSam(sSam);
  8418. AddPrefixSuffix(pAcct, pOptions);
  8419. VariantClear(&var);
  8420. }
  8421. else
  8422. {
  8423. wcscpy(sSam, sText);
  8424. pAcct->SetSourceSam(sSam);
  8425. TruncateSam(sSam, pAcct, pOptions, acctList);
  8426. pAcct->SetTargetSam(sSam);
  8427. AddPrefixSuffix(pAcct, pOptions);
  8428. }
  8429. SysFreeString(sText);
  8430. sText = NULL;
  8431. // Don't know why it is different for WinNT to ADSI
  8432. if ( pOptions->srcDomainVer > 4 )
  8433. hr = pAds->Get(L"profilePath", &var);
  8434. else
  8435. hr = pAds->Get(L"profile", &var);
  8436. if ( SUCCEEDED(hr))
  8437. {
  8438. pAcct->SetSourceProfile((WCHAR*) V_BSTR(&var));
  8439. VariantClear(&var);
  8440. }
  8441. else
  8442. {
  8443. hr = S_OK;
  8444. }
  8445. if ( bBuiltIn )
  8446. {
  8447. // Builtin account so we are going to ignore this account. ( by setting the operation mask to 0 )
  8448. err.MsgWrite(ErrW, DCT_MSG_IGNORING_BUILTIN_S, pAcct->GetSourceSam());
  8449. Mark(L"warnings", pAcct->GetType());
  8450. pAcct->operations = 0;
  8451. }
  8452. }
  8453. else
  8454. {
  8455. err.SysMsgWrite(ErrE, hr, DCT_MSG_OBJECT_NOT_FOUND_SSD, pAcct->GetSourcePath(), opt.srcDomain, hr);
  8456. Mark(L"errors", (wcslen(pAcct->GetType()) > 0) ? pAcct->GetType() : L"generic");
  8457. pAcct->operations = 0;
  8458. }
  8459. return hr;
  8460. }
  8461. //---------------------------------------------------------------------------------------------------------
  8462. // GetNt4Type : Given the account name and the domain finds the type of account.
  8463. //---------------------------------------------------------------------------------------------------------
  8464. BOOL CAcctRepl::GetNt4Type(const WCHAR *sComp, const WCHAR *sAcct, WCHAR *sType)
  8465. {
  8466. DWORD rc = 0;
  8467. USER_INFO_0 * buf;
  8468. BOOL ret = FALSE;
  8469. USER_INFO_1 * specialBuf;
  8470. if ( (rc = NetUserGetInfo(sComp, sAcct, 1, (LPBYTE *) &specialBuf)) == NERR_Success )
  8471. {
  8472. if ( specialBuf->usri1_flags & UF_WORKSTATION_TRUST_ACCOUNT
  8473. || specialBuf->usri1_flags & UF_SERVER_TRUST_ACCOUNT
  8474. || specialBuf->usri1_flags & UF_INTERDOMAIN_TRUST_ACCOUNT )
  8475. {
  8476. // this is not really a user (maybe a computer or a trust account) So we will ignore it.
  8477. ret = FALSE;
  8478. }
  8479. else
  8480. {
  8481. wcscpy(sType, L"user");
  8482. ret = TRUE;
  8483. }
  8484. NetApiBufferFree(specialBuf);
  8485. }
  8486. else if ( (rc = NetGroupGetInfo(sComp, sAcct, 0, (LPBYTE *) &buf)) == NERR_Success )
  8487. {
  8488. wcscpy(sType, L"group");
  8489. NetApiBufferFree(buf);
  8490. ret = TRUE;
  8491. }
  8492. else if ( (rc = NetLocalGroupGetInfo(sComp, sAcct, 0, (LPBYTE *) &buf)) == NERR_Success )
  8493. {
  8494. wcscpy(sType, L"group");
  8495. NetApiBufferFree(buf);
  8496. ret = TRUE;
  8497. }
  8498. return ret;
  8499. }
  8500. //------------------------------------------------------------------------------
  8501. // UndoCopy: This function Undoes the copying of the accounts. It currently
  8502. // does the following tasks. Add to it if needed.
  8503. // 1. Deletes the target account if Inter-Forest, but replace source acocunts
  8504. // in local groups for accounts migrated by ADMT.
  8505. // 2. Moves the object back to its original position if Intra-Forest.
  8506. // 3. Calls the Undo function on the Extensions
  8507. //------------------------------------------------------------------------------
  8508. int CAcctRepl::UndoCopy(
  8509. Options * options, // in -options
  8510. TNodeListSortable * acctlist, // in -list of accounts to process
  8511. ProgressFn * progress, // in -window to write progress messages to
  8512. TError & error, // in -window to write error messages to
  8513. IStatusObj * pStatus, // in -status object to support cancellation
  8514. void WindowUpdate (void ) // in - window update function
  8515. )
  8516. {
  8517. BOOL bSameForest = FALSE;
  8518. // sort the account list by Source Type\Source Name
  8519. acctlist->CompareSet(&TNodeCompareAccountType);
  8520. acctlist->SortedToScrambledTree();
  8521. acctlist->Sort(&TNodeCompareAccountType);
  8522. acctlist->Balance();
  8523. long rc;
  8524. // Since these are Win2k domains we need to process it with Win2k code.
  8525. MCSDCTWORKEROBJECTSLib::IAccessCheckerPtr pAccess(__uuidof(MCSDCTWORKEROBJECTSLib::AccessChecker));
  8526. // First of all we need to find out if they are in the same forest.
  8527. HRESULT hr = S_OK;
  8528. if ( BothWin2K(options) )
  8529. {
  8530. hr = pAccess->raw_IsInSameForest(options->srcDomainDns,options->tgtDomainDns, (long*)&bSameForest);
  8531. }
  8532. if ( SUCCEEDED(hr) )
  8533. {
  8534. if ( !bSameForest )
  8535. // Different forest we need to delete the one that we had previously created.
  8536. rc = DeleteObject(options, acctlist, progress, pStatus);
  8537. else
  8538. {
  8539. // Within a forest we can move the object around.
  8540. TNodeListSortable * pList = NULL;
  8541. hr = MakeAcctListFromMigratedObjects(options, options->lUndoActionID, pList,TRUE);
  8542. if ( SUCCEEDED(hr) && pList )
  8543. {
  8544. if ( pList->IsTree() ) pList->ToSorted();
  8545. pList->CompareSet(&TNodeCompareAccountType);
  8546. pList->UnsortedToTree();
  8547. pList->Balance();
  8548. rc = MoveObj2K(options, pList, progress, pStatus);
  8549. }
  8550. else
  8551. {
  8552. err.SysMsgWrite(ErrE,hr,DCT_MSG_FAILED_TO_LOAD_UNDO_LIST_D,hr);
  8553. Mark(L"errors", L"generic");
  8554. }
  8555. }
  8556. if ( progress )
  8557. progress(L"");
  8558. }
  8559. return rc;
  8560. }
  8561. int CAcctRepl::DeleteObject(
  8562. Options * pOptions, //in -Options that we recieved from the user
  8563. TNodeListSortable * acctlist, //in -AcctList of accounts to be copied.
  8564. ProgressFn * progress, //in -Progress Function to display messages
  8565. IStatusObj * pStatus // in -status object to support cancellation
  8566. )
  8567. {
  8568. TNodeListSortable * pList = NULL;
  8569. TNodeTreeEnum tenum;
  8570. TAcctReplNode * acct = NULL, * tNext = NULL;
  8571. HRESULT hr = S_OK;
  8572. DWORD rc = 0;
  8573. WCHAR mesg[LEN_Path];
  8574. IUnknown * pUnk = NULL;
  8575. IVarSetPtr pVs(__uuidof(VarSet));
  8576. _variant_t var;
  8577. hr = MakeAcctListFromMigratedObjects(pOptions, pOptions->lUndoActionID, pList,FALSE);
  8578. if ( SUCCEEDED(hr) && pList )
  8579. {
  8580. if ( pList->IsTree() ) pList->ToSorted();
  8581. pList->SortedToScrambledTree();
  8582. pList->Sort(&TNodeCompareAccountSam);
  8583. pList->Balance();
  8584. /* restore source account of account being deleted in local groups prior to deleting
  8585. the target account */
  8586. wcscpy(mesg, GET_STRING(IDS_LG_MEMBER_FIXUP_UNDO));
  8587. if ( progress )
  8588. progress(mesg);
  8589. ReplaceSourceInLocalGroup(pList, pOptions, pStatus);
  8590. for ( acct = (TAcctReplNode *)tenum.OpenFirst(pList) ; acct ; acct = tNext)
  8591. {
  8592. // Call the extensions for undo
  8593. wsprintf(mesg, GET_STRING(IDS_RUNNING_EXTS_S), acct->GetTargetPath());
  8594. if ( progress )
  8595. progress(mesg);
  8596. Mark(L"processed",acct->GetType());
  8597. // Close the log file if it is open
  8598. WCHAR filename[LEN_Path];
  8599. err.LogClose();
  8600. if (m_pExt)
  8601. m_pExt->Process(acct, pOptions->tgtDomain, pOptions,FALSE);
  8602. safecopy (filename,opt.logFile);
  8603. err.LogOpen(filename,1 /*append*/ );
  8604. if ( acct->GetStatus() & AR_Status_Created )
  8605. {
  8606. wsprintf(mesg, GET_STRING(IDS_DELETING_S), acct->GetTargetPath());
  8607. if ( progress ) progress(mesg);
  8608. if ( ! _wcsicmp(acct->GetType(),L"computer") )
  8609. {
  8610. // do not delete the computer accounts, because if we do,
  8611. // the computer will be immediately locked out of the domain
  8612. tNext = (TAcctReplNode *) tenum.Next();
  8613. pList->Remove(acct);
  8614. delete acct;
  8615. continue;
  8616. }
  8617. //
  8618. // If updating of rights was specified and objects are being deleted from a W2K domain
  8619. // then explicitly remove rights for objects as W2K does not automatically remove
  8620. // rights when an object is deleted.
  8621. //
  8622. if (m_UpdateUserRights && (pOptions->tgtDomainVer == 5) && (pOptions->tgtDomainVerMinor == 0))
  8623. {
  8624. hr = EnumerateAccountRights(TRUE, acct);
  8625. if (SUCCEEDED(hr))
  8626. {
  8627. RemoveAccountRights(TRUE, acct);
  8628. }
  8629. }
  8630. // Now delete the account.
  8631. if ( !_wcsicmp(acct->GetType(), s_ClassUser) || !_wcsicmp(acct->GetType(), s_ClassInetOrgPerson) )
  8632. rc = NetUserDel(pOptions->tgtComp, acct->GetTargetSam());
  8633. else
  8634. {
  8635. // Must be a group try both local and global.
  8636. rc = NetGroupDel(pOptions->tgtComp, acct->GetTargetSam());
  8637. if ( rc )
  8638. rc = NetLocalGroupDel(pOptions->tgtComp, acct->GetTargetSam());
  8639. }
  8640. // Log a message
  8641. if ( !rc )
  8642. {
  8643. err.MsgWrite(0, DCT_MSG_ACCOUNT_DELETED_S, (WCHAR*)acct->GetTargetPath());
  8644. Mark(L"created",acct->GetType());
  8645. }
  8646. else
  8647. {
  8648. err.SysMsgWrite(ErrE, rc, DCT_MSG_DELETE_ACCOUNT_FAILED_SD, (WCHAR*)acct->GetTargetPath(), rc);
  8649. Mark(L"errors", acct->GetType());
  8650. }
  8651. }
  8652. else
  8653. {
  8654. err.MsgWrite(ErrW, DCT_MSG_NO_DELETE_WAS_REPLACED_S, acct->GetTargetPath());
  8655. Mark(L"warnings",acct->GetType());
  8656. }
  8657. tNext = (TAcctReplNode *) tenum.Next();
  8658. pList->Remove(acct);
  8659. delete acct;
  8660. }
  8661. tenum.Close();
  8662. delete pList;
  8663. }
  8664. if ( pUnk ) pUnk->Release();
  8665. return rc;
  8666. }
  8667. HRESULT CAcctRepl::MakeAcctListFromMigratedObjects(Options * pOptions, long lUndoActionID, TNodeListSortable *& pAcctList,BOOL bReverseDomains)
  8668. {
  8669. IVarSetPtr pVs(__uuidof(VarSet));
  8670. IUnknown * pUnk = NULL;
  8671. HRESULT hr = S_OK;
  8672. _bstr_t sSName, sTName, sSSam, sTSam, sType, sSUPN, sSDSid;
  8673. long lSRid, lTRid;
  8674. long lStat;
  8675. WCHAR sActionInfo[LEN_Path];
  8676. hr = pVs->QueryInterface(IID_IUnknown, (void**)&pUnk);
  8677. if ( SUCCEEDED(hr) )
  8678. hr = pOptions->pDb->raw_GetMigratedObjects( pOptions->lUndoActionID, &pUnk);
  8679. if ( SUCCEEDED(hr) )
  8680. {
  8681. pAcctList = new TNodeListSortable();
  8682. if (!pAcctList)
  8683. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  8684. long lCnt = pVs->get("MigratedObjects");
  8685. for ( long l = 0; l < lCnt; l++)
  8686. {
  8687. wsprintf(sActionInfo, L"MigratedObjects.%d.%s", l, GET_STRING(DB_SourceAdsPath));
  8688. sSName = pVs->get(sActionInfo);
  8689. wsprintf(sActionInfo, L"MigratedObjects.%d.%s", l, GET_STRING(DB_TargetAdsPath));
  8690. sTName = pVs->get(sActionInfo);
  8691. wsprintf(sActionInfo, L"MigratedObjects.%d.%s", l, GET_STRING(DB_status));
  8692. lStat = pVs->get(sActionInfo);
  8693. wsprintf(sActionInfo, L"MigratedObjects.%d.%s", l, GET_STRING(DB_TargetSamName));
  8694. sTSam = pVs->get(sActionInfo);
  8695. wsprintf(sActionInfo, L"MigratedObjects.%d.%s", l, GET_STRING(DB_SourceSamName));
  8696. sSSam = pVs->get(sActionInfo);
  8697. wsprintf(sActionInfo, L"MigratedObjects.%d.%s", l, GET_STRING(DB_Type));
  8698. sType = pVs->get(sActionInfo);
  8699. wsprintf(sActionInfo, L"MigratedObjects.%d.%s", l, GET_STRING(DB_SourceDomainSid));
  8700. sSDSid = pVs->get(sActionInfo);
  8701. wsprintf(sActionInfo, L"MigratedObjects.%d.%s", l, GET_STRING(DB_SourceRid));
  8702. lSRid = pVs->get(sActionInfo);
  8703. wsprintf(sActionInfo, L"MigratedObjects.%d.%s", l, GET_STRING(DB_TargetRid));
  8704. lTRid = pVs->get(sActionInfo);
  8705. TAcctReplNode * pNode = new TAcctReplNode();
  8706. if (!pNode)
  8707. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  8708. if ( bReverseDomains )
  8709. {
  8710. pNode->SetSourcePath((WCHAR*) sTName);
  8711. pNode->SetTargetPath((WCHAR*) sSName);
  8712. pNode->SetSourceSam((WCHAR*) sTSam);
  8713. pNode->SetTargetSam((WCHAR*) sSSam);
  8714. pNode->SetSourceRid(lTRid);
  8715. pNode->SetTargetRid(lSRid);
  8716. //if we are moving the acounts back during an undo, get the source UPN for this account
  8717. GetAccountUPN(pOptions, sSName, sSUPN);
  8718. pNode->SetSourceUPN((WCHAR*) sSUPN);
  8719. }
  8720. else
  8721. {
  8722. pNode->SetSourcePath((WCHAR*) sSName);
  8723. pNode->SetTargetPath((WCHAR*) sTName);
  8724. pNode->SetSourceSam((WCHAR*) sSSam);
  8725. pNode->SetTargetSam((WCHAR*) sTSam);
  8726. pNode->SetSourceRid(lSRid);
  8727. pNode->SetTargetRid(lTRid);
  8728. }
  8729. pNode->SetType((WCHAR*) sType);
  8730. pNode->SetStatus(lStat);
  8731. pNode->SetSourceSid(SidFromString((WCHAR*)sSDSid));
  8732. pAcctList->InsertBottom((TNode*) pNode);
  8733. }
  8734. }
  8735. return hr;
  8736. }
  8737. void CAcctRepl::AddPrefixSuffix( TAcctReplNode * pNode, Options * pOptions )
  8738. {
  8739. DWORD dwLen = 0;
  8740. c_array<WCHAR> achSs(LEN_Path);
  8741. c_array<WCHAR> achTgt(LEN_Path);
  8742. c_array<WCHAR> achPref(LEN_Path);
  8743. c_array<WCHAR> achSuf(LEN_Path);
  8744. c_array<WCHAR> achTemp(LEN_Path);
  8745. c_array<WCHAR> achTargetSamName(LEN_Path);
  8746. wcscpy(achTargetSamName, pNode->GetTargetSam());
  8747. if ( wcslen(pOptions->globalPrefix) )
  8748. {
  8749. int truncate = 255;
  8750. if ( !_wcsicmp(pNode->GetType(), s_ClassUser) || !_wcsicmp(pNode->GetType(), s_ClassInetOrgPerson) )
  8751. {
  8752. truncate = 20 - wcslen(pOptions->globalPrefix);
  8753. }
  8754. else if ( !_wcsicmp(pNode->GetType(), L"computer") )
  8755. {
  8756. // fix up the trailing $
  8757. // assume that achTargetSamName always ends with $
  8758. // if the length of achTargetSamName (including $) is greater than truncate,
  8759. // we need to add in $ before string terminator
  8760. // since the trailing $ comes from the original sam name, we use MAX_COMPUTERNAME_LENGTH + 1
  8761. // to calculate the truncation position
  8762. truncate = MAX_COMPUTERNAME_LENGTH + 1 - wcslen(pOptions->globalPrefix);
  8763. if (truncate < wcslen(achTargetSamName))
  8764. {
  8765. WCHAR sTruncatedSamName[LEN_Path];
  8766. wcscpy(sTruncatedSamName, achTargetSamName);
  8767. sTruncatedSamName[truncate - 1] = L'$';
  8768. sTruncatedSamName[truncate] = L'\0';
  8769. err.MsgWrite(0, DCT_MSG_TRUNCATED_COMPUTER_NAME_SSD, (WCHAR*)achTargetSamName, sTruncatedSamName, MAX_COMPUTERNAME_LENGTH);
  8770. achTargetSamName[truncate - 1] = L'$';
  8771. }
  8772. }
  8773. // make sure we truncate the account so we dont get account names that are very large.
  8774. achTargetSamName[truncate] = L'\0';
  8775. // Prefix is specified so lets just add that.
  8776. wsprintf(achTemp, L"%s%s", pOptions->globalPrefix, (WCHAR*)achTargetSamName);
  8777. wcscpy(achTgt, pNode->GetTargetName());
  8778. for ( DWORD z = 0; z < wcslen(achTgt); z++ )
  8779. {
  8780. if ( achTgt[z] == L'=' ) break;
  8781. }
  8782. if ( z < wcslen(achTgt) )
  8783. {
  8784. // Get the prefix part ex.CN=
  8785. wcsncpy(achPref, achTgt, z+1);
  8786. achPref[z+1] = 0;
  8787. wcscpy(achSuf, achTgt+z+1);
  8788. }
  8789. else
  8790. {
  8791. wcscpy(achPref,L"");
  8792. wcscpy(achSuf,achTgt);
  8793. }
  8794. // Remove the \ if it is escaping the space
  8795. if ( achSuf[0] == L'\\' && achSuf[1] == L' ' )
  8796. {
  8797. WCHAR achTemp[LEN_Path];
  8798. wcscpy(achTemp, achSuf+1);
  8799. wcscpy(achSuf, achTemp);
  8800. }
  8801. // Build the target string with the Prefix
  8802. wsprintf(achTgt, L"%s%s%s", (WCHAR*)achPref, pOptions->globalPrefix, (WCHAR*)achSuf);
  8803. pNode->SetTargetSam(achTemp);
  8804. pNode->SetTargetName(achTgt);
  8805. }
  8806. else if ( wcslen(pOptions->globalSuffix) )
  8807. {
  8808. int truncate = 255;
  8809. if ( !_wcsicmp(pNode->GetType(), s_ClassUser) || !_wcsicmp(pNode->GetType(), s_ClassInetOrgPerson) )
  8810. {
  8811. truncate = 20 - wcslen(pOptions->globalSuffix);
  8812. }
  8813. else if ( !_wcsicmp(pNode->GetType(), L"computer") )
  8814. {
  8815. // since the trailing $ does not come from the original sam name, we have to use MAX_COMPUTERNAME_LENGTH
  8816. // to calculate the truncation position
  8817. truncate = MAX_COMPUTERNAME_LENGTH - wcslen(pOptions->globalSuffix);
  8818. if (truncate < wcslen(achTargetSamName))
  8819. {
  8820. WCHAR sTruncatedSamName[LEN_Path];
  8821. wcscpy(sTruncatedSamName, achTargetSamName);
  8822. sTruncatedSamName[truncate] = L'$';
  8823. sTruncatedSamName[truncate + 1] = L'\0';
  8824. err.MsgWrite(0, DCT_MSG_TRUNCATED_COMPUTER_NAME_SSD, (WCHAR*)achTargetSamName, sTruncatedSamName, MAX_COMPUTERNAME_LENGTH);
  8825. }
  8826. }
  8827. // make sure we truncate the account so we dont get account names that are very large.
  8828. achTargetSamName[truncate] = L'\0';
  8829. // Suffix is specified.
  8830. if ( !_wcsicmp( pNode->GetType(), L"computer") )
  8831. {
  8832. // We need to make sure we take into account the $ sign in computer sam name.
  8833. dwLen = wcslen(achTargetSamName);
  8834. // Get rid of the $ sign
  8835. wcscpy(achSs, achTargetSamName);
  8836. if ( achSs[dwLen - 1] == L'$' )
  8837. {
  8838. achSs[dwLen - 1] = L'\0';
  8839. }
  8840. wsprintf(achTemp, L"%s%s$", (WCHAR*)achSs, pOptions->globalSuffix);
  8841. }
  8842. else
  8843. {
  8844. //Simply add the suffix to all other accounts.
  8845. wsprintf(achTemp, L"%s%s", (WCHAR*)achTargetSamName, pOptions->globalSuffix);
  8846. }
  8847. // Remove the trailing space \ escape sequence
  8848. wcscpy(achTgt, pNode->GetName());
  8849. for ( int i = wcslen(achTgt)-1; i >= 0; i-- )
  8850. {
  8851. if ( achTgt[i] != L' ' )
  8852. break;
  8853. }
  8854. if ( achTgt[i] == L'\\' )
  8855. {
  8856. WCHAR * pTemp = &achTgt[i];
  8857. *pTemp = 0;
  8858. wcscpy(achPref, achTgt);
  8859. wcscpy(achSuf, pTemp+1);
  8860. }
  8861. else
  8862. {
  8863. wcscpy(achPref, achTgt);
  8864. wcscpy(achSuf, L"");
  8865. }
  8866. wsprintf(achTgt, L"%s%s%s", (WCHAR*)achPref, (WCHAR*)achSuf, pOptions->globalSuffix);
  8867. pNode->SetTargetSam(achTemp);
  8868. pNode->SetTargetName(achTgt);
  8869. }
  8870. }
  8871. void CAcctRepl::BuildTargetPath(WCHAR const * sCN, WCHAR const * tgtOU, WCHAR * stgtPath)
  8872. {
  8873. WCHAR pTemp[LEN_Path];
  8874. DWORD dwArraySizeOfpTemp = sizeof(pTemp)/sizeof(pTemp[0]);
  8875. if (tgtOU == NULL)
  8876. _com_issue_error(E_INVALIDARG);
  8877. if (wcslen(tgtOU) >= dwArraySizeOfpTemp)
  8878. _com_issue_error(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
  8879. wcscpy(pTemp, tgtOU);
  8880. *stgtPath = L'\0';
  8881. // Make sure it is a LDAP path.
  8882. if ( !wcsncmp(L"LDAP://", pTemp, 7) )
  8883. {
  8884. // Get the LDAP://<DOMAIN>/ part.
  8885. WCHAR * p = wcschr(pTemp + 7, L'/');
  8886. // Build the string.
  8887. if (p)
  8888. {
  8889. *p = L'\0';
  8890. wsprintf(stgtPath, L"%s/%s,%s", pTemp, sCN, p+1);
  8891. }
  8892. }
  8893. }
  8894. HRESULT CAcctRepl::BetterHR(HRESULT hr)
  8895. {
  8896. HRESULT temp = hr;
  8897. if ( hr == 0x8007001f || hr == 0x80071392 ) temp = HRESULT_FROM_WIN32(NERR_UserExists);
  8898. else if ( hr == 0x80072030 || hr == 0x80070534 ) temp = HRESULT_FROM_WIN32(NERR_UserNotFound);
  8899. return temp;
  8900. }
  8901. HRESULT CAcctRepl::GetThePrimaryGroupMembers(Options * pOptions, WCHAR * sGroupSam, IEnumVARIANT ** pVar)
  8902. {
  8903. // This function looks for accounts that have the primaryGroupID set to the rid of the
  8904. // group in the argument.
  8905. BSTR pCols = L"aDSPath";
  8906. DWORD rid = 0;
  8907. HRESULT hr;
  8908. if ( GetRidForGroup(pOptions, sGroupSam, rid) )
  8909. hr = QueryPrimaryGroupMembers(pCols, pOptions, rid, pVar);
  8910. else
  8911. hr = HRESULT_FROM_WIN32(GetLastError());
  8912. return hr;
  8913. }
  8914. HRESULT CAcctRepl::AddPrimaryGroupMembers(Options * pOptions, SAFEARRAY * multiVals, WCHAR * sGroupSam)
  8915. {
  8916. // This function will get the accounts with primarygroupID = Group's RID and
  8917. // adds the DN for these Accounts to the safearry in the argument list.
  8918. BSTR pCols = L"distinguishedName";
  8919. DWORD rid = 0, dwFetch = 0;
  8920. IEnumVARIANT * pEnum = NULL;
  8921. HRESULT hr = S_OK;
  8922. _variant_t var;
  8923. _variant_t * var2;
  8924. SAFEARRAYBOUND bd;
  8925. long lb, ub;
  8926. _variant_t * pData = NULL;
  8927. SafeArrayGetLBound(multiVals, 1, &lb);
  8928. SafeArrayGetUBound(multiVals, 1, &ub);
  8929. bd.lLbound = lb;
  8930. bd.cElements = ub - lb + 1;
  8931. if ( GetRidForGroup(pOptions, sGroupSam, rid) )
  8932. {
  8933. hr = QueryPrimaryGroupMembers(pCols, pOptions, rid, &pEnum);
  8934. if ( SUCCEEDED(hr) )
  8935. {
  8936. while ( pEnum->Next(1, &var, &dwFetch) == S_OK )
  8937. {
  8938. if (var.vt == (VT_ARRAY|VT_VARIANT))
  8939. {
  8940. SAFEARRAY * pArray = var.parray;
  8941. hr = SafeArrayAccessData(pArray, (void **)&var2);
  8942. if ( SUCCEEDED(hr) )
  8943. {
  8944. // Add one more element to the array.
  8945. bd.cElements++;
  8946. hr = SafeArrayRedim(multiVals, &bd);
  8947. }
  8948. // Fill in the new element with the information in the variant.
  8949. if ( SUCCEEDED(hr) )
  8950. hr = SafeArrayAccessData(multiVals, (void HUGEP**) &pData);
  8951. if ( SUCCEEDED(hr) )
  8952. {
  8953. pData[++ub] = *var2;
  8954. SafeArrayUnaccessData(multiVals);
  8955. }
  8956. if ( SUCCEEDED(hr) )
  8957. SafeArrayUnaccessData(pArray);
  8958. var.Clear();
  8959. }
  8960. else
  8961. // Something really bad happened we should not get here in normal cond
  8962. hr = E_FAIL;
  8963. }
  8964. }
  8965. }
  8966. else
  8967. hr = HRESULT_FROM_WIN32(GetLastError());
  8968. if ( pEnum ) pEnum->Release();
  8969. return hr;
  8970. }
  8971. bool CAcctRepl::GetRidForGroup(Options * pOptions, WCHAR * sGroupSam, DWORD& rid)
  8972. {
  8973. // We lookup the Account name and get its SID. Once we have the SID we extract the RID and return that
  8974. SID_NAME_USE use;
  8975. PSID sid = (PSID) new BYTE[LEN_Path];
  8976. WCHAR dom[LEN_Path];
  8977. DWORD cbsid = LEN_Path, cbDom = LEN_Path;
  8978. bool ret = true;
  8979. if (!sid)
  8980. return false;
  8981. if ( LookupAccountName(pOptions->srcComp, sGroupSam, sid, &cbsid, dom, &cbDom, &use) )
  8982. {
  8983. // we now have the sid so get its sub authority count.
  8984. PUCHAR pSubCnt = GetSidSubAuthorityCount(sid);
  8985. DWORD * pRid = GetSidSubAuthority(sid, (*pSubCnt) -1 );
  8986. rid = *pRid;
  8987. }
  8988. else
  8989. ret = false;
  8990. delete [] sid;
  8991. return ret;
  8992. }
  8993. HRESULT CAcctRepl::QueryPrimaryGroupMembers(BSTR cols, Options * pOptions, DWORD rid, IEnumVARIANT** pEnum)
  8994. {
  8995. WCHAR sQuery[LEN_Path];
  8996. WCHAR sCont[LEN_Path];
  8997. SAFEARRAY * colNames;
  8998. SAFEARRAYBOUND bd = { 1, 0 };
  8999. INetObjEnumeratorPtr pQuery(__uuidof(NetObjEnumerator));
  9000. BSTR * pData;
  9001. HRESULT hr;
  9002. wsprintf(sQuery, L"(primaryGroupID=%d)", rid);
  9003. wsprintf(sCont, L"LDAP://%s", pOptions->srcDomain);
  9004. colNames = SafeArrayCreate(VT_BSTR, 1, &bd);
  9005. hr = SafeArrayAccessData(colNames, (void**)&pData);
  9006. if ( SUCCEEDED(hr) )
  9007. {
  9008. pData[0] = SysAllocString(cols);
  9009. hr = SafeArrayUnaccessData(colNames);
  9010. }
  9011. if ( SUCCEEDED(hr) )
  9012. hr = pQuery->SetQuery(sCont, pOptions->srcDomain, sQuery, ADS_SCOPE_SUBTREE, FALSE);
  9013. if ( SUCCEEDED(hr) )
  9014. hr = pQuery->SetColumns(colNames);
  9015. if ( SUCCEEDED(hr) )
  9016. hr = pQuery->Execute(pEnum);
  9017. return hr;
  9018. }
  9019. // CheckBuiltInWithNTApi - This function makes sure that the account really is a
  9020. // builtin account with the NT APIs. In case of NT4 accounts
  9021. // there are certain special accounts that the WinNT provider
  9022. // gives us a SID that is the SYSTEM sid ( example Service ).
  9023. // To make sure that this account exists we use LookupAccountName
  9024. // with domain qualified account name to make sure that the account
  9025. // is really builtin or not.
  9026. BOOL CAcctRepl::CheckBuiltInWithNTApi(PSID pSid, WCHAR *sSam, Options * pOptions)
  9027. {
  9028. BOOL retVal = TRUE;
  9029. WCHAR sName[LEN_Path];
  9030. SID_NAME_USE use;
  9031. DWORD cbDomain = LEN_Path, cbSid = LEN_Path;
  9032. PSID pAccSid = new BYTE[LEN_Path];
  9033. WCHAR sDomain[LEN_Path];
  9034. if (!pAccSid)
  9035. return TRUE;
  9036. wsprintf(sName, L"%s\\%s", pOptions->srcDomainFlat, sSam);
  9037. if ( LookupAccountName(pOptions->srcComp, sName, pAccSid, &cbSid, sDomain, &cbDomain, &use) )
  9038. {
  9039. // We found the account now we need to check the sid with the sid passed in and if they
  9040. // are the same then this is a builtin account otherwise its not.
  9041. retVal = EqualSid(pSid, pAccSid);
  9042. }
  9043. delete [] pAccSid;
  9044. return retVal;
  9045. }
  9046. BOOL CAcctRepl::StuffComputerNameinLdapPath(WCHAR *sAdsPath, DWORD nPathLen, WCHAR *sSubPath, Options *pOptions, BOOL bTarget)
  9047. {
  9048. BOOL ret = FALSE;
  9049. _bstr_t sTemp;
  9050. if ((!sAdsPath) || (!sSubPath))
  9051. return FALSE;
  9052. WCHAR * pTemp = wcschr(sSubPath + 7, L'/'); // Filter out the 'LDAP://<domain-name>/' from the path
  9053. if ( pTemp )
  9054. {
  9055. sTemp = L"LDAP://";
  9056. if ( bTarget )
  9057. sTemp += (pOptions->tgtComp + 2);
  9058. else
  9059. sTemp += (pOptions->srcComp + 2);
  9060. sTemp += L"/";
  9061. sTemp += (pTemp + 1);
  9062. if (sTemp.length() > 0)
  9063. {
  9064. wcsncpy(sAdsPath, sTemp, nPathLen-1);
  9065. ret = TRUE;
  9066. }
  9067. }
  9068. return ret;
  9069. }
  9070. BOOL CAcctRepl::DoesTargetObjectAlreadyExist(TAcctReplNode * pAcct, Options * pOptions)
  9071. {
  9072. // Check to see if the target object already exists
  9073. WCHAR sPath[LEN_Path];
  9074. DWORD nPathLen = LEN_Path;
  9075. BOOL bObjectExists = FALSE;
  9076. WCHAR * pRelativeTgtOUPath;
  9077. WCHAR path[LEN_Path] = L"";
  9078. IADs * pAdsTemp = NULL;
  9079. WCHAR sSrcTemp[LEN_Path];
  9080. WCHAR * pTemp = NULL;
  9081. // First, check the target path, to see if an object with the same CN already exists
  9082. if ( ! pOptions->bUndo )
  9083. {
  9084. MakeFullyQualifiedAdsPath(sPath, nPathLen, pOptions->tgtOUPath, pOptions->tgtDomain, pOptions->tgtNamingContext);
  9085. pRelativeTgtOUPath = wcschr(sPath + UStrLen(L"LDAP://") + 2,L'/');
  9086. }
  9087. else
  9088. {
  9089. UStrCpy(sPath,pAcct->GetTargetPath());
  9090. pRelativeTgtOUPath = wcschr(sPath + UStrLen(L"LDAP://") + 2,L'/');
  9091. if (pRelativeTgtOUPath)
  9092. {
  9093. // get the target CN name
  9094. pTemp = pRelativeTgtOUPath + 1;
  9095. (*pRelativeTgtOUPath) = 0;
  9096. do
  9097. {
  9098. pRelativeTgtOUPath = wcschr(pRelativeTgtOUPath+1,L',');
  9099. } while ((pRelativeTgtOUPath) && ( *(pRelativeTgtOUPath-1) == L'\\' ));
  9100. }
  9101. }
  9102. if ( pRelativeTgtOUPath )
  9103. {
  9104. *pRelativeTgtOUPath = 0;
  9105. if ( pOptions->bUndo && pTemp )
  9106. {
  9107. pAcct->SetTargetName(pTemp);
  9108. // get the source CN name for the account
  9109. UStrCpy(sSrcTemp,pAcct->GetSourcePath());
  9110. WCHAR * start = wcschr(sSrcTemp + UStrLen(L"LDAP://")+2,L'/');
  9111. *start = 0;
  9112. start++;
  9113. WCHAR * comma = start-1;
  9114. do
  9115. {
  9116. comma = wcschr(comma+1,L',');
  9117. } while ( *(comma-1) == L'\\' );
  9118. *comma = 0;
  9119. pAcct->SetName(start);
  9120. }
  9121. swprintf(path,L"%ls/%ls,%ls",sPath,pAcct->GetTargetName(),pRelativeTgtOUPath+1);
  9122. if ( pOptions->bUndo )
  9123. {
  9124. UStrCpy(pOptions->tgtOUPath,pRelativeTgtOUPath+1);
  9125. }
  9126. }
  9127. else
  9128. {
  9129. MakeFullyQualifiedAdsPath(path, nPathLen, pOptions->tgtOUPath, pOptions->tgtDomain, pOptions->tgtNamingContext);
  9130. }
  9131. HRESULT hr = ADsGetObject(path,IID_IADs,(void**)&pAdsTemp);
  9132. if ( SUCCEEDED(hr) )
  9133. {
  9134. pAdsTemp->Release();
  9135. bObjectExists = TRUE;
  9136. }
  9137. // Also, check the SAM name to see if it exists on the target
  9138. hr = LookupAccountInTarget(pOptions,const_cast<WCHAR*>(pAcct->GetTargetSam()),sPath);
  9139. if ( SUCCEEDED(hr) )
  9140. {
  9141. bObjectExists = TRUE;
  9142. }
  9143. else
  9144. {
  9145. hr = 0;
  9146. }
  9147. return bObjectExists;
  9148. }
  9149. //-----------------------------------------------------------------------------------------
  9150. // UpdateMemberToGroups This function updates the groups that the accounts are members of.
  9151. // adding this member to all the groups that have been migrated.
  9152. //-----------------------------------------------------------------------------------------
  9153. HRESULT CAcctRepl::UpdateMemberToGroups(TNodeListSortable * acctList, Options *pOptions, BOOL bGrpsOnly)
  9154. {
  9155. TNodeListSortable newList;
  9156. WCHAR mesg[LEN_Path];
  9157. HRESULT hr = S_OK;
  9158. // Expand the containers and the membership
  9159. wcscpy(mesg, GET_STRING(IDS_EXPANDING_MEMBERSHIP));
  9160. Progress(mesg);
  9161. // Expand the list to include all the groups that the accounts in this list are members of
  9162. newList.CompareSet(&TNodeCompareAccountTypeAndRDN); //set to sort by type and source path RDN
  9163. if ( !newList.IsTree() ) newList.SortedToTree();
  9164. // Call expand membership function to get a list of all groups that contain as members objects in our account list
  9165. if ( ExpandMembership( acctList, pOptions, &newList, Progress, bGrpsOnly, TRUE) )
  9166. {
  9167. if ( newList.IsTree() ) newList.ToSorted();
  9168. TNodeListEnum e;
  9169. Lookup p;
  9170. for (TAcctReplNode* pNode = (TAcctReplNode *)e.OpenFirst((TNodeList*)&newList); pNode; pNode = (TAcctReplNode*)e.Next())
  9171. {
  9172. hr = S_OK;
  9173. IADsGroupPtr spGroup;
  9174. _bstr_t strGroupName;
  9175. // go through each of the account nodes in the newly added account list. Since
  9176. // we have special map that contain the members information we can use that
  9177. //For each member in the member map for this group, find the account node that corresponds to the member
  9178. //information and possibly add the member to the group
  9179. CGroupMemberMap::iterator itGrpMemberMap;
  9180. for (itGrpMemberMap = pNode->mapGrpMember.begin(); itGrpMemberMap != pNode->mapGrpMember.end(); itGrpMemberMap++)
  9181. {
  9182. p.pName = (WCHAR*)(itGrpMemberMap->first);
  9183. p.pType = (WCHAR*)(itGrpMemberMap->second);
  9184. TAcctReplNode * pNodeMember = (TAcctReplNode *) acctList->Find(&TNodeFindAccountName, &p);
  9185. bool bIgnored = false;
  9186. if (pNodeMember)
  9187. bIgnored = ((!pNodeMember->WasReplaced()) && (pNodeMember->GetStatus() & AR_Status_AlreadyExisted));
  9188. // If we found one ( we should always find one. ) and the member was successfuly
  9189. // added or replaced the member information.
  9190. if ( pNodeMember && ((pNodeMember->WasCreated() || pNodeMember->WasReplaced()) || bIgnored))
  9191. {
  9192. // Get the Group pointer (once per group) and add the target object to the member.
  9193. if (spGroup)
  9194. {
  9195. hr = S_OK;
  9196. }
  9197. else
  9198. {
  9199. hr = ADsGetObject(const_cast<WCHAR*>(pNode->GetTargetPath()), IID_IADsGroup, (void**)&spGroup);
  9200. if (SUCCEEDED(hr))
  9201. {
  9202. BSTR bstr = 0;
  9203. spGroup->get_Name(&bstr);
  9204. strGroupName = _bstr_t(bstr, false);
  9205. }
  9206. }
  9207. if ( SUCCEEDED(hr) )
  9208. {
  9209. if ( pOptions->nochange )
  9210. {
  9211. VARIANT_BOOL bIsMem;
  9212. hr = spGroup->IsMember(const_cast<WCHAR*>(pNodeMember->GetTargetPath()), &bIsMem);
  9213. if ( SUCCEEDED(hr) )
  9214. {
  9215. if ( bIsMem )
  9216. hr = HRESULT_FROM_WIN32(NERR_UserExists);
  9217. }
  9218. }
  9219. else
  9220. {
  9221. //add the new account to the group
  9222. hr = spGroup->Add(const_cast<WCHAR*>(pNodeMember->GetTargetPath()));
  9223. /* if the new account's source account is also in the group, remove it */
  9224. IIManageDBPtr pDB = pOptions->pDb;
  9225. IVarSetPtr pVsTemp(__uuidof(VarSet));
  9226. IUnknownPtr spUnknown(pVsTemp);
  9227. IUnknown* pUnk = spUnknown;
  9228. //is this account in the migrated objects table
  9229. HRESULT hrGet = pDB->raw_GetAMigratedObject(_bstr_t(pNodeMember->GetSourceSam()), pOptions->srcDomain, pOptions->tgtDomain, &pUnk);
  9230. if (hrGet == S_OK)
  9231. {
  9232. //remove the source account from the group
  9233. RemoveSourceAccountFromGroup(spGroup, pVsTemp, pOptions);
  9234. }
  9235. }//end else not no change mode
  9236. }//end if got group pointer
  9237. if ( SUCCEEDED(hr) )
  9238. err.MsgWrite(0, DCT_MSG_ADDED_TO_GROUP_SS, pNodeMember->GetTargetPath(), (WCHAR*)strGroupName);
  9239. else
  9240. {
  9241. if (strGroupName.length() == 0)
  9242. strGroupName = pNode->GetTargetPath();
  9243. hr = BetterHR(hr);
  9244. if ( HRESULT_CODE(hr) == NERR_UserExists )
  9245. {
  9246. err.MsgWrite(0,DCT_MSG_USER_IN_GROUP_SS,pNodeMember->GetTargetPath(), (WCHAR*)strGroupName);
  9247. }
  9248. else if ( HRESULT_CODE(hr) == NERR_UserNotFound )
  9249. {
  9250. err.SysMsgWrite(0, hr, DCT_MSG_MEMBER_NONEXIST_SS, pNodeMember->GetTargetPath(), (WCHAR*)strGroupName, hr);
  9251. }
  9252. else
  9253. {
  9254. // message for the generic failure case
  9255. err.SysMsgWrite(ErrW, hr, DCT_MSG_FAILED_TO_ADD_TO_GROUP_SSD, pNodeMember->GetTargetPath(), (WCHAR*)strGroupName, hr);
  9256. Mark(L"warnings", pNodeMember->GetType());
  9257. }
  9258. }//end else failed to add account to group
  9259. }//end if found account to add to group
  9260. }//for each member in the enumerated group node's member map
  9261. }//for each account being migrated
  9262. // Clean up the list.
  9263. TAcctReplNode * pNext = NULL;
  9264. for ( pNode = (TAcctReplNode *)e.OpenFirst(&newList); pNode; pNode = pNext)
  9265. {
  9266. pNext = (TAcctReplNode *)e.Next();
  9267. newList.Remove(pNode);
  9268. delete pNode;
  9269. }
  9270. }
  9271. return hr;
  9272. }
  9273. // This function enumerates all members of the Universal/Global groups and for each member
  9274. // checks if that member has been migrated. If it is then it removes the source member and
  9275. // adds the target member.
  9276. HRESULT CAcctRepl::ResetMembersForUnivGlobGroups(Options *pOptions, TAcctReplNode *pAcct)
  9277. {
  9278. IADsGroup * pGroup;
  9279. HRESULT hr;
  9280. _bstr_t sMember;
  9281. _bstr_t sTgtMem;
  9282. WCHAR sSrcPath[LEN_Path];
  9283. WCHAR sTgtPath[LEN_Path];
  9284. DWORD nPathLen = LEN_Path;
  9285. IVarSetPtr pVs(__uuidof(VarSet));
  9286. IUnknown * pUnk;
  9287. IADsMembers * pMembers = NULL;
  9288. IEnumVARIANT * pEnum = NULL;
  9289. _variant_t var;
  9290. if ( pAcct->WasReplaced() )
  9291. {
  9292. WCHAR subPath[LEN_Path];
  9293. WCHAR sPaths[LEN_Path];
  9294. wcscpy(subPath, pAcct->GetTargetPath());
  9295. StuffComputerNameinLdapPath(sPaths, nPathLen, subPath, pOptions, TRUE);
  9296. hr = ADsGetObject(sPaths, IID_IADsGroup, (void**) &pGroup);
  9297. err.MsgWrite(0, DCT_UPDATING_MEMBERS_TO_GROUP_SS, pAcct->GetTargetName(), sPaths);
  9298. }
  9299. else
  9300. return S_OK;
  9301. if ( FAILED(hr) ) return hr;
  9302. hr = pGroup->Members(&pMembers);
  9303. if ( SUCCEEDED(hr) )
  9304. {
  9305. hr = pMembers->get__NewEnum((IUnknown**)&pEnum);
  9306. }
  9307. if ( SUCCEEDED(hr) )
  9308. {
  9309. DWORD dwFet = 0;
  9310. while ( pEnum->Next(1, &var, &dwFet) == S_OK )
  9311. {
  9312. IDispatch * pDisp = var.pdispVal;
  9313. IADs * pAds = NULL;
  9314. pDisp->QueryInterface(IID_IADs, (void**)&pAds);
  9315. pAds->Get(L"distinguishedName", &var);
  9316. pAds->Release();
  9317. sMember = var;
  9318. pVs->QueryInterface(IID_IUnknown, (void**)&pUnk);
  9319. hr = pOptions->pDb->raw_GetMigratedObjectBySourceDN(sMember, &pUnk);
  9320. pUnk->Release();
  9321. if ( hr == S_OK )
  9322. {
  9323. // Since we have moved this member we should remove it from the group
  9324. // and add target member to the group.
  9325. sTgtMem = pVs->get(L"MigratedObjects.TargetAdsPath");
  9326. _bstr_t sTgtType = pVs->get(L"MigratedObjects.Type");
  9327. if ( !_wcsicmp(L"computer", (WCHAR*) sTgtType ) )
  9328. {
  9329. MakeFullyQualifiedAdsPath(sSrcPath, nPathLen, (WCHAR*)sMember, pOptions->srcComp + 2, L"");
  9330. MakeFullyQualifiedAdsPath(sTgtPath, nPathLen, (WCHAR*)sTgtMem, pOptions->tgtComp + 2, L"");
  9331. // HRESULT hr1 = pGroup->Remove(sSrcPath);
  9332. pGroup->Remove(sSrcPath);
  9333. if ( ! pOptions->nochange )
  9334. hr = pGroup->Add(sTgtPath);
  9335. else
  9336. hr = 0;
  9337. if ( SUCCEEDED(hr) )
  9338. {
  9339. err.MsgWrite(0, DCT_REPLACE_MEMBER_TO_GROUP_SSS, (WCHAR*)sMember, (WCHAR*) sTgtMem, pAcct->GetTargetName());
  9340. }
  9341. else
  9342. {
  9343. err.SysMsgWrite(ErrE, hr, DCT_REPLACE_MEMBER_FAILED_SSS, (WCHAR*)sMember, (WCHAR*) sTgtMem, pAcct->GetTargetName());
  9344. }
  9345. }
  9346. }
  9347. }
  9348. }
  9349. if ( pEnum ) pEnum->Release();
  9350. if ( pMembers ) pMembers->Release();
  9351. return hr;
  9352. }
  9353. /* This function will get the varset from the action history table for the given
  9354. undo action ID. We will find the given source name and retrieve the UPN for
  9355. that account */
  9356. void CAcctRepl::GetAccountUPN(Options * pOptions, _bstr_t sSName, _bstr_t& sSUPN)
  9357. {
  9358. HRESULT hr;
  9359. IUnknown * pUnk = NULL;
  9360. IVarSetPtr pVsAH(__uuidof(VarSet));
  9361. sSUPN = L"";
  9362. hr = pVsAH->QueryInterface(IID_IUnknown, (void**)&pUnk);
  9363. //fill a varset with the setting from the action to be undone from the Action History table
  9364. if ( SUCCEEDED(hr) )
  9365. hr = pOptions->pDb->raw_GetActionHistory(pOptions->lUndoActionID, &pUnk);
  9366. if (pUnk) pUnk->Release();
  9367. if ( hr == S_OK )
  9368. {
  9369. WCHAR key[MAX_PATH];
  9370. bool bFound = false;
  9371. int i = 0;
  9372. long numAccounts = pVsAH->get(GET_BSTR(DCTVS_Accounts_NumItems));
  9373. _bstr_t tempName;
  9374. while ((i<numAccounts) && (!bFound))
  9375. {
  9376. swprintf(key,GET_STRING(DCTVSFmt_Accounts_D),i);
  9377. tempName = pVsAH->get(key);
  9378. if (_wcsicmp((WCHAR*)tempName, (WCHAR*)sSName) == 0)
  9379. {
  9380. bFound = true;
  9381. swprintf(key,GET_STRING(DCTVSFmt_Accounts_SourceUPN_D),i);
  9382. sSUPN = pVsAH->get(key);
  9383. }
  9384. i++;
  9385. }//end while
  9386. }//end if S_OK
  9387. }
  9388. /*********************************************************************
  9389. * *
  9390. * Written by: Paul Thompson *
  9391. * Date: 1 NOV 2000 *
  9392. * *
  9393. * This function is responsible for updating the *
  9394. * manager\directReports properties for a migrated user or the *
  9395. * managedBy\managedObjects properties for a migrated group. *
  9396. * *
  9397. *********************************************************************/
  9398. //BEGIN UpdateManagement
  9399. HRESULT CAcctRepl::UpdateManagement(TNodeListSortable * acctList, Options *pOptions)
  9400. {
  9401. /* local variables */
  9402. HRESULT hr = S_OK;
  9403. TAcctReplNode * pAcct;
  9404. IEnumVARIANT * pEnum;
  9405. INetObjEnumeratorPtr pQuery(__uuidof(NetObjEnumerator));
  9406. INetObjEnumeratorPtr pQuery2(__uuidof(NetObjEnumerator));
  9407. LPWSTR sUCols[] = { L"directReports",L"managedObjects", L"manager"};
  9408. int nUCols = DIM(sUCols);
  9409. LPWSTR sGCols[] = { L"managedBy" };
  9410. int nGCols = DIM(sGCols);
  9411. SAFEARRAY * cols;
  9412. SAFEARRAYBOUND bdU = { nUCols, 0 };
  9413. SAFEARRAYBOUND bdG = { nGCols, 0 };
  9414. BSTR HUGEP * pData = NULL;
  9415. _bstr_t sQuery;
  9416. _variant_t varMgr;
  9417. _variant_t varDR;
  9418. _variant_t varMdO;
  9419. _variant_t varMain;
  9420. _variant_t HUGEP * pDt, * pVar;
  9421. DWORD dwf;
  9422. _bstr_t sTPath;
  9423. _bstr_t sPath;
  9424. _bstr_t sSam;
  9425. _bstr_t sType;
  9426. _bstr_t sName;
  9427. _bstr_t sTgtName;
  9428. long lgrpType;
  9429. WCHAR mesg[LEN_Path];
  9430. IADs * pDSE = NULL;
  9431. WCHAR strText[LEN_Path];
  9432. _variant_t varGC;
  9433. /* function body */
  9434. //change from a tree to a sorted list
  9435. if ( acctList->IsTree() ) acctList->ToSorted();
  9436. //prepare to connect to the GC
  9437. _bstr_t sGCDomain = pOptions->srcDomainDns;
  9438. swprintf(strText,L"LDAP://%ls/RootDSE",pOptions->srcDomainDns);
  9439. hr = ADsGetObject(strText,IID_IADs,(void**)&pDSE);
  9440. if ( SUCCEEDED(hr) )
  9441. {
  9442. hr = pDSE->Get(L"RootDomainNamingContext",&varGC);
  9443. if ( SUCCEEDED(hr) )
  9444. sGCDomain = GetDomainDNSFromPath(varGC.bstrVal);
  9445. }
  9446. _bstr_t sGCPath = _bstr_t(L"GC://") + sGCDomain;
  9447. //for each account migrated, if not excluded, migrate the manager\directReports
  9448. for ( pAcct = (TAcctReplNode*)acctList->Head(); pAcct; pAcct = (TAcctReplNode*)pAcct->Next())
  9449. {
  9450. if ( pOptions->pStatus )
  9451. {
  9452. LONG status = 0;
  9453. HRESULT hr = pOptions->pStatus->get_Status(&status);
  9454. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  9455. {
  9456. if ( !bAbortMessageWritten )
  9457. {
  9458. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  9459. bAbortMessageWritten = true;
  9460. }
  9461. break;
  9462. }
  9463. }
  9464. //update the message
  9465. wsprintf(mesg, GET_STRING(IDS_UPDATING_MGR_PROPS_S), pAcct->GetName());
  9466. Progress(mesg);
  9467. //build the path to the source object
  9468. WCHAR sPathSource[LEN_Path];
  9469. DWORD nPathLen = LEN_Path;
  9470. StuffComputerNameinLdapPath(sPathSource, nPathLen, const_cast<WCHAR*>(pAcct->GetSourcePath()), pOptions, FALSE);
  9471. //connect to the GC instead of a specific DC
  9472. WCHAR * pTemp = wcschr(sPathSource + 7, L'/');
  9473. if ( pTemp )
  9474. {
  9475. _bstr_t sNewPath = sGCPath + _bstr_t(pTemp);
  9476. wcscpy(sPathSource, sNewPath);
  9477. }
  9478. //for user, migrate the manager\directReports managedObjects relationship
  9479. if (!_wcsicmp(pAcct->GetType(), s_ClassUser) || !_wcsicmp(pAcct->GetType(), s_ClassInetOrgPerson))
  9480. {
  9481. bool bDoManager = false;
  9482. bool bDoManagedObjects = false;
  9483. //if the property has explicitly been excluded from migration by the user, don't migrate it
  9484. if (pOptions->bExcludeProps)
  9485. {
  9486. PCWSTR pszExcludeList;
  9487. if (!_wcsicmp(pAcct->GetType(), s_ClassUser))
  9488. {
  9489. pszExcludeList = pOptions->sExcUserProps;
  9490. }
  9491. else if (!_wcsicmp(pAcct->GetType(), s_ClassInetOrgPerson))
  9492. {
  9493. pszExcludeList = pOptions->sExcInetOrgPersonProps;
  9494. }
  9495. else
  9496. {
  9497. pszExcludeList = NULL;
  9498. }
  9499. if (!IsStringInDelimitedString(pszExcludeList, L"*", L','))
  9500. {
  9501. if (!IsStringInDelimitedString(pszExcludeList, L"manager", L',') &&
  9502. !IsStringInDelimitedString(pszExcludeList, L"directReports", L','))
  9503. {
  9504. bDoManager = true;
  9505. }
  9506. if (!IsStringInDelimitedString(pszExcludeList, L"managedObjects", L','))
  9507. {
  9508. bDoManagedObjects = true;
  9509. }
  9510. }
  9511. }
  9512. else
  9513. {
  9514. bDoManager = true;
  9515. bDoManagedObjects = true;
  9516. }
  9517. if (!bDoManager && !bDoManagedObjects)
  9518. {
  9519. continue;
  9520. }
  9521. /* get the "manager", and "directReports", and "managedObjects" property */
  9522. //build the column array
  9523. cols = SafeArrayCreate(VT_BSTR, 1, &bdU);
  9524. SafeArrayAccessData(cols, (void HUGEP **) &pData);
  9525. for ( int i = 0; i < nUCols; i++)
  9526. pData[i] = SysAllocString(sUCols[i]);
  9527. SafeArrayUnaccessData(cols);
  9528. sQuery = L"(objectClass=*)";
  9529. //query the information
  9530. hr = pQuery->raw_SetQuery(sPathSource, pOptions->srcDomain, sQuery, ADS_SCOPE_SUBTREE, TRUE);
  9531. if (FAILED(hr)) return FALSE;
  9532. hr = pQuery->raw_SetColumns(cols);
  9533. if (FAILED(hr)) return FALSE;
  9534. hr = pQuery->raw_Execute(&pEnum);
  9535. if (FAILED(hr)) return FALSE;
  9536. while (pEnum->Next(1, &varMain, &dwf) == S_OK)
  9537. {
  9538. SAFEARRAY * vals = V_ARRAY(&varMain);
  9539. // Get the VARIANT Array out
  9540. SafeArrayAccessData(vals, (void HUGEP**) &pDt);
  9541. varDR = pDt[0];
  9542. varMdO = pDt[1];
  9543. varMgr = pDt[2];
  9544. SafeArrayUnaccessData(vals);
  9545. //process the manager by setting the manager on the moved user if the
  9546. //source user's manager has been migrated
  9547. if ( bDoManager && (varMgr.vt & VT_ARRAY) )
  9548. {
  9549. //we always get an Array of variants
  9550. SAFEARRAY * multiVals = varMgr.parray;
  9551. SafeArrayAccessData(multiVals, (void HUGEP **) &pVar);
  9552. for ( DWORD dw = 0; dw < multiVals->rgsabound->cElements; dw++ )
  9553. {
  9554. _bstr_t sManager = _bstr_t(V_BSTR(&pVar[dw]));
  9555. sManager = PadDN(sManager);
  9556. _bstr_t sSrcDomain = GetDomainDNSFromPath(sManager);
  9557. sPath = _bstr_t(L"LDAP://") + sSrcDomain + _bstr_t(L"/") + sManager;
  9558. if (GetSamFromPath(sPath, sSam, sType, sName, sTgtName, lgrpType, pOptions))
  9559. {
  9560. IVarSetPtr pVs(__uuidof(VarSet));
  9561. IUnknown * pUnk = NULL;
  9562. pVs->QueryInterface(IID_IUnknown, (void**) &pUnk);
  9563. WCHAR sDomainNB[LEN_Path];
  9564. WCHAR sDNS[LEN_Path];
  9565. //get NetBIOS of the objects source domain
  9566. GetDnsAndNetbiosFromName(sSrcDomain, sDomainNB, sDNS);
  9567. // See if the manager was migrated
  9568. hr = pOptions->pDb->raw_GetAMigratedObjectToAnyDomain((WCHAR*)sSam, sDomainNB, &pUnk);
  9569. if ( hr == S_OK )
  9570. {
  9571. VerifyAndUpdateMigratedTarget(pOptions, pVs);
  9572. _variant_t var;
  9573. //get the manager's target adspath
  9574. var = pVs->get(L"MigratedObjects.TargetAdsPath");
  9575. sTPath = V_BSTR(&var);
  9576. if ( wcslen((WCHAR*)sTPath) > 0 )
  9577. {
  9578. IADsUser * pUser = NULL;
  9579. //set the manager on the target object
  9580. hr = ADsGetObject((WCHAR*)pAcct->GetTargetPath(), IID_IADsUser, (void**)&pUser);
  9581. if ( SUCCEEDED(hr) )
  9582. {
  9583. _bstr_t sTemp = _bstr_t(wcsstr((WCHAR*)sTPath, L"CN="));
  9584. var = sTemp;
  9585. hr = pUser->Put(L"Manager", var);
  9586. if ( SUCCEEDED(hr) )
  9587. {
  9588. hr = pUser->SetInfo();
  9589. if (FAILED(hr))
  9590. err.SysMsgWrite(0, hr, DCT_MSG_MANAGER_MIG_FAILED, (WCHAR*)sTPath, (WCHAR*)pAcct->GetTargetPath(), hr);
  9591. }
  9592. else
  9593. err.SysMsgWrite(0, hr, DCT_MSG_MANAGER_MIG_FAILED, (WCHAR*)sTPath, (WCHAR*)pAcct->GetTargetPath(), hr);
  9594. pUser->Release();
  9595. }
  9596. else
  9597. err.SysMsgWrite(0, hr, DCT_MSG_MANAGER_MIG_FAILED, (WCHAR*)sTPath, (WCHAR*)pAcct->GetTargetPath(), hr);
  9598. }//end if got the path to the manager on the target
  9599. }//end if manager was migrated
  9600. pUnk->Release();
  9601. }//end if got source sam
  9602. }//for each manager (only one)
  9603. SafeArrayUnaccessData(multiVals);
  9604. }//end if variant array (it will be)
  9605. //process the directReports by setting the manager on the previously moved
  9606. //user if the source user's manager has been migrated
  9607. if ( bDoManager && (varDR.vt & VT_ARRAY) )
  9608. {
  9609. //we always get an Array of variants
  9610. SAFEARRAY * multiVals = varDR.parray;
  9611. SafeArrayAccessData(multiVals, (void HUGEP **) &pVar);
  9612. for ( DWORD dw = 0; dw < multiVals->rgsabound->cElements; dw++ )
  9613. {
  9614. _bstr_t sDirectReport = _bstr_t(V_BSTR(&pVar[dw]));
  9615. sDirectReport = PadDN(sDirectReport);
  9616. _bstr_t sSrcDomain = GetDomainDNSFromPath(sDirectReport);
  9617. sPath = _bstr_t(L"LDAP://") + sSrcDomain + _bstr_t(L"/") + sDirectReport;
  9618. if (GetSamFromPath(sPath, sSam, sType, sName, sTgtName, lgrpType, pOptions))
  9619. {
  9620. IVarSetPtr pVs(__uuidof(VarSet));
  9621. IUnknown * pUnk = NULL;
  9622. pVs->QueryInterface(IID_IUnknown, (void**) &pUnk);
  9623. WCHAR sDomainNB[LEN_Path];
  9624. WCHAR sDNS[LEN_Path];
  9625. //get NetBIOS of the objects source domain
  9626. GetDnsAndNetbiosFromName(sSrcDomain, sDomainNB, sDNS);
  9627. // See if the direct report was migrated
  9628. hr = pOptions->pDb->raw_GetAMigratedObjectToAnyDomain((WCHAR*)sSam, sDomainNB, &pUnk);
  9629. if ( hr == S_OK )
  9630. {
  9631. VerifyAndUpdateMigratedTarget(pOptions, pVs);
  9632. _variant_t var;
  9633. //get the direct report's target adspath
  9634. var = pVs->get(L"MigratedObjects.TargetAdsPath");
  9635. sTPath = V_BSTR(&var);
  9636. if ( wcslen((WCHAR*)sTPath) > 0 )
  9637. {
  9638. IADsUser * pUser = NULL;
  9639. //set the manager on the target object
  9640. hr = ADsGetObject(sTPath, IID_IADsUser, (void**)&pUser);
  9641. if ( SUCCEEDED(hr) )
  9642. {
  9643. _bstr_t sTemp = _bstr_t(wcsstr((WCHAR*)pAcct->GetTargetPath(), L"CN="));
  9644. var = sTemp;
  9645. hr = pUser->Put(L"Manager", var);
  9646. if ( SUCCEEDED(hr) )
  9647. {
  9648. hr = pUser->SetInfo();
  9649. if (FAILED(hr))
  9650. err.SysMsgWrite(0, hr, DCT_MSG_MANAGER_MIG_FAILED, (WCHAR*)pAcct->GetTargetPath(), (WCHAR*)sTPath, hr);
  9651. }
  9652. else
  9653. err.SysMsgWrite(0, hr, DCT_MSG_MANAGER_MIG_FAILED, (WCHAR*)pAcct->GetTargetPath(), (WCHAR*)sTPath, hr);
  9654. pUser->Release();
  9655. }
  9656. else
  9657. err.SysMsgWrite(0, hr, DCT_MSG_MANAGER_MIG_FAILED, (WCHAR*)pAcct->GetTargetPath(), (WCHAR*)sTPath, hr);
  9658. }//end if got the path to the manager on the target
  9659. }//end if manager was migrated
  9660. pUnk->Release();
  9661. }//end if got source sam
  9662. }//for each directReport
  9663. SafeArrayUnaccessData(multiVals);
  9664. }//end if variant array (it will be)
  9665. /* get the "managedObjects" property */
  9666. //process the managedObjects by setting the managedBy on the moved group if the
  9667. //source user's managed group has been migrated
  9668. if ( bDoManagedObjects && (varMdO.vt & VT_ARRAY) )
  9669. {
  9670. //we always get an Array of variants
  9671. SAFEARRAY * multiVals = varMdO.parray;
  9672. SafeArrayAccessData(multiVals, (void HUGEP **) &pVar);
  9673. for ( DWORD dw = 0; dw < multiVals->rgsabound->cElements; dw++ )
  9674. {
  9675. _bstr_t sManaged = _bstr_t(V_BSTR(&pVar[dw]));
  9676. sManaged = PadDN(sManaged);
  9677. _bstr_t sSrcDomain = GetDomainDNSFromPath(sManaged);
  9678. sPath = _bstr_t(L"LDAP://") + sSrcDomain + _bstr_t(L"/") + sManaged;
  9679. if (GetSamFromPath(sPath, sSam, sType, sName, sTgtName, lgrpType, pOptions))
  9680. {
  9681. IVarSetPtr pVs(__uuidof(VarSet));
  9682. IUnknown * pUnk = NULL;
  9683. pVs->QueryInterface(IID_IUnknown, (void**) &pUnk);
  9684. WCHAR sDomainNB[LEN_Path];
  9685. WCHAR sDNS[LEN_Path];
  9686. //get NetBIOS of the objects source domain
  9687. GetDnsAndNetbiosFromName(sSrcDomain, sDomainNB, sDNS);
  9688. // See if the managed object was migrated
  9689. hr = pOptions->pDb->raw_GetAMigratedObjectToAnyDomain((WCHAR*)sSam, sDomainNB, &pUnk);
  9690. if ( hr == S_OK )
  9691. {
  9692. VerifyAndUpdateMigratedTarget(pOptions, pVs);
  9693. _variant_t var;
  9694. //get the managed object's target adspath
  9695. var = pVs->get(L"MigratedObjects.TargetAdsPath");
  9696. sTPath = V_BSTR(&var);
  9697. if ( wcslen((WCHAR*)sTPath) > 0 )
  9698. {
  9699. IADsGroup * pGroup = NULL;
  9700. //set the manager on the target object
  9701. hr = ADsGetObject(sTPath, IID_IADsGroup, (void**)&pGroup);
  9702. if ( SUCCEEDED(hr) )
  9703. {
  9704. _bstr_t sTemp = _bstr_t(wcsstr((WCHAR*)pAcct->GetTargetPath(), L"CN="));
  9705. var = sTemp;
  9706. hr = pGroup->Put(L"ManagedBy", var);
  9707. if ( SUCCEEDED(hr) )
  9708. {
  9709. hr = pGroup->SetInfo();
  9710. if (FAILED(hr))
  9711. err.SysMsgWrite(0, hr, DCT_MSG_MANAGER_MIG_FAILED, (WCHAR*)pAcct->GetTargetPath(), (WCHAR*)sTPath, hr);
  9712. }
  9713. else
  9714. err.SysMsgWrite(0, hr, DCT_MSG_MANAGER_MIG_FAILED, (WCHAR*)pAcct->GetTargetPath(), (WCHAR*)sTPath, hr);
  9715. pGroup->Release();
  9716. }
  9717. else
  9718. err.SysMsgWrite(0, hr, DCT_MSG_MANAGER_MIG_FAILED, (WCHAR*)pAcct->GetTargetPath(), (WCHAR*)sTPath, hr);
  9719. }//end if got the path to the manager on the target
  9720. }//end if manager was migrated
  9721. pUnk->Release();
  9722. }//end if got source sam
  9723. }//for each manager (only one)
  9724. SafeArrayUnaccessData(multiVals);
  9725. }//end if variant array (it will be)
  9726. varMgr.Clear();
  9727. varMdO.Clear();
  9728. varDR.Clear();
  9729. VariantInit(&varMain); // data not owned by varMain so clear VARTYPE
  9730. }
  9731. if (pEnum)
  9732. pEnum->Release();
  9733. // SafeArrayDestroy(cols);
  9734. }//end if user
  9735. //for group, migrate the managedBy\managedObjects relationship
  9736. if (!_wcsicmp(pAcct->GetType(), L"group"))
  9737. {
  9738. //if the managedBy property has explicitly been excluded from migration by the user, don't migrate it
  9739. if (pOptions->bExcludeProps &&
  9740. (IsStringInDelimitedString(pOptions->sExcGroupProps, L"managedBy", L',') ||
  9741. IsStringInDelimitedString(pOptions->sExcGroupProps, L"*", L',')))
  9742. continue;
  9743. /* get the "managedBy" property */
  9744. //build the column array
  9745. cols = SafeArrayCreate(VT_BSTR, 1, &bdG);
  9746. SafeArrayAccessData(cols, (void HUGEP **) &pData);
  9747. for ( int i = 0; i < nGCols; i++)
  9748. pData[i] = SysAllocString(sGCols[i]);
  9749. SafeArrayUnaccessData(cols);
  9750. sQuery = L"(objectClass=*)";
  9751. //query the information
  9752. hr = pQuery->raw_SetQuery(sPathSource, pOptions->srcDomain, sQuery, ADS_SCOPE_BASE, TRUE);
  9753. if (FAILED(hr)) return FALSE;
  9754. hr = pQuery->raw_SetColumns(cols);
  9755. if (FAILED(hr)) return FALSE;
  9756. hr = pQuery->raw_Execute(&pEnum);
  9757. if (FAILED(hr)) return FALSE;
  9758. while (pEnum->Next(1, &varMain, &dwf) == S_OK)
  9759. {
  9760. SAFEARRAY * vals = V_ARRAY(&varMain);
  9761. // Get the VARIANT Array out
  9762. SafeArrayAccessData(vals, (void HUGEP**) &pDt);
  9763. varMgr = pDt[0];
  9764. SafeArrayUnaccessData(vals);
  9765. //process the managedBy by setting the managedBy on the moved group if the
  9766. //source group's manager has been migrated
  9767. if ( varMgr.vt & VT_BSTR )
  9768. {
  9769. _bstr_t sManager = varMgr;
  9770. sManager = PadDN(sManager);
  9771. _bstr_t sSrcDomain = GetDomainDNSFromPath(sManager);
  9772. sPath = _bstr_t(L"LDAP://") + sSrcDomain + _bstr_t(L"/") + sManager;
  9773. if (GetSamFromPath(sPath, sSam, sType, sName, sTgtName, lgrpType, pOptions))
  9774. {
  9775. IVarSetPtr pVs(__uuidof(VarSet));
  9776. IUnknown * pUnk = NULL;
  9777. pVs->QueryInterface(IID_IUnknown, (void**) &pUnk);
  9778. WCHAR sDomainNB[LEN_Path];
  9779. WCHAR sDNS[LEN_Path];
  9780. //get NetBIOS of the objects source domain
  9781. GetDnsAndNetbiosFromName(sSrcDomain, sDomainNB, sDNS);
  9782. // See if the manager was migrated
  9783. hr = pOptions->pDb->raw_GetAMigratedObjectToAnyDomain((WCHAR*)sSam, sDomainNB, &pUnk);
  9784. if ( hr == S_OK )
  9785. {
  9786. VerifyAndUpdateMigratedTarget(pOptions, pVs);
  9787. _variant_t var;
  9788. //get the manager's target adspath
  9789. var = pVs->get(L"MigratedObjects.TargetAdsPath");
  9790. sTPath = V_BSTR(&var);
  9791. if ( wcslen((WCHAR*)sTPath) > 0 )
  9792. {
  9793. IADsGroup * pGroup = NULL;
  9794. //set the manager on the target object
  9795. hr = ADsGetObject((WCHAR*)pAcct->GetTargetPath(), IID_IADsGroup, (void**)&pGroup);
  9796. if ( SUCCEEDED(hr) )
  9797. {
  9798. _bstr_t sTemp = _bstr_t(wcsstr((WCHAR*)sTPath, L"CN="));
  9799. var = sTemp;
  9800. hr = pGroup->Put(L"ManagedBy", var);
  9801. if ( SUCCEEDED(hr) )
  9802. {
  9803. hr = pGroup->SetInfo();
  9804. if (FAILED(hr))
  9805. err.SysMsgWrite(0, hr, DCT_MSG_MANAGER_MIG_FAILED, (WCHAR*)sTPath, (WCHAR*)pAcct->GetTargetPath(), hr);
  9806. }
  9807. else
  9808. err.SysMsgWrite(0, hr, DCT_MSG_MANAGER_MIG_FAILED, (WCHAR*)sTPath, (WCHAR*)pAcct->GetTargetPath(), hr);
  9809. pGroup->Release();
  9810. }
  9811. else
  9812. err.SysMsgWrite(0, hr, DCT_MSG_MANAGER_MIG_FAILED, (WCHAR*)sTPath, (WCHAR*)pAcct->GetTargetPath(), hr);
  9813. }//end if got the path to the manager on the target
  9814. }//end if manager was migrated
  9815. pUnk->Release();
  9816. }//end if got source sam
  9817. }//end if variant array (it will be)
  9818. varMgr.Clear();
  9819. varMain.Clear();
  9820. }
  9821. if (pEnum)
  9822. pEnum->Release();
  9823. // SafeArrayDestroy(cols);
  9824. }//end if group
  9825. }//end for each account being migrated
  9826. wcscpy(mesg, L"");
  9827. Progress(mesg);
  9828. return hr;
  9829. }
  9830. //END UpdateManagement
  9831. /*********************************************************************
  9832. * *
  9833. * Written by: Paul Thompson *
  9834. * Date: 29 NOV 2000 *
  9835. * *
  9836. * This function is responsible for removing the escape character*
  9837. * in front of any '/' characters. *
  9838. * *
  9839. *********************************************************************/
  9840. //BEGIN GetUnEscapedNameWithFwdSlash
  9841. _bstr_t CAcctRepl::GetUnEscapedNameWithFwdSlash(_bstr_t strName)
  9842. {
  9843. /* local variables */
  9844. WCHAR szNameOld[MAX_PATH];
  9845. WCHAR szNameNew[MAX_PATH];
  9846. WCHAR * pchBeg = NULL;
  9847. _bstr_t sNewName = L"";
  9848. /* function body */
  9849. if (strName.length())
  9850. {
  9851. safecopy(szNameOld, (WCHAR*)strName);
  9852. for (WCHAR* pch = wcschr(szNameOld, _T('\\')); pch; pch = wcschr(pch + 1, _T('\\')))
  9853. {
  9854. if ((*(pch + 1)) == L'/')
  9855. {
  9856. if (pchBeg == NULL)
  9857. {
  9858. wcsncpy(szNameNew, szNameOld, pch - szNameOld);
  9859. szNameNew[pch - szNameOld] = L'\0';
  9860. }
  9861. else
  9862. {
  9863. size_t cch = wcslen(szNameNew);
  9864. wcsncat(szNameNew, pchBeg, pch - pchBeg);
  9865. szNameNew[cch + (pch - pchBeg)] = L'\0';
  9866. }
  9867. pchBeg = pch + 1;
  9868. }
  9869. }
  9870. if (pchBeg == NULL)
  9871. wcscpy(szNameNew, szNameOld);
  9872. else
  9873. wcscat(szNameNew, pchBeg);
  9874. sNewName = szNameNew;
  9875. }
  9876. return sNewName;
  9877. }
  9878. //END GetUnEscapedNameWithFwdSlash
  9879. /*********************************************************************
  9880. * *
  9881. * Written by: Paul Thompson *
  9882. * Date: 29 NOV 2000 *
  9883. * *
  9884. * This function is responsible for gets the CN name of an object*
  9885. * from an ADsPath and returns that CN name if it was retrieved or *
  9886. * NULL otherwise. *
  9887. * *
  9888. *********************************************************************/
  9889. //BEGIN GetCNFromPath
  9890. _bstr_t CAcctRepl::GetCNFromPath(_bstr_t sPath)
  9891. {
  9892. /* local variables */
  9893. BOOL bFound = FALSE;
  9894. WCHAR sName[LEN_Path];
  9895. WCHAR sTempPath[LEN_Path];
  9896. _bstr_t sCNName = L"";
  9897. WCHAR * sTempDN;
  9898. /* function body */
  9899. if ((sPath.length() > 0) && (sPath.length() < LEN_Path ))
  9900. {
  9901. wcscpy(sTempPath, (WCHAR*)sPath);
  9902. sTempDN = wcsstr(sTempPath, L"CN=");
  9903. if (sTempDN)
  9904. {
  9905. wcscpy(sName, sTempDN);
  9906. sTempDN = wcsstr(sName, L",OU=");
  9907. if (sTempDN)
  9908. {
  9909. bFound = TRUE;
  9910. *sTempDN = L'\0';
  9911. }
  9912. sTempDN = wcsstr(sName, L",CN=");
  9913. if (sTempDN)
  9914. {
  9915. bFound = TRUE;
  9916. *sTempDN = L'\0';
  9917. }
  9918. sTempDN = wcsstr(sName, L",DC=");
  9919. if (sTempDN)
  9920. {
  9921. bFound = TRUE;
  9922. *sTempDN = L'\0';
  9923. }
  9924. }
  9925. }
  9926. if (bFound)
  9927. sCNName = sName;
  9928. return sCNName;
  9929. }
  9930. //END GetCNFromPath
  9931. /*********************************************************************
  9932. * *
  9933. * Written by: Paul Thompson *
  9934. * Date: 26 FEB 2001 *
  9935. * *
  9936. * This function is responsible for replacing the source account *
  9937. * for a given list of accounts in any local groups they are a member*
  9938. * of on the target, if that account was migrated by ADMT. This *
  9939. * function is called during the undo process. *
  9940. * *
  9941. *********************************************************************/
  9942. //BEGIN ReplaceSourceInLocalGroup
  9943. BOOL CAcctRepl::ReplaceSourceInLocalGroup(TNodeListSortable *acctlist, //in- Accounts being processed
  9944. Options *pOptions, //in- Options specified by the user
  9945. IStatusObj *pStatus) // in -status object to support cancellation
  9946. {
  9947. /* local variables */
  9948. TAcctReplNode * pAcct;
  9949. IEnumVARIANT * pEnum;
  9950. INetObjEnumeratorPtr pQuery(__uuidof(NetObjEnumerator));
  9951. LPWSTR sCols[] = { L"memberOf" };
  9952. int nCols = DIM(sCols);
  9953. SAFEARRAY * psaCols;
  9954. SAFEARRAYBOUND bd = { nCols, 0 };
  9955. BSTR HUGEP * pData;
  9956. WCHAR sQuery[LEN_Path];
  9957. _variant_t HUGEP * pDt, * pVar;
  9958. _variant_t vx;
  9959. _variant_t varMain;
  9960. DWORD dwf = 0;
  9961. HRESULT hr = S_OK;
  9962. _bstr_t sDomPath = L"";
  9963. _bstr_t sDomain = L"";
  9964. /* function body */
  9965. FillNamingContext(pOptions);
  9966. //for each account, enumerate all local groups it is a member of and add the account's
  9967. //source account in that local group
  9968. for ( pAcct = (TAcctReplNode*)acctlist->Head(); pAcct; pAcct = (TAcctReplNode*)pAcct->Next())
  9969. {
  9970. // Do we need to abort ?
  9971. if ( pStatus )
  9972. {
  9973. LONG status = 0;
  9974. HRESULT hr = pStatus->get_Status(&status);
  9975. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  9976. {
  9977. if ( !bAbortMessageWritten )
  9978. {
  9979. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  9980. bAbortMessageWritten = true;
  9981. }
  9982. break;
  9983. }
  9984. }
  9985. //enumerate the groups this account is a member of
  9986. sDomain = GetDomainDNSFromPath(pAcct->GetTargetPath());
  9987. if (!_wcsicmp(pAcct->GetType(), s_ClassUser))
  9988. wsprintf(sQuery, L"(&(sAMAccountName=%s)(objectCategory=Person)(objectClass=user))", pAcct->GetTargetSam());
  9989. else if (!_wcsicmp(pAcct->GetType(), s_ClassInetOrgPerson))
  9990. wsprintf(sQuery, L"(&(sAMAccountName=%s)(objectCategory=Person)(objectClass=inetOrgPerson))", pAcct->GetTargetSam());
  9991. else
  9992. wsprintf(sQuery, L"(&(sAMAccountName=%s)(objectCategory=Group))", pAcct->GetTargetSam());
  9993. psaCols = SafeArrayCreate(VT_BSTR, 1, &bd);
  9994. SafeArrayAccessData(psaCols, (void HUGEP **)&pData);
  9995. for ( int i = 0; i < nCols; i++ )
  9996. pData[i] = SysAllocString(sCols[i]);
  9997. SafeArrayUnaccessData(psaCols);
  9998. hr = pQuery->raw_SetQuery(sDomPath, sDomain, sQuery, ADS_SCOPE_SUBTREE, FALSE);
  9999. if (FAILED(hr)) return FALSE;
  10000. hr = pQuery->raw_SetColumns(psaCols);
  10001. if (FAILED(hr)) return FALSE;
  10002. hr = pQuery->raw_Execute(&pEnum);
  10003. if (FAILED(hr)) return FALSE;
  10004. //while more groups
  10005. while (pEnum->Next(1, &varMain, &dwf) == S_OK)
  10006. {
  10007. SAFEARRAY * vals = V_ARRAY(&varMain);
  10008. // Get the VARIANT Array out
  10009. SafeArrayAccessData(vals, (void HUGEP**) &pDt);
  10010. vx = pDt[0];
  10011. SafeArrayUnaccessData(vals);
  10012. if ( vx.vt == VT_BSTR )
  10013. {
  10014. _bstr_t sPath;
  10015. BSTR sGrpName = NULL;
  10016. IADsGroup * pGrp = NULL;
  10017. _variant_t var;
  10018. _bstr_t sDN = vx.bstrVal;
  10019. if (wcslen((WCHAR*)sDN) == 0)
  10020. continue;
  10021. sDN = PadDN(sDN);
  10022. sPath = _bstr_t(L"LDAP://") + sDomain + _bstr_t(L"/") + sDN;
  10023. //connect to the target group
  10024. hr = ADsGetObject(sPath, IID_IADsGroup, (void**)&pGrp);
  10025. if (FAILED(hr))
  10026. continue;
  10027. //get this group's type and name
  10028. hr = pGrp->get_Name(&sGrpName);
  10029. hr = pGrp->Get(L"groupType", &var);
  10030. //if this is a local group, get this account source path and add it as a member
  10031. if ((SUCCEEDED(hr)) && (var.lVal & 4))
  10032. {
  10033. //add the account's source account to the local group, using the sid string format
  10034. WCHAR strSid[MAX_PATH] = L"";
  10035. WCHAR strRid[MAX_PATH] = L"";
  10036. DWORD lenStrSid = DIM(strSid);
  10037. GetTextualSid(pAcct->GetSourceSid(), strSid, &lenStrSid);
  10038. _bstr_t sSrcDmSid = strSid;
  10039. _ltow((long)(pAcct->GetSourceRid()), strRid, 10);
  10040. _bstr_t sSrcRid = strRid;
  10041. if ((!sSrcDmSid.length()) || (!sSrcRid.length()))
  10042. {
  10043. hr = E_INVALIDARG;
  10044. err.SysMsgWrite(ErrW, hr, DCT_MSG_FAILED_TO_READD_TO_GROUP_SSD, pAcct->GetSourcePath(), (WCHAR*)sGrpName, hr);
  10045. continue;
  10046. }
  10047. //build an LDAP path to the src object in the group
  10048. _bstr_t sSrcSid = sSrcDmSid + _bstr_t(L"-") + sSrcRid;
  10049. _bstr_t sSrcLDAPPath = L"LDAP://";
  10050. sSrcLDAPPath += _bstr_t(pOptions->tgtComp + 2);
  10051. sSrcLDAPPath += L"/CN=";
  10052. sSrcLDAPPath += sSrcSid;
  10053. sSrcLDAPPath += L",CN=ForeignSecurityPrincipals,";
  10054. sSrcLDAPPath += pOptions->tgtNamingContext;
  10055. //add the source account to the local group
  10056. hr = pGrp->Add(sSrcLDAPPath);
  10057. if (SUCCEEDED(hr))
  10058. err.MsgWrite(0,DCT_MSG_READD_MEMBER_TO_GROUP_SS, pAcct->GetSourcePath(), (WCHAR*)sGrpName);
  10059. else
  10060. err.SysMsgWrite(ErrW, hr, DCT_MSG_FAILED_TO_READD_TO_GROUP_SSD, pAcct->GetSourcePath(), (WCHAR*)sGrpName, hr);
  10061. }//end if local group
  10062. if (pGrp)
  10063. pGrp->Release();
  10064. }//end if bstr
  10065. else if ( vx.vt & VT_ARRAY )
  10066. {
  10067. // We must have got an Array of multivalued properties
  10068. // Access the BSTR elements of this variant array
  10069. SAFEARRAY * multiVals = vx.parray;
  10070. SafeArrayAccessData(multiVals, (void HUGEP **) &pVar);
  10071. //for each group
  10072. for ( DWORD dw = 0; dw < multiVals->rgsabound->cElements; dw++ )
  10073. {
  10074. // Do we need to abort ?
  10075. if ( pStatus )
  10076. {
  10077. LONG status = 0;
  10078. HRESULT hr = pStatus->get_Status(&status);
  10079. if ( SUCCEEDED(hr) && status == DCT_STATUS_ABORTING )
  10080. {
  10081. if ( !bAbortMessageWritten )
  10082. {
  10083. err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
  10084. bAbortMessageWritten = true;
  10085. }
  10086. break;
  10087. }
  10088. }
  10089. _bstr_t sPath;
  10090. BSTR sGrpName = NULL;
  10091. IADsGroup * pGrp = NULL;
  10092. _variant_t var;
  10093. _bstr_t sDN = _bstr_t(V_BSTR(&pVar[dw]));
  10094. sDN = PadDN(sDN);
  10095. sPath = _bstr_t(L"LDAP://") + sDomain + _bstr_t(L"/") + sDN;
  10096. //connect to the target group
  10097. hr = ADsGetObject(sPath, IID_IADsGroup, (void**)&pGrp);
  10098. if (FAILED(hr))
  10099. continue;
  10100. //get this group's type and name
  10101. hr = pGrp->get_Name(&sGrpName);
  10102. hr = pGrp->Get(L"groupType", &var);
  10103. //if this is a local group, get this account source path and add it as a member
  10104. if ((SUCCEEDED(hr)) && (var.lVal & 4))
  10105. {
  10106. //add the account's source account to the local group, using the sid string format
  10107. WCHAR strSid[MAX_PATH];
  10108. WCHAR strRid[MAX_PATH];
  10109. DWORD lenStrSid = DIM(strSid);
  10110. GetTextualSid(pAcct->GetSourceSid(), strSid, &lenStrSid);
  10111. _bstr_t sSrcDmSid = strSid;
  10112. _ltow((long)(pAcct->GetSourceRid()), strRid, 10);
  10113. _bstr_t sSrcRid = strRid;
  10114. if ((!sSrcDmSid.length()) || (!sSrcRid.length()))
  10115. {
  10116. hr = E_INVALIDARG;
  10117. err.SysMsgWrite(ErrW, hr, DCT_MSG_FAILED_TO_READD_TO_GROUP_SSD, pAcct->GetSourcePath(), (WCHAR*)sGrpName, hr);
  10118. continue;
  10119. }
  10120. //build an LDAP path to the src object in the group
  10121. _bstr_t sSrcSid = sSrcDmSid + _bstr_t(L"-") + sSrcRid;
  10122. _bstr_t sSrcLDAPPath = L"LDAP://";
  10123. sSrcLDAPPath += _bstr_t(pOptions->tgtComp + 2);
  10124. sSrcLDAPPath += L"/CN=";
  10125. sSrcLDAPPath += sSrcSid;
  10126. sSrcLDAPPath += L",CN=ForeignSecurityPrincipals,";
  10127. sSrcLDAPPath += pOptions->tgtNamingContext;
  10128. //add the source account to the local group
  10129. hr = pGrp->Add(sSrcLDAPPath);
  10130. if (SUCCEEDED(hr))
  10131. err.MsgWrite(0,DCT_MSG_READD_MEMBER_TO_GROUP_SS, pAcct->GetSourcePath(), (WCHAR*)sGrpName);
  10132. else
  10133. err.SysMsgWrite(ErrW, hr, DCT_MSG_FAILED_TO_READD_TO_GROUP_SSD, pAcct->GetSourcePath, (WCHAR*)sGrpName, hr);
  10134. }//end if local group
  10135. if (pGrp)
  10136. pGrp->Release();
  10137. }//end for each group
  10138. SafeArrayUnaccessData(multiVals);
  10139. }//end if array of groups
  10140. }//end while groups
  10141. pEnum->Release();
  10142. VariantInit(&vx);
  10143. VariantInit(&varMain);
  10144. SafeArrayDestroy(psaCols);
  10145. }//end for each account
  10146. return TRUE;
  10147. }
  10148. //END ReplaceSourceInLocalGroup
  10149. /*********************************************************************
  10150. * *
  10151. * Written by: Paul Thompson *
  10152. * Date: 6 MAR 2001 *
  10153. * *
  10154. * This function is responsible for retrieving the actual source *
  10155. * domain, from the Migrated Objects table, of a given path if that *
  10156. * path is one to a foreign security principal. We are also given a *
  10157. * pointer to the object reflected by the path parameter. *
  10158. * *
  10159. *********************************************************************/
  10160. //BEGIN GetDomainOfMigratedForeignSecPrincipal
  10161. _bstr_t CAcctRepl::GetDomainOfMigratedForeignSecPrincipal(IADs * pAds, _bstr_t sPath)
  10162. {
  10163. /* local variables */
  10164. IVarSetPtr pVs(__uuidof(VarSet));
  10165. IUnknown * pUnk = NULL;
  10166. HRESULT hr = S_OK;
  10167. _variant_t varName;
  10168. _bstr_t sDomainSid, sRid;
  10169. _bstr_t sDomain = L"";
  10170. BOOL bSplit = FALSE;
  10171. /* function body */
  10172. //if this account is outside the domain, lookup the account
  10173. //in the migrated objects table to retrieve it's actual source domain
  10174. if (wcsstr((WCHAR*)sPath, L"CN=ForeignSecurityPrincipals"))
  10175. {
  10176. //get the sid of this account
  10177. //use already valid pointer to the object
  10178. if (pAds)
  10179. {
  10180. hr = pAds->Get(L"name", &varName);
  10181. }
  10182. else //else connect to the object
  10183. {
  10184. IADs * pTempAds = NULL;
  10185. hr = ADsGetObject(sPath,IID_IADs,(void**)&pTempAds);
  10186. if (SUCCEEDED(hr))
  10187. {
  10188. hr = pTempAds->Get(L"name",&varName);
  10189. pTempAds->Release();
  10190. }
  10191. }
  10192. if (SUCCEEDED(hr))
  10193. {
  10194. WCHAR sName[MAX_PATH];
  10195. _bstr_t sTempName = varName;
  10196. if (!sTempName == false)
  10197. {
  10198. wcscpy(sName, sTempName);
  10199. //break the sid into domain sid and account rid
  10200. WCHAR * pTemp = wcsrchr(sName, L'-');
  10201. if (pTemp)
  10202. {
  10203. sRid = (pTemp + 1);
  10204. *pTemp = L'\0';
  10205. sDomainSid = sName;
  10206. bSplit = TRUE;
  10207. }
  10208. }
  10209. }
  10210. //if we got the rid and domain sid, look in MOT for account's
  10211. //real source domain
  10212. if (bSplit)
  10213. {
  10214. pVs->QueryInterface(IID_IUnknown, (void**)&pUnk);
  10215. try
  10216. {
  10217. IIManageDBPtr pDB(CLSID_IManageDB);
  10218. hr = pDB->raw_GetAMigratedObjectBySidAndRid(sDomainSid, sRid, &pUnk);
  10219. if (SUCCEEDED(hr))
  10220. sDomain = pVs->get(L"MigratedObjects.SourceDomain");
  10221. }
  10222. catch(_com_error& e)
  10223. {
  10224. hr = e.Error();
  10225. }
  10226. catch(...)
  10227. {
  10228. hr = E_FAIL;
  10229. }
  10230. if (pUnk)
  10231. pUnk->Release();
  10232. }
  10233. }
  10234. return sDomain;
  10235. }
  10236. //END GetDomainOfMigratedForeignSecPrincipal
  10237. /*********************************************************************
  10238. * *
  10239. * Written by: Paul Thompson *
  10240. * Date: 22 APR 2001 *
  10241. * *
  10242. * This function is responsible for removing the source account *
  10243. * object, represented by its VarSet entry from the Migrated Objects *
  10244. * Table, from the given group. This helper function is used by *
  10245. * "UpdateMemberToGroups" and "UpdateGroupMembership" after *
  10246. * successfully adding the cloned account to this same group. *
  10247. * *
  10248. *********************************************************************/
  10249. //BEGIN RemoveSourceAccountFromGroup
  10250. void CAcctRepl::RemoveSourceAccountFromGroup(IADsGroup * pGroup, IVarSetPtr pMOTVarSet, Options * pOptions)
  10251. {
  10252. /* local variables */
  10253. _bstr_t sSrcDmSid, sSrcRid, sSrcPath, sGrpName = L"";
  10254. HRESULT hr = S_OK;
  10255. /* function body */
  10256. //get the target group's name
  10257. BSTR bstr = NULL;
  10258. hr = pGroup->get_Name(&bstr);
  10259. if ( SUCCEEDED(hr) )
  10260. sGrpName = _bstr_t(bstr, false);
  10261. //get the source object's sid from the migrate objects table
  10262. sSrcDmSid = pMOTVarSet->get(L"MigratedObjects.SourceDomainSid");
  10263. sSrcRid = pMOTVarSet->get(L"MigratedObjects.SourceRid");
  10264. sSrcPath = pMOTVarSet->get(L"MigratedObjects.SourceAdsPath");
  10265. if ((wcslen((WCHAR*)sSrcDmSid) > 0) && (wcslen((WCHAR*)sSrcPath) > 0)
  10266. && (wcslen((WCHAR*)sSrcRid) > 0))
  10267. {
  10268. //build an LDAP path to the src object in the group
  10269. _bstr_t sSrcSid = sSrcDmSid + _bstr_t(L"-") + sSrcRid;
  10270. _bstr_t sSrcLDAPPath = L"LDAP://";
  10271. sSrcLDAPPath += _bstr_t(pOptions->tgtComp + 2);
  10272. sSrcLDAPPath += L"/CN=";
  10273. sSrcLDAPPath += sSrcSid;
  10274. sSrcLDAPPath += L",CN=ForeignSecurityPrincipals,";
  10275. sSrcLDAPPath += pOptions->tgtNamingContext;
  10276. VARIANT_BOOL bIsMem = VARIANT_FALSE;
  10277. //got the source LDAP path, now see if that account is in the group
  10278. pGroup->IsMember(sSrcLDAPPath, &bIsMem);
  10279. if (bIsMem)
  10280. {
  10281. hr = pGroup->Remove(sSrcLDAPPath);//remove the src account
  10282. if ( SUCCEEDED(hr) )
  10283. err.MsgWrite(0,DCT_MSG_REMOVE_FROM_GROUP_SS, (WCHAR*)sSrcPath, (WCHAR*)sGrpName);
  10284. }
  10285. }
  10286. }
  10287. //END RemoveSourceAccountFromGroup
  10288. // GetDomainDnFromPath
  10289. //
  10290. // Retrieves domain distinguished name from ADsPath
  10291. _bstr_t __stdcall GetDomainDnFromPath(_bstr_t strADsPath)
  10292. {
  10293. CADsPathName pnPathname(strADsPath);
  10294. for (long lCount = pnPathname.GetNumElements(); lCount > 0; lCount--)
  10295. {
  10296. _bstr_t str = pnPathname.GetElement(0);
  10297. if (!str || (_tcsnicmp(str, _T("DC="), 3) == 0))
  10298. {
  10299. break;
  10300. }
  10301. pnPathname.RemoveLeafElement();
  10302. }
  10303. return pnPathname.Retrieve(ADS_FORMAT_X500_DN);
  10304. }
  10305. //----------------------------------------------------------------------------
  10306. // VerifyAndUpdateMigratedTarget Method
  10307. //
  10308. // Verifies target path and if changed then retrieves new path and updates
  10309. // database.
  10310. //----------------------------------------------------------------------------
  10311. void CAcctRepl::VerifyAndUpdateMigratedTarget(Options* pOptions, IVarSetPtr spAccountVarSet)
  10312. {
  10313. WCHAR szADsPath[LEN_Path];
  10314. // retrieve migrated objects ADsPath and update server to current domain controller
  10315. _bstr_t strGuid = spAccountVarSet->get(L"MigratedObjects.GUID");
  10316. _bstr_t strOldPath = spAccountVarSet->get(L"MigratedObjects.TargetAdsPath");
  10317. // attempt to connect to object
  10318. IADsPtr spTargetObject;
  10319. StuffComputerNameinLdapPath(szADsPath, LEN_Path, strOldPath, pOptions, TRUE);
  10320. HRESULT hr = ADsGetObject(szADsPath, __uuidof(IADs), (VOID**)&spTargetObject);
  10321. //
  10322. // If able to bind to an object with the old distinguished name verify
  10323. // that the GUID is equal to the previously migrated object.
  10324. //
  10325. bool bGuidEqual = false;
  10326. if (SUCCEEDED(hr))
  10327. {
  10328. BSTR bstr = NULL;
  10329. hr = spTargetObject->get_GUID(&bstr);
  10330. if (SUCCEEDED(hr))
  10331. {
  10332. _bstr_t strGuidB = _bstr_t(bstr, false);
  10333. PCTSTR pszGuidA = strGuid;
  10334. PCTSTR pszGuidB = strGuidB;
  10335. if (pszGuidA && pszGuidB)
  10336. {
  10337. if (_tcsicmp(pszGuidA, pszGuidB) == 0)
  10338. {
  10339. bGuidEqual = true;
  10340. }
  10341. }
  10342. }
  10343. else
  10344. {
  10345. _com_issue_error(hr);
  10346. }
  10347. }
  10348. //
  10349. // If bind failed because object no longer exists at given path or the GUID of the object does not
  10350. // equal the previously migrated object then attempt to bind to object using GUID to retrieve updated
  10351. // distinguished name and SAM account name.
  10352. //
  10353. if ((hr == HRESULT_FROM_WIN32(ERROR_DS_NO_SUCH_OBJECT)) || (SUCCEEDED(hr) && (bGuidEqual == false)))
  10354. {
  10355. // retrieve object based on GUID
  10356. _bstr_t strGuidPath = _bstr_t(L"LDAP://") + _bstr_t(pOptions->tgtDomainDns) + _bstr_t(L"/<GUID=") + strGuid + _bstr_t(L">");
  10357. hr = ADsGetObject(strGuidPath, __uuidof(IADs), (VOID**)&spTargetObject);
  10358. // if object found then...
  10359. if (SUCCEEDED(hr))
  10360. {
  10361. VARIANT var;
  10362. hr = spTargetObject->Get(_bstr_t(L"distinguishedName"), &var);
  10363. if (SUCCEEDED(hr))
  10364. {
  10365. CADsPathName pathname;
  10366. pathname.Set(L"LDAP", ADS_SETTYPE_PROVIDER);
  10367. pathname.Set(pOptions->tgtDomainDns, ADS_SETTYPE_SERVER);
  10368. pathname.Set(_bstr_t(_variant_t(var)), ADS_SETTYPE_DN);
  10369. _bstr_t strNewPath = pathname.Retrieve(ADS_FORMAT_X500);
  10370. // retrieve domain distinguished names
  10371. _bstr_t strOldDomainDn = GetDomainDnFromPath(strOldPath);
  10372. _bstr_t strNewDomainDn = GetDomainDnFromPath(strNewPath);
  10373. // if domains are equal than update path
  10374. if (strOldDomainDn.length() && strNewDomainDn.length() && (_tcsicmp(strOldDomainDn, strNewDomainDn) == 0))
  10375. {
  10376. // replace server with current target domain controller
  10377. StuffComputerNameinLdapPath(szADsPath, LEN_Path, strNewPath, pOptions, TRUE);
  10378. // update ADsPath
  10379. spAccountVarSet->put(L"MigratedObjects.TargetAdsPath", _bstr_t(szADsPath));
  10380. // update SAMAccountName
  10381. hr = spTargetObject->Get(_bstr_t(L"sAMAccountName"), &var);
  10382. if (SUCCEEDED(hr))
  10383. {
  10384. spAccountVarSet->put(L"MigratedObjects.TargetSamName", _bstr_t(_variant_t(var)));
  10385. }
  10386. // update database
  10387. pOptions->pDb->UpdateMigratedTargetObject(IUnknownPtr(spAccountVarSet));
  10388. }
  10389. }
  10390. }
  10391. }
  10392. }
  10393. //-----------------------------------------------------------------------------
  10394. // GenerateSourceToTargetDnMap Method
  10395. //
  10396. // Synopsis
  10397. // Generates a mapping of source object distinguished names to target object
  10398. // distinguished names. This is used during copying of distinguished name type
  10399. // attributes to translate the distinguished name for the source object to the
  10400. // distinguished name of the target object.
  10401. //
  10402. // Parameters
  10403. // IN acctlist - list of account node objects
  10404. //
  10405. // Return Value
  10406. // A VarSet data object whose keys are the source distinguished names and whose
  10407. // values are the target distinguished names.
  10408. //-----------------------------------------------------------------------------
  10409. IVarSetPtr CAcctRepl::GenerateSourceToTargetDnMap(TNodeListSortable* acctlist)
  10410. {
  10411. IVarSetPtr spVarSet(__uuidof(VarSet));
  10412. if (opt.srcDomainVer > 4)
  10413. {
  10414. TNodeTreeEnum nteEnum;
  10415. CADsPathName pnSource;
  10416. CADsPathName pnTarget;
  10417. //
  10418. // For each object being migrated...
  10419. //
  10420. for (TAcctReplNode* parnNode = (TAcctReplNode *)nteEnum.OpenFirst(acctlist); parnNode; parnNode = (TAcctReplNode *)nteEnum.Next())
  10421. {
  10422. //
  10423. // If either the object has been created or will be replaced then add object to map.
  10424. //
  10425. if ((opt.flags & F_REPLACE) || parnNode->WasCreated())
  10426. {
  10427. pnSource.Set(parnNode->GetSourcePath(), ADS_SETTYPE_FULL);
  10428. pnTarget.Set(parnNode->GetTargetPath(), ADS_SETTYPE_FULL);
  10429. spVarSet->put(pnSource.Retrieve(ADS_FORMAT_X500_DN), pnTarget.Retrieve(ADS_FORMAT_X500_DN));
  10430. }
  10431. }
  10432. }
  10433. return spVarSet;
  10434. }