Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1437 lines
43 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1997.
//
// File: D E V I C E . C P P
//
// Contents: Functions dealing with UPnP controlled devices
//
// Notes:
//
// Author: danielwe 28 Oct 1999
//
//----------------------------------------------------------------------------
#include "pch.h"
#pragma hdrstop
#include "ncbase.h"
#include <oleauto.h>
#include <wininet.h>
#include "updiagp.h"
#include "ncinet.h"
#include "setupapi.h"
#include "util.h"
extern const STANDARD_OPERATION_LIST c_Ops;
UPNPDEV *PDevCur()
{
return g_ctx.pdevCur[g_ctx.idevStackIndex - 1];
}
VOID PushDev(UPNPDEV *pdev)
{
g_ctx.pdevCur[g_ctx.idevStackIndex++] = pdev;
g_ctx.ectx = CTX_CD;
}
UPNPDEV *PopDev()
{
UPNPDEV * pdev;
pdev = PDevCur();
g_ctx.idevStackIndex--;
return pdev;
}
VOID RgPropsFromSst(SST *psst, UPNP_PROPERTY **prgProps)
{
DWORD iProp;
UPNP_PROPERTY * rgProps;
rgProps = new UPNP_PROPERTY[psst->cRows];
for (iProp = 0; iProp < psst->cRows; iProp++)
{
rgProps[iProp].szName = SzFromTsz(psst->rgRows[iProp].szPropName);
VARIANT varDest;
VariantInit(&varDest);
VariantChangeType(&varDest, &psst->rgRows[iProp].varValue, 0, VT_BSTR);
//$ BUGBUG (danielwe) 25 Oct 1999: Remove this when SSDP is built
// Unicode.
//
LPWSTR wszVal = varDest.bstrVal;
rgProps[iProp].szValue = SzFromWsz(wszVal);
rgProps[iProp].dwFlags = 0;
}
*prgProps = rgProps;
}
BOOL FRegisterDeviceAsUpnpService(LPSTR szDescDocUrl, UPNPDEV *pdev)
{
SSDP_MESSAGE msg = {0};
CHAR szBuffer[256];
CHAR szUdn[256];
CHAR szType[256];
msg.iLifeTime = 15000;
msg.szLocHeader = szDescDocUrl;
Assert(pdev->szUdn);
Assert(pdev->szDeviceType);
TszToSzBuf(szUdn, pdev->szUdn, celems(szUdn));
TszToSzBuf(szType, pdev->szDeviceType, celems(szType));
// Only register this type if this is a root device
//
if (pdev->fRoot)
{
wsprintfA(szBuffer, "%s::upnp:rootdevice", szUdn);
msg.szType = "upnp:rootdevice";
msg.szUSN = szBuffer;
pdev->hSvc[0] = RegisterService(&msg, 0);
if (pdev->hSvc[0] != INVALID_HANDLE_VALUE)
{
TraceTag(ttidUpdiag, "Registered %s as an SSDP Service.",
msg.szType);
}
else
{
TraceTag(ttidUpdiag, "Failed to register %s as an SSDP Service! Error = %d.",
msg.szType, GetLastError());
return FALSE;
}
}
msg.szUSN = szUdn;
msg.szType = szUdn;
pdev->hSvc[1] = RegisterService(&msg, 0);
if (pdev->hSvc[1] != INVALID_HANDLE_VALUE)
{
TraceTag(ttidUpdiag, "Registered %s as an SSDP Service.",
msg.szType);
}
else
{
TraceTag(ttidUpdiag, "Failed to register %s as an SSDP Service! Error = %d.",
msg.szType, GetLastError());
return FALSE;
}
wsprintfA(szBuffer, "%s::%s", szUdn, szType);
msg.szUSN = szBuffer;
msg.szType = szType;
pdev->hSvc[2] = RegisterService(&msg, 0);
if (pdev->hSvc[2] != INVALID_HANDLE_VALUE)
{
TraceTag(ttidUpdiag, "Registered %s as an SSDP Service.",
msg.szType);
}
else
{
TraceTag(ttidUpdiag, "Failed to register %s as an SSDP Service! Error = %d.",
msg.szType, GetLastError());
return FALSE;
}
return TRUE;
}
VOID GetServiceConfigFile(UPNPDEV * pdev, UPNPSVC * psvc, LPCTSTR szDevConfigFile)
{
HRESULT hr;
HINF hinf;
INFCONTEXT ctx;
UINT unErrorLine;
TCHAR szKey[LINE_LEN]; // LINE_LEN defined in setupapi.h as 256
TCHAR szDeviceType[LINE_LEN];
TCHAR szServiceType[LINE_LEN];
// full path to the inf file is %windir%\inf\szConfigFile
TCHAR szDevConfigFileWithPath[MAX_PATH];
GetWindowsDirectory (szDevConfigFileWithPath, MAX_PATH);
lstrcat (szDevConfigFileWithPath, TEXT("\\inf\\"));
lstrcat(szDevConfigFileWithPath, szDevConfigFile);
hr = HrSetupOpenConfigFile(szDevConfigFileWithPath, &unErrorLine, &hinf);
if (S_OK == hr)
{
Assert(IsValidHandle(hinf));
// Loop over the Devices section
hr = HrSetupFindFirstLine(hinf, TEXT("Devices"), NULL, &ctx);
if (S_OK == hr)
{
do
{
// Retrieve a line from the Devices section
hr = HrSetupGetStringField(ctx,0,szKey,celems(szKey),NULL);
if(S_OK == hr)
{
// varify this is an "Device"
szKey[celems(szKey)-1] = L'\0';
if (lstrcmpi(szKey, TEXT("Device")))
{
TraceTag(ttidUpdiag, "Wrong key in the Devices section: %s", szKey);
continue;
}
// Query the DeviceType
hr = HrSetupGetStringField(ctx, 1, szDeviceType, celems(szDeviceType),
NULL);
if (S_OK == hr)
{
if (!lstrcmpi(szDeviceType, pdev->szDeviceType))
{
break;
}
else
{
continue;
}
}
}
}
while (S_OK == (hr = HrSetupFindNextLine(ctx, &ctx)));
}
if (hr == S_FALSE)
{
// we didn't find the device !
TraceTag(ttidUpdiag, "Error!! Config section for device %s is not found.",
pdev->szDeviceType);
}
else
{
// Loop over section for this device
hr = HrSetupFindFirstLine(hinf, szDeviceType, NULL, &ctx);
if (S_OK == hr)
{
do
{
// Retrieve a line from the Devices section
hr = HrSetupGetStringField(ctx,0,szKey,celems(szKey),NULL);
if(S_OK == hr)
{
// varify this is an "Device"
szKey[celems(szKey)-1] = L'\0';
if (lstrcmpi(szKey, TEXT("Service")))
{
TraceTag(ttidUpdiag, "Wrong key in the Device's section: %s", szKey);
continue;
}
// Query the Service Type
hr = HrSetupGetStringField(ctx, 1, szServiceType, celems(szServiceType),
NULL);
if (S_OK == hr)
{
if (!lstrcmpi(szServiceType, psvc->szServiceType))
{
break;
}
else
{
continue;
}
}
}
}
while (S_OK == (hr = HrSetupFindNextLine(ctx, &ctx)));
if (hr == S_FALSE)
{
// we didn't find the service !
TraceTag(ttidUpdiag, "Error!! Config section for service %s is not found.",
psvc->szServiceType);
}
else
{
hr = HrSetupFindFirstLine(hinf, szServiceType, NULL, &ctx);
if (S_OK == hr)
{
// Retrieve a line from the Devices section
hr = HrSetupGetStringField(ctx,0,szKey,celems(szKey),NULL);
if(S_OK == hr)
{
// varify this is an "Device"
szKey[celems(szKey)-1] = L'\0';
if (lstrcmpi(szKey, TEXT("ServiceInf")))
{
TraceTag(ttidUpdiag, "Wrong key in the Service's section: %s", szKey);
}
else
{
// Query the Service config file
TCHAR szConfigFile[MAX_PATH];
hr = HrSetupGetStringField(ctx, 1, szConfigFile,
celems(szConfigFile), NULL);
if (S_OK ==hr)
{
// full path to the inf file is %windir%\inf\szConfigFile
TCHAR szDevConfigFileWithPath[MAX_PATH];
GetWindowsDirectory (psvc->szConfigFile, MAX_PATH);
lstrcat (psvc->szConfigFile, TEXT("\\inf\\"));
lstrcat(psvc->szConfigFile, szConfigFile);
}
}
}
}
}
}
}
SetupCloseInfFileSafe(hinf);
}
}
HRESULT HrAddAllowedValue(SST_ROW * pRow, TCHAR * szValueRange)
{
HRESULT hr = S_OK;
if(*szValueRange == '(')
{
szValueRange++;
IXMLDOMElement * pAllowedValueNode = NULL;
BSTR bstrElementName;
// we assume that ".." specifies a range, otherwise it's a comma
// separated list of allowed values
TCHAR * pChar = _tcsstr(szValueRange, TEXT(".."));
if (pChar)
{
// we have a range
TCHAR * pNextItem = szValueRange;
// BUGBUG: we should check if data type of the min, max & step
// matches the variable type
*pChar = '\0';
lstrcpy(pRow->szMin, pNextItem);
pNextItem = pChar+2;
pChar = _tcschr(pNextItem, TEXT(','));
if (pChar)
{
*pChar = '\0';
lstrcpy(pRow->szMax, pNextItem);
pNextItem = ++pChar;
pChar = _tcschr(pNextItem, TEXT(')'));
if (pChar)
{
*pChar = '\0';
lstrcpy(pRow->szStep, pNextItem);
}
else
{
TraceTag(ttidUpdiag, "HrAddAllowedValue: missing closing )");
hr = E_INVALIDARG;
}
}
else
{
TraceTag(ttidUpdiag, "HrAddAllowedValue: step not specified");
hr = E_INVALIDARG;
}
}
else
{
// we have a list of allowed values
pChar = _tcschr(szValueRange, TEXT(')'));
if (pChar)
{
*pChar = '\0';
if (lstrlen(szValueRange))
{
lstrcpy(pRow->mszAllowedValueList, szValueRange);
TCHAR * pNextItem = pRow->mszAllowedValueList;
while ((S_OK ==hr) && (pChar = _tcschr(pNextItem, TEXT(','))))
{
*pChar = '\0';
pNextItem = ++pChar;
}
// add the last one
if (*pNextItem)
{
pNextItem += lstrlen(pNextItem);
pNextItem ++;
*pNextItem = '\0';
}
else
{
TraceTag(ttidUpdiag, "HrAddAllowedValue: invalid syntax");
hr = E_INVALIDARG;
}
}
}
else
{
TraceTag(ttidUpdiag, "HrAddAllowedValue: missing closing )");
hr = E_INVALIDARG;
}
}
}
else
{
TraceTag(ttidUpdiag, "HrAddAllowedValue: missing begining (");
hr = E_INVALIDARG;
}
TraceError("HrAddAllowedValue", hr);
return hr;
}
HRESULT HrAddVariable(UPNPSVC * psvc, LPTSTR szVariableLine)
{
HRESULT hr = S_OK;
DWORD iRow = psvc->sst.cRows;
if (iRow < MAX_SST_ROWS)
{
SST_ROW * pRow = &psvc->sst.rgRows[iRow];
// fill in variable name
TCHAR szName[MAX_PATH];
if (fGetNextField(&szVariableLine, szName))
{
lstrcpy(pRow->szPropName, szName);
TCHAR szType[MAX_PATH];
fGetNextField(&szVariableLine, szType);
TCHAR szValueRange[MAX_PATH];
fGetNextField(&szVariableLine, szValueRange);
TCHAR szDefaultValue[MAX_PATH];
fGetNextField(&szVariableLine, szDefaultValue);
// fill in variable type and default value
if (*szType && *szDefaultValue)
{
VariantInit(&pRow->varValue);
pRow->varValue.vt = VT_BSTR;
WCHAR * wszDefaultValue = WszFromTsz(szDefaultValue);
V_BSTR(&pRow->varValue) = SysAllocString(wszDefaultValue);
delete wszDefaultValue;
VARTYPE vt = VT_EMPTY;
if (lstrcmpi(szType, TEXT("number")) == 0)
{
vt = VT_I4;
}
else if (lstrcmpi(szType, TEXT("string")) == 0)
{
vt = VT_BSTR;
}
else if (lstrcmpi(szType, TEXT("dateTime")) == 0)
{
vt = VT_DATE;
}
else if (lstrcmpi(szType, TEXT("boolean")) == 0)
{
vt = VT_BOOL;
}
else if (lstrcmpi(szType, TEXT("ByteBlock")) == 0)
{
vt = VT_UI1 | VT_ARRAY;
}
if (vt != VT_EMPTY)
{
hr = VariantChangeType(&pRow->varValue, &pRow->varValue, 0, vt);
}
else
{
hr = E_INVALIDARG;
TraceTag(ttidUpdiag, "HrAddVariable: invalid data type %s.", szType);
}
if (S_OK == hr)
{
// fill in value range or list of allowed values if specified
*pRow->mszAllowedValueList = '\0';
*pRow->szMin = '\0';
*pRow->szMax = '\0';
*pRow->szStep = '\0';
if (*szValueRange)
{
hr = HrAddAllowedValue(pRow, szValueRange);
}
// successfully added a new row
if (S_OK == hr)
{
psvc->sst.cRows++;
}
}
}
else
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
TraceTag(ttidUpdiag, "HrAddVariable: data type or default value missing for variable %s.",
szName);
}
}
else
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
TraceTag(ttidUpdiag, "HrAddVariable: variable name not found");
}
}
else
{
hr = E_FAIL;
TraceTag(ttidUpdiag, "HrAddVariable: max number of rows in state table exceeded");
}
TraceError("HrAddVariable", hr);
return hr;
}
// input: the state table and variable name
// output: if the variable exists in the state table
// NYI
BOOL IsValidVariable(SST * pSST, TCHAR * szVarName)
{
return TRUE;
}
// parses a line and fill in a new operation of an action
HRESULT HrAddOperation(UPNPSVC * psvc, ACTION * pAction, TCHAR * szLine)
{
HRESULT hr = S_OK;
DWORD iOperation = pAction->cOperations;
if (iOperation < MAX_OPERATIONS)
{
OPERATION_DATA * pOperation = &pAction->rgOperations[iOperation];
// get the operations's name
TCHAR * pChar = _tcschr(szLine, TEXT('('));
if (pChar)
{
*pChar ='\0';
lstrcpy(pOperation->szOpName, szLine);
szLine = ++pChar;
DWORD nArgs;
DWORD nConsts;
if (IsStandardOperation(pOperation->szOpName, &nArgs, &nConsts))
{
TraceTag(ttidUpdiag, "=== Operation %s ===", pOperation->szOpName);
// get the Variable name
if (nArgs+nConsts ==0)
{
pChar = _tcschr(szLine, TEXT(')'));
}
else
{
pChar = _tcschr(szLine, TEXT(','));
}
if (pChar)
{
*pChar = TEXT('\0');
lstrcpy(pOperation->szVariableName, szLine);
szLine = ++pChar;
if(IsValidVariable(&psvc->sst, pOperation->szVariableName))
{
TraceTag(ttidUpdiag, "Variable: %s", pOperation->szVariableName);
BOOL fError = FALSE;
// skip the arguments
while (nArgs > 0)
{
if (nArgs + nConsts == 1)
{
pChar = _tcschr(szLine, TEXT(')'));
}
else
{
pChar = _tcschr(szLine, TEXT(','));
}
if (pChar)
{
*pChar = TEXT('\0');
TraceTag(ttidUpdiag, "Argument: %s", szLine);
szLine = ++pChar;
}
else
{
fError = TRUE;
TraceTag(ttidUpdiag, "ERROR! HrAddOperation: Syntax error: missing arguments");
break;
}
nArgs--;
}
if (!fError)
{
TCHAR * pNextConst = pOperation->mszConstantList;
// now get the constants, all in string form
while (nConsts >0)
{
if (nConsts == 1)
{
pChar = _tcschr(szLine, TEXT(')'));
}
else
{
pChar = _tcschr(szLine, TEXT(','));
}
if (pChar)
{
*pChar = TEXT('\0');
TraceTag(ttidUpdiag, "Constant: %s", szLine);
lstrcpy(pNextConst, szLine);
pNextConst+=lstrlen(pNextConst);
pNextConst++;
szLine = ++pChar;
}
else
{
fError = TRUE;
TraceTag(ttidUpdiag, "ERROR! HrAddOperation: Syntax error: missing constants");
break;
}
nConsts--;
}
*pNextConst = TEXT('\0');
}
if (!fError)
{
// all is well, increment the count of operations
pAction->cOperations++;
}
}
else
{
hr = E_INVALIDARG;
TraceTag(ttidUpdiag, "ERROR! HrAddOperation: variable %s not in state table",
pOperation->szVariableName);
}
}
else
{
hr = E_INVALIDARG;
TraceTag(ttidUpdiag, "ERROR! HrAddOperation: Invalid syntax for operation: %s, affected state variable not found",
pOperation->szOpName);
}
}
else
{
hr = E_INVALIDARG;
TraceTag(ttidUpdiag, "ERROR! HrAddOperation: Invalid operation: %s", pOperation->szOpName);
}
}
}
TraceError("HrAddOperation", hr);
return hr;
}
HRESULT HrAddAction(UPNPSVC * psvc, HINF hinf, TCHAR * szActionName)
{
HRESULT hr = S_OK;
INFCONTEXT ctx;
DWORD iAction = psvc->action_set.cActions;
ACTION * pAction = &(psvc->action_set.rgActions[iAction]);
// initialize the new action
lstrcpy(pAction->szActionName, szActionName);
pAction->cOperations = 0;
// Loop over the list of operations for this action
hr = HrSetupFindFirstLine(hinf, szActionName, NULL, &ctx);
if (S_OK == hr)
{
do
{
TCHAR szKey[LINE_LEN]; // LINE_LEN defined in setupapi.h as 256
TCHAR szOpLine[LINE_LEN];
// Retrieve a line from the Action
hr = HrSetupGetStringField(ctx, 0, szKey, celems(szKey),
NULL);
if(S_OK == hr)
{
// varify this is an "Operation"
szKey[celems(szKey)-1] = L'\0';
if (lstrcmpi(szKey, TEXT("Operation")))
{
TraceTag(ttidUpdiag, "ERROR! HrAddAction: Wrong key in the Operation section: %s", szKey);
continue;
}
// Query the OperationLine
// get the line text
hr = HrSetupGetLineText(ctx, szOpLine, celems(szOpLine), NULL);
if (S_OK == hr)
{
// Add operations in this action
hr = HrAddOperation(psvc, pAction, szOpLine);
}
}
}
while (S_OK == (hr = HrSetupFindNextLine(ctx, &ctx)));
}
if (hr == S_FALSE)
{
// S_FALSE will terminate the loop successfully, so convert it to S_OK
// here.
hr = S_OK;
}
// we just successfully added a new action
if (S_OK == hr)
psvc->action_set.cActions++;
return hr;
}
HRESULT HrLoadSSTAndActionSet(UPNPSVC * psvc)
{
HRESULT hr = S_OK;
HINF hinf = NULL;
INFCONTEXT ctx;
TCHAR szKey[LINE_LEN]; // LINE_LEN defined in setupapi.h as 256
UINT unErrorLine;
hr = HrSetupOpenConfigFile(psvc->szConfigFile, &unErrorLine, &hinf);
if (S_OK == hr)
{
Assert(IsValidHandle(hinf));
// Process [StateTable] section
TraceTag(ttidUpdiag, "Reading StateTable for service %s", psvc->szId);
TCHAR szVariableLine[LINE_LEN];
hr = HrSetupFindFirstLine(hinf, TEXT("StateTable"), NULL, &ctx);
if (S_OK == hr)
{
do
{
// Retrieve a line from the StateTable section
hr = HrSetupGetStringField(ctx,0,szKey,celems(szKey),NULL);
if(S_OK == hr)
{
// varify this is a "Variable"
szKey[celems(szKey)-1] = L'\0';
if (lstrcmpi(szKey, TEXT("Variable")))
{
TraceTag(ttidUpdiag, "Wrong key in the StateTable section: %s", szKey);
continue;
}
// get the line text
hr = HrSetupGetLineText(ctx, szVariableLine, celems(szVariableLine),
NULL);
if (S_OK == hr)
{
// Add variable in this line
hr = HrAddVariable(psvc, szVariableLine);
}
}
}
while (S_OK == (hr = HrSetupFindNextLine(ctx, &ctx)));
if (hr == S_FALSE)
{
// S_FALSE will terminate the loop successfully, so convert it to S_OK
// here.
hr = S_OK;
}
}
// Process [ActionSet] section
TraceTag(ttidUpdiag, "Reading ActionSet for service %s", psvc->szId);
TCHAR szActionName[LINE_LEN];
hr = HrSetupFindFirstLine(hinf, TEXT("ActionSet"), NULL, &ctx);
if (S_OK == hr)
{
do
{
// Retrieve a line from the ActionSet section
hr = HrSetupGetStringField(ctx,0,szKey,celems(szKey),NULL);
if(S_OK == hr)
{
// varify this is an "Action"
szKey[celems(szKey)-1] = L'\0';
if (lstrcmpi(szKey, TEXT("Action")))
{
TraceTag(ttidUpdiag, "Wrong key in the ActionList section: %s", szKey);
continue;
}
// Query the ActionName
hr = HrSetupGetLineText(ctx, szActionName, celems(szActionName),
NULL);
if (S_OK == hr)
{
// Remove argument list if specified
TCHAR * pChar = _tcschr(szActionName, TEXT('('));
if (pChar)
*pChar = '\0';
// Add operations in this action
hr = HrAddAction(psvc, hinf, szActionName);
}
}
}
while (S_OK == (hr = HrSetupFindNextLine(ctx, &ctx)));
}
if (hr == S_FALSE)
{
// S_FALSE will terminate the loop successfully, so convert it to S_OK
// here.
hr = S_OK;
}
SetupCloseInfFileSafe(hinf);
}
else
{
TraceTag(ttidUpdiag, "Failed to open file %s, line = %d",
psvc->szConfigFile, unErrorLine);
}
TraceError("HrLoadSSTAndActionSet", hr);
return hr;
}
VOID AttachServiceControl(UPNPSVC *psvc)
{
// set the control ID for this service from the control url
TCHAR * pChar = _tcschr(psvc->szControlUrl, TEXT('?'));
if (pChar)
{
pChar++;
lstrcpy(psvc->szControlId, pChar);
// If it's a demo service then hook up the Demo control
for (DWORD isvc = 0; isvc < c_cDemoSvc; isvc++)
{
if (!_tcsicmp(c_rgSvc[isvc].szServiceId, psvc->szControlId))
{
psvc->psvcDemoCtl = &c_rgSvc[isvc];
TraceTag(ttidUpdiag, "Attached service demo control '%s' to '%s'.",
psvc->psvcDemoCtl->szServiceId, psvc->szServiceType);
break;
}
}
if (isvc == c_cDemoSvc)
{
TraceTag(ttidUpdiag, "No demo service control handler found for id '%s'.",
psvc->szControlId);
}
}
else
{
TraceTag(ttidUpdiag, "Control URL for '%s' doesn't have proper "
"format: %s.", psvc->szServiceType, psvc->szControlUrl);
}
}
void
FreeServiceInfo(UPNP_SERVICE_PRIVATE * pusp)
{
Assert(pusp);
CoTaskMemFree(pusp->wszServiceType);
CoTaskMemFree(pusp->wszServiceId);
CoTaskMemFree(pusp->wszControlUrl);
CoTaskMemFree(pusp->wszEventSubUrl);
CoTaskMemFree(pusp->wszScpd);
}
HRESULT HrReadServices(LPSTR szDescDocUrl, LPCTSTR szDevConfigFile,
IUPnPDevice * pud, UPNPDEV *pdev)
{
HRESULT hr = S_OK;
IUPnPDevicePrivate *pudp = NULL;
hr = pud->QueryInterface(IID_IUPnPDevicePrivate, (LPVOID *)&pudp);
if (SUCCEEDED(hr))
{
ULONG cServices;
UPNP_SERVICE_PRIVATE * rgusp;
hr = pudp->GetNumServices(&cServices);
if (SUCCEEDED(hr))
{
Assert(cServices > 0);
}
rgusp = (UPNP_SERVICE_PRIVATE *) CoTaskMemAlloc(cServices *
sizeof(UPNP_SERVICE_PRIVATE));
if (rgusp)
{
ULONG ulSvcs;
hr = pudp->GetServiceInfo(0, cServices, rgusp, &ulSvcs);
if (SUCCEEDED(hr))
{
DWORD isvc;
pdev->cSvcs = ulSvcs;
for (isvc = 0; isvc < ulSvcs; isvc++)
{
UPNPSVC * psvc = new UPNPSVC;
ZeroMemory(psvc, sizeof(UPNPSVC));
pdev->rgSvcs[isvc] = psvc;
WcharToTcharInPlace(psvc->szControlUrl, rgusp[isvc].wszControlUrl);
WcharToTcharInPlace(psvc->szEvtUrl, rgusp[isvc].wszEventSubUrl);
WcharToTcharInPlace(psvc->szScpdUrl, rgusp[isvc].wszScpd);
WcharToTcharInPlace(psvc->szServiceType, rgusp[isvc].wszServiceType);
WcharToTcharInPlace(psvc->szId, rgusp[isvc].wszServiceId);
// get the service's config file path and name
GetServiceConfigFile(pdev, psvc, szDevConfigFile);
// initialize the service state table and action list
// BUGBUG: allow services with no config file to be created
// (e.g. midi player)
HRESULT hr2;
hr2 = HrLoadSSTAndActionSet(psvc);
SSDP_MESSAGE msg = {0};
CHAR szBuffer[256];
CHAR szUdn[256];
CHAR szType[256];
Assert(pdev->szUdn);
Assert(psvc->szServiceType);
TszToSzBuf(szUdn, pdev->szUdn, celems(szUdn));
TszToSzBuf(szType, psvc->szServiceType, celems(szType));
msg.iLifeTime = 15000;
msg.szLocHeader = szDescDocUrl;
wsprintfA(szBuffer, "%s::%s", szUdn, szType);
msg.szType = szType;
msg.szUSN = szBuffer;
psvc->hSvc = RegisterService(&msg, 0);
if (psvc->hSvc && psvc->hSvc != INVALID_HANDLE_VALUE)
{
TraceTag(ttidUpdiag, "Successfully registered "
"service %s.", psvc->szServiceType);
}
else
{
TraceTag(ttidUpdiag, "Failed to register %s as "
"a service! Error = %d.",
psvc->szServiceType, GetLastError());
}
AttachServiceControl(psvc);
{
CHAR szEvtUrl[INTERNET_MAX_URL_LENGTH];
LPSTR pszEvtUrl;
Assert(psvc->szEvtUrl);
pszEvtUrl = SzFromTsz(psvc->szEvtUrl);
if (pszEvtUrl)
{
hr = HrGetRequestUriA(pszEvtUrl,
INTERNET_MAX_URL_LENGTH,
szEvtUrl);
if (SUCCEEDED(hr))
{
// convert SST to UPNP_PROPERTY
UPNP_PROPERTY * rgProps;
RgPropsFromSst(&psvc->sst, &rgProps);
if(RegisterUpnpEventSource(szEvtUrl,
psvc->sst.cRows,
rgProps))
{
TraceTag(ttidUpdiag, "Successfully registered "
"event source %s.", szEvtUrl);
}
else
{
TraceTag(ttidUpdiag, "Failed to register %s as "
"an event source! Error = %d.",
psvc->szEvtUrl, GetLastError());
}
for (DWORD iRow = 0; iRow < psvc->sst.cRows; iRow++)
{
#ifdef _UNICODE
delete(rgProps[iRow].szName);
#else
free(rgProps[iRow].szName);
#endif
delete(rgProps[iRow].szValue);
}
delete [] rgProps;
}
delete [] pszEvtUrl;
}
else
{
TraceTag(ttidUpdiag, "HrReadServices: SzFromTsz failed");
}
}
// free the strings allocated by GetServiceInfo() above
FreeServiceInfo(&(rgusp[isvc]));
}
}
CoTaskMemFree((LPVOID)rgusp);
}
ReleaseObj(pudp);
}
TraceError("HrReadServices", hr);
return hr;
}
HRESULT HrReadDevice(BOOL fRoot, LPSTR szDescDocUrl, LPCTSTR szConfigFile,
IUPnPDevice * pud)
{
HRESULT hr;
BSTR bstr;
BOOL fPop = FALSE;
hr = pud->get_FriendlyName(&bstr);
if (SUCCEEDED(hr))
{
UPNPDEV * pdev;
TraceTag(ttidUpdiag, "Loading device %S.", bstr);
pdev = new UPNPDEV;
ZeroMemory(pdev, sizeof(UPNPDEV));
WcharToTcharInPlace(pdev->szFriendlyName, bstr);
SysFreeString(bstr);
pdev->fRoot = fRoot;
pud->get_Type(&bstr);
WcharToTcharInPlace(pdev->szDeviceType, bstr);
SysFreeString(bstr);
pud->get_UniqueDeviceName(&bstr);
WcharToTcharInPlace(pdev->szUdn, bstr);
SysFreeString(bstr);
if (FRegisterDeviceAsUpnpService(szDescDocUrl, pdev))
{
if (fRoot)
{
g_params.rgCd[g_params.cCd++] = pdev;
}
else
{
PDevCur()->rgDevs[PDevCur()->cDevs++] = pdev;
}
PushDev(pdev);
fPop = TRUE;
hr = HrReadServices(szDescDocUrl, szConfigFile, pud, pdev);
}
else
{
CleanupCd(pdev);
}
}
// read in the child devices
IUPnPDevices * puds;
puds = NULL;
hr = pud->get_Children(&puds);
if (SUCCEEDED(hr))
{
Assert(puds);
IUnknown * punkEnum;
punkEnum = NULL;
hr = puds->get__NewEnum(&punkEnum);
if(SUCCEEDED(hr))
{
IEnumUnknown * peu;
peu = NULL;
hr = punkEnum->QueryInterface(IID_IEnumUnknown, (void**) &peu);
if (SUCCEEDED(hr))
{
while (S_OK == hr)
{
IUnknown * punkDevice;
punkDevice = NULL;
hr = peu->Next(1, &punkDevice, NULL);
if (S_FALSE == hr)
{
Assert(!punkDevice);
// we're done
}
else if (SUCCEEDED(hr))
{
Assert(punkDevice);
IUPnPDevice * pudChild;
pudChild = NULL;
hr = punkDevice->QueryInterface(IID_IUPnPDevice, (void**) &pudChild);
if (SUCCEEDED(hr))
{
Assert(pudChild);
hr = HrReadDevice(FALSE, szDescDocUrl, szConfigFile, pudChild);
ReleaseObj(pudChild);
}
ReleaseObj(punkDevice);
}
}
if (S_FALSE == hr)
{
hr = S_OK;
}
ReleaseObj(peu);
}
ReleaseObj(punkEnum);
}
ReleaseObj(puds);
}
if ((!fRoot) && (fPop))
{
PopDev();
}
TraceError("HrReadDevice", hr);
return hr;
}
BOOL DoNewCd(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs)
{
UPNPDEV * pdev;
UPNPSVC * psvc;
HRESULT hr = S_OK;
if ((cArgs == 3) || (cArgs ==2))
{
IUPnPDevice * pud = NULL;
IUPnPDescriptionDocument * pudd = NULL;
// This is the file name
// Load device from description doc
_tprintf(TEXT("Loading device from doc: %s...\n"), rgArgs[1]);
hr = CoCreateInstance(CLSID_UPnPDescriptionDocument,
NULL,
CLSCTX_INPROC_SERVER,
IID_IUPnPDescriptionDocument,
(LPVOID *)&pudd);
if (SUCCEEDED(hr))
{
Assert(pudd);
LPWSTR pszUrl;
pszUrl = WszFromTsz(rgArgs[1]);
if (pszUrl)
{
BSTR bstrUrl;
bstrUrl = ::SysAllocString(pszUrl);
if (bstrUrl)
{
hr = pudd->Load(bstrUrl);
if (SUCCEEDED(hr))
{
_tprintf(TEXT("Loaded %s.\n"), rgArgs[1]);
hr = pudd->RootDevice(&pud);
if (FAILED(hr))
{
TraceTag(ttidUpdiag, "IUPnPDescriptionDocument::RootDevice (URL=%S) failed, hr=%x", pszUrl, hr);
pud = NULL;
}
}
else
{
TraceTag(ttidUpdiag, "Failed to load %S. hr=%x", pszUrl, hr);
_tprintf(TEXT("Failed to load %s. hr=%d\n"), rgArgs[1], hr);
}
::SysFreeString(bstrUrl);
}
else
{
// SysAllocString failed
hr = E_OUTOFMEMORY;
}
delete [] pszUrl;
}
else
{
// WszFromTsz failed
hr = E_OUTOFMEMORY;
}
ReleaseObj(pudd);
}
else
{
_tprintf(TEXT("Could not create description doc. is upnp.dll registered?\n"));
}
if (FAILED(hr))
{
Assert(!pud);
return FALSE;
}
Assert(pud);
// read root device properties into structs
Assert(rgArgs[1]);
LPSTR pszDescDocUrl = SzFromTsz(rgArgs[1]);
if (pszDescDocUrl)
{
if (cArgs==2)
{
hr = HrReadDevice(TRUE, pszDescDocUrl, TEXT(""), pud);
}
else
{
hr = HrReadDevice(TRUE, pszDescDocUrl, rgArgs[2], pud);
}
delete [] pszDescDocUrl;
}
else
{
hr = E_OUTOFMEMORY;
}
TraceError("DoNewCd", hr);
ReleaseObj(pud);
// Can the ISAPI DLL get more than one request at the same time? If
// so we need to queue control changes to SSTs.
#ifdef NEVER
UPNP_PROPERTY * rgProps;
RgPropsFromSst(&psvc->sst, &rgProps);
if (RegisterUpnpEventSource(psvc->szEvtUrl, psvc->sst.cRows, rgProps))
{
}
else
{
TraceTag(ttidUpdiag, "Failed to register %s as an event source! Error = %d.",
psvc->szEvtUrl, GetLastError());
}
#endif
}
else
{
Usage(iCmd);
}
return FALSE;
}
BOOL DoSwitchCd(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs)
{
Assert(g_ctx.ectx == CTX_ROOT || g_ctx.ectx == CTX_CD);
if (cArgs == 2)
{
DWORD idev;
UPNPDEV * pdev;
idev = _tcstoul(rgArgs[1], NULL, 10);
if (g_ctx.ectx == CTX_CD)
{
if (idev &&
idev <= PDevCur()->cDevs &&
PDevCur()->rgDevs[idev - 1])
{
pdev = PDevCur()->rgDevs[idev - 1];
}
}
else
{
if (idev &&
idev <= g_params.cCd &&
g_params.rgCd[idev - 1])
{
pdev = g_params.rgCd[idev - 1];
}
}
if (pdev)
{
PushDev(pdev);
}
else
{
_tprintf(TEXT("%d is not a valid CD index!\n"), idev);
}
}
else
{
Usage(iCmd);
}
return FALSE;
}
BOOL DoDelCd(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs)
{
Assert(g_ctx.ectx == CTX_ROOT);
if (cArgs == 2)
{
DWORD idev;
UPNPDEV * pdev;
idev = _tcstoul(rgArgs[1], NULL, 10);
pdev = g_params.rgCd[idev - 1];
if (idev &&
idev <= g_params.cCd &&
pdev)
{
_tprintf(TEXT("Deleted device %s.\n"), pdev->szFriendlyName);
// Move last item into gap and decrement the count
g_params.rgCd[idev - 1] = g_params.rgCd[g_params.cCd - 1];
CleanupCd(pdev);
g_params.cCd--;
}
else
{
_tprintf(TEXT("%d is not a valid CD index!\n"), idev);
}
}
else
{
Usage(iCmd);
}
return FALSE;
}
BOOL DoListDevices(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs)
{
DWORD idev;
if (g_ctx.ectx == CTX_ROOT)
{
_tprintf(TEXT("Listing all Controlled Devices\n"));
_tprintf(TEXT("------------------------------\n"));
for (idev = 0; idev < g_params.cCd; idev++)
{
_tprintf(TEXT("%d) %s\n"), idev + 1, g_params.rgCd[idev]->szFriendlyName);
}
_tprintf(TEXT("------------------------------\n\n"));
}
else if (g_ctx.ectx == CTX_CD)
{
_tprintf(TEXT("Listing all sub-devices of %s\n"), PDevCur()->szFriendlyName);
_tprintf(TEXT("------------------------------\n"));
for (idev = 0; idev < PDevCur()->cDevs; idev++)
{
_tprintf(TEXT("%d) %s\n"), idev + 1, PDevCur()->rgDevs[idev]->szFriendlyName);
}
_tprintf(TEXT("------------------------------\n\n"));
}
return FALSE;
}
VOID CleanupCd(UPNPDEV *pcd)
{
DWORD i;
for (i = 0; i < pcd->cDevs; i++)
{
CleanupCd(pcd->rgDevs[i]);
}
for (i = 0; i < pcd->cSvcs; i++)
{
CleanupService(pcd->rgSvcs[i]);
}
for (i = 0; i < 3; i++)
{
if (pcd->hSvc[i] && (pcd->hSvc[i] != INVALID_HANDLE_VALUE))
{
DeregisterService(pcd->hSvc[i], TRUE);
}
}
delete pcd;
}