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

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997.
  5. //
  6. // File: D E V I C E . C P P
  7. //
  8. // Contents: Functions dealing with UPnP controlled devices
  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 <oleauto.h>
  19. #include <wininet.h>
  20. #include "updiagp.h"
  21. #include "ncinet.h"
  22. #include "setupapi.h"
  23. #include "util.h"
  24. extern const STANDARD_OPERATION_LIST c_Ops;
  25. UPNPDEV *PDevCur()
  26. {
  27. return g_ctx.pdevCur[g_ctx.idevStackIndex - 1];
  28. }
  29. VOID PushDev(UPNPDEV *pdev)
  30. {
  31. g_ctx.pdevCur[g_ctx.idevStackIndex++] = pdev;
  32. g_ctx.ectx = CTX_CD;
  33. }
  34. UPNPDEV *PopDev()
  35. {
  36. UPNPDEV * pdev;
  37. pdev = PDevCur();
  38. g_ctx.idevStackIndex--;
  39. return pdev;
  40. }
  41. VOID RgPropsFromSst(SST *psst, UPNP_PROPERTY **prgProps)
  42. {
  43. DWORD iProp;
  44. UPNP_PROPERTY * rgProps;
  45. rgProps = new UPNP_PROPERTY[psst->cRows];
  46. for (iProp = 0; iProp < psst->cRows; iProp++)
  47. {
  48. rgProps[iProp].szName = SzFromTsz(psst->rgRows[iProp].szPropName);
  49. VARIANT varDest;
  50. VariantInit(&varDest);
  51. VariantChangeType(&varDest, &psst->rgRows[iProp].varValue, 0, VT_BSTR);
  52. //$ BUGBUG (danielwe) 25 Oct 1999: Remove this when SSDP is built
  53. // Unicode.
  54. //
  55. LPWSTR wszVal = varDest.bstrVal;
  56. rgProps[iProp].szValue = SzFromWsz(wszVal);
  57. rgProps[iProp].dwFlags = 0;
  58. }
  59. *prgProps = rgProps;
  60. }
  61. BOOL FRegisterDeviceAsUpnpService(LPSTR szDescDocUrl, UPNPDEV *pdev)
  62. {
  63. SSDP_MESSAGE msg = {0};
  64. CHAR szBuffer[256];
  65. CHAR szUdn[256];
  66. CHAR szType[256];
  67. msg.iLifeTime = 15000;
  68. msg.szLocHeader = szDescDocUrl;
  69. Assert(pdev->szUdn);
  70. Assert(pdev->szDeviceType);
  71. TszToSzBuf(szUdn, pdev->szUdn, celems(szUdn));
  72. TszToSzBuf(szType, pdev->szDeviceType, celems(szType));
  73. // Only register this type if this is a root device
  74. //
  75. if (pdev->fRoot)
  76. {
  77. wsprintfA(szBuffer, "%s::upnp:rootdevice", szUdn);
  78. msg.szType = "upnp:rootdevice";
  79. msg.szUSN = szBuffer;
  80. pdev->hSvc[0] = RegisterService(&msg, 0);
  81. if (pdev->hSvc[0] != INVALID_HANDLE_VALUE)
  82. {
  83. TraceTag(ttidUpdiag, "Registered %s as an SSDP Service.",
  84. msg.szType);
  85. }
  86. else
  87. {
  88. TraceTag(ttidUpdiag, "Failed to register %s as an SSDP Service! Error = %d.",
  89. msg.szType, GetLastError());
  90. return FALSE;
  91. }
  92. }
  93. msg.szUSN = szUdn;
  94. msg.szType = szUdn;
  95. pdev->hSvc[1] = RegisterService(&msg, 0);
  96. if (pdev->hSvc[1] != INVALID_HANDLE_VALUE)
  97. {
  98. TraceTag(ttidUpdiag, "Registered %s as an SSDP Service.",
  99. msg.szType);
  100. }
  101. else
  102. {
  103. TraceTag(ttidUpdiag, "Failed to register %s as an SSDP Service! Error = %d.",
  104. msg.szType, GetLastError());
  105. return FALSE;
  106. }
  107. wsprintfA(szBuffer, "%s::%s", szUdn, szType);
  108. msg.szUSN = szBuffer;
  109. msg.szType = szType;
  110. pdev->hSvc[2] = RegisterService(&msg, 0);
  111. if (pdev->hSvc[2] != INVALID_HANDLE_VALUE)
  112. {
  113. TraceTag(ttidUpdiag, "Registered %s as an SSDP Service.",
  114. msg.szType);
  115. }
  116. else
  117. {
  118. TraceTag(ttidUpdiag, "Failed to register %s as an SSDP Service! Error = %d.",
  119. msg.szType, GetLastError());
  120. return FALSE;
  121. }
  122. return TRUE;
  123. }
  124. VOID GetServiceConfigFile(UPNPDEV * pdev, UPNPSVC * psvc, LPCTSTR szDevConfigFile)
  125. {
  126. HRESULT hr;
  127. HINF hinf;
  128. INFCONTEXT ctx;
  129. UINT unErrorLine;
  130. TCHAR szKey[LINE_LEN]; // LINE_LEN defined in setupapi.h as 256
  131. TCHAR szDeviceType[LINE_LEN];
  132. TCHAR szServiceType[LINE_LEN];
  133. // full path to the inf file is %windir%\inf\szConfigFile
  134. TCHAR szDevConfigFileWithPath[MAX_PATH];
  135. GetWindowsDirectory (szDevConfigFileWithPath, MAX_PATH);
  136. lstrcat (szDevConfigFileWithPath, TEXT("\\inf\\"));
  137. lstrcat(szDevConfigFileWithPath, szDevConfigFile);
  138. hr = HrSetupOpenConfigFile(szDevConfigFileWithPath, &unErrorLine, &hinf);
  139. if (S_OK == hr)
  140. {
  141. Assert(IsValidHandle(hinf));
  142. // Loop over the Devices section
  143. hr = HrSetupFindFirstLine(hinf, TEXT("Devices"), NULL, &ctx);
  144. if (S_OK == hr)
  145. {
  146. do
  147. {
  148. // Retrieve a line from the Devices section
  149. hr = HrSetupGetStringField(ctx,0,szKey,celems(szKey),NULL);
  150. if(S_OK == hr)
  151. {
  152. // varify this is an "Device"
  153. szKey[celems(szKey)-1] = L'\0';
  154. if (lstrcmpi(szKey, TEXT("Device")))
  155. {
  156. TraceTag(ttidUpdiag, "Wrong key in the Devices section: %s", szKey);
  157. continue;
  158. }
  159. // Query the DeviceType
  160. hr = HrSetupGetStringField(ctx, 1, szDeviceType, celems(szDeviceType),
  161. NULL);
  162. if (S_OK == hr)
  163. {
  164. if (!lstrcmpi(szDeviceType, pdev->szDeviceType))
  165. {
  166. break;
  167. }
  168. else
  169. {
  170. continue;
  171. }
  172. }
  173. }
  174. }
  175. while (S_OK == (hr = HrSetupFindNextLine(ctx, &ctx)));
  176. }
  177. if (hr == S_FALSE)
  178. {
  179. // we didn't find the device !
  180. TraceTag(ttidUpdiag, "Error!! Config section for device %s is not found.",
  181. pdev->szDeviceType);
  182. }
  183. else
  184. {
  185. // Loop over section for this device
  186. hr = HrSetupFindFirstLine(hinf, szDeviceType, NULL, &ctx);
  187. if (S_OK == hr)
  188. {
  189. do
  190. {
  191. // Retrieve a line from the Devices section
  192. hr = HrSetupGetStringField(ctx,0,szKey,celems(szKey),NULL);
  193. if(S_OK == hr)
  194. {
  195. // varify this is an "Device"
  196. szKey[celems(szKey)-1] = L'\0';
  197. if (lstrcmpi(szKey, TEXT("Service")))
  198. {
  199. TraceTag(ttidUpdiag, "Wrong key in the Device's section: %s", szKey);
  200. continue;
  201. }
  202. // Query the Service Type
  203. hr = HrSetupGetStringField(ctx, 1, szServiceType, celems(szServiceType),
  204. NULL);
  205. if (S_OK == hr)
  206. {
  207. if (!lstrcmpi(szServiceType, psvc->szServiceType))
  208. {
  209. break;
  210. }
  211. else
  212. {
  213. continue;
  214. }
  215. }
  216. }
  217. }
  218. while (S_OK == (hr = HrSetupFindNextLine(ctx, &ctx)));
  219. if (hr == S_FALSE)
  220. {
  221. // we didn't find the service !
  222. TraceTag(ttidUpdiag, "Error!! Config section for service %s is not found.",
  223. psvc->szServiceType);
  224. }
  225. else
  226. {
  227. hr = HrSetupFindFirstLine(hinf, szServiceType, NULL, &ctx);
  228. if (S_OK == hr)
  229. {
  230. // Retrieve a line from the Devices section
  231. hr = HrSetupGetStringField(ctx,0,szKey,celems(szKey),NULL);
  232. if(S_OK == hr)
  233. {
  234. // varify this is an "Device"
  235. szKey[celems(szKey)-1] = L'\0';
  236. if (lstrcmpi(szKey, TEXT("ServiceInf")))
  237. {
  238. TraceTag(ttidUpdiag, "Wrong key in the Service's section: %s", szKey);
  239. }
  240. else
  241. {
  242. // Query the Service config file
  243. TCHAR szConfigFile[MAX_PATH];
  244. hr = HrSetupGetStringField(ctx, 1, szConfigFile,
  245. celems(szConfigFile), NULL);
  246. if (S_OK ==hr)
  247. {
  248. // full path to the inf file is %windir%\inf\szConfigFile
  249. TCHAR szDevConfigFileWithPath[MAX_PATH];
  250. GetWindowsDirectory (psvc->szConfigFile, MAX_PATH);
  251. lstrcat (psvc->szConfigFile, TEXT("\\inf\\"));
  252. lstrcat(psvc->szConfigFile, szConfigFile);
  253. }
  254. }
  255. }
  256. }
  257. }
  258. }
  259. }
  260. SetupCloseInfFileSafe(hinf);
  261. }
  262. }
  263. HRESULT HrAddAllowedValue(SST_ROW * pRow, TCHAR * szValueRange)
  264. {
  265. HRESULT hr = S_OK;
  266. if(*szValueRange == '(')
  267. {
  268. szValueRange++;
  269. IXMLDOMElement * pAllowedValueNode = NULL;
  270. BSTR bstrElementName;
  271. // we assume that ".." specifies a range, otherwise it's a comma
  272. // separated list of allowed values
  273. TCHAR * pChar = _tcsstr(szValueRange, TEXT(".."));
  274. if (pChar)
  275. {
  276. // we have a range
  277. TCHAR * pNextItem = szValueRange;
  278. // BUGBUG: we should check if data type of the min, max & step
  279. // matches the variable type
  280. *pChar = '\0';
  281. lstrcpy(pRow->szMin, pNextItem);
  282. pNextItem = pChar+2;
  283. pChar = _tcschr(pNextItem, TEXT(','));
  284. if (pChar)
  285. {
  286. *pChar = '\0';
  287. lstrcpy(pRow->szMax, pNextItem);
  288. pNextItem = ++pChar;
  289. pChar = _tcschr(pNextItem, TEXT(')'));
  290. if (pChar)
  291. {
  292. *pChar = '\0';
  293. lstrcpy(pRow->szStep, pNextItem);
  294. }
  295. else
  296. {
  297. TraceTag(ttidUpdiag, "HrAddAllowedValue: missing closing )");
  298. hr = E_INVALIDARG;
  299. }
  300. }
  301. else
  302. {
  303. TraceTag(ttidUpdiag, "HrAddAllowedValue: step not specified");
  304. hr = E_INVALIDARG;
  305. }
  306. }
  307. else
  308. {
  309. // we have a list of allowed values
  310. pChar = _tcschr(szValueRange, TEXT(')'));
  311. if (pChar)
  312. {
  313. *pChar = '\0';
  314. if (lstrlen(szValueRange))
  315. {
  316. lstrcpy(pRow->mszAllowedValueList, szValueRange);
  317. TCHAR * pNextItem = pRow->mszAllowedValueList;
  318. while ((S_OK ==hr) && (pChar = _tcschr(pNextItem, TEXT(','))))
  319. {
  320. *pChar = '\0';
  321. pNextItem = ++pChar;
  322. }
  323. // add the last one
  324. if (*pNextItem)
  325. {
  326. pNextItem += lstrlen(pNextItem);
  327. pNextItem ++;
  328. *pNextItem = '\0';
  329. }
  330. else
  331. {
  332. TraceTag(ttidUpdiag, "HrAddAllowedValue: invalid syntax");
  333. hr = E_INVALIDARG;
  334. }
  335. }
  336. }
  337. else
  338. {
  339. TraceTag(ttidUpdiag, "HrAddAllowedValue: missing closing )");
  340. hr = E_INVALIDARG;
  341. }
  342. }
  343. }
  344. else
  345. {
  346. TraceTag(ttidUpdiag, "HrAddAllowedValue: missing begining (");
  347. hr = E_INVALIDARG;
  348. }
  349. TraceError("HrAddAllowedValue", hr);
  350. return hr;
  351. }
  352. HRESULT HrAddVariable(UPNPSVC * psvc, LPTSTR szVariableLine)
  353. {
  354. HRESULT hr = S_OK;
  355. DWORD iRow = psvc->sst.cRows;
  356. if (iRow < MAX_SST_ROWS)
  357. {
  358. SST_ROW * pRow = &psvc->sst.rgRows[iRow];
  359. // fill in variable name
  360. TCHAR szName[MAX_PATH];
  361. if (fGetNextField(&szVariableLine, szName))
  362. {
  363. lstrcpy(pRow->szPropName, szName);
  364. TCHAR szType[MAX_PATH];
  365. fGetNextField(&szVariableLine, szType);
  366. TCHAR szValueRange[MAX_PATH];
  367. fGetNextField(&szVariableLine, szValueRange);
  368. TCHAR szDefaultValue[MAX_PATH];
  369. fGetNextField(&szVariableLine, szDefaultValue);
  370. // fill in variable type and default value
  371. if (*szType && *szDefaultValue)
  372. {
  373. VariantInit(&pRow->varValue);
  374. pRow->varValue.vt = VT_BSTR;
  375. WCHAR * wszDefaultValue = WszFromTsz(szDefaultValue);
  376. V_BSTR(&pRow->varValue) = SysAllocString(wszDefaultValue);
  377. delete wszDefaultValue;
  378. VARTYPE vt = VT_EMPTY;
  379. if (lstrcmpi(szType, TEXT("number")) == 0)
  380. {
  381. vt = VT_I4;
  382. }
  383. else if (lstrcmpi(szType, TEXT("string")) == 0)
  384. {
  385. vt = VT_BSTR;
  386. }
  387. else if (lstrcmpi(szType, TEXT("dateTime")) == 0)
  388. {
  389. vt = VT_DATE;
  390. }
  391. else if (lstrcmpi(szType, TEXT("boolean")) == 0)
  392. {
  393. vt = VT_BOOL;
  394. }
  395. else if (lstrcmpi(szType, TEXT("ByteBlock")) == 0)
  396. {
  397. vt = VT_UI1 | VT_ARRAY;
  398. }
  399. if (vt != VT_EMPTY)
  400. {
  401. hr = VariantChangeType(&pRow->varValue, &pRow->varValue, 0, vt);
  402. }
  403. else
  404. {
  405. hr = E_INVALIDARG;
  406. TraceTag(ttidUpdiag, "HrAddVariable: invalid data type %s.", szType);
  407. }
  408. if (S_OK == hr)
  409. {
  410. // fill in value range or list of allowed values if specified
  411. *pRow->mszAllowedValueList = '\0';
  412. *pRow->szMin = '\0';
  413. *pRow->szMax = '\0';
  414. *pRow->szStep = '\0';
  415. if (*szValueRange)
  416. {
  417. hr = HrAddAllowedValue(pRow, szValueRange);
  418. }
  419. // successfully added a new row
  420. if (S_OK == hr)
  421. {
  422. psvc->sst.cRows++;
  423. }
  424. }
  425. }
  426. else
  427. {
  428. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  429. TraceTag(ttidUpdiag, "HrAddVariable: data type or default value missing for variable %s.",
  430. szName);
  431. }
  432. }
  433. else
  434. {
  435. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  436. TraceTag(ttidUpdiag, "HrAddVariable: variable name not found");
  437. }
  438. }
  439. else
  440. {
  441. hr = E_FAIL;
  442. TraceTag(ttidUpdiag, "HrAddVariable: max number of rows in state table exceeded");
  443. }
  444. TraceError("HrAddVariable", hr);
  445. return hr;
  446. }
  447. // input: the state table and variable name
  448. // output: if the variable exists in the state table
  449. // NYI
  450. BOOL IsValidVariable(SST * pSST, TCHAR * szVarName)
  451. {
  452. return TRUE;
  453. }
  454. // parses a line and fill in a new operation of an action
  455. HRESULT HrAddOperation(UPNPSVC * psvc, ACTION * pAction, TCHAR * szLine)
  456. {
  457. HRESULT hr = S_OK;
  458. DWORD iOperation = pAction->cOperations;
  459. if (iOperation < MAX_OPERATIONS)
  460. {
  461. OPERATION_DATA * pOperation = &pAction->rgOperations[iOperation];
  462. // get the operations's name
  463. TCHAR * pChar = _tcschr(szLine, TEXT('('));
  464. if (pChar)
  465. {
  466. *pChar ='\0';
  467. lstrcpy(pOperation->szOpName, szLine);
  468. szLine = ++pChar;
  469. DWORD nArgs;
  470. DWORD nConsts;
  471. if (IsStandardOperation(pOperation->szOpName, &nArgs, &nConsts))
  472. {
  473. TraceTag(ttidUpdiag, "=== Operation %s ===", pOperation->szOpName);
  474. // get the Variable name
  475. if (nArgs+nConsts ==0)
  476. {
  477. pChar = _tcschr(szLine, TEXT(')'));
  478. }
  479. else
  480. {
  481. pChar = _tcschr(szLine, TEXT(','));
  482. }
  483. if (pChar)
  484. {
  485. *pChar = TEXT('\0');
  486. lstrcpy(pOperation->szVariableName, szLine);
  487. szLine = ++pChar;
  488. if(IsValidVariable(&psvc->sst, pOperation->szVariableName))
  489. {
  490. TraceTag(ttidUpdiag, "Variable: %s", pOperation->szVariableName);
  491. BOOL fError = FALSE;
  492. // skip the arguments
  493. while (nArgs > 0)
  494. {
  495. if (nArgs + nConsts == 1)
  496. {
  497. pChar = _tcschr(szLine, TEXT(')'));
  498. }
  499. else
  500. {
  501. pChar = _tcschr(szLine, TEXT(','));
  502. }
  503. if (pChar)
  504. {
  505. *pChar = TEXT('\0');
  506. TraceTag(ttidUpdiag, "Argument: %s", szLine);
  507. szLine = ++pChar;
  508. }
  509. else
  510. {
  511. fError = TRUE;
  512. TraceTag(ttidUpdiag, "ERROR! HrAddOperation: Syntax error: missing arguments");
  513. break;
  514. }
  515. nArgs--;
  516. }
  517. if (!fError)
  518. {
  519. TCHAR * pNextConst = pOperation->mszConstantList;
  520. // now get the constants, all in string form
  521. while (nConsts >0)
  522. {
  523. if (nConsts == 1)
  524. {
  525. pChar = _tcschr(szLine, TEXT(')'));
  526. }
  527. else
  528. {
  529. pChar = _tcschr(szLine, TEXT(','));
  530. }
  531. if (pChar)
  532. {
  533. *pChar = TEXT('\0');
  534. TraceTag(ttidUpdiag, "Constant: %s", szLine);
  535. lstrcpy(pNextConst, szLine);
  536. pNextConst+=lstrlen(pNextConst);
  537. pNextConst++;
  538. szLine = ++pChar;
  539. }
  540. else
  541. {
  542. fError = TRUE;
  543. TraceTag(ttidUpdiag, "ERROR! HrAddOperation: Syntax error: missing constants");
  544. break;
  545. }
  546. nConsts--;
  547. }
  548. *pNextConst = TEXT('\0');
  549. }
  550. if (!fError)
  551. {
  552. // all is well, increment the count of operations
  553. pAction->cOperations++;
  554. }
  555. }
  556. else
  557. {
  558. hr = E_INVALIDARG;
  559. TraceTag(ttidUpdiag, "ERROR! HrAddOperation: variable %s not in state table",
  560. pOperation->szVariableName);
  561. }
  562. }
  563. else
  564. {
  565. hr = E_INVALIDARG;
  566. TraceTag(ttidUpdiag, "ERROR! HrAddOperation: Invalid syntax for operation: %s, affected state variable not found",
  567. pOperation->szOpName);
  568. }
  569. }
  570. else
  571. {
  572. hr = E_INVALIDARG;
  573. TraceTag(ttidUpdiag, "ERROR! HrAddOperation: Invalid operation: %s", pOperation->szOpName);
  574. }
  575. }
  576. }
  577. TraceError("HrAddOperation", hr);
  578. return hr;
  579. }
  580. HRESULT HrAddAction(UPNPSVC * psvc, HINF hinf, TCHAR * szActionName)
  581. {
  582. HRESULT hr = S_OK;
  583. INFCONTEXT ctx;
  584. DWORD iAction = psvc->action_set.cActions;
  585. ACTION * pAction = &(psvc->action_set.rgActions[iAction]);
  586. // initialize the new action
  587. lstrcpy(pAction->szActionName, szActionName);
  588. pAction->cOperations = 0;
  589. // Loop over the list of operations for this action
  590. hr = HrSetupFindFirstLine(hinf, szActionName, NULL, &ctx);
  591. if (S_OK == hr)
  592. {
  593. do
  594. {
  595. TCHAR szKey[LINE_LEN]; // LINE_LEN defined in setupapi.h as 256
  596. TCHAR szOpLine[LINE_LEN];
  597. // Retrieve a line from the Action
  598. hr = HrSetupGetStringField(ctx, 0, szKey, celems(szKey),
  599. NULL);
  600. if(S_OK == hr)
  601. {
  602. // varify this is an "Operation"
  603. szKey[celems(szKey)-1] = L'\0';
  604. if (lstrcmpi(szKey, TEXT("Operation")))
  605. {
  606. TraceTag(ttidUpdiag, "ERROR! HrAddAction: Wrong key in the Operation section: %s", szKey);
  607. continue;
  608. }
  609. // Query the OperationLine
  610. // get the line text
  611. hr = HrSetupGetLineText(ctx, szOpLine, celems(szOpLine), NULL);
  612. if (S_OK == hr)
  613. {
  614. // Add operations in this action
  615. hr = HrAddOperation(psvc, pAction, szOpLine);
  616. }
  617. }
  618. }
  619. while (S_OK == (hr = HrSetupFindNextLine(ctx, &ctx)));
  620. }
  621. if (hr == S_FALSE)
  622. {
  623. // S_FALSE will terminate the loop successfully, so convert it to S_OK
  624. // here.
  625. hr = S_OK;
  626. }
  627. // we just successfully added a new action
  628. if (S_OK == hr)
  629. psvc->action_set.cActions++;
  630. return hr;
  631. }
  632. HRESULT HrLoadSSTAndActionSet(UPNPSVC * psvc)
  633. {
  634. HRESULT hr = S_OK;
  635. HINF hinf = NULL;
  636. INFCONTEXT ctx;
  637. TCHAR szKey[LINE_LEN]; // LINE_LEN defined in setupapi.h as 256
  638. UINT unErrorLine;
  639. hr = HrSetupOpenConfigFile(psvc->szConfigFile, &unErrorLine, &hinf);
  640. if (S_OK == hr)
  641. {
  642. Assert(IsValidHandle(hinf));
  643. // Process [StateTable] section
  644. TraceTag(ttidUpdiag, "Reading StateTable for service %s", psvc->szId);
  645. TCHAR szVariableLine[LINE_LEN];
  646. hr = HrSetupFindFirstLine(hinf, TEXT("StateTable"), NULL, &ctx);
  647. if (S_OK == hr)
  648. {
  649. do
  650. {
  651. // Retrieve a line from the StateTable section
  652. hr = HrSetupGetStringField(ctx,0,szKey,celems(szKey),NULL);
  653. if(S_OK == hr)
  654. {
  655. // varify this is a "Variable"
  656. szKey[celems(szKey)-1] = L'\0';
  657. if (lstrcmpi(szKey, TEXT("Variable")))
  658. {
  659. TraceTag(ttidUpdiag, "Wrong key in the StateTable section: %s", szKey);
  660. continue;
  661. }
  662. // get the line text
  663. hr = HrSetupGetLineText(ctx, szVariableLine, celems(szVariableLine),
  664. NULL);
  665. if (S_OK == hr)
  666. {
  667. // Add variable in this line
  668. hr = HrAddVariable(psvc, szVariableLine);
  669. }
  670. }
  671. }
  672. while (S_OK == (hr = HrSetupFindNextLine(ctx, &ctx)));
  673. if (hr == S_FALSE)
  674. {
  675. // S_FALSE will terminate the loop successfully, so convert it to S_OK
  676. // here.
  677. hr = S_OK;
  678. }
  679. }
  680. // Process [ActionSet] section
  681. TraceTag(ttidUpdiag, "Reading ActionSet for service %s", psvc->szId);
  682. TCHAR szActionName[LINE_LEN];
  683. hr = HrSetupFindFirstLine(hinf, TEXT("ActionSet"), NULL, &ctx);
  684. if (S_OK == hr)
  685. {
  686. do
  687. {
  688. // Retrieve a line from the ActionSet section
  689. hr = HrSetupGetStringField(ctx,0,szKey,celems(szKey),NULL);
  690. if(S_OK == hr)
  691. {
  692. // varify this is an "Action"
  693. szKey[celems(szKey)-1] = L'\0';
  694. if (lstrcmpi(szKey, TEXT("Action")))
  695. {
  696. TraceTag(ttidUpdiag, "Wrong key in the ActionList section: %s", szKey);
  697. continue;
  698. }
  699. // Query the ActionName
  700. hr = HrSetupGetLineText(ctx, szActionName, celems(szActionName),
  701. NULL);
  702. if (S_OK == hr)
  703. {
  704. // Remove argument list if specified
  705. TCHAR * pChar = _tcschr(szActionName, TEXT('('));
  706. if (pChar)
  707. *pChar = '\0';
  708. // Add operations in this action
  709. hr = HrAddAction(psvc, hinf, szActionName);
  710. }
  711. }
  712. }
  713. while (S_OK == (hr = HrSetupFindNextLine(ctx, &ctx)));
  714. }
  715. if (hr == S_FALSE)
  716. {
  717. // S_FALSE will terminate the loop successfully, so convert it to S_OK
  718. // here.
  719. hr = S_OK;
  720. }
  721. SetupCloseInfFileSafe(hinf);
  722. }
  723. else
  724. {
  725. TraceTag(ttidUpdiag, "Failed to open file %s, line = %d",
  726. psvc->szConfigFile, unErrorLine);
  727. }
  728. TraceError("HrLoadSSTAndActionSet", hr);
  729. return hr;
  730. }
  731. VOID AttachServiceControl(UPNPSVC *psvc)
  732. {
  733. // set the control ID for this service from the control url
  734. TCHAR * pChar = _tcschr(psvc->szControlUrl, TEXT('?'));
  735. if (pChar)
  736. {
  737. pChar++;
  738. lstrcpy(psvc->szControlId, pChar);
  739. // If it's a demo service then hook up the Demo control
  740. for (DWORD isvc = 0; isvc < c_cDemoSvc; isvc++)
  741. {
  742. if (!_tcsicmp(c_rgSvc[isvc].szServiceId, psvc->szControlId))
  743. {
  744. psvc->psvcDemoCtl = &c_rgSvc[isvc];
  745. TraceTag(ttidUpdiag, "Attached service demo control '%s' to '%s'.",
  746. psvc->psvcDemoCtl->szServiceId, psvc->szServiceType);
  747. break;
  748. }
  749. }
  750. if (isvc == c_cDemoSvc)
  751. {
  752. TraceTag(ttidUpdiag, "No demo service control handler found for id '%s'.",
  753. psvc->szControlId);
  754. }
  755. }
  756. else
  757. {
  758. TraceTag(ttidUpdiag, "Control URL for '%s' doesn't have proper "
  759. "format: %s.", psvc->szServiceType, psvc->szControlUrl);
  760. }
  761. }
  762. void
  763. FreeServiceInfo(UPNP_SERVICE_PRIVATE * pusp)
  764. {
  765. Assert(pusp);
  766. CoTaskMemFree(pusp->wszServiceType);
  767. CoTaskMemFree(pusp->wszServiceId);
  768. CoTaskMemFree(pusp->wszControlUrl);
  769. CoTaskMemFree(pusp->wszEventSubUrl);
  770. CoTaskMemFree(pusp->wszScpd);
  771. }
  772. HRESULT HrReadServices(LPSTR szDescDocUrl, LPCTSTR szDevConfigFile,
  773. IUPnPDevice * pud, UPNPDEV *pdev)
  774. {
  775. HRESULT hr = S_OK;
  776. IUPnPDevicePrivate *pudp = NULL;
  777. hr = pud->QueryInterface(IID_IUPnPDevicePrivate, (LPVOID *)&pudp);
  778. if (SUCCEEDED(hr))
  779. {
  780. ULONG cServices;
  781. UPNP_SERVICE_PRIVATE * rgusp;
  782. hr = pudp->GetNumServices(&cServices);
  783. if (SUCCEEDED(hr))
  784. {
  785. Assert(cServices > 0);
  786. }
  787. rgusp = (UPNP_SERVICE_PRIVATE *) CoTaskMemAlloc(cServices *
  788. sizeof(UPNP_SERVICE_PRIVATE));
  789. if (rgusp)
  790. {
  791. ULONG ulSvcs;
  792. hr = pudp->GetServiceInfo(0, cServices, rgusp, &ulSvcs);
  793. if (SUCCEEDED(hr))
  794. {
  795. DWORD isvc;
  796. pdev->cSvcs = ulSvcs;
  797. for (isvc = 0; isvc < ulSvcs; isvc++)
  798. {
  799. UPNPSVC * psvc = new UPNPSVC;
  800. ZeroMemory(psvc, sizeof(UPNPSVC));
  801. pdev->rgSvcs[isvc] = psvc;
  802. WcharToTcharInPlace(psvc->szControlUrl, rgusp[isvc].wszControlUrl);
  803. WcharToTcharInPlace(psvc->szEvtUrl, rgusp[isvc].wszEventSubUrl);
  804. WcharToTcharInPlace(psvc->szScpdUrl, rgusp[isvc].wszScpd);
  805. WcharToTcharInPlace(psvc->szServiceType, rgusp[isvc].wszServiceType);
  806. WcharToTcharInPlace(psvc->szId, rgusp[isvc].wszServiceId);
  807. // get the service's config file path and name
  808. GetServiceConfigFile(pdev, psvc, szDevConfigFile);
  809. // initialize the service state table and action list
  810. // BUGBUG: allow services with no config file to be created
  811. // (e.g. midi player)
  812. HRESULT hr2;
  813. hr2 = HrLoadSSTAndActionSet(psvc);
  814. SSDP_MESSAGE msg = {0};
  815. CHAR szBuffer[256];
  816. CHAR szUdn[256];
  817. CHAR szType[256];
  818. Assert(pdev->szUdn);
  819. Assert(psvc->szServiceType);
  820. TszToSzBuf(szUdn, pdev->szUdn, celems(szUdn));
  821. TszToSzBuf(szType, psvc->szServiceType, celems(szType));
  822. msg.iLifeTime = 15000;
  823. msg.szLocHeader = szDescDocUrl;
  824. wsprintfA(szBuffer, "%s::%s", szUdn, szType);
  825. msg.szType = szType;
  826. msg.szUSN = szBuffer;
  827. psvc->hSvc = RegisterService(&msg, 0);
  828. if (psvc->hSvc && psvc->hSvc != INVALID_HANDLE_VALUE)
  829. {
  830. TraceTag(ttidUpdiag, "Successfully registered "
  831. "service %s.", psvc->szServiceType);
  832. }
  833. else
  834. {
  835. TraceTag(ttidUpdiag, "Failed to register %s as "
  836. "a service! Error = %d.",
  837. psvc->szServiceType, GetLastError());
  838. }
  839. AttachServiceControl(psvc);
  840. {
  841. CHAR szEvtUrl[INTERNET_MAX_URL_LENGTH];
  842. LPSTR pszEvtUrl;
  843. Assert(psvc->szEvtUrl);
  844. pszEvtUrl = SzFromTsz(psvc->szEvtUrl);
  845. if (pszEvtUrl)
  846. {
  847. hr = HrGetRequestUriA(pszEvtUrl,
  848. INTERNET_MAX_URL_LENGTH,
  849. szEvtUrl);
  850. if (SUCCEEDED(hr))
  851. {
  852. // convert SST to UPNP_PROPERTY
  853. UPNP_PROPERTY * rgProps;
  854. RgPropsFromSst(&psvc->sst, &rgProps);
  855. if(RegisterUpnpEventSource(szEvtUrl,
  856. psvc->sst.cRows,
  857. rgProps))
  858. {
  859. TraceTag(ttidUpdiag, "Successfully registered "
  860. "event source %s.", szEvtUrl);
  861. }
  862. else
  863. {
  864. TraceTag(ttidUpdiag, "Failed to register %s as "
  865. "an event source! Error = %d.",
  866. psvc->szEvtUrl, GetLastError());
  867. }
  868. for (DWORD iRow = 0; iRow < psvc->sst.cRows; iRow++)
  869. {
  870. #ifdef _UNICODE
  871. delete(rgProps[iRow].szName);
  872. #else
  873. free(rgProps[iRow].szName);
  874. #endif
  875. delete(rgProps[iRow].szValue);
  876. }
  877. delete [] rgProps;
  878. }
  879. delete [] pszEvtUrl;
  880. }
  881. else
  882. {
  883. TraceTag(ttidUpdiag, "HrReadServices: SzFromTsz failed");
  884. }
  885. }
  886. // free the strings allocated by GetServiceInfo() above
  887. FreeServiceInfo(&(rgusp[isvc]));
  888. }
  889. }
  890. CoTaskMemFree((LPVOID)rgusp);
  891. }
  892. ReleaseObj(pudp);
  893. }
  894. TraceError("HrReadServices", hr);
  895. return hr;
  896. }
  897. HRESULT HrReadDevice(BOOL fRoot, LPSTR szDescDocUrl, LPCTSTR szConfigFile,
  898. IUPnPDevice * pud)
  899. {
  900. HRESULT hr;
  901. BSTR bstr;
  902. BOOL fPop = FALSE;
  903. hr = pud->get_FriendlyName(&bstr);
  904. if (SUCCEEDED(hr))
  905. {
  906. UPNPDEV * pdev;
  907. TraceTag(ttidUpdiag, "Loading device %S.", bstr);
  908. pdev = new UPNPDEV;
  909. ZeroMemory(pdev, sizeof(UPNPDEV));
  910. WcharToTcharInPlace(pdev->szFriendlyName, bstr);
  911. SysFreeString(bstr);
  912. pdev->fRoot = fRoot;
  913. pud->get_Type(&bstr);
  914. WcharToTcharInPlace(pdev->szDeviceType, bstr);
  915. SysFreeString(bstr);
  916. pud->get_UniqueDeviceName(&bstr);
  917. WcharToTcharInPlace(pdev->szUdn, bstr);
  918. SysFreeString(bstr);
  919. if (FRegisterDeviceAsUpnpService(szDescDocUrl, pdev))
  920. {
  921. if (fRoot)
  922. {
  923. g_params.rgCd[g_params.cCd++] = pdev;
  924. }
  925. else
  926. {
  927. PDevCur()->rgDevs[PDevCur()->cDevs++] = pdev;
  928. }
  929. PushDev(pdev);
  930. fPop = TRUE;
  931. hr = HrReadServices(szDescDocUrl, szConfigFile, pud, pdev);
  932. }
  933. else
  934. {
  935. CleanupCd(pdev);
  936. }
  937. }
  938. // read in the child devices
  939. IUPnPDevices * puds;
  940. puds = NULL;
  941. hr = pud->get_Children(&puds);
  942. if (SUCCEEDED(hr))
  943. {
  944. Assert(puds);
  945. IUnknown * punkEnum;
  946. punkEnum = NULL;
  947. hr = puds->get__NewEnum(&punkEnum);
  948. if(SUCCEEDED(hr))
  949. {
  950. IEnumUnknown * peu;
  951. peu = NULL;
  952. hr = punkEnum->QueryInterface(IID_IEnumUnknown, (void**) &peu);
  953. if (SUCCEEDED(hr))
  954. {
  955. while (S_OK == hr)
  956. {
  957. IUnknown * punkDevice;
  958. punkDevice = NULL;
  959. hr = peu->Next(1, &punkDevice, NULL);
  960. if (S_FALSE == hr)
  961. {
  962. Assert(!punkDevice);
  963. // we're done
  964. }
  965. else if (SUCCEEDED(hr))
  966. {
  967. Assert(punkDevice);
  968. IUPnPDevice * pudChild;
  969. pudChild = NULL;
  970. hr = punkDevice->QueryInterface(IID_IUPnPDevice, (void**) &pudChild);
  971. if (SUCCEEDED(hr))
  972. {
  973. Assert(pudChild);
  974. hr = HrReadDevice(FALSE, szDescDocUrl, szConfigFile, pudChild);
  975. ReleaseObj(pudChild);
  976. }
  977. ReleaseObj(punkDevice);
  978. }
  979. }
  980. if (S_FALSE == hr)
  981. {
  982. hr = S_OK;
  983. }
  984. ReleaseObj(peu);
  985. }
  986. ReleaseObj(punkEnum);
  987. }
  988. ReleaseObj(puds);
  989. }
  990. if ((!fRoot) && (fPop))
  991. {
  992. PopDev();
  993. }
  994. TraceError("HrReadDevice", hr);
  995. return hr;
  996. }
  997. BOOL DoNewCd(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs)
  998. {
  999. UPNPDEV * pdev;
  1000. UPNPSVC * psvc;
  1001. HRESULT hr = S_OK;
  1002. if ((cArgs == 3) || (cArgs ==2))
  1003. {
  1004. IUPnPDevice * pud = NULL;
  1005. IUPnPDescriptionDocument * pudd = NULL;
  1006. // This is the file name
  1007. // Load device from description doc
  1008. _tprintf(TEXT("Loading device from doc: %s...\n"), rgArgs[1]);
  1009. hr = CoCreateInstance(CLSID_UPnPDescriptionDocument,
  1010. NULL,
  1011. CLSCTX_INPROC_SERVER,
  1012. IID_IUPnPDescriptionDocument,
  1013. (LPVOID *)&pudd);
  1014. if (SUCCEEDED(hr))
  1015. {
  1016. Assert(pudd);
  1017. LPWSTR pszUrl;
  1018. pszUrl = WszFromTsz(rgArgs[1]);
  1019. if (pszUrl)
  1020. {
  1021. BSTR bstrUrl;
  1022. bstrUrl = ::SysAllocString(pszUrl);
  1023. if (bstrUrl)
  1024. {
  1025. hr = pudd->Load(bstrUrl);
  1026. if (SUCCEEDED(hr))
  1027. {
  1028. _tprintf(TEXT("Loaded %s.\n"), rgArgs[1]);
  1029. hr = pudd->RootDevice(&pud);
  1030. if (FAILED(hr))
  1031. {
  1032. TraceTag(ttidUpdiag, "IUPnPDescriptionDocument::RootDevice (URL=%S) failed, hr=%x", pszUrl, hr);
  1033. pud = NULL;
  1034. }
  1035. }
  1036. else
  1037. {
  1038. TraceTag(ttidUpdiag, "Failed to load %S. hr=%x", pszUrl, hr);
  1039. _tprintf(TEXT("Failed to load %s. hr=%d\n"), rgArgs[1], hr);
  1040. }
  1041. ::SysFreeString(bstrUrl);
  1042. }
  1043. else
  1044. {
  1045. // SysAllocString failed
  1046. hr = E_OUTOFMEMORY;
  1047. }
  1048. delete [] pszUrl;
  1049. }
  1050. else
  1051. {
  1052. // WszFromTsz failed
  1053. hr = E_OUTOFMEMORY;
  1054. }
  1055. ReleaseObj(pudd);
  1056. }
  1057. else
  1058. {
  1059. _tprintf(TEXT("Could not create description doc. is upnp.dll registered?\n"));
  1060. }
  1061. if (FAILED(hr))
  1062. {
  1063. Assert(!pud);
  1064. return FALSE;
  1065. }
  1066. Assert(pud);
  1067. // read root device properties into structs
  1068. Assert(rgArgs[1]);
  1069. LPSTR pszDescDocUrl = SzFromTsz(rgArgs[1]);
  1070. if (pszDescDocUrl)
  1071. {
  1072. if (cArgs==2)
  1073. {
  1074. hr = HrReadDevice(TRUE, pszDescDocUrl, TEXT(""), pud);
  1075. }
  1076. else
  1077. {
  1078. hr = HrReadDevice(TRUE, pszDescDocUrl, rgArgs[2], pud);
  1079. }
  1080. delete [] pszDescDocUrl;
  1081. }
  1082. else
  1083. {
  1084. hr = E_OUTOFMEMORY;
  1085. }
  1086. TraceError("DoNewCd", hr);
  1087. ReleaseObj(pud);
  1088. // Can the ISAPI DLL get more than one request at the same time? If
  1089. // so we need to queue control changes to SSTs.
  1090. #ifdef NEVER
  1091. UPNP_PROPERTY * rgProps;
  1092. RgPropsFromSst(&psvc->sst, &rgProps);
  1093. if (RegisterUpnpEventSource(psvc->szEvtUrl, psvc->sst.cRows, rgProps))
  1094. {
  1095. }
  1096. else
  1097. {
  1098. TraceTag(ttidUpdiag, "Failed to register %s as an event source! Error = %d.",
  1099. psvc->szEvtUrl, GetLastError());
  1100. }
  1101. #endif
  1102. }
  1103. else
  1104. {
  1105. Usage(iCmd);
  1106. }
  1107. return FALSE;
  1108. }
  1109. BOOL DoSwitchCd(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs)
  1110. {
  1111. Assert(g_ctx.ectx == CTX_ROOT || g_ctx.ectx == CTX_CD);
  1112. if (cArgs == 2)
  1113. {
  1114. DWORD idev;
  1115. UPNPDEV * pdev;
  1116. idev = _tcstoul(rgArgs[1], NULL, 10);
  1117. if (g_ctx.ectx == CTX_CD)
  1118. {
  1119. if (idev &&
  1120. idev <= PDevCur()->cDevs &&
  1121. PDevCur()->rgDevs[idev - 1])
  1122. {
  1123. pdev = PDevCur()->rgDevs[idev - 1];
  1124. }
  1125. }
  1126. else
  1127. {
  1128. if (idev &&
  1129. idev <= g_params.cCd &&
  1130. g_params.rgCd[idev - 1])
  1131. {
  1132. pdev = g_params.rgCd[idev - 1];
  1133. }
  1134. }
  1135. if (pdev)
  1136. {
  1137. PushDev(pdev);
  1138. }
  1139. else
  1140. {
  1141. _tprintf(TEXT("%d is not a valid CD index!\n"), idev);
  1142. }
  1143. }
  1144. else
  1145. {
  1146. Usage(iCmd);
  1147. }
  1148. return FALSE;
  1149. }
  1150. BOOL DoDelCd(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs)
  1151. {
  1152. Assert(g_ctx.ectx == CTX_ROOT);
  1153. if (cArgs == 2)
  1154. {
  1155. DWORD idev;
  1156. UPNPDEV * pdev;
  1157. idev = _tcstoul(rgArgs[1], NULL, 10);
  1158. pdev = g_params.rgCd[idev - 1];
  1159. if (idev &&
  1160. idev <= g_params.cCd &&
  1161. pdev)
  1162. {
  1163. _tprintf(TEXT("Deleted device %s.\n"), pdev->szFriendlyName);
  1164. // Move last item into gap and decrement the count
  1165. g_params.rgCd[idev - 1] = g_params.rgCd[g_params.cCd - 1];
  1166. CleanupCd(pdev);
  1167. g_params.cCd--;
  1168. }
  1169. else
  1170. {
  1171. _tprintf(TEXT("%d is not a valid CD index!\n"), idev);
  1172. }
  1173. }
  1174. else
  1175. {
  1176. Usage(iCmd);
  1177. }
  1178. return FALSE;
  1179. }
  1180. BOOL DoListDevices(DWORD iCmd, DWORD cArgs, LPTSTR *rgArgs)
  1181. {
  1182. DWORD idev;
  1183. if (g_ctx.ectx == CTX_ROOT)
  1184. {
  1185. _tprintf(TEXT("Listing all Controlled Devices\n"));
  1186. _tprintf(TEXT("------------------------------\n"));
  1187. for (idev = 0; idev < g_params.cCd; idev++)
  1188. {
  1189. _tprintf(TEXT("%d) %s\n"), idev + 1, g_params.rgCd[idev]->szFriendlyName);
  1190. }
  1191. _tprintf(TEXT("------------------------------\n\n"));
  1192. }
  1193. else if (g_ctx.ectx == CTX_CD)
  1194. {
  1195. _tprintf(TEXT("Listing all sub-devices of %s\n"), PDevCur()->szFriendlyName);
  1196. _tprintf(TEXT("------------------------------\n"));
  1197. for (idev = 0; idev < PDevCur()->cDevs; idev++)
  1198. {
  1199. _tprintf(TEXT("%d) %s\n"), idev + 1, PDevCur()->rgDevs[idev]->szFriendlyName);
  1200. }
  1201. _tprintf(TEXT("------------------------------\n\n"));
  1202. }
  1203. return FALSE;
  1204. }
  1205. VOID CleanupCd(UPNPDEV *pcd)
  1206. {
  1207. DWORD i;
  1208. for (i = 0; i < pcd->cDevs; i++)
  1209. {
  1210. CleanupCd(pcd->rgDevs[i]);
  1211. }
  1212. for (i = 0; i < pcd->cSvcs; i++)
  1213. {
  1214. CleanupService(pcd->rgSvcs[i]);
  1215. }
  1216. for (i = 0; i < 3; i++)
  1217. {
  1218. if (pcd->hSvc[i] && (pcd->hSvc[i] != INVALID_HANDLE_VALUE))
  1219. {
  1220. DeregisterService(pcd->hSvc[i], TRUE);
  1221. }
  1222. }
  1223. delete pcd;
  1224. }