1405 lines
41 KiB
C++
1405 lines
41 KiB
C++
#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;
|
|
}
|