/*========================================================================== * * Copyright (C) 2000-2002 Microsoft Corporation. All Rights Reserved. * * File: ClassFac.cpp * Content: Parsing engine *@@BEGIN_MSINTERNAL * History: * Date By Reason * ==== == ====== * 02/04/2000 rmt Created * 02/17/2000 rmt Parameter validation work * 02/21/2000 rmt Updated to make core Unicode and remove ANSI calls * 03/21/2000 rmt Renamed all DirectPlayAddress8's to DirectPlay8Addresses * 07/21/2000 rmt Bug #39940 - Addressing library doesn't properly parse stopbits in URLs *@@END_MSINTERNAL * ***************************************************************************/ #include "dnaddri.h" #undef DPF_MODNAME #define DPF_MODNAME "DP8ADDRESSPARSE::DP8ADDRESSPARSE" DP8ADDRESSPARSE::DP8ADDRESSPARSE( ): m_pwszCurrentLocation(NULL), m_pwszCurrentKey(NULL), m_pwszCurrentValue(NULL), m_pbUserData(NULL), m_dwUserDataSize(0), m_dp8State(DP8AP_IDLE), m_dwLenURL(0) { } #undef DPF_MODNAME #define DPF_MODNAME "DP8ADDRESSPARSE::~DP8ADDRESSPARSE" DP8ADDRESSPARSE::~DP8ADDRESSPARSE() { if( m_pwszCurrentKey != NULL ) { DNFree(m_pwszCurrentKey); } if( m_pwszCurrentValue != NULL ) { DNFree(m_pwszCurrentValue); } if ( m_pbUserData != NULL ) { DNFree(m_pbUserData); } } #undef DPF_MODNAME #define DPF_MODNAME "DP8ADDRESSPARSE::ParseURL" HRESULT DP8ADDRESSPARSE::ParseURL( DP8ADDRESSOBJECT *dp8aObject, WCHAR *pstrURL ) { HRESULT hr; if( m_pwszCurrentKey != NULL ) { DNFree(m_pwszCurrentKey); m_pwszCurrentKey = NULL; } if( m_pwszCurrentValue != NULL ) { DNFree(m_pwszCurrentValue); m_pwszCurrentValue = NULL; } if( m_pbUserData != NULL ) { DNFree(m_pbUserData); m_pbUserData = NULL; } m_dwUserDataSize = 0; m_pwszCurrentLocation = pstrURL; m_dwLenURL = wcslen(pstrURL); if( m_dwLenURL < wcslen( DPNA_HEADER ) ) { DPFX(DPFPREP, 0, "Invalid URL" ); return DPNERR_INVALIDURL; } if( wcsncmp( pstrURL, DPNA_HEADER, wcslen(DPNA_HEADER) ) != 0 ) { DPFX(DPFPREP, 0, "No header, invalid URL" ); return DPNERR_INVALIDURL; } m_pwszCurrentLocation += wcslen( DPNA_HEADER ); m_pwszCurrentKey = (WCHAR*) DNMalloc((m_dwLenURL+1)*sizeof(WCHAR)); if( !m_pwszCurrentKey ) { DPFX(DPFPREP, 0, "Error allocating memory" ); return DPNERR_OUTOFMEMORY; } m_pwszCurrentValue = (WCHAR*) DNMalloc((m_dwLenURL+1)*sizeof(WCHAR)); if( !m_pwszCurrentValue ) { DNFree(m_pwszCurrentKey); m_pwszCurrentKey = NULL; DPFX(DPFPREP, 0, "Error allocating memory" ); return DPNERR_OUTOFMEMORY; } m_pbUserData = (BYTE*) DNMalloc(m_dwLenURL+1); if( !m_pbUserData ) { DNFree(m_pwszCurrentKey); DNFree(m_pwszCurrentValue); m_pwszCurrentKey = NULL; m_pwszCurrentValue = NULL; DPFX(DPFPREP, 0, "Error allocating memory" ); return DPNERR_OUTOFMEMORY; } m_dp8State = DP8AP_IDLE; // Loop until the string is done while( *m_pwszCurrentLocation != L'\0' ) { switch( m_dp8State ) { case DP8AP_IDLE: if( *m_pwszCurrentLocation == DPNA_SEPARATOR_USERDATA ) { m_dp8State = DP8AP_USERDATA; m_pwszCurrentLocation++; } else { m_dp8State = DP8AP_KEY; } break; case DP8AP_KEY: if( *m_pwszCurrentLocation == DPNA_SEPARATOR_USERDATA ) { m_dp8State = DP8AP_USERDATA; m_pwszCurrentLocation++; break; } hr = FSM_Key(); if( FAILED( hr ) ) { DPFX(DPFPREP, 0, "Error parsing key hr = 0x%x", hr ); return hr; } // Parse ended with an equals if( *m_pwszCurrentLocation == DPNA_SEPARATOR_KEYVALUE ) { m_dp8State = DP8AP_VALUE; m_pwszCurrentLocation++; } else { DPFX(DPFPREP, 0, "keyname without associated value hr=0x%x", hr ); return DPNERR_INVALIDURL; } break; case DP8AP_VALUE: hr = FSM_Value(); if( FAILED( hr ) ) { DPFX(DPFPREP, 0, "Error parsing value hr=0x%x", hr ); return hr; } // Parse ended with an equals if( *m_pwszCurrentLocation == DPNA_SEPARATOR_COMPONENT ) { m_dp8State = DP8AP_KEY; m_pwszCurrentLocation++; } else if( *m_pwszCurrentLocation == DPNA_SEPARATOR_USERDATA ) { m_dp8State = DP8AP_USERDATA; m_pwszCurrentLocation++; } else if( *m_pwszCurrentLocation == L'\0' ) { m_dp8State = DP8AP_IDLE; } else { DPFX(DPFPREP, 0, "Error parsing next key" ); hr = DPNERR_INVALIDURL; return hr; } hr = FSM_CommitEntry(dp8aObject); if( hr == DPNERR_INVALIDPARAM ) { DPFX(DPFPREP, 0, "Invalid value specified in URL hr=0x%x", hr ); hr = DPNERR_INVALIDURL; return hr; } else if( FAILED( hr ) ) { DPFX(DPFPREP, 0, "Error commiting entry hr=0x%x", hr ); return hr; } break; case DP8AP_USERDATA: hr = FSM_UserData(); if( FAILED( hr ) ) { DPFX(DPFPREP, 0, "Error parsing user data hr=0x%x", hr ); return hr; } hr = dp8aObject->SetUserData( m_pbUserData, m_dwUserDataSize ); if( FAILED( hr ) ) { DPFX(DPFPREP, 0, "Error setting user data hr=0x%x", hr ); return hr; } break; } } if( m_dp8State != DP8AP_IDLE && m_dp8State != DP8AP_USERDATA ) { DPFX(DPFPREP, 0, "Parsing error hr=0x%x", hr ); hr = DPNERR_INVALIDURL; return hr; } return DPN_OK; } #undef DPF_MODNAME #define DPF_MODNAME "DP8ADDRESSPARSE::IsValidKeyChar" BOOL DP8ADDRESSPARSE::IsValidKeyChar( WCHAR ch ) { if( ch >= L'A' && ch <= L'Z' ) return TRUE; if( ch >= L'a' && ch <= L'z' ) return TRUE; if( ch >= L'0' && ch <= L'9' ) return TRUE; if( ch == L'-' || ch == L'?' || ch == L'.' || ch == L',' || ch == L'+' || ch == L'_' ) return TRUE; return FALSE; } #undef DPF_MODNAME #define DPF_MODNAME "DP8ADDRESSPARSE::IsValidKeyTerminator" BOOL DP8ADDRESSPARSE::IsValidKeyTerminator( WCHAR ch ) { if( ch == 0 ) return TRUE; if( ch == DPNA_SEPARATOR_USERDATA ) return TRUE; if( ch == DPNA_SEPARATOR_COMPONENT ) return TRUE; if( ch == DPNA_SEPARATOR_KEYVALUE ) return TRUE; return FALSE; } #undef DPF_MODNAME #define DPF_MODNAME "DP8ADDRESSPARSE::FSM_Key" // FSM_Key // // Parse a keyname, or return an error on error // HRESULT DP8ADDRESSPARSE::FSM_Key() { DWORD dwKeyLoc = 0; m_pwszCurrentKey[0] = 0; HRESULT hr = DPN_OK; while( 1 ) { if( IsValidKeyChar(*m_pwszCurrentLocation) ) { m_pwszCurrentKey[dwKeyLoc] = *m_pwszCurrentLocation; } else if( IsValidKeyTerminator(*m_pwszCurrentLocation) ) { m_pwszCurrentKey[dwKeyLoc] = 0; break; } else { m_pwszCurrentKey[dwKeyLoc] = 0; hr = DPNERR_INVALIDURL; break; } dwKeyLoc++; m_pwszCurrentLocation++; } return hr; } #undef DPF_MODNAME #define DPF_MODNAME "DP8ADDRESSPARSE::IsValidNumber" BOOL DP8ADDRESSPARSE::IsValidNumber( WCHAR ch ) { if( ch < L'0' || ch > L'9' ) { return FALSE; } else { return TRUE; } } #undef DPF_MODNAME #define DPF_MODNAME "DP8ADDRESSPARSE::IsValidHex" BOOL DP8ADDRESSPARSE::IsValidHex( WCHAR ch ) { if( IsValidNumber( ch ) ) return TRUE; if( ch >= L'A' || ch <= L'F' ) return TRUE; if( ch >= L'a' || ch <= L'f' ) return TRUE; return FALSE; } #undef DPF_MODNAME #define DPF_MODNAME "DP8ADDRESSPARSE::HexToChar" WCHAR DP8ADDRESSPARSE::HexToChar( const WCHAR *sz ) { WCHAR chResult = sz[0]; // First digit if( sz[0] >= L'0' && sz[0] <= L'9' ) chResult = sz[0]-L'0'; if( sz[0] >= L'A' && sz[0] <= L'F' ) chResult = sz[0]-L'A'+10; if( sz[0] >= L'a' && sz[0] <= L'f' ) chResult = sz[0]-L'a'+10; chResult <<= 4; // Second digit if( sz[1] >= L'0' && sz[1] <= L'9' ) chResult += sz[1]-'0'; if( sz[1] >= L'A' && sz[1] <= L'F' ) chResult += sz[1]-L'A'+10; if( sz[1] >= L'a' && sz[1] <= L'f' ) chResult += sz[1]-L'a'+10; return chResult; } #undef DPF_MODNAME #define DPF_MODNAME "DP8ADDRESSPARSE::FSM_Value" HRESULT DP8ADDRESSPARSE::FSM_Value() { m_fNonNumeric = FALSE; m_pwszCurrentValue[0] = 0; HRESULT hr = DPN_OK; m_dwValueLen = 0; while( 1 ) { // Just add it if( IsValidKeyChar( *m_pwszCurrentLocation ) ) { m_pwszCurrentValue[m_dwValueLen] = *m_pwszCurrentLocation; if( !IsValidNumber( *m_pwszCurrentLocation ) ) { m_fNonNumeric = TRUE; } } // ESCAPE SEQUENCE else if( *m_pwszCurrentLocation == DPNA_ESCAPECHAR ) { m_fNonNumeric = TRUE; if( *(m_pwszCurrentLocation+1) == DPNA_ESCAPECHAR ) { m_pwszCurrentValue[m_dwValueLen] = DPNA_ESCAPECHAR; m_pwszCurrentLocation += 2; } if( wcslen( m_pwszCurrentLocation ) < 3 ) { DPFX(DPFPREP, 0, "Unexpected end in escape sequence" ); hr = DPNERR_INVALIDURL; break; } if( !IsValidHex( *(m_pwszCurrentLocation+1) ) || !IsValidHex( *(m_pwszCurrentLocation+2) ) ) { DPFX(DPFPREP, 0, "Invalid escape sequence" ); hr = DPNERR_INVALIDURL; break; } m_pwszCurrentLocation ++; m_pwszCurrentValue[m_dwValueLen] = HexToChar(m_pwszCurrentLocation); m_pwszCurrentLocation ++; } else if( IsValidKeyTerminator(*m_pwszCurrentLocation) ) { m_pwszCurrentValue[m_dwValueLen] = 0; break; } else { m_pwszCurrentValue[m_dwValueLen] = 0; DPFX(DPFPREP, 0, "Unexpected character in URL" ); hr = DPNERR_INVALIDURL; break; } m_dwValueLen++; m_pwszCurrentLocation++; } if( m_dwValueLen < 1 ) { DPFX(DPFPREP, DP8A_ERRORLEVEL, "0 length value" ); return DPNERR_INVALIDURL; } return hr; } #undef DPF_MODNAME #define DPF_MODNAME "DP8ADDRESSPARSE::FSM_UserData" HRESULT DP8ADDRESSPARSE::FSM_UserData() { m_pwszCurrentValue[0] = 0; HRESULT hr = DPN_OK; DWORD dwValueLoc = 0; while( 1 ) { // Just add it if( IsValidKeyChar( *m_pwszCurrentLocation ) ) { m_pbUserData[dwValueLoc] = (CHAR) *m_pwszCurrentLocation; } // ESCAPE SEQUENCE else if( *m_pwszCurrentLocation == DPNA_ESCAPECHAR ) { if( *(m_pwszCurrentLocation+1) == DPNA_ESCAPECHAR ) { m_pbUserData[dwValueLoc] = DPNA_ESCAPECHAR; m_pwszCurrentLocation += 2; } if( wcslen( m_pwszCurrentLocation ) < 3 ) { DPFX(DPFPREP, 0, "Unexpected end in escape sequence" ); hr = DPNERR_INVALIDURL; break; } if( !IsValidHex( *(m_pwszCurrentLocation+1) ) || !IsValidHex( *(m_pwszCurrentLocation+2) ) ) { DPFX(DPFPREP, 0, "Invalid escape sequence" ); hr = DPNERR_INVALIDURL; break; } m_pwszCurrentLocation ++; m_pbUserData[dwValueLoc] = (CHAR) HexToChar(m_pwszCurrentLocation); m_pwszCurrentLocation ++; } else if( IsValidKeyTerminator(*m_pwszCurrentLocation) ) { m_pwszCurrentValue[dwValueLoc] = 0; break; } else { m_pwszCurrentValue[dwValueLoc] = 0; hr = DPNERR_INVALIDURL; break; } dwValueLoc++; m_pwszCurrentLocation++; } m_dwUserDataSize = dwValueLoc; return hr; } #undef DPF_MODNAME #define DPF_MODNAME "DP8ADDRESSPARSE::FSM_CommitEntry" HRESULT DP8ADDRESSPARSE::FSM_CommitEntry(DP8ADDRESSOBJECT *pdp8aObject) { DWORD dwDataType = 0xFFFFFFFF; // Ensure that datatype is correct in case the key is a reserved key for( DWORD dwIndex = 0; dwIndex < c_dwNumBaseStrings; dwIndex++ ) { if( _wcsicmp( g_szBaseStrings[dwIndex], m_pwszCurrentKey ) == 0 ) { dwDataType = g_dwBaseRequiredTypes[dwIndex] ; break; } } // If it's numeric if( (dwDataType == DPNA_DATATYPE_DWORD || dwDataType == 0xFFFFFFFF) && !m_fNonNumeric && wcslen(m_pwszCurrentValue)<=10) { DWORD dwTmpValue; dwTmpValue = wcstoul( m_pwszCurrentValue, NULL, 10 ); return pdp8aObject->SetElement( m_pwszCurrentKey, &dwTmpValue, sizeof(DWORD), DPNA_DATATYPE_DWORD ); } // We've read a GUID if (dwDataType == DPNA_DATATYPE_GUID || dwDataType == 0xFFFFFFFF) { // L"{%8X-%4X-%4X-%2X%2X-%2X%2X%2X%2X%2X%2X}" typedef enum { OpenBrace, HexDWord1, HexWord1, HexWord2, DoubleHexBytes, HexBytes, CloseBrace, Invalid, } GUID_PARSE_STATE; GUID guidValue; GUID_PARSE_STATE GuidParseState; BOOL fStateChange; ULARGE_INTEGER uliValue; WCHAR * pwcStateStart; WCHAR * pwcCurrent; DWORD_PTR dwLength; GuidParseState = OpenBrace; fStateChange = FALSE; uliValue.QuadPart = 0; pwcCurrent = m_pwszCurrentValue; pwcStateStart = pwcCurrent; // // Loop until we run out of string. // while ((*pwcCurrent) != 0) { switch (*pwcCurrent) { case L'0': case L'1': case L'2': case L'3': case L'4': case L'5': case L'6': case L'7': case L'8': case L'9': { uliValue.QuadPart = uliValue.QuadPart * 16 + ((*pwcCurrent) - L'0'); break; } case L'a': case L'b': case L'c': case L'd': case L'e': case L'f': { uliValue.QuadPart = uliValue.QuadPart * 16 + ((*pwcCurrent) - L'a' + 10); break; } case L'A': case L'B': case L'C': case L'D': case L'E': case L'F': { uliValue.QuadPart = uliValue.QuadPart * 16 + ((*pwcCurrent) - L'A' + 10); break; } case L'{': case L'}': case L'-': { dwLength = ((DWORD_PTR) pwcCurrent) - ((DWORD_PTR) pwcStateStart); switch (GuidParseState) { case OpenBrace: { if (((*pwcCurrent) == L'{') && (dwLength == 0)) { fStateChange = TRUE; } else { GuidParseState = Invalid; } break; } case HexDWord1: { if (((*pwcCurrent) == L'-') && (dwLength <= (sizeof(DWORD) * 2 * sizeof(WCHAR)))) { guidValue.Data1 = uliValue.LowPart; fStateChange = TRUE; } else { GuidParseState = Invalid; } break; } case HexWord1: { if (((*pwcCurrent) == L'-') && (dwLength <= (sizeof(WORD) * 2 * sizeof(WCHAR)))) { guidValue.Data2 = (WORD) uliValue.LowPart; fStateChange = TRUE; } else { GuidParseState = Invalid; } break; } case HexWord2: { if (((*pwcCurrent) == L'-') && (dwLength <= (sizeof(WORD) * 2 * sizeof(WCHAR)))) { guidValue.Data3 = (WORD) uliValue.LowPart; fStateChange = TRUE; } else { GuidParseState = Invalid; } break; } case DoubleHexBytes: { if (((*pwcCurrent) == L'-') && (dwLength == (2 * 2 * sizeof(WCHAR)))) { guidValue.Data4[0] = (BYTE) ((uliValue.LowPart & 0x0000FF00) >> 8); guidValue.Data4[1] = (BYTE) (uliValue.LowPart & 0x000000FF); fStateChange = TRUE; } else { GuidParseState = Invalid; } break; } case HexBytes: { if (((*pwcCurrent) == L'}') && (dwLength == (6 * 2 * sizeof(WCHAR)))) { guidValue.Data4[2] = (BYTE) ((uliValue.HighPart & 0x0000FF00) >> 8); guidValue.Data4[3] = (BYTE) (uliValue.HighPart & 0x000000FF); guidValue.Data4[4] = (BYTE) ((uliValue.LowPart & 0xFF000000) >> 24); guidValue.Data4[5] = (BYTE) ((uliValue.LowPart & 0x00FF0000) >> 16); guidValue.Data4[6] = (BYTE) ((uliValue.LowPart & 0x0000FF00) >> 8); guidValue.Data4[7] = (BYTE) (uliValue.LowPart & 0x000000FF); fStateChange = TRUE; } else { GuidParseState = Invalid; } break; } default: { GuidParseState = Invalid; break; } } // end switch (on parse state) // // Move to next parse state (unless the string is bogus). // if (fStateChange) { fStateChange = FALSE; uliValue.QuadPart = 0; pwcStateStart = pwcCurrent + 1; (*((int*) (&GuidParseState)))++; } break; } default: { GuidParseState = Invalid; break; } } // end switch (on current character) if (GuidParseState == Invalid) { break; } pwcCurrent++; if ((pwcCurrent - m_pwszCurrentValue) > 38 * sizeof(WCHAR)) { break; } } // end while (not at end of string) // // If we hit the end of the string when parsing the last element, // we've succeeded. Commit the GUID. // if (GuidParseState == CloseBrace) { return pdp8aObject->SetElement( m_pwszCurrentKey, &guidValue, sizeof(GUID), DPNA_DATATYPE_GUID ); } } // If there are no NULLs it's probably a string if( ( (dwDataType == DPNA_DATATYPE_STRING) || (dwDataType == 0xFFFFFFFF) #ifndef DPNBUILD_ONLYONESP || (wcscmp(DPNA_KEY_PROVIDER,m_pwszCurrentKey)==0) #endif // ! DPNBUILD_ONLYONESP ) && wcslen( m_pwszCurrentValue ) == m_dwValueLen ) { // Otherwise it's a string return pdp8aObject->SetElement( m_pwszCurrentKey, m_pwszCurrentValue, (wcslen(m_pwszCurrentValue)+1)*sizeof(WCHAR), DPNA_DATATYPE_STRING ); } // Otherwise it's a binary (although the bytes were extended into WORDs/WCHARs. return pdp8aObject->SetElement( m_pwszCurrentKey, m_pwszCurrentValue, (m_dwValueLen * sizeof(WCHAR)), DPNA_DATATYPE_BINARY ); }