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.

2132 lines
85 KiB

  1. /*---------------------------------------------------------------------------
  2. File: ObjPropBuilder.cpp
  3. Comments: Implementation of CObjPropBuilder COM object. This COM object
  4. is used to access/set properties for Win2K active directory
  5. objects. This COM object supports following operations
  6. 1. GetClassPropeEnum : This method allows users to get all the
  7. the properties for a class in a domain.
  8. 2. GetObjectProperty : This method gathers values for properties
  9. on a given AD object.
  10. 3. MapProperties : Constructs a set of properties that are common
  11. to two classes in the AD.
  12. 4. SetPropFromVarset : Sets properties for a AD object from a varset.
  13. 5. CopyProperties : Copies common properties from source AD object
  14. to target AD object.
  15. (c) Copyright 1999, Mission Critical Software, Inc., All Rights Reserved
  16. Proprietary and confidential to Mission Critical Software, Inc.
  17. REVISION LOG ENTRY
  18. Revision By: Sham Chauthani
  19. Revised on 07/02/99 12:40:00
  20. ---------------------------------------------------------------------------
  21. */
  22. #include "stdafx.h"
  23. #include "EaLen.hpp"
  24. #include "ResStr.h"
  25. #include "ADsProp.h"
  26. #include "ObjProp.h"
  27. #include "iads.h"
  28. #include <lm.h>
  29. #include "ErrDct.hpp"
  30. #include "TReg.hpp"
  31. #include "StrHelp.h"
  32. #include "pwgen.hpp"
  33. #include "AdsiHelpers.h"
  34. #include "GetDcName.h"
  35. #include "TxtSid.h"
  36. #include <Sddl.h>
  37. #include <set>
  38. StringLoader gString;
  39. //#import "\bin\NetEnum.tlb" no_namespace
  40. //#import "\bin\DBManager.tlb" no_namespace
  41. #import "NetEnum.tlb" no_namespace
  42. #import "DBMgr.tlb" no_namespace
  43. #ifndef ADS_SYSTEMFLAG_SCHEMA_BASE_OBJECT
  44. #define ADS_SYSTEMFLAG_SCHEMA_BASE_OBJECT 0x10
  45. #endif
  46. #ifndef ADS_SYSTEMFLAG_ATTR_IS_OPERATIONAL
  47. #define ADS_SYSTEMFLAG_ATTR_IS_OPERATIONAL 0x8
  48. #endif
  49. #ifndef ADS_SYSTEMFLAG_ATTR_REQ_PARTIAL_SET_MEMBER
  50. #define ADS_SYSTEMFLAG_ATTR_REQ_PARTIAL_SET_MEMBER 0x2
  51. #endif
  52. #ifndef IADsPtr
  53. _COM_SMARTPTR_TYPEDEF(IADs, IID_IADs);
  54. #endif
  55. TErrorDct err;
  56. TError & errCommon = err;
  57. /////////////////////////////////////////////////////////////////////////////
  58. // CObjPropBuilder
  59. BOOL CObjPropBuilder::GetProgramDirectory(
  60. WCHAR * filename // out - buffer that will contain path to program directory
  61. )
  62. {
  63. DWORD rc = 0;
  64. BOOL bFound = FALSE;
  65. TRegKey key;
  66. rc = key.OpenRead(GET_STRING(IDS_HKLM_DomainAdmin_Key),HKEY_LOCAL_MACHINE);
  67. if ( ! rc )
  68. {
  69. rc = key.ValueGetStr(L"Directory",filename,MAX_PATH);
  70. if ( ! rc )
  71. {
  72. if ( *filename )
  73. bFound = TRUE;
  74. }
  75. }
  76. if ( ! bFound )
  77. {
  78. UStrCpy(filename,L"C:\\"); // if all else fails, default to the C: drive
  79. }
  80. return bFound;
  81. }
  82. //---------------------------------------------------------------------------
  83. // GetClassPropEnum: This function fills the varset with all the properties
  84. // for a given class in a given domain. The Varset has
  85. // values stored by the OID and then by their name with
  86. // MandatoryProperties/OptionalProperties as parent nodes
  87. // as applicable.
  88. //---------------------------------------------------------------------------
  89. STDMETHODIMP CObjPropBuilder::GetClassPropEnum(
  90. BSTR sClassName, //in -Class name to get the properties for
  91. BSTR sDomainName, //in -Domain name
  92. long lVer, //in -The domain version
  93. IUnknown **ppVarset //out-Varset filled with the properties
  94. )
  95. {
  96. // This function goes through the list of properties for the specified class in specified domain
  97. // Builds the given varset with the properties and their values.
  98. WCHAR sAdsPath[LEN_Path];
  99. DWORD dwArraySizeOfsAdsPath = sizeof(sAdsPath)/sizeof(sAdsPath[0]);
  100. HRESULT hr = E_INVALIDARG;
  101. _variant_t dnsName;
  102. if (sDomainName == NULL || sClassName == NULL)
  103. return hr;
  104. if ( lVer > 4 )
  105. {
  106. // For this Domain get the default naming context
  107. wsprintfW(sAdsPath, L"LDAP://%s/rootDSE", sDomainName);
  108. IADs * pAds = NULL;
  109. hr = ADsGetObject(sAdsPath, IID_IADs, (void**)&pAds);
  110. if ( SUCCEEDED(hr) )
  111. {
  112. hr = pAds->Get(L"defaultNamingContext", &dnsName);
  113. }
  114. if ( SUCCEEDED(hr) )
  115. {
  116. wcscpy(m_sNamingConvention, dnsName.bstrVal);
  117. // Build LDAP path to the schema
  118. if (wcslen(L"LDAP://") + wcslen(sDomainName)
  119. + wcslen(L"/") + wcslen(sClassName)
  120. + wcslen(L", schema") >= dwArraySizeOfsAdsPath)
  121. hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  122. else
  123. {
  124. wcscpy(sAdsPath, L"LDAP://");
  125. wcscat(sAdsPath, sDomainName);
  126. wcscat(sAdsPath, L"/");
  127. wcscat(sAdsPath, sClassName);
  128. wcscat(sAdsPath, L", schema");
  129. hr = S_OK;
  130. }
  131. }
  132. if ( pAds )
  133. pAds->Release();
  134. }
  135. else
  136. {
  137. wsprintf(sAdsPath, L"WinNT://%s/Schema/%s", sDomainName, sClassName);
  138. hr = S_OK;
  139. }
  140. if ( SUCCEEDED(hr) )
  141. {
  142. wcscpy(m_sDomainName, sDomainName);
  143. m_lVer = lVer;
  144. // Get the class object.
  145. IADsClass * pIClass=NULL;
  146. hr = ADsGetObject(sAdsPath, IID_IADsClass, (void **)&pIClass);
  147. // Without the object we can not go any further so we will stop here.
  148. if ( SUCCEEDED(hr) )
  149. {
  150. // Let the Auxilliary function take care of Getting properties and filling up the Varset.
  151. hr = GetClassProperties( pIClass, *ppVarset );
  152. pIClass->Release();
  153. }
  154. }
  155. return hr;
  156. }
  157. //---------------------------------------------------------------------------
  158. // GetClassProperties: This function fills the varset with properties of the class.
  159. //---------------------------------------------------------------------------
  160. HRESULT CObjPropBuilder::GetClassProperties(
  161. IADsClass * pClass, //in -IADsClass * to the class
  162. IUnknown *& pVarSet //out-Varset to fill the properties
  163. )
  164. {
  165. HRESULT hr;
  166. _variant_t variant;
  167. // mandatory properties
  168. hr = pClass->get_MandatoryProperties(&variant);
  169. if ( SUCCEEDED(hr) )
  170. {
  171. hr = FillupVarsetFromVariant(pClass, &variant, L"MandatoryProperties", pVarSet);
  172. }
  173. variant.Clear();
  174. // optional properties
  175. hr = pClass->get_OptionalProperties(&variant);
  176. if ( SUCCEEDED(hr) )
  177. {
  178. hr = FillupVarsetFromVariant(pClass, &variant, L"OptionalProperties", pVarSet);
  179. }
  180. variant.Clear();
  181. return hr;
  182. }
  183. //---------------------------------------------------------------------------
  184. // FillupVarsetFromVariant: This function fills in the Varset property info
  185. // with the info in a variant.
  186. //---------------------------------------------------------------------------
  187. HRESULT CObjPropBuilder::FillupVarsetFromVariant(
  188. IADsClass * pClass, //in -IADsClass * to the class
  189. VARIANT * pVar, //in -Variant to lookup info.
  190. BSTR sPropType, //in -Type of the property
  191. IUnknown *& pVarSet //out-Varset with the info,
  192. )
  193. {
  194. HRESULT hr;
  195. BSTR sPropName;
  196. USHORT type;
  197. type = pVar->vt;
  198. if ( type & VT_ARRAY )
  199. {
  200. if ( type == (VT_ARRAY|VT_VARIANT) )
  201. {
  202. hr = FillupVarsetFromVariantArray(pClass, pVar->parray, sPropType, pVarSet);
  203. if ( FAILED ( hr ) )
  204. return hr;
  205. }
  206. else
  207. return S_FALSE;
  208. }
  209. else
  210. {
  211. if ( type == VT_BSTR )
  212. {
  213. // Only other thing that the VARIANT could be is a BSTR.
  214. sPropName = pVar->bstrVal;
  215. hr = FillupVarsetWithProperty(sPropName, sPropType, pVarSet);
  216. if ( FAILED ( hr ) )
  217. return hr;
  218. }
  219. else
  220. return S_FALSE;
  221. }
  222. return S_OK;
  223. }
  224. //---------------------------------------------------------------------------
  225. // FillupVarsetWithProperty: Given the class prop name and the prop type this
  226. // function fills info into the varset.
  227. //---------------------------------------------------------------------------
  228. HRESULT CObjPropBuilder::FillupVarsetWithProperty(
  229. BSTR sPropName, //in -Property name
  230. BSTR sPropType, //in -Property type
  231. IUnknown *& pVarSet //out-Varset to fill in the information
  232. )
  233. {
  234. if ( wcslen(sPropName) == 0 )
  235. return S_OK;
  236. // This function fills up the Varset for a given property
  237. HRESULT hr;
  238. _variant_t var;
  239. _variant_t varSO;
  240. _variant_t varID;
  241. IVarSetPtr pVar;
  242. WCHAR sAdsPath[LEN_Path];
  243. DWORD dwArraySizeOfsAdsPath = sizeof(sAdsPath)/sizeof(sAdsPath[0]);
  244. IADsProperty * pProp = NULL;
  245. BSTR objID = NULL;
  246. BSTR sPath = NULL;
  247. BSTR sClass = NULL;
  248. WCHAR sPropPut[LEN_Path];
  249. if (sPropName == NULL || sPropType == NULL)
  250. return E_INVALIDARG;
  251. // Get the OID for the property
  252. // First we need a IADsProperty pointer to the property schema
  253. if ( m_lVer > 4 )
  254. {
  255. if (wcslen(L"LDAP://") + wcslen(m_sDomainName)
  256. + wcslen(L"/") + wcslen(sPropName) + wcslen(L", schema") >= dwArraySizeOfsAdsPath)
  257. return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  258. wcscpy(sAdsPath, L"LDAP://");
  259. wcscat(sAdsPath, m_sDomainName);
  260. wcscat(sAdsPath, L"/");
  261. wcscat(sAdsPath, sPropName);
  262. wcscat(sAdsPath, L", schema");
  263. }
  264. else
  265. {
  266. wsprintf(sAdsPath, L"WinNT://%s/Schema/%s", m_sDomainName, sPropName);
  267. }
  268. hr = ADsGetObject(sAdsPath, IID_IADsProperty, (void **)&pProp);
  269. if (SUCCEEDED(hr))
  270. {
  271. // Get the objectID for the property
  272. hr = pProp->get_OID(&objID);
  273. if (SUCCEEDED(hr))
  274. {
  275. hr = pProp->get_ADsPath(&sPath);
  276. if (SUCCEEDED(hr))
  277. {
  278. hr = pProp->get_Class(&sClass);
  279. if (SUCCEEDED(hr))
  280. {
  281. // Get the varset from the parameter
  282. pVar = pVarSet;
  283. // Set up the variant to put into the varset
  284. var = objID;
  285. // Put the value into the varset
  286. wcscpy(sPropPut, sPropType);
  287. wcscat(sPropPut, L".");
  288. wcscat(sPropPut, sPropName);
  289. hr = pVar->put(sPropPut, var);
  290. if (SUCCEEDED(hr))
  291. {
  292. // Set up the variant to put into the varset
  293. var = sPropName;
  294. // Put the value with the ObjectID as the key.
  295. hr = pVar->put(objID, var);
  296. }
  297. }
  298. }
  299. }
  300. }
  301. SysFreeString(objID);
  302. SysFreeString(sPath);
  303. SysFreeString(sClass);
  304. if (pProp)
  305. {
  306. pProp->Release();
  307. }
  308. return hr;
  309. }
  310. //---------------------------------------------------------------------------
  311. // FillupVarsetFromVariantArray: Given the class, SafeArray of props and the
  312. // prop type this function fills info into the
  313. // varset.
  314. //---------------------------------------------------------------------------
  315. HRESULT CObjPropBuilder::FillupVarsetFromVariantArray(
  316. IADsClass * pClass, //in -IADsClass* to the class in question
  317. SAFEARRAY * pArray, //in -SafeArray pointer with the prop names
  318. BSTR sPropType, //in -Property type
  319. IUnknown *& pVarSet //out-Varset with the information filled in
  320. )
  321. {
  322. HRESULT hr = S_FALSE;
  323. DWORD nDim; // number of dimensions, must be one
  324. LONG nLBound; // lower bound of array
  325. LONG nUBound; // upper bound of array
  326. LONG indices[1]; // array indices to access elements
  327. DWORD rc; // SafeArray return code
  328. VARIANT variant; // one element in array
  329. nDim = SafeArrayGetDim(pArray);
  330. VariantInit(&variant);
  331. if ( nDim == 1 )
  332. {
  333. SafeArrayGetLBound(pArray, 1, &nLBound);
  334. SafeArrayGetUBound(pArray, 1, &nUBound);
  335. for ( indices[0] = nLBound, rc = 0;
  336. indices[0] <= nUBound && !rc;
  337. indices[0] += 1 )
  338. {
  339. rc = SafeArrayGetElement(pArray,indices,&variant);
  340. if ( !rc )
  341. hr = FillupVarsetFromVariant(pClass, &variant, sPropType, pVarSet);
  342. VariantClear(&variant);
  343. }
  344. }
  345. return hr;
  346. }
  347. //---------------------------------------------------------------------------
  348. // GetProperties: This function gets the values for the properties specified
  349. // in the varset in the ADS_ATTR_INFO array for the object
  350. // specified in a given domain.
  351. //---------------------------------------------------------------------------
  352. DWORD CObjPropBuilder::GetProperties(
  353. BSTR sObjPath, //in -Path to the object for which we are getting the props
  354. // BSTR sDomainName, //in -Domain name where the object resides
  355. IVarSet * pVar, //in -Varset listing all the property names that we need to get.
  356. ADS_ATTR_INFO*& pAttrInfo //out-Attribute values for the property
  357. )
  358. {
  359. // Construct the LDAP path.
  360. WCHAR sPath[LEN_Path];
  361. VARIANT var;
  362. // Get the path to the source object
  363. safecopy(sPath, sObjPath);
  364. // Get the Varset pointer and enumerate the properties asked for and build an array to send to IADsDirectory
  365. long lRet=0;
  366. SAFEARRAY * keys = NULL;
  367. SAFEARRAY * vals = NULL;
  368. IDirectoryObject * pDir;
  369. DWORD dwRet = 0;
  370. LPWSTR * pAttrNames = new LPWSTR[pVar->GetCount()];
  371. HRESULT hr = pVar->raw_getItems(NULL, NULL, 1, 10000, &keys, &vals, &lRet);
  372. VariantInit(&var);
  373. if (!pAttrNames)
  374. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  375. if ( SUCCEEDED( hr ) )
  376. {
  377. // Build the Attribute array from the varset.
  378. for ( long x = 0; x < lRet; x++ )
  379. {
  380. ::SafeArrayGetElement(keys, &x, &var);
  381. int len = wcslen(var.bstrVal);
  382. pAttrNames[x] = new WCHAR[len + 2];
  383. if (!(pAttrNames[x]))
  384. {
  385. for (int j=0; j<x; j++)
  386. delete [] pAttrNames[j];
  387. delete [] pAttrNames;
  388. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  389. }
  390. wcscpy(pAttrNames[x], var.bstrVal);
  391. VariantClear(&var);
  392. }
  393. // Now get the IDirectoryObject Ptr for the given object.
  394. hr = ADsGetObject(sPath, IID_IDirectoryObject, (void **)&pDir);
  395. if ( FAILED( hr ) )
  396. {
  397. dwRet = 0;
  398. }
  399. else
  400. {
  401. // Get the Property values for the object.
  402. hr = pDir->GetObjectAttributes(pAttrNames, lRet, &pAttrInfo, &dwRet);
  403. pDir->Release();
  404. }
  405. for ( long y = 0 ; y < lRet; y++ )
  406. {
  407. delete [] pAttrNames[y];
  408. }
  409. SafeArrayDestroy(keys);
  410. SafeArrayDestroy(vals);
  411. }
  412. delete [] pAttrNames;
  413. return dwRet;
  414. }
  415. //---------------------------------------------------------------------------
  416. // GetObjectProperty: This function takes in a varset with property names.
  417. // Then it fills up the varset with values by getting them
  418. // from the Object.
  419. //---------------------------------------------------------------------------
  420. STDMETHODIMP CObjPropBuilder::GetObjectProperty(
  421. BSTR sobjSubPath, //in- LDAP Sub path to the object
  422. // BSTR sDomainName, //in- Domain name where the object resides
  423. IUnknown **ppVarset //out-Varset filled with the information
  424. )
  425. {
  426. IVarSetPtr pVar;
  427. ADS_ATTR_INFO * pAttrInfo=NULL;
  428. pVar = *ppVarset;
  429. // Get the properties from the directory
  430. DWORD dwRet = GetProperties(sobjSubPath, /*sDomainName,*/ pVar, pAttrInfo);
  431. // The GetProperties return value is overloaded: either a failure HRESULT
  432. // (e.g., a negetive value), or a count if successful
  433. if ( FAILED(dwRet) )
  434. return dwRet;
  435. _ASSERT(dwRet >= 0);
  436. // Go through the property values and put them into the varset.
  437. for ( DWORD dwIdx = 0; dwIdx < dwRet; dwIdx++ )
  438. {
  439. SetValuesInVarset(pAttrInfo[dwIdx], pVar);
  440. }
  441. if ( pAttrInfo )
  442. FreeADsMem( pAttrInfo );
  443. return S_OK;
  444. }
  445. //---------------------------------------------------------------------------
  446. // SetValuesInVarset: This function sets the values for the properties into
  447. // a varset.
  448. //---------------------------------------------------------------------------
  449. void CObjPropBuilder::SetValuesInVarset(
  450. ADS_ATTR_INFO attrInfo, //in -The property value in ADS_ATTR_INFO struct.
  451. IVarSetPtr pVar //in,out -The VarSet where we need to put the values
  452. )
  453. {
  454. // This function extraces values from ADS_ATTR_INFO struct and puts it into the Varset.
  455. LPWSTR sKeyName = attrInfo.pszAttrName;
  456. _variant_t var;
  457. // Got through each value ( in case of multivalued entries ) and depending on the type put it into the varset
  458. // the way we put in single value entries is to put the propertyName as key and put its value as value. Although
  459. // in case of a multivalued entry we put PropertyName.### and each of the values in it.
  460. for ( DWORD dw = 0; dw < attrInfo.dwNumValues; dw++)
  461. {
  462. var = L"";
  463. if ( attrInfo.dwNumValues > 1 )
  464. // Multivalued property name
  465. wsprintfW(sKeyName, L"%s.%d", attrInfo.pszAttrName, dw);
  466. else
  467. // Single value keyname.
  468. wcscpy(sKeyName,attrInfo.pszAttrName);
  469. // Fill in the values as per the varset.
  470. switch (attrInfo.dwADsType)
  471. {
  472. case ADSTYPE_DN_STRING : var.vt = VT_BSTR;
  473. var.bstrVal = ::SysAllocString(attrInfo.pADsValues[dw].DNString);
  474. break;
  475. case ADSTYPE_CASE_EXACT_STRING : var.vt = VT_BSTR;
  476. var.bstrVal = attrInfo.pADsValues[dw].CaseExactString;
  477. break;
  478. case ADSTYPE_CASE_IGNORE_STRING : var.vt = VT_BSTR;
  479. var.bstrVal = ::SysAllocString(attrInfo.pADsValues[dw].CaseIgnoreString);
  480. break;
  481. case ADSTYPE_PRINTABLE_STRING : var.vt = VT_BSTR;
  482. var.bstrVal = ::SysAllocString(attrInfo.pADsValues[dw].PrintableString);
  483. break;
  484. case ADSTYPE_NUMERIC_STRING : var.vt = VT_BSTR;
  485. var.bstrVal = ::SysAllocString(attrInfo.pADsValues[dw].NumericString);
  486. break;
  487. case ADSTYPE_INTEGER : var.vt = VT_I4;
  488. var.lVal = attrInfo.pADsValues[dw].Integer;
  489. break;
  490. case ADSTYPE_OCTET_STRING : {
  491. var.vt = VT_ARRAY | VT_UI1;
  492. long * pData;
  493. DWORD dwLength = attrInfo.pADsValues[dw].OctetString.dwLength;
  494. SAFEARRAY * sA;
  495. SAFEARRAYBOUND rgBound = {dwLength, 0};
  496. sA = ::SafeArrayCreate(VT_UI1, 1, &rgBound);
  497. ::SafeArrayAccessData( sA, (void**)&pData);
  498. for ( DWORD i = 0; i < dwLength; i++ )
  499. pData[i] = attrInfo.pADsValues[dw].OctetString.lpValue[i];
  500. ::SafeArrayUnaccessData(sA);
  501. var.parray = sA;
  502. }
  503. break;
  504. /* case ADSTYPE_UTC_TIME : wcscat(sKeyName,L".ERROR");
  505. var.vt = VT_BSTR;
  506. var.bstrVal = ::SysAllocString(L"Date not supported.");
  507. break;
  508. case ADSTYPE_LARGE_INTEGER : wcscat(sKeyName,L".ERROR");
  509. var.vt = VT_BSTR;
  510. var.bstrVal = ::SysAllocString(L"Large Integer not supported.");
  511. break;
  512. case ADSTYPE_PROV_SPECIFIC : wcscat(sKeyName,L".ERROR");
  513. var.vt = VT_BSTR;
  514. var.bstrVal = ::SysAllocString(L"Provider specific strings not supported.");
  515. break;
  516. case ADSTYPE_OBJECT_CLASS : var.vt = VT_BSTR;
  517. var.bstrVal = ::SysAllocString(attrInfo.pADsValues[dw].ClassName);
  518. break;
  519. case ADSTYPE_CASEIGNORE_LIST : wcscat(sKeyName,L".ERROR");
  520. var.vt = VT_BSTR;
  521. var.bstrVal = L"Case ignore lists are not supported.";
  522. break;
  523. case ADSTYPE_OCTET_LIST : wcscat(sKeyName,L".ERROR");
  524. var.vt = VT_BSTR;
  525. var.bstrVal = L"Octet lists are not supported.";
  526. break;
  527. case ADSTYPE_PATH : wcscat(sKeyName,L".ERROR");
  528. var.vt = VT_BSTR;
  529. var.bstrVal = L"Path type not supported.";
  530. break;
  531. case ADSTYPE_POSTALADDRESS : wcscat(sKeyName,L".ERROR");
  532. var.vt = VT_BSTR;
  533. var.bstrVal = L"Postal addresses are not supported.";
  534. break;
  535. case ADSTYPE_TIMESTAMP : var.vt = VT_UI4;
  536. var.lVal = attrInfo.pADsValues[dw].UTCTime;
  537. break;
  538. case ADSTYPE_BACKLINK : wcscat(sKeyName,L".ERROR");
  539. var.vt = VT_BSTR;
  540. var.bstrVal = L"Backlink is not supported.";
  541. break;
  542. case ADSTYPE_TYPEDNAME : wcscat(sKeyName,L".ERROR");
  543. var.vt = VT_BSTR;
  544. var.bstrVal = L"Typed name not supported.";
  545. break;
  546. case ADSTYPE_HOLD : wcscat(sKeyName,L".ERROR");
  547. var.vt = VT_BSTR;
  548. var.bstrVal = L"Hold not supported.";
  549. break;
  550. case ADSTYPE_NETADDRESS : wcscat(sKeyName,L".ERROR");
  551. var.vt = VT_BSTR;
  552. var.bstrVal = L"NetAddress not supported.";
  553. break;
  554. case ADSTYPE_REPLICAPOINTER : wcscat(sKeyName,L".ERROR");
  555. var.vt = VT_BSTR;
  556. var.bstrVal = L"Replica pointer not supported.";
  557. break;
  558. case ADSTYPE_FAXNUMBER : wcscat(sKeyName,L".ERROR");
  559. var.vt = VT_BSTR;
  560. var.bstrVal = L"Faxnumber not supported.";
  561. break;
  562. case ADSTYPE_EMAIL : wcscat(sKeyName,L".ERROR");
  563. var.vt = VT_BSTR;
  564. var.bstrVal = L"Email not supported.";
  565. break;
  566. case ADSTYPE_NT_SECURITY_DESCRIPTOR : wcscat(sKeyName,L".ERROR");
  567. var.vt = VT_BSTR;
  568. var.bstrVal = L"Security Descriptor not supported.";
  569. break;
  570. */
  571. default : wcscat(sKeyName,GET_STRING(DCTVS_SUB_ERROR));
  572. var.vt = VT_BSTR;
  573. var.bstrVal = GET_BSTR(IDS_UNKNOWN_TYPE);
  574. break;
  575. }
  576. pVar->put(sKeyName, var);
  577. if ( attrInfo.dwADsType == ADSTYPE_OCTET_STRING)
  578. var.vt = VT_EMPTY;
  579. }
  580. }
  581. //---------------------------------------------------------------------------
  582. // CopyProperties: This function copies properties, specified in the varset,
  583. // by getting the values
  584. // from the source account and the setting the values in
  585. // the target account.
  586. //---------------------------------------------------------------------------
  587. STDMETHODIMP CObjPropBuilder::CopyProperties(
  588. BSTR sSourcePath, //in -Source path to the object
  589. BSTR sSourceDomain, //in -Source domain name
  590. BSTR sTargetPath, //in -Target object LDAP path
  591. BSTR sTargetDomain, //in -Target domain name
  592. IUnknown *pPropSet, //in -Varset listing all the props to copy
  593. IUnknown *pDBManager, //in -DB Manager that has a open connection to the DB.
  594. IUnknown* pVarSetDnMap //in -VarSet containing a mapping of source to target DNs
  595. )
  596. {
  597. IIManageDBPtr pDb = pDBManager;
  598. IVarSetPtr spDnMap = pVarSetDnMap;
  599. ADS_ATTR_INFO * pAttrInfo = NULL;
  600. IVarSetPtr pVarset = pPropSet;
  601. HRESULT hr = S_OK;
  602. bool * pAllocArray = NULL;
  603. // Get properties from the source
  604. DWORD dwRet = GetProperties(sSourcePath, /*sSourceDomain,*/ pVarset, pAttrInfo);
  605. if ( dwRet > 0 )
  606. {
  607. pAllocArray = new bool[dwRet];
  608. if (!pAllocArray)
  609. {
  610. hr = E_OUTOFMEMORY;
  611. }
  612. else
  613. {
  614. ZeroMemory(pAllocArray, sizeof(bool)*dwRet);
  615. if (!TranslateDNs(pAttrInfo, dwRet, sSourceDomain, sTargetDomain, pDBManager, spDnMap, pAllocArray))
  616. {
  617. hr = E_FAIL;
  618. }
  619. else
  620. {
  621. for ( DWORD dwIdx = 0; dwIdx < dwRet; dwIdx++)
  622. {
  623. pAttrInfo[dwIdx].dwControlCode = ADS_ATTR_UPDATE;
  624. //we do not want to copy over the account enable\disable bit since we want this target
  625. //account to remain disabled at this time, so make sure that bit is cleared
  626. if (!_wcsicmp(pAttrInfo[dwIdx].pszAttrName, L"userAccountControl"))
  627. {
  628. if (pAttrInfo[dwIdx].dwADsType == ADSTYPE_INTEGER)
  629. pAttrInfo[dwIdx].pADsValues->Integer |= UF_ACCOUNTDISABLE;
  630. }
  631. }
  632. // Set the source properties in the target.
  633. hr = SetProperties(sTargetPath, /*sTargetDomain,*/ pAttrInfo, dwRet);
  634. }
  635. }
  636. }
  637. else
  638. {
  639. hr = dwRet;
  640. }
  641. // Need to free those strings allocated by TranslateDNs
  642. if (pAllocArray)
  643. {
  644. if (pAttrInfo)
  645. {
  646. for (DWORD i=0; i < dwRet; i++)
  647. {
  648. if (pAllocArray[i] == true)
  649. {
  650. FreeADsStr(pAttrInfo[i].pADsValues->DNString);
  651. }
  652. }
  653. }
  654. delete [] pAllocArray;
  655. }
  656. if ( pAttrInfo )
  657. FreeADsMem( pAttrInfo );
  658. return hr;
  659. }
  660. //---------------------------------------------------------------------------
  661. // SetProperties: This function sets the properties for a given object from
  662. // the attr info array.
  663. //---------------------------------------------------------------------------
  664. HRESULT CObjPropBuilder::SetProperties(
  665. BSTR sTargetPath, //in -Target object path.
  666. // BSTR sTargetDomain, //in - Target domain name
  667. ADS_ATTR_INFO * pAttrInfo, //in - ADSATTRINFO array with values for props
  668. DWORD dwItems //in - number of properties in the array
  669. )
  670. {
  671. IDirectoryObject * pDir;
  672. DWORD dwRet=0;
  673. IVarSetPtr pSucc(__uuidof(VarSet));
  674. IVarSetPtr pFail(__uuidof(VarSet));
  675. // Get the IDirectory Object interface to the Object.
  676. HRESULT hr = ADsGetObject(sTargetPath, IID_IDirectoryObject, (void**) &pDir);
  677. if ( FAILED(hr) )
  678. return hr;
  679. // Set the Object Attributes.
  680. hr = pDir->SetObjectAttributes(pAttrInfo, dwItems, &dwRet);
  681. if ( FAILED(hr) )
  682. {
  683. // we are going to try it one at a time and see what causes problems
  684. for (DWORD dw = 0; dw < dwItems; dw++)
  685. {
  686. hr = pDir->SetObjectAttributes(&pAttrInfo[dw], 1, &dwRet);
  687. _bstr_t x = pAttrInfo[dw].pszAttrName;
  688. _variant_t var;
  689. if ( FAILED(hr))
  690. {
  691. DWORD dwLastError;
  692. WCHAR szErrorBuf[LEN_Path];
  693. WCHAR szNameBuf[LEN_Path];
  694. //Get extended error value.
  695. HRESULT hr_return =S_OK;
  696. hr_return = ADsGetLastError( &dwLastError,
  697. szErrorBuf,
  698. LEN_Path-1,
  699. szNameBuf,
  700. LEN_Path-1);
  701. //var = szErrorBuf;
  702. var = hr;
  703. pFail->put(x, var);
  704. hr = S_OK;
  705. }
  706. else
  707. {
  708. pSucc->put(x, var);
  709. }
  710. }
  711. }
  712. pDir->Release();
  713. return hr;
  714. }
  715. //---------------------------------------------------------------------------
  716. // SetPropertiesFromVarset: This function sets values for properties from
  717. // the varset. The varset should contain the
  718. // propname (containing the val) and the
  719. // propname.Type ( containing the type of Val)
  720. //---------------------------------------------------------------------------
  721. STDMETHODIMP CObjPropBuilder::SetPropertiesFromVarset(
  722. BSTR sTargetPath, //in -LDAP path to the target object
  723. // BSTR sTragetDomain, //in - Domain name for the Target domain
  724. IUnknown *pUnk, //in - Varset to fetch the values from
  725. DWORD dwControl //in - Cotnrol code to use for Updating/Deleting etc..
  726. )
  727. {
  728. // This function loads up properties and their values from the Varset and sets them for a given user
  729. IVarSetPtr pVar;
  730. SAFEARRAY * keys;
  731. SAFEARRAY * vals;
  732. long lRet;
  733. VARIANT var;
  734. _variant_t varX;
  735. pVar = pUnk;
  736. VariantInit(&var);
  737. ADS_ATTR_INFO FAR * pAttrInfo = new ADS_ATTR_INFO[pVar->GetCount()];
  738. if (!pAttrInfo)
  739. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  740. HRESULT hr = pVar->getItems(L"", L"", 0, 10000, &keys, &vals, &lRet);
  741. if ( FAILED (hr) )
  742. {
  743. delete [] pAttrInfo;
  744. return hr;
  745. }
  746. // Build the Property Name/Value array from the varset.
  747. for ( long x = 0; x < lRet; x++ )
  748. {
  749. // Get the property name
  750. ::SafeArrayGetElement(keys, &x, &var);
  751. _bstr_t keyName = var.bstrVal;
  752. int len = wcslen(keyName);
  753. pAttrInfo[x].pszAttrName = new WCHAR[len + 2];
  754. if (!(pAttrInfo[x].pszAttrName))
  755. {
  756. for (int z=0; z<x; z++)
  757. delete [] pAttrInfo[z].pszAttrName;
  758. delete [] pAttrInfo;
  759. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  760. }
  761. wcscpy(pAttrInfo[x].pszAttrName, keyName);
  762. VariantClear(&var);
  763. // Get the property value
  764. ::SafeArrayGetElement(vals, &x, &var);
  765. keyName = keyName + _bstr_t(L".Type");
  766. varX = pVar->get(keyName);
  767. if(GetAttrInfo(varX, var, pAttrInfo[x]))
  768. {
  769. pAttrInfo[x].dwControlCode = dwControl;
  770. pAttrInfo[x].dwNumValues = 1;
  771. }
  772. VariantClear(&var);
  773. }
  774. SafeArrayDestroy(keys);
  775. SafeArrayDestroy(vals);
  776. // Once we build the array of name and property values then we call the sister function to do the rest
  777. if ( lRet > 0 ) SetProperties(sTargetPath, /*sTragetDomain,*/ pAttrInfo, lRet);
  778. // Always cleanup after yourself...
  779. for ( x = 0; x < lRet; x++ )
  780. {
  781. if (pAttrInfo[x].pADsValues)
  782. {
  783. switch (pAttrInfo[x].dwADsType)
  784. {
  785. case ADSTYPE_DN_STRING:
  786. if (pAttrInfo[x].pADsValues->DNString)
  787. FreeADsStr(pAttrInfo[x].pADsValues->DNString);
  788. break;
  789. case ADSTYPE_CASE_EXACT_STRING:
  790. if (pAttrInfo[x].pADsValues->CaseExactString)
  791. FreeADsStr(pAttrInfo[x].pADsValues->CaseExactString);
  792. break;
  793. case ADSTYPE_CASE_IGNORE_STRING:
  794. if (pAttrInfo[x].pADsValues->CaseIgnoreString)
  795. FreeADsStr(pAttrInfo[x].pADsValues->CaseIgnoreString);
  796. break;
  797. case ADSTYPE_PRINTABLE_STRING:
  798. if (pAttrInfo[x].pADsValues->PrintableString)
  799. FreeADsStr(pAttrInfo[x].pADsValues->PrintableString);
  800. break;
  801. case ADSTYPE_NUMERIC_STRING:
  802. if (pAttrInfo[x].pADsValues->NumericString)
  803. FreeADsStr(pAttrInfo[x].pADsValues->NumericString);
  804. break;
  805. default:
  806. break;
  807. }
  808. }
  809. delete pAttrInfo[x].pADsValues;
  810. delete [] pAttrInfo[x].pszAttrName;
  811. }
  812. delete [] pAttrInfo;
  813. return S_OK;
  814. }
  815. //------------------------------------------------------------------------------
  816. // GetAttrInfo: Given a variant this function fills in the ADS_ATTR_INFO struct
  817. //------------------------------------------------------------------------------
  818. bool CObjPropBuilder::GetAttrInfo(
  819. _variant_t varX, //in - Variant containing the Type of prop
  820. const _variant_t & var, //in - Variant containing the Prop value
  821. ADS_ATTR_INFO& attrInfo //out - The filled up attr info structure
  822. )
  823. {
  824. switch (varX.lVal)
  825. {
  826. case ADSTYPE_DN_STRING : {
  827. attrInfo.dwADsType = ADSTYPE_DN_STRING;
  828. ADSVALUE * pAd = new ADSVALUE();
  829. if (!pAd)
  830. return false;
  831. pAd->dwType = ADSTYPE_DN_STRING;
  832. pAd->DNString = AllocADsStr(var.bstrVal);
  833. if (!pAd->DNString && var.bstrVal)
  834. {
  835. delete pAd;
  836. return false;
  837. }
  838. attrInfo.pADsValues = pAd;
  839. break;
  840. }
  841. case ADSTYPE_CASE_EXACT_STRING : {
  842. attrInfo.dwADsType = ADSTYPE_CASE_EXACT_STRING;
  843. ADSVALUE * pAd = new ADSVALUE();
  844. if (!pAd)
  845. return false;
  846. pAd->dwType = ADSTYPE_CASE_EXACT_STRING;
  847. pAd->CaseExactString = AllocADsStr(var.bstrVal);
  848. if (!pAd->CaseExactString && var.bstrVal)
  849. {
  850. delete pAd;
  851. return false;
  852. }
  853. attrInfo.pADsValues = pAd;
  854. break;
  855. }
  856. case ADSTYPE_CASE_IGNORE_STRING : {
  857. attrInfo.dwADsType = ADSTYPE_CASE_IGNORE_STRING;
  858. ADSVALUE * pAd = new ADSVALUE();
  859. if (!pAd)
  860. return false;
  861. pAd->dwType = ADSTYPE_CASE_IGNORE_STRING;
  862. pAd->CaseIgnoreString = AllocADsStr(var.bstrVal);
  863. if (!pAd->CaseIgnoreString && var.bstrVal)
  864. {
  865. delete pAd;
  866. return false;
  867. }
  868. attrInfo.pADsValues = pAd;
  869. break;
  870. }
  871. case ADSTYPE_PRINTABLE_STRING : {
  872. attrInfo.dwADsType = ADSTYPE_PRINTABLE_STRING;
  873. ADSVALUE * pAd = new ADSVALUE();
  874. if (!pAd)
  875. return false;
  876. pAd->dwType = ADSTYPE_PRINTABLE_STRING;
  877. pAd->PrintableString = AllocADsStr(var.bstrVal);
  878. if (!pAd->PrintableString && var.bstrVal)
  879. {
  880. delete pAd;
  881. return false;
  882. }
  883. attrInfo.pADsValues = pAd;
  884. break;
  885. }
  886. case ADSTYPE_NUMERIC_STRING : {
  887. attrInfo.dwADsType = ADSTYPE_NUMERIC_STRING;
  888. ADSVALUE * pAd = new ADSVALUE();
  889. if (!pAd)
  890. return false;
  891. pAd->dwType = ADSTYPE_NUMERIC_STRING;
  892. pAd->NumericString = AllocADsStr(var.bstrVal);
  893. if (!pAd->NumericString && var.bstrVal)
  894. {
  895. delete pAd;
  896. return false;
  897. }
  898. attrInfo.pADsValues = pAd;
  899. break;
  900. }
  901. case ADSTYPE_INTEGER : {
  902. attrInfo.dwADsType = ADSTYPE_INTEGER;
  903. ADSVALUE * pAd = new ADSVALUE();
  904. if (!pAd)
  905. return false;
  906. pAd->dwType = ADSTYPE_INTEGER;
  907. pAd->Integer = var.lVal;
  908. attrInfo.pADsValues = pAd;
  909. break;
  910. }
  911. default : {
  912. // Don't support this type then get it out of the way.
  913. return false;
  914. break;
  915. }
  916. }
  917. return true;
  918. }
  919. //------------------------------------------------------------------------------
  920. // MapProperties: Using the OID of the properties this function creates a set
  921. // of properties that are common to both source and target domain.
  922. //------------------------------------------------------------------------------
  923. STDMETHODIMP CObjPropBuilder::MapProperties(
  924. BSTR sSourceClass, //in- Source Class name
  925. BSTR sSourceDomain, //in- Source domain name
  926. long lSourceVer, //in- Source domain version
  927. BSTR sTargetClass, //in - Target class name
  928. BSTR sTargetDomain, //in - Target domain name
  929. long lTargetVer, //in - Target Domain version
  930. BOOL bIncNames, //in - flag telling if varset should include propname
  931. IUnknown **ppUnk //out - Varset with the mapped properties
  932. )
  933. {
  934. ATLTRACE(_T("E CObjPropBuilder::MapProperties(sSourceClass='%s', sSourceDomain='%s', lSourceVer=%ld, sTargetClass='%s', sTargetDomain='%s', lTargetVer=%ld, bIncNames=%s, ppUnk=...)\n"), sSourceClass, sSourceDomain, lSourceVer, sTargetClass, sTargetDomain, lTargetVer, bIncNames ? _T("TRUE") : _T("FALSE"));
  935. IVarSetPtr pSource(__uuidof(VarSet));
  936. IVarSetPtr pTarget(__uuidof(VarSet));
  937. IVarSetPtr pMerged = *ppUnk;
  938. IVarSetPtr pFailed(__uuidof(VarSet));
  939. IUnknown * pUnk;
  940. SAFEARRAY * keys;
  941. SAFEARRAY * vals;
  942. long lRet;
  943. VARIANT var;
  944. _variant_t varTarget;
  945. _variant_t varEmpty;
  946. bool bSystemFlag;
  947. WCHAR sPath[LEN_Path];
  948. WCHAR sProgDir[LEN_Path];
  949. bool bUnMapped = false;
  950. VariantInit(&var);
  951. GetProgramDirectory(sProgDir);
  952. wsprintf(sPath, L"%s%s", sProgDir, L"Logs\\PropMap.log");
  953. err.LogOpen(sPath,0,0);
  954. // Get the list of props for source and target first
  955. HRESULT hr = pSource->QueryInterface(IID_IUnknown, (void **)&pUnk);
  956. GetClassPropEnum(sSourceClass, sSourceDomain, lSourceVer, &pUnk);
  957. pUnk->Release();
  958. hr = pTarget->QueryInterface(IID_IUnknown, (void **)&pUnk);
  959. GetClassPropEnum(sTargetClass, sTargetDomain, lTargetVer, &pUnk);
  960. pUnk->Release();
  961. // For every item in source try to find same OID in target. If it exists in both source and target then put it into merged Varset
  962. hr = pSource->getItems(L"", L"", 1, 10000, &keys, &vals, &lRet);
  963. if ( FAILED (hr) )
  964. return hr;
  965. // Build the Property Name/Value array from the varset.
  966. _bstr_t val;
  967. _bstr_t keyName;
  968. for ( long x = 0; x < lRet; x++ )
  969. {
  970. // Get the property name
  971. ::SafeArrayGetElement(keys, &x, &var);
  972. keyName = var.bstrVal;
  973. VariantClear(&var);
  974. if ( lSourceVer > 4 )
  975. {
  976. // Windows 2000 domain so we map by OID
  977. if ((wcsncmp(keyName, L"Man", 3) != 0) && (wcsncmp(keyName, L"Opt", 3) != 0) )
  978. {
  979. //
  980. // Only leaf VarSet nodes which represent a complete OID should be processed.
  981. //
  982. // Each component of a key string delimited by the period character becomes
  983. // a subkey of the previous component.
  984. //
  985. // For example given the OID 1.2.840.113556.1.2.102 for the memberOf attribute
  986. // the VarSet stores each component in a node where only the leaf node contains
  987. // a value.
  988. //
  989. // Key Value
  990. // 1 VT_EMPTY
  991. // 1.2 VT_EMPTY
  992. // 1.2.840 VT_EMPTY
  993. // 1.2.840.113556 VT_EMPTY
  994. // 1.2.840.113556.1 VT_EMPTY
  995. // 1.2.840.113556.1.2 VT_EMPTY
  996. // 1.2.840.113556.1.2.102 "memberOf"
  997. //
  998. // The empty nodes in the VarSet do not correspond to valid attributes and
  999. // therefore must be ignored.
  1000. //
  1001. _variant_t vntValue = pSource->get(keyName);
  1002. if (V_VT(&vntValue) == VT_BSTR)
  1003. {
  1004. // Only go through the OID keys to get the name of the properties.
  1005. varTarget = pTarget->get(keyName);
  1006. if ( varTarget.vt == VT_BSTR )
  1007. {
  1008. val = varTarget.bstrVal;
  1009. //
  1010. // The member, sAMAccountName and userPrincipalName attributes are set by other
  1011. // components which depend upon the attribute not being copied by this component.
  1012. //
  1013. // The legacyExchangeDN attribute is managed by Exchange and should never be copied.
  1014. //
  1015. // TODO: The objectSid attribute is a system only attribute therefore this is redundant.
  1016. //
  1017. // The isCriticalSystemObject, pwdLastSet, rid and userPassword attributes probably
  1018. // cannot be set as the system won't allow it so it is okay not to try and copy them.
  1019. //
  1020. // TODO: The c, l, st and userAccountControl attributes were previously considered
  1021. // 'system' attributes as they are required to be members of the partial set. Therefore
  1022. // explicitly including them is no longer necessary.
  1023. //
  1024. if ((!IsPropSystemOnly(val, sTargetDomain, bSystemFlag) && (wcscmp(val, L"objectSid") != 0)
  1025. && (wcscmp(val, L"sAMAccountName") != 0) && (_wcsicmp(val, L"Rid") != 0)
  1026. && (wcscmp(val, L"pwdLastSet") != 0) && (wcscmp(val, L"userPassword") != 0)
  1027. && (wcscmp(val, L"member") != 0) && (wcscmp(val, L"userPrincipalName") != 0)
  1028. && (wcscmp(val, L"isCriticalSystemObject") != 0) && (wcscmp(val, L"legacyExchangeDN") != 0))
  1029. || ( !_wcsicmp(val, L"c") || !_wcsicmp(val, L"l") || !_wcsicmp(val, L"st")
  1030. || !_wcsicmp(val, L"userAccountControl") ) ) // These properties are exceptions.
  1031. {
  1032. if (bIncNames)
  1033. pMerged->put(keyName, val);
  1034. else
  1035. pMerged->put(val, varEmpty);
  1036. }
  1037. else
  1038. pFailed->put(val, varEmpty);
  1039. }
  1040. else if (!bIncNames)
  1041. {
  1042. err.MsgWrite(ErrE, DCT_MSG_FAILED_TO_MAP_PROP_SSSSS, (WCHAR*)_bstr_t(vntValue), (WCHAR*) sSourceClass,
  1043. (WCHAR*) sSourceDomain, (WCHAR*) sTargetClass, (WCHAR*) sTargetDomain);
  1044. bUnMapped = true;
  1045. }
  1046. }
  1047. }
  1048. }
  1049. else
  1050. {
  1051. // NT4 code is the one that we map by Names.
  1052. if ( keyName.length() > 0 )
  1053. {
  1054. WCHAR propName[LEN_Path];
  1055. if (wcsncmp(keyName, L"Man", 3) == 0)
  1056. wcscpy(propName, (WCHAR*) keyName+20);
  1057. else
  1058. wcscpy(propName, (WCHAR*) keyName+19);
  1059. varTarget = pSource->get(keyName);
  1060. if ( varTarget.vt == VT_BSTR )
  1061. pMerged->put(propName, varEmpty);
  1062. }
  1063. }
  1064. }
  1065. SafeArrayDestroy(keys);
  1066. SafeArrayDestroy(vals);
  1067. err.LogClose();
  1068. ATLTRACE(_T("L CObjPropBuilder::MapProperties()\n"));
  1069. if (bUnMapped)
  1070. return DCT_MSG_PROPERTIES_NOT_MAPPED;
  1071. else
  1072. return S_OK;
  1073. }
  1074. //------------------------------------------------------------------------------
  1075. // IsPropSystemOnly: This function determines if a specific property is
  1076. // System Only or not
  1077. //------------------------------------------------------------------------------
  1078. bool CObjPropBuilder::IsPropSystemOnly(
  1079. const WCHAR * sName, //in- Name of the property
  1080. const WCHAR * sDomain, //in- Domain name where we need to check
  1081. bool& bSystemFlag, //out- Tells us if it failed due to system flag.
  1082. bool* pbBaseObject //out- whether attribute is part of the base schema
  1083. )
  1084. {
  1085. // we will lookup the property name in target domain schema and see if it is system only or not.
  1086. // First build an LDAP path to the Schema container.
  1087. HRESULT hr = S_OK;
  1088. WCHAR sQuery[LEN_Path];
  1089. LPWSTR sCols[] = { L"systemOnly", L"systemFlags" };
  1090. ADS_SEARCH_HANDLE hSearch;
  1091. ADS_SEARCH_COLUMN col;
  1092. bool bSystemOnly = true;
  1093. if (pbBaseObject)
  1094. {
  1095. *pbBaseObject = false;
  1096. }
  1097. if (m_strSchemaDomain != _bstr_t(sDomain))
  1098. {
  1099. m_strSchemaDomain = sDomain;
  1100. m_spSchemaSearch.Release();
  1101. //
  1102. // Retrieve schema naming context and bind to IDirectorySearch
  1103. // interface of schema container in domain. Note that three
  1104. // attempts are made to bind to RootDSE and the schema container.
  1105. //
  1106. IADsPtr spRootDse;
  1107. _bstr_t strSchemaNamingContext;
  1108. int nTry = 0;
  1109. do
  1110. {
  1111. if (FAILED(hr))
  1112. {
  1113. Sleep(5000);
  1114. }
  1115. hr = ADsGetObject(L"LDAP://" + m_strSchemaDomain + L"/rootDSE", IID_IADs, (void**)&spRootDse);
  1116. if (SUCCEEDED(hr))
  1117. {
  1118. VARIANT var;
  1119. VariantInit(&var);
  1120. hr = spRootDse->Get(L"schemaNamingContext", &var);
  1121. if (SUCCEEDED(hr))
  1122. {
  1123. strSchemaNamingContext = _variant_t(var, false);
  1124. }
  1125. }
  1126. }
  1127. while (FAILED(hr) && (++nTry < 3));
  1128. if (SUCCEEDED(hr))
  1129. {
  1130. nTry = 0;
  1131. do
  1132. {
  1133. if (FAILED(hr))
  1134. {
  1135. Sleep(5000);
  1136. }
  1137. hr = ADsGetObject(
  1138. L"LDAP://" + m_strSchemaDomain + L"/" + strSchemaNamingContext,
  1139. IID_IDirectorySearch,
  1140. (void**)&m_spSchemaSearch
  1141. );
  1142. }
  1143. while (FAILED(hr) && (++nTry < 3));
  1144. }
  1145. if (FAILED(hr))
  1146. {
  1147. err.SysMsgWrite(ErrW, hr, DCT_MSG_IS_SYSTEM_PROPERTY_CANNOT_BIND_TO_SCHEMA_S, sDomain);
  1148. }
  1149. }
  1150. if (SUCCEEDED(hr) && m_spSchemaSearch)
  1151. {
  1152. // Build the query string
  1153. wsprintf(sQuery, L"(lDAPDisplayName=%s)", sName);
  1154. // Now search for this property
  1155. hr = m_spSchemaSearch->ExecuteSearch(sQuery, sCols, 2, &hSearch);
  1156. if ( SUCCEEDED(hr) )
  1157. {// Get the systemOnly flag and return its value.
  1158. hr = m_spSchemaSearch->GetFirstRow(hSearch);
  1159. if (hr == S_OK)
  1160. {
  1161. hr = m_spSchemaSearch->GetColumn( hSearch, sCols[0], &col );
  1162. if ( SUCCEEDED(hr) )
  1163. {
  1164. bSystemOnly = ( col.pADsValues->Boolean == TRUE);
  1165. m_spSchemaSearch->FreeColumn( &col );
  1166. }
  1167. else if (hr == E_ADS_COLUMN_NOT_SET)
  1168. {
  1169. //
  1170. // If systemOnly attribute is not defined for this attribute
  1171. // then the attribute cannot be a 'system only' attribute.
  1172. //
  1173. bSystemOnly = false;
  1174. hr = S_OK;
  1175. }
  1176. // Check the system flags
  1177. hr = m_spSchemaSearch->GetColumn( hSearch, sCols[1], &col );
  1178. if ( SUCCEEDED(hr) )
  1179. {
  1180. //
  1181. // If the attribute is a base schema object then check the system flags. If
  1182. // the attribute is not a base schema object it cannot be a system attribute
  1183. // and therefore may be copied.
  1184. //
  1185. if (col.pADsValues->Integer & ADS_SYSTEMFLAG_SCHEMA_BASE_OBJECT)
  1186. {
  1187. //
  1188. // If the attribute is an operational attribute, is a constructed attribute
  1189. // or the attribute is not replicated then the attribute should not be copied.
  1190. //
  1191. // The nTSecurityDescriptor attribute is an example of an operational attribute
  1192. // that should not be copied. The canonicalName attribute is an example of a
  1193. // constructed attribute that cannot be copied. The distinguishedName attribute
  1194. // is an example of a not replicated attribute that cannot be copied.
  1195. //
  1196. const ADS_INTEGER SYSTEM_FLAGS =
  1197. ADS_SYSTEMFLAG_ATTR_IS_OPERATIONAL|ADS_SYSTEMFLAG_ATTR_IS_CONSTRUCTED|ADS_SYSTEMFLAG_ATTR_REQ_PARTIAL_SET_MEMBER|ADS_SYSTEMFLAG_ATTR_NOT_REPLICATED;
  1198. bSystemFlag = (col.pADsValues->Integer & SYSTEM_FLAGS) != 0;
  1199. bSystemOnly = bSystemOnly || bSystemFlag;
  1200. if (pbBaseObject)
  1201. {
  1202. *pbBaseObject = true;
  1203. }
  1204. }
  1205. m_spSchemaSearch->FreeColumn(&col);
  1206. }
  1207. else if (hr == E_ADS_COLUMN_NOT_SET)
  1208. {
  1209. //
  1210. // If systemFlags attribute is not defined for this attribute
  1211. // then return fact that systemFlags attribute cannot be reason
  1212. // attribute was marked as 'system only' if it is.
  1213. //
  1214. bSystemFlag = false;
  1215. hr = S_OK;
  1216. }
  1217. }
  1218. else if (hr == S_ADS_NOMORE_ROWS)
  1219. {
  1220. //
  1221. // If neither the systemOnly or systemFlags attribute is defined for
  1222. // this attribute then the attribute cannot be a 'system only' attribute.
  1223. //
  1224. bSystemOnly = false;
  1225. hr = S_OK;
  1226. }
  1227. m_spSchemaSearch->CloseSearchHandle(hSearch);
  1228. }
  1229. if (FAILED(hr))
  1230. {
  1231. err.SysMsgWrite(ErrW, hr, DCT_MSG_IS_SYSTEM_PROPERTY_CANNOT_VERIFY_SYSTEM_ONLY_SS, sName, sDomain);
  1232. }
  1233. }
  1234. return bSystemOnly;
  1235. }
  1236. //------------------------------------------------------------------------------
  1237. // TranslateDNs: This function Translates object properties that are
  1238. // distinguished names to point to same object in target domain
  1239. // as the object in the source domain.
  1240. //------------------------------------------------------------------------------
  1241. BOOL CObjPropBuilder::TranslateDNs(
  1242. ADS_ATTR_INFO *pAttrInfo, //in -Array
  1243. DWORD dwRet, BSTR sSource,
  1244. BSTR sTarget,
  1245. IUnknown *pCheckList, //in -Object that will check the list if an account Exists
  1246. IVarSet* pDnMap,
  1247. bool *pAllocArray
  1248. )
  1249. {
  1250. HRESULT hr;
  1251. IIManageDBPtr spDatabase = pCheckList;
  1252. //
  1253. // Initialize source pathname object. If able to retrieve name of global catalog
  1254. // server in the source forest then initialize pathname to global catalog otherwise
  1255. // initialize pathname to source domain.
  1256. //
  1257. _bstr_t strSourceGC;
  1258. _bstr_t strTargetGC;
  1259. GetGlobalCatalogServer5(sSource, strSourceGC);
  1260. GetGlobalCatalogServer5(sTarget, strTargetGC);
  1261. CADsPathName pnSourcePath;
  1262. if ((PCWSTR)strSourceGC)
  1263. {
  1264. pnSourcePath.Set(L"GC", ADS_SETTYPE_PROVIDER);
  1265. pnSourcePath.Set(strSourceGC, ADS_SETTYPE_SERVER);
  1266. }
  1267. else
  1268. {
  1269. pnSourcePath.Set(L"LDAP", ADS_SETTYPE_PROVIDER);
  1270. pnSourcePath.Set(sSource, ADS_SETTYPE_SERVER);
  1271. }
  1272. //
  1273. // For each ADSTYPE_DN_STRING attribute...
  1274. //
  1275. for (DWORD iAttribute = 0; iAttribute < dwRet; iAttribute++)
  1276. {
  1277. if (pAttrInfo[iAttribute].dwADsType != ADSTYPE_DN_STRING)
  1278. {
  1279. continue;
  1280. }
  1281. //
  1282. // For each value in attribute...
  1283. //
  1284. DWORD cValue = pAttrInfo[iAttribute].dwNumValues;
  1285. for (DWORD iValue = 0; iValue < cValue; iValue++)
  1286. {
  1287. ADSVALUE& value = pAttrInfo[iAttribute].pADsValues[iValue];
  1288. //
  1289. // If the object is currently being migrated then the source
  1290. // and target distinguished names will be in the distinguished
  1291. // name map. As this will be the most current information and
  1292. // the least expensive to query the map is queried first before
  1293. // querying the database.
  1294. //
  1295. _bstr_t strTargetDn = pDnMap->get(_bstr_t(value.DNString));
  1296. if (strTargetDn.length() > 0)
  1297. {
  1298. LPWSTR pszName = AllocADsStr(strTargetDn);
  1299. if (pszName)
  1300. {
  1301. value.DNString = pszName;
  1302. pAllocArray[iAttribute] = true;
  1303. }
  1304. continue;
  1305. }
  1306. //
  1307. // If the object has previously been migrated then a record that maps the source object
  1308. // to the target object will exist in the migrated objects table. The SID of the source
  1309. // object is used to query for the source object in the table as this uniquely identifies
  1310. // the object.
  1311. //
  1312. IDirectoryObjectPtr spSourceObject;
  1313. _variant_t vntSid;
  1314. pnSourcePath.Set(value.DNString, ADS_SETTYPE_DN);
  1315. hr = ADsGetObject(pnSourcePath.Retrieve(ADS_FORMAT_X500), IID_IDirectoryObject, (VOID**)&spSourceObject);
  1316. if (SUCCEEDED(hr))
  1317. {
  1318. LPWSTR pszNames[] = { L"objectSid" };
  1319. PADS_ATTR_INFO pAttrInfo = NULL;
  1320. DWORD cAttrInfo = 0;
  1321. hr = spSourceObject->GetObjectAttributes(pszNames, 1, &pAttrInfo, &cAttrInfo);
  1322. if (SUCCEEDED(hr))
  1323. {
  1324. if (pAttrInfo && (_wcsicmp(pAttrInfo->pszAttrName, pszNames[0]) == 0))
  1325. {
  1326. if (pAttrInfo->dwADsType == ADSTYPE_OCTET_STRING)
  1327. {
  1328. ADS_OCTET_STRING& os = pAttrInfo->pADsValues->OctetString;
  1329. SAFEARRAY* psa = SafeArrayCreateVector(VT_UI1, 0, os.dwLength);
  1330. if (psa)
  1331. {
  1332. memcpy(psa->pvData, os.lpValue, os.dwLength);
  1333. V_VT(&vntSid) = VT_ARRAY|VT_UI1;
  1334. V_ARRAY(&vntSid) = psa;
  1335. }
  1336. else
  1337. {
  1338. hr = E_OUTOFMEMORY;
  1339. }
  1340. }
  1341. else
  1342. {
  1343. hr = E_FAIL;
  1344. }
  1345. }
  1346. else
  1347. {
  1348. hr = E_FAIL;
  1349. }
  1350. FreeADsMem(pAttrInfo);
  1351. }
  1352. }
  1353. if (FAILED(hr))
  1354. {
  1355. continue;
  1356. }
  1357. _bstr_t strSid;
  1358. _bstr_t strRid;
  1359. if (!GetSidAndRidFromVariant(vntSid, strSid, strRid))
  1360. {
  1361. continue;
  1362. }
  1363. IVarSetPtr spVarSet(__uuidof(VarSet));
  1364. IUnknownPtr spUnknown(spVarSet);
  1365. IUnknown* punk = spUnknown;
  1366. hr = spDatabase->raw_GetAMigratedObjectBySidAndRid(strSid, strRid, &punk);
  1367. //
  1368. // If the object was found in the database
  1369. // the result will be S_OK otherwise S_FALSE.
  1370. //
  1371. if (hr != S_OK)
  1372. {
  1373. continue;
  1374. }
  1375. //
  1376. // Bind to the target object using the GUID property.
  1377. //
  1378. _bstr_t strGuid = spVarSet->get(_bstr_t(L"MigratedObjects.GUID"));
  1379. if ((PCWSTR)strGuid)
  1380. {
  1381. tstring strGuidPath;
  1382. if ((PCWSTR)strTargetGC)
  1383. {
  1384. strGuidPath = _T("GC://");
  1385. strGuidPath += strTargetGC;
  1386. }
  1387. else
  1388. {
  1389. strGuidPath = _T("LDAP://");
  1390. strGuidPath += sTarget;
  1391. }
  1392. strGuidPath += _T("/<GUID=");
  1393. strGuidPath += strGuid;
  1394. strGuidPath += _T(">");
  1395. IADsPtr spTargetObject;
  1396. hr = ADsGetObject(strGuidPath.c_str(), IID_IADs, (VOID**)&spTargetObject);
  1397. if (SUCCEEDED(hr))
  1398. {
  1399. //
  1400. // Retrieve distinguished name and update DN
  1401. // attribute value.
  1402. //
  1403. VARIANT varName;
  1404. VariantInit(&varName);
  1405. hr = spTargetObject->Get(_bstr_t(L"distinguishedName"), &varName);
  1406. if (SUCCEEDED(hr))
  1407. {
  1408. LPWSTR pszName = AllocADsStr(V_BSTR(&varName));
  1409. if (pszName)
  1410. {
  1411. value.DNString = pszName;
  1412. pAllocArray[iAttribute] = true;
  1413. }
  1414. VariantClear(&varName);
  1415. }
  1416. }
  1417. }
  1418. }
  1419. }
  1420. return TRUE;
  1421. }
  1422. STDMETHODIMP CObjPropBuilder::ChangeGroupType(BSTR sGroupPath, long lGroupType)
  1423. {
  1424. HRESULT hr;
  1425. IADsGroup * pGroup;
  1426. _variant_t var;
  1427. long lType;
  1428. // Get the group type info from the object.
  1429. hr = ADsGetObject( sGroupPath, IID_IADsGroup, (void**) &pGroup);
  1430. if (FAILED(hr)) return hr;
  1431. hr = pGroup->Get(L"groupType", &var);
  1432. if (FAILED(hr)) return hr;
  1433. // Check if Security group or Distribution group and then set the type accordingly.
  1434. lType = var.lVal;
  1435. if (lType & 0x80000000 )
  1436. lType = lGroupType | 0x80000000;
  1437. else
  1438. lType = lGroupType;
  1439. // Set the value into the Group Information
  1440. var = lType;
  1441. hr = pGroup->Put(L"groupType", var);
  1442. if (FAILED(hr)) return hr;
  1443. hr = pGroup->SetInfo();
  1444. if (FAILED(hr)) return hr;
  1445. pGroup->Release();
  1446. return S_OK;
  1447. }
  1448. //------------------------------------------------------------------------------------------------------------------------------
  1449. // CopyNT4Props : Uses Net APIs to get info from the source account and then set it to the target account.
  1450. //------------------------------------------------------------------------------------------------------------------------------
  1451. STDMETHODIMP CObjPropBuilder::CopyNT4Props(BSTR sSourceSam, BSTR sTargetSam, BSTR sSourceServer, BSTR sTargetServer, BSTR sType, long lGrpType, BSTR sExclude)
  1452. {
  1453. DWORD dwError = ERROR_SUCCESS;
  1454. #define ISEXCLUDE(a) IsStringInDelimitedString(sExclude, L#a, L',')
  1455. if (_wcsicmp(sType, L"user") == 0)
  1456. {
  1457. //
  1458. // user
  1459. //
  1460. USER_INFO_3 ui;
  1461. PUSER_INFO_3 puiSource = NULL;
  1462. PUSER_INFO_3 puiTarget = NULL;
  1463. dwError = NetUserGetInfo(sSourceServer, sSourceSam, 3, (LPBYTE*)&puiSource);
  1464. if (dwError == ERROR_SUCCESS)
  1465. {
  1466. dwError = NetUserGetInfo(sTargetServer, sTargetSam, 3, (LPBYTE*)&puiTarget);
  1467. if (dwError == ERROR_SUCCESS)
  1468. {
  1469. // note that attributes with the comment ignored are ignored by NetUserSetInfo
  1470. // setting to target value just so that they have a valid value
  1471. ui.usri3_name = puiTarget->usri3_name; // ignored
  1472. ui.usri3_password = NULL; // must not set during copy properties
  1473. ui.usri3_password_age = puiTarget->usri3_password_age; // ignored
  1474. ui.usri3_priv = puiTarget->usri3_priv; // ignored
  1475. ui.usri3_home_dir = ISEXCLUDE(homeDirectory) ? puiTarget->usri3_home_dir : puiSource->usri3_home_dir;
  1476. ui.usri3_comment = ISEXCLUDE(description) ? puiTarget->usri3_comment : puiSource->usri3_comment;
  1477. ui.usri3_flags = puiSource->usri3_flags;
  1478. // translate a local account to a domain account
  1479. ui.usri3_flags &= ~UF_TEMP_DUPLICATE_ACCOUNT;
  1480. // disable the account in case no password has been set
  1481. ui.usri3_flags |= UF_ACCOUNTDISABLE;
  1482. ui.usri3_script_path = ISEXCLUDE(scriptPath) ? puiTarget->usri3_script_path : puiSource->usri3_script_path;
  1483. ui.usri3_auth_flags = puiTarget->usri3_auth_flags; // ignored
  1484. ui.usri3_full_name = ISEXCLUDE(displayName) ? puiTarget->usri3_full_name : puiSource->usri3_full_name;
  1485. ui.usri3_usr_comment = ISEXCLUDE(comment) ? puiTarget->usri3_usr_comment : puiSource->usri3_usr_comment;
  1486. ui.usri3_parms = ISEXCLUDE(userParameters) ? puiTarget->usri3_parms : puiSource->usri3_parms;
  1487. ui.usri3_workstations = ISEXCLUDE(userWorkstations) ? puiTarget->usri3_workstations : puiSource->usri3_workstations;
  1488. ui.usri3_last_logon = puiTarget->usri3_last_logon; // ignored
  1489. ui.usri3_last_logoff = ISEXCLUDE(lastLogoff) ? puiTarget->usri3_last_logoff : puiSource->usri3_last_logoff;
  1490. ui.usri3_acct_expires = ISEXCLUDE(accountExpires) ? puiTarget->usri3_acct_expires : puiSource->usri3_acct_expires;
  1491. ui.usri3_max_storage = ISEXCLUDE(maxStorage) ? puiTarget->usri3_max_storage : puiSource->usri3_max_storage;
  1492. ui.usri3_units_per_week = puiTarget->usri3_units_per_week; // ignored
  1493. ui.usri3_logon_hours = ISEXCLUDE(logonHours) ? puiTarget->usri3_logon_hours : puiSource->usri3_logon_hours;
  1494. ui.usri3_bad_pw_count = puiTarget->usri3_bad_pw_count; // ignored
  1495. ui.usri3_num_logons = puiTarget->usri3_num_logons; // ignored
  1496. ui.usri3_logon_server = puiTarget->usri3_logon_server; // ignored
  1497. ui.usri3_country_code = ISEXCLUDE(countryCode) ? puiTarget->usri3_country_code : puiSource->usri3_country_code;
  1498. ui.usri3_code_page = ISEXCLUDE(codePage) ? puiTarget->usri3_code_page : puiSource->usri3_code_page;
  1499. ui.usri3_user_id = puiTarget->usri3_user_id; // ignored
  1500. // if not excluded set the primary group to the Domain Users group
  1501. ui.usri3_primary_group_id = ISEXCLUDE(primaryGroupID) ? puiTarget->usri3_primary_group_id : DOMAIN_GROUP_RID_USERS;
  1502. ui.usri3_profile = ISEXCLUDE(profilePath) ? puiTarget->usri3_profile : puiSource->usri3_profile;
  1503. ui.usri3_home_dir_drive = ISEXCLUDE(homeDrive) ? puiTarget->usri3_home_dir_drive : puiSource->usri3_home_dir_drive;
  1504. ui.usri3_password_expired = puiTarget->usri3_password_expired;
  1505. dwError = NetUserSetInfo(sTargetServer, sTargetSam, 3, (LPBYTE)&ui, NULL);
  1506. if (dwError == NERR_UserNotInGroup)
  1507. {
  1508. // if the setInfo failed because of the primary group property, try again, using the primary group
  1509. // that is already defined for the target account
  1510. ui.usri3_primary_group_id = puiTarget->usri3_primary_group_id;
  1511. dwError = NetUserSetInfo(sTargetServer, sTargetSam, 3, (LPBYTE)&ui, NULL);
  1512. }
  1513. }
  1514. if (puiTarget)
  1515. {
  1516. NetApiBufferFree(puiTarget);
  1517. }
  1518. if (puiSource)
  1519. {
  1520. NetApiBufferFree(puiSource);
  1521. }
  1522. }
  1523. }
  1524. else if (_wcsicmp(sType, L"group") == 0)
  1525. {
  1526. // if description attribute is not excluded then copy comment attribute
  1527. // note that the only downlevel group attribute that will be copied is the description (comment) attribute
  1528. if (ISEXCLUDE(description) == FALSE)
  1529. {
  1530. if (lGrpType & 4)
  1531. {
  1532. //
  1533. // local group
  1534. //
  1535. PLOCALGROUP_INFO_1 plgi = NULL;
  1536. dwError = NetLocalGroupGetInfo(sSourceServer, sSourceSam, 1, (LPBYTE*)&plgi);
  1537. if (dwError == ERROR_SUCCESS)
  1538. {
  1539. dwError = NetLocalGroupSetInfo(sTargetServer, sTargetSam, 1, (LPBYTE)plgi, NULL);
  1540. NetApiBufferFree(plgi);
  1541. }
  1542. }
  1543. else
  1544. {
  1545. //
  1546. // global group
  1547. //
  1548. PGROUP_INFO_1 pgi = NULL;
  1549. dwError = NetGroupGetInfo(sSourceServer, sSourceSam, 1, (LPBYTE*)&pgi);
  1550. if (dwError == ERROR_SUCCESS)
  1551. {
  1552. dwError = NetGroupSetInfo(sTargetServer, sTargetSam, 1, (LPBYTE)pgi, NULL);
  1553. NetApiBufferFree(pgi);
  1554. }
  1555. }
  1556. }
  1557. }
  1558. else if (_wcsicmp(sType, L"computer") == 0)
  1559. {
  1560. //
  1561. // computer
  1562. //
  1563. USER_INFO_3 ui;
  1564. PUSER_INFO_3 puiSource = NULL;
  1565. PUSER_INFO_3 puiTarget = NULL;
  1566. dwError = NetUserGetInfo(sSourceServer, sSourceSam, 3, (LPBYTE*)&puiSource);
  1567. if (dwError == ERROR_SUCCESS)
  1568. {
  1569. dwError = NetUserGetInfo(sTargetServer, sTargetSam, 3, (LPBYTE*)&puiTarget);
  1570. if (dwError == ERROR_SUCCESS)
  1571. {
  1572. // note that attributes with the comment ignored are ignored by NetUserSetInfo
  1573. // setting to target value just so that they have a valid value
  1574. ui.usri3_name = puiTarget->usri3_name; // ignored
  1575. ui.usri3_password = NULL; // must not set during copy properties
  1576. ui.usri3_password_age = puiTarget->usri3_password_age; // ignored
  1577. ui.usri3_priv = puiTarget->usri3_priv; // ignored
  1578. ui.usri3_home_dir = puiTarget->usri3_home_dir;
  1579. ui.usri3_comment = ISEXCLUDE(description) ? puiTarget->usri3_comment : puiSource->usri3_comment;
  1580. ui.usri3_flags = puiSource->usri3_flags;
  1581. // translate a local account to a domain account
  1582. ui.usri3_flags &= ~UF_TEMP_DUPLICATE_ACCOUNT;
  1583. // disable the account in case no password has been set
  1584. //ui.usri3_flags |= UF_ACCOUNTDISABLE;
  1585. ui.usri3_script_path = puiTarget->usri3_script_path;
  1586. ui.usri3_auth_flags = puiTarget->usri3_auth_flags; // ignored
  1587. ui.usri3_full_name = ISEXCLUDE(displayName) ? puiTarget->usri3_full_name : puiSource->usri3_full_name;
  1588. ui.usri3_usr_comment = ISEXCLUDE(comment) ? puiTarget->usri3_usr_comment : puiSource->usri3_usr_comment;
  1589. ui.usri3_parms = puiTarget->usri3_parms;
  1590. ui.usri3_workstations = puiTarget->usri3_workstations;
  1591. ui.usri3_last_logon = puiTarget->usri3_last_logon; // ignored
  1592. ui.usri3_last_logoff = puiTarget->usri3_last_logoff;
  1593. ui.usri3_acct_expires = puiTarget->usri3_acct_expires;
  1594. ui.usri3_max_storage = puiTarget->usri3_max_storage;
  1595. ui.usri3_units_per_week = puiTarget->usri3_units_per_week; // ignored
  1596. ui.usri3_logon_hours = puiTarget->usri3_logon_hours;
  1597. ui.usri3_bad_pw_count = puiTarget->usri3_bad_pw_count; // ignored
  1598. ui.usri3_num_logons = puiTarget->usri3_num_logons; // ignored
  1599. ui.usri3_logon_server = puiTarget->usri3_logon_server; // ignored
  1600. ui.usri3_country_code = puiTarget->usri3_country_code;
  1601. ui.usri3_code_page = puiTarget->usri3_code_page;
  1602. ui.usri3_user_id = puiTarget->usri3_user_id; // ignored
  1603. ui.usri3_primary_group_id = puiTarget->usri3_primary_group_id;
  1604. ui.usri3_profile = puiTarget->usri3_profile;
  1605. ui.usri3_home_dir_drive = puiTarget->usri3_home_dir_drive;
  1606. ui.usri3_password_expired = puiTarget->usri3_password_expired;
  1607. dwError = NetUserSetInfo(sTargetServer, sTargetSam, 3, (LPBYTE)&ui, NULL);
  1608. }
  1609. if (puiTarget)
  1610. {
  1611. NetApiBufferFree(puiTarget);
  1612. }
  1613. if (puiSource)
  1614. {
  1615. NetApiBufferFree(puiSource);
  1616. }
  1617. }
  1618. }
  1619. else
  1620. {
  1621. _ASSERT(FALSE);
  1622. }
  1623. return HRESULT_FROM_WIN32(dwError);
  1624. }
  1625. /*********************************************************************
  1626. * *
  1627. * Written by: Paul Thompson *
  1628. * Date: 31 OCT 2000 *
  1629. * *
  1630. * This function is responsible for copying all properties, from *
  1631. * an incoming varset of properties, into a new varset but excluding *
  1632. * those properties listed in a given exclusion list. The exclusion *
  1633. * list is a comma-seperated string of property names. *
  1634. * *
  1635. *********************************************************************/
  1636. //BEGIN ExcludeProperties
  1637. STDMETHODIMP CObjPropBuilder::ExcludeProperties(
  1638. BSTR sExclusionList, //in- list of props to exclude
  1639. IUnknown *pPropSet, //in -Varset listing all the props to copy
  1640. IUnknown **ppUnk //out - Varset with all the props except those excluded
  1641. )
  1642. {
  1643. /* local variables */
  1644. IVarSetPtr pVarsetNew = *ppUnk;
  1645. IVarSetPtr pVarset = pPropSet;
  1646. SAFEARRAY * keys;
  1647. SAFEARRAY * vals;
  1648. long lRet;
  1649. VARIANT var;
  1650. _variant_t varEmpty;
  1651. BOOL bFound = FALSE;
  1652. HRESULT hr;
  1653. /* function body */
  1654. VariantInit(&var);
  1655. //retrieve all item in the incoming varset
  1656. hr = pVarset->getItems(L"", L"", 1, 10000, &keys, &vals, &lRet);
  1657. if ( FAILED (hr) )
  1658. return hr;
  1659. //get each property name and if it is not in the exclusion list
  1660. //place it in the new varset
  1661. _bstr_t keyName;
  1662. for ( long x = 0; x < lRet; x++ )
  1663. {
  1664. //get the property name
  1665. ::SafeArrayGetElement(keys, &x, &var);
  1666. keyName = var.bstrVal;
  1667. VariantClear(&var);
  1668. //see if this name is in the exclusion list
  1669. bFound = IsStringInDelimitedString((WCHAR*)sExclusionList,
  1670. (WCHAR*)keyName,
  1671. L',');
  1672. //if the property was not found in the exclusion list, place it
  1673. //in the new varset
  1674. if (!bFound)
  1675. pVarsetNew->put(keyName, varEmpty);
  1676. }//end for each property
  1677. SafeArrayDestroy(keys);
  1678. SafeArrayDestroy(vals);
  1679. return S_OK;
  1680. }
  1681. //END ExcludeProperties
  1682. //-----------------------------------------------------------------------------
  1683. // GetNonBaseProperties
  1684. //
  1685. // Synopsis
  1686. // Retrieves list of properties for object classes that ADMT migrates and that
  1687. // are not marked as being part of the base schema.
  1688. //
  1689. // Arguments
  1690. // IN bstrDomainName - the name of the domain to query
  1691. // OUT pbstrPropertyList - a comma separated list of attributes that are not
  1692. // base schema attributes
  1693. //
  1694. // Return Value
  1695. // Standard HRESULT return codes.
  1696. //-----------------------------------------------------------------------------
  1697. STDMETHODIMP CObjPropBuilder::GetNonBaseProperties(BSTR bstrDomainName, BSTR* pbstrPropertyList)
  1698. {
  1699. typedef std::set<tstring> StringSet;
  1700. static PCTSTR s_pszClasses[] = { _T("user"), _T("inetOrgPerson"), _T("group"), _T("computer") };
  1701. HRESULT hr = S_OK;
  1702. try
  1703. {
  1704. //
  1705. // Retrieve all mandatory and optional properties for all object classes that ADMT migrates.
  1706. //
  1707. StringSet setProperties;
  1708. for (size_t iClass = 0; iClass < countof(s_pszClasses); iClass++)
  1709. {
  1710. IVarSetPtr spVarSet(__uuidof(VarSet));
  1711. IUnknownPtr spunk(spVarSet);
  1712. IUnknown* punk = spunk;
  1713. HRESULT hrEnum = GetClassPropEnum(_bstr_t(s_pszClasses[iClass]), bstrDomainName, 5L, &punk);
  1714. if (SUCCEEDED(hrEnum))
  1715. {
  1716. IEnumVARIANTPtr spEnum = spVarSet->_NewEnum;
  1717. if (spEnum)
  1718. {
  1719. VARIANT varKey;
  1720. VariantInit(&varKey);
  1721. while (spEnum->Next(1UL, &varKey, NULL) == S_OK)
  1722. {
  1723. //
  1724. // The returned VarSet contains two sets of attribute mapping. The first set of values maps
  1725. // OIDs to lDAPDisplayNames. The second set of values maps lDAPDisplayNames prefixed with
  1726. // either MandatoryProperties or OptionalProperties to OIDs. Therefore don't include
  1727. // the second set of values.
  1728. //
  1729. if (V_BSTR(&varKey) && (wcsncmp(V_BSTR(&varKey), L"Man", 3) != 0) && (wcsncmp(V_BSTR(&varKey), L"Opt", 3) != 0))
  1730. {
  1731. //
  1732. // If lDAPDisplayName value defined then add to set of properties.
  1733. //
  1734. // The VarSet generates a series of hierarchical values based on the period character
  1735. // as follows. Therefore only the leaf entries actually contain lDAPDisplayName
  1736. // values.
  1737. //
  1738. // 2002-10-21 18:05:52 [0] <Empty>
  1739. // 2002-10-21 18:05:52 [0.9] <Empty>
  1740. // 2002-10-21 18:05:52 [0.9.2342] <Empty>
  1741. // 2002-10-21 18:05:52 [0.9.2342.19200300] <Empty>
  1742. // 2002-10-21 18:05:52 [0.9.2342.19200300.100] <Empty>
  1743. // 2002-10-21 18:05:52 [0.9.2342.19200300.100.1] <Empty>
  1744. // 2002-10-21 18:05:52 [0.9.2342.19200300.100.1.1] uid
  1745. //
  1746. _variant_t vntValue = spVarSet->get(V_BSTR(&varKey));
  1747. if (V_VT(&vntValue) == VT_BSTR)
  1748. {
  1749. setProperties.insert(tstring(V_BSTR(&vntValue)));
  1750. }
  1751. }
  1752. VariantClear(&varKey);
  1753. }
  1754. }
  1755. }
  1756. }
  1757. //
  1758. // Generate a comma separated list of lDAPDisplayNames of
  1759. // attributes that are not base schema attributes.
  1760. //
  1761. tstring strAttributes;
  1762. for (StringSet::const_iterator it = setProperties.begin(); it != setProperties.end(); ++it)
  1763. {
  1764. const tstring& strProperty = *it;
  1765. if (strProperty.empty() == false)
  1766. {
  1767. bool bSystemFlag;
  1768. bool bBaseObject = false;
  1769. IsPropSystemOnly(strProperty.c_str(), bstrDomainName, bSystemFlag, &bBaseObject);
  1770. if (bBaseObject == false)
  1771. {
  1772. if (!strAttributes.empty())
  1773. {
  1774. strAttributes += _T(",");
  1775. }
  1776. strAttributes += strProperty;
  1777. }
  1778. }
  1779. }
  1780. *pbstrPropertyList = _bstr_t(strAttributes.c_str()).copy();
  1781. }
  1782. catch (_com_error& ce)
  1783. {
  1784. hr = ce.Error();
  1785. }
  1786. return hr;
  1787. }
  1788. //------------------------------------------------------------------------------
  1789. // GetSidAndRidFromVariant Function
  1790. //
  1791. // Synopsis
  1792. // Retrieves the domain SID and object RID as strings.
  1793. //
  1794. // Arguments
  1795. // IN varSid - SID as an array of bytes (this is the form received from ADSI)
  1796. // OUT strSid - domain SID as a string
  1797. // OUT strRid - object RID as a string
  1798. //
  1799. // Return
  1800. // True if successful otherwise false.
  1801. //------------------------------------------------------------------------------
  1802. bool __stdcall CObjPropBuilder::GetSidAndRidFromVariant(const VARIANT& varSid, _bstr_t& strSid, _bstr_t& strRid)
  1803. {
  1804. bool bGet = false;
  1805. if ((V_VT(&varSid) == (VT_ARRAY|VT_UI1)) && varSid.parray)
  1806. {
  1807. PSID pSid = SafeCopySid((PSID)varSid.parray->pvData);
  1808. if (pSid)
  1809. {
  1810. PUCHAR puchCount = GetSidSubAuthorityCount(pSid);
  1811. DWORD dwCount = static_cast<DWORD>(*puchCount);
  1812. PDWORD pdwRid = GetSidSubAuthority(pSid, dwCount - 1);
  1813. DWORD dwRid = *pdwRid;
  1814. --(*puchCount);
  1815. LPTSTR pszSid = NULL;
  1816. if (ConvertSidToStringSid(pSid, &pszSid))
  1817. {
  1818. strSid = pszSid;
  1819. strRid = _variant_t(long(dwRid));
  1820. LocalFree(pszSid);
  1821. if ((PCWSTR)strSid && (PCWSTR)strRid)
  1822. {
  1823. bGet = true;
  1824. }
  1825. }
  1826. FreeSid(pSid);
  1827. }
  1828. }
  1829. return bGet;
  1830. }