📄 notify.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
#include <ssdppch.h>
#pragma hdrstop
#include "http_status.h"
#include "notify.h"
// Forward Declarations
void DebugMessageQueueSize(msg_queue msgQueue, const char* function);
/******************************************************************************
* g_pNotificationMgr:
*
* - single notification manager externally referenced
* - manages HTTP NOTIFY requests into the upnpsvc
*
******************************************************************************/
notification_mgr* g_pNotificationMgr;
/******************************************************************************
* EventsExtensionProc:
*
* - HTTP NOTIFY ISAPI Extension entry point
* - passes the NOTIFY message to the notification manager
* - sends the HTTP response
*
******************************************************************************/
DWORD EventsExtensionProc(LPEXTENSION_CONTROL_BLOCK pecb)
{
if(g_pNotificationMgr)
{
pecb->dwHttpStatusCode = g_pNotificationMgr->event(pecb);
HSE_SEND_HEADER_EX_INFO hse = {0};
hse.pszStatus = ce::http_status_string(pecb->dwHttpStatusCode);
hse.cchStatus = strlen(hse.pszStatus);
// send response
pecb->ServerSupportFunction(pecb->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &hse, NULL, NULL);
}
return HSE_STATUS_SUCCESS;
}
// NotifyAliveByebye
bool NotifyAliveByebye(PSSDP_REQUEST pSsdpRequest)
{
bool retCode = false;
if(g_pNotificationMgr)
{
retCode = g_pNotificationMgr->alive_byebye(pSsdpRequest);
}
return retCode;
}
// RegisterNotificationSink
BOOL RegisterNotificationSink(
__in HANDLE hOwner,
__in DWORD dwNotificationType,
__in ce::marshal_arg<ce::copy_in, LPCWSTR> pwszUSN,
__in ce::marshal_arg<ce::copy_in, LPCWSTR> pwszQueryString,
__in ce::marshal_arg<ce::copy_in, LPCWSTR> pwszMsgQueue,
__out ce::marshal_arg<ce::copy_out, HANDLE*> phNotify)
{
// the buffer for the output handle must be valid
if(!phNotify)
{
goto Finish;
}
// the buffer for the message queue must be valid
if(!pwszMsgQueue || !*pwszMsgQueue)
{
goto Finish;
}
// the universal service number for alive/bye-bye messages must be valid to determine source
if(!pwszUSN || !*pwszUSN)
{
goto Finish;
}
// the query string for event messages must be valid to determine event source
if(!pwszQueryString || !*pwszQueryString)
{
goto Finish;
}
if(g_pNotificationMgr)
{
*phNotify = g_pNotificationMgr->register_notification(dwNotificationType, pwszUSN, pwszQueryString, pwszMsgQueue, hOwner);
}
return (*phNotify != NULL);
Finish:
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
// DeregisterNotificationSink
BOOL DeregisterNotificationSink(ce::PSL_HANDLE hSubscription)
{
if(g_pNotificationMgr)
{
g_pNotificationMgr->deregister_notification((HANDLE)hSubscription);
}
return TRUE;
}
// CleanupNotifications
void CleanupNotifications(HANDLE hOwner)
{
if(g_pNotificationMgr)
{
g_pNotificationMgr->cleanup_notifications(hOwner);
}
}
// CheckListNotifyForAliveByebye
VOID CheckListNotifyForAliveByebye(PSSDP_REQUEST pSsdpRequest)
{
}
/******************************************************************************
* notification_mgr::event:
*
* - handle event notification
* - find the associated notification_sink based on the QueryString SID (subscription ID)
*
******************************************************************************/
DWORD notification_mgr::event(LPEXTENSION_CONTROL_BLOCK pecb)
{
ce::wstring strQueryString;
DWORD dw;
DWORD dwEventSEQ;
char pszHeaderBuf[22] = {0};
int status = HTTP_STATUS_PRECOND_FAILED;
///////////////////////
// verify body length
if(pecb->cbAvailable != pecb->cbTotalBytes)
if(pecb->cbTotalBytes == 0)
// CONTENT-LENGTH header missing (required by UPnP spec)
return HTTP_STATUS_LENGTH_REQUIRED;
else
// event body larger than data preloaded by web server (48K by default)
// reject the event for security reasons
return HTTP_STATUS_REQUEST_TOO_LARGE;
///////////////////////
// verify headers
// NT: upnp:event
if(!pecb->GetServerVariable(pecb->ConnID, "HTTP_NT", pszHeaderBuf, &(dw = sizeof(pszHeaderBuf) - 1)))
if(ERROR_NO_DATA == GetLastError())
// missing NT header -> 400 Bad request
return HTTP_STATUS_BAD_REQUEST;
else
// invalid NT header -> 412 Precondition failed
return HTTP_STATUS_PRECOND_FAILED;
else
if(strcmp(pszHeaderBuf, "upnp:event"))
// invalid NT header -> 412 Precondition failed
return HTTP_STATUS_PRECOND_FAILED;
// NTS: upnp:propchange
if(!pecb->GetServerVariable(pecb->ConnID, "HTTP_NTS", pszHeaderBuf, &(dw = sizeof(pszHeaderBuf) - 1)))
if(ERROR_NO_DATA == GetLastError())
// missing NT header -> 400 Bad request
return HTTP_STATUS_BAD_REQUEST;
else
// invalid NTS header -> 412 Precondition failed
return HTTP_STATUS_PRECOND_FAILED;
else
if(strcmp(pszHeaderBuf, "upnp:propchange"))
// invalid NTS header -> 412 Precondition failed
return HTTP_STATUS_PRECOND_FAILED;
// SID:
if(!pecb->GetServerVariable(pecb->ConnID, "HTTP_SID", pszHeaderBuf, &(dw = sizeof(pszHeaderBuf) - 1)))
if(GetLastError() == ERROR_NO_DATA)
// missing SID header -> 400 Bad request
return HTTP_STATUS_BAD_REQUEST;
// SEQ:
if(!pecb->GetServerVariable(pecb->ConnID, "HTTP_SEQ", pszHeaderBuf, &(dw = sizeof(pszHeaderBuf) - 1)))
// invalid or missing SEQ -> 412 Precondition failed
return HTTP_STATUS_BAD_REQUEST;
else
dwEventSEQ = atoi(pszHeaderBuf);
// convert query string to unicode - it is part of URL so it is limited to ANSI code page
ce::MultiByteToWideChar(CP_ACP, pecb->lpszQueryString, -1, &strQueryString);
ce::gate<ce::critical_section> _gate(m_cs);
// find notification sink for the query string
for(ce::list<notification_sink>::iterator it = m_listSinks.begin(), itEnd = m_listSinks.end(); it != itEnd; ++it)
if(it->getQueryString() == strQueryString && (it->getType() & NOTIFY_PROP_CHANGE))
{
Assert(pecb->cbAvailable == pecb->cbTotalBytes);
ce::wstring strMessage;
// assuming UTF8 for content encoding but fallback to ANSI if UTF8 is not available
if(ce::MultiByteToWideChar(CP_UTF8, (LPCSTR)pecb->lpbData, pecb->cbAvailable, &strMessage))
{
// watermark
if(it->getWatermark().IncrementWatermark())
{
it->event(strMessage, dwEventSEQ);
status = HTTP_STATUS_OK;
}
else
{
status = HTTP_STATUS_SERVICE_UNAVAIL;
}
it->getWatermark().DecrementWatermark();
}
}
return status;
}
/******************************************************************************
* notification_mgr::alive_byebye:
*
* - handle alive/byebye notification
* - find the associated notification_sink based on the USN
*
******************************************************************************/
bool notification_mgr::alive_byebye(PSSDP_REQUEST pSsdpRequest)
{
Assert(pSsdpRequest->Headers[SSDP_NTS]);
bool bFound = false;
ce::wstring strUSN;
ce::wstring strNT;
// ANSI code page for USN
ce::MultiByteToWideChar(CP_ACP, pSsdpRequest->Headers[SSDP_USN], -1, &strUSN);
// ANSI code page for NT
ce::MultiByteToWideChar(CP_ACP, pSsdpRequest->Headers[SSDP_NT], -1, &strNT);
ce::gate<ce::critical_section> _gate(m_cs);
// find notification sink for the Unique Service Name
for(ce::list<notification_sink>::iterator it = m_listSinks.begin(), itEnd = m_listSinks.end(); it != itEnd; ++it)
if(it->getUSN() == strUSN || 0 == wcsncmp(it->getUSN(), strNT, wcslen(it->getUSN())))
{
bFound = true;
if(it->getWatermark().IncrementWatermark())
{
if((it->getType() & NOTIFY_BYEBYE) && 0 == strcmp("ssdp:byebye", pSsdpRequest->Headers[SSDP_NTS]))
it->byebye(pSsdpRequest);
if((it->getType() & NOTIFY_ALIVE) && 0 == strcmp("ssdp:alive", pSsdpRequest->Headers[SSDP_NTS]))
it->alive(pSsdpRequest);
}
it->getWatermark().DecrementWatermark();
}
return bFound;
}
/******************************************************************************
* notification_mgr::register_notification:
*
* - registers a new control point for notifications
*
******************************************************************************/
HANDLE notification_mgr::register_notification(DWORD dwNotificationType, LPCWSTR pwszUSN, LPCWSTR pwszQueryString, LPCWSTR pwszMsgQueue, HANDLE hOwner)
{
ce::gate<ce::critical_section> _gate(m_cs);
ce::list<msg_queue>::iterator it, itEnd;
// find message queue
for(it = m_listMsgQueues.begin(), itEnd = m_listMsgQueues.end(); it != itEnd; ++it)
if(it->name() == pwszMsgQueue)
break;
if(it == itEnd)
{
// not found -> new message queue
if(m_listMsgQueues.push_front(msg_queue(pwszMsgQueue, hOwner)))
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -