1438 lines
43 KiB
C++
1438 lines
43 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
|