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.

1941 lines
54 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997.
  5. //
  6. // File: E V T A P I . C P P
  7. //
  8. // Contents: Private low-level APIs dealing with UPnP events.
  9. //
  10. // Notes:
  11. //
  12. // Author: danielwe 18 Oct 1999
  13. //
  14. //----------------------------------------------------------------------------
  15. #include <pch.h>
  16. #pragma hdrstop
  17. #include <hostinc.h>
  18. #include "evtapi.h"
  19. #include "stdio.h"
  20. #include "interfacelist.h"
  21. #include <wininet.h>
  22. #include <winsock.h>
  23. HANDLE g_hTimerQ = NULL;
  24. CRITICAL_SECTION g_csListEventSource;
  25. UPNP_EVENT_SOURCE * g_pesList = NULL;
  26. HINTERNET g_hInetSess = NULL;
  27. static const DWORD c_csecTimeout = 30; // Internet connect
  28. // timeout (in seconds)
  29. // Default subscription timeout (6 hours)
  30. static const DWORD c_csecDefSubsTimeout = 60 * 60 * 6;
  31. // Minimum subscription timeout (10 minutes??)
  32. static const DWORD c_csecMinSubsTimeout = 60 * 10;
  33. VOID FreeEventSourceBlocking(UPNP_EVENT_SOURCE * pes);
  34. //+---------------------------------------------------------------------------
  35. //
  36. // Function: HrInitEventApi
  37. //
  38. // Purpose: Initializes the low-level eventing API
  39. //
  40. // Arguments:
  41. // (none)
  42. //
  43. // Returns: Nothing
  44. //
  45. // Author: danielwe 4 Aug 2000
  46. //
  47. // Notes:
  48. //
  49. HRESULT HrInitEventApi()
  50. {
  51. HRESULT hr = S_OK;
  52. InitializeCriticalSection(&g_csListEventSource);
  53. if (SUCCEEDED(hr))
  54. {
  55. AssertSz(!g_hTimerQ, "Already initialized timer queue?!?");
  56. g_hTimerQ = CreateTimerQueue();
  57. if (!g_hTimerQ)
  58. {
  59. hr = HrFromLastWin32Error();
  60. TraceError("HrInitEventApi: CreateTimerQueue", hr);
  61. }
  62. }
  63. TraceError("HrInitEventApi", hr);
  64. return hr;
  65. }
  66. HRESULT HrInitInternetSession()
  67. {
  68. HRESULT hr = S_OK;
  69. AssertSz(!g_hInetSess, "Already initialized?");
  70. g_hInetSess = InternetOpen(L"Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)",
  71. INTERNET_OPEN_TYPE_DIRECT,
  72. NULL, NULL, 0);
  73. if (g_hInetSess)
  74. {
  75. DWORD dwTimeout = c_csecTimeout * 1000;
  76. if (!InternetSetOption(g_hInetSess, INTERNET_OPTION_CONNECT_TIMEOUT,
  77. (LPVOID)&dwTimeout, sizeof(DWORD)))
  78. {
  79. hr = HrFromLastWin32Error();
  80. TraceError("HrFromLastWin32Error: InternetSetOption",
  81. HrFromLastWin32Error());
  82. }
  83. else
  84. {
  85. TraceTag(ttidEventServer, "HrFromLastWin32Error: Suscessfully set "
  86. "internet connect timeout to %d seconds",
  87. c_csecTimeout);
  88. }
  89. if(SUCCEEDED(hr))
  90. {
  91. INTERNET_PROXY_INFO ipi;
  92. ZeroMemory(&ipi, sizeof(ipi));
  93. ipi.dwAccessType = INTERNET_OPEN_TYPE_DIRECT;
  94. if(!InternetSetOption(g_hInetSess, INTERNET_OPTION_PROXY, &ipi, sizeof(ipi)))
  95. {
  96. hr = HrFromLastWin32Error();
  97. TraceError("HrFromLastWin32Error: InternetSetOption",
  98. HrFromLastWin32Error());
  99. }
  100. }
  101. }
  102. else
  103. {
  104. hr = HrFromLastWin32Error();
  105. TraceError("HrInitInternetSession: InternetOpen", hr);
  106. }
  107. TraceError("HrInitInternetSession", hr);
  108. return hr;
  109. }
  110. //+---------------------------------------------------------------------------
  111. //
  112. // Function: DeInitEventApi
  113. //
  114. // Purpose: De-initializes the low-level eventing API
  115. //
  116. // Arguments:
  117. // (none)
  118. //
  119. // Returns: Nothing
  120. //
  121. // Author: danielwe 4 Aug 2000
  122. //
  123. // Notes: The debug version fills the critsec struct after deleting it
  124. // to catch use afterwards
  125. //
  126. VOID DeInitEventApi()
  127. {
  128. UPNP_EVENT_SOURCE * pesCur;
  129. UPNP_EVENT_SOURCE * pesNext;
  130. // Delete any remaining event sources from the list. This will block until
  131. // all event sources have been deleted
  132. //
  133. EnterCriticalSection(&g_csListEventSource);
  134. for (pesCur = g_pesList; pesCur; pesCur = pesNext)
  135. {
  136. pesNext = pesCur->pesNext;
  137. FreeEventSourceBlocking(pesCur);
  138. }
  139. g_pesList = NULL;
  140. LeaveCriticalSection(&g_csListEventSource);
  141. if (g_hInetSess)
  142. {
  143. InternetCloseHandle(g_hInetSess);
  144. g_hInetSess = NULL;
  145. }
  146. if (g_hTimerQ)
  147. {
  148. // This will wait for all callback threads to finish before continuing
  149. // (in other words, it blocks)
  150. //
  151. DeleteTimerQueueEx(g_hTimerQ, INVALID_HANDLE_VALUE);
  152. TraceTag(ttidEventServer, "DeInitEventApi: Deleted timer queue");
  153. g_hTimerQ = NULL;
  154. }
  155. DeleteCriticalSection(&g_csListEventSource);
  156. #if DBG
  157. FillMemory(&g_csListEventSource, sizeof(CRITICAL_SECTION), 0xDA);
  158. #endif
  159. }
  160. //+---------------------------------------------------------------------------
  161. //
  162. // Function: FreeSubscriber
  163. //
  164. // Purpose: Frees the memory and resources used by a subscriber and frees
  165. // the subscriber itself
  166. //
  167. // Arguments:
  168. // psub [in] Subscriber to free
  169. //
  170. // Returns: Nothing
  171. //
  172. // Author: danielwe 4 Aug 2000
  173. //
  174. // Notes:
  175. //
  176. VOID FreeSubscriber(UPNP_SUBSCRIBER *psub)
  177. {
  178. UPNP_EVENT * pevtCur;
  179. UPNP_EVENT * pevtNext;
  180. HANDLE hWait = NULL;
  181. if (!psub)
  182. {
  183. return;
  184. }
  185. #if DBG
  186. if (psub->szSid)
  187. {
  188. TraceTag(ttidEventServer, "Freeing subscriber %S", psub->szSid);
  189. }
  190. #endif
  191. DWORD isz;
  192. for (isz = 0; isz < psub->cszUrl; isz++)
  193. {
  194. delete [] psub->rgszUrl[isz];
  195. }
  196. delete [] psub->szSid;
  197. delete [] psub->rgszUrl;
  198. // Free the event queue
  199. //
  200. for (pevtCur = psub->pevtQueue;
  201. pevtCur;
  202. pevtCur = pevtNext)
  203. {
  204. delete [] pevtCur->szBody;
  205. pevtNext = pevtCur->pevtNext;
  206. delete pevtCur;
  207. }
  208. if (psub->hWait)
  209. {
  210. TraceTag(ttidEventServer, "About to call UnregisterWaitEx()");
  211. // This will wait for all callback threads to finish before continuing
  212. // (in other words, it blocks)
  213. //
  214. if (!UnregisterWaitEx(psub->hWait, INVALID_HANDLE_VALUE))
  215. {
  216. TraceError("FreeSubscriber: UnregisterWaitEx",
  217. HrFromLastWin32Error());
  218. }
  219. else
  220. {
  221. TraceTag(ttidEventServer, "FreeSubscriber: Unregistered wait");
  222. }
  223. }
  224. if (psub->hEventQ && psub->hEventQ != INVALID_HANDLE_VALUE)
  225. {
  226. CloseHandle(psub->hEventQ);
  227. TraceTag(ttidEventServer, "FreeSubscriber: Closed event handle");
  228. }
  229. if (psub->hTimer)
  230. {
  231. AssertSz(g_hTimerQ, "No timer queue??");
  232. if (!DeleteTimerQueueTimer(g_hTimerQ, psub->hTimer,
  233. INVALID_HANDLE_VALUE))
  234. {
  235. TraceError("FreeSubscriber: DeleteTimerQueueTimer",
  236. HrFromLastWin32Error());
  237. }
  238. else
  239. {
  240. TraceTag(ttidEventServer, "FreeSubscriber: Deleted timer "
  241. "queue timer");
  242. }
  243. }
  244. // Delete renewal params
  245. //
  246. delete [] psub->ur.szEsid;
  247. delete [] psub->ur.szSid;
  248. // Delete event queue worker wait params
  249. //
  250. delete [] psub->uwp.szEsid;
  251. delete [] psub->uwp.szSid;
  252. delete psub;
  253. }
  254. VOID FreeEventSourceBlocking(UPNP_EVENT_SOURCE * pes)
  255. {
  256. UPNP_SUBSCRIBER * psubCur;
  257. UPNP_SUBSCRIBER * psubNext;
  258. for (psubCur = pes->psubList;
  259. psubCur;
  260. psubCur = psubNext)
  261. {
  262. psubNext = psubCur->psubNext;
  263. FreeSubscriber(psubCur);
  264. }
  265. delete [] pes->szEsid;
  266. delete pes;
  267. }
  268. //+---------------------------------------------------------------------------
  269. //
  270. // Function: FreeEventSourceWorker
  271. //
  272. // Purpose: Worker function to free an event source and the resources it
  273. // uses
  274. //
  275. // Arguments:
  276. // pvContext [in] Context data = event source to free
  277. //
  278. // Returns: 0
  279. //
  280. // Author: danielwe 4 Aug 2000
  281. //
  282. // Notes: This function is always called from a separate thread
  283. //
  284. DWORD WINAPI FreeEventSourceWorker(LPVOID pvContext)
  285. {
  286. UPNP_EVENT_SOURCE * pes;
  287. pes = (UPNP_EVENT_SOURCE *)pvContext;
  288. Assert(pes);
  289. FreeEventSourceBlocking(pes);
  290. return 0;
  291. }
  292. //+---------------------------------------------------------------------------
  293. //
  294. // Function: FreeSubscriberWorker
  295. //
  296. // Purpose: Worker function to free a subscriber and the resources it uses
  297. //
  298. // Arguments:
  299. // pvContext [in] Context data = subscriber to free
  300. //
  301. // Returns: 0
  302. //
  303. // Author: danielwe 4 Aug 2000
  304. //
  305. // Notes: This function is always called from a separate thread
  306. //
  307. DWORD WINAPI FreeSubscriberWorker(LPVOID pvContext)
  308. {
  309. UPNP_SUBSCRIBER * psub;
  310. psub = (UPNP_SUBSCRIBER *)pvContext;
  311. Assert(psub);
  312. TraceTag(ttidEventServer, "FreeSubscriberWorker: Freeing subscriber %S",
  313. psub->szSid);
  314. FreeSubscriber(psub);
  315. return 0;
  316. }
  317. //+---------------------------------------------------------------------------
  318. //
  319. // Function: FreeEventSource
  320. //
  321. // Purpose: Frees an event source structure and the resources it uses
  322. //
  323. // Arguments:
  324. // pes [in] Event source to free
  325. //
  326. // Returns: Nothing
  327. //
  328. // Author: danielwe 4 Aug 2000
  329. //
  330. // Notes: The free is done asynchronously, on a separate thread
  331. //
  332. VOID FreeEventSource(UPNP_EVENT_SOURCE * pes)
  333. {
  334. if (pes)
  335. {
  336. #if DBG
  337. EnterCriticalSection(&g_csListEventSource);
  338. AssertSz(!PesFindEventSource(pes->szEsid), "I will not let you free"
  339. " an event source that's still in the global list!");
  340. LeaveCriticalSection(&g_csListEventSource);
  341. #endif
  342. TraceTag(ttidEventServer, "Queueing a work item to free event "
  343. "source %S", pes->szEsid);
  344. // Now that the event source is off the list and no external function can
  345. // access it anymore we can queue a work item to do the time consuming stuff
  346. //
  347. QueueUserWorkItem(FreeEventSourceWorker, (LPVOID)pes,
  348. WT_EXECUTELONGFUNCTION);
  349. }
  350. }
  351. //+---------------------------------------------------------------------------
  352. //
  353. // Function: HrRegisterEventSource
  354. //
  355. // Purpose: Registers a service as an event source
  356. //
  357. // Arguments:
  358. // szEsid [in] Event source identifier
  359. //
  360. // Returns: S_OK if successful, E_OUTOFMEMORY, or Win32 error
  361. //
  362. // Author: danielwe 10 Jul 2000
  363. //
  364. // Notes:
  365. //
  366. HRESULT HrRegisterEventSource(LPCWSTR szEsid)
  367. {
  368. HRESULT hr = S_OK;
  369. UPNP_EVENT_SOURCE * pesNew = NULL;
  370. Assert(szEsid && *szEsid);
  371. EnterCriticalSection(&g_csListEventSource);
  372. if (!PesFindEventSource(szEsid))
  373. {
  374. pesNew = new UPNP_EVENT_SOURCE;
  375. if (pesNew)
  376. {
  377. ZeroMemory(pesNew, sizeof(UPNP_EVENT_SOURCE));
  378. pesNew->szEsid = WszDupWsz(szEsid);
  379. if (!pesNew->szEsid)
  380. {
  381. hr = E_OUTOFMEMORY;
  382. }
  383. }
  384. else
  385. {
  386. hr = E_OUTOFMEMORY;
  387. }
  388. }
  389. else
  390. {
  391. TraceTag(ttidEventServer, "HrRegisterEventSource - duplicated event "
  392. "source %S", szEsid);
  393. hr = E_INVALIDARG;
  394. }
  395. if (SUCCEEDED(hr))
  396. {
  397. // Link in this event source at the head of the global list
  398. //
  399. pesNew->pesNext = g_pesList;
  400. g_pesList = pesNew;
  401. }
  402. LeaveCriticalSection(&g_csListEventSource);
  403. if (FAILED(hr))
  404. {
  405. FreeEventSource(pesNew);
  406. }
  407. else
  408. {
  409. //DbgDumpListEventSource();
  410. }
  411. TraceError("HrRegisterEventSource", hr);
  412. return hr;
  413. }
  414. //+---------------------------------------------------------------------------
  415. //
  416. // Function: HrDeregisterEventSource
  417. //
  418. // Purpose: Deregisters a service as an event source
  419. //
  420. // Arguments:
  421. // szEsid [in] Event source identifier
  422. //
  423. // Returns: S_OK if success, E_INVALIDARG
  424. //
  425. // Author: danielwe 4 Aug 2000
  426. //
  427. // Notes:
  428. //
  429. HRESULT HrDeregisterEventSource(LPCWSTR szEsid)
  430. {
  431. HRESULT hr = S_OK;
  432. UPNP_EVENT_SOURCE * pesCur;
  433. UPNP_EVENT_SOURCE * pesPrev;
  434. EnterCriticalSection(&g_csListEventSource);
  435. for (pesCur = pesPrev = g_pesList;
  436. pesCur;
  437. pesPrev = pesCur, pesCur = pesCur->pesNext)
  438. {
  439. if (!lstrcmpi(pesCur->szEsid, szEsid))
  440. {
  441. TraceTag(ttidEventServer, "Deregistering event source %S", szEsid);
  442. if (pesCur == g_pesList)
  443. {
  444. g_pesList = pesCur->pesNext;
  445. }
  446. else
  447. {
  448. AssertSz(pesPrev != pesCur, "Event sourcelist is messed up!");
  449. AssertSz(pesCur != g_pesList, "Event sourcelist is messed up!");
  450. pesPrev->pesNext = pesCur->pesNext;
  451. }
  452. break;
  453. }
  454. }
  455. if (pesCur)
  456. {
  457. FreeEventSource(pesCur);
  458. }
  459. else
  460. {
  461. TraceTag(ttidEventServer, "Event source %S not found!", szEsid);
  462. hr = E_INVALIDARG;
  463. }
  464. LeaveCriticalSection(&g_csListEventSource);
  465. //DbgDumpListEventSource();
  466. TraceError("HrDeregisterEventSource", hr);
  467. return hr;
  468. }
  469. //+---------------------------------------------------------------------------
  470. //
  471. // Function: SzGetNewSid
  472. //
  473. // Purpose: Returns a new "uuid:{SID}" identifier
  474. //
  475. // Arguments:
  476. // (none)
  477. //
  478. // Returns: Newly allocated SID string
  479. //
  480. // Author: danielwe 13 Oct 1999
  481. //
  482. // Notes: Caller must free the returned string with delete []
  483. //
  484. LPWSTR SzGetNewSid()
  485. {
  486. WCHAR szSid[256];
  487. UUID uuid;
  488. unsigned short *szUuid;
  489. if (UuidCreate(&uuid) == RPC_S_OK)
  490. {
  491. if (UuidToString(&uuid, &szUuid) == RPC_S_OK)
  492. {
  493. wsprintf(szSid, L"uuid:%s", szUuid);
  494. RpcStringFree(&szUuid);
  495. return WszDupWsz(szSid);
  496. }
  497. }
  498. return NULL;
  499. }
  500. //+---------------------------------------------------------------------------
  501. //
  502. // Function: EventQueueWorker
  503. //
  504. // Purpose: Worker function to remove an event off the event queue for
  505. // a specific subscriber and submit it to that subscriber
  506. //
  507. // Arguments:
  508. // pvContext [in] Context data = event source and subscriber
  509. // fTimeOut [in] UNUSED
  510. //
  511. // Returns: Nothing
  512. //
  513. // Author: danielwe 4 Aug 2000
  514. //
  515. // Notes: This function calls into WinINET
  516. //
  517. VOID WINAPI EventQueueWorker(LPVOID pvContext, BOOLEAN fTimeOut)
  518. {
  519. UPNP_WAIT_PARAMS * puwp;
  520. LPWSTR szSid = NULL;
  521. LPWSTR * rgszUrl = NULL;
  522. DWORD cszUrl = 0;
  523. HRESULT hr = S_OK;
  524. BOOL fLeave = TRUE;
  525. UPNP_EVENT_SOURCE * pes;
  526. DWORD isz;
  527. puwp = (UPNP_WAIT_PARAMS *)pvContext;
  528. Assert(puwp);
  529. TraceTag(ttidEventServer, "Event queue worker (%S:%S) entering critsec...",
  530. puwp->szEsid, puwp->szSid);
  531. EnterCriticalSection(&g_csListEventSource);
  532. TraceTag(ttidEventServer, "...entered");
  533. pes = PesFindEventSource(puwp->szEsid);
  534. if (pes)
  535. {
  536. UPNP_SUBSCRIBER * psub;
  537. UPNP_EVENT * pevt;
  538. DWORD iSeq;
  539. HANDLE hEvent;
  540. psub = PsubFindSubscriber(pes, puwp->szSid);
  541. if (psub)
  542. {
  543. if (CUPnPInterfaceList::Instance().FShouldSendOnInterface(psub->dwIpAddr))
  544. {
  545. BOOL fEmpty;
  546. // Remove first event off the list
  547. //
  548. pevt = psub->pevtQueue;
  549. TraceTag(ttidEventServer, "Processing event %p", pevt);
  550. AssertSz(pevt, "Worker is awake but nothing to do today!");
  551. psub->pevtQueue = pevt->pevtNext;
  552. // Any more items on the queue?
  553. fEmpty = !psub->pevtQueue;
  554. if (fEmpty)
  555. {
  556. psub->pevtQueueTail = NULL;
  557. }
  558. TraceTag(ttidEventServer, "Event queue is%s empty",
  559. fEmpty ? "" : " NOT");
  560. szSid = WszDupWsz(psub->szSid);
  561. if (!szSid)
  562. {
  563. hr = E_OUTOFMEMORY;
  564. goto cleanup;
  565. }
  566. // Copy the list of URLs so we can access it safely outside of the
  567. // critsec
  568. cszUrl = psub->cszUrl;
  569. Assert(cszUrl);
  570. rgszUrl = new LPWSTR[cszUrl];
  571. if (!rgszUrl)
  572. {
  573. hr = E_OUTOFMEMORY;
  574. goto cleanup;
  575. }
  576. for (isz = 0; isz < cszUrl; isz++)
  577. {
  578. rgszUrl[isz] = WszDupWsz(psub->rgszUrl[isz]);
  579. if (!rgszUrl[isz])
  580. {
  581. hr = E_OUTOFMEMORY;
  582. goto cleanup;
  583. }
  584. }
  585. // Wrap sequence number to 1 to avoid overflow
  586. if (psub->iSeq == MAXDWORD)
  587. {
  588. psub->iSeq = 1;
  589. }
  590. // Increment the sequence number after assigning it to a local
  591. // variable.
  592. //
  593. iSeq = psub->iSeq++;
  594. TraceTag(ttidEventServer, "New sequence # is %d. About to send "
  595. "sequence #%d", psub->iSeq, iSeq);
  596. // Last thing to do is signal the queue event so another worker
  597. // can pick up the next event off the queue. Only do this if the
  598. // event queue is still not empty.
  599. //
  600. if (!fEmpty)
  601. {
  602. TraceTag(ttidEventServer, "Signalling event again");
  603. SetEvent(psub->hEventQ);
  604. }
  605. // Don't need the lock anymore
  606. LeaveCriticalSection(&g_csListEventSource);
  607. TraceTag(ttidEventServer, "Released lock on global event source list");
  608. fLeave = FALSE;
  609. LPWSTR szHeaders;
  610. hr = HrComposeUpnpNotifyHeaders(iSeq, szSid, &szHeaders);
  611. if (SUCCEEDED(hr))
  612. {
  613. hr = E_FAIL;
  614. // Try the list of URLs until either we run out of them, or
  615. // we succeed
  616. //
  617. for (isz = 0; FAILED(hr) && isz < cszUrl; isz++)
  618. {
  619. hr = HrSubmitNotifyToSubscriber(szHeaders, pevt->szBody,
  620. rgszUrl[isz]);
  621. }
  622. delete [] szHeaders;
  623. }
  624. delete [] pevt->szBody;
  625. delete pevt;
  626. }
  627. else
  628. {
  629. TraceTag(ttidEventServer, "EventQueueWorker: Not sending to subscriber since it"
  630. " came in on IP address %s",
  631. inet_ntoa(*(struct in_addr *)&psub->dwIpAddr));
  632. }
  633. }
  634. else
  635. {
  636. TraceTag(ttidEventServer, "EventQueueWorker: Did not find "
  637. "subscriber %S in event source %S", puwp->szEsid,
  638. puwp->szSid);
  639. }
  640. }
  641. else
  642. {
  643. TraceTag(ttidEventServer, "EventQueueWorker: Did not find "
  644. "event source %S", puwp->szEsid);
  645. }
  646. cleanup:
  647. delete [] szSid;
  648. if (rgszUrl)
  649. {
  650. for (isz = 0; isz < cszUrl; isz++)
  651. {
  652. delete [] rgszUrl[isz];
  653. }
  654. }
  655. delete [] rgszUrl;
  656. if (fLeave)
  657. {
  658. LeaveCriticalSection(&g_csListEventSource);
  659. TraceTag(ttidEventServer, "Release lock (2) on global event source list");
  660. }
  661. TraceError("EventQueueWorker", hr)
  662. }
  663. //+---------------------------------------------------------------------------
  664. //
  665. // Function: RenewalCallback
  666. //
  667. // Purpose: Callback function that is called when a subscriber's renewal
  668. // timer has expired, which means it should be removed
  669. //
  670. // Arguments:
  671. // pvContext [in] Context data = event source identifier and subscriber
  672. // fTimeOut [in] UNUSED
  673. //
  674. // Returns: Nothing
  675. //
  676. // Author: danielwe 4 Aug 2000
  677. //
  678. // Notes:
  679. //
  680. VOID WINAPI RenewalCallback(LPVOID pvContext, BOOLEAN fTimeOut)
  681. {
  682. UPNP_RENEWAL * pur;
  683. UPNP_EVENT_SOURCE * pes;
  684. HRESULT hr = S_OK;
  685. UPNP_SUBSCRIBER * psubToDelete = NULL;
  686. pur = (UPNP_RENEWAL *)pvContext;
  687. Assert(pur);
  688. TraceTag(ttidEventServer, "RenewalCallback: Called for %S:%S (%d)",
  689. pur->szEsid, pur->szSid, pur->iRenewal);
  690. EnterCriticalSection(&g_csListEventSource);
  691. pes = PesFindEventSource(pur->szEsid);
  692. if (pes)
  693. {
  694. UPNP_SUBSCRIBER * psubCur;
  695. UPNP_SUBSCRIBER * psubPrev;
  696. for (psubCur = psubPrev = pes->psubList;
  697. psubCur;
  698. psubPrev = psubCur,
  699. psubCur = psubCur->psubNext)
  700. {
  701. if (!lstrcmpi(psubCur->szSid, pur->szSid))
  702. {
  703. if (psubCur->cRenewals == pur->iRenewal)
  704. {
  705. TraceTag(ttidEventServer, "RenewalCallback: Removing subscriber"
  706. " %S from event source %S", psubCur->szSid,
  707. pes->szEsid);
  708. // Remove subscriber from the list
  709. if (psubCur == pes->psubList)
  710. {
  711. // Removal of head item
  712. pes->psubList = psubCur->psubNext;
  713. }
  714. else
  715. {
  716. psubPrev->psubNext = psubCur->psubNext;
  717. }
  718. TraceTag(ttidEventServer, "RenewalCallback: Queuing work item"
  719. " to free subscriber %S", pur->szSid);
  720. // Can no longer rely on this because once the subscriber is
  721. // removed from the list, its owning event source is off limits
  722. //
  723. psubCur->pes = NULL;
  724. psubToDelete = psubCur;
  725. }
  726. else
  727. {
  728. TraceTag(ttidEventServer, "RenewalCallback: Found subscriber %S"
  729. "but renewal counter does not match %d vs. %d",
  730. psubCur->szSid, psubCur->cRenewals, pur->iRenewal);
  731. }
  732. break;
  733. }
  734. }
  735. }
  736. else
  737. {
  738. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  739. TraceTag(ttidEventServer, "RenewalCallback: Did not find event"
  740. " source %S", pur->szEsid);
  741. }
  742. LeaveCriticalSection(&g_csListEventSource);
  743. if (psubToDelete)
  744. {
  745. QueueUserWorkItem(FreeSubscriberWorker, (LPVOID)psubToDelete,
  746. WT_EXECUTELONGFUNCTION);
  747. }
  748. }
  749. //+---------------------------------------------------------------------------
  750. //
  751. // Function: HrAddSubscriber
  752. //
  753. // Purpose: Adds a new subscriber to the list for a particular event source
  754. //
  755. // Arguments:
  756. // szEsid [in] Event source identifier
  757. // dwIpAddr [in] Local IP address that the subscribe came in on
  758. // cszUrl [in] Number of callback URLs
  759. // rgszCallbackUrl [in] Callback URLs of subscriber
  760. // pcsecTimeout [in out] Subscription timeout requested by subscriber
  761. // Upon return, receives the timeout chosen by
  762. // the device host
  763. // pszSid [out] Returns the newly allocated SID
  764. //
  765. // Returns: S_OK if success, E_OUTOFMEMORY,
  766. // or ERROR_FILE_NOT_FOUND if the event source did not exist
  767. //
  768. // Author: danielwe 4 Aug 2000
  769. //
  770. // Notes: Caller should free the returned pszSid with delete []
  771. //
  772. HRESULT HrAddSubscriber(LPCWSTR szEsid, DWORD dwIpAddr, DWORD cszUrl,
  773. LPCWSTR *rgszCallbackUrl,
  774. LPCWSTR szEventBody, DWORD *pcsecTimeout,
  775. LPWSTR *pszSid)
  776. {
  777. HRESULT hr = S_OK;
  778. UPNP_SUBSCRIBER * psub;
  779. UPNP_WAIT_PARAMS * puwp;
  780. UPNP_EVENT_SOURCE * pes;
  781. LPWSTR szSid = NULL;
  782. Assert(pszSid);
  783. Assert(pcsecTimeout);
  784. TraceTag(ttidEventServer, "Adding subscriber from %S (%d) to %S",
  785. rgszCallbackUrl[0], cszUrl, szEsid);
  786. psub = new UPNP_SUBSCRIBER;
  787. if (!psub)
  788. {
  789. hr = E_OUTOFMEMORY;
  790. goto cleanup;
  791. }
  792. ZeroMemory(psub, sizeof(UPNP_SUBSCRIBER));
  793. psub->dwIpAddr = dwIpAddr;
  794. psub->hEventQ = CreateEvent(NULL, FALSE, FALSE, NULL);
  795. if (!psub->hEventQ || psub->hEventQ == INVALID_HANDLE_VALUE)
  796. {
  797. hr = HrFromLastWin32Error();
  798. TraceError("HrAddSubscriber: CreateEvent", hr);
  799. goto cleanup;
  800. }
  801. psub->szSid = SzGetNewSid();
  802. if (!psub->szSid)
  803. {
  804. hr = E_OUTOFMEMORY;
  805. goto cleanup;
  806. }
  807. TraceTag(ttidEventServer, "Allocated new SID: %S", psub->szSid);
  808. // Make a local copy of this for later use in HrSubmitEventZero() and also
  809. // so we can return it to the caller
  810. //
  811. szSid = WszDupWsz(psub->szSid);
  812. if (!szSid)
  813. {
  814. hr = E_OUTOFMEMORY;
  815. goto cleanup;
  816. }
  817. DWORD isz;
  818. psub->cszUrl = cszUrl;
  819. psub->rgszUrl = new LPWSTR[cszUrl];
  820. if (!psub->rgszUrl)
  821. {
  822. hr = E_OUTOFMEMORY;
  823. goto cleanup;
  824. }
  825. for (isz = 0; isz < cszUrl; isz++)
  826. {
  827. psub->rgszUrl[isz] = WszDupWsz(rgszCallbackUrl[isz]);
  828. if (!psub->rgszUrl[isz])
  829. {
  830. hr = E_OUTOFMEMORY;
  831. goto cleanup;
  832. }
  833. }
  834. psub->uwp.szEsid = WszDupWsz(szEsid);
  835. if (!psub->uwp.szEsid)
  836. {
  837. hr = E_OUTOFMEMORY;
  838. goto cleanup;
  839. }
  840. psub->uwp.szSid = WszDupWsz(psub->szSid);
  841. if (!psub->uwp.szSid)
  842. {
  843. hr = E_OUTOFMEMORY;
  844. goto cleanup;
  845. }
  846. // ISSUE-2000/10/2-danielwe: Registering the wait with the
  847. // WT_EXECUTELONGFUNCTION flag means that a new thread will be created
  848. // FOR EACH SUBSCRIBER. This may be a bad thing depending on how many
  849. // subscribers there are expected to be. Creating threads with this flag
  850. // would be to handle the case where one or more subscribers are timing
  851. // out sending the NOTIFY to them or they are just plain slow. If this
  852. // flag is not used, these subscribers will cause the eventing queues to
  853. // bottleneck because no free threads are available to service them. So,
  854. // to summarize:
  855. //
  856. // Using the WT_EXECUTELONGFUNCTION flag:
  857. // --------------------------------------
  858. // Pros: Never a bottleneck sending event notifications. They always
  859. // arrive when expected.
  860. // Cons: Will end up with lots of threads if there are lots of subscribers
  861. // However, once the subscribers unsubscribe, the thread count would
  862. // eventually go back down again.
  863. //
  864. // Not using the flag:
  865. // -------------------
  866. // Pros: Efficient. Only create the threads that are needed.
  867. // Cons: May end up with events backing up in the queue if subscribers
  868. // time out frequently.
  869. //
  870. // Choice is still up in the air. We'll set the flag for now and see how
  871. // bad this gets during stress time.
  872. //
  873. if (!RegisterWaitForSingleObject(&psub->hWait, psub->hEventQ,
  874. EventQueueWorker, (LPVOID)&psub->uwp,
  875. INFINITE, WT_EXECUTELONGFUNCTION))
  876. {
  877. hr = HrFromLastWin32Error();
  878. TraceError("HrAddSubscriber: RegisterWaitForSingleObject", hr);
  879. goto cleanup;
  880. }
  881. EnterCriticalSection(&g_csListEventSource);
  882. pes = PesFindEventSource(szEsid);
  883. if (!pes)
  884. {
  885. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  886. TraceTag(ttidEventServer, "HrAddSubscriber: Event source %S not found!",
  887. szEsid);
  888. LeaveCriticalSection(&g_csListEventSource);
  889. goto cleanup;
  890. }
  891. psub->ur.iRenewal = psub->cRenewals;
  892. psub->ur.szEsid = WszDupWsz(pes->szEsid);
  893. if (!psub->ur.szEsid)
  894. {
  895. hr = E_OUTOFMEMORY;
  896. LeaveCriticalSection(&g_csListEventSource);
  897. goto cleanup;
  898. }
  899. psub->ur.szSid = WszDupWsz(psub->szSid);
  900. if (!psub->ur.szSid)
  901. {
  902. hr = E_OUTOFMEMORY;
  903. LeaveCriticalSection(&g_csListEventSource);
  904. goto cleanup;
  905. }
  906. if (!*pcsecTimeout)
  907. {
  908. *pcsecTimeout = c_csecDefSubsTimeout;
  909. }
  910. else
  911. {
  912. *pcsecTimeout = max(c_csecMinSubsTimeout, *pcsecTimeout);
  913. }
  914. psub->csecTimeout = *pcsecTimeout;
  915. if (!CreateTimerQueueTimer(&psub->hTimer, g_hTimerQ,
  916. RenewalCallback, (LPVOID)&psub->ur,
  917. *pcsecTimeout * 1000, 0, WT_EXECUTEINTIMERTHREAD))
  918. {
  919. hr = HrFromLastWin32Error();
  920. TraceError("HrAddSubscriber: CreateTimerQueueTimer", hr);
  921. LeaveCriticalSection(&g_csListEventSource);
  922. goto cleanup;
  923. }
  924. psub->pes = pes;
  925. // Link in the new subscriber to the event source's list (add at head
  926. // of list because it's quicker and order doesn't matter one bit)
  927. //
  928. if (!pes->psubList)
  929. {
  930. pes->psubList = psub;
  931. }
  932. else
  933. {
  934. psub->psubNext = pes->psubList;
  935. pes->psubList = psub;
  936. }
  937. TraceTag(ttidEventServer, "Adding psub = %p to list", psub);
  938. LeaveCriticalSection(&g_csListEventSource);
  939. *pszSid = szSid;
  940. TraceTag(ttidEventServer, "Adding event zero notification for %S:%S",
  941. szEsid, szSid);
  942. hr = HrSubmitEventZero(szEsid, szSid, szEventBody);
  943. done:
  944. TraceError("HrAddSubscriber", hr);
  945. return hr;
  946. cleanup:
  947. delete [] szSid;
  948. FreeSubscriber(psub);
  949. goto done;
  950. }
  951. //+---------------------------------------------------------------------------
  952. //
  953. // Function: HrRenewSubscriber
  954. //
  955. // Purpose: Renews the given subscriber's subscription
  956. //
  957. // Arguments:
  958. // szEsid [in] Event source identifier
  959. // pcsecTimeout [in out] Subscription timeout requested by subscriber
  960. // Upon return, receives the timeout chosen by
  961. // the device host
  962. // szSid [in] Subscriber identifier (SID)
  963. //
  964. // Returns: S_OK if success, ERROR_FILE_NOT_FOUND if event source or
  965. // subscription was not found
  966. //
  967. // Author: danielwe 4 Aug 2000
  968. //
  969. // Notes:
  970. //
  971. HRESULT HrRenewSubscriber(LPCWSTR szEsid, DWORD *pcsecTimeout, LPCWSTR szSid)
  972. {
  973. HRESULT hr = S_OK;
  974. UPNP_EVENT_SOURCE * pes;
  975. HANDLE hTimerDel = NULL;
  976. TraceTag(ttidEventServer, "HrRenewSubscriber: Renewing subscriber with "
  977. "SID %S for event source %S", szSid, szEsid);
  978. TraceTag(ttidEventServer, "Tickcount for renewal callback is %d",
  979. GetTickCount());
  980. EnterCriticalSection(&g_csListEventSource);
  981. pes = PesFindEventSource(szEsid);
  982. if (pes)
  983. {
  984. UPNP_SUBSCRIBER * psub;
  985. psub = PsubFindSubscriber(pes, szSid);
  986. if (psub)
  987. {
  988. // We don't care if the timer is currently executing because we're
  989. // inside the critsec right now and so if we got here before the
  990. // timer proc did, then we made it just in time to bump the
  991. // renewal count so the proc doesn't delete this guy. If the timer
  992. // proc had acquired the critsec first, then we couldn't possibly
  993. // be here because it would have removed the subscriber from the
  994. // list already
  995. //
  996. hTimerDel = psub->hTimer;
  997. psub->cRenewals++;
  998. // Delete the old renewal structure
  999. //
  1000. delete [] psub->ur.szEsid;
  1001. delete [] psub->ur.szSid;
  1002. psub->ur.szEsid = NULL;
  1003. psub->ur.szSid = NULL;
  1004. psub->ur.iRenewal = psub->cRenewals;
  1005. psub->ur.szEsid = WszDupWsz(pes->szEsid);
  1006. if (!psub->ur.szEsid)
  1007. {
  1008. hr = E_OUTOFMEMORY;
  1009. }
  1010. else
  1011. {
  1012. psub->ur.szSid = WszDupWsz(psub->szSid);
  1013. if (!psub->ur.szSid)
  1014. {
  1015. hr = E_OUTOFMEMORY;
  1016. }
  1017. else
  1018. {
  1019. if (!*pcsecTimeout)
  1020. {
  1021. *pcsecTimeout = c_csecDefSubsTimeout;
  1022. }
  1023. else
  1024. {
  1025. *pcsecTimeout = max(c_csecMinSubsTimeout,
  1026. *pcsecTimeout);
  1027. }
  1028. psub->csecTimeout = *pcsecTimeout;
  1029. if (!CreateTimerQueueTimer(&psub->hTimer, g_hTimerQ,
  1030. RenewalCallback,
  1031. (LPVOID)&psub->ur,
  1032. *pcsecTimeout * 1000, 0,
  1033. WT_EXECUTEINTIMERTHREAD))
  1034. {
  1035. hr = HrFromLastWin32Error();
  1036. TraceError("HrRenewSubscriber: CreateTimerQueueTimer", hr);
  1037. }
  1038. else
  1039. {
  1040. TraceTag(ttidEventServer, "Started server renewal "
  1041. "timer for %d seconds at tickcount %d",
  1042. *pcsecTimeout, GetTickCount());
  1043. }
  1044. }
  1045. }
  1046. }
  1047. else
  1048. {
  1049. // Return 412 Precondition Failed
  1050. hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
  1051. TraceTag(ttidEventServer, "HrRenewSubscriber: Did not find"
  1052. " subscriber %S in event source %S", szSid, szEsid);
  1053. }
  1054. }
  1055. else
  1056. {
  1057. // Return 404 Not Found
  1058. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  1059. TraceTag(ttidEventServer, "HrRenewSubscriber: Did not find event"
  1060. " source %S", szEsid);
  1061. }
  1062. LeaveCriticalSection(&g_csListEventSource);
  1063. // ISSUE-2000/12/1-danielwe: DeleteTimerQueueTimer() apparently
  1064. // will block if called on a timer that is currently executing
  1065. // its callback. It is unknown whether this is a bug in its
  1066. // implementation or not. To work around this problem, we'll
  1067. // leave the critsec so that the RenewalCallback() function can complete
  1068. // and then delete the timer. After deleting the timer, we signal the
  1069. // event that allows FreeEventSourceWorker() to delete the timer queue
  1070. //
  1071. if (hTimerDel)
  1072. {
  1073. DeleteTimerQueueTimer(g_hTimerQ, hTimerDel, NULL);
  1074. }
  1075. TraceError("HrRenewSubscriber", hr);
  1076. return hr;
  1077. }
  1078. //+---------------------------------------------------------------------------
  1079. //
  1080. // Function: HrRemoveSubscriber
  1081. //
  1082. // Purpose: Removes a subscriber from the list of subscribers to an
  1083. // event source
  1084. //
  1085. // Arguments:
  1086. // szEsid [in] Event source identifier
  1087. // szSid [in] Subscriber identifier (SID)
  1088. //
  1089. // Returns: S_OK if success, ERROR_FILE_NOT_FOUND if event source or
  1090. // subscription was not found
  1091. //
  1092. // Author: danielwe 4 Aug 2000
  1093. //
  1094. // Notes:
  1095. //
  1096. HRESULT HrRemoveSubscriber(LPCWSTR szEsid, LPCWSTR szSid)
  1097. {
  1098. HRESULT hr = S_OK;
  1099. UPNP_EVENT_SOURCE * pes;
  1100. TraceTag(ttidEventServer, "HrRemoveSubscriber: Removing subscriber with "
  1101. "SID %S for event source %S", szSid, szEsid);
  1102. EnterCriticalSection(&g_csListEventSource);
  1103. pes = PesFindEventSource(szEsid);
  1104. if (pes)
  1105. {
  1106. UPNP_SUBSCRIBER * psubCur;
  1107. UPNP_SUBSCRIBER * psubPrev;
  1108. for (psubCur = psubPrev = pes->psubList;
  1109. psubCur;
  1110. psubPrev = psubCur,
  1111. psubCur = psubCur->psubNext)
  1112. {
  1113. if (!lstrcmpi(psubCur->szSid, szSid))
  1114. {
  1115. TraceTag(ttidEventServer, "HrRemoveSubscriber: Removing subscriber"
  1116. " %S from event source %S", psubCur->szSid, pes->szEsid);
  1117. // Remove subscriber from the list
  1118. if (psubCur == pes->psubList)
  1119. {
  1120. // Removal of head item
  1121. pes->psubList = psubCur->psubNext;
  1122. }
  1123. else
  1124. {
  1125. psubPrev->psubNext = psubCur->psubNext;
  1126. }
  1127. break;
  1128. }
  1129. }
  1130. if (psubCur)
  1131. {
  1132. TraceTag(ttidEventServer, "HrRemoveSubscriber: Removing subscriber"
  1133. " %S", szSid);
  1134. // Can no longer rely on this because once the subscriber is
  1135. // removed from the list, its owning event source is off limits
  1136. //
  1137. psubCur->pes = NULL;
  1138. QueueUserWorkItem(FreeSubscriberWorker, (LPVOID)psubCur,
  1139. WT_EXECUTELONGFUNCTION);
  1140. }
  1141. else
  1142. {
  1143. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  1144. TraceTag(ttidEventServer, "HrRemoveSubscriber: Did not find"
  1145. " subscriber %S in event source %S", szSid, szEsid);
  1146. }
  1147. }
  1148. else
  1149. {
  1150. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  1151. TraceTag(ttidEventServer, "HrRemoveSubscriber: Did not find event"
  1152. " source %S", szEsid);
  1153. }
  1154. LeaveCriticalSection(&g_csListEventSource);
  1155. TraceError("HrRemoveSubscriber", hr);
  1156. return hr;
  1157. }
  1158. //+---------------------------------------------------------------------------
  1159. //
  1160. // Function: HrSubmitEvent
  1161. //
  1162. // Purpose: Submits an event for an event source
  1163. //
  1164. // Arguments:
  1165. // szEsid [in] Event source identifier
  1166. // szEventBody [in] Full XML body of event message
  1167. //
  1168. // Returns: S_OK if success, E_OUTOFMEMORY, or ERROR_FILE_NOT_FOUND if
  1169. // event source was not found
  1170. //
  1171. // Author: danielwe 4 Aug 2000
  1172. //
  1173. // Notes:
  1174. //
  1175. HRESULT HrSubmitEvent(LPCWSTR szEsid, LPCWSTR szEventBody)
  1176. {
  1177. HRESULT hr = S_OK;
  1178. TraceTag(ttidEventServer, "HrSubmitEvent: Submitting event for %S ",
  1179. szEsid);
  1180. Assert(szEsid);
  1181. EnterCriticalSection(&g_csListEventSource);
  1182. UPNP_EVENT_SOURCE * pes;
  1183. if (!g_hInetSess)
  1184. {
  1185. hr = HrInitInternetSession();
  1186. }
  1187. if (SUCCEEDED(hr))
  1188. {
  1189. Assert(g_hInetSess);
  1190. pes = PesFindEventSource(szEsid);
  1191. if (pes)
  1192. {
  1193. UPNP_SUBSCRIBER * psub;
  1194. for (psub = pes->psubList;
  1195. psub;
  1196. psub = psub->psubNext)
  1197. {
  1198. if (psub->iSeq > 0)
  1199. {
  1200. UPNP_EVENT * pevt;
  1201. pevt = new UPNP_EVENT;
  1202. if (!pevt)
  1203. {
  1204. hr = E_OUTOFMEMORY;
  1205. break;
  1206. }
  1207. else
  1208. {
  1209. pevt->pevtNext = NULL;
  1210. pevt->szBody = WszDupWsz(szEventBody);
  1211. if (pevt->szBody)
  1212. {
  1213. AppendToEventQueue(psub, pevt);
  1214. }
  1215. else
  1216. {
  1217. delete pevt;
  1218. hr = E_OUTOFMEMORY;
  1219. break;
  1220. }
  1221. }
  1222. }
  1223. }
  1224. }
  1225. else
  1226. {
  1227. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  1228. TraceTag(ttidEventServer, "HrSubmitEvent: Did not find event"
  1229. " source %S", szEsid);
  1230. }
  1231. }
  1232. LeaveCriticalSection(&g_csListEventSource);
  1233. TraceError("HrSubmitEvent", hr);
  1234. return hr;
  1235. }
  1236. //+---------------------------------------------------------------------------
  1237. //
  1238. // Function: AppendToEventQueue
  1239. //
  1240. // Purpose: Adds the given event structure to the end of the event queue
  1241. // for that subscriber
  1242. //
  1243. // Arguments:
  1244. // psub [in] Subscriber to add event to
  1245. // pevt [in] Event to add
  1246. //
  1247. // Returns: Nothing
  1248. //
  1249. // Author: danielwe 4 Aug 2000
  1250. //
  1251. // Notes:
  1252. //
  1253. VOID AppendToEventQueue(UPNP_SUBSCRIBER * psub, UPNP_EVENT * pevt)
  1254. {
  1255. if (psub->pevtQueue)
  1256. {
  1257. psub->pevtQueueTail->pevtNext = pevt;
  1258. psub->pevtQueueTail = pevt;
  1259. TraceTag(ttidEventServer, "Adding %p to event queue for sub %S",
  1260. pevt, psub->szSid);
  1261. }
  1262. else
  1263. {
  1264. AssertSz(!psub->pevtQueueTail, "If head is NULL so should tail be too");
  1265. psub->pevtQueue = pevt;
  1266. psub->pevtQueueTail = pevt;
  1267. TraceTag(ttidEventServer, "Adding %p to event queue for sub %S and"
  1268. " signalling event", pevt, psub->szSid);
  1269. // Signal the event that says that a new item is ready on the queue
  1270. //
  1271. SetEvent(psub->hEventQ);
  1272. }
  1273. Assert(!pevt->pevtNext);
  1274. AssertSz(psub->pevtQueueTail == pevt, "Didn't insert at the tail?");
  1275. }
  1276. //+---------------------------------------------------------------------------
  1277. //
  1278. // Function: HrSubmitEventZero
  1279. //
  1280. // Purpose: Submits the initial notify event for a subscriber
  1281. //
  1282. // Arguments:
  1283. // szEsid [in] Event source identifier
  1284. // szSid [in] Subscriber to submit the event to
  1285. // szEventBody [in] XML body of event message
  1286. //
  1287. // Returns: S_OK if success, E_OUTOFMEMORY
  1288. //
  1289. // Author: danielwe 4 Aug 2000
  1290. //
  1291. // Notes: The subscriber's event queue MUST be empty when this function
  1292. // is called
  1293. //
  1294. HRESULT HrSubmitEventZero(LPCWSTR szEsid, LPCWSTR szSid, LPCWSTR szEventBody)
  1295. {
  1296. HRESULT hr = S_OK;
  1297. UPNP_EVENT_SOURCE * pes;
  1298. UPNP_SUBSCRIBER * psub;
  1299. EnterCriticalSection(&g_csListEventSource);
  1300. if (!g_hInetSess)
  1301. {
  1302. hr = HrInitInternetSession();
  1303. }
  1304. if (SUCCEEDED(hr))
  1305. {
  1306. Assert(g_hInetSess);
  1307. pes = PesFindEventSource(szEsid);
  1308. if (pes)
  1309. {
  1310. UPNP_EVENT * pevt;
  1311. psub = PsubFindSubscriber(pes, szSid);
  1312. if (psub)
  1313. {
  1314. pevt = new UPNP_EVENT;
  1315. if (!pevt)
  1316. {
  1317. hr = E_OUTOFMEMORY;
  1318. }
  1319. else
  1320. {
  1321. pevt->pevtNext = NULL;
  1322. pevt->szBody = WszDupWsz(szEventBody);
  1323. if (pevt->szBody)
  1324. {
  1325. AssertSz(!psub->pevtQueue, "Event queue is not empty!!!");
  1326. AppendToEventQueue(psub, pevt);
  1327. }
  1328. else
  1329. {
  1330. delete pevt;
  1331. hr = E_OUTOFMEMORY;
  1332. }
  1333. }
  1334. }
  1335. else
  1336. {
  1337. TraceTag(ttidEventServer, "Interesting.. Subscriber %S was removed"
  1338. " before event zero was submitted for a subscriber?? Oh well"
  1339. " no big deal.", szSid);
  1340. }
  1341. }
  1342. else
  1343. {
  1344. TraceTag(ttidEventServer, "Interesting.. Event source %S was removed"
  1345. " before event zero was submitted for a subscriber?? Oh well"
  1346. " no big deal.", szEsid);
  1347. }
  1348. LeaveCriticalSection(&g_csListEventSource);
  1349. }
  1350. TraceError("HrSubmitEventZero", hr);
  1351. return hr;
  1352. }
  1353. static const WCHAR c_szHeaderNt[] = L"NT";
  1354. static const WCHAR c_szHeaderNts[] = L"NTS";
  1355. static const WCHAR c_szHeaderSid[] = L"SID";
  1356. static const WCHAR c_szHeaderSeq[] = L"SEQ";
  1357. static const WCHAR c_szHeaderContentType[] = L"Content-Type";
  1358. const WCHAR c_szNotifyMethod[] = L"NOTIFY";
  1359. const WCHAR c_szHttpVersion[] = L"HTTP/1.1";
  1360. static const DWORD c_cchHeaderNt = celems(c_szHeaderNt);
  1361. static const DWORD c_cchHeaderNts = celems(c_szHeaderNts);
  1362. static const DWORD c_cchHeaderSid = celems(c_szHeaderSid);
  1363. static const DWORD c_cchHeaderSeq = celems(c_szHeaderSeq);
  1364. static const DWORD c_cchHeaderContentType = celems(c_szHeaderContentType);
  1365. static const WCHAR c_szNt[] = L"upnp:event";
  1366. static const WCHAR c_szNts[] = L"upnp:propchange";
  1367. static const DWORD c_cchNt = celems(c_szNt);
  1368. static const DWORD c_cchNts = celems(c_szNts);
  1369. static const WCHAR c_szColon[] = L":";
  1370. static const WCHAR c_szCrlf[] = L"\r\n";
  1371. static const DWORD c_cchColon = celems(c_szColon);
  1372. static const DWORD c_cchCrlf = celems(c_szCrlf);
  1373. const WCHAR c_szTextXml[] = L"text/xml";
  1374. const DWORD c_cchTextXml = celems(c_szTextXml);
  1375. //+---------------------------------------------------------------------------
  1376. //
  1377. // Function: HrComposeUpnpNotifyHeaders
  1378. //
  1379. // Purpose: Composes the headers for a NOTIFY request to be sent to a
  1380. // subscriber.
  1381. //
  1382. // Arguments:
  1383. // iSeq [in] Sequence number of event
  1384. // szSid [in] SID of subscriber
  1385. // pszHeaders [out] Returns newly allocated headers in proper format
  1386. //
  1387. // Returns: S_OK if success or E_OUTOFMEMORY if no memory
  1388. //
  1389. // Author: danielwe 12 Oct 1999
  1390. //
  1391. // Notes: Caller must free pszHeaders with delete []
  1392. //
  1393. HRESULT HrComposeUpnpNotifyHeaders(DWORD iSeq, LPCTSTR szSid,
  1394. LPWSTR *pszHeaders)
  1395. {
  1396. DWORD cchHeaders = 0;
  1397. WCHAR szSeq[32];
  1398. LPWSTR szHeaders;
  1399. DWORD iNumOfBytes = 0;
  1400. HRESULT hr = S_OK;
  1401. wsprintf(szSeq, L"%d", iSeq);
  1402. cchHeaders += c_cchHeaderNt + c_cchColon + c_cchNt + c_cchCrlf;
  1403. cchHeaders += c_cchHeaderNts + c_cchColon + c_cchNts + c_cchCrlf;
  1404. cchHeaders += c_cchHeaderSid + c_cchColon + lstrlen(szSid) + c_cchCrlf;
  1405. cchHeaders += c_cchHeaderSeq + c_cchColon + lstrlen(szSeq) + c_cchCrlf;
  1406. cchHeaders += c_cchHeaderContentType + c_cchColon + c_cchTextXml + c_cchCrlf;
  1407. szHeaders = new WCHAR[cchHeaders + 1];
  1408. if (szHeaders)
  1409. {
  1410. iNumOfBytes += wsprintf(szHeaders + iNumOfBytes, L"%s%s%s%s",
  1411. c_szHeaderNt, c_szColon, c_szNt, c_szCrlf);
  1412. iNumOfBytes += wsprintf(szHeaders + iNumOfBytes, L"%s%s%s%s",
  1413. c_szHeaderNts, c_szColon, c_szNts, c_szCrlf);
  1414. iNumOfBytes += wsprintf(szHeaders + iNumOfBytes, L"%s%s%s%s",
  1415. c_szHeaderSid, c_szColon, szSid, c_szCrlf);
  1416. iNumOfBytes += wsprintf(szHeaders + iNumOfBytes, L"%s%s%s%s",
  1417. c_szHeaderSeq, c_szColon, szSeq, c_szCrlf);
  1418. iNumOfBytes += wsprintf(szHeaders + iNumOfBytes, L"%s%s%s%s",
  1419. c_szHeaderContentType, c_szColon,
  1420. c_szTextXml, c_szCrlf);
  1421. *pszHeaders = szHeaders;
  1422. }
  1423. else
  1424. {
  1425. hr = E_OUTOFMEMORY;
  1426. }
  1427. TraceError("HrComposeUpnpNotifyHeaders", hr);
  1428. return hr;
  1429. }
  1430. //+---------------------------------------------------------------------------
  1431. //
  1432. // Function: HrSubmitNotifyToSubscriber
  1433. //
  1434. // Purpose: Submits a NOTIFY request to the given URL
  1435. //
  1436. // Arguments:
  1437. // szHeaders [in] Headers of request
  1438. // szBody [in] Body of request (in XML)
  1439. // szUrl [in] URL to send request to
  1440. //
  1441. // Returns: S_OK if successful, E_UNEXPECTED if the internet session
  1442. // was not initialized
  1443. //
  1444. // Author: danielwe 7 Aug 2000
  1445. //
  1446. // Notes:
  1447. //
  1448. HRESULT HrSubmitNotifyToSubscriber(LPCWSTR szHeaders, LPCWSTR szBody,
  1449. LPCWSTR szUrl)
  1450. {
  1451. HRESULT hr = S_OK;
  1452. URL_COMPONENTS urlComp = {0};
  1453. WCHAR szHostName[INTERNET_MAX_HOST_NAME_LENGTH];
  1454. WCHAR szUrlPath[INTERNET_MAX_URL_LENGTH];
  1455. urlComp.dwStructSize = sizeof(URL_COMPONENTS);
  1456. urlComp.lpszHostName = (LPWSTR) &szHostName;
  1457. urlComp.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
  1458. urlComp.lpszUrlPath = (LPWSTR) &szUrlPath;
  1459. urlComp.dwUrlPathLength = INTERNET_MAX_URL_LENGTH;
  1460. if (InternetCrackUrl(szUrl, 0, 0, &urlComp))
  1461. {
  1462. // Hack for not able to send to loopback in LocalService
  1463. if(0 == lstrcmp(szHostName, L"127.0.0.1"))
  1464. {
  1465. lstrcpy(szHostName, L"localhost");
  1466. }
  1467. HINTERNET hinC;
  1468. if (g_hInetSess)
  1469. {
  1470. hinC = InternetConnect(g_hInetSess, szHostName, urlComp.nPort,
  1471. NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
  1472. if (hinC)
  1473. {
  1474. HINTERNET hinR;
  1475. TraceTag(ttidEventServer, "Connected to host %S:%d.",
  1476. szHostName, urlComp.nPort);
  1477. hinR = HttpOpenRequest(hinC, c_szNotifyMethod, szUrlPath,
  1478. c_szHttpVersion, NULL, NULL,
  1479. INTERNET_FLAG_KEEP_CONNECTION, 0);
  1480. if (hinR)
  1481. {
  1482. LPSTR szaBody;
  1483. TraceTag(ttidEventServer, "Sending the following request to "
  1484. "subscriber at %S:", szUrlPath);
  1485. TraceTag(ttidEventServer, "-------------------------------------------");
  1486. TraceTag(ttidEventServer, "\n%S\n%S", szHeaders, szBody);
  1487. TraceTag(ttidEventServer, "-------------------------------------------");
  1488. szaBody = Utf8FromWsz(szBody);
  1489. if (szaBody)
  1490. {
  1491. if (!HttpSendRequest(hinR, szHeaders, 0, (LPVOID)szaBody,
  1492. CbOfSza(szaBody)))
  1493. {
  1494. TraceTag(ttidError, "Failed to send request [http://%S:%d%S]",
  1495. szHostName, urlComp.nPort, szUrlPath);
  1496. hr = HrFromLastWin32Error();
  1497. TraceError("HrSubmitNotifyToSubscriber: "
  1498. "HttpSendRequest", hr);
  1499. }
  1500. else
  1501. {
  1502. TraceTag(ttidEventServer, "Request sent successfully!");
  1503. }
  1504. delete [] szaBody;
  1505. }
  1506. else
  1507. {
  1508. hr = E_OUTOFMEMORY;
  1509. TraceError("HrSubmitNotifyToSubscriber: SzFromWsz", hr);
  1510. }
  1511. InternetCloseHandle(hinR);
  1512. }
  1513. else
  1514. {
  1515. hr = HrFromLastWin32Error();
  1516. TraceError("HrSubmitNotifyToSubscriber: HttpOpenRequest",
  1517. hr);
  1518. }
  1519. InternetCloseHandle(hinC);
  1520. }
  1521. else
  1522. {
  1523. hr = HrFromLastWin32Error();
  1524. TraceError("HrSubmitNotifyToSubscriber: InternetConnect",
  1525. hr);
  1526. }
  1527. }
  1528. else
  1529. {
  1530. hr = E_UNEXPECTED;
  1531. TraceError("HrSubmitEventToSubscriber: No internet session!", hr);
  1532. }
  1533. }
  1534. else
  1535. {
  1536. hr = HrFromLastWin32Error();
  1537. TraceError("HrSubmitNotifyToSubscriber: InternetCrackUrl", hr);
  1538. }
  1539. TraceError("HrSubmitNotifyToSubscriber", hr);
  1540. return hr;
  1541. }
  1542. //+---------------------------------------------------------------------------
  1543. //
  1544. // Function: PesFindEventSource
  1545. //
  1546. // Purpose: Helper function to return the event source identified by
  1547. // szEsid.
  1548. //
  1549. // Arguments:
  1550. // szEsid [in] Event source identifier
  1551. //
  1552. // Returns: Pointer to event source that matches the identifier passed in
  1553. // or NULL if not found
  1554. //
  1555. // Author: danielwe 4 Aug 2000
  1556. //
  1557. // Notes:
  1558. //
  1559. UPNP_EVENT_SOURCE *PesFindEventSource(LPCWSTR szEsid)
  1560. {
  1561. UPNP_EVENT_SOURCE * pesCur;
  1562. for (pesCur = g_pesList; pesCur; pesCur = pesCur->pesNext)
  1563. {
  1564. if (!lstrcmpi(pesCur->szEsid, szEsid))
  1565. {
  1566. break;
  1567. }
  1568. }
  1569. return pesCur;
  1570. }
  1571. //+---------------------------------------------------------------------------
  1572. //
  1573. // Function: PsubFindSubscriber
  1574. //
  1575. // Purpose: Helper function to return the subscriber identified by the
  1576. // SID passed in
  1577. //
  1578. // Arguments:
  1579. // pes [in] Event source to search in
  1580. // szSid [in] Subscription identifier
  1581. //
  1582. // Returns: Pointer to subscriber that matches the SID or NULL if not
  1583. // found
  1584. //
  1585. // Author: danielwe 4 Aug 2000
  1586. //
  1587. // Notes:
  1588. //
  1589. UPNP_SUBSCRIBER *PsubFindSubscriber(UPNP_EVENT_SOURCE *pes, LPCWSTR szSid)
  1590. {
  1591. UPNP_SUBSCRIBER * psubCur;
  1592. for (psubCur = pes->psubList;
  1593. psubCur;
  1594. psubCur = psubCur->psubNext)
  1595. {
  1596. if (!lstrcmpi(psubCur->szSid, szSid))
  1597. {
  1598. break;
  1599. }
  1600. }
  1601. return psubCur;
  1602. }
  1603. //
  1604. // Debug functions
  1605. //
  1606. VOID DbgDumpSubscriber(UPNP_SUBSCRIBER *psub)
  1607. {
  1608. SYSTEMTIME st;
  1609. WCHAR szLocalDate[255];
  1610. WCHAR szLocalTime[255];
  1611. FileTimeToSystemTime(&psub->ftTimeout, &st);
  1612. GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL,
  1613. szLocalDate, 255);
  1614. GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL,
  1615. szLocalTime, 255);
  1616. TraceTag(ttidEventServer, "Subscription at address 0x%08X", psub);
  1617. TraceTag(ttidEventServer, "--------------------------------------");
  1618. TraceTag(ttidEventServer, "Subscription timeout is %d seconds from "
  1619. "now. It expires at %S %S", psub->csecTimeout,
  1620. szLocalDate, szLocalTime);
  1621. TraceTag(ttidEventServer, "Sequence # : %d", psub->iSeq);
  1622. TraceTag(ttidEventServer, "Callback Url: %S", psub->rgszUrl[0]);
  1623. TraceTag(ttidEventServer, "SID : %S", psub->szSid);
  1624. TraceTag(ttidEventServer, "--------------------------------------");
  1625. }
  1626. VOID DbgDumpEventSource(UPNP_EVENT_SOURCE *pes)
  1627. {
  1628. DWORD iVar;
  1629. UPNP_SUBSCRIBER * psubCur;
  1630. TraceTag(ttidEventServer, "Event source 0x%08X - %S", pes, pes->szEsid);
  1631. TraceTag(ttidEventServer, "-------------------------------------------------");
  1632. if (pes->psubList)
  1633. {
  1634. for (psubCur = pes->psubList; psubCur; psubCur = psubCur->psubNext)
  1635. {
  1636. DbgDumpSubscriber(psubCur);
  1637. }
  1638. }
  1639. else
  1640. {
  1641. TraceTag(ttidEventServer, "NO SUBSCRIBERS");
  1642. }
  1643. TraceTag(ttidEventServer, "-------------------------------------------------");
  1644. }
  1645. VOID DbgDumpListEventSource()
  1646. {
  1647. UPNP_EVENT_SOURCE * pesCur;
  1648. if (g_pesList)
  1649. {
  1650. EnterCriticalSection(&g_csListEventSource);
  1651. for (pesCur = g_pesList; pesCur; pesCur = pesCur->pesNext)
  1652. {
  1653. DbgDumpEventSource(pesCur);
  1654. }
  1655. LeaveCriticalSection(&g_csListEventSource);
  1656. }
  1657. else
  1658. {
  1659. TraceTag(ttidEventServer, "Event source list is EMPTY!");
  1660. }
  1661. }