/*++ Copyright (c) 2001 Microsoft Corporation Module Name : metabase.cxx Abstract: Class that is used to modify the metabase Author: Christopher Achille (cachille) Project: Internet Services Setup Revision History: June 2001: Created --*/ #include "stdafx.h" #include "iadm.h" #include "iiscnfgp.h" #include "mdkey.h" #include "mdentry.h" #include "metabase.hxx" #include "iiscnfg.h" #include "strfn.h" // function: GetSizeBasedOnMetaType // // Returns the DataSize for an object based on its type // DWORD CMetaBase::GetSizeBasedOnMetaType(DWORD dwDataType,LPTSTR szString) { DWORD dwRet = 0; switch (dwDataType) { case DWORD_METADATA: dwRet = 4; break; case STRING_METADATA: case EXPANDSZ_METADATA: if (szString == NULL) { dwRet = 0; } else { dwRet = (_tcslen((LPTSTR)szString) + 1) * sizeof(TCHAR); } break; case MULTISZ_METADATA: if (szString == NULL) { dwRet = 0; } else { dwRet = GetMultiStrSize((LPTSTR)szString) * sizeof(TCHAR); } break; case BINARY_METADATA: break; } return dwRet; } // function: FindStringinMultiSz // // Finds a string inside of a MultiSz // // Parameters: // szMultiSz - The MultiSz to search // szSearchString - The String to find // // Return: // True - It was found // False - It was not found BOOL CMetaBase::FindStringinMultiSz(LPTSTR szMultiSz, LPTSTR szSearchString) { LPTSTR szSearchCurrent; do { szSearchCurrent = szSearchString; while ( ( *szMultiSz != '\0' ) && ( *szSearchCurrent != '\0' ) && ( *szMultiSz == *szSearchCurrent )) { // While the strings are the same, and they do not hit a null terminator szMultiSz++; szSearchCurrent++; } if ( ( *szMultiSz == '\0' ) && ( *szMultiSz == *szSearchCurrent ) ) { // The strings matched return TRUE; } while ( *szMultiSz != '\0' ) { // Go to the end of this string szMultiSz++; } // Step right past the string terminator szMultiSz++; } while (*szMultiSz); return FALSE; } // function: VerifyParameters // // Verify the parameters are correct // BOOL CMetaBase_SetValue::VerifyParameters(CItemList &ciParams) { if ( ( ( ciParams.GetNumberOfItems() == 7 ) || ( ( ciParams.GetNumberOfItems() == 8 ) && ciParams.IsNumber(7) ) ) && ciParams.IsNumber(1) && ciParams.IsNumber(2) && ciParams.IsNumber(3) && ciParams.IsNumber(4) && ciParams.IsNumber(5) ) { return TRUE; } return FALSE; } // funtion: DoInternalWork // // Set a value in the metabase // // Parameters: // ciList - Parameters for OpenNode, they are the following: // 0 -Location // 1- Id // 2- Inheritable // 3- UserType // 4- DataType // 5- Len // 6- Value // 7- bDontReplace - Should we replace the value if one already exists? (default==false) BOOL CMetaBase_SetValue::DoInternalWork(CItemList &ciParams) { CMDKey cmdKey; CMDValue cmdMetaValue; DWORD dwSize = ciParams.GetNumber(5); BOOL bRet = TRUE; if ( FAILED(cmdKey.OpenNode(ciParams.GetItem(0) ) ) ) { // Could not open the key, so we fail return FALSE; } if (dwSize == 0) { dwSize = GetSizeBasedOnMetaType(ciParams.GetNumber(4),ciParams.GetItem(6)); } if ( ( ciParams.GetNumberOfItems() == 8 ) && ( ciParams.GetNumber(7) == 1 ) ) { // Make sure the value does not exist yet, because we don't want to replace the old one bRet = !cmdKey.GetData( cmdMetaValue, ciParams.GetNumber(1) ); } if ( bRet ) { // Set the value for this node cmdMetaValue.SetValue(ciParams.GetNumber(1), ciParams.GetNumber(2), ciParams.GetNumber(3), ciParams.GetNumber(4), dwSize, (LPTSTR) ciParams.GetItem(6)); bRet = cmdKey.SetData(cmdMetaValue, ciParams.GetNumber(1)); } cmdKey.Close(); return bRet; } // function: GetMethodName // // Return the Method Name for this Class // LPTSTR CMetaBase_SetValue::GetMethodName() { return _T("Metabase_SetValue"); } // function: VerifyParameters // // Verify the parameters are correct // BOOL CMetaBase_DelIDOnEverySite::VerifyParameters(CItemList &ciParams) { if ( ( ciParams.GetNumberOfItems() == 2 ) && ciParams.IsNumber(0) && ciParams.IsNumber(1) ) { return TRUE; } return FALSE; } // funtion: DoInternalWork // // Set a value in the metabase // // Parameters: // ciList - Parameters for OpenNode, they are the following: // 0 - Id // 1 - Metabase Data Type BOOL CMetaBase_DelIDOnEverySite::DoInternalWork(CItemList &ciParams) { CMDKey cmdKey; DWORD dwId = ciParams.GetNumber(0); DWORD dwType = ciParams.GetNumber(1); BOOL bRet; CStringList cslpathList; POSITION pos; CString csPath; WCHAR wchKeyPath[_MAX_PATH]; LPWSTR szwKeyPath = wchKeyPath; CString csBasePath = _T("LM/W3SVC"); if ( FAILED( cmdKey.OpenNode( csBasePath ) ) ) { // Could not open the key, so we fail return FALSE; } // get datapaths on the specified ID. if (FAILED( cmdKey.GetDataPaths( dwId, dwType, cslpathList) )) { // Could not GetDataPaths for this value return FALSE; } // close it so that we can open the various // other nodes that returned and delete the values from them cmdKey.Close(); pos = cslpathList.GetHeadPosition(); while ( NULL != pos ) { bRet = TRUE; csPath = cslpathList.GetNext( pos ); //iisDebugOut((LOG_TYPE_TRACE,_T("%s"),csPath)); #if defined(UNICODE) || defined(_UNICODE) szwKeyPath = csPath.GetBuffer(0); #else if (MultiByteToWideChar(CP_ACP, 0, (LPCSTR)csPath.GetBuffer(0), -1, (LPWSTR)wchKeyPath, _MAX_PATH)==0) { // We could not convert the string to wide, so lets skip this one continue; } #endif // make a special case of the "/" path if ( wcscmp( szwKeyPath, L"/" ) != 0 ) { //iisDebugOut((LOG_TYPE_TRACE,_T("%s -- del"),csPath)); CString csNewPath; csNewPath = csBasePath + szwKeyPath; if ( SUCCEEDED(cmdKey.OpenNode(csNewPath) ) ) { if ( FAILED(cmdKey.DeleteData(dwId, dwType) ) ) { // i guess we don't need to report it.. } cmdKey.Close(); } } } return bRet; } // function: GetMethodName // // Return the Method Name for this Class // LPTSTR CMetaBase_DelIDOnEverySite::GetMethodName() { return _T("CMetaBase_DelIDOnEverySite"); } // function: VerifyParameters // // Verify the parameters are correct // BOOL CMetaBase_IsAnotherSiteonPort80::VerifyParameters(CItemList &ciParams) { return ( ciParams.GetNumberOfItems() == 0 ); } // function: FindSiteUsing80 // // Find a Site that is using port80 // // Parameters: // cmdKey - The key to the metabase node // dwId - The id of the item to find. // // Return // TRUE - It has been found // FALSE - It has not been found BOOL CMetaBase_IsAnotherSiteonPort80::SearchMultiSzforPort80(CMDKey &cmdKey, DWORD dwId) { CStringList cslpathList; POSITION pos; CString csPath; CMDValue cmdValue; WCHAR wchKeyPath[_MAX_PATH]; LPWSTR szwKeyPath = wchKeyPath; if (FAILED( cmdKey.GetDataPaths( dwId, MULTISZ_METADATA, cslpathList) )) { // Could not GetDataPaths for this value return FALSE; } pos = cslpathList.GetHeadPosition(); while ( NULL != pos ) { csPath = cslpathList.GetNext( pos ); #if defined(UNICODE) || defined(_UNICODE) szwKeyPath = csPath.GetBuffer(0); #else if (MultiByteToWideChar(CP_ACP, 0, (LPCSTR)csPath.GetBuffer(0), -1, (LPWSTR)wchKeyPath, _MAX_PATH)==0) { // We could not convert the string to wide, so lets skip this one continue; } #endif if ( ( wcscmp( szwKeyPath, L"/1/" ) != 0 ) && ( cmdKey.GetData( cmdValue, dwId, szwKeyPath ) ) && ( cmdValue.GetDataType() == MULTISZ_METADATA ) && ( FindStringinMultiSz( (LPTSTR) cmdValue.GetData(), _T(":80:") ) ) ) { if ( ( !cmdKey.GetData( cmdValue, MD_SERVER_AUTOSTART, szwKeyPath ) ) || ( !cmdValue.IsEqual( DWORD_METADATA, 4, (DWORD) 0 ) ) ) { // If GetData failed, or it succedded and the value is not 0, then we // have found a match. return TRUE; break; } } } return FALSE; } // function: IsAnotherSiteonPort80 // // Reports back whether another site besides /W3SVC/1 is configured to bind to port 80 // AND has AutoStart set to True // // Parameters // None // // Return // TRUE - Another site is running on port 80 // FALSE - No other site is running on port 80 BOOL CMetaBase_IsAnotherSiteonPort80::DoInternalWork(CItemList &ciList) { BOOL bRet = FALSE; CMDKey cmdKey; if ( FAILED( cmdKey.OpenNode( _T("LM/W3SVC") ) ) ) { // Could not open the w3svc node return FALSE; } if ( SearchMultiSzforPort80( cmdKey, MD_SERVER_BINDINGS) || SearchMultiSzforPort80( cmdKey, MD_SECURE_BINDINGS) ) { bRet = TRUE; } cmdKey.Close(); return bRet; } // function: GetMethodName // // Return the Method Name for this Class // LPTSTR CMetaBase_IsAnotherSiteonPort80::GetMethodName() { return _T("Metabase_IsAnotherSiteonPort80"); } // function: VerifyParameters // // Verify the parameters are correct // BOOL CMetaBase_VerifyValue::VerifyParameters(CItemList &ciParams) { if ( ( ciParams.GetNumberOfItems() == 5 ) && ciParams.IsNumber(1) && ciParams.IsNumber(2) ) { return TRUE; } return FALSE; } // function: GetMethodName // // Return the Method Name for this Class // LPTSTR CMetaBase_VerifyValue::GetMethodName() { return _T("Metabase_VerifyValue"); } // function: VerifyValue::DoWork // // Verify that the value in the particular metabase location, matches the // value in param[4]. If it does, return true. // // Parameters // 0 - Metabase Location // 1 - Metabase Id // 2 - Metabase Data Type // 3 - Data Length = 0 (default value is 0 (must calculate)) // 4 - Metabase Value // // Return // TRUE - The values match // FALSE - The values do not match BOOL CMetaBase_VerifyValue::DoInternalWork(CItemList &ciList) { CMDKey cmdKey; CMDValue cmdValue; DWORD dwSize = ciList.GetNumber(3); BOOL bRet = FALSE; if ( FAILED( cmdKey.OpenNode( ciList.GetItem(0) ) ) ) { // Could not open the w3svc node return FALSE; } if (dwSize == 0) { dwSize = GetSizeBasedOnMetaType(ciList.GetNumber(2),ciList.GetItem(4)); } if ( cmdKey.GetData( cmdValue, ciList.GetNumber(1) ) ) { if ( ( ( ciList.GetNumber(2) == DWORD_METADATA ) && ( cmdValue.IsEqual( ciList.GetNumber(2), dwSize, (DWORD) ciList.GetNumber(4) ) ) ) || ( ( ciList.GetNumber(2) != DWORD_METADATA ) && ( cmdValue.IsEqual( ciList.GetNumber(2), dwSize, (LPVOID) ciList.GetItem(4) ) ) ) ) { bRet = TRUE; } } cmdKey.Close(); return bRet; } // function: GetMethodName // // Return the Method Name for this Class // LPTSTR CMetaBase_ImportRestrictionList::GetMethodName() { return _T("Metabase_ImportRestrictionList"); } // function: VerifyParameters // // Verify the parameters are correct // BOOL CMetaBase_ImportRestrictionList::VerifyParameters(CItemList &ciParams) { return ( (ciParams.GetNumberOfItems() == 3) && ( ciParams.IsNumber(1) ) ); } // function: CreateMultiSzFromList // // Create a MultiSz from a List of comman delim items. So, take // ,,,... (ie. "0","c:\inetpub\scripts\mycgi.exe","c:\inetpub\scripts\pagecount.exe") // and convert to // <0/1>\0\0\0\0 (ie. 0\0c:\inetpub\scripts\mycgi.exe\0c:\inetpub\scripts\pagecount.exe\0\0) // // Parameters: // pBuff [out] - BUFFER class that is used to store the multisz // pdwRetSize [out] - The size of the multisz that was created (in Bytes, not chars) // szItems [in] - List of Items to put in MultiSz // cDelimeter [in[ - The delimeter for the list // // Return Values // TRUE - Successfull // FALSE - It failed to construct MultiSz BOOL CMetaBase_ImportRestrictionList::CreateMultiSzFromList(BUFFER *pBuff, DWORD *pdwRetSize, LPTSTR szItems, TCHAR cDelimeter) { LPTSTR szSource; LPTSTR szDest; BOOL bInQuotes = FALSE; // Fail if we don't have a string, or can not get a big enought buffer if ( ( !szItems ) || ( !pBuff->Resize( ( _tcslen( szItems ) + 2 ) * sizeof(TCHAR) ) ) // Add 2, one for string term, and one for multisz term ) { return FALSE; } szSource = szItems; szDest = (LPTSTR) pBuff->QueryPtr(); while ( *szSource ) { if ( *szSource == '"' ) { bInQuotes = !bInQuotes; } else if ( ( *szSource == cDelimeter ) && !bInQuotes ) { if ( ( szDest != pBuff->QueryPtr() ) && ( *(szDest - 1) != '\0') ) { *szDest = '\0'; szDest++; } } else { *szDest = *szSource; szDest++; } szSource++; } // Lets make sure that the last string was terminated. if ( ( szDest != pBuff->QueryPtr() ) && ( *(szDest - 1) != '\0') ) { // Terminate last string *szDest = '\0'; szDest++; } // Terminate MultiSz, and calc length *szDest++ = '\0'; *pdwRetSize = (DWORD) (DWORD_PTR) (((LPBYTE) szDest) - ((LPBYTE) pBuff->QueryPtr())); return TRUE; } BOOL CMetaBase_ImportRestrictionList::ExpandEnvVar(BUFFER *pBuff) { BUFFER buffOriginal; DWORD dwLength; if ( _tcsstr( (LPTSTR) pBuff->QueryPtr(), _T("%") ) == NULL ) { // There are no environment variables, so we can return return TRUE; } if ( !buffOriginal.Resize( pBuff->QuerySize() ) ) { // Could not resize oringal big enough return FALSE; } memcpy(buffOriginal.QueryPtr(), pBuff->QueryPtr(), pBuff->QuerySize()); dwLength = ExpandEnvironmentStrings( (LPTSTR) buffOriginal.QueryPtr(), NULL, 0); if ( ( dwLength == 0 ) || ( !pBuff->Resize( dwLength * sizeof(TCHAR) ) ) ) { // Something failed in ExpandEnvironmentStrings return FALSE; } dwLength = ExpandEnvironmentStrings( (LPTSTR) buffOriginal.QueryPtr(), (LPTSTR) pBuff->QueryPtr() , pBuff->QuerySize() ); if ( (dwLength != 0) && (dwLength <= pBuff->QuerySize() ) ) { // It worked return TRUE; } return FALSE; } // function: DoInternalWork // // Import the RestrictionList // // Parameters: // 0 - The name of the item in the inf // 1 - The id // 2 - The Default value (MultiSz with ';' as seperator) // BOOL CMetaBase_ImportRestrictionList::DoInternalWork(CItemList &ciParams) { CMDKey cmdKey; CMDValue cmdMetaValue; BUFFER buffImportList; DWORD dwImportList; LPTSTR szList = NULL; INFCONTEXT Context; BOOL bRet = FALSE; BOOL bForceSetting = FALSE; if (g_pTheApp->m_fUnattended) { // If unattended, then look in unattend file for correct line if ( SetupFindFirstLine_Wrapped(g_pTheApp->m_hUnattendFile, UNATTEND_FILE_SECTION, ciParams.GetItem(0), &Context) ) { DWORD dwRequiredSize = 0; BUFFER buffUnattend; BUFFER buffUnattendExpanded; if ( SetupGetLineText( &Context, INVALID_HANDLE_VALUE, NULL, NULL, NULL, 0, &dwRequiredSize) ) { if ( ( buffUnattend.Resize( ( dwRequiredSize + 1 ) * sizeof(TCHAR) ) ) && ( SetupGetLineText( &Context, INVALID_HANDLE_VALUE, NULL, NULL, (LPTSTR) buffUnattend.QueryPtr(), buffUnattend.QuerySize()/sizeof(TCHAR), NULL) ) && ( ExpandEnvVar( &buffUnattend ) ) && ( CreateMultiSzFromList( &buffImportList, &dwImportList, (LPTSTR) buffUnattend.QueryPtr(), RESTRICTIONLIST_DELIMITER ) ) ) { szList = (LPTSTR) buffImportList.QueryPtr(); bForceSetting = TRUE; } } } } if ( !szList ) { // If it could not be retrieved, set it to the default. if ( ( buffImportList.Resize(( _tcslen( ciParams.GetItem(2) ) + 2 ) * sizeof(TCHAR) ) ) && ( CreateMultiSzFromList( &buffImportList, &dwImportList, ciParams.GetItem(2), RESTRICTIONLIST_DELIMITER) ) ) { szList = (LPTSTR) buffImportList.QueryPtr(); } } if ( szList == NULL ) { return FALSE; } // Set Value if ( FAILED(cmdKey.OpenNode( _T("LM/W3SVC") ) ) ) { // Could not open the key, so we fail return FALSE; } if ( bForceSetting || !cmdKey.GetData(cmdMetaValue, ciParams.GetNumber(1) ) ) { // Set the value for this node if ( cmdMetaValue.SetValue( ciParams.GetNumber(1), // ID 0x0, // Attributes (Not inheritable) IIS_MD_UT_SERVER, // UType MULTISZ_METADATA, // DType dwImportList, // Size (LPTSTR) szList) ) { bRet = cmdKey.SetData(cmdMetaValue, ciParams.GetNumber(1)); } } cmdKey.Close(); return bRet; } // function: GetMethodName // // Return the Method Name for this Class // LPTSTR CMetaBase_UpdateCustomDescList::GetMethodName() { return _T("Metabase_UpdateCustomDescList"); } // function: VerifyParameters // // Verify the parameters are correct // BOOL CMetaBase_UpdateCustomDescList::VerifyParameters(CItemList &ciParams) { return ( (ciParams.GetNumberOfItems() == 3) && ( ciParams.IsNumber(1) ) ); } // function: CreateMultiSzFromList // // Create a MultiSz from a List of comman delim items. So, take // ,,,... (ie. "1","asp scripts","c:\winnt\system32\inetsrv\asp.dll") // and convert to // <0/1>\0\0\0\0 (ie. 1\0asp scripts\0c:\winnt\system32\inetsrv\asp.dll\0\0) // // Parameters: // pBuff [out] - BUFFER class that is used to store the multisz // pdwRetSize [out] - The size of the multisz that was created (in Bytes, not chars) // szItems [in] - List of Items to put in MultiSz // cDelimeter [in[ - The delimeter for the list // // Return Values // TRUE - Successfull // FALSE - It failed to construct MultiSz BOOL CMetaBase_UpdateCustomDescList::CreateMultiSzFromList(BUFFER *pBuff, DWORD *pdwRetSize, LPTSTR szItems, TCHAR cDelimeter) { LPTSTR szSource; LPTSTR szDest; BOOL bInQuotes = FALSE; // Fail if we don't have a string, or can not get a big enought buffer if ( ( !szItems ) || ( !pBuff->Resize( ( _tcslen( szItems ) + 2 ) * sizeof(TCHAR) ) ) // Add 2, one for string term, and one for multisz term ) { return FALSE; } szSource = szItems; szDest = (LPTSTR) pBuff->QueryPtr(); while ( *szSource ) { if ( *szSource == '"' ) { bInQuotes = !bInQuotes; } else if ( ( *szSource == cDelimeter ) && !bInQuotes ) { if ( ( szDest != pBuff->QueryPtr() ) && ( *(szDest - 1) != '\0') ) { *szDest = '\0'; szDest++; } } else { *szDest = *szSource; szDest++; } szSource++; } // Lets make sure that the last string was terminated. if ( ( szDest != pBuff->QueryPtr() ) && ( *(szDest - 1) != '\0') ) { // Terminate last string *szDest = '\0'; szDest++; } // Terminate MultiSz, and calc length *szDest++ = '\0'; *pdwRetSize = (DWORD) (DWORD_PTR) (((LPBYTE) szDest) - ((LPBYTE) pBuff->QueryPtr())); return TRUE; } BOOL CMetaBase_UpdateCustomDescList::ExpandEnvVar(BUFFER *pBuff) { BUFFER buffOriginal; DWORD dwLength; if ( _tcsstr( (LPTSTR) pBuff->QueryPtr(), _T("%") ) == NULL ) { // There are no environment variables, so we can return return TRUE; } if ( !buffOriginal.Resize( pBuff->QuerySize() ) ) { // Could not resize oringal big enough return FALSE; } memcpy(buffOriginal.QueryPtr(), pBuff->QueryPtr(), pBuff->QuerySize()); dwLength = ExpandEnvironmentStrings( (LPTSTR) buffOriginal.QueryPtr(), NULL, 0); if ( ( dwLength == 0 ) || ( !pBuff->Resize( dwLength * sizeof(TCHAR) ) ) ) { // Something failed in ExpandEnvironmentStrings return FALSE; } dwLength = ExpandEnvironmentStrings( (LPTSTR) buffOriginal.QueryPtr(), (LPTSTR) pBuff->QueryPtr() , pBuff->QuerySize()/sizeof(TCHAR) ); if ( (dwLength != 0) && (dwLength <= ( pBuff->QuerySize()/sizeof(TCHAR) ) ) ) { // It worked return TRUE; } return FALSE; } // function: DoInternalWork // // Import the RestrictionList // // Parameters: // 0 - The name of the item in the inf // 1 - The id // 2 - The Default value (MultiSz with ';' as seperator) // BOOL CMetaBase_UpdateCustomDescList::DoInternalWork(CItemList &ciParams) { CMDKey cmdKey; CMDValue cmdMetaValue; BUFFER buffMyList; DWORD dwMyList; LPTSTR szList = NULL; INFCONTEXT Context; BOOL bRet = FALSE; BOOL bForceSetting = FALSE; if (g_pTheApp->m_fUnattended) { // If unattended, then look in unattend file for correct line if ( SetupFindFirstLine_Wrapped(g_pTheApp->m_hUnattendFile, UNATTEND_FILE_SECTION, ciParams.GetItem(0), &Context) ) { DWORD dwRequiredSize = 0; BUFFER buffUnattend; BUFFER buffUnattendExpanded; if ( SetupGetLineText( &Context, INVALID_HANDLE_VALUE, NULL, NULL, NULL, 0, &dwRequiredSize) ) { if ( ( buffUnattend.Resize( ( dwRequiredSize + 1 ) * sizeof(TCHAR) ) ) && ( SetupGetLineText( &Context, INVALID_HANDLE_VALUE, NULL, NULL, (LPTSTR) buffUnattend.QueryPtr(), buffUnattend.QuerySize()/sizeof(TCHAR), NULL) ) && ( ExpandEnvVar( &buffUnattend ) ) && ( CreateMultiSzFromList( &buffMyList, &dwMyList, (LPTSTR) buffUnattend.QueryPtr(), CUSTOMDESCLIST_DELIMITER ) ) ) { szList = (LPTSTR) buffMyList.QueryPtr(); bForceSetting = TRUE; } } } } if ( !szList ) { // If it could not be retrieved, set it to the default. if ( ( buffMyList.Resize(( _tcslen( ciParams.GetItem(2) ) + 2 ) * sizeof(TCHAR) ) ) && ( CreateMultiSzFromList( &buffMyList, &dwMyList, ciParams.GetItem(2), CUSTOMDESCLIST_DELIMITER) ) ) { szList = (LPTSTR) buffMyList.QueryPtr(); } } if ( szList == NULL ) { return FALSE; } // Set Value if ( FAILED(cmdKey.OpenNode( _T("LM/W3SVC") ) ) ) { // Could not open the key, so we fail return FALSE; } // See if there is an existing value... // if there is then update the value with this one... // otherwise set it. CStringList cslMyNewAdditions; if (ERROR_SUCCESS != ConvertDoubleNullListToStringList(szList,cslMyNewAdditions,-1)) { bForceSetting = TRUE; } // get the data DWORD dwAttributes = 0; DWORD dwMDDataType = MULTISZ_METADATA; CStringList cslMyOriginalList; if (bForceSetting || FAILED(cmdKey.GetMultiSzAsStringList(ciParams.GetNumber(1),&dwMDDataType,&dwAttributes,cslMyOriginalList))) { // do a fresh entry... // Set the value for this node if ( cmdMetaValue.SetValue(ciParams.GetNumber(1), // ID 0x0, // Attributes (Not inheritable) IIS_MD_UT_SERVER, // UType MULTISZ_METADATA, // DType dwMyList, // Size (LPTSTR) szList) ) { bRet = cmdKey.SetData(cmdMetaValue, ciParams.GetNumber(1)); } } else { // able to get it...so let's update it. // loop thru it and see if our entry is already in it... CString csNewEntryPart1; CString csNewEntryPart2; CString csNewEntryPart3; CString csExistingFilePath; BOOL bItsInTheListAlready = FALSE; BOOL bSomethingChanged = FALSE; POSITION pos = cslMyNewAdditions.GetHeadPosition(); POSITION pos2 = NULL; while (pos) { bItsInTheListAlready = FALSE; // get the next value csNewEntryPart1 = cslMyNewAdditions.GetNext(pos); if (!pos){break;} csNewEntryPart2 = cslMyNewAdditions.GetNext(pos); if (!pos){break;} csNewEntryPart3 = cslMyNewAdditions.GetNext(pos); // see if this value is in our list... pos2 = cslMyOriginalList.GetHeadPosition(); while (pos2) { // get the next value cslMyOriginalList.GetNext(pos2); if (!pos2){break;} csExistingFilePath = cslMyOriginalList.GetNext(pos2); if (!pos2){break;} // compare with other value //iisDebugOut((LOG_TYPE_TRACE,_T("UpdateCustomDescList:%s,%s"),csNewEntryPart2,csExistingFilePath)); if (0 == _tcsicmp(csNewEntryPart2,csExistingFilePath)) { // it's found in there! bItsInTheListAlready = TRUE; break; } cslMyOriginalList.GetNext(pos2); } if (FALSE == bItsInTheListAlready) { // Add it to the end of the list cslMyOriginalList.AddTail(csNewEntryPart1); cslMyOriginalList.AddTail(csNewEntryPart2); cslMyOriginalList.AddTail(csNewEntryPart3); bSomethingChanged = TRUE; } } if (TRUE == bSomethingChanged) { bRet = FALSE; if (ERROR_SUCCESS == cmdKey.SetMultiSzAsStringList(ciParams.GetNumber(1),MULTISZ_METADATA,0x0,cslMyOriginalList)) { bRet = TRUE; } } } cmdKey.Close(); return bRet; }