/* Copyright (c) 1998-1999 Microsoft Corporation Module Name: sdpblob.cpp Abstract: Implementation of CSdpConferenceBlob Author: */ #include "stdafx.h" #include "blbgen.h" #include "sdpblb.h" #include "sdpblob.h" #include "blbreg.h" #include "addrgen.h" #include #include #include "blbtico.h" #include "blbmeco.h" // ZoltanS: This is the multicast address we give by default. The app must then // go and get a real address via MDHCP and explicitly set it to a meaningful // value. const long DUMMY_ADDRESS = 0xe0000000; // A dummy address that qualifies as Class D. ///////////////////////////////////////////////////////////////////////////// // CSdpConferenceBlob ///////////////////////////////////////////////////////////////////////////// // // definition for the critical section needed by // CObjectSafeImpl // CComAutoCriticalSection CObjectSafeImpl::s_CritSection; CCritSection g_DllLock; const USHORT MAX_IP_ADDRESS_STRLEN = 15; const USHORT NUM_CONF_BLOB_TEMPLATE_PARAMS = 9; // static SDP_REG_READER gs_SdpRegReader; // a 1-1 mapping from the index (BLOB_CHARACTER_SET) to the SDP_CHAR_SET // ASSUMPTION: BCS_UTF8 is the last value in the enumeration BLOB_CHARACTER_SET SDP_CHARACTER_SET const CSdpConferenceBlob::gs_SdpCharSetMapping[BCS_UTF8] = { CS_ASCII, CS_UTF7, CS_UTF8 }; CSdpConferenceBlob * CSdpConferenceBlob::GetConfBlob( ) { return this; } HRESULT CSdpConferenceBlob::WriteStartTime( IN DWORD MinStartTime ) { CLock Lock(g_DllLock); int NumEntries = (int)GetTimeList().GetSize(); // set the first time entry to the MinStartTime if ( 0 < NumEntries ) { // need to make sure that the stop time is after the start time or unbounded (0) HRESULT HResult = ((SDP_TIME *)GetTimeList().GetAt(0))->SetStartTime(MinStartTime); BAIL_ON_FAILURE(HResult); } else // create an entry { // create a new entry, use vb 1 based indices HRESULT HResult = ((MY_TIME_COLL_IMPL *)m_TimeCollection)->Create(1, MinStartTime, 0); BAIL_ON_FAILURE(HResult); } // iterate through the time list and for each other time entry // modify the start time (if the start time is before MinStartTime, change it to MinStartTime) for(UINT i = 1; (int)i < NumEntries; i++ ) { ULONG StartTime; HRESULT HResult = ((SDP_TIME *)GetTimeList().GetAt(i))->GetStartTime(StartTime); // ignore invalid values and continue if ( FAILED(HResult) ) { continue; } if ( StartTime < MinStartTime ) { HRESULT HResult = ((SDP_TIME *)GetTimeList().GetAt(i))->SetStartTime(MinStartTime); // ignore invalid values and continue if ( FAILED(HResult) ) { continue; } } } return S_OK; } HRESULT CSdpConferenceBlob::WriteStopTime( IN DWORD MaxStopTime ) { CLock Lock(g_DllLock); int NumEntries = (int)GetTimeList().GetSize(); // set the first time entry to the MaxStopTime if ( 0 < NumEntries ) { // need to make sure that the stop time is after the start time or unbounded (0) ((SDP_TIME *)GetTimeList().GetAt(0))->SetStopTime(MaxStopTime); } else // create an entry { // create a new entry, use vb 1 based indices HRESULT HResult = ((MY_TIME_COLL_IMPL *)m_TimeCollection)->Create(1, 0, MaxStopTime); BAIL_ON_FAILURE(HResult); } // iterate through the time list and for each other time entry // modify the stop time (if the stop time is after MaxStopTime, change it to MaxStopTime) for(UINT i = 1; (int)i < NumEntries; i++ ) { ULONG StopTime; HRESULT HResult = ((SDP_TIME *)GetTimeList().GetAt(i))->GetStopTime(StopTime); // ignore invalid values and continue if ( FAILED(HResult) ) { continue; } if ( StopTime > MaxStopTime ) { ((SDP_TIME *)GetTimeList().GetAt(i))->SetStopTime(MaxStopTime); } } return S_OK; } /* enum RND_ADVERTISING_SCOPE // as per SDP recommendations { RAS_LOCAL, // ttl <=1 recommended ttl 1 RAS_SITE, // <=15 15 RAS_REGION, // <=63 63 RAS_WORLD // <=255 127 } RND_ADVERTISING_SCOPE; modification in target action ITConference SDP_BLOB for each connection line, the ttl is set to a max of the recommended ttl for the new advertising scope ITConnection CONFERENCE the SDP_BLOB component determines the max ttl and notifies the CONFERENCE of the new advertising scope this is similar to the way start/stop time modifications are handled. */ HRESULT CSdpConferenceBlob::WriteAdvertisingScope( IN DWORD MaxAdvertisingScope ) { CLock Lock(g_DllLock); // ZoltanS: bug # 191413: check for out of range advertising scopes if ( ( MaxAdvertisingScope > RAS_WORLD ) || ( MaxAdvertisingScope < RAS_LOCAL ) ) { return E_INVALIDARG; } BYTE MaxTtl = GetTtl((RND_ADVERTISING_SCOPE)MaxAdvertisingScope); // set the default connection ttl to the max ttl if ( GetConnection().GetTtl().IsValid() ) { GetConnection().GetTtl().SetValue(MaxTtl); } else // hack** : using SetConnection method. instead, the ttl field should always be kept valid { // get the current address and the number of addresses value and use the SetConnection method // this puts the ttl field into the array of member fields BSTR StartAddress = NULL; BAIL_ON_FAILURE(GetConnection().GetStartAddress().GetBstr(&StartAddress)); HRESULT HResult = GetConnection().SetConnection( StartAddress, GetConnection().GetNumAddresses().GetValue(), MaxTtl ); BAIL_ON_FAILURE(HResult); } // iterate through the sdp media list and for each connection entry, set the ttl // to maxttl if its more than it int NumEntries = (int)GetMediaList().GetSize(); for(UINT i = 1; (int)i < NumEntries; i++ ) { SDP_CONNECTION &SdpConnection = ((SDP_MEDIA *)GetMediaList().GetAt(i))->GetConnection(); if ( SdpConnection.GetTtl().IsValid() && (SdpConnection.GetTtl().GetValue() > MaxTtl) ) { SdpConnection.GetTtl().SetValue(MaxTtl); } } return S_OK; } STDMETHODIMP CSdpConferenceBlob::Init( /*[in]*/ BSTR pName, /*[in]*/ BLOB_CHARACTER_SET CharacterSet, /*[in]*/ BSTR pBlob ) { CLock Lock(g_DllLock); // validate the parameters // the name cannot be null, if no blob has been specified (if a blob is specified then the name // is implicit in it if ( (NULL == pName) && (NULL == pBlob) ) { return E_INVALIDARG; } // initialize the sdp if ( !SDP_BLOB::Init() ) { return HRESULT_FROM_ERROR_CODE(GetLastError()); } // create media/time collection, query for I*Collection i/f CComObject *MediaCollection; try { MediaCollection = new CComObject; } catch(...) { MediaCollection = NULL; } BAIL_IF_NULL(MediaCollection, HRESULT_FROM_ERROR_CODE(ERROR_INVALID_DATA)); HRESULT HResult = MediaCollection->Init(*this, GetMediaList()); if ( FAILED(HResult) ) { delete MediaCollection; return HResult; } // inform the sdp instance that it needn't delete the media list on destruction ClearDestroyMediaListFlag(); // query for the ITMediaCollection i/f HResult = MediaCollection->_InternalQueryInterface(IID_ITMediaCollection, (void **)&m_MediaCollection); if ( FAILED(HResult) ) { delete MediaCollection; return HResult; } // on failure, just delete the time collection and return // no need to delete the media collection as well since that's taken care of by the destructor CComObject *TimeCollection; try { TimeCollection = new CComObject; } catch(...) { TimeCollection = NULL; } BAIL_IF_NULL(TimeCollection, HRESULT_FROM_ERROR_CODE(ERROR_INVALID_DATA)); HResult = TimeCollection->Init(*this, GetTimeList()); if ( FAILED(HResult) ) { delete TimeCollection; return HResult; } // inform the sdp instance that it needn't delete the time list on destruction ClearDestroyTimeListFlag(); // query for the ITTimeCollection i/f HResult = TimeCollection->_InternalQueryInterface(IID_ITTimeCollection, (void **)&m_TimeCollection); if ( FAILED(HResult) ) { delete TimeCollection; return HResult; } // check if a default sdp needs to be created, else use the sdp provided by the user if ( NULL == pBlob ) { BAIL_ON_FAILURE( CreateDefault( pName, gs_SdpCharSetMapping[CharacterSet-1]) ); } else { // we change the Character set from the CS_UTF8 // if the blob containts the "a=charset:" attribute CharacterSet = GetBlobCharacterSet( pBlob); // HACK ** we don't want notifications of blob contents when the sdp blob is being passed in // (conference directory - enumeration scenario), so the notification owner is set after the // conference blob is processed BAIL_ON_FAILURE(SetConferenceBlob(CharacterSet, pBlob)); // at this point, the sdp is either passed in by the non-dir-user or is obtained by enumerating // the directory. // clear the modified state on the sdp (parsing in an sdp sets the state to modified) so that // only true modifications are tracked ClearModifiedState(); } return S_OK; } STDMETHODIMP CSdpConferenceBlob::get_IsModified(VARIANT_BOOL * pVal) { BAIL_IF_NULL(pVal, E_INVALIDARG); CLock Lock(g_DllLock); *pVal = ( (GetWasModified() || SDP::IsModified()) ? VARIANT_TRUE : VARIANT_FALSE); return S_OK; } STDMETHODIMP CSdpConferenceBlob::get_TimeCollection( ITTimeCollection * *ppTimeCollection ) { BAIL_IF_NULL(ppTimeCollection, E_INVALIDARG); CLock Lock(g_DllLock); ASSERT(NULL != m_TimeCollection); if ( NULL == m_TimeCollection ) { return HRESULT_FROM_ERROR_CODE(ERROR_INVALID_DATA); } // increase the ref count m_TimeCollection->AddRef(); *ppTimeCollection = m_TimeCollection; return S_OK; } STDMETHODIMP CSdpConferenceBlob::get_MediaCollection( ITMediaCollection * *ppMediaCollection ) { BAIL_IF_NULL(ppMediaCollection, E_INVALIDARG); CLock Lock(g_DllLock); ASSERT(NULL != m_MediaCollection); if ( NULL == m_MediaCollection ) { return HRESULT_FROM_ERROR_CODE(ERROR_INVALID_DATA); } // increase the ref count m_MediaCollection->AddRef(); *ppMediaCollection = m_MediaCollection; return S_OK; } STDMETHODIMP CSdpConferenceBlob::get_IsValid(VARIANT_BOOL * pVal) { BAIL_IF_NULL(pVal, E_INVALIDARG); CLock Lock(g_DllLock); *pVal = (IsValid() ? VARIANT_TRUE : VARIANT_FALSE); return S_OK; } STDMETHODIMP CSdpConferenceBlob::get_CharacterSet(BLOB_CHARACTER_SET *pCharacterSet) { // map the sdp character set value for SDP_BLOB to the BLOB_CHARACTER_SET BAIL_IF_NULL(pCharacterSet, E_INVALIDARG); CLock Lock(g_DllLock); // check the sdp char set values corresponding to the blob character sets for a match // if a match is found, return the index as the blob character set // NOTE: the for loop is dependent upon the order of declaration of the BLOB_CHARACTER_SET // enum values for( UINT BlobCharSet = BCS_ASCII; BCS_UTF8 >= BlobCharSet; BlobCharSet++ ) { // BCS_ASCII is 1, but the array starts with 0, so the index is BlobCharSet - 1. if ( gs_SdpCharSetMapping[BlobCharSet -1] == SDP::GetCharacterSet() ) { *pCharacterSet = (BLOB_CHARACTER_SET)BlobCharSet; return S_OK; } } return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } STDMETHODIMP CSdpConferenceBlob::get_ConferenceBlob(BSTR * pVal) { CLock Lock(g_DllLock); return GetBstrCopy(pVal); } HRESULT CSdpConferenceBlob::WriteConferenceBlob( IN SDP_CHARACTER_SET SdpCharSet, IN BSTR newVal ) { // set the character set of the SDP_BSTRING VERIFY(SetCharacterSet(SdpCharSet)); // set the bstr to the passed in value, this is converted to the ascii representation and parsed HRESULT HResult = SetBstr(newVal); BAIL_ON_FAILURE(HResult); HResult = ((MEDIA_COLLECTION *)m_MediaCollection)->Init(*this, GetMediaList()); BAIL_ON_FAILURE(HResult); HResult = ((TIME_COLLECTION *)m_TimeCollection)->Init(*this, GetTimeList()); BAIL_ON_FAILURE(HResult); return S_OK; } STDMETHODIMP CSdpConferenceBlob::SetConferenceBlob(BLOB_CHARACTER_SET CharacterSet, BSTR newVal) { // validate the passed in character set value // although this is an enumeration, someone may try and pass in a different value // NOTE: Assumes BCS_ASCII is the first and BCS_UTF8 is the last enumerated value if ( !( (BCS_ASCII <= CharacterSet) && (BCS_UTF8 >= CharacterSet) ) ) { return E_INVALIDARG; } CLock Lock(g_DllLock); // map the BLOB_CHARACTER_SET value to the sdp character set (since the BCS value start at 1, // subtract 1 from the blob character set to index into the array // write the conference blob, also send notifications for any modified dependent attributes HRESULT HResult = WriteConferenceBlob(gs_SdpCharSetMapping[CharacterSet-1], newVal); BAIL_ON_FAILURE(HResult); return S_OK; } STDMETHODIMP CSdpConferenceBlob::get_ProtocolVersion(BYTE * pVal) { BAIL_IF_NULL(pVal, E_INVALIDARG); CLock Lock(g_DllLock); // cast the ulong value to a byte because vb doesn't take a ulong, this shouldn't be // a problem until version 256 *pVal = (BYTE)GetProtocolVersion().GetVersionValue(); return S_OK; } STDMETHODIMP CSdpConferenceBlob::get_SessionId(DOUBLE * pVal) { BAIL_IF_NULL(pVal, E_INVALIDARG); CLock Lock(g_DllLock); // vb doesn't take ulong - cast the ulong value to a double, the next bigger type *pVal = (DOUBLE)GetOrigin().GetSessionId().GetValue(); return S_OK; } STDMETHODIMP CSdpConferenceBlob::get_SessionVersion(DOUBLE * pVal) { BAIL_IF_NULL(pVal, E_INVALIDARG); CLock Lock(g_DllLock); // vb doesn't take ulong - cast the ulong value to a double, the next bigger type *pVal = (DOUBLE)GetOrigin().GetSessionVersion().GetValue(); return S_OK; } STDMETHODIMP CSdpConferenceBlob::put_SessionVersion(DOUBLE newVal) { // the bandwidth value must be a valid ULONG value (vb restrictions) if ( !((0 <= newVal) && (ULONG(-1) > newVal)) ) { return E_INVALIDARG; } // check if there is any fractional part, this check is valid as it is a valid ULONG value if ( newVal != (ULONG)newVal ) { return E_INVALIDARG; } CLock Lock(g_DllLock); GetOrigin().GetSessionVersion().SetValue((ULONG)newVal); return S_OK; } STDMETHODIMP CSdpConferenceBlob::get_MachineAddress(BSTR * pVal) { CLock Lock(g_DllLock); return GetOrigin().GetAddress().GetBstrCopy(pVal); } STDMETHODIMP CSdpConferenceBlob::put_MachineAddress(BSTR newVal) { CLock Lock(g_DllLock); return GetOrigin().GetAddress().SetBstr(newVal); } STDMETHODIMP CSdpConferenceBlob::get_Name(BSTR * pVal) { CLock Lock(g_DllLock); return GetSessionName().GetBstrCopy(pVal); } STDMETHODIMP CSdpConferenceBlob::put_Name(BSTR newVal) { // write the new session name // acquire lock inside return WriteName(newVal); } STDMETHODIMP CSdpConferenceBlob::get_Description(BSTR * pVal) { CLock Lock(g_DllLock); return GetSessionTitle().GetBstrCopy(pVal); } STDMETHODIMP CSdpConferenceBlob::put_Description(BSTR newVal) { // write the new session title / description // acquire lock inside HRESULT HResult = WriteSessionTitle(newVal); BAIL_ON_FAILURE(HResult); return S_OK; } STDMETHODIMP CSdpConferenceBlob::get_Url(BSTR * pVal) { CLock Lock(g_DllLock); return GetUri().GetBstrCopy(pVal); } STDMETHODIMP CSdpConferenceBlob::put_Url(BSTR newVal) { // write the new Url // acquire lock inside HRESULT HResult = WriteUrl(newVal); BAIL_ON_FAILURE(HResult); return S_OK; } STDMETHODIMP CSdpConferenceBlob::GetEmailNames( VARIANT /*SAFEARRAY(BSTR)*/ *Addresses, VARIANT /*SAFEARRAY(BSTR)*/ *Names ) { CLock Lock(g_DllLock); return GetEmailList().GetSafeArray(Addresses, Names); } STDMETHODIMP CSdpConferenceBlob::SetEmailNames( VARIANT /*SAFEARRAY(BSTR)*/ Addresses, VARIANT /*SAFEARRAY(BSTR)*/ Names ) { CLock Lock(g_DllLock); return GetEmailList().SetSafeArray(Addresses, Names); } STDMETHODIMP CSdpConferenceBlob::GetPhoneNumbers( VARIANT /*SAFEARRAY(BSTR)*/ *Numbers, VARIANT /*SAFEARRAY(BSTR)*/ *Names ) { CLock Lock(g_DllLock); return GetPhoneList().GetSafeArray(Numbers, Names); } STDMETHODIMP CSdpConferenceBlob::SetPhoneNumbers( VARIANT /*SAFEARRAY(BSTR)*/ Numbers, VARIANT /*SAFEARRAY(BSTR)*/ Names ) { CLock Lock(g_DllLock); return GetPhoneList().SetSafeArray(Numbers, Names); } STDMETHODIMP CSdpConferenceBlob::get_Originator(BSTR * pVal) { CLock Lock(g_DllLock); return GetOrigin().GetUserName().GetBstrCopy(pVal); } STDMETHODIMP CSdpConferenceBlob::put_Originator(BSTR newVal) { // write the new user name // acquire lock inside HRESULT HResult = WriteOriginator(newVal); BAIL_ON_FAILURE(HResult); return S_OK; } inline WORD GetEvenValue( IN WORD Value ) { return (0 == (Value % 2))? Value : (Value - 1); } TCHAR * CSdpConferenceBlob::GenerateSdpBlob( IN BSTR Name, IN SDP_CHARACTER_SET CharacterSet ) { ASSERT(NULL != Name); // // Get multicast ports. We don't set addresses; that's the app's // responsibility via MDHCP. // MSA_PORT_GROUP PortGroup; PortGroup.PortType = VIDEO_PORT; WORD FirstVideoPort; // allocate video port if ( !MSAAllocatePorts(&PortGroup, FALSE, 2, &FirstVideoPort) ) { return NULL; } PortGroup.PortType = AUDIO_PORT; WORD FirstAudioPort; // allocate audio port if ( !MSAAllocatePorts(&PortGroup, FALSE, 2, &FirstAudioPort) ) { return NULL; } // convert the returned ports to even values for RTP compliance // ASSUMPTION : the sdp template read from the registry uses RTP as the transport FirstAudioPort = GetEvenValue(FirstAudioPort); FirstVideoPort = GetEvenValue(FirstVideoPort); IP_ADDRESS AudioIpAddress(DUMMY_ADDRESS); IP_ADDRESS VideoIpAddress(DUMMY_ADDRESS); const DWORD MAX_USER_NAME_LEN = 100; DWORD OriginatorBufLen = MAX_USER_NAME_LEN+1; TCHAR Originator[MAX_USER_NAME_LEN+1]; if ( !GetUserName(Originator, &OriginatorBufLen) ) { return NULL; } // altconv.h - requires this declaration for W2T to work USES_CONVERSION; // convert the provided name to a tchar; the returned tchar string is // allocated on the stack - no need to delete it TCHAR *TcharName = W2T(Name); BAIL_IF_NULL(TcharName, NULL); // allocate enough memory for the sdp blob TCHAR *SdpBlob; try { SdpBlob = new TCHAR[ SDP_REG_READER::GetConfBlobTemplateLen() + lstrlen(Originator) + lstrlen(TcharName) + MAXHOSTNAME + 3*MAX_NTP_TIME_STRLEN + 2*MAX_IP_ADDRESS_STRLEN + 2*MAX_PORT_STRLEN - 2*NUM_CONF_BLOB_TEMPLATE_PARAMS ]; } catch(...) { SdpBlob = NULL; } if ( NULL == SdpBlob ) { SetLastError(ERROR_OUTOFMEMORY); return NULL; } // // ZoltanS: get the local host name (replaces all this IP address nonsense) // char szLocalHostName[MAXHOSTNAME + 1]; char * pszHost; int WsockErrorCode; WsockErrorCode = gethostname(szLocalHostName, MAXHOSTNAME); if ( 0 == WsockErrorCode ) { struct hostent *hostp = gethostbyname(szLocalHostName); if ( hostp ) { pszHost = hostp->h_name; } else { // if we can't resolve our own hostname (yuck!) then we can // still do *something* (but it may be bad for l2tp scenarios). pszHost = SDP_REG_READER::GetHostIpAddress(); } } else { // if we can't get a hostname (yuck!) then we can still do // *something* (but it may be bad for l2tp scenarios). pszHost = SDP_REG_READER::GetHostIpAddress(); } // stprintf the string to create the conference blob // check if the stprintf operation succeeded CHAR* szCharacterSet = (CHAR*)UTF8_STRING; switch( CharacterSet ) { case CS_ASCII: szCharacterSet = (CHAR*)ASCII_STRING; break; case CS_UTF7: szCharacterSet = (CHAR*)UTF7_STRING; break; } if ( 0 == _stprintf(SdpBlob, SDP_REG_READER::GetConfBlobTemplate(), Originator, GetCurrentNtpTime(), pszHost, // ZoltanS was: SDP_REG_READER::GetHostIpAddress(), // local machine ip address string, TcharName, AudioIpAddress.GetTstr(), // common c field GetCurrentNtpTime() + SDP_REG_READER::GetStartTimeOffset(), // start time - current time + start offset, GetCurrentNtpTime() + SDP_REG_READER::GetStopTimeOffset(), // stop time - current time + stop offset szCharacterSet, FirstAudioPort, FirstVideoPort, VideoIpAddress.GetTstr() ) ) { delete SdpBlob; return NULL; } return SdpBlob; } HRESULT CSdpConferenceBlob::CreateDefault( IN BSTR Name, IN SDP_CHARACTER_SET CharacterSet ) { BAIL_IF_NULL(Name, E_INVALIDARG); // check if the registry entries were read without errors if ( !SDP_REG_READER::IsValid() ) { return HRESULT_FROM_ERROR_CODE(SDP_REG_READER::GetErrorCode()); } // check if a valid conference blob already exists, return error if ( SDP_BLOB::IsValid() ) { return E_FAIL; } // use the registry values to generate the default sdp CHAR *SdpBlob = GenerateSdpBlob(Name, CharacterSet); BAIL_IF_NULL(SdpBlob, HRESULT_FROM_ERROR_CODE(GetLastError())); ASSERT(NULL != SdpBlob); // parse in the sdp HRESULT HResult = SetTstr(SdpBlob); delete SdpBlob; BAIL_ON_FAILURE(HResult); HResult = ((MEDIA_COLLECTION *)m_MediaCollection)->Init(*this, GetMediaList()); BAIL_ON_FAILURE(HResult); HResult = ((TIME_COLLECTION *)m_TimeCollection)->Init(*this, GetTimeList()); BAIL_ON_FAILURE(HResult); return S_OK; } typedef IDispatchImpl, &IID_ITConferenceBlob, &LIBID_SDPBLBLib> CTConferenceBlob; typedef IDispatchImpl, &IID_ITSdp, &LIBID_SDPBLBLib> CTSdp; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++= // // CSdpConferenceBlob::GetIDsOfNames // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++= STDMETHODIMP CSdpConferenceBlob::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid ) { HRESULT hr = DISP_E_UNKNOWNNAME; // // See if the requsted method belongs to the default interface // hr = CTConferenceBlob::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); if (SUCCEEDED(hr)) { rgdispid[0] |= IDISPCONFBLOB; return hr; } // // If not, then try the ITSdp base class // hr = CTSdp::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); if (SUCCEEDED(hr)) { rgdispid[0] |= IDISPSDP; return hr; } // // If not, then try the ITConnection base class // hr = ITConnectionImpl::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); if (SUCCEEDED(hr)) { rgdispid[0] |= IDISPCONNECTION; return hr; } // // If not, then try the ITAttributeList base class // hr = ITAttributeListImpl::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); if (SUCCEEDED(hr)) { rgdispid[0] |= IDISPATTRLIST; return hr; } return hr; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++= // // CSdpConferenceBlob::Invoke // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++= STDMETHODIMP CSdpConferenceBlob::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr ) { HRESULT hr = DISP_E_MEMBERNOTFOUND; DWORD dwInterface = (dispidMember & INTERFACEMASK); // // Call invoke for the required interface // switch (dwInterface) { case IDISPCONFBLOB: { hr = CTConferenceBlob::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr ); break; } case IDISPSDP: { hr = CTSdp::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr ); break; } case IDISPCONNECTION: { hr = ITConnectionImpl::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr ); break; } case IDISPATTRLIST: { hr = ITAttributeListImpl::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr ); break; } } // end switch (dwInterface) return hr; } BLOB_CHARACTER_SET CSdpConferenceBlob::GetBlobCharacterSet( IN BSTR bstrBlob ) { BLOB_CHARACTER_SET CharSet = BCS_ASCII; const WCHAR szCharacterSet[] = L"a=charset:"; WCHAR* szCharacterSetAttribute = wcsstr( bstrBlob, szCharacterSet); if( szCharacterSetAttribute == NULL) { //We don't have the attribute //We consider, for backward compability the default ASCII return CharSet; } // We have an attribute entry szCharacterSetAttribute += wcslen( szCharacterSet ); if( wcsstr( szCharacterSetAttribute, L"unicode-1-1-utf8")) { CharSet = BCS_UTF8; } else if (wcsstr( szCharacterSetAttribute, L"unicode-1-1-utf7")) { CharSet = BCS_UTF7; } else if (wcsstr( szCharacterSetAttribute, L"ascii")) { CharSet = BCS_ASCII; } return CharSet; }