#include "stdafx.h" #include "compdata.h" #include "wizinfo.hpp" #include "ncattr.hpp" #include "select.h" // // defaultObjectCategory of the classes derived from the following should be set // to defaultObjectCategory of the parent class. // // the first column contains class ldap names, // the second contains their corresponding OIDs (in case the user specifies them) const TCHAR * rgszSpecialClassesLdapNames[] = { USER_CLASS_NAME, GROUP_CLASS_NAME, COMPUTER_CLASS_NAME, PRINTER_CLASS_NAME, TEXT("volume"), TEXT("contact"), NULL }; // must match rgszSpecialClassesLdapNames[]. const TCHAR * rgszSpecialClassesOIDs[] = { TEXT("1.2.840.113556.1.5.9"), // USER_CLASS_NAME TEXT("1.2.840.113556.1.5.8"), // GROUP_CLASS_NAME TEXT("1.2.840.113556.1.3.30"), // COMPUTER_CLASS_NAME TEXT("1.2.840.113556.1.5.23"), // PRINTER_CLASS_NAME TEXT("1.2.840.113556.1.5.36"), // TEXT("volume") TEXT("1.2.840.113556.1.5.15"), // TEXT("contact") NULL }; const DWORD NewClassAttributesPage::help_map[] = { IDC_MANDATORY_LIST, IDH_CLASS_MMB_MANDATORY_ATTRIBUTES, IDC_MANDATORY_ADD, IDH_CLASS_MMB_MANDATORY_ADD, IDC_MANDATORY_REMOVE, IDH_CLASS_MMB_MANDATORY_REMOVE, IDC_OPTIONAL_LIST, IDH_CLASS_MMB_OPTIONAL_ATTRIBUTES, IDC_OPTIONAL_ADD, IDH_CLASS_MMB_OPTIONAL_ADD, IDC_OPTIONAL_REMOVE, IDH_CLASS_MMB_OPTIONAL_REMOVE, 0, 0 }; BEGIN_MESSAGE_MAP(NewClassAttributesPage, CPropertyPage) ON_BN_CLICKED(IDC_OPTIONAL_ADD, OnButtonOptionalAdd) ON_BN_CLICKED(IDC_OPTIONAL_REMOVE, OnButtonOptionalRemove) ON_BN_CLICKED(IDC_MANDATORY_ADD, OnButtonMandatoryAdd) ON_BN_CLICKED(IDC_MANDATORY_REMOVE, OnButtonMandatoryRemove) ON_LBN_SELCHANGE(IDC_MANDATORY_LIST,OnMandatorySelChange) ON_LBN_SELCHANGE(IDC_OPTIONAL_LIST, OnOptionalSelChange) ON_MESSAGE(WM_HELP, OnHelp) ON_MESSAGE(WM_CONTEXTMENU, OnContextHelp) END_MESSAGE_MAP() NewClassAttributesPage::NewClassAttributesPage( CreateClassWizardInfo* wi, ComponentData* cd) : CPropertyPage(IDD_CREATE_CLASS_ATTRIBUTES), wiz_info(*wi), parent_ComponentData(*cd) { } BOOL NewClassAttributesPage::OnInitDialog() { // This calls must be done before DDX binding listbox_mandatory.InitType( &parent_ComponentData, SELECT_ATTRIBUTES, IDC_MANDATORY_REMOVE ); listbox_optional.InitType( &parent_ComponentData, SELECT_ATTRIBUTES, IDC_OPTIONAL_REMOVE ); CPropertyPage::OnInitDialog(); return FALSE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } void NewClassAttributesPage::OnOK() { CPropertyPage::OnOK(); } BOOL NewClassAttributesPage::OnKillActive() { if (saveAndValidate()) { // allow loss of focus return TRUE; } return FALSE; } bool NewClassAttributesPage::saveAndValidate() { // save settings wiz_info.strlistMandatory.RemoveAll(); HRESULT hr = RetrieveEditItemsWithExclusions( listbox_mandatory, wiz_info.strlistMandatory, 0); ASSERT(SUCCEEDED(hr)); wiz_info.strlistOptional.RemoveAll(); hr = RetrieveEditItemsWithExclusions( listbox_optional, wiz_info.strlistOptional, 0); ASSERT(SUCCEEDED(hr)); // nothing to validate... return true; } BOOL NewClassAttributesPage::OnSetActive() { OnMandatorySelChange(); OnOptionalSelChange(); CPropertySheet* parent = (CPropertySheet*) GetParent(); parent->SetWizardButtons(PSWIZB_BACK | PSWIZB_FINISH); return TRUE; } BOOL NewClassAttributesPage::OnWizardFinish() { if (!saveAndValidate()) { return FALSE; } // Create the class object. We do the create here (instead of at the point // where DoModal is invoked) because we want the wizard to remain if the // create fails for some reason. CWaitCursor wait; HRESULT hr = S_OK; SchemaObject* new_object = 0; do { // bind to the schema container CString schema_path; parent_ComponentData.GetBasePathsInfo()->GetSchemaPath(schema_path); CComPtr schema_container; hr = ::ADsGetObject( // ADSI guys don't grok const. const_cast(static_cast(schema_path)), IID_IADsContainer, reinterpret_cast(&schema_container)); BREAK_ON_FAILED_HRESULT(hr); // Get Relative Name CString strRelativeName; parent_ComponentData.GetSchemaObjectPath( wiz_info.cn, strRelativeName, ADS_FORMAT_LEAF ); // create the class object CComPtr dispatch; hr = schema_container->Create( CComBSTR(g_ClassFilter), CComBSTR(strRelativeName), &dispatch); BREAK_ON_FAILED_HRESULT(hr); CComPtr iads; hr = dispatch->QueryInterface(IID_IADs, reinterpret_cast(&iads)); BREAK_ON_FAILED_HRESULT(hr); // // populate the class object's properties // // OID { CComVariant value(CComBSTR(wiz_info.oid)); hr = iads->Put(CComBSTR(g_GlobalClassID), value); BREAK_ON_FAILED_HRESULT(hr); } // class type { CComVariant value(wiz_info.type + 1); hr = iads->Put(CComBSTR(g_ObjectClassCategory), value); BREAK_ON_FAILED_HRESULT(hr); } // description if (!wiz_info.description.IsEmpty()) { CComVariant value(CComBSTR(wiz_info.description)); hr = iads->Put(CComBSTR(g_Description), value); BREAK_ON_FAILED_HRESULT(hr); } // default security descriptor { // authenticated users - full access // system - full control // domain admins - full control static const PWSTR defsd = L"D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)" L"(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)"; CComVariant value(defsd); hr = iads->Put(CComBSTR(g_DefaultAcl), value); BREAK_ON_FAILED_HRESULT(hr); } // LDAP display name if (!wiz_info.ldapDisplayName.IsEmpty()) { CComVariant value(wiz_info.ldapDisplayName); hr = iads->Put(CComBSTR(g_DisplayName), value); BREAK_ON_FAILED_HRESULT(hr); } // parent class if (!wiz_info.parentClass.IsEmpty()) { PCWSTR pstr = NULL; SchemaObject * parent_class = parent_ComponentData.g_SchemaCache.LookupSchemaObject( wiz_info.parentClass, SCHMMGMT_CLASS); if( parent_class ) { pstr = parent_class->oid; } else { pstr = wiz_info.parentClass; } CComVariant value(pstr); hr = iads->Put(CComBSTR(g_SubclassOf), value); if( FAILED(hr) ) { parent_ComponentData.g_SchemaCache.ReleaseRef(parent_class); break; } // check if parent is one of the magic classes whose defaultObjectCategory // should be the same as the parent. BOOL fIsSpecialParent = FALSE; ASSERT( sizeof(rgszSpecialClassesOIDs) == sizeof(rgszSpecialClassesLdapNames) ); if( parent_class ) { fIsSpecialParent = IsInList( rgszSpecialClassesLdapNames, parent_class->ldapDisplayName ); } else { UINT uIndex = 0; // lookup by LDAP failed. check if parent is specified by OID fIsSpecialParent = IsInList( rgszSpecialClassesOIDs, wiz_info.parentClass, &uIndex ); if( fIsSpecialParent ) { parent_class = parent_ComponentData.g_SchemaCache.LookupSchemaObject( rgszSpecialClassesLdapNames[uIndex], SCHMMGMT_CLASS); ASSERT( parent_class ); // the schema cache must contain well known classes. } } // if this is a special class, get parent's defaultObjectCategory. if( fIsSpecialParent && parent_class ) { CString szParentPath; IADs * pIADsParentObject = NULL; VARIANT adsValue; VariantInit( &adsValue ); do { // one pass do-while loop to help with error handling // if any errors occure, ignore them. // Find out the defaultObjectCategory of the parent class & use it parent_ComponentData.GetSchemaObjectPath( parent_class->commonName, szParentPath ); if( szParentPath.IsEmpty() ) break; hr = ADsGetObject( const_cast((LPCWSTR)szParentPath), IID_IADs, (void **)&pIADsParentObject ); if ( !pIADsParentObject || FAILED(hr) ) { DoErrMsgBox( ::GetActiveWindow(), TRUE, GetErrorMessage(hr) ); hr = NULL; break; } hr = pIADsParentObject->Get( const_cast((LPCTSTR)g_DefaultCategory), &adsValue ); if( FAILED(hr) ) { DoErrMsgBox( ::GetActiveWindow(), TRUE, GetErrorMessage(hr,TRUE) ); hr = NULL; break; } ASSERT( adsValue.vt == VT_BSTR ); // preserve hr so that save fails after this loop hr = iads->Put( const_cast((LPCTSTR)g_DefaultCategory), adsValue ); } while (FALSE); VariantClear( &adsValue ); parent_ComponentData.g_SchemaCache.ReleaseRef(parent_class); if( pIADsParentObject ) pIADsParentObject->Release(); BREAK_ON_FAILED_HRESULT(hr); } else { parent_ComponentData.g_SchemaCache.ReleaseRef(parent_class); } } // optional attributes if (!wiz_info.strlistOptional.IsEmpty()) { VARIANT value; ::VariantInit(&value); hr = StringListToVariant(value, wiz_info.strlistOptional); // don't break: plod onward. ASSERT(SUCCEEDED(hr)); hr = iads->PutEx(ADS_PROPERTY_UPDATE, g_MayContain, value); ::VariantClear(&value); BREAK_ON_FAILED_HRESULT(hr); } // mandatory attributes if (!wiz_info.strlistMandatory.IsEmpty()) { VARIANT value; ::VariantInit(&value); hr = StringListToVariant(value, wiz_info.strlistMandatory); // don't break: plod onward. ASSERT(SUCCEEDED(hr)); hr = iads->PutEx(ADS_PROPERTY_UPDATE, g_MustContain, value); ::VariantClear(&value); BREAK_ON_FAILED_HRESULT(hr); } // commit the create hr = iads->SetInfo(); BREAK_ON_FAILED_HRESULT(hr); // if there was no ldap name, and it worked, cn was used as ldap name if( wiz_info.ldapDisplayName.IsEmpty() ) { CComVariant value; hr = iads->Get(CComBSTR(g_DisplayName), &value); ASSERT( SUCCEEDED(hr) ); // should be there!!! if( SUCCEEDED(hr) ) { ASSERT( value.vt == VT_BSTR ); wiz_info.ldapDisplayName = value.bstrVal; } } // create a cache entry for the new class object new_object = new SchemaObject; new_object->schemaObjectType = SCHMMGMT_CLASS; new_object->commonName = wiz_info.cn; new_object->ldapDisplayName = wiz_info.ldapDisplayName; new_object->oid = wiz_info.oid; new_object->dwClassType = wiz_info.type + 1; new_object->subClassOf = wiz_info.parentClass; ListEntry* new_list = 0; hr = StringListToColumnList( &parent_ComponentData, wiz_info.strlistOptional, &new_list); BREAK_ON_FAILED_HRESULT(hr); new_object->mayContain = new_list; new_list = 0; hr = StringListToColumnList( &parent_ComponentData, wiz_info.strlistMandatory, &new_list); BREAK_ON_FAILED_HRESULT(hr); new_object->mustContain = new_list; // stuff the new cache entry into the cache hr = parent_ComponentData.g_SchemaCache.InsertSchemaObject(new_object); BREAK_ON_FAILED_HRESULT(hr); hr = parent_ComponentData.g_SchemaCache.InsertSortedSchemaObject(new_object); BREAK_ON_FAILED_HRESULT(hr); // insert the new cache object into the snapin ui parent_ComponentData.g_ClassCookieList.InsertSortedDisplay( &parent_ComponentData, new_object); } while (0); if (FAILED(hr)) { delete new_object; if (hr == ADS_EXTENDED_ERROR) { DoExtErrMsgBox(); } else { CString title; title.LoadString(AFX_IDS_APP_TITLE); CString error_text; CString name; HRESULT last_ads_hr = GetLastADsError(hr, error_text, name); if (HRESULT_CODE(last_ads_hr) == ERROR_DS_INVALID_LDAP_DISPLAY_NAME) { error_text.LoadString(IDS_LDAPDISPLAYNAME_FORMAT_ERROR); } else { error_text = GetErrorMessage(hr,TRUE); } ::MessageBox( m_hWnd, error_text, title, MB_OK | MB_ICONSTOP); } return FALSE; } // end the wizard // @@ call base::OnWizardFinish()? return TRUE; } void NewClassAttributesPage::OnButtonOptionalAdd() { listbox_optional.AddNewObjectToList(); } void NewClassAttributesPage::OnButtonMandatoryAdd() { listbox_mandatory.AddNewObjectToList(); } void NewClassAttributesPage::OnButtonOptionalRemove() { listbox_optional.RemoveListBoxItem(); } void NewClassAttributesPage::OnButtonMandatoryRemove() { listbox_mandatory.RemoveListBoxItem(); } void NewClassAttributesPage::OnMandatorySelChange() { listbox_mandatory.OnSelChange(); } void NewClassAttributesPage::OnOptionalSelChange() { listbox_optional.OnSelChange(); } void NewClassAttributesPage::DoDataExchange(CDataExchange *pDX) { CPropertyPage::DoDataExchange(pDX); DDX_Control(pDX, IDC_MANDATORY_LIST, listbox_mandatory); DDX_Control(pDX, IDC_OPTIONAL_LIST, listbox_optional); }