Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2531 lines
72 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[] =
{
// NTRAID#NTBUG9-540278-2002/05/15-lucios
// Added SYNTAX_CASE_STRING_TYPE
// This list should be kept in alphabetical order since the combo box is not sorted
// so that the index from the combo box can map to an entry in this table
// nResourceID, fIsSigned, fIsANRCapable, pszAttributeSyntax, nOmSyntax, dwOmObjectClass, pOmObjectClass
/*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_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_BOOLEAN_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_BOOLEAN, FALSE, FALSE, _T("2.5.5.8") , /*OM_S_BOOLEAN */ 1 , 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_CASE_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_CASE_STR, FALSE, TRUE , _T("2.5.5.3"), /*OM_S_GENERAL_STRING */ 27 , 0, NULL ),
/*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_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_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_INTEGER_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_ENUMERATION, TRUE, FALSE, _T("2.5.5.9") , /*OM_S_ENUMERATION */ 10 , 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_PRINT_CASE_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_I5_STR, FALSE, TRUE, _T("2.5.5.5") , /*OM_S_IA5_STRING */ 22 , 0, NULL ),
/*SYNTAX_INTEGER_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_INTEGER, TRUE, FALSE, _T("2.5.5.9") , /*OM_S_INTEGER */ 2 , 0, NULL ),
/*SYNTAX_I8_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_LINT, FALSE, FALSE, _T("2.5.5.16"), /*OM_S_I8 */ 65 , 0, NULL ),
/*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_NUMERIC_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_NUMSTR, FALSE, FALSE, _T("2.5.5.6") , /*OM_S_NUMERIC_STRING */ 18 , 0, NULL ),
/*SYNTAX_OBJECT_ID_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_OID, FALSE, FALSE, _T("2.5.5.2") , /*OM_S_OBJECT_IDENTIFIER_STRING */ 6 , 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_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_PRINT_CASE_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_PRCS_STR, FALSE, TRUE, _T("2.5.5.5") , /*OM_S_PRINTABLE_STRING */ 19 , 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_SID_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_SID, FALSE, FALSE, _T("2.5.5.17"), /*OM_S_OCTET_STRING */ 4 , 0, NULL ),
/*SYNTAX_UNICODE_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_UNICODE, FALSE, TRUE, _T("2.5.5.12"), /*OM_S_UNICODE_STRING */ 64 , 0, NULL ),
/*SYNTAX_TIME_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_UTC, FALSE, FALSE, _T("2.5.5.11"), /*OM_S_UTC_TIME_STRING */ 23 , 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;
//
// 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) );
//
// 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[VIEW_DEFUNCT_OBJECTS].LoadString
(
IDS_MENU_VIEW_DEFUNCT_OBJECTS
)
);
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[VIEW_DEFUNCT_OBJECTS].LoadString
(
IDS_STATUS_VIEW_DEFUNCT_OBJECTS
)
);
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.
HRESULT hr // IN: the error code (optional)
)
/***
Display a message box with the error.
***/
{
CString Error;
VERIFY( Error.LoadString( wIdString ) );
return DoErrMsgBox( hwndParent, fError, Error, hr );
}
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.
HRESULT hr // IN: the error code (optional)
)
/***
Display a message box with the error.
***/
{
CThemeContextActivator activator;
PTSTR ptzSysMsg = NULL;
int cch = 0;
if (FAILED(hr))
{
cch = cchLoadHrMsg(hr, &ptzSysMsg, TRUE);
}
if (!cch)
{
return MessageBox(
hwndParent,
pszError,
(fError ? g_MsgBoxErr : g_MsgBoxWarn),
(fError ? MB_ICONSTOP : MB_ICONEXCLAMATION) | MB_OK
);
}
CString szError = pszError;
szError += L"\n";
szError += ptzSysMsg;
return MessageBox(
hwndParent,
szError,
(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);
// NTRAID#NTBUG9-540866-2002/02/13-dantra-Schema Manager: passing WCHAR * instead of BSTR to method requiring a BSTR
// NOTE: const_cast not necessary here since ADsGetObject takes a WSTR for the first parameter.
hr = SchemaOpenObject(
( 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.
//
// NTRAID#NTBUG9-540866-2002/02/13-dantra-Schema Manager: passing WCHAR * instead of BSTR to method requiring a BSTR
hr = pSchemaRootDse->PutEx( ADS_PROPERTY_APPEND,
const_cast<BSTR>((LPCTSTR)g_UpdateSchema),
AdsArray );
if(FAILED(hr)) {hr=S_FALSE;break;} // schema is read only
hr = pSchemaRootDse->SetInfo();
if(FAILED(hr)) {hr=S_FALSE;break;} // schema is read only
} 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 = SchemaOpenObject(
(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;
// NTRAID#NTBUG9-540866-2002/02/13-dantra-Schema Manager: passing WCHAR * instead of BSTR to method requiring a BSTR
hr = pSchemaContainer->Put( const_cast<BSTR>((LPCTSTR)g_UpdateSchema),
AdsValue );
// NTRAID#NTBUG9-542354-2002/02/14-dantra-Errors returned by IADs::Put and PutEx are being masked.
if ( 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, &current, &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, &current, &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
)
{
CThemeContextActivator activator;
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;
// NTRAID#NTBUG9-562405-2002/03/04-dantra-Possible use of uninitalized SCOPEDATAITEM
SCOPEDATAITEM ScopeItem={0};
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;
}
}
int 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)
{
CThemeContextActivator activator;
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 );
// NTRAID#NTBUG9-540866-2002/02/13-dantra-Schema Manager: passing WCHAR * instead of BSTR to method requiring a BSTR
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
// NTRAID#NTBUG9-540866-2002/02/13-dantra-Schema Manager: passing WCHAR * instead of BSTR to method requiring a BSTR
hr = pIADsObject->GetEx( CComBSTR(*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;
}
// FUTURE-2002-03/94/2002-dantra-Needs comments
// NTRAID#NTBUG9-567089-2002/03/06-dantra-OIDHasValidFormat should use the safe string functions.
bool OIDHasValidFormat (PCWSTR pszOidValue, int& rErrorTypeStrID)
{
rErrorTypeStrID = 0;
bool bFormatIsValid = false;
// NOTE: Safe Use - query length including NULL terminator
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 )
{
// NOTE: Safe Use
ZeroMemory (pszAnsiBuf, nLen*sizeof(CHAR));
// NOTE: Safe Use - nLen obtained from first call to WideCharToMultiByte
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;
// FUTURE-2002-03/94/2002-dantra-Although this is a safe usage of ZeroMemory, suggest changing
// the definition CRYPT_ATTRIBUTE cryptAttr = {0} and removing the ZeroMemory call.
::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
{
rErrorTypeStrID = IDS_OID_MUST_NOT_BE_EMPTY;
DebugTrace(L"WideCharToMultiByte (%s) return: 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;
}
// NTRAID#NTBUG9-540866-2002/02/13-dantra-Schema Manager: passing WCHAR * instead of BSTR to method requiring a BSTR
hr = spIADsPathname->Set( CComBSTR(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 = ::AdminToolsOpenObject( sbstrParentPath, NULL, NULL, ADS_SECURE_AUTHENTICATION,
IID_IADsContainer, (void**)&spContainer);
if ( FAILED(hr) )
{
break;
}
// NTRAID#NTBUG9-540866-2002/02/13-dantra-Schema Manager: passing WCHAR * instead of BSTR to method requiring a BSTR
hr = spContainer->Delete( CComBSTR(pszClass), sbstrRDN );
if ( FAILED(hr) )
{
break;
}
} while (false);
return hr;
}
//////////////////////////////////////////////////////////////////
// Theming support
HPROPSHEETPAGE MyCreatePropertySheetPage(AFX_OLDPROPSHEETPAGE* psp)
{
PROPSHEETPAGE_V3 sp_v3 = {0};
// NTRAID#NTBUG9-567166-2002/03/06-dantra-Possible buffer overrun in MyCreatePropertySheetPage
CopyMemory (&sp_v3, psp, psp->dwSize);
sp_v3.dwSize = sizeof(sp_v3);
return (::CreatePropertySheetPage(&sp_v3));
}
// Menu Helper
HRESULT
_InsertMenuHelper(
LPCONTEXTMENUCALLBACK piCallback,
long lInsertionPointID,
int index,
BOOL fEnabled, /* = TRUE */
BOOL fChecked /* = FALSE */)
{
CONTEXTMENUITEM MenuItem;
MenuItem.lInsertionPointID = lInsertionPointID;
MenuItem.fFlags = (fEnabled ? 0 : MF_GRAYED) |
(fChecked ? MF_CHECKED : 0);
MenuItem.fSpecialFlags = 0;
MenuItem.strName = const_cast<BSTR>(
(LPCTSTR)g_MenuStrings[index] );
MenuItem.strStatusBarText = const_cast<BSTR>(
(LPCTSTR)g_StatusStrings[index] );
MenuItem.lCommandID = index;
return piCallback->AddItem( &MenuItem );
}
HRESULT
SchemaOpenObject
(
PCWSTR pathName,
REFIID riid,
void** object
)
{
return
(
AdminToolsOpenObject
(
const_cast<LPWSTR>((LPCWSTR) pathName),
NULL,
NULL,
ADS_SECURE_AUTHENTICATION,
riid,
object
)
);
}