mirror of https://github.com/tongzx/nt5src
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.
2404 lines
64 KiB
2404 lines
64 KiB
/****
|
|
|
|
SchmUtil.cpp
|
|
|
|
Various common utility routines for the Schema Editor Snap-In.
|
|
|
|
****/
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "macros.h"
|
|
USE_HANDLE_MACROS("SCHMMGMT(schmutil.cpp)")
|
|
|
|
#include "resource.h"
|
|
#include "cache.h"
|
|
#include "schmutil.h"
|
|
#include "compdata.h"
|
|
|
|
#include <wincrypt.h> // CryptEncodeObject() and CryptDecodeObject()
|
|
|
|
//
|
|
// Removed from public headers by DS guys
|
|
// See bug 454342 XOM will not survive the transition to Win64
|
|
//
|
|
//#include <xom.h>
|
|
|
|
//
|
|
// Global strings for classes and attributes in the DS.
|
|
// These are NOT subject to localization.
|
|
//
|
|
|
|
LPWSTR g_DisplayName = L"ldapDisplayName";
|
|
LPWSTR g_ClassFilter = L"classSchema";
|
|
LPWSTR g_AttributeFilter = L"attributeSchema";
|
|
LPWSTR g_Description = L"adminDescription";
|
|
LPWSTR g_MayContain = L"mayContain";
|
|
LPWSTR g_MustContain = L"mustContain";
|
|
LPWSTR g_SystemMayContain = L"systemMayContain";
|
|
LPWSTR g_SystemMustContain = L"systemMustContain";
|
|
LPWSTR g_AuxiliaryClass = L"auxiliaryClass";
|
|
LPWSTR g_SystemAuxiliaryClass = L"systemAuxiliaryClass";
|
|
LPWSTR g_SubclassOf = L"subclassOf";
|
|
LPWSTR g_ObjectClassCategory = L"objectClassCategory";
|
|
LPWSTR g_ObjectClass = L"objectClass";
|
|
LPWSTR g_omObjectClass = L"oMObjectClass";
|
|
LPWSTR g_CN = L"CN";
|
|
LPWSTR g_omSyntax = L"oMSyntax";
|
|
LPWSTR g_AttributeSyntax = L"attributeSyntax";
|
|
LPWSTR g_SystemOnly = L"systemOnly";
|
|
LPWSTR g_Superiors = L"possSuperiors";
|
|
LPWSTR g_SystemSuperiors = L"systemPossSuperiors";
|
|
LPWSTR g_GlobalClassID = L"governsID";
|
|
LPWSTR g_GlobalAttributeID = L"attributeID";
|
|
LPWSTR g_RangeUpper = L"rangeUpper";
|
|
LPWSTR g_RangeLower = L"rangeLower";
|
|
LPWSTR g_IsSingleValued = L"isSingleValued";
|
|
LPWSTR g_IndexFlag = L"searchFlags";
|
|
LPWSTR g_ShowInAdvViewOnly = L"showInAdvancedViewOnly";
|
|
LPWSTR g_UpdateSchema = LDAP_OPATT_SCHEMA_UPDATE_NOW_W;
|
|
LPWSTR g_BecomeFsmo = LDAP_OPATT_BECOME_SCHEMA_MASTER_W;
|
|
LPWSTR g_isDefunct = L"isDefunct";
|
|
LPWSTR g_GCReplicated = L"isMemberOfPartialAttributeSet";
|
|
LPWSTR g_DefaultAcl = L"defaultSecurityDescriptor";
|
|
LPWSTR g_DefaultCategory = L"defaultObjectCategory";
|
|
LPWSTR g_systemFlags = L"systemFlags";
|
|
LPWSTR g_fsmoRoleOwner = L"fsmoRoleOwner";
|
|
|
|
LPWSTR g_allowedChildClassesEffective = L"allowedChildClassesEffective";
|
|
LPWSTR g_allowedAttributesEffective = L"allowedAttributesEffective";
|
|
|
|
|
|
LPWSTR g_ClassSearchRequest = L"objectClass=classSchema";
|
|
LPWSTR g_AttribSearchRequest = L"objectClass=attributeSchema";
|
|
|
|
//
|
|
// Syntax values. Not subject to localization.
|
|
//
|
|
|
|
class CSyntaxDescriptor g_Syntax[] =
|
|
{
|
|
/*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" ),
|
|
/*SYNTAX_OBJECT_ID_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_OID, FALSE, FALSE, _T("2.5.5.2") , /*OM_S_OBJECT_IDENTIFIER_STRING */ 6 , 0, NULL ),
|
|
/*SYNTAX_NOCASE_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_NOCASE_STR, FALSE, TRUE, _T("2.5.5.4") , /*OM_S_TELETEX_STRING */ 20 , 0, NULL ),
|
|
/*SYNTAX_PRINT_CASE_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_PRCS_STR, FALSE, TRUE, _T("2.5.5.5") , /*OM_S_PRINTABLE_STRING */ 19 , 0, NULL ),
|
|
/*SYNTAX_PRINT_CASE_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_I5_STR, FALSE, TRUE, _T("2.5.5.5") , /*OM_S_IA5_STRING */ 22 , 0, NULL ),
|
|
/*SYNTAX_NUMERIC_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_NUMSTR, FALSE, FALSE, _T("2.5.5.6") , /*OM_S_NUMERIC_STRING */ 18 , 0, NULL ),
|
|
/*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" ),
|
|
/*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" ),
|
|
/*SYNTAX_BOOLEAN_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_BOOLEAN, FALSE, FALSE, _T("2.5.5.8") , /*OM_S_BOOLEAN */ 1 , 0, NULL ),
|
|
/*SYNTAX_INTEGER_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_INTEGER, TRUE, FALSE, _T("2.5.5.9") , /*OM_S_INTEGER */ 2 , 0, NULL ),
|
|
/*SYNTAX_INTEGER_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_ENUMERATION, TRUE, FALSE, _T("2.5.5.9") , /*OM_S_ENUMERATION */ 10 , 0, NULL ),
|
|
/*SYNTAX_OCTET_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_OCTET, FALSE, FALSE, _T("2.5.5.10"), /*OM_S_OCTET_STRING */ 4 , 0, NULL ),
|
|
/*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" ),
|
|
/*SYNTAX_TIME_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_UTC, FALSE, FALSE, _T("2.5.5.11"), /*OM_S_UTC_TIME_STRING */ 23 , 0, NULL ),
|
|
/*SYNTAX_TIME_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_GEN_TIME, FALSE, FALSE, _T("2.5.5.11"), /*OM_S_GENERALISED_TIME_STRING */ 24 , 0, NULL ),
|
|
/*SYNTAX_UNICODE_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_UNICODE, FALSE, TRUE, _T("2.5.5.12"), /*OM_S_UNICODE_STRING */ 64 , 0, NULL ),
|
|
/*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" ),
|
|
/*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" ),
|
|
/*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" ),
|
|
/*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 ),
|
|
/*SYNTAX_I8_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_LINT, FALSE, FALSE, _T("2.5.5.16"), /*OM_S_I8 */ 65 , 0, NULL ),
|
|
/*SYNTAX_SID_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_SID, FALSE, FALSE, _T("2.5.5.17"), /*OM_S_OCTET_STRING */ 4 , 0, NULL ),
|
|
/* *** unknown -- must be last *** */ CSyntaxDescriptor( IDS_SYNTAX_UNKNOWN, FALSE, TRUE, NULL, 0 , 0, NULL ),
|
|
};
|
|
|
|
const UINT SCHEMA_SYNTAX_UNKNOWN = sizeof( g_Syntax ) / sizeof( g_Syntax[0] ) - 1;
|
|
|
|
|
|
// Number formating printf strings
|
|
const LPWSTR g_UINT32_FORMAT = L"%u";
|
|
|
|
#ifdef ENABLE_NEGATIVE_INT
|
|
const LPWSTR g_INT32_FORMAT = L"%d";
|
|
#else
|
|
// if there is no negative numbers support, format as unsigned
|
|
const LPWSTR g_INT32_FORMAT = g_UINT32_FORMAT;
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// *******************************************************************
|
|
// These are loaded from the resources as they need to be localizable.
|
|
// *******************************************************************
|
|
//
|
|
|
|
//
|
|
// Global strings for our static nodes.
|
|
//
|
|
|
|
CString g_strSchmMgmt;
|
|
CString g_strClasses;
|
|
CString g_strAttributes;
|
|
|
|
//
|
|
// Strings for various object types.
|
|
//
|
|
|
|
CString g_88Class;
|
|
CString g_StructuralClass;
|
|
CString g_AuxClass;
|
|
CString g_AbstractClass;
|
|
CString g_MandatoryAttribute;
|
|
CString g_OptionalAttribute;
|
|
CString g_Yes;
|
|
CString g_No;
|
|
CString g_Unknown;
|
|
CString g_Defunct;
|
|
CString g_Active;
|
|
|
|
//
|
|
// Message strings.
|
|
//
|
|
|
|
CString g_NoDescription;
|
|
CString g_NoName;
|
|
CString g_MsgBoxErr;
|
|
CString g_MsgBoxWarn;
|
|
CString g_SysClassString;
|
|
CString g_SysAttrString;
|
|
|
|
//
|
|
// Menu strings.
|
|
//
|
|
|
|
CString g_MenuStrings[MENU_LAST_COMMAND];
|
|
CString g_StatusStrings[MENU_LAST_COMMAND];
|
|
|
|
BOOL g_fScopeStringsLoaded = FALSE;
|
|
|
|
//
|
|
// Utility functions.
|
|
//
|
|
|
|
void
|
|
LoadGlobalCookieStrings(
|
|
)
|
|
/***
|
|
|
|
Load the global strings out of our resource table.
|
|
|
|
***/
|
|
{
|
|
if ( !g_fScopeStringsLoaded )
|
|
{
|
|
//
|
|
// Static node strings.
|
|
//
|
|
|
|
VERIFY( g_strSchmMgmt.LoadString(IDS_SCOPE_SCHMMGMT) );
|
|
VERIFY( g_strClasses.LoadString(IDS_SCOPE_CLASSES) );
|
|
VERIFY( g_strAttributes.LoadString(IDS_SCOPE_ATTRIBUTES) );
|
|
|
|
//
|
|
// Object name strings.
|
|
//
|
|
|
|
VERIFY( g_88Class.LoadString(IDS_CLASS_88) );
|
|
VERIFY( g_StructuralClass.LoadString(IDS_CLASS_STRUCTURAL) );
|
|
VERIFY( g_AuxClass.LoadString(IDS_CLASS_AUXILIARY) );
|
|
VERIFY( g_AbstractClass.LoadString(IDS_CLASS_ABSTRACT) );
|
|
VERIFY( g_MandatoryAttribute.LoadString(IDS_ATTRIBUTE_MANDATORY) );
|
|
VERIFY( g_OptionalAttribute.LoadString(IDS_ATTRIBUTE_OPTIONAL) );
|
|
VERIFY( g_Yes.LoadString(IDS_YES) );
|
|
VERIFY( g_No.LoadString(IDS_NO) );
|
|
VERIFY( g_Unknown.LoadString(IDS_UNKNOWN) );
|
|
VERIFY( g_Defunct.LoadString(IDS_DEFUNCT) );
|
|
VERIFY( g_Active.LoadString(IDS_ACTIVE) );
|
|
|
|
//
|
|
// Message strings.
|
|
//
|
|
|
|
VERIFY( g_NoDescription.LoadString(IDS_ERR_NO_DESCRIPTION) );
|
|
VERIFY( g_NoName.LoadString(IDS_ERR_NO_NAME) );
|
|
VERIFY( g_MsgBoxErr.LoadString(IDS_ERR_ERROR) );
|
|
VERIFY( g_MsgBoxWarn.LoadString(IDS_ERR_WARNING) );
|
|
VERIFY( g_SysClassString.LoadString(IDS_CLASS_SYSTEM) );
|
|
VERIFY( g_SysAttrString.LoadString(IDS_ATTR_SYSTEM) );
|
|
|
|
//
|
|
// Syntax strings.
|
|
//
|
|
|
|
for( UINT i = 0; i <= SCHEMA_SYNTAX_UNKNOWN; i++ )
|
|
{
|
|
ASSERT( g_Syntax[i].m_nResourceID );
|
|
VERIFY( g_Syntax[i].m_strSyntaxName.LoadString( g_Syntax[i].m_nResourceID ) );
|
|
}
|
|
|
|
|
|
//
|
|
// Menu Strings
|
|
//
|
|
|
|
VERIFY( g_MenuStrings[CLASSES_CREATE_CLASS].LoadString(IDS_MENU_CLASS) );
|
|
VERIFY( g_MenuStrings[NEW_CLASS].LoadString(IDS_MENU_NEW_CLASS) );
|
|
VERIFY( g_MenuStrings[ATTRIBUTES_CREATE_ATTRIBUTE].LoadString(
|
|
IDS_MENU_ATTRIBUTE) );
|
|
VERIFY(g_MenuStrings[NEW_ATTRIBUTE].LoadString(IDS_MENU_NEW_ATTRIBUTE));
|
|
|
|
VERIFY( g_MenuStrings[SCHEMA_RETARGET].LoadString(IDS_MENU_RETARGET) );
|
|
VERIFY( g_MenuStrings[SCHEMA_EDIT_FSMO].LoadString(IDS_MENU_EDIT_FSMO) );
|
|
VERIFY( g_MenuStrings[SCHEMA_REFRESH].LoadString(IDS_MENU_REFRESH) );
|
|
VERIFY( g_MenuStrings[SCHEMA_SECURITY].LoadString(IDS_MENU_SECURITY) );
|
|
|
|
|
|
VERIFY( g_StatusStrings[CLASSES_CREATE_CLASS].LoadString(
|
|
IDS_STATUS_CREATE_CLASS) );
|
|
VERIFY( g_StatusStrings[ATTRIBUTES_CREATE_ATTRIBUTE].LoadString(
|
|
IDS_STATUS_CREATE_ATTRIBUTE) );
|
|
VERIFY( g_StatusStrings[NEW_CLASS].LoadString(
|
|
IDS_STATUS_CREATE_CLASS) );
|
|
VERIFY( g_StatusStrings[NEW_ATTRIBUTE].LoadString(
|
|
IDS_STATUS_CREATE_ATTRIBUTE) );
|
|
VERIFY( g_StatusStrings[SCHEMA_RETARGET].LoadString(IDS_STATUS_RETARGET) );
|
|
VERIFY( g_StatusStrings[SCHEMA_EDIT_FSMO].LoadString(IDS_STATUS_EDIT_FSMO) );
|
|
VERIFY( g_StatusStrings[SCHEMA_REFRESH].LoadString(IDS_STATUS_REFRESH) );
|
|
VERIFY( g_StatusStrings[SCHEMA_SECURITY].LoadString(IDS_STATUS_SECURITY) );
|
|
|
|
g_fScopeStringsLoaded = TRUE;
|
|
}
|
|
}
|
|
|
|
INT
|
|
DoErrMsgBox(
|
|
HWND hwndParent, // IN: Parent of the dialog box
|
|
BOOL fError, // IN: Is this a warning or an error?
|
|
UINT wIdString // IN: String resource Id of the error.
|
|
)
|
|
/***
|
|
|
|
Display a message box with the error.
|
|
|
|
***/
|
|
{
|
|
CString Error;
|
|
|
|
VERIFY( Error.LoadString( wIdString ) );
|
|
|
|
return DoErrMsgBox( hwndParent, fError, Error );
|
|
}
|
|
|
|
|
|
INT
|
|
DoErrMsgBox(
|
|
HWND hwndParent, // IN: Parent of the dialog box
|
|
BOOL fError, // IN: Is this a warning or an error?
|
|
PCWSTR pszError // IN: String to display.
|
|
)
|
|
/***
|
|
|
|
Display a message box with the error.
|
|
|
|
***/
|
|
{
|
|
return MessageBox(
|
|
hwndParent,
|
|
pszError,
|
|
(fError ? g_MsgBoxErr : g_MsgBoxWarn),
|
|
(fError ? MB_ICONSTOP : MB_ICONEXCLAMATION) | MB_OK
|
|
);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ComponentData::ForceDsSchemaCacheUpdate(
|
|
VOID
|
|
)
|
|
/***
|
|
|
|
Force the schema container to reload its interal cache.
|
|
If this succeeds, it returns TRUE. Otherwise, it returns
|
|
FALSE.
|
|
|
|
***/
|
|
{
|
|
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
|
|
CWaitCursor wait;
|
|
CString RootDsePath;
|
|
IADs *pSchemaRootDse = NULL;
|
|
|
|
SAFEARRAYBOUND RootDseBoundary[1];
|
|
SAFEARRAY* pSafeArray = NULL;
|
|
VARIANT AdsArray, AdsValue;
|
|
long ArrayLen = 1;
|
|
long ArrayPos = 0;
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
do
|
|
{
|
|
//
|
|
// Open the root DSE on the current focus server.
|
|
//
|
|
|
|
GetBasePathsInfo()->GetRootDSEPath(RootDsePath);
|
|
|
|
hr = ADsGetObject(
|
|
( const_cast<BSTR>((LPCTSTR) RootDsePath ) ),
|
|
IID_IADs,
|
|
(void **)&pSchemaRootDse );
|
|
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
//
|
|
// Create the safe array for the PutEx call.
|
|
//
|
|
|
|
RootDseBoundary[0].lLbound = 0;
|
|
RootDseBoundary[0].cElements = ArrayLen;
|
|
|
|
pSafeArray = SafeArrayCreate( VT_VARIANT, ArrayLen, RootDseBoundary );
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
VariantInit( &AdsArray );
|
|
V_VT( &AdsArray ) = VT_ARRAY | VT_VARIANT;
|
|
V_ARRAY( &AdsArray ) = pSafeArray;
|
|
|
|
VariantInit( &AdsValue );
|
|
|
|
V_VT(&AdsValue) = VT_I4;
|
|
V_I4(&AdsValue) = 1;
|
|
|
|
hr = SafeArrayPutElement( pSafeArray, &ArrayPos, &AdsValue );
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
//
|
|
// Write the update parameter. This is synchronous
|
|
// and when it returns, the cache is up to date.
|
|
//
|
|
|
|
hr = pSchemaRootDse->PutEx( ADS_PROPERTY_APPEND,
|
|
const_cast<BSTR>((LPCTSTR)g_UpdateSchema),
|
|
AdsArray );
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
hr = pSchemaRootDse->SetInfo();
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
} while( FALSE );
|
|
|
|
|
|
SafeArrayDestroy( pSafeArray );
|
|
|
|
if( pSchemaRootDse )
|
|
pSchemaRootDse->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
BOOLEAN
|
|
ComponentData::AsynchForceDsSchemaCacheUpdate(
|
|
VOID
|
|
) {
|
|
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
|
|
CWaitCursor wait;
|
|
CString szSchemaContainerPath;
|
|
IADs *pSchemaContainer;
|
|
VARIANT AdsValue;
|
|
HRESULT hr;
|
|
SYSTEMTIME CurrentTime;
|
|
double variant_time;
|
|
|
|
//
|
|
// Get the schema container path.
|
|
//
|
|
|
|
GetBasePathsInfo()->GetSchemaPath(szSchemaContainerPath);
|
|
|
|
if (szSchemaContainerPath.IsEmpty() ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Open the schema container.
|
|
//
|
|
|
|
hr = ADsGetObject(
|
|
(LPWSTR)(LPCWSTR)szSchemaContainerPath,
|
|
IID_IADs,
|
|
(void **)&pSchemaContainer );
|
|
|
|
if ( FAILED(hr) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Write the update parameter.
|
|
//
|
|
|
|
GetSystemTime( &CurrentTime );
|
|
BOOL result = SystemTimeToVariantTime( &CurrentTime, &variant_time );
|
|
|
|
ASSERT( result );
|
|
|
|
VariantInit( &AdsValue );
|
|
|
|
V_VT(&AdsValue) = VT_DATE;
|
|
V_DATE(&AdsValue) = variant_time;
|
|
|
|
hr = pSchemaContainer->Put( const_cast<BSTR>((LPCTSTR)g_UpdateSchema),
|
|
AdsValue );
|
|
ASSERT( SUCCEEDED( hr ) );
|
|
|
|
hr = pSchemaContainer->SetInfo();
|
|
|
|
pSchemaContainer->Release();
|
|
|
|
if ( FAILED( hr ) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
InsertEditItems(
|
|
HWND hwnd,
|
|
VARIANT *AdsResult
|
|
) {
|
|
|
|
HRESULT hr;
|
|
SAFEARRAY *saAttributes;
|
|
long start, end, current;
|
|
VARIANT SingleResult;
|
|
|
|
//
|
|
// Check the VARIANT to make sure we have
|
|
// an array of variants.
|
|
//
|
|
|
|
ASSERT( V_VT(AdsResult) == ( VT_ARRAY | VT_VARIANT ) );
|
|
saAttributes = V_ARRAY( AdsResult );
|
|
|
|
//
|
|
// Figure out the dimensions of the array.
|
|
//
|
|
|
|
hr = SafeArrayGetLBound( saAttributes, 1, &start );
|
|
|
|
if ( FAILED(hr) ) {
|
|
return S_FALSE;
|
|
}
|
|
|
|
hr = SafeArrayGetUBound( saAttributes, 1, &end );
|
|
|
|
if ( FAILED(hr) ) {
|
|
return S_FALSE;
|
|
}
|
|
|
|
VariantInit( &SingleResult );
|
|
|
|
//
|
|
// Process the array elements.
|
|
//
|
|
|
|
for ( current = start ;
|
|
current <= end ;
|
|
current++ ) {
|
|
|
|
hr = SafeArrayGetElement( saAttributes, ¤t, &SingleResult );
|
|
|
|
if ( SUCCEEDED( hr ) ) {
|
|
|
|
ASSERT( V_VT(&SingleResult) == VT_BSTR );
|
|
|
|
::SendMessage( hwnd, LB_ADDSTRING, 0,
|
|
reinterpret_cast<LPARAM>(V_BSTR(&SingleResult)) );
|
|
|
|
::SendMessage( hwnd, LB_SETITEMDATA, 0, NULL );
|
|
|
|
VariantClear( &SingleResult );
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
HRESULT
|
|
InsertEditItems(
|
|
CListBox& refListBox,
|
|
CStringList& refstringlist
|
|
)
|
|
{
|
|
POSITION pos = refstringlist.GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
int iItem = refListBox.AddString( refstringlist.GetNext(pos) );
|
|
if (0 > iItem)
|
|
{
|
|
ASSERT(FALSE);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
VERIFY( LB_ERR != refListBox.SetItemDataPtr( iItem, NULL ) );
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
inline BOOL
|
|
IsEqual( ADS_OCTET_STRING * ostr1, ADS_OCTET_STRING * ostr2 )
|
|
{
|
|
ASSERT(ostr1);
|
|
ASSERT(ostr2);
|
|
|
|
if( ostr1->dwLength == ostr2->dwLength )
|
|
{
|
|
if( 0 == ostr1->dwLength )
|
|
return TRUE;
|
|
else
|
|
return !memcmp( ostr1->lpValue, ostr2->lpValue, ostr1->dwLength );
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
UINT
|
|
GetSyntaxOrdinal( PCTSTR attributeSyntax, UINT omSyntax, ADS_OCTET_STRING * pOmObjectClass )
|
|
{
|
|
ASSERT( attributeSyntax );
|
|
ASSERT( omSyntax );
|
|
ASSERT( pOmObjectClass );
|
|
|
|
//
|
|
// Return the syntax ordinal, or the unknown syntax ordinal.
|
|
//
|
|
|
|
UINT Ordinal = 0;
|
|
|
|
while ( Ordinal < SCHEMA_SYNTAX_UNKNOWN) {
|
|
|
|
if ( !_tcscmp(g_Syntax[Ordinal].m_pszAttributeSyntax, attributeSyntax))
|
|
{
|
|
if( omSyntax && g_Syntax[Ordinal].m_nOmSyntax == omSyntax &&
|
|
IsEqual( &g_Syntax[Ordinal].m_octstrOmObjectClass, pOmObjectClass ) )
|
|
break;
|
|
}
|
|
|
|
Ordinal++;
|
|
}
|
|
|
|
return Ordinal;
|
|
}
|
|
|
|
|
|
// Coded to fail on anything suspicious
|
|
HRESULT
|
|
VariantToStringList(
|
|
VARIANT& refvar,
|
|
CStringList& refstringlist
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
long start, end, current;
|
|
|
|
//
|
|
// Check the VARIANT to make sure we have
|
|
// an array of variants.
|
|
//
|
|
|
|
if ( V_VT(&refvar) != ( VT_ARRAY | VT_VARIANT ) )
|
|
{
|
|
ASSERT(FALSE);
|
|
return E_UNEXPECTED;
|
|
}
|
|
SAFEARRAY *saAttributes = V_ARRAY( &refvar );
|
|
|
|
//
|
|
// Figure out the dimensions of the array.
|
|
//
|
|
|
|
hr = SafeArrayGetLBound( saAttributes, 1, &start );
|
|
if( FAILED(hr) )
|
|
return hr;
|
|
|
|
hr = SafeArrayGetUBound( saAttributes, 1, &end );
|
|
if( FAILED(hr) )
|
|
return hr;
|
|
|
|
VARIANT SingleResult;
|
|
VariantInit( &SingleResult );
|
|
|
|
//
|
|
// Process the array elements.
|
|
//
|
|
|
|
for ( current = start ;
|
|
current <= end ;
|
|
current++ ) {
|
|
|
|
hr = SafeArrayGetElement( saAttributes, ¤t, &SingleResult );
|
|
if( FAILED(hr) )
|
|
return hr;
|
|
if ( V_VT(&SingleResult) != VT_BSTR )
|
|
return E_UNEXPECTED;
|
|
|
|
refstringlist.AddHead( V_BSTR(&SingleResult) );
|
|
VariantClear( &SingleResult );
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
StringListToVariant(
|
|
VARIANT& refvar,
|
|
CStringList& refstringlist
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int cCount = (int) refstringlist.GetCount();
|
|
|
|
SAFEARRAYBOUND rgsabound[1];
|
|
rgsabound[0].lLbound = 0;
|
|
rgsabound[0].cElements = cCount;
|
|
|
|
SAFEARRAY* psa = SafeArrayCreate(VT_VARIANT, 1, rgsabound);
|
|
if (NULL == psa)
|
|
return E_OUTOFMEMORY;
|
|
|
|
VariantClear( &refvar );
|
|
V_VT(&refvar) = VT_VARIANT|VT_ARRAY;
|
|
V_ARRAY(&refvar) = psa;
|
|
|
|
VARIANT SingleResult;
|
|
VariantInit( &SingleResult );
|
|
V_VT(&SingleResult) = VT_BSTR;
|
|
POSITION pos = refstringlist.GetHeadPosition();
|
|
long i;
|
|
for (i = 0; i < cCount, pos != NULL; i++)
|
|
{
|
|
V_BSTR(&SingleResult) = T2BSTR((LPCTSTR)refstringlist.GetNext(pos));
|
|
hr = SafeArrayPutElement(psa, &i, &SingleResult);
|
|
if( FAILED(hr) )
|
|
return hr;
|
|
}
|
|
if (i != cCount || pos != NULL)
|
|
return E_UNEXPECTED;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
StringListToColumnList(
|
|
ComponentData* pScopeControl,
|
|
CStringList& refstringlist,
|
|
ListEntry **ppNewList
|
|
) {
|
|
|
|
//
|
|
// Make a column list from a string list. We use
|
|
// this to update the cached attributes lists.
|
|
//
|
|
|
|
int cCount = (int) refstringlist.GetCount();
|
|
ListEntry *pHead = NULL;
|
|
ListEntry *pCurrent = NULL, *pPrevious = NULL;
|
|
POSITION pos = refstringlist.GetHeadPosition();
|
|
CString Name;
|
|
SchemaObject *pSchemaObject, *pSchemaHead;
|
|
BOOLEAN fNameFound;
|
|
|
|
for ( long i = 0; i < cCount, pos != NULL; i++ ) {
|
|
|
|
pCurrent = new ListEntry;
|
|
if ( !pCurrent ) {
|
|
pScopeControl->g_SchemaCache.FreeColumnList( pHead );
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if ( !pHead ) {
|
|
|
|
pHead = pPrevious = pCurrent;
|
|
|
|
} else {
|
|
|
|
pPrevious->pNext = pCurrent;
|
|
pPrevious = pCurrent;
|
|
}
|
|
|
|
//
|
|
// We need to list all of these by their ldapDisplayNames,
|
|
// so we have to reverse lookup the oid entries.
|
|
//
|
|
|
|
Name = ((LPCTSTR)refstringlist.GetNext(pos));
|
|
pSchemaObject = pScopeControl->g_SchemaCache.LookupSchemaObject(
|
|
Name,
|
|
SCHMMGMT_CLASS );
|
|
|
|
if ( !pSchemaObject ) {
|
|
|
|
pSchemaObject = pScopeControl->g_SchemaCache.LookupSchemaObject(
|
|
Name,
|
|
SCHMMGMT_ATTRIBUTE );
|
|
|
|
if ( !pSchemaObject) {
|
|
|
|
//
|
|
// We have to look up this oid.
|
|
// First try the list of classes.
|
|
//
|
|
|
|
pSchemaHead = pScopeControl->g_SchemaCache.pSortedClasses;
|
|
pSchemaObject = pSchemaHead;
|
|
fNameFound = FALSE;
|
|
|
|
do {
|
|
|
|
if ( pSchemaObject->oid == Name ) {
|
|
|
|
Name = pSchemaObject->ldapDisplayName;
|
|
fNameFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
pSchemaObject = pSchemaObject->pSortedListFlink;
|
|
|
|
} while ( pSchemaObject != pSchemaHead );
|
|
|
|
//
|
|
// Then try the list of attributes.
|
|
//
|
|
|
|
if ( !fNameFound ) {
|
|
|
|
pSchemaHead = pScopeControl->g_SchemaCache.pSortedAttribs;
|
|
pSchemaObject = pSchemaHead;
|
|
|
|
do {
|
|
|
|
if ( pSchemaObject->oid == Name ) {
|
|
|
|
Name = pSchemaObject->ldapDisplayName;
|
|
fNameFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
pSchemaObject = pSchemaObject->pSortedListFlink;
|
|
|
|
} while ( pSchemaObject != pSchemaHead );
|
|
}
|
|
|
|
ASSERT( fNameFound );
|
|
|
|
} else {
|
|
|
|
pScopeControl->g_SchemaCache.ReleaseRef( pSchemaObject );
|
|
}
|
|
|
|
} else {
|
|
|
|
pScopeControl->g_SchemaCache.ReleaseRef( pSchemaObject );
|
|
}
|
|
|
|
//
|
|
// This is the ldapDisplayName!!
|
|
//
|
|
|
|
pCurrent->Attribute = Name;
|
|
}
|
|
|
|
ASSERT( cCount == i );
|
|
ASSERT( pos == NULL );
|
|
|
|
*ppNewList = pHead;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
const UINT MAX_ERROR_BUF = 2048;
|
|
|
|
|
|
VOID
|
|
DoExtErrMsgBox(
|
|
VOID
|
|
)
|
|
{
|
|
DWORD dwLastError;
|
|
WCHAR szErrorBuf[MAX_ERROR_BUF + 1];
|
|
WCHAR szNameBuf[MAX_ERROR_BUF + 1];
|
|
|
|
// get extended error value
|
|
HRESULT hr_return = ADsGetLastError( &dwLastError,
|
|
szErrorBuf,
|
|
MAX_ERROR_BUF,
|
|
szNameBuf,
|
|
MAX_ERROR_BUF);
|
|
if (SUCCEEDED(hr_return))
|
|
{
|
|
MessageBox( ::GetActiveWindow(),
|
|
szErrorBuf,
|
|
szNameBuf,
|
|
MB_OK | MB_ICONSTOP );
|
|
}
|
|
else
|
|
ASSERT( FALSE );
|
|
}
|
|
|
|
|
|
// INVALID_POINTER is returned by CListBox::GetItemDataPtr() in case of an error.
|
|
const VOID * INVALID_POINTER = reinterpret_cast<void *>( LB_ERR );
|
|
|
|
|
|
HRESULT
|
|
RetrieveEditItemsWithExclusions(
|
|
CListBox& refListBox,
|
|
CStringList& refstringlist,
|
|
CStringList* pstringlistExclusions)
|
|
{
|
|
CString str;
|
|
CString * pstr = NULL;
|
|
int nCount = refListBox.GetCount();
|
|
|
|
if (LB_ERR == nCount)
|
|
{
|
|
ASSERT(FALSE);
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
for (INT i = 0; i < nCount; i++)
|
|
{
|
|
pstr = static_cast<CString *>( refListBox.GetItemDataPtr(i) );
|
|
ASSERT( INVALID_POINTER != pstr );
|
|
|
|
// don't need to search for pstr because pstr can only be a new item,
|
|
// and they are never excluded.
|
|
|
|
if( pstr && INVALID_POINTER != pstr )
|
|
{
|
|
refstringlist.AddHead( *pstr );
|
|
}
|
|
else
|
|
{
|
|
refListBox.GetText( i, str );
|
|
if (NULL != pstringlistExclusions)
|
|
{
|
|
POSITION pos = pstringlistExclusions->Find( str );
|
|
if (NULL != pos)
|
|
continue;
|
|
}
|
|
|
|
refstringlist.AddHead( str );
|
|
}
|
|
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//
|
|
// The global cookie lists for scope and result pane items.
|
|
//
|
|
|
|
VOID
|
|
CCookieList::AddCookie(
|
|
Cookie *pCookie,
|
|
HSCOPEITEM hScope
|
|
) {
|
|
|
|
CCookieListEntry *pNewEntry = new CCookieListEntry;
|
|
|
|
//
|
|
// If there's no memory, we can't remember this and hence
|
|
// our display may get a little out of whack.
|
|
//
|
|
|
|
if ( !pNewEntry ) {
|
|
return;
|
|
}
|
|
|
|
pNewEntry->pCookie = pCookie;
|
|
pNewEntry->hScopeItem = hScope;
|
|
|
|
if ( !pHead ) {
|
|
|
|
//
|
|
// If this is the first one, just set the
|
|
// head pointer. The constructor for the
|
|
// list entry has already set the next and
|
|
// back pointers.
|
|
//
|
|
|
|
pHead = pNewEntry;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Insert this at the end of the circular
|
|
// doubly-linked list.
|
|
//
|
|
|
|
pNewEntry->pBack = pHead->pBack;
|
|
pNewEntry->pNext = pHead;
|
|
pHead->pBack->pNext = pNewEntry;
|
|
pHead->pBack = pNewEntry;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
CCookieList::InsertSortedDisplay(
|
|
ComponentData *pScopeControl,
|
|
SchemaObject *pNewObject
|
|
)
|
|
/***
|
|
|
|
Notes:
|
|
|
|
This function inserts the object into the
|
|
sorted display list.
|
|
|
|
If the object is a class and the ComponentData
|
|
interface pointer is provided, this routine will
|
|
also create a cookie for this object and insert
|
|
the scope item into the view.
|
|
|
|
***/
|
|
{
|
|
|
|
HRESULT hr;
|
|
CCookieListEntry *pNewEntry = NULL, *pCurrent = NULL;
|
|
SCOPEDATAITEM ScopeItem;
|
|
Cookie *pNewCookie= NULL;
|
|
int compare;
|
|
|
|
//
|
|
// If this cookie list is empty, there's nothing
|
|
// in the display and we don't need to do anything.
|
|
//
|
|
|
|
if ( !pHead ) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Allocate a new cookie list entry. If we can't
|
|
// do nothing. The display will be out of sync
|
|
// until the user refreshes.
|
|
//
|
|
|
|
pNewEntry = new CCookieListEntry;
|
|
|
|
if ( !pNewEntry ) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Prepare the required mmc structures.
|
|
//
|
|
|
|
if ( pNewObject->schemaObjectType == SCHMMGMT_CLASS ) {
|
|
|
|
if ( !pScopeControl ) {
|
|
|
|
//
|
|
// If there's no scope control, we can't insert anything.
|
|
//
|
|
|
|
delete pNewEntry;
|
|
return;
|
|
}
|
|
|
|
// prefix believes that this allocation (or construction) may throw
|
|
// an exception, and if an exception is thrown, pNewEntry is
|
|
// leaked. After a lot of digging, it's possible that a
|
|
// CMemoryException instance may be thrown by one of the base
|
|
// classes of one the members of CBaseCookieBlock, which is a base
|
|
// clase of Cookie.
|
|
// NTRAID#NTBUG9-294879-2001/01/26-sburns
|
|
|
|
try
|
|
{
|
|
pNewCookie = new Cookie( SCHMMGMT_CLASS,
|
|
pParentCookie->QueryNonNULLMachineName() );
|
|
}
|
|
catch (...)
|
|
{
|
|
delete pNewEntry;
|
|
return;
|
|
}
|
|
|
|
if ( !pNewCookie ) {
|
|
|
|
//
|
|
// If we can't allocate a cookie, do nothing.
|
|
//
|
|
|
|
delete pNewEntry;
|
|
return;
|
|
}
|
|
|
|
pNewCookie->pParentCookie = pParentCookie;
|
|
pNewCookie->strSchemaObject = pNewObject->commonName;
|
|
|
|
pParentCookie->m_listScopeCookieBlocks.AddHead(
|
|
(CBaseCookieBlock*)pNewCookie );
|
|
|
|
pNewEntry->pCookie = pNewCookie;
|
|
|
|
::ZeroMemory( &ScopeItem, sizeof(ScopeItem) );
|
|
ScopeItem.displayname = MMC_CALLBACK;
|
|
ScopeItem.nState = 0;
|
|
|
|
ScopeItem.lParam = reinterpret_cast<LPARAM>((CCookie*)pNewCookie);
|
|
ScopeItem.nImage = pScopeControl->QueryImage( *pNewCookie, FALSE );
|
|
ScopeItem.nOpenImage = pScopeControl->QueryImage( *pNewCookie, TRUE );
|
|
|
|
}
|
|
|
|
//
|
|
// Should this be the new head of the list?
|
|
//
|
|
|
|
compare = pNewObject->ldapDisplayName.CompareNoCase(
|
|
pHead->pCookie->strSchemaObject );
|
|
|
|
if ( compare < 0 ) {
|
|
|
|
if ( pNewObject->schemaObjectType == SCHMMGMT_CLASS ) {
|
|
|
|
//
|
|
// Insert this into the scope pane.
|
|
//
|
|
|
|
ScopeItem.mask = SDI_STR | SDI_IMAGE | SDI_OPENIMAGE | SDI_STATE |
|
|
SDI_PARAM | SDI_NEXT | SDI_CHILDREN;
|
|
ScopeItem.cChildren = 0;
|
|
|
|
ScopeItem.relativeID = pHead->hScopeItem;
|
|
|
|
hr = pScopeControl->m_pConsoleNameSpace->InsertItem( &ScopeItem );
|
|
|
|
pNewEntry->hScopeItem = ScopeItem.ID;
|
|
pNewEntry->pCookie->m_hScopeItem = ScopeItem.ID;
|
|
|
|
} else {
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
|
|
pNewEntry->pNext = pHead;
|
|
pNewEntry->pBack = pHead->pBack;
|
|
pHead->pBack->pNext = pNewEntry;
|
|
pHead->pBack = pNewEntry;
|
|
|
|
pHead = pNewEntry;
|
|
|
|
} else {
|
|
|
|
delete pNewEntry;
|
|
delete pNewCookie;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Determine the sorted insertion point. The sorted list is circular.
|
|
//
|
|
|
|
pCurrent = pHead;
|
|
|
|
while ( pCurrent->pNext != pHead ) {
|
|
|
|
compare = pNewObject->ldapDisplayName.CompareNoCase(
|
|
pCurrent->pNext->pCookie->strSchemaObject );
|
|
|
|
if ( compare < 0 ) {
|
|
break;
|
|
}
|
|
|
|
pCurrent = pCurrent->pNext;
|
|
}
|
|
|
|
//
|
|
// We want to insert the new object after pCurrent.
|
|
//
|
|
|
|
if ( pNewObject->schemaObjectType == SCHMMGMT_CLASS ) {
|
|
|
|
ScopeItem.mask = SDI_STR | SDI_IMAGE | SDI_OPENIMAGE | SDI_STATE |
|
|
SDI_PARAM | SDI_PREVIOUS | SDI_CHILDREN;
|
|
ScopeItem.cChildren = 0;
|
|
|
|
ScopeItem.relativeID = pCurrent->hScopeItem;
|
|
|
|
hr = pScopeControl->m_pConsoleNameSpace->InsertItem( &ScopeItem );
|
|
|
|
pNewEntry->hScopeItem = ScopeItem.ID;
|
|
pNewEntry->pCookie->m_hScopeItem = ScopeItem.ID;
|
|
|
|
} else {
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
if ( SUCCEEDED( hr )) {
|
|
|
|
pNewEntry->pNext = pCurrent->pNext;
|
|
pNewEntry->pBack = pCurrent;
|
|
pCurrent->pNext->pBack = pNewEntry;
|
|
pCurrent->pNext = pNewEntry;
|
|
|
|
} else {
|
|
|
|
delete pNewEntry;
|
|
delete pNewCookie;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
bool
|
|
CCookieList::DeleteCookie(Cookie* pCookie)
|
|
{
|
|
bool result = false;
|
|
|
|
if (!pHead)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
// walk the links and stop when the scope item matches.
|
|
// Since the list is circular,
|
|
// we use pHead as the sentinal value instead of null.
|
|
|
|
CCookieListEntry* pCurrent = pHead;
|
|
do
|
|
{
|
|
ASSERT(pCurrent);
|
|
|
|
if (pCurrent->pCookie == pCookie)
|
|
{
|
|
// Remove the node from the list
|
|
|
|
pCurrent->pBack->pNext = pCurrent->pNext;
|
|
pCurrent->pNext->pBack = pCurrent->pBack;
|
|
|
|
if (pCurrent == pHead)
|
|
{
|
|
pHead = pCurrent->pNext;
|
|
}
|
|
|
|
result = true;
|
|
|
|
delete pCurrent;
|
|
break;
|
|
}
|
|
|
|
pCurrent = pCurrent->pNext;
|
|
|
|
} while (pCurrent != pHead);
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
CCookieList::DeleteAll()
|
|
{
|
|
if (!pHead)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
CCookieListEntry* pCurrent = pHead;
|
|
do
|
|
{
|
|
CCookieListEntry* next = pCurrent->pNext;
|
|
delete pCurrent;
|
|
pCurrent = next;
|
|
}
|
|
while (pCurrent != pHead);
|
|
|
|
pHead = 0;
|
|
}
|
|
|
|
|
|
|
|
CString
|
|
GetHelpFilename()
|
|
{
|
|
TCHAR buf[MAX_PATH + 1];
|
|
|
|
UINT result = ::GetSystemWindowsDirectory(buf, MAX_PATH);
|
|
ASSERT(result != 0 && result <= MAX_PATH);
|
|
|
|
CString f(buf);
|
|
f += TEXT("\\help\\schmmgmt.hlp");
|
|
|
|
return f;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ShowHelp( HWND hParent, WPARAM wParam, LPARAM lParam, const DWORD ids[], BOOL fContextMenuHelp )
|
|
{
|
|
HWND hWndMain = NULL;
|
|
UINT uCommand = 0;
|
|
|
|
if( !fContextMenuHelp )
|
|
{
|
|
// The user has clicked ? and the control, or just F1 (if enabled)
|
|
const LPHELPINFO pHelpInfo = (LPHELPINFO)lParam;
|
|
if (pHelpInfo && pHelpInfo->iContextType == HELPINFO_WINDOW)
|
|
{
|
|
hWndMain = (HWND) pHelpInfo->hItemHandle;
|
|
uCommand = HELP_WM_HELP;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hWndMain = (HWND) wParam;
|
|
uCommand = HELP_CONTEXTMENU;
|
|
|
|
// Optimization for non-static enabled windows.
|
|
// This way users don't have to do an extra menu click
|
|
|
|
// $$ don't know why this call returns NULL all the time
|
|
// HWND hWnd = ChildWindowFromPoint( hParent, CPoint(lParam) );
|
|
// if( hWnd )
|
|
// hWndMain = hWnd;
|
|
|
|
if( -1 != GET_X_LPARAM(lParam) &&
|
|
-1 != GET_Y_LPARAM(lParam) &&
|
|
hParent &&
|
|
hWndMain != hParent )
|
|
{
|
|
uCommand = HELP_WM_HELP;
|
|
}
|
|
}
|
|
|
|
|
|
if( hWndMain && uCommand )
|
|
{
|
|
// Display context help for a control
|
|
::WinHelp( hWndMain,
|
|
GetHelpFilename(),
|
|
uCommand,
|
|
(DWORD_PTR) ids );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#if 0
|
|
|
|
VOID
|
|
DebugTrace(
|
|
LPWSTR Format,
|
|
...
|
|
) {
|
|
|
|
WCHAR DbgString[1024];
|
|
va_list arglist;
|
|
int Length;
|
|
|
|
//
|
|
// Format the output into a buffer and then print it.
|
|
//
|
|
|
|
va_start(arglist, Format);
|
|
|
|
Length = wvsprintf( DbgString, Format, arglist );
|
|
|
|
va_end(arglist);
|
|
|
|
ASSERT( Length <= 1024 );
|
|
ASSERT( Length != 0 );
|
|
|
|
OutputDebugString( DbgString );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
|
|
VOID
|
|
DebugTrace(
|
|
LPWSTR,
|
|
...
|
|
) {
|
|
;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Attempt to locate a message in a given module. Return the message string
|
|
// if found, the empty string if not.
|
|
//
|
|
// flags - FormatMessage flags to use
|
|
//
|
|
// module - module handle of message dll to look in, or 0 to use the system
|
|
// message table.
|
|
//
|
|
// code - message code to look for
|
|
|
|
CString
|
|
getMessageHelper(DWORD flags, HMODULE module, HRESULT code)
|
|
{
|
|
ASSERT(code);
|
|
ASSERT(flags & FORMAT_MESSAGE_ALLOCATE_BUFFER);
|
|
|
|
CString message;
|
|
|
|
TCHAR* sys_message = 0;
|
|
DWORD result =
|
|
::FormatMessage(
|
|
flags,
|
|
module,
|
|
static_cast<DWORD>(code),
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
reinterpret_cast<LPTSTR>(&sys_message),
|
|
0,
|
|
0);
|
|
if (result)
|
|
{
|
|
ASSERT(sys_message);
|
|
if (sys_message)
|
|
{
|
|
message = sys_message;
|
|
::LocalFree(sys_message);
|
|
}
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
|
|
#define MAX_ERROR_BUF 2048
|
|
|
|
HRESULT
|
|
GetLastADsError( HRESULT hr, CString& refErrorMsg, CString& refName )
|
|
{
|
|
ASSERT(FAILED(hr));
|
|
|
|
refErrorMsg.Empty();
|
|
refName.Empty();
|
|
|
|
if (!FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if( FACILITY_WIN32 == HRESULT_FACILITY(hr) )
|
|
{
|
|
DWORD dwLastError = 0;
|
|
WCHAR szErrorBuf[ MAX_ERROR_BUF + 1 ];
|
|
WCHAR szNameBuf[ MAX_ERROR_BUF + 1 ];
|
|
|
|
//Get extended error value.
|
|
HRESULT hr_return = ADsGetLastError( &dwLastError,
|
|
szErrorBuf,
|
|
MAX_ERROR_BUF,
|
|
szNameBuf,
|
|
MAX_ERROR_BUF );
|
|
|
|
ASSERT( SUCCEEDED(hr_return) );
|
|
if( SUCCEEDED(hr_return) && dwLastError )
|
|
{
|
|
refErrorMsg = szErrorBuf;
|
|
refName = szNameBuf;
|
|
return HRESULT_FROM_WIN32( dwLastError );
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Attempts to locate message strings for various facility codes in the
|
|
// HRESULT. If fTryADSIExtError is true, check ADsGetLastError() first
|
|
|
|
|
|
CString
|
|
GetErrorMessage( HRESULT hr, BOOL fTryADSIExtError /* = FALSE */ )
|
|
{
|
|
ASSERT(FAILED(hr));
|
|
|
|
if (!FAILED(hr))
|
|
{
|
|
// no messages for success!
|
|
return CString();
|
|
}
|
|
|
|
|
|
CString strExtMsg;
|
|
|
|
if( fTryADSIExtError &&
|
|
FACILITY_WIN32 == HRESULT_FACILITY(hr) )
|
|
{
|
|
DWORD dwLastError = 0;
|
|
WCHAR szErrorBuf[ MAX_ERROR_BUF + 1 ];
|
|
WCHAR szNameBuf[ MAX_ERROR_BUF + 1 ];
|
|
|
|
//Get extended error value.
|
|
HRESULT hr_return = ADsGetLastError( &dwLastError,
|
|
szErrorBuf,
|
|
MAX_ERROR_BUF,
|
|
szNameBuf,
|
|
MAX_ERROR_BUF );
|
|
|
|
ASSERT( SUCCEEDED(hr_return) );
|
|
if( SUCCEEDED(hr_return) && dwLastError )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( dwLastError );
|
|
|
|
strExtMsg = szErrorBuf;
|
|
}
|
|
}
|
|
|
|
HRESULT code = HRESULT_CODE(hr);
|
|
|
|
CString message;
|
|
|
|
// default is the system error message table
|
|
HMODULE module = 0;
|
|
|
|
DWORD flags =
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER
|
|
| FORMAT_MESSAGE_IGNORE_INSERTS
|
|
| FORMAT_MESSAGE_FROM_SYSTEM;
|
|
|
|
int facility = HRESULT_FACILITY(hr);
|
|
switch (facility)
|
|
{
|
|
case FACILITY_WIN32: // 0x7
|
|
{
|
|
// included here:
|
|
// lanman error codes (in it's own dll)
|
|
// dns
|
|
// winsock
|
|
|
|
static HMODULE lm_err_res_dll = 0;
|
|
if (code >= NERR_BASE && code <= MAX_NERR)
|
|
{
|
|
// use the net error message resource dll
|
|
if (lm_err_res_dll == 0)
|
|
{
|
|
lm_err_res_dll =
|
|
::LoadLibraryEx(
|
|
L"netmsg.dll",
|
|
0,
|
|
LOAD_LIBRARY_AS_DATAFILE);
|
|
}
|
|
|
|
module = lm_err_res_dll;
|
|
flags |= FORMAT_MESSAGE_FROM_HMODULE;
|
|
}
|
|
break;
|
|
}
|
|
case 0x0:
|
|
{
|
|
if (code >= 0x5000 && code <= 0x50FF)
|
|
{
|
|
// It's an ADSI error. They put the facility code (5) in the
|
|
// wrong place!
|
|
static HMODULE adsi_err_res_dll = 0;
|
|
// use the net error message resource dll
|
|
if (adsi_err_res_dll == 0)
|
|
{
|
|
adsi_err_res_dll =
|
|
::LoadLibraryEx(
|
|
L"activeds.dll",
|
|
0,
|
|
LOAD_LIBRARY_AS_DATAFILE);
|
|
}
|
|
|
|
module = adsi_err_res_dll;
|
|
flags |= FORMAT_MESSAGE_FROM_HMODULE;
|
|
|
|
// the message dll expects the entire error code
|
|
code = hr;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// do nothing
|
|
break;
|
|
}
|
|
}
|
|
|
|
message = getMessageHelper(flags, module, code);
|
|
|
|
|
|
#ifdef SHOW_EXT_LDAP_MSG
|
|
|
|
if( !strExtMsg.IsEmpty() )
|
|
message += L"\n" + strExtMsg;
|
|
|
|
#endif //SHOW_EXT_LDAP_MSG
|
|
|
|
|
|
if (message.IsEmpty())
|
|
{
|
|
message.LoadString(IDS_UNKNOWN_ERROR_MESSAGE);
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Get range from edit controls, Verify the range, attempt to correct, make sure lower <= upper.
|
|
//
|
|
// an exception will be thrown in case of an error.
|
|
//
|
|
void DDXV_VerifyAttribRange( CDataExchange *pDX, BOOL fIsSigned,
|
|
UINT idcLower, CString & strLower,
|
|
UINT idcUpper, CString & strUpper )
|
|
{
|
|
INT64 llLower = 0;
|
|
INT64 llUpper = 0;
|
|
|
|
ASSERT( pDX );
|
|
ASSERT( pDX->m_pDlgWnd );
|
|
|
|
|
|
// Update the values.
|
|
llLower = DDXV_SigUnsigINT32Value( pDX, fIsSigned, idcLower, strLower );
|
|
llUpper = DDXV_SigUnsigINT32Value( pDX, fIsSigned, idcUpper, strUpper );
|
|
|
|
|
|
#ifdef ENABLE_NEGATIVE_INT
|
|
// verify that lower <= upper -- only if supporting ENABLE_NEGATIVE_INT
|
|
if ( pDX->m_bSaveAndValidate && !strLower.IsEmpty() && !strUpper.IsEmpty() )
|
|
{
|
|
if( llLower > llUpper )
|
|
{
|
|
DoErrMsgBox( pDX->m_pDlgWnd->m_hWnd, TRUE, IDS_ERR_EDIT_MINMAX );
|
|
pDX->Fail(); // we are still at the second edit control.
|
|
}
|
|
}
|
|
#endif //ENABLE_NEGATIVE_INT
|
|
}
|
|
|
|
|
|
//
|
|
// Get string from an edit control, verify it attempting to correct
|
|
//
|
|
// an exception will be thrown in case of an error.
|
|
//
|
|
// Returns corrected value
|
|
//
|
|
INT64 DDXV_SigUnsigINT32Value( CDataExchange *pDX, BOOL fIsSigned,
|
|
UINT idc, CString & str )
|
|
{
|
|
INT64 llVal = 0;
|
|
HRESULT hr = S_OK;
|
|
|
|
ASSERT( pDX );
|
|
ASSERT( pDX->m_pDlgWnd );
|
|
|
|
// Get/Put the string
|
|
DDX_Text( pDX, idc, str );
|
|
|
|
if ( pDX->m_bSaveAndValidate )
|
|
{
|
|
if( !str.IsEmpty() )
|
|
{
|
|
hr = GetSafeINT32FromString( pDX->m_pDlgWnd, llVal, str,
|
|
fIsSigned, GETSAFEINT_ALLOW_CANCEL );
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
pDX->Fail();
|
|
}
|
|
else if( S_VALUE_MODIFIED == hr )
|
|
{
|
|
// update the string in case of some conversion things ('010' --> '10')
|
|
// or if the value was changed
|
|
pDX->m_pDlgWnd->SetDlgItemText( idc, str );
|
|
}
|
|
}
|
|
}
|
|
|
|
return llVal;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Converts a string to a DWORD, asks to correct to be within the range.
|
|
// returns HRESULT:
|
|
// S_OK llDst is the value from string
|
|
// S_VALUE_MODIFIED llDst is the truncated value, strSrc is updated
|
|
// E_ABORT llDst is unchanged; E_ABORT may be returned only if fAllowCancel is TRUE
|
|
//
|
|
HRESULT GetSafeSignedDWORDFromString( CWnd * pwndParent, DWORD & dwDst, CString & strSrc,
|
|
BOOL fIsSigned, BOOL fAllowCancel /* =FALSE */)
|
|
{
|
|
INT64 llDst = 0;
|
|
HRESULT hr = GetSafeINT32FromString( pwndParent, llDst, strSrc, fIsSigned, fAllowCancel );
|
|
|
|
if( SUCCEEDED( hr ) )
|
|
dwDst = (DWORD) llDst;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// *** internal use ***
|
|
// Converts a string to a INT64, asks to correct to be within the range.
|
|
// returns HRESULT:
|
|
// S_OK llDst is the value from string
|
|
// S_VALUE_MODIFIED llDst is the truncated value, strSrc is updated
|
|
// E_ABORT llDst is the truncated value
|
|
// E_ABORT only happens if fAllowCancel is TRUE
|
|
//
|
|
HRESULT GetSafeINT32FromString( CWnd * pwndParent, INT64 & llDst, CString & strSrc,
|
|
BOOL fIsSigned, BOOL fAllowCancel)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT nMessageBoxType = 0;
|
|
CString szMsg;
|
|
CString szSugestedNumber;
|
|
BOOL fIsValidNumber = TRUE;
|
|
BOOL fIsValidString = TRUE;
|
|
|
|
|
|
ASSERT( pwndParent );
|
|
|
|
// the string must be limited in length & not empty
|
|
ASSERT( !strSrc.IsEmpty() );
|
|
ASSERT( strSrc.GetLength() <= cchMinMaxRange );
|
|
|
|
fIsValidString = IsValidNumberString( strSrc );
|
|
|
|
llDst = _wtoi64( (LPCWSTR) strSrc );
|
|
fIsValidNumber = IsValidNumber32( llDst, fIsSigned );
|
|
szSugestedNumber.Format( fIsSigned ? g_INT32_FORMAT : g_UINT32_FORMAT, (DWORD) llDst );
|
|
|
|
if( !fIsValidString || !fIsValidNumber )
|
|
{
|
|
szMsg.FormatMessage( !fIsValidString ? IDS_ERR_NUM_IS_ILLIGAL : IDS_ERR_INT_OVERFLOW,
|
|
(LPCWSTR) strSrc, (LPCWSTR) szSugestedNumber );
|
|
|
|
// make sure the user wants to do it
|
|
nMessageBoxType = (fAllowCancel ? MB_OKCANCEL : MB_OK) | MB_ICONEXCLAMATION ;
|
|
|
|
if( IDOK == pwndParent->MessageBox( szMsg, g_MsgBoxErr, nMessageBoxType ) )
|
|
{
|
|
strSrc = szSugestedNumber;
|
|
hr = S_VALUE_MODIFIED;
|
|
}
|
|
else
|
|
{
|
|
hr = E_ABORT;
|
|
}
|
|
}
|
|
else if( strSrc != szSugestedNumber )
|
|
{
|
|
// fixing number formating
|
|
strSrc = szSugestedNumber;
|
|
hr = S_VALUE_MODIFIED;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// Verify & correct Min/Max for a signed/unsigned INT64 value
|
|
//
|
|
BOOL IsValidNumber32( INT64 & llVal, BOOL fIsSigned )
|
|
{
|
|
|
|
#ifdef ENABLE_NEGATIVE_INT
|
|
const INT64 llMinVal = fIsSigned ? (INT64) _I32_MIN : (INT64) 0 ;
|
|
const INT64 llMaxVal = fIsSigned ? (INT64) _I32_MAX : (INT64) _UI32_MAX ;
|
|
#else
|
|
// if there is no negative numbers support, always use unsigned numbers
|
|
const INT64 llMinVal = (INT64) 0;
|
|
const INT64 llMaxVal = (INT64) _UI32_MAX;
|
|
#endif
|
|
|
|
BOOL fIsValid = FALSE;
|
|
|
|
// if larger than 32bit number (signed/unsigned), truncate...
|
|
if( llVal < llMinVal )
|
|
{
|
|
llVal = llMinVal;
|
|
}
|
|
else if( llVal > llMaxVal )
|
|
{
|
|
llVal = llMaxVal;
|
|
}
|
|
else
|
|
{
|
|
fIsValid = TRUE;
|
|
}
|
|
|
|
return fIsValid;
|
|
}
|
|
|
|
|
|
//
|
|
// Search number string for illigal characters.
|
|
//
|
|
BOOL IsValidNumberString( CString & str )
|
|
{
|
|
int i = 0;
|
|
|
|
#ifdef ENABLE_NEGATIVE_INT
|
|
if( str.GetLength() > 0 && // allow negative sign in front of the number
|
|
g_chNegativeSign == str[ i ] )
|
|
{
|
|
i++; // skip first character
|
|
}
|
|
#endif //ENABLE_NEGATIVE_INT
|
|
|
|
for( ; i < str.GetLength(); i++ )
|
|
{
|
|
if( !IsCharNumeric( str[i] ) )
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ParsedEdit
|
|
|
|
BEGIN_MESSAGE_MAP(CParsedEdit, CEdit)
|
|
//{{AFX_MSG_MAP(CParsedEdit)
|
|
ON_WM_CHAR()
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Initialize subclassing
|
|
|
|
BOOL CParsedEdit::SubclassEdit( UINT nID,
|
|
CWnd* pParent,
|
|
int cchMaxTextSize ) // 0 == unlimited
|
|
{
|
|
ASSERT( IsInitialized() );
|
|
ASSERT( nID );
|
|
ASSERT( pParent );
|
|
ASSERT( pParent->GetDlgItem(nID) );
|
|
ASSERT( cchMaxTextSize >= 0 );
|
|
|
|
( static_cast<CEdit *>( pParent->GetDlgItem(nID) ) ) -> LimitText( cchMaxTextSize ) ;
|
|
|
|
if( EDIT_TYPE_GENERIC == GetEditType() )
|
|
{
|
|
return TRUE; // no need to subclass - everything is allowed.
|
|
}
|
|
else
|
|
{
|
|
return SubclassDlgItem(nID, pParent);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Input character filter
|
|
|
|
void CParsedEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
|
|
{
|
|
ASSERT( IsInitialized() ); // initialized?
|
|
|
|
BOOL fIsSpecialChar = ( nChar < 0x20 );
|
|
|
|
BOOL fAllowChar = FALSE;
|
|
|
|
|
|
if( fIsSpecialChar )
|
|
{
|
|
fAllowChar = TRUE; // always allow control chars
|
|
}
|
|
else
|
|
{
|
|
// is this a digit?
|
|
BOOL fIsDigit = IsCharNumeric( (TCHAR)nChar );
|
|
|
|
switch( GetEditType() )
|
|
{
|
|
default:
|
|
ASSERT( FALSE );
|
|
break;
|
|
|
|
case EDIT_TYPE_GENERIC: // everything is allowed
|
|
fAllowChar = TRUE;
|
|
break;
|
|
|
|
case EDIT_TYPE_INT32:
|
|
case EDIT_TYPE_UINT32:
|
|
{
|
|
#ifdef ENABLE_NEGATIVE_INT
|
|
const BOOL fAllowNegativeNumbers = TRUE;
|
|
#else
|
|
const BOOL fAllowNegativeNumbers = FALSE;
|
|
#endif
|
|
|
|
DWORD dwSel = GetSel();
|
|
|
|
// is the caret in the begining of the box
|
|
BOOL fLineFront = ! LOWORD( dwSel );
|
|
|
|
// is the first character selected? (thus, typing anything will overide it)
|
|
BOOL fIsSelFirstChar = fLineFront && HIWORD( dwSel );
|
|
|
|
BOOL fIsNegSign = ( (TCHAR)nChar == g_chNegativeSign );
|
|
|
|
WCHAR szBuf[ 2 ] = {0}; // we only need the first character to check for '-'
|
|
|
|
// if the first character is selected, no matter what we type it will be overwritten
|
|
// an empty value is a positive value.
|
|
// rellies on left to right execution.
|
|
BOOL fIsAlreadyNeg = ( !fIsSelFirstChar &&
|
|
GetWindowText( szBuf, 2 ) &&
|
|
g_chNegativeSign == szBuf[0] );
|
|
|
|
ASSERT( !fIsDigit || !fIsNegSign ); // cannot be both!
|
|
|
|
|
|
if (
|
|
( fIsDigit && // allow numeric if ...
|
|
( !fAllowNegativeNumbers || // ignore error checking if false
|
|
!fLineFront || // not first position
|
|
(fLineFront && !fIsAlreadyNeg)) // first pos & no '-' sign
|
|
)
|
|
|
|
||
|
|
|
|
( fIsNegSign && // allow '-' if
|
|
fAllowNegativeNumbers && // negatives are allowed
|
|
FIsSigned() && // signed numbers are allowed
|
|
!fIsAlreadyNeg && // the number was positive
|
|
fLineFront // entering as the first character
|
|
)
|
|
)
|
|
{
|
|
fAllowChar = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EDIT_TYPE_OID: // do a light checking -- allow digits & periods
|
|
{
|
|
if( fIsDigit ||
|
|
g_chPeriod == (TCHAR)nChar )
|
|
{
|
|
fAllowChar = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if( fAllowChar )
|
|
{
|
|
CEdit::OnChar(nChar, nRepCnt, nFlags); // permitted
|
|
}
|
|
else
|
|
{ // not permitted
|
|
MessageBeep((UINT)-1); // Standard beep
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Search a list of PCTSTR for a strValue, returns TRUE if found
|
|
// rgszList[] last element must be NULL
|
|
//
|
|
// puIndex - optional pointer, will be set to the position of the value if found.
|
|
//
|
|
BOOL
|
|
IsInList( PCTSTR rgszList[], const CString & strValue, UINT * puIndex /* = NULL */ )
|
|
{
|
|
UINT uIndex = 0;
|
|
|
|
while( rgszList[ uIndex ] )
|
|
{
|
|
if( !strValue.CompareNoCase( rgszList[uIndex] ) )
|
|
{
|
|
if( puIndex )
|
|
*puIndex = uIndex;
|
|
return TRUE;
|
|
}
|
|
else
|
|
uIndex++;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
#define ADS_SYSTEMFLAG_SCHEMA_CONSTRUCTED 0x04
|
|
#define ADS_SYSTEMFLAG_SCHEMA_BASE_OBJECT 0x10
|
|
|
|
|
|
//
|
|
// Determine if the object pointed to by pIADsObject is a constructed object.
|
|
//
|
|
HRESULT
|
|
IsConstructedObject( IADs *pIADsObject, BOOL & fIsConstructed )
|
|
{
|
|
LONG fSysAttribs = 0;
|
|
HRESULT hr = GetSystemAttributes( pIADsObject, fSysAttribs );
|
|
|
|
if( SUCCEEDED(hr) )
|
|
fIsConstructed = ADS_SYSTEMFLAG_SCHEMA_CONSTRUCTED & fSysAttribs;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Determine if the object pointed to by pIADsObject is category 1 object.
|
|
//
|
|
HRESULT
|
|
IsCategory1Object( IADs *pIADsObject, BOOL & fIsCategory1 )
|
|
{
|
|
LONG fSysAttribs = 0;
|
|
HRESULT hr = GetSystemAttributes( pIADsObject, fSysAttribs );
|
|
|
|
if( SUCCEEDED(hr) )
|
|
fIsCategory1 = ADS_SYSTEMFLAG_SCHEMA_BASE_OBJECT & fSysAttribs;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Read object's System Attribute
|
|
//
|
|
HRESULT
|
|
GetSystemAttributes( IADs *pIADsObject, LONG &fSysAttribs )
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
VARIANT AdsResult;
|
|
|
|
if( !pIADsObject )
|
|
ASSERT( FALSE );
|
|
else
|
|
{
|
|
VariantInit( &AdsResult );
|
|
|
|
hr = pIADsObject->Get( const_cast<BSTR>((LPCTSTR)g_systemFlags), &AdsResult );
|
|
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
ASSERT(AdsResult.vt == VT_I4);
|
|
fSysAttribs = V_I4(&AdsResult);
|
|
}
|
|
else if( E_ADS_PROPERTY_NOT_FOUND == hr )
|
|
{
|
|
fSysAttribs = 0;
|
|
hr = S_OK;
|
|
}
|
|
|
|
VariantClear( &AdsResult );
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
DissableReadOnlyAttributes( CWnd * pwnd, IADs *pIADsObject, const CDialogControlsInfo * pCtrls, UINT cCtrls )
|
|
{
|
|
ASSERT( pwnd );
|
|
ASSERT( pIADsObject );
|
|
ASSERT( pCtrls );
|
|
ASSERT( cCtrls );
|
|
|
|
HRESULT hr = S_OK;
|
|
CStringList strlist;
|
|
|
|
do
|
|
{
|
|
// extract the list of allowed attributes
|
|
hr = GetStringListElement( pIADsObject, &g_allowedAttributesEffective, strlist );
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
for( UINT ind = 0; ind < cCtrls; ind++ )
|
|
{
|
|
BOOL fFound = FALSE;
|
|
|
|
// search for needed attributes
|
|
for( POSITION pos = strlist.GetHeadPosition(); !fFound && pos != NULL; )
|
|
{
|
|
CString * pstr = &strlist.GetNext( pos );
|
|
|
|
if( !pstr->CompareNoCase( pCtrls[ind].m_pszAttributeName ) )
|
|
{
|
|
fFound = TRUE;
|
|
}
|
|
}
|
|
|
|
if( !fFound )
|
|
{
|
|
ASSERT( pwnd->GetDlgItem( pCtrls[ind].m_nID ) );
|
|
|
|
if( pCtrls[ind].m_fIsEditBox )
|
|
reinterpret_cast<CEdit *>( pwnd->GetDlgItem(pCtrls[ind].m_nID) )->SetReadOnly();
|
|
else
|
|
pwnd->GetDlgItem(pCtrls[ind].m_nID)->EnableWindow( FALSE );
|
|
}
|
|
}
|
|
|
|
} while( FALSE );
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT GetStringListElement( IADs *pIADsObject, LPWSTR *lppPathNames, CStringList &strlist )
|
|
{
|
|
ASSERT( pIADsObject );
|
|
ASSERT( lppPathNames );
|
|
ASSERT( *lppPathNames );
|
|
|
|
HRESULT hr = S_OK;
|
|
VARIANT varAttributes;
|
|
|
|
VariantInit( &varAttributes );
|
|
|
|
strlist.RemoveAll();
|
|
|
|
do
|
|
{
|
|
// build an array of one element
|
|
hr = ADsBuildVarArrayStr( lppPathNames, 1, &varAttributes );
|
|
ASSERT_BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
hr = pIADsObject->GetInfoEx( varAttributes, 0 );
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
hr = VariantClear( &varAttributes );
|
|
ASSERT_BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
|
|
// Get all allowed attributes
|
|
hr = pIADsObject->GetEx( *lppPathNames, &varAttributes );
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
// Convert result to a string list
|
|
hr = VariantToStringList( varAttributes, strlist );
|
|
ASSERT( SUCCEEDED(hr) || E_ADS_PROPERTY_NOT_FOUND == hr );
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
} while( FALSE );
|
|
|
|
VariantClear( &varAttributes );
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
bool OIDHasValidFormat (PCWSTR pszOidValue, int& rErrorTypeStrID)
|
|
{
|
|
rErrorTypeStrID = 0;
|
|
|
|
bool bFormatIsValid = false;
|
|
int nLen = WideCharToMultiByte(
|
|
CP_ACP, // code page
|
|
0, // performance and mapping flags
|
|
pszOidValue, // wide-character string
|
|
(int) wcslen (pszOidValue), // number of chars in string
|
|
0, // buffer for new string
|
|
0, // size of buffer
|
|
0, // default for unmappable chars
|
|
0); // set when default char used
|
|
if ( nLen > 0 )
|
|
{
|
|
nLen++; // account for Null terminator
|
|
PSTR pszAnsiBuf = new CHAR[nLen];
|
|
if ( pszAnsiBuf )
|
|
{
|
|
ZeroMemory (pszAnsiBuf, nLen*sizeof(CHAR));
|
|
nLen = WideCharToMultiByte(
|
|
CP_ACP, // code page
|
|
0, // performance and mapping flags
|
|
pszOidValue, // wide-character string
|
|
(int) wcslen (pszOidValue), // number of chars in string
|
|
pszAnsiBuf, // buffer for new string
|
|
nLen, // size of buffer
|
|
0, // default for unmappable chars
|
|
0); // set when default char used
|
|
if ( nLen )
|
|
{
|
|
// According to PhilH:
|
|
// The first number is limited to
|
|
// 0,1 or 2. The second number is
|
|
// limited to 0 - 39 when the first
|
|
// number is 0 or 1. Otherwise, any
|
|
// number.
|
|
// Also, according to X.208, there
|
|
// must be at least 2 numbers.
|
|
bFormatIsValid = true;
|
|
size_t cbAnsiBufLen = strlen (pszAnsiBuf);
|
|
|
|
// check for only digits and "."
|
|
size_t nIdx = strspn (pszAnsiBuf, "0123456789.\0");
|
|
if ( nIdx > 0 && nIdx < cbAnsiBufLen )
|
|
{
|
|
bFormatIsValid = false;
|
|
rErrorTypeStrID = IDS_OID_CONTAINS_NON_DIGITS;
|
|
}
|
|
|
|
// check for consecutive "."s - string not valid if present
|
|
if ( bFormatIsValid && strstr (pszAnsiBuf, "..") )
|
|
{
|
|
bFormatIsValid = false;
|
|
rErrorTypeStrID = IDS_OID_CONTAINS_CONSECUTIVE_DOTS;
|
|
}
|
|
|
|
|
|
// must begin with "0." or "1." or "2."
|
|
bool bFirstNumberIs0 = false;
|
|
bool bFirstNumberIs1 = false;
|
|
bool bFirstNumberIs2 = false;
|
|
if ( bFormatIsValid )
|
|
{
|
|
if ( !strncmp (pszAnsiBuf, "0.", 2) )
|
|
bFirstNumberIs0 = true;
|
|
else if ( !strncmp (pszAnsiBuf, "1.", 2) )
|
|
bFirstNumberIs1 = true;
|
|
else if ( !strncmp (pszAnsiBuf, "2.", 2) )
|
|
bFirstNumberIs2 = true;
|
|
|
|
if ( !bFirstNumberIs0 && !bFirstNumberIs1 && !bFirstNumberIs2 )
|
|
{
|
|
bFormatIsValid = false;
|
|
rErrorTypeStrID = IDS_OID_MUST_START_WITH_0_1_2;
|
|
}
|
|
}
|
|
|
|
if ( bFormatIsValid && ( bFirstNumberIs0 || bFirstNumberIs1 ) )
|
|
{
|
|
PSTR pszBuf = pszAnsiBuf;
|
|
pszBuf += 2;
|
|
|
|
// there must be a number after the dot
|
|
if ( strlen (pszBuf) )
|
|
{
|
|
// truncate the string at the next dot, if any
|
|
PSTR pszDot = strstr (pszBuf, ".");
|
|
if ( pszDot )
|
|
pszDot[0] = 0;
|
|
|
|
// convert the string to a number and check for range 0-39
|
|
int nValue = atoi (pszBuf);
|
|
if ( nValue < 0 || nValue > 39 )
|
|
{
|
|
bFormatIsValid = false;
|
|
rErrorTypeStrID = IDS_OID_0_1_MUST_BE_0_TO_39;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bFormatIsValid = false;
|
|
rErrorTypeStrID = IDS_OID_MUST_HAVE_TWO_NUMBERS;
|
|
}
|
|
}
|
|
|
|
// ensure no trailing "."
|
|
if ( bFormatIsValid )
|
|
{
|
|
if ( '.' == pszAnsiBuf[cbAnsiBufLen - 1] )
|
|
{
|
|
bFormatIsValid = false;
|
|
rErrorTypeStrID = IDS_OID_CANNOT_END_WITH_DOT;
|
|
}
|
|
}
|
|
|
|
if ( bFormatIsValid )
|
|
{
|
|
bFormatIsValid = false;
|
|
CRYPT_ATTRIBUTE cryptAttr;
|
|
::ZeroMemory (&cryptAttr, sizeof (CRYPT_ATTRIBUTE));
|
|
|
|
cryptAttr.cValue = 0;
|
|
cryptAttr.pszObjId = pszAnsiBuf;
|
|
cryptAttr.rgValue = 0;
|
|
|
|
DWORD cbEncoded = 0;
|
|
BOOL bResult = CryptEncodeObject (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
PKCS_ATTRIBUTE,
|
|
&cryptAttr,
|
|
NULL,
|
|
&cbEncoded);
|
|
if ( cbEncoded > 0 )
|
|
{
|
|
BYTE* pBuffer = new BYTE[cbEncoded];
|
|
if ( pBuffer )
|
|
{
|
|
bResult = CryptEncodeObject (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
PKCS_ATTRIBUTE,
|
|
&cryptAttr,
|
|
pBuffer,
|
|
&cbEncoded);
|
|
if ( bResult )
|
|
{
|
|
DWORD cbStructInfo = 0;
|
|
bResult = CryptDecodeObject (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
PKCS_ATTRIBUTE,
|
|
pBuffer,
|
|
cbEncoded,
|
|
0,
|
|
0,
|
|
&cbStructInfo);
|
|
if ( cbStructInfo > 0 )
|
|
{
|
|
BYTE* pStructBuf = new BYTE[cbStructInfo];
|
|
if ( pStructBuf )
|
|
{
|
|
bResult = CryptDecodeObject (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
PKCS_ATTRIBUTE,
|
|
pBuffer,
|
|
cbEncoded,
|
|
0,
|
|
pStructBuf,
|
|
&cbStructInfo);
|
|
if ( bResult )
|
|
{
|
|
CRYPT_ATTRIBUTE* pCryptAttr = (CRYPT_ATTRIBUTE*) pStructBuf;
|
|
if ( !strcmp (pszAnsiBuf, pCryptAttr->pszObjId) )
|
|
{
|
|
bFormatIsValid = true;
|
|
}
|
|
}
|
|
delete [] pStructBuf;
|
|
}
|
|
}
|
|
}
|
|
delete [] pBuffer;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DebugTrace(L"WideCharToMultiByte (%s) failed: 0x%x\n", pszOidValue,
|
|
GetLastError ());
|
|
}
|
|
|
|
delete [] pszAnsiBuf;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DebugTrace(L"WideCharToMultiByte (%s) failed: 0x%x\n", pszOidValue,
|
|
GetLastError ());
|
|
}
|
|
|
|
return bFormatIsValid;
|
|
}
|
|
|
|
HRESULT
|
|
DeleteObject(
|
|
const CString& path,
|
|
Cookie* pcookie,
|
|
PCWSTR pszClass
|
|
)
|
|
/***
|
|
|
|
This deletes an attribute from the schema
|
|
|
|
***/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
do
|
|
{
|
|
if ( !pcookie )
|
|
{
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
|
|
CComPtr<IADsPathname> spIADsPathname;
|
|
|
|
hr = ::CoCreateInstance( CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IADsPathname, (void**)&spIADsPathname);
|
|
if ( FAILED(hr) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = spIADsPathname->Set( (PWSTR)(PCWSTR)path, ADS_SETTYPE_FULL );
|
|
if ( FAILED(hr) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Get the RDN so that we have it for deleting
|
|
|
|
CComBSTR sbstrRDN;
|
|
hr = spIADsPathname->Retrieve( ADS_FORMAT_LEAF, &sbstrRDN );
|
|
if ( FAILED(hr) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Get the path to the parent container
|
|
|
|
hr = spIADsPathname->RemoveLeafElement();
|
|
if ( FAILED(hr) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
CComBSTR sbstrParentPath;
|
|
hr = spIADsPathname->Retrieve( ADS_FORMAT_X500, &sbstrParentPath );
|
|
if ( FAILED(hr) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Now open the parent object
|
|
|
|
CComPtr<IADsContainer> spContainer;
|
|
hr = ::ADsOpenObject( sbstrParentPath, NULL, NULL, ADS_SECURE_AUTHENTICATION,
|
|
IID_IADsContainer, (void**)&spContainer);
|
|
if ( FAILED(hr) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = spContainer->Delete( (PWSTR)pszClass, sbstrRDN );
|
|
if ( FAILED(hr) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
} while (false);
|
|
|
|
return hr;
|
|
}
|