Source code of Windows XP (NT5)
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.

1541 lines
62 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. StringLoader gString;
  34. //#import "\bin\NetEnum.tlb" no_namespace
  35. //#import "\bin\DBManager.tlb" no_namespace
  36. #import "NetEnum.tlb" no_namespace
  37. #import "DBMgr.tlb" no_namespace
  38. #ifndef IADsPtr
  39. _COM_SMARTPTR_TYPEDEF(IADs, IID_IADs);
  40. #endif
  41. TErrorDct err;
  42. TError & errCommon = err;
  43. /////////////////////////////////////////////////////////////////////////////
  44. // CObjPropBuilder
  45. BOOL CObjPropBuilder::GetProgramDirectory(
  46. WCHAR * filename // out - buffer that will contain path to program directory
  47. )
  48. {
  49. DWORD rc = 0;
  50. BOOL bFound = FALSE;
  51. TRegKey key;
  52. rc = key.OpenRead(GET_STRING(IDS_HKLM_DomainAdmin_Key),HKEY_LOCAL_MACHINE);
  53. if ( ! rc )
  54. {
  55. rc = key.ValueGetStr(L"Directory",filename,MAX_PATH);
  56. if ( ! rc )
  57. {
  58. if ( *filename )
  59. bFound = TRUE;
  60. }
  61. }
  62. if ( ! bFound )
  63. {
  64. UStrCpy(filename,L"C:\\"); // if all else fails, default to the C: drive
  65. }
  66. return bFound;
  67. }
  68. //---------------------------------------------------------------------------
  69. // GetClassPropEnum: This function fills the varset with all the properties
  70. // for a given class in a given domain. The Varset has
  71. // values stored by the OID and then by their name with
  72. // MandatoryProperties/OptionalProperties as parent nodes
  73. // as applicable.
  74. //---------------------------------------------------------------------------
  75. STDMETHODIMP CObjPropBuilder::GetClassPropEnum(
  76. BSTR sClassName, //in -Class name to get the properties for
  77. BSTR sDomainName, //in -Domain name
  78. long lVer, //in -The domain version
  79. IUnknown **ppVarset //out-Varset filled with the properties
  80. )
  81. {
  82. // This function goes through the list of properties for the specified class in specified domain
  83. // Builds the given varset with the properties and their values.
  84. WCHAR sAdsPath[LEN_Path];
  85. HRESULT hr = E_INVALIDARG;
  86. _variant_t dnsName;
  87. if ( lVer > 4 )
  88. {
  89. // For this Domain get the default naming context
  90. wsprintfW(sAdsPath, L"LDAP://%s/rootDSE", sDomainName);
  91. IADs * pAds = NULL;
  92. hr = ADsGetObject(sAdsPath, IID_IADs, (void**)&pAds);
  93. if ( SUCCEEDED(hr) )
  94. {
  95. hr = pAds->Get(L"defaultNamingContext", &dnsName);
  96. }
  97. if ( SUCCEEDED(hr) )
  98. {
  99. wcscpy(m_sNamingConvention, dnsName.bstrVal);
  100. // Build LDAP path to the schema
  101. wcscpy(sAdsPath, L"LDAP://");
  102. wcscat(sAdsPath, sDomainName);
  103. wcscat(sAdsPath, L"/");
  104. wcscat(sAdsPath, sClassName);
  105. wcscat(sAdsPath, L", schema");
  106. hr = S_OK;
  107. }
  108. if ( pAds )
  109. pAds->Release();
  110. }
  111. else
  112. {
  113. wsprintf(sAdsPath, L"WinNT://%s/Schema/%s", sDomainName, sClassName);
  114. hr = S_OK;
  115. }
  116. if ( SUCCEEDED(hr) )
  117. {
  118. wcscpy(m_sDomainName, sDomainName);
  119. m_lVer = lVer;
  120. // Get the class object.
  121. IADsClass * pIClass=NULL;
  122. hr = ADsGetObject(sAdsPath, IID_IADsClass, (void **)&pIClass);
  123. // Without the object we can not go any further so we will stop here.
  124. if ( SUCCEEDED(hr) )
  125. {
  126. // Let the Auxilliary function take care of Getting properties and filling up the Varset.
  127. hr = GetClassProperties( pIClass, *ppVarset );
  128. pIClass->Release();
  129. }
  130. }
  131. return hr;
  132. }
  133. //---------------------------------------------------------------------------
  134. // GetClassProperties: This function fills the varset with properties of the class.
  135. //---------------------------------------------------------------------------
  136. HRESULT CObjPropBuilder::GetClassProperties(
  137. IADsClass * pClass, //in -IADsClass * to the class
  138. IUnknown *& pVarSet //out-Varset to fill the properties
  139. )
  140. {
  141. HRESULT hr;
  142. _variant_t variant;
  143. VariantInit(&variant);
  144. // mandatory properties
  145. hr = pClass->get_MandatoryProperties(&variant);
  146. if ( SUCCEEDED(hr) )
  147. {
  148. hr = FillupVarsetFromVariant(pClass, &variant, L"MandatoryProperties", pVarSet);
  149. }
  150. VariantClear(&variant);
  151. // optional properties
  152. hr = pClass->get_OptionalProperties(&variant);
  153. if ( SUCCEEDED(hr) )
  154. {
  155. hr = FillupVarsetFromVariant(pClass, &variant, L"OptionalProperties", pVarSet);
  156. }
  157. VariantClear(&variant);
  158. return hr;
  159. }
  160. //---------------------------------------------------------------------------
  161. // FillupVarsetFromVariant: This function fills in the Varset property info
  162. // with the info in a variant.
  163. //---------------------------------------------------------------------------
  164. HRESULT CObjPropBuilder::FillupVarsetFromVariant(
  165. IADsClass * pClass, //in -IADsClass * to the class
  166. VARIANT * pVar, //in -Variant to lookup info.
  167. BSTR sPropType, //in -Type of the property
  168. IUnknown *& pVarSet //out-Varset with the info,
  169. )
  170. {
  171. HRESULT hr;
  172. BSTR sPropName;
  173. USHORT type;
  174. type = pVar->vt;
  175. if ( type & VT_ARRAY )
  176. {
  177. if ( type == (VT_ARRAY|VT_VARIANT) )
  178. {
  179. hr = FillupVarsetFromVariantArray(pClass, pVar->parray, sPropType, pVarSet);
  180. if ( FAILED ( hr ) )
  181. return hr;
  182. }
  183. else
  184. return S_FALSE;
  185. }
  186. else
  187. {
  188. if ( type == VT_BSTR )
  189. {
  190. // Only other thing that the VARIANT could be is a BSTR.
  191. sPropName = pVar->bstrVal;
  192. hr = FillupVarsetWithProperty(sPropName, sPropType, pVarSet);
  193. if ( FAILED ( hr ) )
  194. return hr;
  195. }
  196. else
  197. return S_FALSE;
  198. }
  199. return S_OK;
  200. }
  201. //---------------------------------------------------------------------------
  202. // FillupVarsetWithProperty: Given the class prop name and the prop type this
  203. // function fills info into the varset.
  204. //---------------------------------------------------------------------------
  205. HRESULT CObjPropBuilder::FillupVarsetWithProperty(
  206. BSTR sPropName, //in -Property name
  207. BSTR sPropType, //in -Property type
  208. IUnknown *& pVarSet //out-Varset to fill in the information
  209. )
  210. {
  211. if ( wcslen(sPropName) == 0 )
  212. return S_OK;
  213. // This function fills up the Varset for a given property
  214. HRESULT hr;
  215. _variant_t var;
  216. _variant_t varSO;
  217. _variant_t varID;
  218. IVarSetPtr pVar;
  219. WCHAR sAdsPath[LEN_Path];
  220. IADsProperty * pProp = NULL;
  221. BSTR objID = NULL;
  222. BSTR sPath = NULL;
  223. BSTR sClass = NULL;
  224. WCHAR sPropPut[LEN_Path];
  225. // Get the OID for the property
  226. // First we need a IADsProperty pointer to the property schema
  227. if ( m_lVer > 4 )
  228. {
  229. wcscpy(sAdsPath, L"LDAP://");
  230. wcscat(sAdsPath, m_sDomainName);
  231. wcscat(sAdsPath, L"/");
  232. wcscat(sAdsPath, sPropName);
  233. wcscat(sAdsPath, L", schema");
  234. }
  235. else
  236. {
  237. wsprintf(sAdsPath, L"WinNT://%s/Schema/%s", m_sDomainName, sPropName);
  238. }
  239. hr = ADsGetObject(sAdsPath, IID_IADsProperty, (void **)&pProp);
  240. // Get the objectID for the property
  241. hr = pProp->get_OID(&objID);
  242. hr = pProp->get_ADsPath(&sPath);
  243. pProp->get_Class(&sClass);
  244. // Get the varset from the parameter
  245. pVar = pVarSet;
  246. // Set up the variant to put into the varset
  247. var = objID;
  248. // Put the value into the varset
  249. wcscpy(sPropPut, sPropType);
  250. wcscat(sPropPut, L".");
  251. wcscat(sPropPut, sPropName);
  252. hr = pVar->put(sPropPut, var);
  253. // Set up the variant to put into the varset
  254. var = sPropName;
  255. // Put the value with the ObjectID as the key.
  256. hr = pVar->put(objID, var);
  257. SysFreeString(objID);
  258. SysFreeString(sPath);
  259. SysFreeString(sClass);
  260. pProp->Release();
  261. return hr;
  262. }
  263. //---------------------------------------------------------------------------
  264. // FillupVarsetFromVariantArray: Given the class, SafeArray of props and the
  265. // prop type this function fills info into the
  266. // varset.
  267. //---------------------------------------------------------------------------
  268. HRESULT CObjPropBuilder::FillupVarsetFromVariantArray(
  269. IADsClass * pClass, //in -IADsClass* to the class in question
  270. SAFEARRAY * pArray, //in -SafeArray pointer with the prop names
  271. BSTR sPropType, //in -Property type
  272. IUnknown *& pVarSet //out-Varset with the information filled in
  273. )
  274. {
  275. HRESULT hr = S_FALSE;
  276. DWORD nDim; // number of dimensions, must be one
  277. LONG nLBound; // lower bound of array
  278. LONG nUBound; // upper bound of array
  279. LONG indices[1]; // array indices to access elements
  280. DWORD rc; // SafeArray return code
  281. VARIANT variant; // one element in array
  282. nDim = SafeArrayGetDim(pArray);
  283. VariantInit(&variant);
  284. if ( nDim == 1 )
  285. {
  286. SafeArrayGetLBound(pArray, 1, &nLBound);
  287. SafeArrayGetUBound(pArray, 1, &nUBound);
  288. for ( indices[0] = nLBound, rc = 0;
  289. indices[0] <= nUBound && !rc;
  290. indices[0] += 1 )
  291. {
  292. rc = SafeArrayGetElement(pArray,indices,&variant);
  293. if ( !rc )
  294. hr = FillupVarsetFromVariant(pClass, &variant, sPropType, pVarSet);
  295. VariantClear(&variant);
  296. }
  297. }
  298. return hr;
  299. }
  300. //---------------------------------------------------------------------------
  301. // GetProperties: This function gets the values for the properties specified
  302. // in the varset in the ADS_ATTR_INFO array for the object
  303. // specified in a given domain.
  304. //---------------------------------------------------------------------------
  305. DWORD CObjPropBuilder::GetProperties(
  306. BSTR sObjPath, //in -Path to the object for which we are getting the props
  307. // BSTR sDomainName, //in -Domain name where the object resides
  308. IVarSet * pVar, //in -Varset listing all the property names that we need to get.
  309. ADS_ATTR_INFO*& pAttrInfo //out-Attribute values for the property
  310. )
  311. {
  312. // Construct the LDAP path.
  313. WCHAR sPath[LEN_Path];
  314. VARIANT var;
  315. // Get the path to the source object
  316. safecopy(sPath, sObjPath);
  317. // Get the Varset pointer and enumerate the properties asked for and build an array to send to IADsDirectory
  318. long lRet=0;
  319. SAFEARRAY * keys = NULL;
  320. SAFEARRAY * vals = NULL;
  321. IDirectoryObject * pDir;
  322. DWORD dwRet = 0;
  323. LPWSTR * pAttrNames = new LPWSTR[pVar->GetCount()];
  324. HRESULT hr = pVar->raw_getItems(NULL, NULL, 1, 10000, &keys, &vals, &lRet);
  325. VariantInit(&var);
  326. if (!pAttrNames)
  327. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  328. if ( SUCCEEDED( hr ) )
  329. {
  330. // Build the Attribute array from the varset.
  331. for ( long x = 0; x < lRet; x++ )
  332. {
  333. ::SafeArrayGetElement(keys, &x, &var);
  334. int len = wcslen(var.bstrVal);
  335. pAttrNames[x] = new WCHAR[len + 2];
  336. if (!(pAttrNames[x]))
  337. {
  338. for (int j=0; j<x; j++)
  339. delete [] pAttrNames[j];
  340. delete [] pAttrNames;
  341. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  342. }
  343. wcscpy(pAttrNames[x], var.bstrVal);
  344. VariantClear(&var);
  345. }
  346. // Now get the IDirectoryObject Ptr for the given object.
  347. hr = ADsGetObject(sPath, IID_IDirectoryObject, (void **)&pDir);
  348. if ( FAILED( hr ) )
  349. {
  350. dwRet = 0;
  351. }
  352. else
  353. {
  354. // Get the Property values for the object.
  355. hr = pDir->GetObjectAttributes(pAttrNames, lRet, &pAttrInfo, &dwRet);
  356. pDir->Release();
  357. }
  358. for ( long y = 0 ; y < lRet; y++ )
  359. {
  360. delete [] pAttrNames[y];
  361. }
  362. SafeArrayDestroy(keys);
  363. SafeArrayDestroy(vals);
  364. }
  365. delete [] pAttrNames;
  366. return dwRet;
  367. }
  368. //---------------------------------------------------------------------------
  369. // GetObjectProperty: This function takes in a varset with property names.
  370. // Then it fills up the varset with values by getting them
  371. // from the Object.
  372. //---------------------------------------------------------------------------
  373. STDMETHODIMP CObjPropBuilder::GetObjectProperty(
  374. BSTR sobjSubPath, //in- LDAP Sub path to the object
  375. // BSTR sDomainName, //in- Domain name where the object resides
  376. IUnknown **ppVarset //out-Varset filled with the information
  377. )
  378. {
  379. IVarSetPtr pVar;
  380. ADS_ATTR_INFO * pAttrInfo=NULL;
  381. pVar = *ppVarset;
  382. // Get the properties from the directory
  383. DWORD dwRet = GetProperties(sobjSubPath, /*sDomainName,*/ pVar, pAttrInfo);
  384. if ( dwRet > 0 )
  385. return dwRet;
  386. // Go through the property values and put them into the varset.
  387. for ( DWORD dwIdx = 0; dwIdx < dwRet; dwIdx++ )
  388. {
  389. SetValuesInVarset(pAttrInfo[dwIdx], pVar);
  390. }
  391. if ( pAttrInfo )
  392. FreeADsMem( pAttrInfo );
  393. return S_OK;
  394. }
  395. //---------------------------------------------------------------------------
  396. // SetValuesInVarset: This function sets the values for the properties into
  397. // a varset.
  398. //---------------------------------------------------------------------------
  399. void CObjPropBuilder::SetValuesInVarset(
  400. ADS_ATTR_INFO attrInfo, //in -The property value in ADS_ATTR_INFO struct.
  401. IVarSetPtr pVar //in,out -The VarSet where we need to put the values
  402. )
  403. {
  404. // This function extraces values from ADS_ATTR_INFO struct and puts it into the Varset.
  405. LPWSTR sKeyName = attrInfo.pszAttrName;
  406. _variant_t var;
  407. // Got through each value ( in case of multivalued entries ) and depending on the type put it into the varset
  408. // the way we put in single value entries is to put the propertyName as key and put its value as value. Although
  409. // in case of a multivalued entry we put PropertyName.### and each of the values in it.
  410. for ( DWORD dw = 0; dw < attrInfo.dwNumValues; dw++)
  411. {
  412. var = L"";
  413. if ( attrInfo.dwNumValues > 1 )
  414. // Multivalued property name
  415. wsprintfW(sKeyName, L"%s.%d", attrInfo.pszAttrName, dw);
  416. else
  417. // Single value keyname.
  418. wcscpy(sKeyName,attrInfo.pszAttrName);
  419. // Fill in the values as per the varset.
  420. switch (attrInfo.dwADsType)
  421. {
  422. case ADSTYPE_DN_STRING : var.vt = VT_BSTR;
  423. var.bstrVal = ::SysAllocString(attrInfo.pADsValues[dw].DNString);
  424. break;
  425. case ADSTYPE_CASE_EXACT_STRING : var.vt = VT_BSTR;
  426. var.bstrVal = attrInfo.pADsValues[dw].CaseExactString;
  427. break;
  428. case ADSTYPE_CASE_IGNORE_STRING : var.vt = VT_BSTR;
  429. var.bstrVal = ::SysAllocString(attrInfo.pADsValues[dw].CaseIgnoreString);
  430. break;
  431. case ADSTYPE_PRINTABLE_STRING : var.vt = VT_BSTR;
  432. var.bstrVal = ::SysAllocString(attrInfo.pADsValues[dw].PrintableString);
  433. break;
  434. case ADSTYPE_NUMERIC_STRING : var.vt = VT_BSTR;
  435. var.bstrVal = ::SysAllocString(attrInfo.pADsValues[dw].NumericString);
  436. break;
  437. case ADSTYPE_INTEGER : var.vt = VT_I4;
  438. var.lVal = attrInfo.pADsValues[dw].Integer;
  439. break;
  440. case ADSTYPE_OCTET_STRING : {
  441. var.vt = VT_ARRAY | VT_UI1;
  442. long * pData;
  443. DWORD dwLength = attrInfo.pADsValues[dw].OctetString.dwLength;
  444. SAFEARRAY * sA;
  445. SAFEARRAYBOUND rgBound = {dwLength, 0};
  446. sA = ::SafeArrayCreate(VT_UI1, 1, &rgBound);
  447. ::SafeArrayAccessData( sA, (void**)&pData);
  448. for ( DWORD i = 0; i < dwLength; i++ )
  449. pData[i] = attrInfo.pADsValues[dw].OctetString.lpValue[i];
  450. ::SafeArrayUnaccessData(sA);
  451. var.parray = sA;
  452. }
  453. break;
  454. /* case ADSTYPE_UTC_TIME : wcscat(sKeyName,L".ERROR");
  455. var.vt = VT_BSTR;
  456. var.bstrVal = ::SysAllocString(L"Date not supported.");
  457. break;
  458. case ADSTYPE_LARGE_INTEGER : wcscat(sKeyName,L".ERROR");
  459. var.vt = VT_BSTR;
  460. var.bstrVal = ::SysAllocString(L"Large Integer not supported.");
  461. break;
  462. case ADSTYPE_PROV_SPECIFIC : wcscat(sKeyName,L".ERROR");
  463. var.vt = VT_BSTR;
  464. var.bstrVal = ::SysAllocString(L"Provider specific strings not supported.");
  465. break;
  466. case ADSTYPE_OBJECT_CLASS : var.vt = VT_BSTR;
  467. var.bstrVal = ::SysAllocString(attrInfo.pADsValues[dw].ClassName);
  468. break;
  469. case ADSTYPE_CASEIGNORE_LIST : wcscat(sKeyName,L".ERROR");
  470. var.vt = VT_BSTR;
  471. var.bstrVal = L"Case ignore lists are not supported.";
  472. break;
  473. case ADSTYPE_OCTET_LIST : wcscat(sKeyName,L".ERROR");
  474. var.vt = VT_BSTR;
  475. var.bstrVal = L"Octet lists are not supported.";
  476. break;
  477. case ADSTYPE_PATH : wcscat(sKeyName,L".ERROR");
  478. var.vt = VT_BSTR;
  479. var.bstrVal = L"Path type not supported.";
  480. break;
  481. case ADSTYPE_POSTALADDRESS : wcscat(sKeyName,L".ERROR");
  482. var.vt = VT_BSTR;
  483. var.bstrVal = L"Postal addresses are not supported.";
  484. break;
  485. case ADSTYPE_TIMESTAMP : var.vt = VT_UI4;
  486. var.lVal = attrInfo.pADsValues[dw].UTCTime;
  487. break;
  488. case ADSTYPE_BACKLINK : wcscat(sKeyName,L".ERROR");
  489. var.vt = VT_BSTR;
  490. var.bstrVal = L"Backlink is not supported.";
  491. break;
  492. case ADSTYPE_TYPEDNAME : wcscat(sKeyName,L".ERROR");
  493. var.vt = VT_BSTR;
  494. var.bstrVal = L"Typed name not supported.";
  495. break;
  496. case ADSTYPE_HOLD : wcscat(sKeyName,L".ERROR");
  497. var.vt = VT_BSTR;
  498. var.bstrVal = L"Hold not supported.";
  499. break;
  500. case ADSTYPE_NETADDRESS : wcscat(sKeyName,L".ERROR");
  501. var.vt = VT_BSTR;
  502. var.bstrVal = L"NetAddress not supported.";
  503. break;
  504. case ADSTYPE_REPLICAPOINTER : wcscat(sKeyName,L".ERROR");
  505. var.vt = VT_BSTR;
  506. var.bstrVal = L"Replica pointer not supported.";
  507. break;
  508. case ADSTYPE_FAXNUMBER : wcscat(sKeyName,L".ERROR");
  509. var.vt = VT_BSTR;
  510. var.bstrVal = L"Faxnumber not supported.";
  511. break;
  512. case ADSTYPE_EMAIL : wcscat(sKeyName,L".ERROR");
  513. var.vt = VT_BSTR;
  514. var.bstrVal = L"Email not supported.";
  515. break;
  516. case ADSTYPE_NT_SECURITY_DESCRIPTOR : wcscat(sKeyName,L".ERROR");
  517. var.vt = VT_BSTR;
  518. var.bstrVal = L"Security Descriptor not supported.";
  519. break;
  520. */
  521. default : wcscat(sKeyName,GET_STRING(DCTVS_SUB_ERROR));
  522. var.vt = VT_BSTR;
  523. var.bstrVal = GET_BSTR(IDS_UNKNOWN_TYPE);
  524. break;
  525. }
  526. pVar->put(sKeyName, var);
  527. if ( attrInfo.dwADsType == ADSTYPE_OCTET_STRING)
  528. var.vt = VT_EMPTY;
  529. }
  530. }
  531. //---------------------------------------------------------------------------
  532. // CopyProperties: This function copies properties, specified in the varset,
  533. // by getting the values
  534. // from the source account and the setting the values in
  535. // the target account.
  536. //---------------------------------------------------------------------------
  537. STDMETHODIMP CObjPropBuilder::CopyProperties(
  538. BSTR sSourcePath, //in -Source path to the object
  539. BSTR sSourceDomain, //in -Source domain name
  540. BSTR sTargetPath, //in -Target object LDAP path
  541. BSTR sTargetDomain, //in -Target domain name
  542. IUnknown *pPropSet, //in -Varset listing all the props to copy
  543. IUnknown *pDBManager //in -DB Manager that has a open connection to the DB.
  544. )
  545. {
  546. IIManageDBPtr pDb = pDBManager;
  547. ADS_ATTR_INFO * pAttrInfo = NULL;
  548. IVarSetPtr pVarset = pPropSet;
  549. HRESULT hr = S_OK;
  550. // Get properties from the source
  551. DWORD dwRet = GetProperties(sSourcePath, /*sSourceDomain,*/ pVarset, pAttrInfo);
  552. if ( dwRet > 0 )
  553. {
  554. TranslateDNs(pAttrInfo, dwRet, sSourceDomain, sTargetDomain, pDBManager);
  555. for ( DWORD dwIdx = 0; dwIdx < dwRet; dwIdx++)
  556. {
  557. pAttrInfo[dwIdx].dwControlCode = ADS_ATTR_UPDATE;
  558. //we do not want to copy over the account enable\disable bit since we want this target
  559. //account to remain disabled at this time, so make sure that bit is cleared
  560. if (!_wcsicmp(pAttrInfo[dwIdx].pszAttrName, L"userAccountControl"))
  561. {
  562. if (pAttrInfo[dwIdx].dwADsType == ADSTYPE_INTEGER)
  563. pAttrInfo[dwIdx].pADsValues->Integer |= UF_ACCOUNTDISABLE;
  564. }
  565. }
  566. // Set the source properties in the target.
  567. hr = SetProperties(sTargetPath, /*sTargetDomain,*/ pAttrInfo, dwRet);
  568. }
  569. if ( pAttrInfo )
  570. FreeADsMem( pAttrInfo );
  571. return hr;
  572. }
  573. //---------------------------------------------------------------------------
  574. // SetProperties: This function sets the properties for a given object from
  575. // the attr info array.
  576. //---------------------------------------------------------------------------
  577. HRESULT CObjPropBuilder::SetProperties(
  578. BSTR sTargetPath, //in -Target object path.
  579. // BSTR sTargetDomain, //in - Target domain name
  580. ADS_ATTR_INFO * pAttrInfo, //in - ADSATTRINFO array with values for props
  581. DWORD dwItems //in - number of properties in the array
  582. )
  583. {
  584. IDirectoryObject * pDir;
  585. DWORD dwRet=0;
  586. IVarSetPtr pSucc(__uuidof(VarSet));
  587. IVarSetPtr pFail(__uuidof(VarSet));
  588. // Get the IDirectory Object interface to the Object.
  589. HRESULT hr = ADsGetObject(sTargetPath, IID_IDirectoryObject, (void**) &pDir);
  590. if ( FAILED(hr) )
  591. return hr;
  592. // Set the Object Attributes.
  593. hr = pDir->SetObjectAttributes(pAttrInfo, dwItems, &dwRet);
  594. if ( FAILED(hr) )
  595. {
  596. // we are going to try it one at a time and see what causes problems
  597. for (DWORD dw = 0; dw < dwItems; dw++)
  598. {
  599. hr = pDir->SetObjectAttributes(&pAttrInfo[dw], 1, &dwRet);
  600. _bstr_t x = pAttrInfo[dw].pszAttrName;
  601. _variant_t var;
  602. if ( FAILED(hr))
  603. {
  604. DWORD dwLastError;
  605. WCHAR szErrorBuf[LEN_Path];
  606. WCHAR szNameBuf[LEN_Path];
  607. //Get extended error value.
  608. HRESULT hr_return =S_OK;
  609. hr_return = ADsGetLastError( &dwLastError,
  610. szErrorBuf,
  611. LEN_Path-1,
  612. szNameBuf,
  613. LEN_Path-1);
  614. //var = szErrorBuf;
  615. var = hr;
  616. pFail->put(x, var);
  617. hr = S_OK;
  618. }
  619. else
  620. {
  621. pSucc->put(x, var);
  622. }
  623. }
  624. }
  625. pDir->Release();
  626. return hr;
  627. }
  628. //---------------------------------------------------------------------------
  629. // SetPropertiesFromVarset: This function sets values for properties from
  630. // the varset. The varset should contain the
  631. // propname (containing the val) and the
  632. // propname.Type ( containing the type of Val)
  633. //---------------------------------------------------------------------------
  634. STDMETHODIMP CObjPropBuilder::SetPropertiesFromVarset(
  635. BSTR sTargetPath, //in -LDAP path to the target object
  636. // BSTR sTragetDomain, //in - Domain name for the Target domain
  637. IUnknown *pUnk, //in - Varset to fetch the values from
  638. DWORD dwControl //in - Cotnrol code to use for Updating/Deleting etc..
  639. )
  640. {
  641. // This function loads up properties and their values from the Varset and sets them for a given user
  642. IVarSetPtr pVar;
  643. SAFEARRAY * keys;
  644. SAFEARRAY * vals;
  645. long lRet;
  646. VARIANT var;
  647. _variant_t varX;
  648. pVar = pUnk;
  649. VariantInit(&var);
  650. ADS_ATTR_INFO FAR * pAttrInfo = new ADS_ATTR_INFO[pVar->GetCount()];
  651. if (!pAttrInfo)
  652. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  653. HRESULT hr = pVar->getItems(L"", L"", 0, 10000, &keys, &vals, &lRet);
  654. if ( FAILED (hr) )
  655. {
  656. delete [] pAttrInfo;
  657. return hr;
  658. }
  659. // Build the Property Name/Value array from the varset.
  660. for ( long x = 0; x < lRet; x++ )
  661. {
  662. // Get the property name
  663. ::SafeArrayGetElement(keys, &x, &var);
  664. _bstr_t keyName = var.bstrVal;
  665. int len = wcslen(keyName);
  666. pAttrInfo[x].pszAttrName = new WCHAR[len + 2];
  667. if (!(pAttrInfo[x].pszAttrName))
  668. {
  669. for (int z=0; z<x; z++)
  670. delete [] pAttrInfo[z].pszAttrName;
  671. delete [] pAttrInfo;
  672. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  673. }
  674. wcscpy(pAttrInfo[x].pszAttrName, keyName);
  675. VariantClear(&var);
  676. // Get the property value
  677. ::SafeArrayGetElement(vals, &x, &var);
  678. keyName = keyName + _bstr_t(L".Type");
  679. varX = pVar->get(keyName);
  680. if(GetAttrInfo(varX, var, pAttrInfo[x]))
  681. {
  682. pAttrInfo[x].dwControlCode = dwControl;
  683. pAttrInfo[x].dwNumValues = 1;
  684. }
  685. VariantClear(&var);
  686. }
  687. SafeArrayDestroy(keys);
  688. SafeArrayDestroy(vals);
  689. // Once we build the array of name and property values then we call the sister function to do the rest
  690. if ( lRet > 0 ) SetProperties(sTargetPath, /*sTragetDomain,*/ pAttrInfo, lRet);
  691. // Always cleanup after yourself...
  692. for ( x = 0; x < lRet; x++ )
  693. {
  694. delete pAttrInfo[x].pADsValues;
  695. delete [] pAttrInfo[x].pszAttrName;
  696. }
  697. delete [] pAttrInfo;
  698. return S_OK;
  699. }
  700. //------------------------------------------------------------------------------
  701. // GetAttrInfo: Given a variant this function fills in the ADS_ATTR_INFO struct
  702. //------------------------------------------------------------------------------
  703. bool CObjPropBuilder::GetAttrInfo(
  704. _variant_t varX, //in - Variant containing the Type of prop
  705. _variant_t var, //in - Variant containing the Prop value
  706. ADS_ATTR_INFO& attrInfo //out - The filled up attr info structure
  707. )
  708. {
  709. switch (varX.lVal)
  710. {
  711. case ADSTYPE_DN_STRING : {
  712. attrInfo.dwADsType = ADSTYPE_DN_STRING;
  713. ADSVALUE * pAd = new ADSVALUE();
  714. if (!pAd)
  715. return false;
  716. pAd->dwType = ADSTYPE_DN_STRING;
  717. pAd->DNString = var.bstrVal;
  718. attrInfo.pADsValues = pAd;
  719. break;
  720. }
  721. case ADSTYPE_CASE_EXACT_STRING : {
  722. attrInfo.dwADsType = ADSTYPE_CASE_EXACT_STRING;
  723. ADSVALUE * pAd = new ADSVALUE();
  724. if (!pAd)
  725. return false;
  726. pAd->dwType = ADSTYPE_CASE_EXACT_STRING;
  727. pAd->CaseExactString = var.bstrVal;
  728. attrInfo.pADsValues = pAd;
  729. break;
  730. }
  731. case ADSTYPE_CASE_IGNORE_STRING : {
  732. attrInfo.dwADsType = ADSTYPE_CASE_IGNORE_STRING;
  733. ADSVALUE * pAd = new ADSVALUE();
  734. if (!pAd)
  735. return false;
  736. pAd->dwType = ADSTYPE_CASE_IGNORE_STRING;
  737. pAd->CaseIgnoreString = var.bstrVal;
  738. attrInfo.pADsValues = pAd;
  739. break;
  740. }
  741. case ADSTYPE_PRINTABLE_STRING : {
  742. attrInfo.dwADsType = ADSTYPE_PRINTABLE_STRING;
  743. ADSVALUE * pAd = new ADSVALUE();
  744. if (!pAd)
  745. return false;
  746. pAd->dwType = ADSTYPE_PRINTABLE_STRING;
  747. pAd->PrintableString = var.bstrVal;
  748. attrInfo.pADsValues = pAd;
  749. break;
  750. }
  751. case ADSTYPE_NUMERIC_STRING : {
  752. attrInfo.dwADsType = ADSTYPE_NUMERIC_STRING;
  753. ADSVALUE * pAd = new ADSVALUE();
  754. if (!pAd)
  755. return false;
  756. pAd->dwType = ADSTYPE_NUMERIC_STRING;
  757. pAd->NumericString = var.bstrVal;
  758. attrInfo.pADsValues = pAd;
  759. break;
  760. }
  761. case ADSTYPE_INTEGER : {
  762. attrInfo.dwADsType = ADSTYPE_INTEGER;
  763. ADSVALUE * pAd = new ADSVALUE();
  764. if (!pAd)
  765. return false;
  766. pAd->dwType = ADSTYPE_INTEGER;
  767. pAd->Integer = var.lVal;
  768. attrInfo.pADsValues = pAd;
  769. break;
  770. }
  771. default : {
  772. // Don't support this type then get it out of the way.
  773. return false;
  774. break;
  775. }
  776. }
  777. return true;
  778. }
  779. //------------------------------------------------------------------------------
  780. // MapProperties: Using the OID of the properties this function creates a set
  781. // of properties that are common to both source and target domain.
  782. //------------------------------------------------------------------------------
  783. STDMETHODIMP CObjPropBuilder::MapProperties(
  784. BSTR sSourceClass, //in- Source Class name
  785. BSTR sSourceDomain, //in- Source domain name
  786. long lSourceVer, //in- Source domain version
  787. BSTR sTargetClass, //in - Target class name
  788. BSTR sTargetDomain, //in - Target domain name
  789. long lTargetVer, //in - Target Domain version
  790. BOOL bIncNames, //in - flag telling if varset should include propname
  791. IUnknown **ppUnk //out - Varset with the mapped properties
  792. )
  793. {
  794. 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"));
  795. IVarSetPtr pSource(__uuidof(VarSet));
  796. IVarSetPtr pTarget(__uuidof(VarSet));
  797. IVarSetPtr pMerged = *ppUnk;
  798. IVarSetPtr pFailed(__uuidof(VarSet));
  799. IUnknown * pUnk;
  800. SAFEARRAY * keys;
  801. SAFEARRAY * vals;
  802. long lRet;
  803. VARIANT var;
  804. _variant_t varTarget;
  805. _variant_t varEmpty;
  806. bool bSystemFlag;
  807. WCHAR sPath[LEN_Path];
  808. WCHAR sProgDir[LEN_Path];
  809. bool bUnMapped = false;
  810. VariantInit(&var);
  811. GetProgramDirectory(sProgDir);
  812. wsprintf(sPath, L"%s%s", sProgDir, L"Logs\\PropMap.log");
  813. err.LogOpen(sPath,0,0);
  814. // Get the list of props for source and target first
  815. HRESULT hr = pSource->QueryInterface(IID_IUnknown, (void **)&pUnk);
  816. GetClassPropEnum(sSourceClass, sSourceDomain, lSourceVer, &pUnk);
  817. pUnk->Release();
  818. hr = pTarget->QueryInterface(IID_IUnknown, (void **)&pUnk);
  819. GetClassPropEnum(sTargetClass, sTargetDomain, lTargetVer, &pUnk);
  820. pUnk->Release();
  821. // 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
  822. hr = pSource->getItems(L"", L"", 1, 10000, &keys, &vals, &lRet);
  823. if ( FAILED (hr) )
  824. return hr;
  825. // Build the Property Name/Value array from the varset.
  826. _bstr_t val;
  827. _bstr_t keyName;
  828. for ( long x = 0; x < lRet; x++ )
  829. {
  830. // Get the property name
  831. ::SafeArrayGetElement(keys, &x, &var);
  832. keyName = var.bstrVal;
  833. VariantClear(&var);
  834. if ( lSourceVer > 4 )
  835. {
  836. // Windows 2000 domain so we map by OID
  837. if ((wcsncmp(keyName, L"Man", 3) != 0) && (wcsncmp(keyName, L"Opt", 3) != 0) )
  838. {
  839. // Only go through the OID keys to get the name of the properties.
  840. varTarget = pTarget->get(keyName);
  841. if ( varTarget.vt == VT_BSTR )
  842. {
  843. val = varTarget.bstrVal;
  844. if ((!IsPropSystemOnly(val, sTargetDomain, bSystemFlag) && (wcscmp(val, L"objectSid") != 0)
  845. && (wcscmp(val, L"sAMAccountName") != 0) && (_wcsicmp(val, L"Rid") != 0)
  846. && (wcscmp(val, L"pwdLastSet") != 0) && (wcscmp(val, L"userPassword") != 0)
  847. && (wcscmp(val, L"isCriticalSystemObject") != 0))
  848. || ( !_wcsicmp(val, L"c") || !_wcsicmp(val, L"l") || !_wcsicmp(val, L"st")
  849. || !_wcsicmp(val, L"userAccountControl") ) ) // These properties are exceptions.
  850. {
  851. if (bIncNames)
  852. pMerged->put(keyName, val);
  853. else
  854. pMerged->put(val, varEmpty);
  855. }
  856. else
  857. pFailed->put(val, varEmpty);
  858. }
  859. else if (!bIncNames)
  860. {
  861. var = pSource->get(keyName);
  862. if (var.vt == VT_BSTR) keyName = var.bstrVal;
  863. err.MsgWrite(ErrE, DCT_MSG_FAILED_TO_MAP_PROP_SSSSS, (WCHAR*) keyName, (WCHAR*) sSourceClass,
  864. (WCHAR*) sSourceDomain, (WCHAR*) sTargetClass, (WCHAR*) sTargetDomain);
  865. bUnMapped = true;
  866. }
  867. }
  868. }
  869. else
  870. {
  871. // NT4 code is the one that we map by Names.
  872. if ( keyName.length() > 0 )
  873. {
  874. WCHAR propName[LEN_Path];
  875. if (wcsncmp(keyName, L"Man", 3) == 0)
  876. wcscpy(propName, (WCHAR*) keyName+20);
  877. else
  878. wcscpy(propName, (WCHAR*) keyName+19);
  879. varTarget = pSource->get(keyName);
  880. if ( varTarget.vt == VT_BSTR )
  881. pMerged->put(propName, varEmpty);
  882. }
  883. }
  884. }
  885. SafeArrayDestroy(keys);
  886. SafeArrayDestroy(vals);
  887. err.LogClose();
  888. ATLTRACE(_T("L CObjPropBuilder::MapProperties()\n"));
  889. if (bUnMapped)
  890. return DCT_MSG_PROPERTIES_NOT_MAPPED;
  891. else
  892. return S_OK;
  893. }
  894. //------------------------------------------------------------------------------
  895. // IsPropSystemOnly: This function determines if a specific property is
  896. // System Only or not
  897. //------------------------------------------------------------------------------
  898. bool CObjPropBuilder::IsPropSystemOnly(
  899. const WCHAR * sName, //in- Name of the property
  900. const WCHAR * sDomain, //in- Domain name where we need to check
  901. bool& bSystemFlag //out- Tells us if it failed due to system flag.
  902. )
  903. {
  904. // we will lookup the property name in target domain schema and see if it is system only or not.
  905. // First build an LDAP path to the Schema container.
  906. HRESULT hr = S_OK;
  907. WCHAR sQuery[LEN_Path];
  908. LPWSTR sCols[] = { L"systemOnly", L"systemFlags" };
  909. ADS_SEARCH_HANDLE hSearch;
  910. ADS_SEARCH_COLUMN col;
  911. bool bSystemOnly = true;
  912. if ((m_strSchemaDomain != _bstr_t(sDomain)) || (m_spSchemaSearch == false))
  913. {
  914. m_strSchemaDomain = sDomain;
  915. IADsPtr spAds;
  916. hr = ADsGetObject(L"LDAP://" + m_strSchemaDomain + L"/rootDSE", IID_IADs, (void**)&spAds);
  917. if ( SUCCEEDED(hr) )
  918. {
  919. _variant_t var;
  920. hr = spAds->Get(L"schemaNamingContext", &var);
  921. if ( SUCCEEDED(hr) )
  922. {
  923. // Get the IDirectorySearch interface to it.
  924. hr = ADsGetObject(
  925. L"LDAP://" + m_strSchemaDomain + L"/" + _bstr_t(var),
  926. IID_IDirectorySearch,
  927. (void**)&m_spSchemaSearch
  928. );
  929. }
  930. }
  931. }
  932. if ( SUCCEEDED(hr) )
  933. {
  934. // Build the query string
  935. wsprintf(sQuery, L"(lDAPDisplayName=%s)", sName);
  936. // Now search for this property
  937. hr = m_spSchemaSearch->ExecuteSearch(sQuery, sCols, 2, &hSearch);
  938. if ( SUCCEEDED(hr) )
  939. {// Get the systemOnly flag and return its value.
  940. hr = m_spSchemaSearch->GetFirstRow(hSearch);
  941. if (hr == S_ADS_NOMORE_ROWS || SUCCEEDED(hr))
  942. {
  943. hr = m_spSchemaSearch->GetColumn( hSearch, sCols[0], &col );
  944. if ( SUCCEEDED(hr) )
  945. {
  946. bSystemOnly = ( col.pADsValues->Boolean == TRUE);
  947. m_spSchemaSearch->FreeColumn( &col );
  948. }
  949. // Check the system flags
  950. hr = m_spSchemaSearch->GetColumn( hSearch, sCols[1], &col );
  951. if ( SUCCEEDED(hr) )
  952. {
  953. bSystemFlag = ((col.pADsValues->Integer & 0x1F) > 16);
  954. bSystemOnly = bSystemOnly || bSystemFlag;
  955. m_spSchemaSearch->FreeColumn(&col);
  956. }
  957. }
  958. m_spSchemaSearch->CloseSearchHandle(hSearch);
  959. }
  960. }
  961. return bSystemOnly;
  962. }
  963. //------------------------------------------------------------------------------
  964. // TranslateDNs: This function Translates object properties that are
  965. // distinguished names to point to same object in target domain
  966. // as the object in the source domain.
  967. //------------------------------------------------------------------------------
  968. BOOL CObjPropBuilder::TranslateDNs(
  969. ADS_ATTR_INFO *pAttrInfo, //in -Array
  970. DWORD dwRet, BSTR sSource,
  971. BSTR sTarget,
  972. IUnknown *pCheckList //in -Object that will check the list if an account Exists
  973. )
  974. {
  975. IVarSetPtr pVs(__uuidof(VarSet));
  976. IUnknown * pUnk;
  977. INetObjEnumeratorPtr pNet(__uuidof(NetObjEnumerator));
  978. WCHAR sPath[LEN_Path];
  979. WCHAR sQuery[LEN_Path];
  980. IADs * pAds = NULL;
  981. _variant_t vName;
  982. _bstr_t sName;
  983. _bstr_t sObjPath;
  984. HRESULT hr;
  985. LPWSTR pNames[] = { L"distinguishedName" };
  986. SAFEARRAY * saCols;
  987. SAFEARRAYBOUND bd = { 1, 0 };
  988. BSTR HUGEP * pBstr = NULL;
  989. IEnumVARIANT * pEnum = NULL;
  990. DWORD dwf = 0;
  991. VARIANT var2;
  992. IIManageDBPtr pList = pCheckList;
  993. VariantInit(&var2);
  994. pVs->QueryInterface(IID_IUnknown, (void**)&pUnk);
  995. for ( DWORD dw = 0; dw < dwRet; dw++)
  996. {
  997. // initialize the varset that we need to send to the Get props function.
  998. if ( pAttrInfo[dw].dwADsType == ADSTYPE_DN_STRING )
  999. {
  1000. // Get the path to the source dn string
  1001. wsprintf(sPath, L"GC://%s/%s", sSource, pAttrInfo[dw].pADsValues->DNString);
  1002. // get the SAM name for this object.
  1003. hr = ADsGetObject(sPath, IID_IADs, (void**)&pAds);
  1004. if ( SUCCEEDED(hr) )
  1005. {
  1006. hr = pAds->Get(L"sAMAccountName", &vName);
  1007. }
  1008. if ( SUCCEEDED(hr) )
  1009. {
  1010. if ( pAds )
  1011. {
  1012. pAds->Release();
  1013. pAds = NULL;
  1014. }
  1015. if ( SUCCEEDED(hr) )
  1016. {
  1017. sName = vName;
  1018. }
  1019. // Check if we are copying this account in this batch
  1020. if ( SUCCEEDED(hr) )
  1021. {
  1022. hr = pList->raw_GetAMigratedObject(sName, sSource, sTarget, &pUnk);
  1023. }
  1024. if ( hr == S_OK )
  1025. {
  1026. // Get the prop from the varset
  1027. sObjPath = pVs->get(L"MigratedObjects.TargetAdsPath");
  1028. if ( wcslen((WCHAR*)sObjPath) > 0 )
  1029. {
  1030. // We have the ads path so lets get the DN and move on
  1031. hr = ADsGetObject(sObjPath, IID_IADs, (void**)&pAds);
  1032. if ( SUCCEEDED(hr) )
  1033. {
  1034. hr = pAds->Get(L"distinguishedName", &vName);
  1035. }
  1036. if ( pAds )
  1037. {
  1038. pAds->Release();
  1039. pAds = NULL;
  1040. }
  1041. if ( SUCCEEDED(hr) )
  1042. pAttrInfo[dw].pADsValues->DNString = vName.bstrVal;
  1043. }
  1044. }
  1045. else
  1046. {
  1047. // Now that we have the SAM name for the source account And we know we are not copying the
  1048. // account in this batch we can search for this account in the target domain
  1049. // and if the account is there then we use the newly found account.
  1050. wsprintf(sPath, L"GC://%s", sTarget);
  1051. WCHAR sTempSamName[LEN_Path];
  1052. wcscpy(sTempSamName, (WCHAR*)sName);
  1053. if ( sTempSamName[0] == L' ' )
  1054. {
  1055. WCHAR sTemp[LEN_Path];
  1056. wsprintf(sTemp, L"\\20%s", sTempSamName + 1);
  1057. wcscpy(sTempSamName, sTemp);
  1058. }
  1059. wsprintf(sQuery, L"(sAMAccountName=%s)", sTempSamName);
  1060. saCols = SafeArrayCreate(VT_BSTR, 1, &bd);
  1061. SafeArrayAccessData(saCols, (void HUGEP **) &pBstr);
  1062. pBstr[0] = SysAllocString(pNames[0]);
  1063. SafeArrayUnaccessData(saCols);
  1064. hr = pNet->raw_SetQuery(sPath, sTarget, sQuery, 2, FALSE);
  1065. if ( SUCCEEDED(hr) )
  1066. {
  1067. hr = pNet->raw_SetColumns(saCols);
  1068. }
  1069. if (SUCCEEDED(hr) )
  1070. {
  1071. hr = pNet->raw_Execute(&pEnum);
  1072. }
  1073. if (SUCCEEDED(hr) )
  1074. {
  1075. pEnum->Next(1, &vName, &dwf);
  1076. if ( dwf == 0 )
  1077. return FALSE;
  1078. dwf = 0;
  1079. ::SafeArrayGetElement(vName.parray, (long*)&dwf, &var2);
  1080. if ( sName.length() > 0 )
  1081. pAttrInfo[dw].pADsValues->DNString = var2.bstrVal;
  1082. else
  1083. pAttrInfo[dw].pADsValues->DNString = L"";
  1084. VariantClear(&var2);
  1085. }
  1086. if ( pEnum )
  1087. pEnum->Release();
  1088. VariantInit(&vName);
  1089. SafeArrayDestroy(saCols);
  1090. }
  1091. }
  1092. }
  1093. }
  1094. pUnk->Release();
  1095. return TRUE;
  1096. }
  1097. STDMETHODIMP CObjPropBuilder::ChangeGroupType(BSTR sGroupPath, long lGroupType)
  1098. {
  1099. HRESULT hr;
  1100. IADsGroup * pGroup;
  1101. _variant_t var;
  1102. long lType;
  1103. // Get the group type info from the object.
  1104. hr = ADsGetObject( sGroupPath, IID_IADsGroup, (void**) &pGroup);
  1105. if (FAILED(hr)) return hr;
  1106. hr = pGroup->Get(L"groupType", &var);
  1107. if (FAILED(hr)) return hr;
  1108. // Check if Security group or Distribution group and then set the type accordingly.
  1109. lType = var.lVal;
  1110. if (lType & 0x80000000 )
  1111. lType = lGroupType | 0x80000000;
  1112. else
  1113. lType = lGroupType;
  1114. // Set the value into the Group Information
  1115. var = lType;
  1116. hr = pGroup->Put(L"groupType", var);
  1117. if (FAILED(hr)) return hr;
  1118. hr = pGroup->SetInfo();
  1119. if (FAILED(hr)) return hr;
  1120. pGroup->Release();
  1121. return S_OK;
  1122. }
  1123. //------------------------------------------------------------------------------------------------------------------------------
  1124. // CopyNT4Props : Uses Net APIs to get info from the source account and then set it to the target account.
  1125. //------------------------------------------------------------------------------------------------------------------------------
  1126. STDMETHODIMP CObjPropBuilder::CopyNT4Props(BSTR sSourceSam, BSTR sTargetSam, BSTR sSourceServer, BSTR sTargetServer, BSTR sType, long lGrpType, BSTR sExclude)
  1127. {
  1128. DWORD dwError = ERROR_SUCCESS;
  1129. #define ISEXCLUDE(a) IsStringInDelimitedString(sExclude, L#a, L',')
  1130. if (_wcsicmp(sType, L"user") == 0)
  1131. {
  1132. //
  1133. // user
  1134. //
  1135. USER_INFO_3 ui;
  1136. PUSER_INFO_3 puiSource = NULL;
  1137. PUSER_INFO_3 puiTarget = NULL;
  1138. dwError = NetUserGetInfo(sSourceServer, sSourceSam, 3, (LPBYTE*)&puiSource);
  1139. if (dwError == ERROR_SUCCESS)
  1140. {
  1141. dwError = NetUserGetInfo(sTargetServer, sTargetSam, 3, (LPBYTE*)&puiTarget);
  1142. if (dwError == ERROR_SUCCESS)
  1143. {
  1144. // note that attributes with the comment ignored are ignored by NetUserSetInfo
  1145. // setting to target value just so that they have a valid value
  1146. ui.usri3_name = puiTarget->usri3_name; // ignored
  1147. ui.usri3_password = NULL; // must not set during copy properties
  1148. ui.usri3_password_age = puiTarget->usri3_password_age; // ignored
  1149. ui.usri3_priv = puiTarget->usri3_priv; // ignored
  1150. ui.usri3_home_dir = ISEXCLUDE(homeDirectory) ? puiTarget->usri3_home_dir : puiSource->usri3_home_dir;
  1151. ui.usri3_comment = ISEXCLUDE(description) ? puiTarget->usri3_comment : puiSource->usri3_comment;
  1152. ui.usri3_flags = puiSource->usri3_flags;
  1153. // translate a local account to a domain account
  1154. ui.usri3_flags &= ~UF_TEMP_DUPLICATE_ACCOUNT;
  1155. // disable the account in case no password has been set
  1156. ui.usri3_flags |= UF_ACCOUNTDISABLE;
  1157. ui.usri3_script_path = ISEXCLUDE(scriptPath) ? puiTarget->usri3_script_path : puiSource->usri3_script_path;
  1158. ui.usri3_auth_flags = puiTarget->usri3_auth_flags; // ignored
  1159. ui.usri3_full_name = ISEXCLUDE(displayName) ? puiTarget->usri3_full_name : puiSource->usri3_full_name;
  1160. ui.usri3_usr_comment = ISEXCLUDE(comment) ? puiTarget->usri3_usr_comment : puiSource->usri3_usr_comment;
  1161. ui.usri3_parms = ISEXCLUDE(userParameters) ? puiTarget->usri3_parms : puiSource->usri3_parms;
  1162. ui.usri3_workstations = ISEXCLUDE(userWorkstations) ? puiTarget->usri3_workstations : puiSource->usri3_workstations;
  1163. ui.usri3_last_logon = puiTarget->usri3_last_logon; // ignored
  1164. ui.usri3_last_logoff = ISEXCLUDE(lastLogoff) ? puiTarget->usri3_last_logoff : puiSource->usri3_last_logoff;
  1165. ui.usri3_acct_expires = ISEXCLUDE(accountExpires) ? puiTarget->usri3_acct_expires : puiSource->usri3_acct_expires;
  1166. ui.usri3_max_storage = ISEXCLUDE(maxStorage) ? puiTarget->usri3_max_storage : puiSource->usri3_max_storage;
  1167. ui.usri3_units_per_week = puiTarget->usri3_units_per_week; // ignored
  1168. ui.usri3_logon_hours = ISEXCLUDE(logonHours) ? puiTarget->usri3_logon_hours : puiSource->usri3_logon_hours;
  1169. ui.usri3_bad_pw_count = puiTarget->usri3_bad_pw_count; // ignored
  1170. ui.usri3_num_logons = puiTarget->usri3_num_logons; // ignored
  1171. ui.usri3_logon_server = puiTarget->usri3_logon_server; // ignored
  1172. ui.usri3_country_code = ISEXCLUDE(countryCode) ? puiTarget->usri3_country_code : puiSource->usri3_country_code;
  1173. ui.usri3_code_page = ISEXCLUDE(codePage) ? puiTarget->usri3_code_page : puiSource->usri3_code_page;
  1174. ui.usri3_user_id = puiTarget->usri3_user_id; // ignored
  1175. // if not excluded set the primary group to the Domain Users group
  1176. ui.usri3_primary_group_id = ISEXCLUDE(primaryGroupID) ? puiTarget->usri3_primary_group_id : DOMAIN_GROUP_RID_USERS;
  1177. ui.usri3_profile = ISEXCLUDE(profilePath) ? puiTarget->usri3_profile : puiSource->usri3_profile;
  1178. ui.usri3_home_dir_drive = ISEXCLUDE(homeDrive) ? puiTarget->usri3_home_dir_drive : puiSource->usri3_home_dir_drive;
  1179. ui.usri3_password_expired = puiTarget->usri3_password_expired;
  1180. dwError = NetUserSetInfo(sTargetServer, sTargetSam, 3, (LPBYTE)&ui, NULL);
  1181. if (dwError == NERR_UserNotInGroup)
  1182. {
  1183. // if the setInfo failed because of the primary group property, try again, using the primary group
  1184. // that is already defined for the target account
  1185. ui.usri3_primary_group_id = puiTarget->usri3_primary_group_id;
  1186. dwError = NetUserSetInfo(sTargetServer, sTargetSam, 3, (LPBYTE)&ui, NULL);
  1187. }
  1188. }
  1189. if (puiTarget)
  1190. {
  1191. NetApiBufferFree(puiTarget);
  1192. }
  1193. if (puiSource)
  1194. {
  1195. NetApiBufferFree(puiSource);
  1196. }
  1197. }
  1198. }
  1199. else if (_wcsicmp(sType, L"group") == 0)
  1200. {
  1201. // if description attribute is not excluded then copy comment attribute
  1202. // note that the only downlevel group attribute that will be copied is the description (comment) attribute
  1203. if (ISEXCLUDE(description) == FALSE)
  1204. {
  1205. if (lGrpType & 4)
  1206. {
  1207. //
  1208. // local group
  1209. //
  1210. PLOCALGROUP_INFO_1 plgi = NULL;
  1211. dwError = NetLocalGroupGetInfo(sSourceServer, sSourceSam, 1, (LPBYTE*)&plgi);
  1212. if (dwError == ERROR_SUCCESS)
  1213. {
  1214. dwError = NetLocalGroupSetInfo(sTargetServer, sTargetSam, 1, (LPBYTE)plgi, NULL);
  1215. NetApiBufferFree(plgi);
  1216. }
  1217. }
  1218. else
  1219. {
  1220. //
  1221. // global group
  1222. //
  1223. PGROUP_INFO_1 pgi = NULL;
  1224. dwError = NetGroupGetInfo(sSourceServer, sSourceSam, 1, (LPBYTE*)&pgi);
  1225. if (dwError == ERROR_SUCCESS)
  1226. {
  1227. dwError = NetGroupSetInfo(sTargetServer, sTargetSam, 1, (LPBYTE)pgi, NULL);
  1228. NetApiBufferFree(pgi);
  1229. }
  1230. }
  1231. }
  1232. }
  1233. else if (_wcsicmp(sType, L"computer") == 0)
  1234. {
  1235. //
  1236. // computer
  1237. //
  1238. USER_INFO_3 ui;
  1239. PUSER_INFO_3 puiSource = NULL;
  1240. PUSER_INFO_3 puiTarget = NULL;
  1241. dwError = NetUserGetInfo(sSourceServer, sSourceSam, 3, (LPBYTE*)&puiSource);
  1242. if (dwError == ERROR_SUCCESS)
  1243. {
  1244. dwError = NetUserGetInfo(sTargetServer, sTargetSam, 3, (LPBYTE*)&puiTarget);
  1245. if (dwError == ERROR_SUCCESS)
  1246. {
  1247. // note that attributes with the comment ignored are ignored by NetUserSetInfo
  1248. // setting to target value just so that they have a valid value
  1249. ui.usri3_name = puiTarget->usri3_name; // ignored
  1250. ui.usri3_password = NULL; // must not set during copy properties
  1251. ui.usri3_password_age = puiTarget->usri3_password_age; // ignored
  1252. ui.usri3_priv = puiTarget->usri3_priv; // ignored
  1253. ui.usri3_home_dir = puiTarget->usri3_home_dir;
  1254. ui.usri3_comment = ISEXCLUDE(description) ? puiTarget->usri3_comment : puiSource->usri3_comment;
  1255. ui.usri3_flags = puiSource->usri3_flags;
  1256. // translate a local account to a domain account
  1257. ui.usri3_flags &= ~UF_TEMP_DUPLICATE_ACCOUNT;
  1258. // disable the account in case no password has been set
  1259. //ui.usri3_flags |= UF_ACCOUNTDISABLE;
  1260. ui.usri3_script_path = puiTarget->usri3_script_path;
  1261. ui.usri3_auth_flags = puiTarget->usri3_auth_flags; // ignored
  1262. ui.usri3_full_name = ISEXCLUDE(displayName) ? puiTarget->usri3_full_name : puiSource->usri3_full_name;
  1263. ui.usri3_usr_comment = ISEXCLUDE(comment) ? puiTarget->usri3_usr_comment : puiSource->usri3_usr_comment;
  1264. ui.usri3_parms = puiTarget->usri3_parms;
  1265. ui.usri3_workstations = puiTarget->usri3_workstations;
  1266. ui.usri3_last_logon = puiTarget->usri3_last_logon; // ignored
  1267. ui.usri3_last_logoff = puiTarget->usri3_last_logoff;
  1268. ui.usri3_acct_expires = puiTarget->usri3_acct_expires;
  1269. ui.usri3_max_storage = puiTarget->usri3_max_storage;
  1270. ui.usri3_units_per_week = puiTarget->usri3_units_per_week; // ignored
  1271. ui.usri3_logon_hours = puiTarget->usri3_logon_hours;
  1272. ui.usri3_bad_pw_count = puiTarget->usri3_bad_pw_count; // ignored
  1273. ui.usri3_num_logons = puiTarget->usri3_num_logons; // ignored
  1274. ui.usri3_logon_server = puiTarget->usri3_logon_server; // ignored
  1275. ui.usri3_country_code = puiTarget->usri3_country_code;
  1276. ui.usri3_code_page = puiTarget->usri3_code_page;
  1277. ui.usri3_user_id = puiTarget->usri3_user_id; // ignored
  1278. ui.usri3_primary_group_id = puiTarget->usri3_primary_group_id;
  1279. ui.usri3_profile = puiTarget->usri3_profile;
  1280. ui.usri3_home_dir_drive = puiTarget->usri3_home_dir_drive;
  1281. ui.usri3_password_expired = puiTarget->usri3_password_expired;
  1282. dwError = NetUserSetInfo(sTargetServer, sTargetSam, 3, (LPBYTE)&ui, NULL);
  1283. }
  1284. if (puiTarget)
  1285. {
  1286. NetApiBufferFree(puiTarget);
  1287. }
  1288. if (puiSource)
  1289. {
  1290. NetApiBufferFree(puiSource);
  1291. }
  1292. }
  1293. }
  1294. else
  1295. {
  1296. _ASSERT(FALSE);
  1297. }
  1298. return HRESULT_FROM_WIN32(dwError);
  1299. }
  1300. /*********************************************************************
  1301. * *
  1302. * Written by: Paul Thompson *
  1303. * Date: 31 OCT 2000 *
  1304. * *
  1305. * This function is responsible for copying all properties, from *
  1306. * an incoming varset of properties, into a new varset but excluding *
  1307. * those properties listed in a given exclusion list. The exclusion *
  1308. * list is a comma-seperated string of property names. *
  1309. * *
  1310. *********************************************************************/
  1311. //BEGIN ExcludeProperties
  1312. STDMETHODIMP CObjPropBuilder::ExcludeProperties(
  1313. BSTR sExclusionList, //in- list of props to exclude
  1314. IUnknown *pPropSet, //in -Varset listing all the props to copy
  1315. IUnknown **ppUnk //out - Varset with all the props except those excluded
  1316. )
  1317. {
  1318. /* local variables */
  1319. IVarSetPtr pVarsetNew = *ppUnk;
  1320. IVarSetPtr pVarset = pPropSet;
  1321. SAFEARRAY * keys;
  1322. SAFEARRAY * vals;
  1323. long lRet;
  1324. VARIANT var;
  1325. _variant_t varEmpty;
  1326. BOOL bFound = FALSE;
  1327. HRESULT hr;
  1328. /* function body */
  1329. VariantInit(&var);
  1330. //retrieve all item in the incoming varset
  1331. hr = pVarset->getItems(L"", L"", 1, 10000, &keys, &vals, &lRet);
  1332. if ( FAILED (hr) )
  1333. return hr;
  1334. //get each property name and if it is not in the exclusion list
  1335. //place it in the new varset
  1336. _bstr_t keyName;
  1337. for ( long x = 0; x < lRet; x++ )
  1338. {
  1339. //get the property name
  1340. ::SafeArrayGetElement(keys, &x, &var);
  1341. keyName = var.bstrVal;
  1342. VariantClear(&var);
  1343. //see if this name is in the exclusion list
  1344. bFound = IsStringInDelimitedString((WCHAR*)sExclusionList,
  1345. (WCHAR*)keyName,
  1346. L',');
  1347. //if the property was not found in the exclusion list, place it
  1348. //in the new varset
  1349. if (!bFound)
  1350. pVarsetNew->put(keyName, varEmpty);
  1351. }//end for each property
  1352. SafeArrayDestroy(keys);
  1353. SafeArrayDestroy(vals);
  1354. return S_OK;
  1355. }
  1356. //END ExcludeProperties