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.

2403 lines
64 KiB

  1. /****
  2. SchmUtil.cpp
  3. Various common utility routines for the Schema Editor Snap-In.
  4. ****/
  5. #include "stdafx.h"
  6. #include "macros.h"
  7. USE_HANDLE_MACROS("SCHMMGMT(schmutil.cpp)")
  8. #include "resource.h"
  9. #include "cache.h"
  10. #include "schmutil.h"
  11. #include "compdata.h"
  12. #include <wincrypt.h> // CryptEncodeObject() and CryptDecodeObject()
  13. //
  14. // Removed from public headers by DS guys
  15. // See bug 454342 XOM will not survive the transition to Win64
  16. //
  17. //#include <xom.h>
  18. //
  19. // Global strings for classes and attributes in the DS.
  20. // These are NOT subject to localization.
  21. //
  22. LPWSTR g_DisplayName = L"ldapDisplayName";
  23. LPWSTR g_ClassFilter = L"classSchema";
  24. LPWSTR g_AttributeFilter = L"attributeSchema";
  25. LPWSTR g_Description = L"adminDescription";
  26. LPWSTR g_MayContain = L"mayContain";
  27. LPWSTR g_MustContain = L"mustContain";
  28. LPWSTR g_SystemMayContain = L"systemMayContain";
  29. LPWSTR g_SystemMustContain = L"systemMustContain";
  30. LPWSTR g_AuxiliaryClass = L"auxiliaryClass";
  31. LPWSTR g_SystemAuxiliaryClass = L"systemAuxiliaryClass";
  32. LPWSTR g_SubclassOf = L"subclassOf";
  33. LPWSTR g_ObjectClassCategory = L"objectClassCategory";
  34. LPWSTR g_ObjectClass = L"objectClass";
  35. LPWSTR g_omObjectClass = L"oMObjectClass";
  36. LPWSTR g_CN = L"CN";
  37. LPWSTR g_omSyntax = L"oMSyntax";
  38. LPWSTR g_AttributeSyntax = L"attributeSyntax";
  39. LPWSTR g_SystemOnly = L"systemOnly";
  40. LPWSTR g_Superiors = L"possSuperiors";
  41. LPWSTR g_SystemSuperiors = L"systemPossSuperiors";
  42. LPWSTR g_GlobalClassID = L"governsID";
  43. LPWSTR g_GlobalAttributeID = L"attributeID";
  44. LPWSTR g_RangeUpper = L"rangeUpper";
  45. LPWSTR g_RangeLower = L"rangeLower";
  46. LPWSTR g_IsSingleValued = L"isSingleValued";
  47. LPWSTR g_IndexFlag = L"searchFlags";
  48. LPWSTR g_ShowInAdvViewOnly = L"showInAdvancedViewOnly";
  49. LPWSTR g_UpdateSchema = LDAP_OPATT_SCHEMA_UPDATE_NOW_W;
  50. LPWSTR g_BecomeFsmo = LDAP_OPATT_BECOME_SCHEMA_MASTER_W;
  51. LPWSTR g_isDefunct = L"isDefunct";
  52. LPWSTR g_GCReplicated = L"isMemberOfPartialAttributeSet";
  53. LPWSTR g_DefaultAcl = L"defaultSecurityDescriptor";
  54. LPWSTR g_DefaultCategory = L"defaultObjectCategory";
  55. LPWSTR g_systemFlags = L"systemFlags";
  56. LPWSTR g_fsmoRoleOwner = L"fsmoRoleOwner";
  57. LPWSTR g_allowedChildClassesEffective = L"allowedChildClassesEffective";
  58. LPWSTR g_allowedAttributesEffective = L"allowedAttributesEffective";
  59. LPWSTR g_ClassSearchRequest = L"objectClass=classSchema";
  60. LPWSTR g_AttribSearchRequest = L"objectClass=attributeSchema";
  61. //
  62. // Syntax values. Not subject to localization.
  63. //
  64. class CSyntaxDescriptor g_Syntax[] =
  65. {
  66. /*SYNTAX_DISTNAME_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_DN, FALSE, FALSE, _T("2.5.5.1") , /*OM_S_OBJECT */ 127, 9, (LPBYTE)"\x2B\x0C\x02\x87\x73\x1C\x00\x85\x4A" ),
  67. /*SYNTAX_OBJECT_ID_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_OID, FALSE, FALSE, _T("2.5.5.2") , /*OM_S_OBJECT_IDENTIFIER_STRING */ 6 , 0, NULL ),
  68. /*SYNTAX_NOCASE_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_NOCASE_STR, FALSE, TRUE, _T("2.5.5.4") , /*OM_S_TELETEX_STRING */ 20 , 0, NULL ),
  69. /*SYNTAX_PRINT_CASE_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_PRCS_STR, FALSE, TRUE, _T("2.5.5.5") , /*OM_S_PRINTABLE_STRING */ 19 , 0, NULL ),
  70. /*SYNTAX_PRINT_CASE_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_I5_STR, FALSE, TRUE, _T("2.5.5.5") , /*OM_S_IA5_STRING */ 22 , 0, NULL ),
  71. /*SYNTAX_NUMERIC_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_NUMSTR, FALSE, FALSE, _T("2.5.5.6") , /*OM_S_NUMERIC_STRING */ 18 , 0, NULL ),
  72. /*SYNTAX_DISTNAME_BINARY_TYPE (OR-Name) */ CSyntaxDescriptor( IDS_SYNTAX_OR_NAME, FALSE, FALSE, _T("2.5.5.7") , /*OM_S_OBJECT */ 127, 7, (LPBYTE)"\x56\x06\x01\x02\x05\x0B\x1D" ),
  73. /*SYNTAX_DISTNAME_BINARY_TYPE (DN-Binary) */ CSyntaxDescriptor( IDS_SYNTAX_DN_BINARY, FALSE, FALSE, _T("2.5.5.7") , /*OM_S_OBJECT */ 127, 10,(LPBYTE)"\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x0B" ),
  74. /*SYNTAX_BOOLEAN_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_BOOLEAN, FALSE, FALSE, _T("2.5.5.8") , /*OM_S_BOOLEAN */ 1 , 0, NULL ),
  75. /*SYNTAX_INTEGER_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_INTEGER, TRUE, FALSE, _T("2.5.5.9") , /*OM_S_INTEGER */ 2 , 0, NULL ),
  76. /*SYNTAX_INTEGER_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_ENUMERATION, TRUE, FALSE, _T("2.5.5.9") , /*OM_S_ENUMERATION */ 10 , 0, NULL ),
  77. /*SYNTAX_OCTET_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_OCTET, FALSE, FALSE, _T("2.5.5.10"), /*OM_S_OCTET_STRING */ 4 , 0, NULL ),
  78. /*SYNTAX_OCTET_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_REPLICA_LINK,FALSE, FALSE, _T("2.5.5.10"), /*OM_S_OBJECT */ 127, 10,(LPBYTE)"\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x06" ),
  79. /*SYNTAX_TIME_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_UTC, FALSE, FALSE, _T("2.5.5.11"), /*OM_S_UTC_TIME_STRING */ 23 , 0, NULL ),
  80. /*SYNTAX_TIME_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_GEN_TIME, FALSE, FALSE, _T("2.5.5.11"), /*OM_S_GENERALISED_TIME_STRING */ 24 , 0, NULL ),
  81. /*SYNTAX_UNICODE_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_UNICODE, FALSE, TRUE, _T("2.5.5.12"), /*OM_S_UNICODE_STRING */ 64 , 0, NULL ),
  82. /*SYNTAX_ADDRESS_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_ADDRESS, FALSE, FALSE, _T("2.5.5.13"), /*OM_S_OBJECT */ 127, 9, (LPBYTE)"\x2B\x0C\x02\x87\x73\x1C\x00\x85\x5C" ),
  83. /*SYNTAX_DISTNAME_STRING_TYPE (Access-Point) */ CSyntaxDescriptor( IDS_SYNTAX_ACCESS_POINT,FALSE, FALSE, _T("2.5.5.14"), /*OM_S_OBJECT */ 127, 9, (LPBYTE)"\x2B\x0C\x02\x87\x73\x1C\x00\x85\x3E" ),
  84. /*SYNTAX_DISTNAME_STRING_TYPE (DN-String) */ CSyntaxDescriptor( IDS_SYNTAX_DNSTRING, FALSE, FALSE, _T("2.5.5.14"), /*OM_S_OBJECT */ 127, 10,(LPBYTE)"\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x0C" ),
  85. /*SYNTAX_NT_SECURITY_DESCRIPTOR_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_SEC_DESC, FALSE, FALSE, _T("2.5.5.15"), /*OM_S_OBJECT_SECURITY_DESCRIPTOR*/ 66 , 0, NULL ),
  86. /*SYNTAX_I8_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_LINT, FALSE, FALSE, _T("2.5.5.16"), /*OM_S_I8 */ 65 , 0, NULL ),
  87. /*SYNTAX_SID_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_SID, FALSE, FALSE, _T("2.5.5.17"), /*OM_S_OCTET_STRING */ 4 , 0, NULL ),
  88. /* *** unknown -- must be last *** */ CSyntaxDescriptor( IDS_SYNTAX_UNKNOWN, FALSE, TRUE, NULL, 0 , 0, NULL ),
  89. };
  90. const UINT SCHEMA_SYNTAX_UNKNOWN = sizeof( g_Syntax ) / sizeof( g_Syntax[0] ) - 1;
  91. // Number formating printf strings
  92. const LPWSTR g_UINT32_FORMAT = L"%u";
  93. #ifdef ENABLE_NEGATIVE_INT
  94. const LPWSTR g_INT32_FORMAT = L"%d";
  95. #else
  96. // if there is no negative numbers support, format as unsigned
  97. const LPWSTR g_INT32_FORMAT = g_UINT32_FORMAT;
  98. #endif
  99. //
  100. // *******************************************************************
  101. // These are loaded from the resources as they need to be localizable.
  102. // *******************************************************************
  103. //
  104. //
  105. // Global strings for our static nodes.
  106. //
  107. CString g_strSchmMgmt;
  108. CString g_strClasses;
  109. CString g_strAttributes;
  110. //
  111. // Strings for various object types.
  112. //
  113. CString g_88Class;
  114. CString g_StructuralClass;
  115. CString g_AuxClass;
  116. CString g_AbstractClass;
  117. CString g_MandatoryAttribute;
  118. CString g_OptionalAttribute;
  119. CString g_Yes;
  120. CString g_No;
  121. CString g_Unknown;
  122. CString g_Defunct;
  123. CString g_Active;
  124. //
  125. // Message strings.
  126. //
  127. CString g_NoDescription;
  128. CString g_NoName;
  129. CString g_MsgBoxErr;
  130. CString g_MsgBoxWarn;
  131. CString g_SysClassString;
  132. CString g_SysAttrString;
  133. //
  134. // Menu strings.
  135. //
  136. CString g_MenuStrings[MENU_LAST_COMMAND];
  137. CString g_StatusStrings[MENU_LAST_COMMAND];
  138. BOOL g_fScopeStringsLoaded = FALSE;
  139. //
  140. // Utility functions.
  141. //
  142. void
  143. LoadGlobalCookieStrings(
  144. )
  145. /***
  146. Load the global strings out of our resource table.
  147. ***/
  148. {
  149. if ( !g_fScopeStringsLoaded )
  150. {
  151. //
  152. // Static node strings.
  153. //
  154. VERIFY( g_strSchmMgmt.LoadString(IDS_SCOPE_SCHMMGMT) );
  155. VERIFY( g_strClasses.LoadString(IDS_SCOPE_CLASSES) );
  156. VERIFY( g_strAttributes.LoadString(IDS_SCOPE_ATTRIBUTES) );
  157. //
  158. // Object name strings.
  159. //
  160. VERIFY( g_88Class.LoadString(IDS_CLASS_88) );
  161. VERIFY( g_StructuralClass.LoadString(IDS_CLASS_STRUCTURAL) );
  162. VERIFY( g_AuxClass.LoadString(IDS_CLASS_AUXILIARY) );
  163. VERIFY( g_AbstractClass.LoadString(IDS_CLASS_ABSTRACT) );
  164. VERIFY( g_MandatoryAttribute.LoadString(IDS_ATTRIBUTE_MANDATORY) );
  165. VERIFY( g_OptionalAttribute.LoadString(IDS_ATTRIBUTE_OPTIONAL) );
  166. VERIFY( g_Yes.LoadString(IDS_YES) );
  167. VERIFY( g_No.LoadString(IDS_NO) );
  168. VERIFY( g_Unknown.LoadString(IDS_UNKNOWN) );
  169. VERIFY( g_Defunct.LoadString(IDS_DEFUNCT) );
  170. VERIFY( g_Active.LoadString(IDS_ACTIVE) );
  171. //
  172. // Message strings.
  173. //
  174. VERIFY( g_NoDescription.LoadString(IDS_ERR_NO_DESCRIPTION) );
  175. VERIFY( g_NoName.LoadString(IDS_ERR_NO_NAME) );
  176. VERIFY( g_MsgBoxErr.LoadString(IDS_ERR_ERROR) );
  177. VERIFY( g_MsgBoxWarn.LoadString(IDS_ERR_WARNING) );
  178. VERIFY( g_SysClassString.LoadString(IDS_CLASS_SYSTEM) );
  179. VERIFY( g_SysAttrString.LoadString(IDS_ATTR_SYSTEM) );
  180. //
  181. // Syntax strings.
  182. //
  183. for( UINT i = 0; i <= SCHEMA_SYNTAX_UNKNOWN; i++ )
  184. {
  185. ASSERT( g_Syntax[i].m_nResourceID );
  186. VERIFY( g_Syntax[i].m_strSyntaxName.LoadString( g_Syntax[i].m_nResourceID ) );
  187. }
  188. //
  189. // Menu Strings
  190. //
  191. VERIFY( g_MenuStrings[CLASSES_CREATE_CLASS].LoadString(IDS_MENU_CLASS) );
  192. VERIFY( g_MenuStrings[NEW_CLASS].LoadString(IDS_MENU_NEW_CLASS) );
  193. VERIFY( g_MenuStrings[ATTRIBUTES_CREATE_ATTRIBUTE].LoadString(
  194. IDS_MENU_ATTRIBUTE) );
  195. VERIFY(g_MenuStrings[NEW_ATTRIBUTE].LoadString(IDS_MENU_NEW_ATTRIBUTE));
  196. VERIFY( g_MenuStrings[SCHEMA_RETARGET].LoadString(IDS_MENU_RETARGET) );
  197. VERIFY( g_MenuStrings[SCHEMA_EDIT_FSMO].LoadString(IDS_MENU_EDIT_FSMO) );
  198. VERIFY( g_MenuStrings[SCHEMA_REFRESH].LoadString(IDS_MENU_REFRESH) );
  199. VERIFY( g_MenuStrings[SCHEMA_SECURITY].LoadString(IDS_MENU_SECURITY) );
  200. VERIFY( g_StatusStrings[CLASSES_CREATE_CLASS].LoadString(
  201. IDS_STATUS_CREATE_CLASS) );
  202. VERIFY( g_StatusStrings[ATTRIBUTES_CREATE_ATTRIBUTE].LoadString(
  203. IDS_STATUS_CREATE_ATTRIBUTE) );
  204. VERIFY( g_StatusStrings[NEW_CLASS].LoadString(
  205. IDS_STATUS_CREATE_CLASS) );
  206. VERIFY( g_StatusStrings[NEW_ATTRIBUTE].LoadString(
  207. IDS_STATUS_CREATE_ATTRIBUTE) );
  208. VERIFY( g_StatusStrings[SCHEMA_RETARGET].LoadString(IDS_STATUS_RETARGET) );
  209. VERIFY( g_StatusStrings[SCHEMA_EDIT_FSMO].LoadString(IDS_STATUS_EDIT_FSMO) );
  210. VERIFY( g_StatusStrings[SCHEMA_REFRESH].LoadString(IDS_STATUS_REFRESH) );
  211. VERIFY( g_StatusStrings[SCHEMA_SECURITY].LoadString(IDS_STATUS_SECURITY) );
  212. g_fScopeStringsLoaded = TRUE;
  213. }
  214. }
  215. INT
  216. DoErrMsgBox(
  217. HWND hwndParent, // IN: Parent of the dialog box
  218. BOOL fError, // IN: Is this a warning or an error?
  219. UINT wIdString // IN: String resource Id of the error.
  220. )
  221. /***
  222. Display a message box with the error.
  223. ***/
  224. {
  225. CString Error;
  226. VERIFY( Error.LoadString( wIdString ) );
  227. return DoErrMsgBox( hwndParent, fError, Error );
  228. }
  229. INT
  230. DoErrMsgBox(
  231. HWND hwndParent, // IN: Parent of the dialog box
  232. BOOL fError, // IN: Is this a warning or an error?
  233. PCWSTR pszError // IN: String to display.
  234. )
  235. /***
  236. Display a message box with the error.
  237. ***/
  238. {
  239. return MessageBox(
  240. hwndParent,
  241. pszError,
  242. (fError ? g_MsgBoxErr : g_MsgBoxWarn),
  243. (fError ? MB_ICONSTOP : MB_ICONEXCLAMATION) | MB_OK
  244. );
  245. }
  246. HRESULT
  247. ComponentData::ForceDsSchemaCacheUpdate(
  248. VOID
  249. )
  250. /***
  251. Force the schema container to reload its interal cache.
  252. If this succeeds, it returns TRUE. Otherwise, it returns
  253. FALSE.
  254. ***/
  255. {
  256. AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
  257. CWaitCursor wait;
  258. CString RootDsePath;
  259. IADs *pSchemaRootDse = NULL;
  260. SAFEARRAYBOUND RootDseBoundary[1];
  261. SAFEARRAY* pSafeArray = NULL;
  262. VARIANT AdsArray, AdsValue;
  263. long ArrayLen = 1;
  264. long ArrayPos = 0;
  265. HRESULT hr = S_OK;
  266. do
  267. {
  268. //
  269. // Open the root DSE on the current focus server.
  270. //
  271. GetBasePathsInfo()->GetRootDSEPath(RootDsePath);
  272. hr = ADsGetObject(
  273. ( const_cast<BSTR>((LPCTSTR) RootDsePath ) ),
  274. IID_IADs,
  275. (void **)&pSchemaRootDse );
  276. BREAK_ON_FAILED_HRESULT(hr);
  277. //
  278. // Create the safe array for the PutEx call.
  279. //
  280. RootDseBoundary[0].lLbound = 0;
  281. RootDseBoundary[0].cElements = ArrayLen;
  282. pSafeArray = SafeArrayCreate( VT_VARIANT, ArrayLen, RootDseBoundary );
  283. BREAK_ON_FAILED_HRESULT(hr);
  284. VariantInit( &AdsArray );
  285. V_VT( &AdsArray ) = VT_ARRAY | VT_VARIANT;
  286. V_ARRAY( &AdsArray ) = pSafeArray;
  287. VariantInit( &AdsValue );
  288. V_VT(&AdsValue) = VT_I4;
  289. V_I4(&AdsValue) = 1;
  290. hr = SafeArrayPutElement( pSafeArray, &ArrayPos, &AdsValue );
  291. BREAK_ON_FAILED_HRESULT(hr);
  292. //
  293. // Write the update parameter. This is synchronous
  294. // and when it returns, the cache is up to date.
  295. //
  296. hr = pSchemaRootDse->PutEx( ADS_PROPERTY_APPEND,
  297. const_cast<BSTR>((LPCTSTR)g_UpdateSchema),
  298. AdsArray );
  299. BREAK_ON_FAILED_HRESULT(hr);
  300. hr = pSchemaRootDse->SetInfo();
  301. BREAK_ON_FAILED_HRESULT(hr);
  302. } while( FALSE );
  303. SafeArrayDestroy( pSafeArray );
  304. if( pSchemaRootDse )
  305. pSchemaRootDse->Release();
  306. return hr;
  307. }
  308. BOOLEAN
  309. ComponentData::AsynchForceDsSchemaCacheUpdate(
  310. VOID
  311. ) {
  312. AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
  313. CWaitCursor wait;
  314. CString szSchemaContainerPath;
  315. IADs *pSchemaContainer;
  316. VARIANT AdsValue;
  317. HRESULT hr;
  318. SYSTEMTIME CurrentTime;
  319. double variant_time;
  320. //
  321. // Get the schema container path.
  322. //
  323. GetBasePathsInfo()->GetSchemaPath(szSchemaContainerPath);
  324. if (szSchemaContainerPath.IsEmpty() ) {
  325. return FALSE;
  326. }
  327. //
  328. // Open the schema container.
  329. //
  330. hr = ADsGetObject(
  331. (LPWSTR)(LPCWSTR)szSchemaContainerPath,
  332. IID_IADs,
  333. (void **)&pSchemaContainer );
  334. if ( FAILED(hr) ) {
  335. return FALSE;
  336. }
  337. //
  338. // Write the update parameter.
  339. //
  340. GetSystemTime( &CurrentTime );
  341. BOOL result = SystemTimeToVariantTime( &CurrentTime, &variant_time );
  342. ASSERT( result );
  343. VariantInit( &AdsValue );
  344. V_VT(&AdsValue) = VT_DATE;
  345. V_DATE(&AdsValue) = variant_time;
  346. hr = pSchemaContainer->Put( const_cast<BSTR>((LPCTSTR)g_UpdateSchema),
  347. AdsValue );
  348. ASSERT( SUCCEEDED( hr ) );
  349. hr = pSchemaContainer->SetInfo();
  350. pSchemaContainer->Release();
  351. if ( FAILED( hr ) ) {
  352. return FALSE;
  353. }
  354. return TRUE;
  355. }
  356. HRESULT
  357. InsertEditItems(
  358. HWND hwnd,
  359. VARIANT *AdsResult
  360. ) {
  361. HRESULT hr;
  362. SAFEARRAY *saAttributes;
  363. long start, end, current;
  364. VARIANT SingleResult;
  365. //
  366. // Check the VARIANT to make sure we have
  367. // an array of variants.
  368. //
  369. ASSERT( V_VT(AdsResult) == ( VT_ARRAY | VT_VARIANT ) );
  370. saAttributes = V_ARRAY( AdsResult );
  371. //
  372. // Figure out the dimensions of the array.
  373. //
  374. hr = SafeArrayGetLBound( saAttributes, 1, &start );
  375. if ( FAILED(hr) ) {
  376. return S_FALSE;
  377. }
  378. hr = SafeArrayGetUBound( saAttributes, 1, &end );
  379. if ( FAILED(hr) ) {
  380. return S_FALSE;
  381. }
  382. VariantInit( &SingleResult );
  383. //
  384. // Process the array elements.
  385. //
  386. for ( current = start ;
  387. current <= end ;
  388. current++ ) {
  389. hr = SafeArrayGetElement( saAttributes, &current, &SingleResult );
  390. if ( SUCCEEDED( hr ) ) {
  391. ASSERT( V_VT(&SingleResult) == VT_BSTR );
  392. ::SendMessage( hwnd, LB_ADDSTRING, 0,
  393. reinterpret_cast<LPARAM>(V_BSTR(&SingleResult)) );
  394. ::SendMessage( hwnd, LB_SETITEMDATA, 0, NULL );
  395. VariantClear( &SingleResult );
  396. }
  397. }
  398. return S_OK;
  399. }
  400. HRESULT
  401. InsertEditItems(
  402. CListBox& refListBox,
  403. CStringList& refstringlist
  404. )
  405. {
  406. POSITION pos = refstringlist.GetHeadPosition();
  407. while (pos != NULL)
  408. {
  409. int iItem = refListBox.AddString( refstringlist.GetNext(pos) );
  410. if (0 > iItem)
  411. {
  412. ASSERT(FALSE);
  413. return E_OUTOFMEMORY;
  414. }
  415. else
  416. {
  417. VERIFY( LB_ERR != refListBox.SetItemDataPtr( iItem, NULL ) );
  418. }
  419. }
  420. return S_OK;
  421. }
  422. inline BOOL
  423. IsEqual( ADS_OCTET_STRING * ostr1, ADS_OCTET_STRING * ostr2 )
  424. {
  425. ASSERT(ostr1);
  426. ASSERT(ostr2);
  427. if( ostr1->dwLength == ostr2->dwLength )
  428. {
  429. if( 0 == ostr1->dwLength )
  430. return TRUE;
  431. else
  432. return !memcmp( ostr1->lpValue, ostr2->lpValue, ostr1->dwLength );
  433. }
  434. else
  435. return FALSE;
  436. }
  437. UINT
  438. GetSyntaxOrdinal( PCTSTR attributeSyntax, UINT omSyntax, ADS_OCTET_STRING * pOmObjectClass )
  439. {
  440. ASSERT( attributeSyntax );
  441. ASSERT( omSyntax );
  442. ASSERT( pOmObjectClass );
  443. //
  444. // Return the syntax ordinal, or the unknown syntax ordinal.
  445. //
  446. UINT Ordinal = 0;
  447. while ( Ordinal < SCHEMA_SYNTAX_UNKNOWN) {
  448. if ( !_tcscmp(g_Syntax[Ordinal].m_pszAttributeSyntax, attributeSyntax))
  449. {
  450. if( omSyntax && g_Syntax[Ordinal].m_nOmSyntax == omSyntax &&
  451. IsEqual( &g_Syntax[Ordinal].m_octstrOmObjectClass, pOmObjectClass ) )
  452. break;
  453. }
  454. Ordinal++;
  455. }
  456. return Ordinal;
  457. }
  458. // Coded to fail on anything suspicious
  459. HRESULT
  460. VariantToStringList(
  461. VARIANT& refvar,
  462. CStringList& refstringlist
  463. )
  464. {
  465. HRESULT hr = S_OK;
  466. long start, end, current;
  467. //
  468. // Check the VARIANT to make sure we have
  469. // an array of variants.
  470. //
  471. if ( V_VT(&refvar) != ( VT_ARRAY | VT_VARIANT ) )
  472. {
  473. ASSERT(FALSE);
  474. return E_UNEXPECTED;
  475. }
  476. SAFEARRAY *saAttributes = V_ARRAY( &refvar );
  477. //
  478. // Figure out the dimensions of the array.
  479. //
  480. hr = SafeArrayGetLBound( saAttributes, 1, &start );
  481. if( FAILED(hr) )
  482. return hr;
  483. hr = SafeArrayGetUBound( saAttributes, 1, &end );
  484. if( FAILED(hr) )
  485. return hr;
  486. VARIANT SingleResult;
  487. VariantInit( &SingleResult );
  488. //
  489. // Process the array elements.
  490. //
  491. for ( current = start ;
  492. current <= end ;
  493. current++ ) {
  494. hr = SafeArrayGetElement( saAttributes, &current, &SingleResult );
  495. if( FAILED(hr) )
  496. return hr;
  497. if ( V_VT(&SingleResult) != VT_BSTR )
  498. return E_UNEXPECTED;
  499. refstringlist.AddHead( V_BSTR(&SingleResult) );
  500. VariantClear( &SingleResult );
  501. }
  502. return S_OK;
  503. }
  504. HRESULT
  505. StringListToVariant(
  506. VARIANT& refvar,
  507. CStringList& refstringlist
  508. )
  509. {
  510. HRESULT hr = S_OK;
  511. int cCount = (int) refstringlist.GetCount();
  512. SAFEARRAYBOUND rgsabound[1];
  513. rgsabound[0].lLbound = 0;
  514. rgsabound[0].cElements = cCount;
  515. SAFEARRAY* psa = SafeArrayCreate(VT_VARIANT, 1, rgsabound);
  516. if (NULL == psa)
  517. return E_OUTOFMEMORY;
  518. VariantClear( &refvar );
  519. V_VT(&refvar) = VT_VARIANT|VT_ARRAY;
  520. V_ARRAY(&refvar) = psa;
  521. VARIANT SingleResult;
  522. VariantInit( &SingleResult );
  523. V_VT(&SingleResult) = VT_BSTR;
  524. POSITION pos = refstringlist.GetHeadPosition();
  525. long i;
  526. for (i = 0; i < cCount, pos != NULL; i++)
  527. {
  528. V_BSTR(&SingleResult) = T2BSTR((LPCTSTR)refstringlist.GetNext(pos));
  529. hr = SafeArrayPutElement(psa, &i, &SingleResult);
  530. if( FAILED(hr) )
  531. return hr;
  532. }
  533. if (i != cCount || pos != NULL)
  534. return E_UNEXPECTED;
  535. return hr;
  536. }
  537. HRESULT
  538. StringListToColumnList(
  539. ComponentData* pScopeControl,
  540. CStringList& refstringlist,
  541. ListEntry **ppNewList
  542. ) {
  543. //
  544. // Make a column list from a string list. We use
  545. // this to update the cached attributes lists.
  546. //
  547. int cCount = (int) refstringlist.GetCount();
  548. ListEntry *pHead = NULL;
  549. ListEntry *pCurrent = NULL, *pPrevious = NULL;
  550. POSITION pos = refstringlist.GetHeadPosition();
  551. CString Name;
  552. SchemaObject *pSchemaObject, *pSchemaHead;
  553. BOOLEAN fNameFound;
  554. for ( long i = 0; i < cCount, pos != NULL; i++ ) {
  555. pCurrent = new ListEntry;
  556. if ( !pCurrent ) {
  557. pScopeControl->g_SchemaCache.FreeColumnList( pHead );
  558. return E_OUTOFMEMORY;
  559. }
  560. if ( !pHead ) {
  561. pHead = pPrevious = pCurrent;
  562. } else {
  563. pPrevious->pNext = pCurrent;
  564. pPrevious = pCurrent;
  565. }
  566. //
  567. // We need to list all of these by their ldapDisplayNames,
  568. // so we have to reverse lookup the oid entries.
  569. //
  570. Name = ((LPCTSTR)refstringlist.GetNext(pos));
  571. pSchemaObject = pScopeControl->g_SchemaCache.LookupSchemaObject(
  572. Name,
  573. SCHMMGMT_CLASS );
  574. if ( !pSchemaObject ) {
  575. pSchemaObject = pScopeControl->g_SchemaCache.LookupSchemaObject(
  576. Name,
  577. SCHMMGMT_ATTRIBUTE );
  578. if ( !pSchemaObject) {
  579. //
  580. // We have to look up this oid.
  581. // First try the list of classes.
  582. //
  583. pSchemaHead = pScopeControl->g_SchemaCache.pSortedClasses;
  584. pSchemaObject = pSchemaHead;
  585. fNameFound = FALSE;
  586. do {
  587. if ( pSchemaObject->oid == Name ) {
  588. Name = pSchemaObject->ldapDisplayName;
  589. fNameFound = TRUE;
  590. break;
  591. }
  592. pSchemaObject = pSchemaObject->pSortedListFlink;
  593. } while ( pSchemaObject != pSchemaHead );
  594. //
  595. // Then try the list of attributes.
  596. //
  597. if ( !fNameFound ) {
  598. pSchemaHead = pScopeControl->g_SchemaCache.pSortedAttribs;
  599. pSchemaObject = pSchemaHead;
  600. do {
  601. if ( pSchemaObject->oid == Name ) {
  602. Name = pSchemaObject->ldapDisplayName;
  603. fNameFound = TRUE;
  604. break;
  605. }
  606. pSchemaObject = pSchemaObject->pSortedListFlink;
  607. } while ( pSchemaObject != pSchemaHead );
  608. }
  609. ASSERT( fNameFound );
  610. } else {
  611. pScopeControl->g_SchemaCache.ReleaseRef( pSchemaObject );
  612. }
  613. } else {
  614. pScopeControl->g_SchemaCache.ReleaseRef( pSchemaObject );
  615. }
  616. //
  617. // This is the ldapDisplayName!!
  618. //
  619. pCurrent->Attribute = Name;
  620. }
  621. ASSERT( cCount == i );
  622. ASSERT( pos == NULL );
  623. *ppNewList = pHead;
  624. return S_OK;
  625. }
  626. const UINT MAX_ERROR_BUF = 2048;
  627. VOID
  628. DoExtErrMsgBox(
  629. VOID
  630. )
  631. {
  632. DWORD dwLastError;
  633. WCHAR szErrorBuf[MAX_ERROR_BUF + 1];
  634. WCHAR szNameBuf[MAX_ERROR_BUF + 1];
  635. // get extended error value
  636. HRESULT hr_return = ADsGetLastError( &dwLastError,
  637. szErrorBuf,
  638. MAX_ERROR_BUF,
  639. szNameBuf,
  640. MAX_ERROR_BUF);
  641. if (SUCCEEDED(hr_return))
  642. {
  643. MessageBox( ::GetActiveWindow(),
  644. szErrorBuf,
  645. szNameBuf,
  646. MB_OK | MB_ICONSTOP );
  647. }
  648. else
  649. ASSERT( FALSE );
  650. }
  651. // INVALID_POINTER is returned by CListBox::GetItemDataPtr() in case of an error.
  652. const VOID * INVALID_POINTER = reinterpret_cast<void *>( LB_ERR );
  653. HRESULT
  654. RetrieveEditItemsWithExclusions(
  655. CListBox& refListBox,
  656. CStringList& refstringlist,
  657. CStringList* pstringlistExclusions)
  658. {
  659. CString str;
  660. CString * pstr = NULL;
  661. int nCount = refListBox.GetCount();
  662. if (LB_ERR == nCount)
  663. {
  664. ASSERT(FALSE);
  665. return E_UNEXPECTED;
  666. }
  667. for (INT i = 0; i < nCount; i++)
  668. {
  669. pstr = static_cast<CString *>( refListBox.GetItemDataPtr(i) );
  670. ASSERT( INVALID_POINTER != pstr );
  671. // don't need to search for pstr because pstr can only be a new item,
  672. // and they are never excluded.
  673. if( pstr && INVALID_POINTER != pstr )
  674. {
  675. refstringlist.AddHead( *pstr );
  676. }
  677. else
  678. {
  679. refListBox.GetText( i, str );
  680. if (NULL != pstringlistExclusions)
  681. {
  682. POSITION pos = pstringlistExclusions->Find( str );
  683. if (NULL != pos)
  684. continue;
  685. }
  686. refstringlist.AddHead( str );
  687. }
  688. }
  689. return S_OK;
  690. }
  691. //
  692. // The global cookie lists for scope and result pane items.
  693. //
  694. VOID
  695. CCookieList::AddCookie(
  696. Cookie *pCookie,
  697. HSCOPEITEM hScope
  698. ) {
  699. CCookieListEntry *pNewEntry = new CCookieListEntry;
  700. //
  701. // If there's no memory, we can't remember this and hence
  702. // our display may get a little out of whack.
  703. //
  704. if ( !pNewEntry ) {
  705. return;
  706. }
  707. pNewEntry->pCookie = pCookie;
  708. pNewEntry->hScopeItem = hScope;
  709. if ( !pHead ) {
  710. //
  711. // If this is the first one, just set the
  712. // head pointer. The constructor for the
  713. // list entry has already set the next and
  714. // back pointers.
  715. //
  716. pHead = pNewEntry;
  717. } else {
  718. //
  719. // Insert this at the end of the circular
  720. // doubly-linked list.
  721. //
  722. pNewEntry->pBack = pHead->pBack;
  723. pNewEntry->pNext = pHead;
  724. pHead->pBack->pNext = pNewEntry;
  725. pHead->pBack = pNewEntry;
  726. }
  727. return;
  728. }
  729. VOID
  730. CCookieList::InsertSortedDisplay(
  731. ComponentData *pScopeControl,
  732. SchemaObject *pNewObject
  733. )
  734. /***
  735. Notes:
  736. This function inserts the object into the
  737. sorted display list.
  738. If the object is a class and the ComponentData
  739. interface pointer is provided, this routine will
  740. also create a cookie for this object and insert
  741. the scope item into the view.
  742. ***/
  743. {
  744. HRESULT hr;
  745. CCookieListEntry *pNewEntry = NULL, *pCurrent = NULL;
  746. SCOPEDATAITEM ScopeItem;
  747. Cookie *pNewCookie= NULL;
  748. int compare;
  749. //
  750. // If this cookie list is empty, there's nothing
  751. // in the display and we don't need to do anything.
  752. //
  753. if ( !pHead ) {
  754. return;
  755. }
  756. //
  757. // Allocate a new cookie list entry. If we can't
  758. // do nothing. The display will be out of sync
  759. // until the user refreshes.
  760. //
  761. pNewEntry = new CCookieListEntry;
  762. if ( !pNewEntry ) {
  763. return;
  764. }
  765. //
  766. // Prepare the required mmc structures.
  767. //
  768. if ( pNewObject->schemaObjectType == SCHMMGMT_CLASS ) {
  769. if ( !pScopeControl ) {
  770. //
  771. // If there's no scope control, we can't insert anything.
  772. //
  773. delete pNewEntry;
  774. return;
  775. }
  776. // prefix believes that this allocation (or construction) may throw
  777. // an exception, and if an exception is thrown, pNewEntry is
  778. // leaked. After a lot of digging, it's possible that a
  779. // CMemoryException instance may be thrown by one of the base
  780. // classes of one the members of CBaseCookieBlock, which is a base
  781. // clase of Cookie.
  782. // NTRAID#NTBUG9-294879-2001/01/26-sburns
  783. try
  784. {
  785. pNewCookie = new Cookie( SCHMMGMT_CLASS,
  786. pParentCookie->QueryNonNULLMachineName() );
  787. }
  788. catch (...)
  789. {
  790. delete pNewEntry;
  791. return;
  792. }
  793. if ( !pNewCookie ) {
  794. //
  795. // If we can't allocate a cookie, do nothing.
  796. //
  797. delete pNewEntry;
  798. return;
  799. }
  800. pNewCookie->pParentCookie = pParentCookie;
  801. pNewCookie->strSchemaObject = pNewObject->commonName;
  802. pParentCookie->m_listScopeCookieBlocks.AddHead(
  803. (CBaseCookieBlock*)pNewCookie );
  804. pNewEntry->pCookie = pNewCookie;
  805. ::ZeroMemory( &ScopeItem, sizeof(ScopeItem) );
  806. ScopeItem.displayname = MMC_CALLBACK;
  807. ScopeItem.nState = 0;
  808. ScopeItem.lParam = reinterpret_cast<LPARAM>((CCookie*)pNewCookie);
  809. ScopeItem.nImage = pScopeControl->QueryImage( *pNewCookie, FALSE );
  810. ScopeItem.nOpenImage = pScopeControl->QueryImage( *pNewCookie, TRUE );
  811. }
  812. //
  813. // Should this be the new head of the list?
  814. //
  815. compare = pNewObject->ldapDisplayName.CompareNoCase(
  816. pHead->pCookie->strSchemaObject );
  817. if ( compare < 0 ) {
  818. if ( pNewObject->schemaObjectType == SCHMMGMT_CLASS ) {
  819. //
  820. // Insert this into the scope pane.
  821. //
  822. ScopeItem.mask = SDI_STR | SDI_IMAGE | SDI_OPENIMAGE | SDI_STATE |
  823. SDI_PARAM | SDI_NEXT | SDI_CHILDREN;
  824. ScopeItem.cChildren = 0;
  825. ScopeItem.relativeID = pHead->hScopeItem;
  826. hr = pScopeControl->m_pConsoleNameSpace->InsertItem( &ScopeItem );
  827. pNewEntry->hScopeItem = ScopeItem.ID;
  828. pNewEntry->pCookie->m_hScopeItem = ScopeItem.ID;
  829. } else {
  830. hr = S_OK;
  831. }
  832. if ( SUCCEEDED(hr) ) {
  833. pNewEntry->pNext = pHead;
  834. pNewEntry->pBack = pHead->pBack;
  835. pHead->pBack->pNext = pNewEntry;
  836. pHead->pBack = pNewEntry;
  837. pHead = pNewEntry;
  838. } else {
  839. delete pNewEntry;
  840. delete pNewCookie;
  841. }
  842. return;
  843. }
  844. //
  845. // Determine the sorted insertion point. The sorted list is circular.
  846. //
  847. pCurrent = pHead;
  848. while ( pCurrent->pNext != pHead ) {
  849. compare = pNewObject->ldapDisplayName.CompareNoCase(
  850. pCurrent->pNext->pCookie->strSchemaObject );
  851. if ( compare < 0 ) {
  852. break;
  853. }
  854. pCurrent = pCurrent->pNext;
  855. }
  856. //
  857. // We want to insert the new object after pCurrent.
  858. //
  859. if ( pNewObject->schemaObjectType == SCHMMGMT_CLASS ) {
  860. ScopeItem.mask = SDI_STR | SDI_IMAGE | SDI_OPENIMAGE | SDI_STATE |
  861. SDI_PARAM | SDI_PREVIOUS | SDI_CHILDREN;
  862. ScopeItem.cChildren = 0;
  863. ScopeItem.relativeID = pCurrent->hScopeItem;
  864. hr = pScopeControl->m_pConsoleNameSpace->InsertItem( &ScopeItem );
  865. pNewEntry->hScopeItem = ScopeItem.ID;
  866. pNewEntry->pCookie->m_hScopeItem = ScopeItem.ID;
  867. } else {
  868. hr = S_OK;
  869. }
  870. if ( SUCCEEDED( hr )) {
  871. pNewEntry->pNext = pCurrent->pNext;
  872. pNewEntry->pBack = pCurrent;
  873. pCurrent->pNext->pBack = pNewEntry;
  874. pCurrent->pNext = pNewEntry;
  875. } else {
  876. delete pNewEntry;
  877. delete pNewCookie;
  878. }
  879. return;
  880. }
  881. bool
  882. CCookieList::DeleteCookie(Cookie* pCookie)
  883. {
  884. bool result = false;
  885. if (!pHead)
  886. {
  887. return result;
  888. }
  889. // walk the links and stop when the scope item matches.
  890. // Since the list is circular,
  891. // we use pHead as the sentinal value instead of null.
  892. CCookieListEntry* pCurrent = pHead;
  893. do
  894. {
  895. ASSERT(pCurrent);
  896. if (pCurrent->pCookie == pCookie)
  897. {
  898. // Remove the node from the list
  899. pCurrent->pBack->pNext = pCurrent->pNext;
  900. pCurrent->pNext->pBack = pCurrent->pBack;
  901. if (pCurrent == pHead)
  902. {
  903. pHead = pCurrent->pNext;
  904. }
  905. result = true;
  906. delete pCurrent;
  907. break;
  908. }
  909. pCurrent = pCurrent->pNext;
  910. } while (pCurrent != pHead);
  911. return result;
  912. }
  913. void
  914. CCookieList::DeleteAll()
  915. {
  916. if (!pHead)
  917. {
  918. return;
  919. }
  920. CCookieListEntry* pCurrent = pHead;
  921. do
  922. {
  923. CCookieListEntry* next = pCurrent->pNext;
  924. delete pCurrent;
  925. pCurrent = next;
  926. }
  927. while (pCurrent != pHead);
  928. pHead = 0;
  929. }
  930. CString
  931. GetHelpFilename()
  932. {
  933. TCHAR buf[MAX_PATH + 1];
  934. UINT result = ::GetSystemWindowsDirectory(buf, MAX_PATH);
  935. ASSERT(result != 0 && result <= MAX_PATH);
  936. CString f(buf);
  937. f += TEXT("\\help\\schmmgmt.hlp");
  938. return f;
  939. }
  940. BOOL
  941. ShowHelp( HWND hParent, WPARAM wParam, LPARAM lParam, const DWORD ids[], BOOL fContextMenuHelp )
  942. {
  943. HWND hWndMain = NULL;
  944. UINT uCommand = 0;
  945. if( !fContextMenuHelp )
  946. {
  947. // The user has clicked ? and the control, or just F1 (if enabled)
  948. const LPHELPINFO pHelpInfo = (LPHELPINFO)lParam;
  949. if (pHelpInfo && pHelpInfo->iContextType == HELPINFO_WINDOW)
  950. {
  951. hWndMain = (HWND) pHelpInfo->hItemHandle;
  952. uCommand = HELP_WM_HELP;
  953. }
  954. }
  955. else
  956. {
  957. hWndMain = (HWND) wParam;
  958. uCommand = HELP_CONTEXTMENU;
  959. // Optimization for non-static enabled windows.
  960. // This way users don't have to do an extra menu click
  961. // $$ don't know why this call returns NULL all the time
  962. // HWND hWnd = ChildWindowFromPoint( hParent, CPoint(lParam) );
  963. // if( hWnd )
  964. // hWndMain = hWnd;
  965. if( -1 != GET_X_LPARAM(lParam) &&
  966. -1 != GET_Y_LPARAM(lParam) &&
  967. hParent &&
  968. hWndMain != hParent )
  969. {
  970. uCommand = HELP_WM_HELP;
  971. }
  972. }
  973. if( hWndMain && uCommand )
  974. {
  975. // Display context help for a control
  976. ::WinHelp( hWndMain,
  977. GetHelpFilename(),
  978. uCommand,
  979. (DWORD_PTR) ids );
  980. }
  981. return TRUE;
  982. }
  983. #if 0
  984. VOID
  985. DebugTrace(
  986. LPWSTR Format,
  987. ...
  988. ) {
  989. WCHAR DbgString[1024];
  990. va_list arglist;
  991. int Length;
  992. //
  993. // Format the output into a buffer and then print it.
  994. //
  995. va_start(arglist, Format);
  996. Length = wvsprintf( DbgString, Format, arglist );
  997. va_end(arglist);
  998. ASSERT( Length <= 1024 );
  999. ASSERT( Length != 0 );
  1000. OutputDebugString( DbgString );
  1001. return;
  1002. }
  1003. #else
  1004. VOID
  1005. DebugTrace(
  1006. LPWSTR,
  1007. ...
  1008. ) {
  1009. ;
  1010. }
  1011. #endif
  1012. // Attempt to locate a message in a given module. Return the message string
  1013. // if found, the empty string if not.
  1014. //
  1015. // flags - FormatMessage flags to use
  1016. //
  1017. // module - module handle of message dll to look in, or 0 to use the system
  1018. // message table.
  1019. //
  1020. // code - message code to look for
  1021. CString
  1022. getMessageHelper(DWORD flags, HMODULE module, HRESULT code)
  1023. {
  1024. ASSERT(code);
  1025. ASSERT(flags & FORMAT_MESSAGE_ALLOCATE_BUFFER);
  1026. CString message;
  1027. TCHAR* sys_message = 0;
  1028. DWORD result =
  1029. ::FormatMessage(
  1030. flags,
  1031. module,
  1032. static_cast<DWORD>(code),
  1033. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  1034. reinterpret_cast<LPTSTR>(&sys_message),
  1035. 0,
  1036. 0);
  1037. if (result)
  1038. {
  1039. ASSERT(sys_message);
  1040. if (sys_message)
  1041. {
  1042. message = sys_message;
  1043. ::LocalFree(sys_message);
  1044. }
  1045. }
  1046. return message;
  1047. }
  1048. #define MAX_ERROR_BUF 2048
  1049. HRESULT
  1050. GetLastADsError( HRESULT hr, CString& refErrorMsg, CString& refName )
  1051. {
  1052. ASSERT(FAILED(hr));
  1053. refErrorMsg.Empty();
  1054. refName.Empty();
  1055. if (!FAILED(hr))
  1056. {
  1057. return hr;
  1058. }
  1059. if( FACILITY_WIN32 == HRESULT_FACILITY(hr) )
  1060. {
  1061. DWORD dwLastError = 0;
  1062. WCHAR szErrorBuf[ MAX_ERROR_BUF + 1 ];
  1063. WCHAR szNameBuf[ MAX_ERROR_BUF + 1 ];
  1064. //Get extended error value.
  1065. HRESULT hr_return = ADsGetLastError( &dwLastError,
  1066. szErrorBuf,
  1067. MAX_ERROR_BUF,
  1068. szNameBuf,
  1069. MAX_ERROR_BUF );
  1070. ASSERT( SUCCEEDED(hr_return) );
  1071. if( SUCCEEDED(hr_return) && dwLastError )
  1072. {
  1073. refErrorMsg = szErrorBuf;
  1074. refName = szNameBuf;
  1075. return HRESULT_FROM_WIN32( dwLastError );
  1076. }
  1077. }
  1078. return hr;
  1079. }
  1080. // Attempts to locate message strings for various facility codes in the
  1081. // HRESULT. If fTryADSIExtError is true, check ADsGetLastError() first
  1082. CString
  1083. GetErrorMessage( HRESULT hr, BOOL fTryADSIExtError /* = FALSE */ )
  1084. {
  1085. ASSERT(FAILED(hr));
  1086. if (!FAILED(hr))
  1087. {
  1088. // no messages for success!
  1089. return CString();
  1090. }
  1091. CString strExtMsg;
  1092. if( fTryADSIExtError &&
  1093. FACILITY_WIN32 == HRESULT_FACILITY(hr) )
  1094. {
  1095. DWORD dwLastError = 0;
  1096. WCHAR szErrorBuf[ MAX_ERROR_BUF + 1 ];
  1097. WCHAR szNameBuf[ MAX_ERROR_BUF + 1 ];
  1098. //Get extended error value.
  1099. HRESULT hr_return = ADsGetLastError( &dwLastError,
  1100. szErrorBuf,
  1101. MAX_ERROR_BUF,
  1102. szNameBuf,
  1103. MAX_ERROR_BUF );
  1104. ASSERT( SUCCEEDED(hr_return) );
  1105. if( SUCCEEDED(hr_return) && dwLastError )
  1106. {
  1107. hr = HRESULT_FROM_WIN32( dwLastError );
  1108. strExtMsg = szErrorBuf;
  1109. }
  1110. }
  1111. HRESULT code = HRESULT_CODE(hr);
  1112. CString message;
  1113. // default is the system error message table
  1114. HMODULE module = 0;
  1115. DWORD flags =
  1116. FORMAT_MESSAGE_ALLOCATE_BUFFER
  1117. | FORMAT_MESSAGE_IGNORE_INSERTS
  1118. | FORMAT_MESSAGE_FROM_SYSTEM;
  1119. int facility = HRESULT_FACILITY(hr);
  1120. switch (facility)
  1121. {
  1122. case FACILITY_WIN32: // 0x7
  1123. {
  1124. // included here:
  1125. // lanman error codes (in it's own dll)
  1126. // dns
  1127. // winsock
  1128. static HMODULE lm_err_res_dll = 0;
  1129. if (code >= NERR_BASE && code <= MAX_NERR)
  1130. {
  1131. // use the net error message resource dll
  1132. if (lm_err_res_dll == 0)
  1133. {
  1134. lm_err_res_dll =
  1135. ::LoadLibraryEx(
  1136. L"netmsg.dll",
  1137. 0,
  1138. LOAD_LIBRARY_AS_DATAFILE);
  1139. }
  1140. module = lm_err_res_dll;
  1141. flags |= FORMAT_MESSAGE_FROM_HMODULE;
  1142. }
  1143. break;
  1144. }
  1145. case 0x0:
  1146. {
  1147. if (code >= 0x5000 && code <= 0x50FF)
  1148. {
  1149. // It's an ADSI error. They put the facility code (5) in the
  1150. // wrong place!
  1151. static HMODULE adsi_err_res_dll = 0;
  1152. // use the net error message resource dll
  1153. if (adsi_err_res_dll == 0)
  1154. {
  1155. adsi_err_res_dll =
  1156. ::LoadLibraryEx(
  1157. L"activeds.dll",
  1158. 0,
  1159. LOAD_LIBRARY_AS_DATAFILE);
  1160. }
  1161. module = adsi_err_res_dll;
  1162. flags |= FORMAT_MESSAGE_FROM_HMODULE;
  1163. // the message dll expects the entire error code
  1164. code = hr;
  1165. }
  1166. break;
  1167. }
  1168. default:
  1169. {
  1170. // do nothing
  1171. break;
  1172. }
  1173. }
  1174. message = getMessageHelper(flags, module, code);
  1175. #ifdef SHOW_EXT_LDAP_MSG
  1176. if( !strExtMsg.IsEmpty() )
  1177. message += L"\n" + strExtMsg;
  1178. #endif //SHOW_EXT_LDAP_MSG
  1179. if (message.IsEmpty())
  1180. {
  1181. message.LoadString(IDS_UNKNOWN_ERROR_MESSAGE);
  1182. }
  1183. return message;
  1184. }
  1185. //
  1186. // Get range from edit controls, Verify the range, attempt to correct, make sure lower <= upper.
  1187. //
  1188. // an exception will be thrown in case of an error.
  1189. //
  1190. void DDXV_VerifyAttribRange( CDataExchange *pDX, BOOL fIsSigned,
  1191. UINT idcLower, CString & strLower,
  1192. UINT idcUpper, CString & strUpper )
  1193. {
  1194. INT64 llLower = 0;
  1195. INT64 llUpper = 0;
  1196. ASSERT( pDX );
  1197. ASSERT( pDX->m_pDlgWnd );
  1198. // Update the values.
  1199. llLower = DDXV_SigUnsigINT32Value( pDX, fIsSigned, idcLower, strLower );
  1200. llUpper = DDXV_SigUnsigINT32Value( pDX, fIsSigned, idcUpper, strUpper );
  1201. #ifdef ENABLE_NEGATIVE_INT
  1202. // verify that lower <= upper -- only if supporting ENABLE_NEGATIVE_INT
  1203. if ( pDX->m_bSaveAndValidate && !strLower.IsEmpty() && !strUpper.IsEmpty() )
  1204. {
  1205. if( llLower > llUpper )
  1206. {
  1207. DoErrMsgBox( pDX->m_pDlgWnd->m_hWnd, TRUE, IDS_ERR_EDIT_MINMAX );
  1208. pDX->Fail(); // we are still at the second edit control.
  1209. }
  1210. }
  1211. #endif //ENABLE_NEGATIVE_INT
  1212. }
  1213. //
  1214. // Get string from an edit control, verify it attempting to correct
  1215. //
  1216. // an exception will be thrown in case of an error.
  1217. //
  1218. // Returns corrected value
  1219. //
  1220. INT64 DDXV_SigUnsigINT32Value( CDataExchange *pDX, BOOL fIsSigned,
  1221. UINT idc, CString & str )
  1222. {
  1223. INT64 llVal = 0;
  1224. HRESULT hr = S_OK;
  1225. ASSERT( pDX );
  1226. ASSERT( pDX->m_pDlgWnd );
  1227. // Get/Put the string
  1228. DDX_Text( pDX, idc, str );
  1229. if ( pDX->m_bSaveAndValidate )
  1230. {
  1231. if( !str.IsEmpty() )
  1232. {
  1233. hr = GetSafeINT32FromString( pDX->m_pDlgWnd, llVal, str,
  1234. fIsSigned, GETSAFEINT_ALLOW_CANCEL );
  1235. if( FAILED(hr) )
  1236. {
  1237. pDX->Fail();
  1238. }
  1239. else if( S_VALUE_MODIFIED == hr )
  1240. {
  1241. // update the string in case of some conversion things ('010' --> '10')
  1242. // or if the value was changed
  1243. pDX->m_pDlgWnd->SetDlgItemText( idc, str );
  1244. }
  1245. }
  1246. }
  1247. return llVal;
  1248. }
  1249. //
  1250. // Converts a string to a DWORD, asks to correct to be within the range.
  1251. // returns HRESULT:
  1252. // S_OK llDst is the value from string
  1253. // S_VALUE_MODIFIED llDst is the truncated value, strSrc is updated
  1254. // E_ABORT llDst is unchanged; E_ABORT may be returned only if fAllowCancel is TRUE
  1255. //
  1256. HRESULT GetSafeSignedDWORDFromString( CWnd * pwndParent, DWORD & dwDst, CString & strSrc,
  1257. BOOL fIsSigned, BOOL fAllowCancel /* =FALSE */)
  1258. {
  1259. INT64 llDst = 0;
  1260. HRESULT hr = GetSafeINT32FromString( pwndParent, llDst, strSrc, fIsSigned, fAllowCancel );
  1261. if( SUCCEEDED( hr ) )
  1262. dwDst = (DWORD) llDst;
  1263. return hr;
  1264. }
  1265. //
  1266. // *** internal use ***
  1267. // Converts a string to a INT64, asks to correct to be within the range.
  1268. // returns HRESULT:
  1269. // S_OK llDst is the value from string
  1270. // S_VALUE_MODIFIED llDst is the truncated value, strSrc is updated
  1271. // E_ABORT llDst is the truncated value
  1272. // E_ABORT only happens if fAllowCancel is TRUE
  1273. //
  1274. HRESULT GetSafeINT32FromString( CWnd * pwndParent, INT64 & llDst, CString & strSrc,
  1275. BOOL fIsSigned, BOOL fAllowCancel)
  1276. {
  1277. HRESULT hr = S_OK;
  1278. UINT nMessageBoxType = 0;
  1279. CString szMsg;
  1280. CString szSugestedNumber;
  1281. BOOL fIsValidNumber = TRUE;
  1282. BOOL fIsValidString = TRUE;
  1283. ASSERT( pwndParent );
  1284. // the string must be limited in length & not empty
  1285. ASSERT( !strSrc.IsEmpty() );
  1286. ASSERT( strSrc.GetLength() <= cchMinMaxRange );
  1287. fIsValidString = IsValidNumberString( strSrc );
  1288. llDst = _wtoi64( (LPCWSTR) strSrc );
  1289. fIsValidNumber = IsValidNumber32( llDst, fIsSigned );
  1290. szSugestedNumber.Format( fIsSigned ? g_INT32_FORMAT : g_UINT32_FORMAT, (DWORD) llDst );
  1291. if( !fIsValidString || !fIsValidNumber )
  1292. {
  1293. szMsg.FormatMessage( !fIsValidString ? IDS_ERR_NUM_IS_ILLIGAL : IDS_ERR_INT_OVERFLOW,
  1294. (LPCWSTR) strSrc, (LPCWSTR) szSugestedNumber );
  1295. // make sure the user wants to do it
  1296. nMessageBoxType = (fAllowCancel ? MB_OKCANCEL : MB_OK) | MB_ICONEXCLAMATION ;
  1297. if( IDOK == pwndParent->MessageBox( szMsg, g_MsgBoxErr, nMessageBoxType ) )
  1298. {
  1299. strSrc = szSugestedNumber;
  1300. hr = S_VALUE_MODIFIED;
  1301. }
  1302. else
  1303. {
  1304. hr = E_ABORT;
  1305. }
  1306. }
  1307. else if( strSrc != szSugestedNumber )
  1308. {
  1309. // fixing number formating
  1310. strSrc = szSugestedNumber;
  1311. hr = S_VALUE_MODIFIED;
  1312. }
  1313. return hr;
  1314. }
  1315. //
  1316. // Verify & correct Min/Max for a signed/unsigned INT64 value
  1317. //
  1318. BOOL IsValidNumber32( INT64 & llVal, BOOL fIsSigned )
  1319. {
  1320. #ifdef ENABLE_NEGATIVE_INT
  1321. const INT64 llMinVal = fIsSigned ? (INT64) _I32_MIN : (INT64) 0 ;
  1322. const INT64 llMaxVal = fIsSigned ? (INT64) _I32_MAX : (INT64) _UI32_MAX ;
  1323. #else
  1324. // if there is no negative numbers support, always use unsigned numbers
  1325. const INT64 llMinVal = (INT64) 0;
  1326. const INT64 llMaxVal = (INT64) _UI32_MAX;
  1327. #endif
  1328. BOOL fIsValid = FALSE;
  1329. // if larger than 32bit number (signed/unsigned), truncate...
  1330. if( llVal < llMinVal )
  1331. {
  1332. llVal = llMinVal;
  1333. }
  1334. else if( llVal > llMaxVal )
  1335. {
  1336. llVal = llMaxVal;
  1337. }
  1338. else
  1339. {
  1340. fIsValid = TRUE;
  1341. }
  1342. return fIsValid;
  1343. }
  1344. //
  1345. // Search number string for illigal characters.
  1346. //
  1347. BOOL IsValidNumberString( CString & str )
  1348. {
  1349. int i = 0;
  1350. #ifdef ENABLE_NEGATIVE_INT
  1351. if( str.GetLength() > 0 && // allow negative sign in front of the number
  1352. g_chNegativeSign == str[ i ] )
  1353. {
  1354. i++; // skip first character
  1355. }
  1356. #endif //ENABLE_NEGATIVE_INT
  1357. for( ; i < str.GetLength(); i++ )
  1358. {
  1359. if( !IsCharNumeric( str[i] ) )
  1360. return FALSE;
  1361. }
  1362. return TRUE;
  1363. }
  1364. /////////////////////////////////////////////////////////////////////////////
  1365. // ParsedEdit
  1366. BEGIN_MESSAGE_MAP(CParsedEdit, CEdit)
  1367. //{{AFX_MSG_MAP(CParsedEdit)
  1368. ON_WM_CHAR()
  1369. //}}AFX_MSG_MAP
  1370. END_MESSAGE_MAP()
  1371. /////////////////////////////////////////////////////////////////////////////
  1372. // Initialize subclassing
  1373. BOOL CParsedEdit::SubclassEdit( UINT nID,
  1374. CWnd* pParent,
  1375. int cchMaxTextSize ) // 0 == unlimited
  1376. {
  1377. ASSERT( IsInitialized() );
  1378. ASSERT( nID );
  1379. ASSERT( pParent );
  1380. ASSERT( pParent->GetDlgItem(nID) );
  1381. ASSERT( cchMaxTextSize >= 0 );
  1382. ( static_cast<CEdit *>( pParent->GetDlgItem(nID) ) ) -> LimitText( cchMaxTextSize ) ;
  1383. if( EDIT_TYPE_GENERIC == GetEditType() )
  1384. {
  1385. return TRUE; // no need to subclass - everything is allowed.
  1386. }
  1387. else
  1388. {
  1389. return SubclassDlgItem(nID, pParent);
  1390. }
  1391. }
  1392. /////////////////////////////////////////////////////////////////////////////
  1393. // Input character filter
  1394. void CParsedEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
  1395. {
  1396. ASSERT( IsInitialized() ); // initialized?
  1397. BOOL fIsSpecialChar = ( nChar < 0x20 );
  1398. BOOL fAllowChar = FALSE;
  1399. if( fIsSpecialChar )
  1400. {
  1401. fAllowChar = TRUE; // always allow control chars
  1402. }
  1403. else
  1404. {
  1405. // is this a digit?
  1406. BOOL fIsDigit = IsCharNumeric( (TCHAR)nChar );
  1407. switch( GetEditType() )
  1408. {
  1409. default:
  1410. ASSERT( FALSE );
  1411. break;
  1412. case EDIT_TYPE_GENERIC: // everything is allowed
  1413. fAllowChar = TRUE;
  1414. break;
  1415. case EDIT_TYPE_INT32:
  1416. case EDIT_TYPE_UINT32:
  1417. {
  1418. #ifdef ENABLE_NEGATIVE_INT
  1419. const BOOL fAllowNegativeNumbers = TRUE;
  1420. #else
  1421. const BOOL fAllowNegativeNumbers = FALSE;
  1422. #endif
  1423. DWORD dwSel = GetSel();
  1424. // is the caret in the begining of the box
  1425. BOOL fLineFront = ! LOWORD( dwSel );
  1426. // is the first character selected? (thus, typing anything will overide it)
  1427. BOOL fIsSelFirstChar = fLineFront && HIWORD( dwSel );
  1428. BOOL fIsNegSign = ( (TCHAR)nChar == g_chNegativeSign );
  1429. WCHAR szBuf[ 2 ] = {0}; // we only need the first character to check for '-'
  1430. // if the first character is selected, no matter what we type it will be overwritten
  1431. // an empty value is a positive value.
  1432. // rellies on left to right execution.
  1433. BOOL fIsAlreadyNeg = ( !fIsSelFirstChar &&
  1434. GetWindowText( szBuf, 2 ) &&
  1435. g_chNegativeSign == szBuf[0] );
  1436. ASSERT( !fIsDigit || !fIsNegSign ); // cannot be both!
  1437. if (
  1438. ( fIsDigit && // allow numeric if ...
  1439. ( !fAllowNegativeNumbers || // ignore error checking if false
  1440. !fLineFront || // not first position
  1441. (fLineFront && !fIsAlreadyNeg)) // first pos & no '-' sign
  1442. )
  1443. ||
  1444. ( fIsNegSign && // allow '-' if
  1445. fAllowNegativeNumbers && // negatives are allowed
  1446. FIsSigned() && // signed numbers are allowed
  1447. !fIsAlreadyNeg && // the number was positive
  1448. fLineFront // entering as the first character
  1449. )
  1450. )
  1451. {
  1452. fAllowChar = TRUE;
  1453. }
  1454. }
  1455. break;
  1456. case EDIT_TYPE_OID: // do a light checking -- allow digits & periods
  1457. {
  1458. if( fIsDigit ||
  1459. g_chPeriod == (TCHAR)nChar )
  1460. {
  1461. fAllowChar = TRUE;
  1462. }
  1463. }
  1464. break;
  1465. }
  1466. }
  1467. if( fAllowChar )
  1468. {
  1469. CEdit::OnChar(nChar, nRepCnt, nFlags); // permitted
  1470. }
  1471. else
  1472. { // not permitted
  1473. MessageBeep((UINT)-1); // Standard beep
  1474. }
  1475. }
  1476. /////////////////////////////////////////////////////////////////////////////
  1477. //
  1478. // Search a list of PCTSTR for a strValue, returns TRUE if found
  1479. // rgszList[] last element must be NULL
  1480. //
  1481. // puIndex - optional pointer, will be set to the position of the value if found.
  1482. //
  1483. BOOL
  1484. IsInList( PCTSTR rgszList[], const CString & strValue, UINT * puIndex /* = NULL */ )
  1485. {
  1486. UINT uIndex = 0;
  1487. while( rgszList[ uIndex ] )
  1488. {
  1489. if( !strValue.CompareNoCase( rgszList[uIndex] ) )
  1490. {
  1491. if( puIndex )
  1492. *puIndex = uIndex;
  1493. return TRUE;
  1494. }
  1495. else
  1496. uIndex++;
  1497. }
  1498. return FALSE;
  1499. }
  1500. #define ADS_SYSTEMFLAG_SCHEMA_CONSTRUCTED 0x04
  1501. #define ADS_SYSTEMFLAG_SCHEMA_BASE_OBJECT 0x10
  1502. //
  1503. // Determine if the object pointed to by pIADsObject is a constructed object.
  1504. //
  1505. HRESULT
  1506. IsConstructedObject( IADs *pIADsObject, BOOL & fIsConstructed )
  1507. {
  1508. LONG fSysAttribs = 0;
  1509. HRESULT hr = GetSystemAttributes( pIADsObject, fSysAttribs );
  1510. if( SUCCEEDED(hr) )
  1511. fIsConstructed = ADS_SYSTEMFLAG_SCHEMA_CONSTRUCTED & fSysAttribs;
  1512. return hr;
  1513. }
  1514. //
  1515. // Determine if the object pointed to by pIADsObject is category 1 object.
  1516. //
  1517. HRESULT
  1518. IsCategory1Object( IADs *pIADsObject, BOOL & fIsCategory1 )
  1519. {
  1520. LONG fSysAttribs = 0;
  1521. HRESULT hr = GetSystemAttributes( pIADsObject, fSysAttribs );
  1522. if( SUCCEEDED(hr) )
  1523. fIsCategory1 = ADS_SYSTEMFLAG_SCHEMA_BASE_OBJECT & fSysAttribs;
  1524. return hr;
  1525. }
  1526. //
  1527. // Read object's System Attribute
  1528. //
  1529. HRESULT
  1530. GetSystemAttributes( IADs *pIADsObject, LONG &fSysAttribs )
  1531. {
  1532. HRESULT hr = E_FAIL;
  1533. VARIANT AdsResult;
  1534. if( !pIADsObject )
  1535. ASSERT( FALSE );
  1536. else
  1537. {
  1538. VariantInit( &AdsResult );
  1539. hr = pIADsObject->Get( const_cast<BSTR>((LPCTSTR)g_systemFlags), &AdsResult );
  1540. if ( SUCCEEDED( hr ) )
  1541. {
  1542. ASSERT(AdsResult.vt == VT_I4);
  1543. fSysAttribs = V_I4(&AdsResult);
  1544. }
  1545. else if( E_ADS_PROPERTY_NOT_FOUND == hr )
  1546. {
  1547. fSysAttribs = 0;
  1548. hr = S_OK;
  1549. }
  1550. VariantClear( &AdsResult );
  1551. }
  1552. return hr;
  1553. }
  1554. HRESULT
  1555. DissableReadOnlyAttributes( CWnd * pwnd, IADs *pIADsObject, const CDialogControlsInfo * pCtrls, UINT cCtrls )
  1556. {
  1557. ASSERT( pwnd );
  1558. ASSERT( pIADsObject );
  1559. ASSERT( pCtrls );
  1560. ASSERT( cCtrls );
  1561. HRESULT hr = S_OK;
  1562. CStringList strlist;
  1563. do
  1564. {
  1565. // extract the list of allowed attributes
  1566. hr = GetStringListElement( pIADsObject, &g_allowedAttributesEffective, strlist );
  1567. BREAK_ON_FAILED_HRESULT(hr);
  1568. for( UINT ind = 0; ind < cCtrls; ind++ )
  1569. {
  1570. BOOL fFound = FALSE;
  1571. // search for needed attributes
  1572. for( POSITION pos = strlist.GetHeadPosition(); !fFound && pos != NULL; )
  1573. {
  1574. CString * pstr = &strlist.GetNext( pos );
  1575. if( !pstr->CompareNoCase( pCtrls[ind].m_pszAttributeName ) )
  1576. {
  1577. fFound = TRUE;
  1578. }
  1579. }
  1580. if( !fFound )
  1581. {
  1582. ASSERT( pwnd->GetDlgItem( pCtrls[ind].m_nID ) );
  1583. if( pCtrls[ind].m_fIsEditBox )
  1584. reinterpret_cast<CEdit *>( pwnd->GetDlgItem(pCtrls[ind].m_nID) )->SetReadOnly();
  1585. else
  1586. pwnd->GetDlgItem(pCtrls[ind].m_nID)->EnableWindow( FALSE );
  1587. }
  1588. }
  1589. } while( FALSE );
  1590. return hr;
  1591. }
  1592. HRESULT GetStringListElement( IADs *pIADsObject, LPWSTR *lppPathNames, CStringList &strlist )
  1593. {
  1594. ASSERT( pIADsObject );
  1595. ASSERT( lppPathNames );
  1596. ASSERT( *lppPathNames );
  1597. HRESULT hr = S_OK;
  1598. VARIANT varAttributes;
  1599. VariantInit( &varAttributes );
  1600. strlist.RemoveAll();
  1601. do
  1602. {
  1603. // build an array of one element
  1604. hr = ADsBuildVarArrayStr( lppPathNames, 1, &varAttributes );
  1605. ASSERT_BREAK_ON_FAILED_HRESULT(hr);
  1606. hr = pIADsObject->GetInfoEx( varAttributes, 0 );
  1607. BREAK_ON_FAILED_HRESULT(hr);
  1608. hr = VariantClear( &varAttributes );
  1609. ASSERT_BREAK_ON_FAILED_HRESULT(hr);
  1610. // Get all allowed attributes
  1611. hr = pIADsObject->GetEx( *lppPathNames, &varAttributes );
  1612. BREAK_ON_FAILED_HRESULT(hr);
  1613. // Convert result to a string list
  1614. hr = VariantToStringList( varAttributes, strlist );
  1615. ASSERT( SUCCEEDED(hr) || E_ADS_PROPERTY_NOT_FOUND == hr );
  1616. BREAK_ON_FAILED_HRESULT(hr);
  1617. } while( FALSE );
  1618. VariantClear( &varAttributes );
  1619. return hr;
  1620. }
  1621. bool OIDHasValidFormat (PCWSTR pszOidValue, int& rErrorTypeStrID)
  1622. {
  1623. rErrorTypeStrID = 0;
  1624. bool bFormatIsValid = false;
  1625. int nLen = WideCharToMultiByte(
  1626. CP_ACP, // code page
  1627. 0, // performance and mapping flags
  1628. pszOidValue, // wide-character string
  1629. (int) wcslen (pszOidValue), // number of chars in string
  1630. 0, // buffer for new string
  1631. 0, // size of buffer
  1632. 0, // default for unmappable chars
  1633. 0); // set when default char used
  1634. if ( nLen > 0 )
  1635. {
  1636. nLen++; // account for Null terminator
  1637. PSTR pszAnsiBuf = new CHAR[nLen];
  1638. if ( pszAnsiBuf )
  1639. {
  1640. ZeroMemory (pszAnsiBuf, nLen*sizeof(CHAR));
  1641. nLen = WideCharToMultiByte(
  1642. CP_ACP, // code page
  1643. 0, // performance and mapping flags
  1644. pszOidValue, // wide-character string
  1645. (int) wcslen (pszOidValue), // number of chars in string
  1646. pszAnsiBuf, // buffer for new string
  1647. nLen, // size of buffer
  1648. 0, // default for unmappable chars
  1649. 0); // set when default char used
  1650. if ( nLen )
  1651. {
  1652. // According to PhilH:
  1653. // The first number is limited to
  1654. // 0,1 or 2. The second number is
  1655. // limited to 0 - 39 when the first
  1656. // number is 0 or 1. Otherwise, any
  1657. // number.
  1658. // Also, according to X.208, there
  1659. // must be at least 2 numbers.
  1660. bFormatIsValid = true;
  1661. size_t cbAnsiBufLen = strlen (pszAnsiBuf);
  1662. // check for only digits and "."
  1663. size_t nIdx = strspn (pszAnsiBuf, "0123456789.\0");
  1664. if ( nIdx > 0 && nIdx < cbAnsiBufLen )
  1665. {
  1666. bFormatIsValid = false;
  1667. rErrorTypeStrID = IDS_OID_CONTAINS_NON_DIGITS;
  1668. }
  1669. // check for consecutive "."s - string not valid if present
  1670. if ( bFormatIsValid && strstr (pszAnsiBuf, "..") )
  1671. {
  1672. bFormatIsValid = false;
  1673. rErrorTypeStrID = IDS_OID_CONTAINS_CONSECUTIVE_DOTS;
  1674. }
  1675. // must begin with "0." or "1." or "2."
  1676. bool bFirstNumberIs0 = false;
  1677. bool bFirstNumberIs1 = false;
  1678. bool bFirstNumberIs2 = false;
  1679. if ( bFormatIsValid )
  1680. {
  1681. if ( !strncmp (pszAnsiBuf, "0.", 2) )
  1682. bFirstNumberIs0 = true;
  1683. else if ( !strncmp (pszAnsiBuf, "1.", 2) )
  1684. bFirstNumberIs1 = true;
  1685. else if ( !strncmp (pszAnsiBuf, "2.", 2) )
  1686. bFirstNumberIs2 = true;
  1687. if ( !bFirstNumberIs0 && !bFirstNumberIs1 && !bFirstNumberIs2 )
  1688. {
  1689. bFormatIsValid = false;
  1690. rErrorTypeStrID = IDS_OID_MUST_START_WITH_0_1_2;
  1691. }
  1692. }
  1693. if ( bFormatIsValid && ( bFirstNumberIs0 || bFirstNumberIs1 ) )
  1694. {
  1695. PSTR pszBuf = pszAnsiBuf;
  1696. pszBuf += 2;
  1697. // there must be a number after the dot
  1698. if ( strlen (pszBuf) )
  1699. {
  1700. // truncate the string at the next dot, if any
  1701. PSTR pszDot = strstr (pszBuf, ".");
  1702. if ( pszDot )
  1703. pszDot[0] = 0;
  1704. // convert the string to a number and check for range 0-39
  1705. int nValue = atoi (pszBuf);
  1706. if ( nValue < 0 || nValue > 39 )
  1707. {
  1708. bFormatIsValid = false;
  1709. rErrorTypeStrID = IDS_OID_0_1_MUST_BE_0_TO_39;
  1710. }
  1711. }
  1712. else
  1713. {
  1714. bFormatIsValid = false;
  1715. rErrorTypeStrID = IDS_OID_MUST_HAVE_TWO_NUMBERS;
  1716. }
  1717. }
  1718. // ensure no trailing "."
  1719. if ( bFormatIsValid )
  1720. {
  1721. if ( '.' == pszAnsiBuf[cbAnsiBufLen - 1] )
  1722. {
  1723. bFormatIsValid = false;
  1724. rErrorTypeStrID = IDS_OID_CANNOT_END_WITH_DOT;
  1725. }
  1726. }
  1727. if ( bFormatIsValid )
  1728. {
  1729. bFormatIsValid = false;
  1730. CRYPT_ATTRIBUTE cryptAttr;
  1731. ::ZeroMemory (&cryptAttr, sizeof (CRYPT_ATTRIBUTE));
  1732. cryptAttr.cValue = 0;
  1733. cryptAttr.pszObjId = pszAnsiBuf;
  1734. cryptAttr.rgValue = 0;
  1735. DWORD cbEncoded = 0;
  1736. BOOL bResult = CryptEncodeObject (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  1737. PKCS_ATTRIBUTE,
  1738. &cryptAttr,
  1739. NULL,
  1740. &cbEncoded);
  1741. if ( cbEncoded > 0 )
  1742. {
  1743. BYTE* pBuffer = new BYTE[cbEncoded];
  1744. if ( pBuffer )
  1745. {
  1746. bResult = CryptEncodeObject (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  1747. PKCS_ATTRIBUTE,
  1748. &cryptAttr,
  1749. pBuffer,
  1750. &cbEncoded);
  1751. if ( bResult )
  1752. {
  1753. DWORD cbStructInfo = 0;
  1754. bResult = CryptDecodeObject (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  1755. PKCS_ATTRIBUTE,
  1756. pBuffer,
  1757. cbEncoded,
  1758. 0,
  1759. 0,
  1760. &cbStructInfo);
  1761. if ( cbStructInfo > 0 )
  1762. {
  1763. BYTE* pStructBuf = new BYTE[cbStructInfo];
  1764. if ( pStructBuf )
  1765. {
  1766. bResult = CryptDecodeObject (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  1767. PKCS_ATTRIBUTE,
  1768. pBuffer,
  1769. cbEncoded,
  1770. 0,
  1771. pStructBuf,
  1772. &cbStructInfo);
  1773. if ( bResult )
  1774. {
  1775. CRYPT_ATTRIBUTE* pCryptAttr = (CRYPT_ATTRIBUTE*) pStructBuf;
  1776. if ( !strcmp (pszAnsiBuf, pCryptAttr->pszObjId) )
  1777. {
  1778. bFormatIsValid = true;
  1779. }
  1780. }
  1781. delete [] pStructBuf;
  1782. }
  1783. }
  1784. }
  1785. delete [] pBuffer;
  1786. }
  1787. }
  1788. }
  1789. }
  1790. else
  1791. {
  1792. DebugTrace(L"WideCharToMultiByte (%s) failed: 0x%x\n", pszOidValue,
  1793. GetLastError ());
  1794. }
  1795. delete [] pszAnsiBuf;
  1796. }
  1797. }
  1798. else
  1799. {
  1800. DebugTrace(L"WideCharToMultiByte (%s) failed: 0x%x\n", pszOidValue,
  1801. GetLastError ());
  1802. }
  1803. return bFormatIsValid;
  1804. }
  1805. HRESULT
  1806. DeleteObject(
  1807. const CString& path,
  1808. Cookie* pcookie,
  1809. PCWSTR pszClass
  1810. )
  1811. /***
  1812. This deletes an attribute from the schema
  1813. ***/
  1814. {
  1815. HRESULT hr = S_OK;
  1816. do
  1817. {
  1818. if ( !pcookie )
  1819. {
  1820. hr = E_INVALIDARG;
  1821. break;
  1822. }
  1823. CComPtr<IADsPathname> spIADsPathname;
  1824. hr = ::CoCreateInstance( CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER,
  1825. IID_IADsPathname, (void**)&spIADsPathname);
  1826. if ( FAILED(hr) )
  1827. {
  1828. break;
  1829. }
  1830. hr = spIADsPathname->Set( (PWSTR)(PCWSTR)path, ADS_SETTYPE_FULL );
  1831. if ( FAILED(hr) )
  1832. {
  1833. break;
  1834. }
  1835. // Get the RDN so that we have it for deleting
  1836. CComBSTR sbstrRDN;
  1837. hr = spIADsPathname->Retrieve( ADS_FORMAT_LEAF, &sbstrRDN );
  1838. if ( FAILED(hr) )
  1839. {
  1840. break;
  1841. }
  1842. // Get the path to the parent container
  1843. hr = spIADsPathname->RemoveLeafElement();
  1844. if ( FAILED(hr) )
  1845. {
  1846. break;
  1847. }
  1848. CComBSTR sbstrParentPath;
  1849. hr = spIADsPathname->Retrieve( ADS_FORMAT_X500, &sbstrParentPath );
  1850. if ( FAILED(hr) )
  1851. {
  1852. break;
  1853. }
  1854. // Now open the parent object
  1855. CComPtr<IADsContainer> spContainer;
  1856. hr = ::ADsOpenObject( sbstrParentPath, NULL, NULL, ADS_SECURE_AUTHENTICATION,
  1857. IID_IADsContainer, (void**)&spContainer);
  1858. if ( FAILED(hr) )
  1859. {
  1860. break;
  1861. }
  1862. hr = spContainer->Delete( (PWSTR)pszClass, sbstrRDN );
  1863. if ( FAILED(hr) )
  1864. {
  1865. break;
  1866. }
  1867. } while (false);
  1868. return hr;
  1869. }