/*

Copyright (c) 1997-1999  Microsoft Corporation

Module Name:
    sdpbstrl.cpp

Abstract:


Author:

*/

#include "sdppch.h"

#include "sdpbstrl.h"

#include <basetyps.h>
#include <oleauto.h>
#include <winsock2.h>
#include <wchar.h>

SDP_ARRAY_BSTR::~SDP_ARRAY_BSTR(
    )
{
    int Size = (int)GetSize();

    if ( 0 < Size )
    {
        for ( int i=0; i < Size; i++ )
        {
            BSTR Member = GetAt(i);

            ASSERT(NULL != Member);
            if ( NULL == Member )
            {
                return;
            }

            SysFreeString(Member);
        }
    }

    RemoveAll();
}


void				
SDP_BSTRING::Reset(
	)
{
	// perform the destructor actions (freeing ptrs) and the constructor actions (initializing
	// member variables to starting values)

	// if there is a bstr, free it
    if ( NULL != m_Bstr )
    {
        SysFreeString(m_Bstr);
    }

	m_Bstr = NULL;
	m_CharacterSet = CS_UTF8;
	m_CodePage = CP_UTF8;

	// call the base class Reset
	SDP_CHAR_STRING::Reset();
}


BOOL
SDP_BSTRING::ConvertToBstr(
    )
{
    // ZoltanS bugfix:
    // MutliByteToWideChar always fails if its input string is empty.
    // Therefore, we must special-case a zero-length string.

    DWORD dwOriginalLength = GetLength();

    if ( 0 == dwOriginalLength )
    {
        // Shrink the member BSTR
        if ( !SysReAllocStringLen(&m_Bstr, NULL, dwOriginalLength) )
        {
            return FALSE;
        }

        // Make sure the member BSTR is emptied
        m_Bstr[0] = L'\0';

    }
    else // we have a nonzero-length string to convert
    {
        // get the size of bstr needed to store the unicode representation
        // cast the const char * returned from GetCharacterString to CHAR * because MultiByteToWideChar
        // doesn't accept const char * (although thats what the parameter should be)
        int BstrSize = MultiByteToWideChar(m_CodePage,  0,  (CHAR *)GetCharacterString(),
                                           dwOriginalLength, NULL,   0
                                          );

        // Check if the token can be converted to an appropriate bstr.
        if (0 == BstrSize)
        {
            return FALSE;
        }

        // re-allocate bstr for the unicode representation
        if ( !SysReAllocStringLen(&m_Bstr, NULL, BstrSize) )
        {
            return FALSE;
        }

        // convert character string to bstr
        // cast the const char * returned from GetCharacterString to CHAR * because MultiByteToWideChar
        // doesn't accept const char * (although thats what the parameter should be)
        if ( BstrSize != MultiByteToWideChar(
                    m_CodePage, 0, (CHAR *)GetCharacterString(),
                    dwOriginalLength, m_Bstr, BstrSize
                    )   )
        {
            return FALSE;
        }
    }

    return TRUE;
}



HRESULT
SDP_BSTRING::GetBstr(
    IN BSTR *pBstr
    )
{
    // ZoltanS
    ASSERT( ! IsBadWritePtr(pBstr, sizeof(BSTR)) );

    if ( !IsValid() )
    {
        return HRESULT_FROM_ERROR_CODE(ERROR_INVALID_DATA);
    }

    *pBstr = m_Bstr;
    return S_OK;
}



HRESULT				
SDP_BSTRING::GetBstrCopy(
	IN BSTR * pBstr
	)
{
    // ZoltanS
	if ( IsBadWritePtr(pBstr, sizeof(BSTR)) )
	{
		return E_POINTER;
	}

	BSTR LocalPtr = NULL;
	HRESULT HResult = GetBstr(&LocalPtr);
	if ( FAILED(HResult) )
	{
		return HResult;
	}

    if ( (*pBstr = SysAllocString(LocalPtr)) == NULL )
    {
	    return E_OUTOFMEMORY;
	}

	return S_OK;
}



HRESULT
SDP_BSTRING::SetBstr(
    IN BSTR Bstr
    )
{
    if ( NULL == Bstr )
    {
        return E_INVALIDARG;
    }

    DWORD   BstrLen =  lstrlenW(Bstr);
    BOOL    DefaultUsed = FALSE;

    // determine length of character string buffer
    // If the codepage is UTF8 the last argument should be NULL
    // if the caracterset is ASCII then we need to determine if the
    // WideCharToMultiByte methods nneds replacment characters

    int BufferSize = WideCharToMultiByte(
                            m_CodePage, 0,  Bstr,  BstrLen+1,
                            NULL,   0,  NULL,
                            (m_CharacterSet == CS_ASCII ) ? &DefaultUsed : NULL
                            );

    if ( 0 == BufferSize )
    {
        return HRESULT_FROM_ERROR_CODE(GetLastError());
    }

    if ( DefaultUsed )
    {
        return HRESULT_FROM_ERROR_CODE(SDP_INVALID_VALUE);
    }

    // now conversion cannot fail because the previous call made sure that
    // the bstr can be converted to this multibyte string
    // since failure is not possible, we do not need any code to restore
    // the previous character string and it may be freed
    if ( !ReAllocCharacterString(BufferSize) )
    {
        return HRESULT_FROM_ERROR_CODE(GetLastError());
    }

    // since the char string has been reallocated, the modifiable string must exist
    // (i.e. the char string should not be by reference at this point)
    ASSERT(NULL != GetModifiableCharString());

    // convert to multibyte string
    if ( BufferSize != WideCharToMultiByte(
                            m_CodePage, 0, Bstr, BstrLen+1,
                            GetModifiableCharString(), BufferSize, NULL, NULL
                            )   )
    {
        ASSERT(FALSE);
        return HRESULT_FROM_ERROR_CODE(GetLastError());
    }

    // reallocate memory and copy bstr
    if ( !SysReAllocStringLen(&m_Bstr, Bstr, BstrLen) )
    {
        return HRESULT_FROM_ERROR_CODE(GetLastError());
    }

    IsValid(TRUE);
    IsModified(TRUE);
    return S_OK;
}


BOOL
SDP_BSTRING::InternalSetCharStrByRef(
    IN      CHAR    *CharacterStringByReference,
	IN		DWORD	Length
    )
{
    if ( !SDP_CHAR_STRING::InternalSetCharStrByRef(CharacterStringByReference, Length) )
    {
        return FALSE;
    }

    if ( !ConvertToBstr() )
    {
        return FALSE;
    }

    return TRUE;
}


BOOL
SDP_BSTRING::InternalSetCharStrByCopy(
    IN	const	CHAR    *CharacterStringByCopy,
	IN			DWORD	Length
    )
{
    if ( !SDP_CHAR_STRING::InternalSetCharStrByCopy(CharacterStringByCopy, Length) )
    {
        return FALSE;
    }

    if ( !ConvertToBstr() )
    {
        return FALSE;
    }

    return TRUE;
}



BOOL
SDP_BSTRING::InternalParseToken(
    IN      CHAR        *Token
    )
{
    UINT    CodePage;

    // parse the token using the base class parsing method
    if ( !SDP_CHAR_STRING::InternalParseToken(Token) )
    {
        return FALSE;
    }

    if ( !ConvertToBstr() )
    {
        return FALSE;
    }

    return TRUE;
}




SDP_BSTRING::~SDP_BSTRING()
{
    if ( NULL != m_Bstr )
    {
        SysFreeString(m_Bstr);
    }
}



void				
SDP_OPTIONAL_BSTRING::Reset(
	)
{
	// perform the destructor actions (freeing ptrs) and the constructor actions (initializing
	// member variables to starting values)

	m_IsBstrCreated = FALSE;

	// call the base class Reset
	SDP_BSTRING::Reset();
}


// returns the bstr for the character string
// creates a bstr if required
HRESULT
SDP_OPTIONAL_BSTRING::GetBstr(
    IN BSTR * pBstr
    )
{
    // ZoltanS
    ASSERT( ! IsBadWritePtr(pBstr, sizeof(BSTR)) );

    if ( !IsValid() )
    {
        return HRESULT_FROM_ERROR_CODE(ERROR_INVALID_DATA);
    }

    if ( !m_IsBstrCreated )
    {
        if ( !ConvertToBstr() )
        {
            return HRESULT_FROM_ERROR_CODE(GetLastError());
        }

        m_IsBstrCreated = TRUE;
    }

    *pBstr = m_Bstr;
    return S_OK;
}


HRESULT
SDP_OPTIONAL_BSTRING::SetBstr(
    IN BSTR Bstr
    )
{
    HRESULT HResult = SDP_BSTRING::SetBstr(Bstr);
    if ( FAILED(HResult) )
    {
        return HResult;
    }

    m_IsBstrCreated = TRUE;
    ASSERT(S_OK == HResult);
    return HResult;
}


BOOL
SDP_OPTIONAL_BSTRING::InternalSetCharStrByRef(
    IN      CHAR    *CharacterStringByReference,
	IN		DWORD	Length
    )
{
    if ( !SDP_CHAR_STRING::InternalSetCharStrByRef(CharacterStringByReference, Length) )
    {
        return FALSE;
    }

    m_IsBstrCreated = FALSE;
    return TRUE;
}



BOOL
SDP_OPTIONAL_BSTRING::InternalSetCharStrByCopy(
    IN	const	CHAR    *CharacterStringByCopy,
	IN			DWORD	Length
    )
{
    if ( !SDP_CHAR_STRING::InternalSetCharStrByCopy(CharacterStringByCopy, Length) )
    {
        return FALSE;
    }

    m_IsBstrCreated = FALSE;
    return TRUE;
}



// since the bstr must only be created on demand, parsing must
// be over-ridden such that the bstr is not created during parsing
BOOL
SDP_OPTIONAL_BSTRING::InternalParseToken(
    IN      CHAR    *Token
    )
{
    return SDP_CHAR_STRING::InternalParseToken(Token);
}



HRESULT
SDP_BSTRING_LINE::GetBstrCopy(
    IN BSTR *pBstr
    )
{
    // ZoltanS
	if ( IsBadWritePtr(pBstr, sizeof(BSTR)) )
	{
		return E_POINTER;
	}

    // if no elements in the field array, then the instance is invalid
    if ( 0 >= m_FieldArray.GetSize() )
    {
        // ZoltanS fix: return a valid empty string! Otherwise we aren't
        // conforming to Bstr semantics.

        *pBstr = SysAllocString(L"");

        if ( (*pBstr) == NULL )
        {
            return E_OUTOFMEMORY;
        }

        return S_OK;
    }

    return GetBstring().GetBstrCopy(pBstr);
}


HRESULT
SDP_BSTRING_LINE::SetBstr(
    IN      BSTR    Bstr
    )
{
    BAIL_ON_FAILURE(GetBstring().SetBstr(Bstr));

    try
    {
        // set the field and separator char array
        m_FieldArray.SetAtGrow(0, &GetBstring());
        m_SeparatorCharArray.SetAtGrow(0, CHAR_NEWLINE);
    }
    catch(...)
    {
        m_FieldArray.RemoveAll();
        m_SeparatorCharArray.RemoveAll();

        return E_OUTOFMEMORY;
    }

    return S_OK;
}


void
SDP_REQD_BSTRING_LINE::InternalReset(
	)
{
	m_Bstring.Reset();
}


void
SDP_CHAR_STRING_LINE::InternalReset(
	)
{
	m_SdpOptionalBstring.Reset();
}


HRESULT
SDP_LIMITED_CHAR_STRING::SetLimitedCharString(
    IN          CHAR    *String
    )
{
    // check if the string is a legal string
    // check if the token is one of the legal strings
    for(UINT i=0; i < m_NumStrings; i++)
    {
        if ( !strcmp(m_LegalStrings[i], String) )
        {
            // parse the string using the base class parsing method
            if ( !SDP_CHAR_STRING::InternalParseToken(String) )
            {
                return HRESULT_FROM_ERROR_CODE(GetLastError());
            }

            return S_OK;
        }
    }

    // no matching legal string
    return HRESULT_FROM_ERROR_CODE(ERROR_INVALID_DATA);
}


BOOL
SDP_LIMITED_CHAR_STRING::InternalParseToken(
    IN      CHAR        *Token
    )
{
    // check if the token is one of the legal strings
    for(UINT i=0; i < m_NumStrings; i++)
    {
        if ( !strcmp(m_LegalStrings[i], Token) )
        {
            // parse the token using the base class parsing method
            if ( !SDP_CHAR_STRING::InternalParseToken(Token) )
            {
                return FALSE;
            }

            return TRUE;
        }
    }

    // the token does not match any of the legal strings
    SetLastError(SDP_INVALID_FORMAT);
    return FALSE;
}


BOOL
SDP_ADDRESS::IsValidIP4Address(
    IN  CHAR    *Address,
    OUT ULONG   &Ip4AddressValue
    )
{
    ASSERT(NULL != Address);

    // check if there are atleast 3 CHAR_DOTs in the address string
    // inet_addr accepts 3,2,1 or even no dots
    CHAR *CurrentChar = Address;
    BYTE NumDots = 0;
    while (EOS != *CurrentChar)
    {
        if (CHAR_DOT == *CurrentChar)
        {
            NumDots++;
            if (3 == NumDots)
            {
                break;
            }
        }

        // advance the ptr to the next char
        CurrentChar++;
    }

    // check for the number of dots
    if (3 != NumDots)
    {
        SetLastError(SDP_INVALID_ADDRESS);
        return FALSE;
    }

    // currently only ip4 is supported
    Ip4AddressValue = inet_addr(Address);

    // check if the address is a valid IP4 address
    if ( (ULONG)INADDR_NONE == Ip4AddressValue )
    {
        SetLastError(SDP_INVALID_ADDRESS);
        return FALSE;
    }

    return TRUE;
}


HRESULT
SDP_ADDRESS::SetAddress(
    IN      BSTR    Address
    )
{
    // SetBstr also sets the is modified and is valid flags on success
    HRESULT ToReturn = SDP_OPTIONAL_BSTRING::SetBstr(Address);
    if ( FAILED(ToReturn) )
    {
        return ToReturn;
    }

    // get the ip address
    ULONG Ip4AddressValue;

    // check if the token is a valid IP4 address
    if ( !IsValidIP4Address(GetCharacterString(), Ip4AddressValue) )
    {
        IsModified(FALSE);
        IsValid(FALSE);
        return HRESULT_FROM_ERROR_CODE(GetLastError());
    }

    m_IsMulticastFlag = IN_MULTICAST(ntohl(Ip4AddressValue));

    // the grammar requires that a multicast address be either an administratively scoped
    // address "239.*" or out of the internet multicast conferencing range "224.2.*"
    // we won't check that here as that may be overly restrictive

    return S_OK;
}


HRESULT
SDP_ADDRESS::SetBstr(
    IN BSTR Bstr
    )
{
    return SetAddress(Bstr);
}



BOOL
SDP_ADDRESS::InternalParseToken(
    IN      CHAR        *Token
    )
{
    ULONG Ip4AddressValue;

    // check if the token is a valid IP4 address
    if ( !IsValidIP4Address(Token, Ip4AddressValue) )
    {
        return FALSE;
    }

    // check if the address(unicast or multicast) is same as whats expected
    if ( IN_MULTICAST(ntohl(Ip4AddressValue)) != m_IsMulticastFlag )
    {
        SetLastError(SDP_INVALID_ADDRESS);
        return FALSE;
    }

    // the grammar requires that a multicast address be either an administratively scoped
    // address "239.*" or out of the internet multicast conferencing range "224.2.*"
    // we won't check that here as that may be overly restrictive

    // call the base class parse token method
    if ( !SDP_CHAR_STRING::InternalParseToken(Token) )
    {
        return FALSE;
    }

    return TRUE;
}