|
|
#include <pch.h>
#pragma hdrstop
#include <wininet.h>
#include "ssdptypes.h"
#include "notify.h"
#include "ssdpnetwork.h"
#include "ssdpfunc.h"
#include "ssdpsrv.h"
#include "event.h"
#include "ncinet.h"
#include "ncbase.h"
#include "upthread.h"
#include "rundown.h"
#define NOTIFY_RESULT_SIZE 2
static const CHAR c_szDeadNts[] = "upnp:dead";
#if DBG
const DWORD c_csecTimeout = 60 * 3; // 3 minute timeout (debug)
#else
const DWORD c_csecTimeout = 60 * 30; // 30 minute timeout
#endif
const TCHAR c_szSubscribe[] = TEXT("SUBSCRIBE"); const TCHAR c_szUnSubscribe[] = TEXT("UNSUBSCRIBE"); extern const TCHAR c_szHttpVersion[] = TEXT("HTTP/1.1"); const TCHAR c_szSubsHeaderFmt[] = TEXT("NT: upnp:event\r\n" "Callback: <http://%s:%d/%s>\r\n" "Timeout: Second-%d\r\n\r\n"); const TCHAR c_szReSubsHeaderFmt[] = TEXT("SID: %s\r\nTimeout: Second-%d\r\n"); const TCHAR c_szUnSubsHeaderFmt[] = TEXT("SID: %s\r\n"); const TCHAR c_szServer[] = TEXT("Server"); const TCHAR c_szTimeout[] = TEXT("Timeout"); const TCHAR c_szSid[] = TEXT("SID");
// URI and port of our listener
const TCHAR c_szNotifyUri[] = TEXT("notify"); const DWORD c_nPort = 5000;
#if DBG
const DWORD c_csecDefaultTimeout = 60 * 1; // 1 minute is default
// subscription timeout (debug)
#else
const DWORD c_csecDefaultTimeout = 60 * 10; // 10 minutes is default
// subscription timeout
#endif
HRESULT HrSendSubscriptionRequest(HINTERNET hin, LPCTSTR szUrl, LPCTSTR szSid, DWORD *pcsecTimeout, LPTSTR *pszSidOut, ESSR_TYPE essrt);
CSsdpPendingNotification::CSsdpPendingNotification() { ZeroMemory(&m_ssdpRequest, sizeof(m_ssdpRequest)); }
CSsdpPendingNotification::~CSsdpPendingNotification() { FreeSsdpRequest(&m_ssdpRequest); }
HRESULT CSsdpPendingNotification::HrInitialize(const SSDP_REQUEST * pRequest) { HRESULT hr = S_OK;
if(!CopySsdpRequest(&m_ssdpRequest, pRequest)) { hr = E_OUTOFMEMORY; }
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpPendingNotification::HrInitialize"); return hr; }
HRESULT CSsdpPendingNotification::HrGetRequest(SSDP_REQUEST * pRequest) { HRESULT hr = S_OK;
if(!CopySsdpRequest(pRequest, &m_ssdpRequest)) { hr = E_FAIL; }
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpPendingNotification::HrGetRequest"); return hr; }
CSsdpNotifyRequest::CSsdpNotifyRequest() : m_timer(*this), m_hNotifySemaphore(INVALID_HANDLE_VALUE) { }
CSsdpNotifyRequest::~CSsdpNotifyRequest() { }
void CSsdpNotifyRequest::OnRundown(CSsdpNotifyRequest * pNotify) { CSsdpNotifyRequestManager::Instance().OnRundown(pNotify); }
class CNotifyRequestTimerAction : public CWorkItem { public: static HRESULT HrCreate( CSsdpNotifyRequest * pRequest, DWORD dwSecTimeout, const CUString & strSid, const CUString & strUrl); private: CNotifyRequestTimerAction(CSsdpNotifyRequest * pRequest, DWORD dwSecTimeout); ~CNotifyRequestTimerAction(); CNotifyRequestTimerAction(const CNotifyRequestTimerAction &); CNotifyRequestTimerAction & operator=(const CNotifyRequestTimerAction &);
DWORD DwRun(); HRESULT HrIntialize( const CUString & strSid, const CUString & strUrl);
CSsdpNotifyRequest * m_pRequest; DWORD m_dwSecTimeout; CUString m_strSid; CUString m_strUrl; };
CNotifyRequestTimerAction::CNotifyRequestTimerAction(CSsdpNotifyRequest * pRequest, DWORD dwSecTimeout) : m_pRequest(pRequest), m_dwSecTimeout(dwSecTimeout) { }
CNotifyRequestTimerAction::~CNotifyRequestTimerAction() { }
HRESULT CNotifyRequestTimerAction::HrCreate( CSsdpNotifyRequest * pRequest, DWORD dwSecTimeout, const CUString & strSid, const CUString & strUrl) { HRESULT hr = S_OK;
CNotifyRequestTimerAction * pAction = new CNotifyRequestTimerAction(pRequest, dwSecTimeout); if(!pAction) { hr = E_OUTOFMEMORY; } if(SUCCEEDED(hr)) { hr = pAction->HrIntialize(strSid, strUrl); if(SUCCEEDED(hr)) { hr = pAction->HrStart(TRUE); } if(FAILED(hr)) { delete pAction; } }
TraceHr(ttidEvents, FAL, hr, FALSE, "CNotifyRequestTimerAction::HrCreate"); return hr; }
DWORD CNotifyRequestTimerAction::DwRun() { HRESULT hr = S_OK;
char * szSid = NULL; char * szUrl = NULL;
hr = m_strSid.HrGetMultiByteWithAlloc(&szSid); if(SUCCEEDED(hr)) { hr = m_strUrl.HrGetMultiByteWithAlloc(&szUrl); if(SUCCEEDED(hr)) { hr = HrSendSubscriptionRequest(g_hInetSess, szUrl, szSid, &m_dwSecTimeout, NULL, SSR_RESUBSCRIBE); } }
if(SUCCEEDED(hr)) { hr = CSsdpNotifyRequestManager::Instance().HrRestartClientResubscribeTimer( m_pRequest, m_dwSecTimeout); } else { // Failed to send a re-subscribe. We now need to notify clients that
// a subscription has been lost. Compose a pending notification to
// let them know this fact.
//
SSDP_REQUEST ssdpRequest; ZeroMemory(&ssdpRequest, sizeof(ssdpRequest)); InitializeSsdpRequest(&ssdpRequest); ssdpRequest.Headers[GENA_SID] = szSid; szSid = NULL; hr = HrCopyString(c_szDeadNts, &ssdpRequest.Headers[SSDP_NTS]); if(SUCCEEDED(hr)) { hr = CSsdpNotifyRequestManager::Instance().HrCheckListNotifyForEvent(&ssdpRequest); } FreeSsdpRequest(&ssdpRequest); }
delete [] szSid; delete [] szUrl;
TraceHr(ttidEvents, FAL, hr, FALSE, "CNotifyRequestTimerAction::DwRun"); return 0; }
HRESULT CNotifyRequestTimerAction::HrIntialize( const CUString & strSid, const CUString & strUrl) { HRESULT hr = S_OK;
hr = m_strSid.HrAssign(strSid); if(SUCCEEDED(hr)) { hr = m_strUrl.HrAssign(strUrl); }
TraceHr(ttidEvents, FAL, hr, FALSE, "CNotifyRequestTimerAction::HrIntialize"); return hr; }
void CSsdpNotifyRequest::TimerFired() { HRESULT hr = S_OK;
hr = CNotifyRequestTimerAction::HrCreate(this, this->m_dwSecTimeout, this->m_strSid, this->m_strUrl);
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpNotifyRequest::TimerFired"); }
BOOL CSsdpNotifyRequest::TimerTryToLock() { return m_critSec.FTryEnter(); }
void CSsdpNotifyRequest::TimerUnlock() { m_critSec.Leave(); }
HRESULT CSsdpNotifyRequest::HrRestartClientResubscribeTimer( DWORD dwSecTimeout) { HRESULT hr = S_OK;
CLock lock(m_critSec);
m_dwSecTimeout = dwSecTimeout;
// Do 65% of timeout in milliseconds
DWORD dwTimeoutInMillis = (dwSecTimeout * 65 * 1000) / 100;
hr = m_timer.HrResetTimer(dwTimeoutInMillis);
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpNotifyRequest::HrRestartClientResubscribeTimer"); return hr; }
HRESULT CSsdpNotifyRequest::HrInitializeAlive( const char * szNT, HANDLE hNotifySemaphore) { if(!szNT) { return E_POINTER; } HRESULT hr = S_OK;
m_nt = NOTIFY_ALIVE; hr = m_strNT.HrAssign(szNT); if(SUCCEEDED(hr)) { m_hNotifySemaphore = hNotifySemaphore; m_dwSecTimeout = 0; }
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpNotifyRequest::HrInitializeAlive"); return hr; }
HRESULT CSsdpNotifyRequest::HrInitializePropChange( const char * szUrl, HANDLE hNotifySemaphore) { if(!szUrl || !*szUrl) { return E_POINTER; } HRESULT hr = S_OK;
m_nt = NOTIFY_PROP_CHANGE; hr = m_strUrl.HrAssign(szUrl); if(SUCCEEDED(hr)) { m_hNotifySemaphore = hNotifySemaphore; m_dwSecTimeout = 0; }
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpNotifyRequest::HrInitializePropChange"); return hr; }
HRESULT CSsdpNotifyRequest::HrSendPropChangeSubscription(SSDP_REGISTER_INFO ** ppRegisterInfo) { HRESULT hr = S_OK;
*ppRegisterInfo = NULL;
char * szUrl = NULL; char * szSid = NULL; hr = m_strUrl.HrGetMultiByteWithAlloc(&szUrl); if(SUCCEEDED(hr)) { TraceTag(ttidEvents, "CSsdpNotifyRequest::HrSendPropChangeSubscription(this=%x) - About to call HrSendSubscriptionRequest", this);
if (!g_hInetSess) { g_hInetSess = HinInternetOpenA("Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); }
if (g_hInetSess) { hr = HrSendSubscriptionRequest(g_hInetSess, szUrl, NULL, &m_dwSecTimeout, &szSid, SSR_SUBSCRIBE); } else { hr = E_UNEXPECTED; TraceTag(ttidEvents, "CSsdpNotifyRequest::HrSendPropChangeSubscription - HinInternetOpenA failed!"); }
if(SUCCEEDED(hr)) { TraceTag(ttidEvents, "CSsdpNotifyRequest::HrSendPropChangeSubscription(this=%x) - Called HrSendSubscriptionRequest - SID:%s", this, szSid);
hr = m_strSid.HrAssign(szSid); delete [] szSid; } delete [] szUrl; }
if(SUCCEEDED(hr)) { CLock lock(m_critSec);
// Do 65% of timeout in milliseconds
DWORD dwTimeoutInMillis = (m_dwSecTimeout * 65 * 1000) / 100;
hr = m_timer.HrSetTimer(dwTimeoutInMillis); if(SUCCEEDED(hr)) { SSDP_REGISTER_INFO * pRegisterInfo = new SSDP_REGISTER_INFO; if(!pRegisterInfo) { hr = E_OUTOFMEMORY; } if(SUCCEEDED(hr)) { pRegisterInfo->csecTimeout = m_dwSecTimeout; hr = m_strSid.HrGetMultiByteWithAlloc(&pRegisterInfo->szSid); if(SUCCEEDED(hr)) { *ppRegisterInfo = pRegisterInfo; } if(FAILED(hr)) { delete pRegisterInfo; } } } }
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpNotifyRequest::HrSendPropChangeSubscription"); return hr; }
HRESULT CSsdpNotifyRequest::HrShutdown() { HRESULT hr = S_OK;
CLock lock(m_critSec); if(NOTIFY_PROP_CHANGE == m_nt) { hr = m_timer.HrDelete(INVALID_HANDLE_VALUE); if(SUCCEEDED(hr)) { char * szUrl = NULL; char * szSid = NULL; hr = m_strUrl.HrGetMultiByteWithAlloc(&szUrl); if(SUCCEEDED(hr)) { hr = m_strSid.HrGetMultiByteWithAlloc(&szSid); if(SUCCEEDED(hr)) { hr = HrSendSubscriptionRequest(g_hInetSess, szUrl, szSid, &m_dwSecTimeout, NULL, SSR_UNSUBSCRIBE); delete [] szSid; } delete [] szUrl; } } }
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpNotifyRequest::HrShutdown"); return hr; }
BOOL CSsdpNotifyRequest::FIsMatchBySemaphore(HANDLE hNotifySemaphore) { return m_hNotifySemaphore == hNotifySemaphore; }
BOOL CSsdpNotifyRequest::FIsMatchingEvent(const SSDP_REQUEST * pRequest) { BOOL bMatch = FALSE;
BOOL bIsPossibleMatch = pRequest->Headers[SSDP_NTS] && !lstrcmpiA(pRequest->Headers[SSDP_NTS], "upnp:propchange");
bIsPossibleMatch = bIsPossibleMatch && pRequest->Headers[CONTENT_TYPE] && !_strnicmp(pRequest->Headers[CONTENT_TYPE], "text/xml", strlen("text/xml"));
bIsPossibleMatch = bIsPossibleMatch && (NOTIFY_PROP_CHANGE == m_nt);
if(bIsPossibleMatch) { // Ensure that we have a valid SID before letting this continue.
// We enter this critsec so that we will wait until a potential
// subscription has been sent and a SID received for it before this
// code continues.
//
if(m_strSid.GetLength() && pRequest->Headers[GENA_SID]) { char * szSid = NULL; HRESULT hr = m_strSid.HrGetMultiByteWithAlloc(&szSid); if(SUCCEEDED(hr)) { bMatch = !lstrcmpiA(pRequest->Headers[GENA_SID], szSid); delete [] szSid; } } }
return bMatch; }
BOOL CSsdpNotifyRequest::FIsMatchingAliveOrByebye(const SSDP_REQUEST * pRequest) { BOOL bMatch = FALSE;
Assert(!lstrcmpiA(pRequest->Headers[SSDP_NTS], "ssdp:alive") || !lstrcmpiA(pRequest->Headers[SSDP_NTS], "ssdp:byebye"));
if(NOTIFY_ALIVE == m_nt) { char * szNT = NULL; HRESULT hr = m_strNT.HrGetMultiByteWithAlloc(&szNT); if(SUCCEEDED(hr)) { bMatch = !lstrcmpA(szNT, pRequest->Headers[SSDP_NT]); delete [] szNT; } }
return bMatch; }
HRESULT CSsdpNotifyRequest::HrQueuePendingNotification(const SSDP_REQUEST * pRequest) { HRESULT hr = S_OK;
#if DBG
if(SSDP_NOTIFY == pRequest->Method && pRequest->Headers[GENA_SID] && pRequest->Headers[GENA_SEQ]) { TraceTag(ttidEvents, "CSsdpNotifyRequest::HrQueuePendingNotification - Event notification for SID:%s", pRequest->Headers[GENA_SID]); }
#endif // DBG
CLock lock(m_critSec);
PendingNotificationList pendingNotificationList; hr = pendingNotificationList.HrPushFrontDefault(); if(SUCCEEDED(hr)) { hr = pendingNotificationList.Front().HrInitialize(pRequest); if(SUCCEEDED(hr)) { m_pendingNotificationList.Append(pendingNotificationList); TraceTag(ttidEvents, "CSsdpNotifyRequest::HrQueuePendingNotification - releasing semaphore %x", m_hNotifySemaphore);
if(!ReleaseSemaphore(m_hNotifySemaphore, 1, NULL)) { hr = HrFromLastWin32Error(); }
} }
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpNotifyRequest::HrQueuePendingNotification"); return hr; }
BOOL CSsdpNotifyRequest::FIsPendingNotification() { CLock lock(m_critSec);
return !m_pendingNotificationList.IsEmpty(); }
HRESULT CSsdpNotifyRequest::HrRetreivePendingNotification(MessageList ** ppSvcList) { HRESULT hr = S_OK;
CLock lock(m_critSec);
// I am just going to return one item
MessageList * pSvcList = new MessageList; if(!pSvcList) { hr = E_OUTOFMEMORY; } if(SUCCEEDED(hr)) { pSvcList->list = new SSDP_REQUEST; pSvcList->size = 1; if(!pSvcList->list) { hr = E_OUTOFMEMORY; } else { ZeroMemory(pSvcList->list, sizeof(SSDP_REQUEST)); } }
if(SUCCEEDED(hr)) { if(m_pendingNotificationList.IsEmpty()) { hr = S_FALSE; TraceTag(ttidEvents, "CSsdpNotifyRequest::HrRetreivePendingNotification - no pending notifications!"); } if(S_OK == hr) { PendingNotificationList pendingNotificationList; PendingNotificationList::Iterator iter; if(S_OK == m_pendingNotificationList.GetIterator(iter)) { iter.HrMoveToList(pendingNotificationList); hr = pendingNotificationList.Front().HrGetRequest(pSvcList->list); } } }
if(FAILED(hr)) { if(pSvcList) { delete pSvcList->list; pSvcList->list = NULL; pSvcList->size = 0; } }
*ppSvcList = pSvcList;
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpNotifyRequest::HrRetreivePendingNotification"); return hr; }
CSsdpNotifyRequestManager CSsdpNotifyRequestManager::s_instance;
CSsdpNotifyRequestManager::CSsdpNotifyRequestManager() : m_unTimestamp(0) { m_hEventTimestamp = CreateEvent(NULL, TRUE, FALSE, NULL); }
CSsdpNotifyRequestManager::~CSsdpNotifyRequestManager() { CloseHandle(m_hEventTimestamp); }
CSsdpNotifyRequestManager & CSsdpNotifyRequestManager::Instance() { return s_instance; }
void CSsdpNotifyRequestManager::OnRundown(CSsdpNotifyRequest * pNotify) { HrRemoveInternal(TRUE, NULL, pNotify); }
HRESULT CSsdpNotifyRequestManager::HrCreateAliveNotifyRequest( PCONTEXT_HANDLE_TYPE * ppContextHandle, const char * szNT, HANDLE hNotifySemaphore) { HRESULT hr = S_OK; CLock lock(m_critSecAliveList);
*ppContextHandle = NULL;
CSsdpNotifyRequest * pRequest = NULL;
NotifyRequestList notifyRequestList; hr = notifyRequestList.HrPushFrontDefault(); if(SUCCEEDED(hr)) { hr = notifyRequestList.Front().HrInitializeAlive(szNT, hNotifySemaphore); if(SUCCEEDED(hr)) { m_aliveList.Prepend(notifyRequestList); *ppContextHandle = &m_aliveList.Front(); pRequest = &m_aliveList.Front(); } } if(SUCCEEDED(hr) && pRequest) { hr = CSsdpRundownSupport::Instance().HrAddRundownItem(pRequest); }
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpNotifyRequestManager::HrCreateAliveNotifyRequest"); return hr; }
HRESULT CSsdpNotifyRequestManager::HrCreatePropChangeNotifyRequest( PCONTEXT_HANDLE_TYPE * ppContextHandle, const char * szUrl, HANDLE hNotifySemaphore, SSDP_REGISTER_INFO ** ppRegisterInfo) { HRESULT hr = S_OK;
*ppContextHandle = NULL;
CSsdpNotifyRequest * pRequest = NULL;
NotifyRequestList notifyRequestList; hr = notifyRequestList.HrPushFrontDefault(); if(SUCCEEDED(hr)) { hr = notifyRequestList.Front().HrInitializePropChange(szUrl, hNotifySemaphore); if(SUCCEEDED(hr)) { __int64 unTimestamp = 0; { CLock lock(m_critSecTimestampList); hr = m_timestampList.HrPushFront(m_unTimestamp); if(SUCCEEDED(hr)) { unTimestamp = m_unTimestamp; ++m_unTimestamp; } } if(SUCCEEDED(hr)) { hr = notifyRequestList.Front().HrSendPropChangeSubscription(ppRegisterInfo); if(SUCCEEDED(hr)) { CLock lock(m_critSecPropChangeList); m_propChangeList.Prepend(notifyRequestList); *ppContextHandle = &m_propChangeList.Front(); pRequest = &m_propChangeList.Front(); } CLock lock(m_critSecTimestampList); TimestampList::Iterator iter; if(S_OK == m_timestampList.GetIterator(iter)) { __int64 * pun = NULL; while(S_OK == iter.HrGetItem(&pun)) { if(*pun == unTimestamp) { iter.HrErase(); break; } if(S_OK != iter.HrNext()) { break; } } } if(!PulseEvent(m_hEventTimestamp)) { hr = HrFromLastWin32Error(); TraceHr(ttidError, FAL, hr, FALSE, "CSsdpNotifyRequestManager::HrCreatePropChangeNotifyRequest - PulseEvent failed!"); } } } }
if(SUCCEEDED(hr) && pRequest) { hr = CSsdpRundownSupport::Instance().HrAddRundownItem(pRequest); }
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpNotifyRequestManager::HrCreatePropChangeNotifyRequest"); return hr; }
HRESULT CSsdpNotifyRequestManager::HrRemoveNotifyRequest( HANDLE hNotifySemaphore) { return HrRemoveInternal(FALSE, hNotifySemaphore, NULL); }
HRESULT CSsdpNotifyRequestManager::HrRemoveNotifyRequestByPointer( CSsdpNotifyRequest * pRequest) { return HrRemoveInternal(FALSE, NULL, pRequest); }
HRESULT CSsdpNotifyRequestManager::HrCheckListNotifyForEvent(const SSDP_REQUEST * pRequest) { HRESULT hr = S_OK;
TraceTag(ttidEvents, "CSsdpNotifyRequestManager::HrCheckListNotifyForEvent");
__int64 unTimestamp = 0;
{ CLock lock(m_critSecTimestampList); unTimestamp = m_unTimestamp; }
bool bFound = FALSE;
while(true) { { CLock lock(m_critSecPropChangeList);
NotifyRequestList::Iterator iter; if(S_OK == m_propChangeList.GetIterator(iter)) { CSsdpNotifyRequest * pNotifyIter = NULL; while(S_OK == iter.HrGetItem(&pNotifyIter)) { if(pNotifyIter->FIsMatchingEvent(pRequest)) { hr = pNotifyIter->HrQueuePendingNotification(pRequest); bFound = TRUE; break; } if(S_OK != iter.HrNext()) { break; } } } }
if(bFound) { break; }
{ BOOL bAllOlder = TRUE;
CLock lock(m_critSecTimestampList); TimestampList::Iterator iter; if(S_OK == m_timestampList.GetIterator(iter)) { __int64 * pun = NULL; while(S_OK == iter.HrGetItem(&pun)) { if(*pun < unTimestamp) { bAllOlder = FALSE; break; } if(S_OK != iter.HrNext()) { break; } } } if(bAllOlder) { break; } }
WaitForSingleObject(m_hEventTimestamp, 2000); }
#if DBG
if(!bFound) { TraceTag(ttidEvents, "CSsdpNotifyRequestManager::HrCheckListNotifyForEvent - not found! SID:%s", pRequest->Headers[GENA_SID] ? pRequest->Headers[GENA_SID] : "<Unknown>"); }
#endif // DBG
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpNotifyRequestManager::HrCheckListNotifyForEvent"); return hr; }
HRESULT CSsdpNotifyRequestManager::HrCheckListNotifyForAliveOrByebye(const SSDP_REQUEST * pRequest) { HRESULT hr = S_OK; CLock lock(m_critSecAliveList);
NotifyRequestList::Iterator iter; if(S_OK == m_aliveList.GetIterator(iter)) { CSsdpNotifyRequest * pNotifyIter = NULL; while(S_OK == iter.HrGetItem(&pNotifyIter)) { if(pNotifyIter->FIsMatchingAliveOrByebye(pRequest)) { hr = pNotifyIter->HrQueuePendingNotification(pRequest); if(FAILED(hr)) { break; } } if(S_OK != iter.HrNext()) { break; } } }
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpNotifyRequestManager::HrCheckListNotifyForAliveOrByebye"); return hr; }
BOOL CSsdpNotifyRequestManager::FIsAliveOrByebyeInListNotify(const SSDP_REQUEST * pRequest) { HRESULT hr = S_OK; CLock lock(m_critSecAliveList); BOOL bRet = FALSE;
NotifyRequestList::Iterator iter; if(S_OK == m_aliveList.GetIterator(iter)) { CSsdpNotifyRequest * pNotifyIter = NULL; while(S_OK == iter.HrGetItem(&pNotifyIter)) { if(pNotifyIter->FIsMatchingAliveOrByebye(pRequest)) { bRet = TRUE; break; } if(S_OK != iter.HrNext()) { break; } } }
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpNotifyRequestManager::FIsAliveOrByebyeInListNotify"); return bRet; }
HRESULT CSsdpNotifyRequestManager::HrRetreivePendingNotification( HANDLE hNotifySemaphore, MessageList ** ppSvcList) { HRESULT hr = S_OK; BOOL bFound = FALSE;
{ CLock lock(m_critSecAliveList); NotifyRequestList::Iterator iter; if(S_OK == m_aliveList.GetIterator(iter)) { CSsdpNotifyRequest * pNotifyIter = NULL; while(S_OK == iter.HrGetItem(&pNotifyIter)) { if(pNotifyIter->FIsMatchBySemaphore(hNotifySemaphore) && pNotifyIter->FIsPendingNotification()) { hr = pNotifyIter->HrRetreivePendingNotification(ppSvcList); bFound = TRUE; break; } if(S_OK != iter.HrNext()) { break; } } } } if(!bFound) { CLock lock(m_critSecPropChangeList); NotifyRequestList::Iterator iter; if(S_OK == m_propChangeList.GetIterator(iter)) { CSsdpNotifyRequest * pNotifyIter = NULL; while(S_OK == iter.HrGetItem(&pNotifyIter)) { if(pNotifyIter->FIsMatchBySemaphore(hNotifySemaphore) && pNotifyIter->FIsPendingNotification()) { hr = pNotifyIter->HrRetreivePendingNotification(ppSvcList); bFound = TRUE; break; } if(S_OK != iter.HrNext()) { break; } } } }
// See if this is a bogus release
if(!bFound) { *ppSvcList = new MessageList; if(!*ppSvcList) { hr = E_OUTOFMEMORY; } if(SUCCEEDED(hr)) { (*ppSvcList)->size = 0; (*ppSvcList)->list = NULL; } }
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpNotifyRequestManager::HrRetreivePendingNotification"); return hr; }
HRESULT CSsdpNotifyRequestManager::HrRestartClientResubscribeTimer( CSsdpNotifyRequest * pRequest, DWORD dwSecTimeout) { HRESULT hr = S_OK; CLock lock(m_critSecPropChangeList);
NotifyRequestList::Iterator iter; if(S_OK == m_propChangeList.GetIterator(iter)) { CSsdpNotifyRequest * pNotifyIter = NULL; while(S_OK == iter.HrGetItem(&pNotifyIter)) { if(pNotifyIter == pRequest) { hr = pNotifyIter->HrRestartClientResubscribeTimer(dwSecTimeout); break; } if(S_OK != iter.HrNext()) { break; } } }
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpNotifyRequestManager::HrRestartClientResubscribeTimer"); return hr; }
HRESULT CSsdpNotifyRequestManager::HrRemoveInternal(BOOL bRundown, HANDLE hNotifySemaphore, CSsdpNotifyRequest * pRequest) { HRESULT hr = S_OK;
NotifyRequestList notifyRequestListRemove;
{ CLock lock(m_critSecAliveList);
NotifyRequestList::Iterator iter; if(S_OK == m_aliveList.GetIterator(iter)) { CSsdpNotifyRequest * pNotifyIter = NULL; while(S_OK == iter.HrGetItem(&pNotifyIter)) { Assert(!hNotifySemaphore == !!pRequest); // One or the other
if((hNotifySemaphore && pNotifyIter->FIsMatchBySemaphore(hNotifySemaphore)) || (pRequest && pNotifyIter == pRequest)) { if(S_OK != iter.HrMoveToList(notifyRequestListRemove)) { break; } } if(S_OK != iter.HrNext()) { break; } } } } { CLock lock(m_critSecPropChangeList);
NotifyRequestList::Iterator iter; if(S_OK == m_propChangeList.GetIterator(iter)) { CSsdpNotifyRequest * pNotifyIter = NULL; while(S_OK == iter.HrGetItem(&pNotifyIter)) { Assert(!hNotifySemaphore == !!pRequest); // One or the other
if((hNotifySemaphore && pNotifyIter->FIsMatchBySemaphore(hNotifySemaphore)) || (pRequest && pNotifyIter == pRequest)) { if(S_OK != iter.HrMoveToList(notifyRequestListRemove)) { break; } } if(S_OK != iter.HrNext()) { break; } } } }
// Delete the items outside of the lock
{ NotifyRequestList::Iterator iter; if(S_OK == notifyRequestListRemove.GetIterator(iter)) { CSsdpNotifyRequest * pNotifyIter = NULL; while(S_OK == iter.HrGetItem(&pNotifyIter)) { if(!bRundown) { CSsdpRundownSupport::Instance().RemoveRundownItem(pNotifyIter); } hr = pNotifyIter->HrShutdown(); if(S_OK != iter.HrNext()) { break; } } } }
TraceHr(ttidEvents, FAL, hr, FALSE, "CSsdpNotifyRequestManager::HrRemoveInternal"); return hr; }
//+---------------------------------------------------------------------------
//
// Function: DwParseTime
//
// Purpose: Parses the Timeout header for a subscription
//
// Arguments:
// szTime [in] Timeout value in the format defined by RFC2518
//
// Returns: Timeout value in SECONDS
//
// Author: danielwe 13 Oct 1999
//
// Notes: NYI
//
DWORD DwParseTime(LPCTSTR szTime) { TCHAR szDigits[64]; const TCHAR c_szTimeout[] = TEXT("Second-"); const INT c_cchTimeout = lstrlen(c_szTimeout); DWORD iDigit = 0;
if (szTime && (lstrlen(szTime) > c_cchTimeout)) { if (!_strnicmp(szTime, c_szTimeout, c_cchTimeout)) { DWORD dwDigits;
// Ok we know we have at least "Timeout-x" now
szTime += c_cchTimeout;
*szDigits = 0;
while (isdigit(*szTime) && (iDigit < sizeof(szDigits) - 1)) { // Copy the digits into the buffer
szDigits[iDigit++] = *szTime++; } szDigits[iDigit] = TEXT('\0');
dwDigits = _tcstoul(szDigits, NULL, 10);
if (dwDigits) { return dwDigits; } else { return c_csecDefaultTimeout; } } }
TraceTag(ttidEvents, "DwParseTime: Invalid timeout header %s. Returning " "default timeout of %d", szTime ? szTime : "<NULL>", c_csecDefaultTimeout);
return c_csecDefaultTimeout; }
//+---------------------------------------------------------------------------
//
// Function: HrQueryHeader
//
// Purpose: Helper function to query a header from an HTTP response
//
// Arguments:
// hinR [in] Handle to request
// szHeader [in] Header to query
// pszValue [out] Returns header data
//
// Returns: S_OK if success, or ERROR_INTERNET_* error if failed
//
// Author: danielwe 18 Oct 1999
//
// Notes: Caller should free pszValue when done with it
//
HRESULT HrQueryHeader(HINTERNET hinR, LPCTSTR szHeader, LPTSTR *pszValue) { DWORD cbBuffer = 0; HRESULT hr = S_OK;
// First get the header length
//
hr = HrHttpQueryInfo(hinR, HTTP_QUERY_CUSTOM, (LPTSTR)szHeader, &cbBuffer, 0); if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr) { DWORD cbBuffLen = 0; cbBuffLen = _tcslen(szHeader); cbBuffer = ( cbBuffer > cbBuffLen ) ? cbBuffer : cbBuffLen ;
*pszValue = (LPTSTR) malloc(cbBuffer + sizeof(TCHAR)); if (*pszValue) { lstrcpy(*pszValue, szHeader); hr = HrHttpQueryInfo(hinR, HTTP_QUERY_CUSTOM, *pszValue, &cbBuffer, 0); } else { hr = E_OUTOFMEMORY; } } else { AssertSz(FAILED(hr), "First call to HttpQueryInfo must fail!"); } TraceError("HrQueryHeader", hr); return hr; }
HRESULT HrHttpQueryStatusCode(HINTERNET hinR, DWORD *pdwStatus) { HRESULT hr; DWORD cbBuf = sizeof(*pdwStatus); Assert(pdwStatus);
*pdwStatus = 0;
hr = HrHttpQueryInfo(hinR, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, pdwStatus, &cbBuf, NULL);
TraceError("HrHttpQueryStatusCode", hr); return hr; }
//+---------------------------------------------------------------------------
//
// Function: HrSendSubscriptionRequest
//
// Purpose: Sends a SUBSCRIBE request based on the data contained within
// pRequest.
//
// Arguments:
// hin [in] Handle to internet session returned from InternetOpen
// pRequest [in] Pointer to SSDP_NOTIFY_REQUEST containing information
// about the subscription that needs to be sent
// essrt [in] Can be one of:
// SSR_SUBSCRIBE - sends a subscription request
// SSR_RESUBSCRIBE - sends a re-subscription request
// SSR_UNSUBSCRIBE - sends an unsubscription request
//
// Returns: S_OK if success, or ERROR_INTERNET_* error if failed
//
// Author: danielwe 18 Oct 1999
//
// Notes:
//
HRESULT HrSendSubscriptionRequest(HINTERNET hin, LPCTSTR szUrl, LPCTSTR szSid, DWORD *pcsecTimeout, LPTSTR *pszSidOut, ESSR_TYPE essrt) { HINTERNET hinC; INTERNET_PORT ipPort; URL_COMPONENTS urlComp = {0}; TCHAR szHostName[INTERNET_MAX_HOST_NAME_LENGTH]; TCHAR szUrlPath[INTERNET_MAX_URL_LENGTH]; HRESULT hr = S_OK;
Assert(pcsecTimeout);
// Parse the server name out of the URL
//
urlComp.dwStructSize = sizeof(URL_COMPONENTS); urlComp.lpszHostName = (LPTSTR) &szHostName; urlComp.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH; urlComp.lpszUrlPath = (LPTSTR) &szUrlPath; urlComp.dwUrlPathLength = INTERNET_MAX_URL_LENGTH;
hr = HrInternetCrackUrlA(szUrl, 0, 0, &urlComp); if (SUCCEEDED(hr)) { hinC = HinInternetConnectA(hin, szHostName, urlComp.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); if (hinC) { HINTERNET hinR; LPCTSTR szMethod = c_szSubscribe;
if (SSR_UNSUBSCRIBE == essrt) { szMethod = c_szUnSubscribe; }
hinR = HinHttpOpenRequestA(hinC, szMethod, szUrlPath, c_szHttpVersion, NULL, NULL, 0, 0); if (hinR) { TCHAR szHeaders[1024];
if (SSR_RESUBSCRIBE == essrt) { wsprintf(szHeaders, c_szReSubsHeaderFmt, szSid, *pcsecTimeout); } else if (SSR_SUBSCRIBE == essrt) { SOCKADDR_IN sockIn; ULONG nAddr;
ZeroMemory(&sockIn, sizeof(SOCKADDR_IN)); hr = GetIpAddress(szHostName, &sockIn);
if (SUCCEEDED(hr)) { nAddr = sockIn.sin_addr.s_addr; wsprintf(szHeaders, c_szSubsHeaderFmt, INET_NTOA(nAddr), c_nPort, c_szNotifyUri, c_csecTimeout); } } else if (SSR_UNSUBSCRIBE == essrt) { wsprintf(szHeaders, c_szUnSubsHeaderFmt, szSid); }
TraceTag(ttidEvents, "Sending request to %s/%s:%d", szHostName, szUrlPath, urlComp.nPort); TraceTag(ttidEvents, "Sending %s request:", essrt == SSR_SUBSCRIBE ? "subscription" : essrt == SSR_RESUBSCRIBE ? "re-subscription" : essrt == SSR_UNSUBSCRIBE ? "unsubscribe" : "Unknown!"); TraceTag(ttidEvents, "-----------------------------"); TraceTag(ttidEvents, "%s", szHeaders); TraceTag(ttidEvents, "-----------------------------");
if (SUCCEEDED(hr)) { hr = HrHttpSendRequestA(hinR, szHeaders, -1, NULL, 0); }
if (SUCCEEDED(hr)) { LPTSTR szTimeout = NULL; LPTSTR szServer = NULL; DWORD dwStatus;
hr = HrHttpQueryStatusCode(hinR, &dwStatus); if (SUCCEEDED(hr) && (dwStatus == HTTP_STATUS_OK) && (SSR_UNSUBSCRIBE != essrt)) { // First get the server header. We don't care what's
// in it, just that it was present
//
hr = HrQueryHeader(hinR, c_szServer, &szServer); if (SUCCEEDED(hr)) { TraceTag(ttidEvents, "Server header contained: %s", szServer);
// Get the SID and Timeout headers from the response
//
hr = HrQueryHeader(hinR, c_szTimeout, &szTimeout); if (SUCCEEDED(hr)) { *pcsecTimeout = DwParseTime(szTimeout);
TraceTag(ttidEvents, "Subscribe response has %d for " "Timeout", *pcsecTimeout);
if (essrt == SSR_RESUBSCRIBE) { LPTSTR szSidNew = NULL;
Assert(szSid);
// Handle re-subscription response
hr = HrQueryHeader(hinR, c_szSid, &szSidNew); if (SUCCEEDED(hr)) { AssertSz(!lstrcmpi(szSidNew, szSid), "SID from re-subscribe response isn't " "the same!");
TraceTag(ttidEvents, "Resubscribe response has %s for " "SID", szSidNew);
free(szSidNew); } } else { // Handle subscription response
Assert(pszSidOut);
hr = HrQueryHeader(hinR, c_szSid, pszSidOut); if (SUCCEEDED(hr)) { TraceTag(ttidEvents, "Subscribe response has %s for " "SID", *pszSidOut);
} }
if (FAILED(hr)) { TraceTag(ttidEvents, "Didn't get SID header from " "subscription response!"); }
free(szTimeout); }
free(szServer); } } else { if (SUCCEEDED(hr) && dwStatus != HTTP_STATUS_OK) { TraceTag(ttidError, "HrSendSubscriptionRequest - Received the following error code (%d)", dwStatus); hr = E_FAIL; } } }
HrInternetCloseHandle(hinR); }
HrInternetCloseHandle(hinC); } else { hr = HrFromLastWin32Error(); } }
TraceError("HrSendSubscriptionRequest", hr); return hr; }
|