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.

844 lines
24 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997.
  5. //
  6. // File: U P D I A G . C P P
  7. //
  8. // Contents: UPnP Diagnostic App and CD and UCP emulator
  9. //
  10. // Notes:
  11. //
  12. // Author: danielwe 28 Oct 1999
  13. //
  14. //----------------------------------------------------------------------------
  15. #include "pch.h"
  16. #pragma hdrstop
  17. #include "ncbase.h"
  18. #include "updiagp.h"
  19. #include "ncinet.h"
  20. UPDIAG_PARAMS g_params = {0};
  21. UPDIAG_CONTEXT g_ctx;
  22. SHARED_DATA * g_pdata = NULL;
  23. HANDLE g_hMapFile = NULL;
  24. HANDLE g_hEvent = NULL;
  25. HANDLE g_hEventRet = NULL;
  26. HANDLE g_hEventCleanup = NULL;
  27. HANDLE g_hMutex = NULL;
  28. HANDLE g_hThreadTime = NULL;
  29. FILE * g_pInputFile = NULL;
  30. static const COMMAND c_rgCommands[] =
  31. {
  32. // Generic commands (some depend on context)
  33. //
  34. {TEXT("?"), TEXT("Help!"), CTX_ANY, TRUE, DoHelp, TEXT("[command]")},
  35. {TEXT("\\"), TEXT("Go to Root context"), CTX_ANY, TRUE, DoRoot, TEXT("")},
  36. {TEXT(".."), TEXT("Go To Previous Context"), CTX_ANY & ~CTX_ROOT, TRUE, DoBack, TEXT("")},
  37. {TEXT("exit"), TEXT("Exit UPDIAG"), CTX_ANY, TRUE, DoExit, TEXT("")},
  38. {TEXT("info"), TEXT("List Information"), CTX_ANY, TRUE, DoInfo, TEXT("")},
  39. {TEXT("script"), TEXT("Run Script"), CTX_ANY, TRUE, DoScript, TEXT("<file name>")},
  40. // Automation specific commands (wont appear in menus)
  41. //
  42. {TEXT("sleep"), TEXT("Sleep for time period"), CTX_AUTO, TRUE, DoSleep, TEXT("<seconds>")},
  43. {TEXT("prompt"), TEXT("Prompt (for user input)"), CTX_AUTO, TRUE, DoPrompt, TEXT("")},
  44. // Listing commands
  45. //
  46. {TEXT("ls"), TEXT("List Services"), CTX_CD | CTX_DEVICE, TRUE, DoListServices, TEXT("")},
  47. {TEXT("ld"), TEXT("List Devices"), CTX_ROOT | CTX_DEVICE | CTX_CD, TRUE, DoListDevices, TEXT("")},
  48. {TEXT("lucp"), TEXT("List Control Points"), CTX_ROOT, TRUE, DoListUcp, TEXT("")},
  49. {TEXT("les"), TEXT("List Event Sources"), CTX_CD, FALSE, DoListEventSources, TEXT("")},
  50. {TEXT("lsubs"), TEXT("List Subscriptions"), CTX_UCP, TRUE, DoListUpnpResults, TEXT("")},
  51. {TEXT("lsres"), TEXT("List Results"), CTX_RESULT, TRUE, DoListUpnpResultMsgs, TEXT("")},
  52. {TEXT("lsrch"), TEXT("List Searches"), CTX_UCP, TRUE, DoListUpnpResults, TEXT("")},
  53. // Context switching commands
  54. //
  55. {TEXT("ucp"), TEXT("Switch To UCP Context"), CTX_ROOT, TRUE, DoSwitchUcp, TEXT("<ucp #>")},
  56. {TEXT("srch"), TEXT("Switch To Search Context"), CTX_UCP, TRUE, DoSwitchResult, TEXT("<search #>")},
  57. {TEXT("dev"), TEXT("Switch To Device Context"), CTX_DEVICE, TRUE, DoNothing, TEXT("")},
  58. {TEXT("svc"), TEXT("Switch To Service Context"), CTX_DEVICE | CTX_CD, TRUE, DoSwitchSvc, TEXT("<service #>")},
  59. {TEXT("es"), TEXT("Switch To Event Source Context"), CTX_CD, FALSE, DoSwitchEs, TEXT("<event source #>")},
  60. {TEXT("gsubs"), TEXT("Switch To Subscription Context"), CTX_UCP, TRUE, DoSwitchResult, TEXT("<subscription #>")},
  61. // UCP commands
  62. //
  63. {TEXT("newucp"), TEXT("Create New Control Point"), CTX_ROOT, TRUE, DoNewUcp, TEXT("<ucp name>")},
  64. {TEXT("delucp"), TEXT("Delete Control Point"), CTX_ROOT, TRUE, DoDelUcp, TEXT("<ucp #>")},
  65. // These test RegisterNotification()
  66. {TEXT("alive"), TEXT("Register For Alive Notification"), CTX_UCP, TRUE, DoAlive, TEXT("<notif type>")},
  67. {TEXT("subs"), TEXT("Subscribe To Service"), CTX_UCP, TRUE, DoSubscribe, TEXT("<event source URL>")},
  68. // These test DeregisterNotification()
  69. {TEXT("unsubs"), TEXT("Unsubscribe To Service"), CTX_UCP, TRUE, DoDelResult, TEXT("<subscription #>")},
  70. {TEXT("unalive"),TEXT("Stop Listening For Alive"), CTX_UCP, TRUE, DoNothing, TEXT("")},
  71. // These test FindServicesCallback()
  72. {TEXT("newf"), TEXT("Create New Search"), CTX_UCP, TRUE, DoFindServices, TEXT("<search type>")},
  73. {TEXT("delf"), TEXT("Delete Search"), CTX_UCP, TRUE, DoDelResult, TEXT("<search #>")},
  74. // These test FindServices()
  75. // --- nothing yet ---
  76. //
  77. // These test UPnP COM interfaces
  78. {TEXT("doc"), TEXT("List Description Document"), CTX_DEVICE, TRUE, DoNothing, TEXT("")},
  79. {TEXT("cmd"), TEXT("Send Command To Service"), CTX_UCP_SVC, TRUE, DoNothing, TEXT("")},
  80. // CD commands
  81. //
  82. // These test RegisterService() and RegisterEventSource()
  83. {TEXT("newcd"), TEXT("Create New CD"), CTX_ROOT, FALSE, DoNewCd, TEXT("<URL to description doc>, <Device INF file>")},
  84. {TEXT("cd"), TEXT("Switch To CD Context"), CTX_ROOT | CTX_CD, FALSE, DoSwitchCd, TEXT("<cd #>")},
  85. {TEXT("delcd"), TEXT("Delete CD"), CTX_ROOT, FALSE, DoDelCd, TEXT("<cd #>")},
  86. // This tests SubmitUpnpPropertyEvent()
  87. {TEXT("evt"), TEXT("Submit An Event"), CTX_EVTSRC, FALSE, DoSubmitEvent, TEXT("{property#:newValue} [...]")},
  88. {TEXT("props"), TEXT("List Event Source Properties"), CTX_EVTSRC, FALSE, DoListProps, TEXT("")},
  89. // Dump StateTable and action set of a service
  90. {TEXT("sst"), TEXT("Print the service state table"), CTX_CD_SVC, FALSE, DoPrintSST, TEXT("")},
  91. {TEXT("actions"), TEXT("Print the service action set"), CTX_CD_SVC, FALSE, DoPrintActionSet, TEXT("")},
  92. };
  93. static const DWORD c_cCmd = celems(c_rgCommands);
  94. BOOL FIsMillenium()
  95. {
  96. OSVERSIONINFO osvi = {0};
  97. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  98. GetVersionEx(&osvi);
  99. return (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
  100. }
  101. VOID Usage(DWORD iCmd)
  102. {
  103. _tprintf(TEXT("%s - %s\nUsage: \n %s %s\n\n"), c_rgCommands[iCmd].szCommand,
  104. c_rgCommands[iCmd].szCmdDesc, c_rgCommands[iCmd].szCommand,
  105. c_rgCommands[iCmd].szUsage);
  106. }
  107. BOOL FCmdFromName(LPCTSTR szName, DWORD *piCmd)
  108. {
  109. DWORD iCmd;
  110. *piCmd = 0xFFFFFFFF;
  111. for (iCmd = 0; iCmd < c_cCmd; iCmd++)
  112. {
  113. if (!_tcsicmp(szName, c_rgCommands[iCmd].szCommand))
  114. {
  115. *piCmd = iCmd;
  116. return TRUE;
  117. }
  118. }
  119. return FALSE;
  120. }
  121. BOOL DoHelp(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs)
  122. {
  123. LPTSTR szName;
  124. if (cArgs == 2)
  125. {
  126. if (FCmdFromName(rgArgs[1], &iCmd))
  127. {
  128. Usage(iCmd);
  129. return FALSE;
  130. }
  131. }
  132. _tprintf(TEXT("Available commands:\n"));
  133. _tprintf(TEXT("-------------------\n"));
  134. for (iCmd = 0; iCmd < c_cCmd; iCmd++)
  135. {
  136. if ((c_rgCommands[iCmd].dwCtx & g_ctx.ectx) &&
  137. (((FIsMillenium() && c_rgCommands[iCmd].fValidOnMillen)) ||
  138. (!FIsMillenium())))
  139. {
  140. _tprintf(TEXT("%-7s - %s\n"), c_rgCommands[iCmd].szCommand,
  141. c_rgCommands[iCmd].szCmdDesc);
  142. }
  143. }
  144. _tprintf(TEXT("-------------------\n\n"));
  145. return FALSE;
  146. }
  147. BOOL DoInfo(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs)
  148. {
  149. if (cArgs == 1)
  150. {
  151. switch (g_ctx.ectx)
  152. {
  153. case CTX_EVTSRC:
  154. DoEvtSrcInfo();
  155. break;
  156. }
  157. }
  158. else
  159. {
  160. Usage(iCmd);
  161. }
  162. return FALSE;
  163. }
  164. BOOL DoSleep(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs)
  165. {
  166. if (cArgs == 2)
  167. {
  168. DWORD iSeconds = _tcstoul(rgArgs[1], NULL, 10);
  169. _tprintf(TEXT("Sleeping for %d second(s).\n\n"), iSeconds);
  170. Sleep(iSeconds * 1000);
  171. }
  172. else
  173. {
  174. Usage(iCmd);
  175. }
  176. return FALSE;
  177. }
  178. BOOL DoPrompt(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs)
  179. {
  180. TCHAR szBuf[10]; // Size doesn't matter (uh, not here anyway)
  181. _tprintf(TEXT("Press [Enter] to continue\n"));
  182. _fgetts(szBuf, sizeof(szBuf), stdin);
  183. return FALSE;
  184. }
  185. BOOL DoScript(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs)
  186. {
  187. if (cArgs == 2)
  188. {
  189. if (g_pInputFile)
  190. {
  191. _tprintf(TEXT("Error. Already in script. Go away.\n\n"));
  192. }
  193. else
  194. {
  195. g_pInputFile = _tfopen(rgArgs[1], TEXT("r"));
  196. if (!g_pInputFile)
  197. {
  198. _tprintf(TEXT("Failed to open input file. Reverting to user-input.\n\n"));
  199. }
  200. else
  201. {
  202. _tprintf(TEXT("Running in scripted mode with file: %S\n\n"), rgArgs[1]);
  203. }
  204. }
  205. }
  206. else
  207. {
  208. Usage(iCmd);
  209. }
  210. return FALSE;
  211. }
  212. BOOL DoNothing(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs)
  213. {
  214. _tprintf(TEXT("Not yet implemented.\n\n"));
  215. return FALSE;
  216. }
  217. BOOL DoRoot(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs)
  218. {
  219. g_ctx.ectx = CTX_ROOT;
  220. g_ctx.idevStackIndex = 0;
  221. return FALSE;
  222. }
  223. BOOL DoBack(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs)
  224. {
  225. switch (g_ctx.ectx)
  226. {
  227. case CTX_UCP:
  228. g_ctx.ectx = CTX_ROOT;
  229. break;
  230. case CTX_CD:
  231. PopDev();
  232. if (!g_ctx.idevStackIndex)
  233. {
  234. // Go back to root context if no more devs on stack
  235. g_ctx.ectx = CTX_ROOT;
  236. }
  237. break;
  238. case CTX_RESULT:
  239. g_ctx.ectx = CTX_UCP;
  240. break;
  241. case CTX_CD_SVC:
  242. case CTX_EVTSRC:
  243. g_ctx.ectx = CTX_CD;
  244. break;
  245. }
  246. return FALSE;
  247. }
  248. VOID Cleanup()
  249. {
  250. DWORD i;
  251. SetEvent(g_hEventCleanup);
  252. TraceTag(ttidUpdiag, "Waiting for time thread to exit");
  253. WaitForSingleObject(g_hThreadTime, INFINITE);
  254. for (i = 0; i < g_params.cCd; i++)
  255. {
  256. CleanupCd(g_params.rgCd[i]);
  257. }
  258. for (i = 0; i < g_params.cUcp; i++)
  259. {
  260. CleanupUcp(g_params.rgUcp[i]);
  261. }
  262. UnmapViewOfFile((LPVOID)g_pdata);
  263. CloseHandle(g_hMapFile);
  264. CloseHandle(g_hEvent);
  265. CloseHandle(g_hEventRet);
  266. CloseHandle(g_hEventCleanup);
  267. CloseHandle(g_hMutex);
  268. CoUninitialize();
  269. UnInitializeNcInet();
  270. SsdpCleanup();
  271. }
  272. BOOL DoExit(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs)
  273. {
  274. if (cArgs == 1)
  275. {
  276. Cleanup();
  277. return TRUE;
  278. }
  279. return FALSE;
  280. }
  281. BOOL ParseCommand(LPTSTR szCommand)
  282. {
  283. DWORD iCmd;
  284. LPTSTR szTemp;
  285. // eat leading spaces
  286. while (*szCommand == ' ')
  287. {
  288. szCommand++;
  289. }
  290. szTemp = _tcstok(szCommand, c_szSeps);
  291. if (szTemp && *szTemp && (*szTemp != ';'))
  292. {
  293. for (iCmd = 0; iCmd < c_cCmd; iCmd++)
  294. {
  295. if (!lstrcmpi(szCommand, c_rgCommands[iCmd].szCommand) &&
  296. (((FIsMillenium() && c_rgCommands[iCmd].fValidOnMillen)) ||
  297. (!FIsMillenium())))
  298. {
  299. if (c_rgCommands[iCmd].dwCtx & (g_ctx.ectx | CTX_AUTO))
  300. {
  301. DWORD iArg = 0;
  302. LPTSTR argv[MAX_ARGS];
  303. ZeroMemory(&argv, sizeof(argv));
  304. while (szTemp && iArg < MAX_ARGS)
  305. {
  306. argv[iArg++] = szTemp;
  307. szTemp = _tcstok(NULL, c_szSeps);
  308. }
  309. return c_rgCommands[iCmd].pfnCommand(iCmd, iArg, argv);
  310. }
  311. else
  312. {
  313. _tprintf(TEXT("'%s' is not valid in this context.\n\n"),
  314. _tcstok(szCommand, TEXT(" \r\n\t")));
  315. return FALSE;
  316. }
  317. }
  318. }
  319. _tprintf(TEXT("Unknown command: '%s'.\n\n"), _tcstok(szCommand, TEXT(" \r\n\t")));
  320. }
  321. return FALSE;
  322. }
  323. BOOL FInit()
  324. {
  325. HANDLE hThread;
  326. SECURITY_ATTRIBUTES sa = {0};
  327. SECURITY_DESCRIPTOR sd = {0};
  328. HRESULT hr = S_OK;
  329. hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  330. if (FAILED(hr))
  331. {
  332. TraceError("FInit", hr);
  333. return FALSE;
  334. }
  335. InitializeDebugging();
  336. if (!SsdpStartup())
  337. {
  338. TraceTag(ttidUpdiag, "SsdpStartup failed! Error %d.",
  339. GetLastError());
  340. return FALSE;
  341. }
  342. InitializeNcInet();
  343. if (FIsMillenium())
  344. {
  345. // Don't need to do any more
  346. return TRUE;
  347. }
  348. InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
  349. SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
  350. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  351. sa.bInheritHandle = FALSE;
  352. sa.lpSecurityDescriptor = &sd;
  353. g_hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE,
  354. 0, sizeof(SHARED_DATA), c_szSharedData);
  355. if (!g_hMapFile)
  356. {
  357. TraceTag(ttidUpdiag, "Could not create shared memory! Error %d.",
  358. GetLastError());
  359. return FALSE;
  360. }
  361. TraceTag(ttidUpdiag, "Created file mapping '%s'.", c_szSharedData);
  362. g_pdata = (SHARED_DATA *)MapViewOfFile(g_hMapFile, FILE_MAP_ALL_ACCESS,
  363. 0, 0, 0);
  364. if (!g_pdata)
  365. {
  366. TraceTag(ttidUpdiag, "Could not map shared memory! Error %d.", GetLastError());
  367. return FALSE;
  368. }
  369. ZeroMemory(g_pdata, sizeof(SHARED_DATA));
  370. g_hEvent = CreateEvent(&sa, FALSE, FALSE, c_szSharedEvent);
  371. if (!g_hEvent)
  372. {
  373. TraceTag(ttidUpdiag, "Could not create %s! Error %d.",
  374. c_szSharedEvent, GetLastError());
  375. return FALSE;
  376. }
  377. TraceTag(ttidUpdiag, "Created %s event.", c_szSharedEvent);
  378. g_hEventRet = CreateEvent(&sa, FALSE, FALSE, c_szSharedEventRet);
  379. if (!g_hEventRet)
  380. {
  381. TraceTag(ttidUpdiag, "Could not create %s! Error %d.",
  382. c_szSharedEventRet, GetLastError());
  383. return FALSE;
  384. }
  385. TraceTag(ttidUpdiag, "Created %s event.", c_szSharedEventRet);
  386. g_hEventCleanup = CreateEvent(NULL, FALSE, FALSE, NULL);
  387. if (!g_hEventCleanup)
  388. {
  389. TraceTag(ttidUpdiag, "Could not create cleanup event! Error %d.",
  390. GetLastError());
  391. return FALSE;
  392. }
  393. TraceTag(ttidUpdiag, "Created %s event.", c_szSharedEventRet);
  394. DWORD dwThreadId;
  395. hThread = CreateThread(NULL, 0, RequestHandlerThreadStart, NULL, 0,
  396. &dwThreadId);
  397. if (!hThread)
  398. {
  399. TraceTag(ttidUpdiag, "Could not create request handler thread! Error %d.",
  400. GetLastError());
  401. return FALSE;
  402. }
  403. TraceTag(ttidUpdiag, "Created shared thread.");
  404. g_hMutex = CreateMutex(&sa, FALSE, c_szSharedMutex);
  405. if (!g_hMutex)
  406. {
  407. TraceTag(ttidUpdiag, "Could not create shared mutex! Error %d.",
  408. GetLastError());
  409. return FALSE;
  410. }
  411. TraceTag(ttidUpdiag, "Created mutex.");
  412. return TRUE;
  413. }
  414. VOID Prompt(LPTSTR szRoot, LPTSTR szParam)
  415. {
  416. const DWORD c_cchMax = 60;
  417. TCHAR szPrompt[81];
  418. if (szParam && *szParam)
  419. {
  420. if (_tcslen(szParam) >= c_cchMax)
  421. {
  422. TCHAR szTemp[c_cchMax + 1] = {0};
  423. lstrcpyn(szTemp, szParam, c_cchMax);
  424. wsprintf(szPrompt, TEXT("%s: %s...>"), szRoot, szTemp);
  425. }
  426. else
  427. {
  428. wsprintf(szPrompt, TEXT("%s: %s>"), szRoot, szParam);
  429. }
  430. }
  431. else
  432. {
  433. wsprintf(szPrompt, TEXT("%s>"), szRoot);
  434. }
  435. _fputts(szPrompt, stdout);
  436. }
  437. BOOL FIsLineAllWhitespace(LPTSTR szBuf)
  438. {
  439. INT iLen = _tcslen(szBuf);
  440. for (INT iLoop = 0; iLoop < iLen; iLoop++)
  441. {
  442. if (!_istspace(szBuf[iLoop]))
  443. {
  444. return FALSE;
  445. }
  446. }
  447. return TRUE;
  448. }
  449. VOID PrintCommandPrompt()
  450. {
  451. switch (g_ctx.ectx)
  452. {
  453. case CTX_ROOT:
  454. Prompt(TEXT("UPDIAG"), NULL);
  455. break;
  456. case CTX_CD:
  457. Prompt(TEXT("CD"), PDevCur()->szFriendlyName);
  458. break;
  459. case CTX_RESULT:
  460. switch (g_ctx.presCur->resType)
  461. {
  462. case RES_FIND:
  463. Prompt(TEXT("SRCH"), g_ctx.presCur->szType);
  464. break;
  465. case RES_NOTIFY:
  466. Prompt(TEXT("NOTIFY"), g_ctx.presCur->szType);
  467. break;
  468. case RES_SUBS:
  469. Prompt(TEXT("SUBS"), g_ctx.presCur->szType);
  470. break;
  471. default:
  472. Prompt(TEXT("BUG!"), g_ctx.presCur->szType);
  473. break;
  474. }
  475. break;
  476. case CTX_CD_SVC:
  477. Prompt(TEXT("CDSVC"), g_ctx.psvcCur->szSti);
  478. break;
  479. case CTX_EVTSRC:
  480. Prompt(TEXT("ES"), g_ctx.psvcCur->szEvtUrl);
  481. break;
  482. case CTX_UCP:
  483. Prompt(TEXT("UCP"), g_ctx.pucpCur->szName);
  484. break;
  485. default:
  486. Prompt(TEXT("UNKNOWN"), NULL);
  487. break;
  488. }
  489. }
  490. EXTERN_C
  491. VOID
  492. __cdecl
  493. wmain (
  494. IN INT argc,
  495. IN PCWSTR argv[])
  496. {
  497. TCHAR szBuf[MAX_PATH];
  498. BOOL fDone = FALSE;
  499. if (!FInit())
  500. {
  501. return;
  502. }
  503. // Check for presence of input file arg. If there, init file input.
  504. //
  505. if (argc > 1)
  506. {
  507. CHAR szFileName[MAX_PATH];
  508. WszToSzBuf(szFileName, argv[1], MAX_PATH);
  509. g_pInputFile = fopen(szFileName, "r");
  510. if (!g_pInputFile)
  511. {
  512. _tprintf(TEXT("Failed to open input file. Reverting to user-input.\n\n"));
  513. }
  514. else
  515. {
  516. _tprintf(TEXT("Running in scripted mode with file: %S\n\n"), argv[1]);
  517. }
  518. }
  519. g_ctx.ectx = CTX_ROOT;
  520. while (!fDone)
  521. {
  522. // If we're running from an input file, continue.
  523. //
  524. if (g_pInputFile)
  525. {
  526. // If there was an error reading the file
  527. //
  528. if (!_fgetts(szBuf, sizeof(szBuf), g_pInputFile))
  529. {
  530. // If it wasn't eof, print an error
  531. //
  532. if (!feof(g_pInputFile))
  533. {
  534. _tprintf(TEXT("\nFailure reading script file\n\n"));
  535. }
  536. else
  537. {
  538. _tprintf(TEXT("\n[Script complete]\n\n"));
  539. }
  540. // regardless, close the file and NULL the handle
  541. //
  542. fclose(g_pInputFile);
  543. g_pInputFile = NULL;
  544. }
  545. else
  546. {
  547. if (!FIsLineAllWhitespace(szBuf))
  548. {
  549. PrintCommandPrompt();
  550. _tprintf(TEXT("%s\n"), szBuf);
  551. fDone = ParseCommand(szBuf);
  552. }
  553. }
  554. }
  555. else
  556. {
  557. PrintCommandPrompt();
  558. _fgetts(szBuf, sizeof(szBuf), stdin);
  559. // Print nice separator so we can distinguish between commands and output
  560. //
  561. _tprintf(TEXT("\n"));
  562. fDone = ParseCommand(szBuf);
  563. }
  564. }
  565. // Print nice terminating separator
  566. //
  567. _tprintf(TEXT("\n"));
  568. if (g_pInputFile)
  569. {
  570. fclose(g_pInputFile);
  571. }
  572. SsdpCleanup();
  573. }
  574. // Copy this from the SSDP implemenation so that BoundsChecker doesn't get
  575. // upset about mismatching new with free.
  576. //
  577. VOID LocalFreeSsdpMessage(PSSDP_MESSAGE pSsdpMessage)
  578. {
  579. delete pSsdpMessage->szAltHeaders;
  580. delete pSsdpMessage->szContent;
  581. delete pSsdpMessage->szLocHeader;
  582. delete pSsdpMessage->szType;
  583. delete pSsdpMessage->szUSN;
  584. delete pSsdpMessage->szSid;
  585. delete pSsdpMessage;
  586. }
  587. // stolen from ssdp\client\message.cpp
  588. BOOL CopySsdpMessage(PSSDP_MESSAGE pDestination, CONST SSDP_MESSAGE * pSource)
  589. {
  590. pDestination->szLocHeader = NULL;
  591. pDestination->szAltHeaders = NULL;
  592. pDestination->szType = NULL;
  593. pDestination->szUSN = NULL;
  594. pDestination->szSid = NULL;
  595. pDestination->szContent = NULL;
  596. pDestination->iLifeTime = 0;
  597. if (pSource->szType != NULL)
  598. {
  599. pDestination->szType = new CHAR [strlen(pSource->szType) + 1];
  600. if (pDestination->szType == NULL)
  601. {
  602. goto cleanup;
  603. }
  604. else
  605. {
  606. strcpy(pDestination->szType, pSource->szType);
  607. }
  608. }
  609. if (pSource->szLocHeader != NULL)
  610. {
  611. pDestination->szLocHeader = new CHAR [strlen(pSource->szLocHeader) + 1];
  612. if (pDestination->szLocHeader == NULL)
  613. {
  614. goto cleanup;
  615. }
  616. else
  617. {
  618. strcpy(pDestination->szLocHeader, pSource->szLocHeader);
  619. }
  620. }
  621. if (pSource->szAltHeaders != NULL)
  622. {
  623. pDestination->szAltHeaders = new CHAR [strlen(pSource->szAltHeaders) + 1];
  624. if (pDestination->szAltHeaders == NULL)
  625. {
  626. goto cleanup;
  627. }
  628. else
  629. {
  630. strcpy(pDestination->szAltHeaders, pSource->szAltHeaders);
  631. }
  632. }
  633. if (pSource->szUSN != NULL)
  634. {
  635. pDestination->szUSN = new CHAR [strlen(pSource->szUSN) + 1];
  636. if (pDestination->szUSN == NULL)
  637. {
  638. goto cleanup;
  639. }
  640. else
  641. {
  642. strcpy(pDestination->szUSN, pSource->szUSN);
  643. }
  644. }
  645. if (pSource->szSid != NULL)
  646. {
  647. pDestination->szSid = new CHAR [strlen(pSource->szSid) + 1];
  648. if (pDestination->szSid == NULL)
  649. {
  650. goto cleanup;
  651. }
  652. else
  653. {
  654. strcpy(pDestination->szSid, pSource->szSid);
  655. }
  656. }
  657. pDestination->iLifeTime = pSource->iLifeTime;
  658. pDestination->iSeq = pSource->iSeq;
  659. return TRUE;
  660. cleanup:
  661. LocalFreeSsdpMessage(pDestination);
  662. return FALSE;
  663. }
  664. VOID NotifyCallback(SSDP_CALLBACK_TYPE ct,
  665. CONST SSDP_MESSAGE *pSsdpService,
  666. LPVOID pContext)
  667. {
  668. UPNPRESULT * pres = (UPNPRESULT *)pContext;
  669. Assert(pres);
  670. switch (ct)
  671. {
  672. case SSDP_DONE:
  673. break;
  674. case SSDP_BYEBYE:
  675. case SSDP_ALIVE:
  676. case SSDP_FOUND:
  677. case SSDP_EVENT:
  678. if (pres->cResult < MAX_RESULT_MSGS)
  679. {
  680. SSDP_MESSAGE * pSsdpMsgCopy;
  681. pSsdpMsgCopy = new SSDP_MESSAGE;
  682. if (pSsdpMsgCopy)
  683. {
  684. BOOL fResult;
  685. fResult = CopySsdpMessage(pSsdpMsgCopy, pSsdpService);
  686. if (fResult)
  687. {
  688. // Overload iSeq in the case of alive or byebye so we know
  689. // which is which
  690. if (ct == SSDP_BYEBYE)
  691. {
  692. pSsdpMsgCopy->iSeq = 1;
  693. }
  694. else if (ct == SSDP_ALIVE)
  695. {
  696. pSsdpMsgCopy->iSeq = 2;
  697. }
  698. else if (ct == SSDP_FOUND)
  699. {
  700. pSsdpMsgCopy->iSeq = 0;
  701. }
  702. pres->rgmsgResult[pres->cResult++] = pSsdpMsgCopy;
  703. }
  704. // else, CopySsdpMessage frees pSsdpMsgCopy
  705. }
  706. }
  707. break;
  708. default:
  709. TraceTag(ttidUpdiag, "Unknown callback type %d!", ct);
  710. break;
  711. }
  712. }