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.

779 lines
21 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997.
  5. //
  6. // File: E V T O B J . C P P
  7. //
  8. // Contents: Implements the Eventing Manager object for the UPnP Device
  9. // Host
  10. //
  11. // Notes:
  12. //
  13. // Author: danielwe 7 Aug 2000
  14. //
  15. //----------------------------------------------------------------------------
  16. #include <pch.h>
  17. #pragma hdrstop
  18. #include <msxml2.h>
  19. #include <upnpatl.h>
  20. #include "hostp.h"
  21. #include "upnphost.h"
  22. #include "evtobj.h"
  23. #include "evtapi.h"
  24. #include "ncbase.h"
  25. #include "ncxml.h"
  26. #include "ComUtility.h"
  27. #include "uhcommon.h"
  28. //
  29. // IUPnPEventingManager
  30. //
  31. //+---------------------------------------------------------------------------
  32. //
  33. // Member: CUPnPEventingManager::Initialize
  34. //
  35. // Purpose: Initializes the Eventing Manager object for a hosted service
  36. //
  37. // Arguments:
  38. // szUdn [in] UDN of the device
  39. // szSid [in] Service identifier of the service within the device
  40. // puap [in] Interface pointer to the service's automation proxy
  41. // punkSvc [in] Interface pointer to the service's object
  42. //
  43. // Returns: S_OK if success, E_OUTOFMEMORY, or any other OLE interface
  44. // error code
  45. //
  46. // Author: danielwe 8 Aug 2000
  47. //
  48. // Notes:
  49. //
  50. STDMETHODIMP CUPnPEventingManager::Initialize(LPCWSTR szUdn,
  51. LPCWSTR szSid,
  52. IUPnPAutomationProxy *puap,
  53. IUnknown *punkSvc,
  54. BOOL bRunning)
  55. {
  56. HRESULT hr = S_OK;
  57. DWORD cch;
  58. hr = HrIsAllowedCOMCallLocality(CALL_LOCALITY_INPROC);
  59. if (FAILED(hr))
  60. {
  61. goto Cleanup;
  62. }
  63. Assert(szUdn);
  64. Assert(szSid);
  65. Assert(punkSvc);
  66. Assert(puap);
  67. Assert(!m_szEsid);
  68. Assert(!m_puap);
  69. Assert(!m_pues);
  70. AddRefObj(m_puap = puap);
  71. cch = lstrlen(szUdn) + lstrlen(szSid) + lstrlen(L"+") + 1;
  72. m_szEsid = new WCHAR[cch];
  73. if (!m_szEsid)
  74. {
  75. hr = E_OUTOFMEMORY;
  76. }
  77. else
  78. {
  79. // Make the event source identifier by combining the UDN and SID
  80. lstrcpy(m_szEsid, szUdn);
  81. lstrcat(m_szEsid, L"+");
  82. lstrcat(m_szEsid, szSid);
  83. hr = punkSvc->QueryInterface(IID_IUPnPEventSource, (LPVOID *)&m_pues);
  84. if (SUCCEEDED(hr))
  85. {
  86. if(bRunning)
  87. {
  88. hr = HrCopyProxyIdentity(m_pues, punkSvc);
  89. }
  90. if(SUCCEEDED(hr))
  91. {
  92. // Get a reference to ourselves to hand out to the hosted service
  93. //
  94. hr = m_pues->Advise(this);
  95. }
  96. if (FAILED(hr))
  97. {
  98. m_pues->Release();
  99. m_pues = NULL;
  100. }
  101. }
  102. else
  103. {
  104. TraceError("CUPnPEventingManager::Initialize - Object passed in"
  105. " does not support IUPnPEventSource!", hr);
  106. }
  107. }
  108. if (FAILED(hr))
  109. {
  110. delete [] m_szEsid;
  111. m_szEsid = NULL;
  112. }
  113. Cleanup:
  114. TraceError("CUPnPEventingManager::Initialize", hr);
  115. return hr;
  116. }
  117. //+---------------------------------------------------------------------------
  118. //
  119. // Member: CUPnPEventingManager::AddSubscriber
  120. //
  121. // Purpose:
  122. //
  123. // Arguments:
  124. // cszUrl [in] Count of callback URLs
  125. // rgszCallbackUrl [in] List of callback URLs to which NOTIFY
  126. // requests will be sent
  127. // dwIpAddr [in] Local IP address that the subscribe came in on
  128. // pcsecTimeout [in out] On entry, this is the requested timeout from
  129. // the control point.
  130. // On exit, this is the timeout the device chose
  131. // pszSid [out] Returns the SID (subscription identifier) for
  132. // the new subscription
  133. //
  134. // Returns:
  135. //
  136. // Author: danielwe 2000/12/28
  137. //
  138. // Notes:
  139. //
  140. STDMETHODIMP CUPnPEventingManager::AddSubscriber(DWORD cszUrl,
  141. LPCWSTR *rgszCallbackUrl,
  142. DWORD dwIpAddr,
  143. DWORD *pcsecTimeout,
  144. LPWSTR *pszSid)
  145. {
  146. HRESULT hr = S_OK;
  147. LPWSTR szBody = NULL;
  148. DWORD cVars;
  149. LPWSTR * rgszNames;
  150. LPWSTR * rgszTypes;
  151. VARIANT * rgvarValues;
  152. DWORD cDispids;
  153. DISPID * rgDispids = NULL;
  154. hr = HrIsAllowedCOMCallLocality(CALL_LOCALITY_INPROC);
  155. if (FAILED(hr))
  156. {
  157. return hr;
  158. }
  159. AssertSz(m_szEsid, "What? Did we not get initialized or something?");
  160. AssertSz(m_puap, "Automation proxy not initialized?");
  161. Assert(rgszCallbackUrl);
  162. Assert(cszUrl);
  163. Assert(pszSid);
  164. *pszSid = NULL;
  165. hr = m_puap->GetDispIdsOfEventedVariables(&cDispids, &rgDispids);
  166. if (SUCCEEDED(hr))
  167. {
  168. hr = m_puap->QueryStateVariablesByDispIds(cDispids, rgDispids, &cVars,
  169. &rgszNames, &rgvarValues,
  170. &rgszTypes);
  171. if (SUCCEEDED(hr))
  172. {
  173. hr = HrComposeEventBody(m_puap, cVars, rgszNames, rgszTypes,
  174. rgvarValues, &szBody);
  175. if (SUCCEEDED(hr))
  176. {
  177. LPWSTR szSid;
  178. hr = HrAddSubscriber(m_szEsid, dwIpAddr, cszUrl,
  179. rgszCallbackUrl, szBody,
  180. pcsecTimeout, &szSid);
  181. if (SUCCEEDED(hr))
  182. {
  183. *pszSid = (LPWSTR)CoTaskMemAlloc(CbOfSzAndTerm(szSid));
  184. if (*pszSid)
  185. {
  186. lstrcpy(*pszSid, szSid);
  187. }
  188. else
  189. {
  190. TraceError("CUPnPEventingManager::AddSubscriber - "
  191. "CoTaskMemAlloc()", hr);
  192. hr = E_OUTOFMEMORY;
  193. }
  194. delete [] szSid;
  195. }
  196. delete [] szBody;
  197. }
  198. DWORD ivar;
  199. for (ivar = 0; ivar < cVars; ivar++)
  200. {
  201. CoTaskMemFree(rgszTypes[ivar]);
  202. CoTaskMemFree(rgszNames[ivar]);
  203. VariantClear(&rgvarValues[ivar]);
  204. }
  205. CoTaskMemFree(rgszTypes);
  206. CoTaskMemFree(rgszNames);
  207. CoTaskMemFree(rgvarValues);
  208. }
  209. CoTaskMemFree(rgDispids);
  210. }
  211. TraceError("CUPnPEventingManager::AddSubscriber", hr);
  212. return hr;
  213. }
  214. //+---------------------------------------------------------------------------
  215. //
  216. // Member: CUPnPEventingManager::RenewSubscriber
  217. //
  218. // Purpose: Renews an existing subscriber to a hosted service
  219. //
  220. // Arguments:
  221. // pcsecTimeout [in out] On entry, this is the requested timeout from
  222. // the control point.
  223. // On exit, this is the timeout the device chose
  224. // szSid [in] The SID (subscription identifier) of the
  225. // subscriber to renew
  226. //
  227. // Returns: S_OK if success, E_OUTOFMEMORY, or any other OLE interface
  228. // error code
  229. //
  230. // Author: danielwe 8 Aug 2000
  231. //
  232. // Notes:
  233. //
  234. STDMETHODIMP CUPnPEventingManager::RenewSubscriber(DWORD *pcsecTimeout,
  235. LPWSTR szSid)
  236. {
  237. HRESULT hr = S_OK;
  238. hr = HrIsAllowedCOMCallLocality(CALL_LOCALITY_INPROC);
  239. if (FAILED(hr))
  240. {
  241. return hr;
  242. }
  243. AssertSz(m_szEsid, "What? Did we not get initialized or something?");
  244. Assert(szSid);
  245. hr = HrRenewSubscriber(m_szEsid, pcsecTimeout, szSid);
  246. TraceError("CUPnPEventingManager::RenewSubscriber", hr);
  247. return hr;
  248. }
  249. //+---------------------------------------------------------------------------
  250. //
  251. // Member: CUPnPEventingManager::RemoveSubscriber
  252. //
  253. // Purpose: Removes a subscriber from the list of subscribers to a hosted
  254. // service
  255. //
  256. // Arguments:
  257. // szSid [in] The SID (subscription identifier) of the
  258. // subscriber to renew
  259. //
  260. // Returns: S_OK if success, E_OUTOFMEMORY, or any other OLE interface
  261. // error code
  262. //
  263. // Author: danielwe 8 Aug 2000
  264. //
  265. // Notes:
  266. //
  267. STDMETHODIMP CUPnPEventingManager::RemoveSubscriber(LPWSTR szSid)
  268. {
  269. HRESULT hr = S_OK;
  270. hr = HrIsAllowedCOMCallLocality(CALL_LOCALITY_INPROC);
  271. if (FAILED(hr))
  272. {
  273. return hr;
  274. }
  275. AssertSz(m_szEsid, "What? Did we not get initialized or something?");
  276. Assert(szSid);
  277. hr = HrRemoveSubscriber(m_szEsid, szSid);
  278. TraceError("CUPnPEventingManager::RemoveSubscriber", hr);
  279. return hr;
  280. }
  281. //
  282. // IUPnPEventSink
  283. //
  284. //+---------------------------------------------------------------------------
  285. //
  286. // Member: CUPnPEventingManager::OnStateChanged
  287. //
  288. // Purpose: Notifies the eventing manager that the state of a service on
  289. // a hosted device has changed
  290. //
  291. // Arguments:
  292. // cChanges [in] Number of state variables that have changed
  293. // rgdispidChanges [in] Array of DISPIDs for those state variables
  294. //
  295. // Returns: S_OK if success, E_OUTOFMEMORY, or any other OLE interface
  296. // error code otherwise.
  297. //
  298. // Author: danielwe 2000/09/21
  299. //
  300. // Notes:
  301. //
  302. STDMETHODIMP CUPnPEventingManager::OnStateChanged(DWORD cChanges,
  303. DISPID rgdispidChanges[])
  304. {
  305. HRESULT hr = S_OK;
  306. LPWSTR szBody = NULL;
  307. DWORD cVars;
  308. LPWSTR * rgszNames;
  309. LPWSTR * rgszTypes;
  310. VARIANT * rgvarValues;
  311. hr = HrIsAllowedCOMCallLocality(CALL_LOCALITY_INPROC);
  312. if (FAILED(hr))
  313. {
  314. goto Cleanup;
  315. }
  316. AssertSz(m_szEsid, "What? Did we not get initialized or something?");
  317. AssertSz(m_puap, "Automation proxy not initialized?");
  318. if (!m_pues)
  319. {
  320. hr = E_UNEXPECTED;
  321. }
  322. else if (!cChanges || !rgdispidChanges)
  323. {
  324. hr = E_INVALIDARG;
  325. TraceError("OnStateChanged() called, but nothing to do.", hr);
  326. }
  327. else
  328. {
  329. hr = m_puap->QueryStateVariablesByDispIds(cChanges, rgdispidChanges,
  330. &cVars, &rgszNames,
  331. &rgvarValues, &rgszTypes);
  332. if (SUCCEEDED(hr))
  333. {
  334. hr = HrComposeEventBody(m_puap, cVars, rgszNames, rgszTypes,
  335. rgvarValues, &szBody);
  336. if (SUCCEEDED(hr))
  337. {
  338. hr = HrSubmitEvent(m_szEsid, szBody);
  339. delete [] szBody;
  340. }
  341. DWORD ivar;
  342. for (ivar = 0; ivar < cVars; ivar++)
  343. {
  344. CoTaskMemFree(rgszTypes[ivar]);
  345. CoTaskMemFree(rgszNames[ivar]);
  346. VariantClear(&rgvarValues[ivar]);
  347. }
  348. CoTaskMemFree(rgvarValues);
  349. CoTaskMemFree(rgszTypes);
  350. CoTaskMemFree(rgszNames);
  351. }
  352. }
  353. Cleanup:
  354. TraceError("CUPnPEventingManager::OnStateChanged", hr);
  355. return hr;
  356. }
  357. //+---------------------------------------------------------------------------
  358. //
  359. // Member: CUPnPEventingManager::OnStateChangedSafe
  360. //
  361. // Purpose: Same as OnStateChanged, except this is for VB users that need
  362. // to pass the array of DISPIDs in a SafeArray.
  363. //
  364. // Arguments:
  365. // psa [in] SafeArray of DISPIDs that have changed
  366. //
  367. // Returns: Same as OnStateChanged
  368. //
  369. // Author: danielwe 2000/09/21
  370. //
  371. // Notes:
  372. //
  373. STDMETHODIMP CUPnPEventingManager::OnStateChangedSafe(VARIANT varsadispidChanges)
  374. {
  375. HRESULT hr = S_OK;
  376. DISPID HUGEP * rgdispids;
  377. SAFEARRAY * psa;
  378. hr = HrIsAllowedCOMCallLocality(CALL_LOCALITY_INPROC);
  379. if (FAILED(hr))
  380. {
  381. goto Cleanup;
  382. }
  383. psa = V_ARRAY(&varsadispidChanges);
  384. if (psa)
  385. {
  386. // Get a pointer to the elements of the array.
  387. hr = SafeArrayAccessData(psa, (void HUGEP**)&rgdispids);
  388. if (SUCCEEDED(hr))
  389. {
  390. hr = OnStateChanged(psa->rgsabound[0].cElements, rgdispids);
  391. SafeArrayUnaccessData(psa);
  392. }
  393. }
  394. else
  395. {
  396. hr = E_INVALIDARG;
  397. }
  398. Cleanup:
  399. TraceError("CUPnPEventingManager::OnStateChangedSafe", hr);
  400. return hr;
  401. }
  402. //+---------------------------------------------------------------------------
  403. //
  404. // Member: CUPnPEventingManager::Shutdown
  405. //
  406. // Purpose: Tells the eventing manager object to go away
  407. //
  408. // Arguments:
  409. // (none)
  410. //
  411. // Returns: E_UNEXPECTED if the object was never initialized.
  412. //
  413. // Author: danielwe 2000/09/21
  414. //
  415. // Notes:
  416. //
  417. STDMETHODIMP CUPnPEventingManager::Shutdown()
  418. {
  419. HRESULT hr = S_OK;
  420. hr = HrIsAllowedCOMCallLocality(CALL_LOCALITY_INPROC);
  421. if (FAILED(hr))
  422. {
  423. goto Cleanup;
  424. }
  425. if (!m_pues)
  426. {
  427. hr = E_UNEXPECTED;
  428. }
  429. else
  430. {
  431. hr = m_pues->Unadvise(this);
  432. ReleaseObj(m_pues);
  433. m_pues = NULL;
  434. }
  435. Cleanup:
  436. TraceError("CUPnPEventingManager::Shutdown", hr);
  437. return hr;
  438. }
  439. //
  440. // ATL Methods
  441. //
  442. //+---------------------------------------------------------------------------
  443. //
  444. // Member: CUPnPEventingManager::FinalRelease
  445. //
  446. // Purpose: Called when the Eventing Manager object is released for the
  447. // last time
  448. //
  449. // Arguments:
  450. // (none)
  451. //
  452. // Returns: Not much.
  453. //
  454. // Author: danielwe 8 Aug 2000
  455. //
  456. // Notes:
  457. //
  458. HRESULT CUPnPEventingManager::FinalRelease()
  459. {
  460. HRESULT hr = S_OK;
  461. delete [] m_szEsid;
  462. ReleaseObj(m_puap);
  463. TraceError("CUPnPEventingManager::FinalRelease", hr);
  464. return hr;
  465. }
  466. //
  467. // Private functions
  468. //
  469. //+---------------------------------------------------------------------------
  470. //
  471. // Function: HrCreateElement
  472. //
  473. // Purpose: Creates an element in the specified DOM Document
  474. //
  475. // Arguments:
  476. // pxdd [in] DOM Document to create element in
  477. // szName [in] Element name
  478. // ppxdnElement [out] Returns the newly created element
  479. //
  480. // Returns: S_OK if successful, E_OUTOFMEMORY, or OLE error code
  481. //
  482. // Author: danielwe 16 Aug 2000
  483. //
  484. // Notes: The element is NOT automatically inserted into the document
  485. //
  486. HRESULT HrCreateElement(IXMLDOMDocument *pxdd, LPCWSTR szName,
  487. IXMLDOMNode **ppxdnElement)
  488. {
  489. HRESULT hr = S_OK;
  490. BSTR bstrElementName;
  491. Assert(pxdd);
  492. Assert(ppxdnElement);
  493. *ppxdnElement = NULL;
  494. bstrElementName = SysAllocString(szName);
  495. if (bstrElementName)
  496. {
  497. IXMLDOMNode * pxdn;
  498. BSTR bstrNamespaceURI;
  499. bstrNamespaceURI = SysAllocString(L"urn:schemas-upnp-org:event-1-0");
  500. if (bstrNamespaceURI)
  501. {
  502. VARIANT varNodeType;
  503. VariantInit(&varNodeType);
  504. varNodeType.vt = VT_I4;
  505. V_I4(&varNodeType) = (int) NODE_ELEMENT;
  506. hr = pxdd->createNode(varNodeType, bstrElementName,
  507. bstrNamespaceURI, &pxdn);
  508. if (SUCCEEDED(hr))
  509. {
  510. *ppxdnElement = pxdn;
  511. }
  512. else
  513. {
  514. ReleaseObj(pxdn);
  515. }
  516. SysFreeString(bstrNamespaceURI);
  517. }
  518. else
  519. {
  520. hr = E_OUTOFMEMORY;
  521. }
  522. SysFreeString(bstrElementName);
  523. }
  524. else
  525. {
  526. hr = E_OUTOFMEMORY;
  527. }
  528. TraceError("HrAddRootElement", hr);
  529. return hr;
  530. }
  531. //+---------------------------------------------------------------------------
  532. //
  533. // Function: HrComposeEventBody
  534. //
  535. // Purpose: Composes the event notification body given a list of names,
  536. // values, and data types
  537. //
  538. // Arguments:
  539. // cVars [in] Number of state variables to work with
  540. // rgszNames [in] List of variable names
  541. // rgszTypes [in] List of variable types
  542. // rgvarValues [in] List of variable values
  543. // pszBody [out] Returns newly created XML body as a string
  544. //
  545. // Returns: S_OK if successful, E_OUTOFMEMORY, or OLE error code
  546. //
  547. // Author: danielwe 16 Aug 2000
  548. //
  549. // Notes: pszBody must be freed by the caller with delete []
  550. //
  551. HRESULT HrComposeEventBody(IUPnPAutomationProxy* puap, DWORD cVars, LPWSTR *rgszNames,
  552. LPWSTR *rgszTypes, VARIANT *rgvarValues, LPWSTR *pszBody)
  553. {
  554. HRESULT hr = S_OK;
  555. IXMLDOMDocument * pxdd;
  556. Assert(pszBody);
  557. Assert(cVars);
  558. *pszBody = NULL;
  559. // Create a new XML DOM Document
  560. //
  561. hr = CoCreateInstance(CLSID_DOMDocument30, NULL, CLSCTX_INPROC_SERVER,
  562. IID_IXMLDOMDocument, (LPVOID *) &pxdd);
  563. if (SUCCEEDED(hr))
  564. {
  565. IXMLDOMNode * pxdnRoot;
  566. hr = HrAppendProcessingInstruction(pxdd, L"xml", L"version=\"1.0\"");
  567. if (SUCCEEDED(hr))
  568. {
  569. hr = HrCreateElement(pxdd, L"e:propertyset", &pxdnRoot);
  570. if (SUCCEEDED(hr))
  571. {
  572. IXMLDOMElement* pxde;
  573. hr = pxdnRoot->QueryInterface(IID_IXMLDOMElement, (void**)&pxde);
  574. if (SUCCEEDED(hr))
  575. {
  576. Assert(puap);
  577. LPWSTR szServiceType = NULL;
  578. hr = puap->GetServiceType(&szServiceType);
  579. if (SUCCEEDED(hr))
  580. {
  581. HrSetTextAttribute(pxde, L"xmlns:s", szServiceType);
  582. CoTaskMemFree(szServiceType);
  583. }
  584. pxde->Release();
  585. }
  586. }
  587. }
  588. if (SUCCEEDED(hr))
  589. {
  590. DWORD iVar;
  591. // Loop thru each passed in variable and create a typed element
  592. // for each, adding to a new container element for "property" as
  593. // described in the UPnP architecture 1.0.
  594. for (iVar = 0;
  595. iVar < cVars && SUCCEEDED(hr);
  596. iVar++)
  597. {
  598. IXMLDOMNode * pxdnProp;
  599. hr = HrCreateElement(pxdd, L"e:property", &pxdnProp);
  600. if (SUCCEEDED(hr))
  601. {
  602. IXMLDOMElement * pxdeVar;
  603. LPWSTR szPrefixedName = new WCHAR[lstrlenW(rgszNames[iVar]) + 3];
  604. if (szPrefixedName)
  605. {
  606. lstrcpyW(szPrefixedName, L"s:");
  607. lstrcatW(szPrefixedName, rgszNames[iVar]);
  608. hr = HrCreateElementWithType(pxdd, szPrefixedName,
  609. (LPCWSTR)rgszTypes[iVar],
  610. rgvarValues[iVar], &pxdeVar);
  611. if (SUCCEEDED(hr))
  612. {
  613. // Add both the container "<e:property>" and the
  614. // variable "<e:[varName]>" to the root
  615. //
  616. hr = pxdnRoot->appendChild(pxdnProp, NULL);
  617. hr = pxdnProp->appendChild(pxdeVar, NULL);
  618. ReleaseObj(pxdeVar);
  619. }
  620. ReleaseObj(pxdnProp);
  621. delete [] szPrefixedName;
  622. }
  623. else
  624. {
  625. hr = E_OUTOFMEMORY;
  626. }
  627. }
  628. }
  629. if (SUCCEEDED(hr))
  630. {
  631. // Add the root element to the DOM itself
  632. hr = pxdd->appendChild(pxdnRoot, NULL);
  633. }
  634. // If all went well, ask the document to give us the XML as a string
  635. //
  636. if (SUCCEEDED(hr))
  637. {
  638. BSTR bstrBody;
  639. hr = pxdd->get_xml(&bstrBody);
  640. if (SUCCEEDED(hr))
  641. {
  642. DWORD cch = SysStringLen(bstrBody) + 1;
  643. LPWSTR szBody;
  644. szBody = new WCHAR[cch];
  645. if (szBody)
  646. {
  647. lstrcpy(szBody, bstrBody);
  648. TraceTag(ttidDefault, "Body is %S", szBody);
  649. *pszBody = szBody;
  650. }
  651. else
  652. {
  653. hr = E_OUTOFMEMORY;
  654. }
  655. SysFreeString(bstrBody);
  656. }
  657. }
  658. ReleaseObj(pxdnRoot);
  659. }
  660. ReleaseObj(pxdd);
  661. }
  662. TraceError("HrComposeEventBody", hr);
  663. return hr;
  664. }