/*

Copyright (c) 1998-1999  Microsoft Corporation

Module Name:
    blbreg.cpp

Abstract:


Author:

*/

#include "stdafx.h"
// #include <windows.h>
// #include <wtypes.h>
#include <winsock2.h>
#include "blbreg.h"


const TCHAR TCHAR_BLANK    = _T(' ');


const TCHAR gsz_SdpRoot[] =
        _T("Software\\Microsoft\\Windows\\CurrentVersion\\Dynamic Directory\\Conference\\Sdp");

const TCHAR gsz_ConfInstRoot[] =
        _T("Software\\Microsoft\\Windows\\CurrentVersion\\Dynamic Directory\\Conference");

const TCHAR gsz_CharNewLine = _T('\n');
const TCHAR gsz_CharRegNewLine = _T('#');


DWORD   REG_READER::ms_ErrorCode = ERROR_INTERNAL_ERROR;


TCHAR   SDP_REG_READER::ms_TimeTemplate[MAX_REG_TSTR_SIZE];
TCHAR   SDP_REG_READER::ms_MediaTemplate[MAX_REG_TSTR_SIZE];
TCHAR   SDP_REG_READER::ms_ConfBlobTemplate[MAX_BLOB_TEMPLATE_SIZE];

USHORT  SDP_REG_READER::ms_TimeTemplateLen;
USHORT  SDP_REG_READER::ms_MediaTemplateLen;
USHORT  SDP_REG_READER::ms_ConfBlobTemplateLen;

DWORD   SDP_REG_READER::ms_StartTimeOffset;
DWORD   SDP_REG_READER::ms_StopTimeOffset;

BOOL    SDP_REG_READER::ms_fInitCalled;
    
BOOL    SDP_REG_READER::ms_fWinsockStarted;

IP_ADDRESS  SDP_REG_READER::ms_HostIpAddress;

static REG_INFO const gs_SdpRegInfoArray[] = 
{
    {TIME_TEMPLATE,        
        sizeof(SDP_REG_READER::ms_TimeTemplate) - 1,    // -1 for the newline
        &SDP_REG_READER::ms_TimeTemplateLen,
        SDP_REG_READER::ms_TimeTemplate},
    {MEDIA_TEMPLATE,            
        sizeof(SDP_REG_READER::ms_MediaTemplate) - 1,    // -1 for the newline
        &SDP_REG_READER::ms_MediaTemplateLen,
        SDP_REG_READER::ms_MediaTemplate},
    {CONFERENCE_BLOB_TEMPLATE,    
        sizeof(SDP_REG_READER::ms_ConfBlobTemplate),    
        &SDP_REG_READER::ms_ConfBlobTemplateLen,
        SDP_REG_READER::ms_ConfBlobTemplate}
};
    
    
inline void
AppendTchar(
    IN  OUT TCHAR   *Tstr, 
    IN  OUT USHORT  &TstrLen, 
    IN      TCHAR   AppendChar
    )
{
    ASSERT(lstrlen(Tstr) == TstrLen);

    Tstr[TstrLen++] = AppendChar;
    Tstr[TstrLen] = TCHAR_EOS;
}


                                                                               

BOOL
REG_READER::ReadRegValues(
    IN    HKEY    Key,
    IN    DWORD    NumValues,
    IN    REG_INFO const RegInfoArray[]
    )
{
    DWORD ValueType = REG_SZ;
    DWORD BufferSize = 0;

    // for each value field, retrieve the value
    for (UINT i=0; i < NumValues; i++)
    {
        // determine the size of the buffer 
        ms_ErrorCode = RegQueryValueEx(
                        Key,
                        RegInfoArray[i].msz_ValueName,
                        0,
                        &ValueType,
                        NULL,
                        &BufferSize
                       );
        if ( ERROR_SUCCESS != ms_ErrorCode )
        {
            return FALSE;
        }

        // check if the reqd buffer is bigger than the max acceptable size
        if ( RegInfoArray[i].m_MaxSize < BufferSize )
        {
            ms_ErrorCode = ERROR_OUTOFMEMORY;
            return FALSE;
        }

        // retrieve the value into the allocated buffer
        ms_ErrorCode = RegQueryValueEx(
                        Key,
                        RegInfoArray[i].msz_ValueName,
                        0,
                        &ValueType,
                        (BYTE *)RegInfoArray[i].msz_Tstr,
                        &BufferSize
                       );
        if ( ERROR_SUCCESS != ms_ErrorCode )
        {
            return FALSE;
        }

        // the reqd buffer size is > 1
        ASSERT(1 > BufferSize );

        // jump over any trailing blank characters - start at the last but one char
        for(UINT j=BufferSize-2; (TCHAR_BLANK == RegInfoArray[i].msz_Tstr[j]); j--)
        {
        }

        // if trailing blank chars, set the EOS beyond the last non-blank char
        if ( j < (BufferSize-2) )
        {
            RegInfoArray[i].msz_Tstr[j+1] = TCHAR_EOS;
        }

        // set the length of the tstr
        *RegInfoArray[i].m_TstrLen = j+1;
    }

    // return success
    return TRUE;
}


// static method
BOOL    
IP_ADDRESS::GetLocalIpAddress(
        OUT    DWORD    &LocalIpAddress
    )
{
    CHAR        LocalHostName[MAXHOSTNAME];
    LPHOSTENT    Hostent;
    int            WsockErrorCode;

    // get the local host name
    WsockErrorCode = gethostname(LocalHostName, MAXHOSTNAME);
    if ( SOCKET_ERROR != WsockErrorCode)
    {
        // resolve host name for local address
        Hostent = gethostbyname((LPSTR)LocalHostName);
        if ( Hostent )
        {
            LocalIpAddress = ntohl(*((u_long *)Hostent->h_addr));
            return TRUE;
        }
    }

    const CHAR *LOOPBACK_ADDRESS_STRING = "127.0.0.1";

    SOCKADDR_IN    LocalAddress;
    SOCKADDR_IN    RemoteAddress;
        INT             AddressSize = sizeof(sockaddr_in);
    SOCKET        Socket;

    // initialize it to 0 to use it as a check later
    LocalIpAddress = 0;

    // initialize the local address to 0
    LocalAddress.sin_addr.s_addr = INADDR_ANY;

    // if still not resolved try the (horrible) second strategy
    Socket = socket(AF_INET, SOCK_DGRAM, 0);
    if ( INVALID_SOCKET != Socket )
    {
        // connect to arbitrary port and address (NOT loopback)
        // if connect is not performed, the provider may not return
        // a valid ip address
        RemoteAddress.sin_family    = AF_INET;
        RemoteAddress.sin_port        = htons(IPPORT_ECHO);

        // this address should ideally be an address that is outside the
        // intranet - but no harm if the address is inside
        RemoteAddress.sin_addr.s_addr = inet_addr(LOOPBACK_ADDRESS_STRING);
        WsockErrorCode = connect(Socket, (sockaddr *)&RemoteAddress, sizeof(sockaddr_in));

        if ( SOCKET_ERROR != WsockErrorCode )
        {
            // get local address
            getsockname(Socket, (sockaddr *)&LocalAddress, (int *)&AddressSize);
            LocalIpAddress = ntohl(LocalAddress.sin_addr.s_addr);
        }

        // close the socket
        closesocket(Socket);
    }

    if ( 0 == LocalIpAddress )
    {
        SetLastError(WSAGetLastError());
        return FALSE;
    }

    return TRUE;
}


BOOL
SDP_REG_READER::ReadTimeValues(
    IN    HKEY SdpKey
    )
{
    DWORD    ValueType = REG_DWORD;
    DWORD    BufferSize = sizeof(DWORD);

    // read the start and stop time offsets
    ms_ErrorCode = RegQueryValueEx(
                    SdpKey,
                    START_TIME_OFFSET,
                    0,
                    &ValueType,
                    (BYTE *)&ms_StartTimeOffset,
                    &BufferSize
                    );
    if ( ERROR_SUCCESS != ms_ErrorCode )
    {
        return FALSE;
    }

    ms_ErrorCode = RegQueryValueEx(
                    SdpKey,
                    STOP_TIME_OFFSET,
                    0,
                    &ValueType,
                    (BYTE *)&ms_StopTimeOffset,
                    &BufferSize
                    );
    if ( ERROR_SUCCESS != ms_ErrorCode )
    {
        return FALSE;
    }

    return TRUE;
}

                                                                                    
BOOL                                                                                
SDP_REG_READER::CheckIfCorrectVersion(                                                 
    )                                                                   
{                                                                                   
    WORD        wVersionRequested;                                                  
    WSADATA     WsaData;                                                            
                                                                                    
    wVersionRequested = MAKEWORD(2, 0);   
                                                                                    
    // call winsock startup                                                         
    int ms_ErrorCode = WSAStartup( wVersionRequested, &WsaData );                   
                                                                                   
    if ( 0 != ms_ErrorCode )                                                        
    {                                                                               
        return FALSE;                                                               
    }                                                                               

    // we'll take any version - no need to check if the requested version is supported
    ms_fWinsockStarted = TRUE;

    return TRUE;                                                                    
}    


void SDP_REG_READER::Init(
    )
{
    ms_fInitCalled = TRUE;

    if ( !CheckIfCorrectVersion() )
    {
        return;
    }

    // try to determine the host ip address
    // ignore, if failed (255.255.255.255)
    DWORD    LocalIpAddress;
    IP_ADDRESS::GetLocalIpAddress(LocalIpAddress);
    ms_HostIpAddress.SetIpAddress((0==LocalIpAddress)?(-1):LocalIpAddress);

    // open sdp key
    HKEY    SdpKey;
    ms_ErrorCode = RegOpenKeyEx(
                    HKEY_LOCAL_MACHINE,
                    gsz_SdpRoot,
                    0,
                    KEY_READ, // ZoltanS was: KEY_ALL_ACCESS
                    &SdpKey
                    );
    if ( ERROR_SUCCESS != ms_ErrorCode )
    {
        return;
    }

    // ZoltanS: NO NEED TO CLOSE THE ABOVE KEY because it is
    // wrapped in the class and closed "automatically."

    KEY_WRAP RendKeyWrap(SdpKey);

    // read the template registry info (tstr values) under the key
    if ( !ReadRegValues(
            SdpKey, 
            sizeof(gs_SdpRegInfoArray)/ sizeof(REG_INFO), 
            gs_SdpRegInfoArray 
            ) )
    {
        return;
    }

    // Insert the "a:charset:%s#" into the sdp conference template
    AddCharacterSetAttribute();

    // replace the registry newline with the real newline character
    // NOTE - this is being done because we don't know how to enter the newline character
    // into a registry string
    for (UINT i=0; TCHAR_EOS != ms_ConfBlobTemplate[i]; i++)
    {
        if ( gsz_CharRegNewLine == ms_ConfBlobTemplate[i] )
        {
            ms_ConfBlobTemplate[i] = gsz_CharNewLine;
        }
    }

    // append newline after the media and time templates
    AppendTchar(ms_MediaTemplate, ms_MediaTemplateLen, gsz_CharNewLine);
    AppendTchar(ms_TimeTemplate, ms_TimeTemplateLen, gsz_CharNewLine);

    if ( !ReadTimeValues(SdpKey) )
    {
        return;
    }

    // success
    ms_ErrorCode = ERROR_SUCCESS;

    return;
}

/*++
AddCharacterSetAttribute

  This methd it's called by SDP_REG_READER::Init
  Try to add the "a:charset:%s#" into ms_ConfBlobTemplate
  This atribute represents the character sets
--*/
void SDP_REG_READER::AddCharacterSetAttribute()
{
    if( _tcsstr( ms_ConfBlobTemplate, _T("a=charset:")))
    {
        // The attribute is already into the Blob template
        return;
    }

    // The attribute charset is not in Blob Template
    // Try to find aut the "m=" (media attribute)
    TCHAR* szMediaTemplate = _tcsstr( ms_ConfBlobTemplate, _T("m="));
    if( szMediaTemplate == NULL)
    {
        // Add at the end of the template
        _tcscat( ms_ConfBlobTemplate, _T("a=charset:%s#"));
        return;
    }

    // We have to insert the
    TCHAR szBuffer[2000];
    _tcscpy( szBuffer, szMediaTemplate );

    // We concatenate the charset attribute
    szMediaTemplate[0] = (TCHAR)0;
    _tcscat( ms_ConfBlobTemplate, _T("a=charset:%s#"));

    // We add the media atrributes
    _tcscat( ms_ConfBlobTemplate, szBuffer);
    return;
}