|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1997.
//
// File: U P D I A G . C P P
//
// Contents: UPnP Diagnostic App and CD and UCP emulator
//
// Notes:
//
// Author: danielwe 28 Oct 1999
//
//----------------------------------------------------------------------------
#include "pch.h"
#pragma hdrstop
#include "ncbase.h"
#include "updiagp.h"
#include "ncinet.h"
UPDIAG_PARAMS g_params = {0}; UPDIAG_CONTEXT g_ctx; SHARED_DATA * g_pdata = NULL; HANDLE g_hMapFile = NULL; HANDLE g_hEvent = NULL; HANDLE g_hEventRet = NULL; HANDLE g_hEventCleanup = NULL; HANDLE g_hMutex = NULL; HANDLE g_hThreadTime = NULL; FILE * g_pInputFile = NULL;
static const COMMAND c_rgCommands[] = { // Generic commands (some depend on context)
//
{TEXT("?"), TEXT("Help!"), CTX_ANY, TRUE, DoHelp, TEXT("[command]")}, {TEXT("\\"), TEXT("Go to Root context"), CTX_ANY, TRUE, DoRoot, TEXT("")}, {TEXT(".."), TEXT("Go To Previous Context"), CTX_ANY & ~CTX_ROOT, TRUE, DoBack, TEXT("")}, {TEXT("exit"), TEXT("Exit UPDIAG"), CTX_ANY, TRUE, DoExit, TEXT("")}, {TEXT("info"), TEXT("List Information"), CTX_ANY, TRUE, DoInfo, TEXT("")}, {TEXT("script"), TEXT("Run Script"), CTX_ANY, TRUE, DoScript, TEXT("<file name>")},
// Automation specific commands (wont appear in menus)
//
{TEXT("sleep"), TEXT("Sleep for time period"), CTX_AUTO, TRUE, DoSleep, TEXT("<seconds>")}, {TEXT("prompt"), TEXT("Prompt (for user input)"), CTX_AUTO, TRUE, DoPrompt, TEXT("")},
// Listing commands
//
{TEXT("ls"), TEXT("List Services"), CTX_CD | CTX_DEVICE, TRUE, DoListServices, TEXT("")}, {TEXT("ld"), TEXT("List Devices"), CTX_ROOT | CTX_DEVICE | CTX_CD, TRUE, DoListDevices, TEXT("")}, {TEXT("lucp"), TEXT("List Control Points"), CTX_ROOT, TRUE, DoListUcp, TEXT("")}, {TEXT("les"), TEXT("List Event Sources"), CTX_CD, FALSE, DoListEventSources, TEXT("")}, {TEXT("lsubs"), TEXT("List Subscriptions"), CTX_UCP, TRUE, DoListUpnpResults, TEXT("")}, {TEXT("lsres"), TEXT("List Results"), CTX_RESULT, TRUE, DoListUpnpResultMsgs, TEXT("")}, {TEXT("lsrch"), TEXT("List Searches"), CTX_UCP, TRUE, DoListUpnpResults, TEXT("")},
// Context switching commands
//
{TEXT("ucp"), TEXT("Switch To UCP Context"), CTX_ROOT, TRUE, DoSwitchUcp, TEXT("<ucp #>")}, {TEXT("srch"), TEXT("Switch To Search Context"), CTX_UCP, TRUE, DoSwitchResult, TEXT("<search #>")}, {TEXT("dev"), TEXT("Switch To Device Context"), CTX_DEVICE, TRUE, DoNothing, TEXT("")}, {TEXT("svc"), TEXT("Switch To Service Context"), CTX_DEVICE | CTX_CD, TRUE, DoSwitchSvc, TEXT("<service #>")}, {TEXT("es"), TEXT("Switch To Event Source Context"), CTX_CD, FALSE, DoSwitchEs, TEXT("<event source #>")}, {TEXT("gsubs"), TEXT("Switch To Subscription Context"), CTX_UCP, TRUE, DoSwitchResult, TEXT("<subscription #>")},
// UCP commands
//
{TEXT("newucp"), TEXT("Create New Control Point"), CTX_ROOT, TRUE, DoNewUcp, TEXT("<ucp name>")}, {TEXT("delucp"), TEXT("Delete Control Point"), CTX_ROOT, TRUE, DoDelUcp, TEXT("<ucp #>")},
// These test RegisterNotification()
{TEXT("alive"), TEXT("Register For Alive Notification"), CTX_UCP, TRUE, DoAlive, TEXT("<notif type>")}, {TEXT("subs"), TEXT("Subscribe To Service"), CTX_UCP, TRUE, DoSubscribe, TEXT("<event source URL>")},
// These test DeregisterNotification()
{TEXT("unsubs"), TEXT("Unsubscribe To Service"), CTX_UCP, TRUE, DoDelResult, TEXT("<subscription #>")}, {TEXT("unalive"),TEXT("Stop Listening For Alive"), CTX_UCP, TRUE, DoNothing, TEXT("")},
// These test FindServicesCallback()
{TEXT("newf"), TEXT("Create New Search"), CTX_UCP, TRUE, DoFindServices, TEXT("<search type>")}, {TEXT("delf"), TEXT("Delete Search"), CTX_UCP, TRUE, DoDelResult, TEXT("<search #>")},
// These test FindServices()
// --- nothing yet ---
//
// These test UPnP COM interfaces
{TEXT("doc"), TEXT("List Description Document"), CTX_DEVICE, TRUE, DoNothing, TEXT("")}, {TEXT("cmd"), TEXT("Send Command To Service"), CTX_UCP_SVC, TRUE, DoNothing, TEXT("")},
// CD commands
//
// These test RegisterService() and RegisterEventSource()
{TEXT("newcd"), TEXT("Create New CD"), CTX_ROOT, FALSE, DoNewCd, TEXT("<URL to description doc>, <Device INF file>")}, {TEXT("cd"), TEXT("Switch To CD Context"), CTX_ROOT | CTX_CD, FALSE, DoSwitchCd, TEXT("<cd #>")}, {TEXT("delcd"), TEXT("Delete CD"), CTX_ROOT, FALSE, DoDelCd, TEXT("<cd #>")},
// This tests SubmitUpnpPropertyEvent()
{TEXT("evt"), TEXT("Submit An Event"), CTX_EVTSRC, FALSE, DoSubmitEvent, TEXT("{property#:newValue} [...]")}, {TEXT("props"), TEXT("List Event Source Properties"), CTX_EVTSRC, FALSE, DoListProps, TEXT("")},
// Dump StateTable and action set of a service
{TEXT("sst"), TEXT("Print the service state table"), CTX_CD_SVC, FALSE, DoPrintSST, TEXT("")}, {TEXT("actions"), TEXT("Print the service action set"), CTX_CD_SVC, FALSE, DoPrintActionSet, TEXT("")}, };
static const DWORD c_cCmd = celems(c_rgCommands);
BOOL FIsMillenium() { OSVERSIONINFO osvi = {0};
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
return (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); }
VOID Usage(DWORD iCmd) { _tprintf(TEXT("%s - %s\nUsage: \n %s %s\n\n"), c_rgCommands[iCmd].szCommand, c_rgCommands[iCmd].szCmdDesc, c_rgCommands[iCmd].szCommand, c_rgCommands[iCmd].szUsage); }
BOOL FCmdFromName(LPCTSTR szName, DWORD *piCmd) { DWORD iCmd;
*piCmd = 0xFFFFFFFF;
for (iCmd = 0; iCmd < c_cCmd; iCmd++) { if (!_tcsicmp(szName, c_rgCommands[iCmd].szCommand)) { *piCmd = iCmd; return TRUE; } }
return FALSE; }
BOOL DoHelp(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs) { LPTSTR szName;
if (cArgs == 2) { if (FCmdFromName(rgArgs[1], &iCmd)) { Usage(iCmd); return FALSE; } }
_tprintf(TEXT("Available commands:\n")); _tprintf(TEXT("-------------------\n")); for (iCmd = 0; iCmd < c_cCmd; iCmd++) { if ((c_rgCommands[iCmd].dwCtx & g_ctx.ectx) && (((FIsMillenium() && c_rgCommands[iCmd].fValidOnMillen)) || (!FIsMillenium())))
{ _tprintf(TEXT("%-7s - %s\n"), c_rgCommands[iCmd].szCommand, c_rgCommands[iCmd].szCmdDesc); } }
_tprintf(TEXT("-------------------\n\n"));
return FALSE; }
BOOL DoInfo(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs) { if (cArgs == 1) { switch (g_ctx.ectx) { case CTX_EVTSRC: DoEvtSrcInfo(); break; } } else { Usage(iCmd); }
return FALSE; }
BOOL DoSleep(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs) { if (cArgs == 2) { DWORD iSeconds = _tcstoul(rgArgs[1], NULL, 10);
_tprintf(TEXT("Sleeping for %d second(s).\n\n"), iSeconds); Sleep(iSeconds * 1000); } else { Usage(iCmd); }
return FALSE; }
BOOL DoPrompt(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs) { TCHAR szBuf[10]; // Size doesn't matter (uh, not here anyway)
_tprintf(TEXT("Press [Enter] to continue\n"));
_fgetts(szBuf, sizeof(szBuf), stdin);
return FALSE; }
BOOL DoScript(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs) { if (cArgs == 2) { if (g_pInputFile) { _tprintf(TEXT("Error. Already in script. Go away.\n\n")); } else { g_pInputFile = _tfopen(rgArgs[1], TEXT("r")); if (!g_pInputFile) { _tprintf(TEXT("Failed to open input file. Reverting to user-input.\n\n")); } else { _tprintf(TEXT("Running in scripted mode with file: %S\n\n"), rgArgs[1]); } } } else { Usage(iCmd); }
return FALSE; }
BOOL DoNothing(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs) { _tprintf(TEXT("Not yet implemented.\n\n")); return FALSE; }
BOOL DoRoot(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs) { g_ctx.ectx = CTX_ROOT; g_ctx.idevStackIndex = 0;
return FALSE; }
BOOL DoBack(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs) { switch (g_ctx.ectx) { case CTX_UCP: g_ctx.ectx = CTX_ROOT; break;
case CTX_CD: PopDev(); if (!g_ctx.idevStackIndex) { // Go back to root context if no more devs on stack
g_ctx.ectx = CTX_ROOT; } break;
case CTX_RESULT: g_ctx.ectx = CTX_UCP; break;
case CTX_CD_SVC: case CTX_EVTSRC: g_ctx.ectx = CTX_CD; break; } return FALSE; }
VOID Cleanup() { DWORD i;
SetEvent(g_hEventCleanup); TraceTag(ttidUpdiag, "Waiting for time thread to exit"); WaitForSingleObject(g_hThreadTime, INFINITE);
for (i = 0; i < g_params.cCd; i++) { CleanupCd(g_params.rgCd[i]); }
for (i = 0; i < g_params.cUcp; i++) { CleanupUcp(g_params.rgUcp[i]); }
UnmapViewOfFile((LPVOID)g_pdata); CloseHandle(g_hMapFile); CloseHandle(g_hEvent); CloseHandle(g_hEventRet); CloseHandle(g_hEventCleanup); CloseHandle(g_hMutex);
CoUninitialize(); UnInitializeNcInet(); SsdpCleanup(); }
BOOL DoExit(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs) { if (cArgs == 1) { Cleanup(); return TRUE; }
return FALSE; }
BOOL ParseCommand(LPTSTR szCommand) { DWORD iCmd; LPTSTR szTemp;
// eat leading spaces
while (*szCommand == ' ') { szCommand++; }
szTemp = _tcstok(szCommand, c_szSeps); if (szTemp && *szTemp && (*szTemp != ';')) { for (iCmd = 0; iCmd < c_cCmd; iCmd++) { if (!lstrcmpi(szCommand, c_rgCommands[iCmd].szCommand) && (((FIsMillenium() && c_rgCommands[iCmd].fValidOnMillen)) || (!FIsMillenium()))) { if (c_rgCommands[iCmd].dwCtx & (g_ctx.ectx | CTX_AUTO)) { DWORD iArg = 0; LPTSTR argv[MAX_ARGS];
ZeroMemory(&argv, sizeof(argv)); while (szTemp && iArg < MAX_ARGS) { argv[iArg++] = szTemp; szTemp = _tcstok(NULL, c_szSeps); }
return c_rgCommands[iCmd].pfnCommand(iCmd, iArg, argv); } else { _tprintf(TEXT("'%s' is not valid in this context.\n\n"), _tcstok(szCommand, TEXT(" \r\n\t"))); return FALSE; } } }
_tprintf(TEXT("Unknown command: '%s'.\n\n"), _tcstok(szCommand, TEXT(" \r\n\t"))); }
return FALSE; }
BOOL FInit() { HANDLE hThread; SECURITY_ATTRIBUTES sa = {0}; SECURITY_DESCRIPTOR sd = {0}; HRESULT hr = S_OK;
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (FAILED(hr)) { TraceError("FInit", hr); return FALSE; }
InitializeDebugging(); if (!SsdpStartup()) { TraceTag(ttidUpdiag, "SsdpStartup failed! Error %d.", GetLastError()); return FALSE; }
InitializeNcInet();
if (FIsMillenium()) { // Don't need to do any more
return TRUE; }
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = FALSE; sa.lpSecurityDescriptor = &sd;
g_hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, sizeof(SHARED_DATA), c_szSharedData);
if (!g_hMapFile) { TraceTag(ttidUpdiag, "Could not create shared memory! Error %d.", GetLastError()); return FALSE; }
TraceTag(ttidUpdiag, "Created file mapping '%s'.", c_szSharedData);
g_pdata = (SHARED_DATA *)MapViewOfFile(g_hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0); if (!g_pdata) { TraceTag(ttidUpdiag, "Could not map shared memory! Error %d.", GetLastError()); return FALSE; }
ZeroMemory(g_pdata, sizeof(SHARED_DATA));
g_hEvent = CreateEvent(&sa, FALSE, FALSE, c_szSharedEvent); if (!g_hEvent) { TraceTag(ttidUpdiag, "Could not create %s! Error %d.", c_szSharedEvent, GetLastError()); return FALSE; }
TraceTag(ttidUpdiag, "Created %s event.", c_szSharedEvent);
g_hEventRet = CreateEvent(&sa, FALSE, FALSE, c_szSharedEventRet); if (!g_hEventRet) { TraceTag(ttidUpdiag, "Could not create %s! Error %d.", c_szSharedEventRet, GetLastError()); return FALSE; }
TraceTag(ttidUpdiag, "Created %s event.", c_szSharedEventRet);
g_hEventCleanup = CreateEvent(NULL, FALSE, FALSE, NULL); if (!g_hEventCleanup) { TraceTag(ttidUpdiag, "Could not create cleanup event! Error %d.", GetLastError()); return FALSE; }
TraceTag(ttidUpdiag, "Created %s event.", c_szSharedEventRet);
DWORD dwThreadId; hThread = CreateThread(NULL, 0, RequestHandlerThreadStart, NULL, 0, &dwThreadId); if (!hThread) { TraceTag(ttidUpdiag, "Could not create request handler thread! Error %d.", GetLastError()); return FALSE; }
TraceTag(ttidUpdiag, "Created shared thread.");
g_hMutex = CreateMutex(&sa, FALSE, c_szSharedMutex); if (!g_hMutex) { TraceTag(ttidUpdiag, "Could not create shared mutex! Error %d.", GetLastError()); return FALSE; }
TraceTag(ttidUpdiag, "Created mutex.");
return TRUE; }
VOID Prompt(LPTSTR szRoot, LPTSTR szParam) { const DWORD c_cchMax = 60; TCHAR szPrompt[81];
if (szParam && *szParam) { if (_tcslen(szParam) >= c_cchMax) { TCHAR szTemp[c_cchMax + 1] = {0};
lstrcpyn(szTemp, szParam, c_cchMax);
wsprintf(szPrompt, TEXT("%s: %s...>"), szRoot, szTemp); } else { wsprintf(szPrompt, TEXT("%s: %s>"), szRoot, szParam); } } else { wsprintf(szPrompt, TEXT("%s>"), szRoot); }
_fputts(szPrompt, stdout); }
BOOL FIsLineAllWhitespace(LPTSTR szBuf) { INT iLen = _tcslen(szBuf);
for (INT iLoop = 0; iLoop < iLen; iLoop++) { if (!_istspace(szBuf[iLoop])) { return FALSE;
} }
return TRUE; }
VOID PrintCommandPrompt() { switch (g_ctx.ectx) { case CTX_ROOT: Prompt(TEXT("UPDIAG"), NULL); break; case CTX_CD: Prompt(TEXT("CD"), PDevCur()->szFriendlyName); break; case CTX_RESULT: switch (g_ctx.presCur->resType) { case RES_FIND: Prompt(TEXT("SRCH"), g_ctx.presCur->szType); break; case RES_NOTIFY: Prompt(TEXT("NOTIFY"), g_ctx.presCur->szType); break; case RES_SUBS: Prompt(TEXT("SUBS"), g_ctx.presCur->szType); break; default: Prompt(TEXT("BUG!"), g_ctx.presCur->szType); break; } break; case CTX_CD_SVC: Prompt(TEXT("CDSVC"), g_ctx.psvcCur->szSti); break; case CTX_EVTSRC: Prompt(TEXT("ES"), g_ctx.psvcCur->szEvtUrl); break; case CTX_UCP: Prompt(TEXT("UCP"), g_ctx.pucpCur->szName); break; default: Prompt(TEXT("UNKNOWN"), NULL); break; } }
EXTERN_C VOID __cdecl wmain ( IN INT argc, IN PCWSTR argv[]) { TCHAR szBuf[MAX_PATH]; BOOL fDone = FALSE;
if (!FInit()) { return; }
// Check for presence of input file arg. If there, init file input.
//
if (argc > 1) { CHAR szFileName[MAX_PATH];
WszToSzBuf(szFileName, argv[1], MAX_PATH); g_pInputFile = fopen(szFileName, "r"); if (!g_pInputFile) { _tprintf(TEXT("Failed to open input file. Reverting to user-input.\n\n")); } else { _tprintf(TEXT("Running in scripted mode with file: %S\n\n"), argv[1]); } }
g_ctx.ectx = CTX_ROOT;
while (!fDone) { // If we're running from an input file, continue.
//
if (g_pInputFile) { // If there was an error reading the file
//
if (!_fgetts(szBuf, sizeof(szBuf), g_pInputFile)) { // If it wasn't eof, print an error
//
if (!feof(g_pInputFile)) { _tprintf(TEXT("\nFailure reading script file\n\n")); } else { _tprintf(TEXT("\n[Script complete]\n\n")); }
// regardless, close the file and NULL the handle
//
fclose(g_pInputFile); g_pInputFile = NULL; } else { if (!FIsLineAllWhitespace(szBuf)) { PrintCommandPrompt(); _tprintf(TEXT("%s\n"), szBuf); fDone = ParseCommand(szBuf); } } } else { PrintCommandPrompt(); _fgetts(szBuf, sizeof(szBuf), stdin);
// Print nice separator so we can distinguish between commands and output
//
_tprintf(TEXT("\n"));
fDone = ParseCommand(szBuf); } }
// Print nice terminating separator
//
_tprintf(TEXT("\n"));
if (g_pInputFile) { fclose(g_pInputFile); }
SsdpCleanup(); }
// Copy this from the SSDP implemenation so that BoundsChecker doesn't get
// upset about mismatching new with free.
//
VOID LocalFreeSsdpMessage(PSSDP_MESSAGE pSsdpMessage) { delete pSsdpMessage->szAltHeaders; delete pSsdpMessage->szContent; delete pSsdpMessage->szLocHeader; delete pSsdpMessage->szType; delete pSsdpMessage->szUSN; delete pSsdpMessage->szSid; delete pSsdpMessage; }
// stolen from ssdp\client\message.cpp
BOOL CopySsdpMessage(PSSDP_MESSAGE pDestination, CONST SSDP_MESSAGE * pSource) { pDestination->szLocHeader = NULL; pDestination->szAltHeaders = NULL; pDestination->szType = NULL; pDestination->szUSN = NULL; pDestination->szSid = NULL; pDestination->szContent = NULL; pDestination->iLifeTime = 0;
if (pSource->szType != NULL) { pDestination->szType = new CHAR [strlen(pSource->szType) + 1]; if (pDestination->szType == NULL) { goto cleanup; } else { strcpy(pDestination->szType, pSource->szType); } }
if (pSource->szLocHeader != NULL) { pDestination->szLocHeader = new CHAR [strlen(pSource->szLocHeader) + 1]; if (pDestination->szLocHeader == NULL) { goto cleanup; } else { strcpy(pDestination->szLocHeader, pSource->szLocHeader); } }
if (pSource->szAltHeaders != NULL) { pDestination->szAltHeaders = new CHAR [strlen(pSource->szAltHeaders) + 1]; if (pDestination->szAltHeaders == NULL) { goto cleanup; } else { strcpy(pDestination->szAltHeaders, pSource->szAltHeaders); } }
if (pSource->szUSN != NULL) { pDestination->szUSN = new CHAR [strlen(pSource->szUSN) + 1]; if (pDestination->szUSN == NULL) { goto cleanup; } else { strcpy(pDestination->szUSN, pSource->szUSN); } }
if (pSource->szSid != NULL) { pDestination->szSid = new CHAR [strlen(pSource->szSid) + 1]; if (pDestination->szSid == NULL) { goto cleanup; } else { strcpy(pDestination->szSid, pSource->szSid); } }
pDestination->iLifeTime = pSource->iLifeTime; pDestination->iSeq = pSource->iSeq;
return TRUE;
cleanup: LocalFreeSsdpMessage(pDestination);
return FALSE; }
VOID NotifyCallback(SSDP_CALLBACK_TYPE ct, CONST SSDP_MESSAGE *pSsdpService, LPVOID pContext) { UPNPRESULT * pres = (UPNPRESULT *)pContext;
Assert(pres);
switch (ct) { case SSDP_DONE: break;
case SSDP_BYEBYE: case SSDP_ALIVE: case SSDP_FOUND: case SSDP_EVENT: if (pres->cResult < MAX_RESULT_MSGS) { SSDP_MESSAGE * pSsdpMsgCopy;
pSsdpMsgCopy = new SSDP_MESSAGE; if (pSsdpMsgCopy) { BOOL fResult;
fResult = CopySsdpMessage(pSsdpMsgCopy, pSsdpService); if (fResult) { // Overload iSeq in the case of alive or byebye so we know
// which is which
if (ct == SSDP_BYEBYE) { pSsdpMsgCopy->iSeq = 1; } else if (ct == SSDP_ALIVE) { pSsdpMsgCopy->iSeq = 2; } else if (ct == SSDP_FOUND) { pSsdpMsgCopy->iSeq = 0; }
pres->rgmsgResult[pres->cResult++] = pSsdpMsgCopy; } // else, CopySsdpMessage frees pSsdpMsgCopy
} } break; default: TraceTag(ttidUpdiag, "Unknown callback type %d!", ct); break; } }
|