Leaked source code of windows server 2003
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.

4864 lines
111 KiB

  1. /*
  2. * HttpRequest.cpp
  3. *
  4. * WinHttp.WinHttpRequest COM component
  5. *
  6. * Copyright (C) 2000 Microsoft Corporation. All rights reserved. *
  7. *
  8. * Much of this code was stolen from our Xml-Http friends over in
  9. * inetcore\xml\http\xmlhttp.cxx. Thanks very much!
  10. *
  11. */
  12. #include <wininetp.h>
  13. #include "httprequest.hxx"
  14. #include <olectl.h>
  15. /////////////////////////////////////////////////////////////////////////////
  16. // private function prototypes
  17. static void WideCharToUtf8(WCHAR * buffer, UINT cch, BYTE * bytebuffer, UINT * cb);
  18. static HRESULT BSTRToUTF8(char ** psz, DWORD * pcbUTF8, BSTR bstr);
  19. static HRESULT AsciiToBSTR(BSTR * pbstr, char * sz, int cch);
  20. static HRESULT BSTRToAscii(char ** psz, BSTR bstr);
  21. static BSTR GetBSTRFromVariant(VARIANT varVariant);
  22. static BOOL GetBoolFromVariant(VARIANT varVariant, BOOL fDefault);
  23. static DWORD GetDwordFromVariant(VARIANT varVariant, DWORD dwDefault);
  24. static long GetLongFromVariant(VARIANT varVariant, long lDefault);
  25. static HRESULT CreateVector(VARIANT * pVar, const BYTE * pData, DWORD cElems);
  26. static HRESULT ReadFromStream(char ** ppData, ULONG * pcbData, IStream * pStm);
  27. static void MessageLoop();
  28. static DWORD UpdateTimeout(DWORD dwTimeout, DWORD dwStartTime);
  29. static HRESULT FillExcepInfo(HRESULT hr, EXCEPINFO * pExcepInfo);
  30. static BOOL IsValidVariant(VARIANT v);
  31. static BOOL s_fWndClassRegistered;
  32. static const char * s_szWinHttpEventMarshallerWndClass = "_WinHttpEventMarshaller";
  33. #define SafeRelease(p) \
  34. { \
  35. if (p) \
  36. (p)->Release();\
  37. (p) = NULL;\
  38. }
  39. #ifndef HWND_MESSAGE
  40. #define HWND_MESSAGE ((HWND)-3)
  41. #endif
  42. inline BOOL IsValidBstr(BSTR bstr)
  43. {
  44. // A BSTR can be NULL, or if non-NULL, it should at least
  45. // point to a 2-byte terminating NULL character.
  46. return (bstr == NULL) || (!IsBadReadPtr(bstr, 2));
  47. }
  48. #ifndef WINHTTP_STATIC_LIBRARY
  49. STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void ** ppv)
  50. {
  51. if (rclsid != CLSID_WinHttpRequest)
  52. return CLASS_E_CLASSNOTAVAILABLE;
  53. if (riid != IID_IClassFactory || ppv == NULL)
  54. return E_INVALIDARG;
  55. CClassFactory * pCF = New CClassFactory();
  56. if (pCF)
  57. {
  58. *ppv = static_cast<IClassFactory *>(pCF);
  59. pCF->AddRef();
  60. return NOERROR;
  61. }
  62. else
  63. {
  64. *ppv = NULL;
  65. return E_OUTOFMEMORY;
  66. }
  67. }
  68. CClassFactory::CClassFactory()
  69. {
  70. _cRefs = 0;
  71. }
  72. STDMETHODIMP CClassFactory::QueryInterface(REFIID riid, void ** ppvObject)
  73. {
  74. if (ppvObject == NULL)
  75. return E_INVALIDARG;
  76. if (riid == IID_IClassFactory || riid == IID_IUnknown)
  77. {
  78. *ppvObject = static_cast<IClassFactory *>(this);
  79. AddRef();
  80. return NOERROR;
  81. }
  82. else
  83. return E_NOINTERFACE;
  84. }
  85. ULONG STDMETHODCALLTYPE CClassFactory::AddRef()
  86. {
  87. return ++_cRefs;
  88. }
  89. ULONG STDMETHODCALLTYPE CClassFactory::Release()
  90. {
  91. if (--_cRefs == 0)
  92. {
  93. delete this;
  94. return 0;
  95. }
  96. return _cRefs;
  97. }
  98. STDMETHODIMP
  99. CClassFactory::CreateInstance(IUnknown * pUnkOuter, REFIID riid, void ** ppvObject)
  100. {
  101. if (pUnkOuter != NULL)
  102. return CLASS_E_NOAGGREGATION;
  103. if (ppvObject == NULL)
  104. return E_INVALIDARG;
  105. return CreateHttpRequest(riid, ppvObject);
  106. }
  107. STDMETHODIMP
  108. CClassFactory::LockServer(BOOL fLock)
  109. {
  110. return NOERROR;
  111. }
  112. #else
  113. STDAPI WinHttpCreateHttpRequestComponent(REFIID riid, void ** ppvObject)
  114. {
  115. return CreateHttpRequest(riid, ppvObject);
  116. }
  117. #endif //WINHTTP_STATIC_LIBRARY
  118. STDMETHODIMP
  119. CreateHttpRequest(REFIID riid, void ** ppvObject)
  120. {
  121. CHttpRequest * pHttpRequest = New CHttpRequest();
  122. HRESULT hr;
  123. if (pHttpRequest)
  124. {
  125. hr = pHttpRequest->QueryInterface(riid, ppvObject);
  126. if (FAILED(hr))
  127. {
  128. delete pHttpRequest;
  129. }
  130. }
  131. else
  132. hr = E_OUTOFMEMORY;
  133. return hr;
  134. }
  135. /*
  136. * CHttpRequest::CHttpRequest constructor
  137. *
  138. */
  139. CHttpRequest::CHttpRequest()
  140. {
  141. Initialize();
  142. }
  143. /*
  144. * CHttpRequest::~CHttpRequest destructor
  145. *
  146. */
  147. CHttpRequest::~CHttpRequest()
  148. {
  149. ReleaseResources();
  150. }
  151. HRESULT STDMETHODCALLTYPE
  152. CHttpRequest::QueryInterface(REFIID riid, void ** ppv)
  153. {
  154. HRESULT hr = NOERROR;
  155. if (ppv == NULL)
  156. {
  157. hr = E_INVALIDARG;
  158. }
  159. else if (riid == IID_IWinHttpRequest || riid == IID_IDispatch || riid == IID_IUnknown)
  160. {
  161. *ppv = static_cast<IWinHttpRequest *>(this);
  162. AddRef();
  163. }
  164. else if (riid == IID_IConnectionPointContainer)
  165. {
  166. *ppv = static_cast<IConnectionPointContainer *>(this);
  167. AddRef();
  168. }
  169. else if (riid == IID_IProvideClassInfo)
  170. {
  171. *ppv = static_cast<IProvideClassInfo *>(static_cast<IProvideClassInfo2 *>(this));
  172. AddRef();
  173. }
  174. else if (riid == IID_IProvideClassInfo2)
  175. {
  176. *ppv = static_cast<IProvideClassInfo2 *>(this);
  177. AddRef();
  178. }
  179. else
  180. hr = E_NOINTERFACE;
  181. return hr;
  182. }
  183. ULONG STDMETHODCALLTYPE
  184. CHttpRequest::AddRef()
  185. {
  186. if (GetCurrentThreadId() == _dwMainThreadId)
  187. ++_cRefsOnMainThread;
  188. return InterlockedIncrement(&_cRefs);
  189. }
  190. ULONG STDMETHODCALLTYPE
  191. CHttpRequest::Release()
  192. {
  193. if (GetCurrentThreadId() == _dwMainThreadId)
  194. {
  195. if ((--_cRefsOnMainThread == 0) && _fAsync)
  196. {
  197. // Clean up the Event Marshaller. This must be done
  198. // on the main thread.
  199. _CP.ShutdownEventSinksMarshaller();
  200. // If the worker thread is still running, abort it
  201. // and wait for it to run down.
  202. Abort();
  203. }
  204. }
  205. DWORD cRefs = InterlockedDecrement(&_cRefs);
  206. if (cRefs == 0)
  207. {
  208. delete this;
  209. return 0;
  210. }
  211. else
  212. return cRefs;
  213. }
  214. HRESULT
  215. CHttpRequest::GetHttpRequestTypeInfo(REFGUID guid, ITypeInfo ** ppTypeInfo)
  216. {
  217. HRESULT hr = NOERROR;
  218. ITypeLib * pTypeLib;
  219. char szPath[MAX_PATH];
  220. OLECHAR wszPath[MAX_PATH];
  221. GetModuleFileName(GlobalDllHandle, szPath, MAX_PATH);
  222. MultiByteToWideChar(CP_ACP, 0, szPath, -1, wszPath, MAX_PATH);
  223. hr = LoadTypeLib(wszPath, &pTypeLib);
  224. if (SUCCEEDED(hr))
  225. {
  226. hr = pTypeLib->GetTypeInfoOfGuid(guid, ppTypeInfo);
  227. pTypeLib->Release();
  228. }
  229. return hr;
  230. }
  231. STDMETHODIMP
  232. CHttpRequest::GetTypeInfoCount(UINT * pctinfo)
  233. {
  234. if (!pctinfo)
  235. return E_INVALIDARG;
  236. *pctinfo = 1;
  237. return NOERROR;
  238. }
  239. STDMETHODIMP
  240. CHttpRequest::GetTypeInfo(UINT iTInfo, LCID, ITypeInfo ** ppTInfo)
  241. {
  242. if (!ppTInfo)
  243. return E_INVALIDARG;
  244. *ppTInfo = NULL;
  245. if (iTInfo != 0)
  246. return DISP_E_BADINDEX;
  247. if (!_pTypeInfo)
  248. {
  249. HRESULT hr = GetHttpRequestTypeInfo(IID_IWinHttpRequest, &_pTypeInfo);
  250. if (FAILED(hr))
  251. return hr;
  252. }
  253. *ppTInfo = _pTypeInfo;
  254. _pTypeInfo->AddRef();
  255. return NOERROR;
  256. }
  257. struct IDMAPPING
  258. {
  259. const OLECHAR * wszMemberName;
  260. DISPID dispId;
  261. };
  262. static const IDMAPPING IdMapping[] =
  263. {
  264. { L"Open", DISPID_HTTPREQUEST_OPEN },
  265. { L"SetRequestHeader", DISPID_HTTPREQUEST_SETREQUESTHEADER },
  266. { L"Send", DISPID_HTTPREQUEST_SEND },
  267. { L"Status", DISPID_HTTPREQUEST_STATUS },
  268. { L"WaitForResponse", DISPID_HTTPREQUEST_WAITFORRESPONSE },
  269. { L"GetResponseHeader", DISPID_HTTPREQUEST_GETRESPONSEHEADER },
  270. { L"ResponseBody", DISPID_HTTPREQUEST_RESPONSEBODY },
  271. { L"ResponseText", DISPID_HTTPREQUEST_RESPONSETEXT },
  272. { L"ResponseStream", DISPID_HTTPREQUEST_RESPONSESTREAM },
  273. { L"StatusText", DISPID_HTTPREQUEST_STATUSTEXT },
  274. { L"SetCredentials", DISPID_HTTPREQUEST_SETCREDENTIALS },
  275. { L"SetProxy", DISPID_HTTPREQUEST_SETPROXY },
  276. { L"GetAllResponseHeaders", DISPID_HTTPREQUEST_GETALLRESPONSEHEADERS },
  277. { L"Abort", DISPID_HTTPREQUEST_ABORT },
  278. { L"SetTimeouts", DISPID_HTTPREQUEST_SETTIMEOUTS },
  279. { L"Option", DISPID_HTTPREQUEST_OPTION }
  280. };
  281. STDMETHODIMP
  282. CHttpRequest::GetIDsOfNames(REFIID riid, LPOLESTR * rgszNames,
  283. UINT cNames,
  284. LCID ,
  285. DISPID * rgDispId)
  286. {
  287. if (riid != IID_NULL)
  288. return E_INVALIDARG;
  289. HRESULT hr = NOERROR;
  290. if (cNames > 0)
  291. {
  292. hr = DISP_E_UNKNOWNNAME;
  293. for (int i = 0; i < (sizeof(IdMapping)/sizeof(IdMapping[0])); i++)
  294. {
  295. if (StrCmpIW(rgszNames[0], IdMapping[i].wszMemberName) == 0)
  296. {
  297. hr = NOERROR;
  298. rgDispId[0] = IdMapping[i].dispId;
  299. break;
  300. }
  301. }
  302. }
  303. return hr;
  304. }
  305. // _DispGetOptionalParam
  306. //
  307. // Helper routine to fetch optional parameters. If DispGetParam returns
  308. // DISP_E_PARAMNOTFOUND, the error is converted to NOERROR.
  309. //
  310. static inline HRESULT _DispGetOptionalParam
  311. (
  312. DISPPARAMS * pDispParams,
  313. DISPID dispid,
  314. VARTYPE vt,
  315. VARIANT * pvarResult,
  316. unsigned int * puArgErr
  317. )
  318. {
  319. HRESULT hr = DispGetParam(pDispParams, dispid, vt, pvarResult, puArgErr);
  320. return (hr == DISP_E_PARAMNOTFOUND) ? NOERROR : hr;
  321. }
  322. STDMETHODIMP
  323. CHttpRequest::Invoke(DISPID dispIdMember, REFIID riid,
  324. LCID,
  325. WORD wFlags,
  326. DISPPARAMS * pDispParams,
  327. VARIANT * pVarResult,
  328. EXCEPINFO * pExcepInfo,
  329. UINT * puArgErr)
  330. {
  331. HRESULT hr = NOERROR;
  332. unsigned int uArgErr;
  333. if (wFlags & ~(DISPATCH_METHOD | DISPATCH_PROPERTYGET | DISPATCH_PROPERTYPUT))
  334. return E_INVALIDARG;
  335. if (riid != IID_NULL)
  336. return DISP_E_UNKNOWNINTERFACE;
  337. if (IsBadReadPtr(pDispParams, sizeof(DISPPARAMS)))
  338. return E_INVALIDARG;
  339. if (!puArgErr)
  340. {
  341. puArgErr = &uArgErr;
  342. }
  343. else if (IsBadWritePtr(puArgErr, sizeof(UINT)))
  344. {
  345. return E_INVALIDARG;
  346. }
  347. if (pVarResult)
  348. {
  349. if (IsBadWritePtr(pVarResult, sizeof(VARIANT)))
  350. return E_INVALIDARG;
  351. VariantInit(pVarResult);
  352. }
  353. switch (dispIdMember)
  354. {
  355. case DISPID_HTTPREQUEST_ABORT:
  356. {
  357. hr = Abort();
  358. break;
  359. }
  360. case DISPID_HTTPREQUEST_SETPROXY:
  361. {
  362. VARIANT varProxySetting;
  363. VARIANT varProxyServer;
  364. VARIANT varBypassList;
  365. VariantInit(&varProxySetting);
  366. VariantInit(&varProxyServer);
  367. VariantInit(&varBypassList);
  368. hr = DispGetParam(pDispParams, 0, VT_UI4, &varProxySetting, puArgErr);
  369. if (SUCCEEDED(hr))
  370. {
  371. hr = _DispGetOptionalParam(pDispParams, 1, VT_BSTR, &varProxyServer, puArgErr);
  372. }
  373. if (SUCCEEDED(hr))
  374. {
  375. hr = _DispGetOptionalParam(pDispParams, 2, VT_BSTR, &varBypassList, puArgErr);
  376. }
  377. if (SUCCEEDED(hr))
  378. {
  379. hr = SetProxy(V_UI4(&varProxySetting), varProxyServer, varBypassList);
  380. }
  381. VariantClear(&varProxySetting);
  382. VariantClear(&varProxyServer);
  383. VariantClear(&varBypassList);
  384. break;
  385. }
  386. case DISPID_HTTPREQUEST_SETCREDENTIALS:
  387. {
  388. VARIANT varUserName;
  389. VARIANT varPassword;
  390. VARIANT varAuthTarget;
  391. VariantInit(&varUserName);
  392. VariantInit(&varPassword);
  393. VariantInit(&varAuthTarget);
  394. hr = DispGetParam(pDispParams, 0, VT_BSTR, &varUserName, puArgErr);
  395. if (SUCCEEDED(hr))
  396. {
  397. hr = DispGetParam(pDispParams, 1, VT_BSTR, &varPassword, puArgErr);
  398. }
  399. if (SUCCEEDED(hr))
  400. {
  401. hr = DispGetParam(pDispParams, 2, VT_UI4, &varAuthTarget, puArgErr);
  402. }
  403. if (SUCCEEDED(hr))
  404. {
  405. hr = SetCredentials(V_BSTR(&varUserName), V_BSTR(&varPassword),
  406. V_UI4(&varAuthTarget));
  407. }
  408. VariantClear(&varUserName);
  409. VariantClear(&varPassword);
  410. VariantClear(&varAuthTarget);
  411. break;
  412. }
  413. case DISPID_HTTPREQUEST_OPEN:
  414. {
  415. VARIANT varMethod;
  416. VARIANT varUrl;
  417. VARIANT varAsync;
  418. VariantInit(&varMethod);
  419. VariantInit(&varUrl);
  420. VariantInit(&varAsync);
  421. hr = DispGetParam(pDispParams, 0, VT_BSTR, &varMethod, puArgErr);
  422. if (SUCCEEDED(hr))
  423. {
  424. hr = DispGetParam(pDispParams, 1, VT_BSTR, &varUrl, puArgErr);
  425. }
  426. if (SUCCEEDED(hr))
  427. {
  428. hr = _DispGetOptionalParam(pDispParams, 2, VT_BOOL, &varAsync, puArgErr);
  429. }
  430. if (SUCCEEDED(hr))
  431. {
  432. hr = Open(V_BSTR(&varMethod), V_BSTR(&varUrl), varAsync);
  433. }
  434. VariantClear(&varMethod);
  435. VariantClear(&varUrl);
  436. VariantClear(&varAsync);
  437. break;
  438. }
  439. case DISPID_HTTPREQUEST_SETREQUESTHEADER:
  440. {
  441. VARIANT varHeader;
  442. VARIANT varValue;
  443. VariantInit(&varHeader);
  444. VariantInit(&varValue);
  445. hr = DispGetParam(pDispParams, 0, VT_BSTR, &varHeader, puArgErr);
  446. if (SUCCEEDED(hr))
  447. {
  448. hr = DispGetParam(pDispParams, 1, VT_BSTR, &varValue, puArgErr);
  449. }
  450. if (SUCCEEDED(hr))
  451. {
  452. hr = SetRequestHeader(V_BSTR(&varHeader), V_BSTR(&varValue));
  453. }
  454. VariantClear(&varHeader);
  455. VariantClear(&varValue);
  456. break;
  457. }
  458. case DISPID_HTTPREQUEST_GETRESPONSEHEADER:
  459. {
  460. VARIANT varHeader;
  461. VariantInit(&varHeader);
  462. hr = DispGetParam(pDispParams, 0, VT_BSTR, &varHeader, puArgErr);
  463. if (SUCCEEDED(hr))
  464. {
  465. BSTR bstrValue = NULL;
  466. hr = GetResponseHeader(V_BSTR(&varHeader), &bstrValue);
  467. if (SUCCEEDED(hr) && pVarResult)
  468. {
  469. V_VT(pVarResult) = VT_BSTR;
  470. V_BSTR(pVarResult) = bstrValue;
  471. }
  472. else
  473. SysFreeString(bstrValue);
  474. }
  475. VariantClear(&varHeader);
  476. break;
  477. }
  478. case DISPID_HTTPREQUEST_GETALLRESPONSEHEADERS:
  479. {
  480. BSTR bstrResponseHeaders = NULL;
  481. hr = GetAllResponseHeaders(&bstrResponseHeaders);
  482. if (SUCCEEDED(hr) && pVarResult)
  483. {
  484. V_VT(pVarResult) = VT_BSTR;
  485. V_BSTR(pVarResult) = bstrResponseHeaders;
  486. }
  487. else
  488. SysFreeString(bstrResponseHeaders);
  489. break;
  490. }
  491. case DISPID_HTTPREQUEST_SEND:
  492. {
  493. if (pDispParams->cArgs <= 1)
  494. {
  495. VARIANT varEmptyBody;
  496. VariantInit(&varEmptyBody);
  497. hr = Send((pDispParams->cArgs == 0) ? varEmptyBody : pDispParams->rgvarg[0]);
  498. }
  499. else
  500. {
  501. hr = DISP_E_BADPARAMCOUNT;
  502. }
  503. break;
  504. }
  505. case DISPID_HTTPREQUEST_STATUS:
  506. {
  507. long Status;
  508. hr = get_Status(&Status);
  509. if (SUCCEEDED(hr) && pVarResult)
  510. {
  511. V_VT(pVarResult) = VT_I4;
  512. V_I4(pVarResult) = Status;
  513. }
  514. break;
  515. }
  516. case DISPID_HTTPREQUEST_STATUSTEXT:
  517. {
  518. BSTR bstrStatus = NULL;
  519. hr = get_StatusText(&bstrStatus);
  520. if (SUCCEEDED(hr) && pVarResult)
  521. {
  522. V_VT(pVarResult) = VT_BSTR;
  523. V_BSTR(pVarResult) = bstrStatus;
  524. }
  525. else
  526. SysFreeString(bstrStatus);
  527. break;
  528. }
  529. case DISPID_HTTPREQUEST_RESPONSETEXT:
  530. {
  531. BSTR bstrResponse = NULL;
  532. hr = get_ResponseText(&bstrResponse);
  533. if (SUCCEEDED(hr) && pVarResult)
  534. {
  535. V_VT(pVarResult) = VT_BSTR;
  536. V_BSTR(pVarResult) = bstrResponse;
  537. }
  538. else
  539. SysFreeString(bstrResponse);
  540. break;
  541. }
  542. case DISPID_HTTPREQUEST_RESPONSEBODY:
  543. {
  544. if (pVarResult)
  545. {
  546. hr = get_ResponseBody(pVarResult);
  547. }
  548. break;
  549. }
  550. case DISPID_HTTPREQUEST_RESPONSESTREAM:
  551. {
  552. if (pVarResult)
  553. {
  554. hr = get_ResponseStream(pVarResult);
  555. }
  556. break;
  557. }
  558. case DISPID_HTTPREQUEST_OPTION:
  559. {
  560. VARIANT varOption;
  561. WinHttpRequestOption Option;
  562. VariantInit(&varOption);
  563. hr = DispGetParam(pDispParams, 0, VT_I4, &varOption, puArgErr);
  564. if (FAILED(hr))
  565. break;
  566. Option = static_cast<WinHttpRequestOption>(V_I4(&varOption));
  567. if (wFlags & (DISPATCH_METHOD | DISPATCH_PROPERTYGET))
  568. {
  569. if (pVarResult)
  570. {
  571. hr = get_Option(Option, pVarResult);
  572. }
  573. }
  574. else if (wFlags & DISPATCH_PROPERTYPUT)
  575. {
  576. hr = put_Option(Option, pDispParams->rgvarg[0]);
  577. }
  578. VariantClear(&varOption);
  579. break;
  580. }
  581. case DISPID_HTTPREQUEST_WAITFORRESPONSE:
  582. {
  583. VARIANT varTimeout;
  584. VARIANT_BOOL boolSucceeded;
  585. VariantInit(&varTimeout);
  586. hr = _DispGetOptionalParam(pDispParams, 0, VT_I4, &varTimeout, puArgErr);
  587. if (SUCCEEDED(hr))
  588. {
  589. hr = WaitForResponse(varTimeout, &boolSucceeded);
  590. }
  591. if (pVarResult)
  592. {
  593. V_VT(pVarResult) = VT_BOOL;
  594. V_BOOL(pVarResult) = boolSucceeded;
  595. }
  596. VariantClear(&varTimeout);
  597. break;
  598. }
  599. case DISPID_HTTPREQUEST_SETTIMEOUTS:
  600. {
  601. VARIANT varResolveTimeout;
  602. VARIANT varConnectTimeout;
  603. VARIANT varSendTimeout;
  604. VARIANT varReceiveTimeout;
  605. VariantInit(&varResolveTimeout);
  606. VariantInit(&varConnectTimeout);
  607. VariantInit(&varSendTimeout);
  608. VariantInit(&varReceiveTimeout);
  609. hr = DispGetParam(pDispParams, 0, VT_I4, &varResolveTimeout, puArgErr);
  610. if (SUCCEEDED(hr))
  611. {
  612. hr = DispGetParam(pDispParams, 1, VT_I4, &varConnectTimeout, puArgErr);
  613. }
  614. if (SUCCEEDED(hr))
  615. {
  616. hr = DispGetParam(pDispParams, 2, VT_I4, &varSendTimeout, puArgErr);
  617. }
  618. if (SUCCEEDED(hr))
  619. {
  620. hr = DispGetParam(pDispParams, 3, VT_I4, &varReceiveTimeout, puArgErr);
  621. }
  622. if (SUCCEEDED(hr))
  623. {
  624. hr = SetTimeouts(V_I4(&varResolveTimeout), V_I4(&varConnectTimeout),
  625. V_I4(&varSendTimeout),
  626. V_I4(&varReceiveTimeout));
  627. }
  628. VariantClear(&varResolveTimeout);
  629. VariantClear(&varConnectTimeout);
  630. VariantClear(&varSendTimeout);
  631. VariantClear(&varReceiveTimeout);
  632. break;
  633. }
  634. default:
  635. hr = DISP_E_MEMBERNOTFOUND;
  636. break;
  637. }
  638. if (FAILED(hr) && (pExcepInfo != NULL))
  639. {
  640. hr = FillExcepInfo(hr, pExcepInfo);
  641. }
  642. return hr;
  643. }
  644. static
  645. HRESULT
  646. FillExcepInfo(HRESULT hr, EXCEPINFO * pExcepInfo)
  647. {
  648. // Don't create excepinfo for these errors to mimic oleaut behavior.
  649. if( hr == DISP_E_BADPARAMCOUNT ||
  650. hr == DISP_E_NONAMEDARGS ||
  651. hr == DISP_E_MEMBERNOTFOUND ||
  652. hr == E_INVALIDARG)
  653. {
  654. return hr;
  655. }
  656. // clear out exception info
  657. IErrorInfo * pei = NULL;
  658. pExcepInfo->wCode = 0;
  659. pExcepInfo->scode = hr;
  660. // if error info exists, use it
  661. GetErrorInfo(0, &pei);
  662. if (pei)
  663. {
  664. // give back to OLE
  665. SetErrorInfo(0, pei);
  666. pei->GetHelpContext(&pExcepInfo->dwHelpContext);
  667. pei->GetSource(&pExcepInfo->bstrSource);
  668. pei->GetDescription(&pExcepInfo->bstrDescription);
  669. pei->GetHelpFile(&pExcepInfo->bstrHelpFile);
  670. // give complete ownership to OLEAUT
  671. pei->Release();
  672. hr = DISP_E_EXCEPTION;
  673. }
  674. return hr;
  675. }
  676. STDMETHODIMP
  677. CHttpRequest::GetClassInfo(ITypeInfo ** ppTI)
  678. {
  679. if (!ppTI)
  680. return E_POINTER;
  681. *ppTI = NULL;
  682. return GetHttpRequestTypeInfo(CLSID_WinHttpRequest, ppTI);
  683. }
  684. STDMETHODIMP
  685. CHttpRequest::GetGUID(DWORD dwGuidKind, GUID * pGUID)
  686. {
  687. if (!pGUID)
  688. return E_POINTER;
  689. if (dwGuidKind == GUIDKIND_DEFAULT_SOURCE_DISP_IID)
  690. {
  691. *pGUID = IID_IWinHttpRequestEvents;
  692. }
  693. else
  694. return E_INVALIDARG;
  695. return NOERROR;
  696. }
  697. STDMETHODIMP
  698. CHttpRequest::EnumConnectionPoints(IEnumConnectionPoints **)
  699. {
  700. return E_NOTIMPL;
  701. }
  702. STDMETHODIMP
  703. CHttpRequest::FindConnectionPoint(REFIID riid, IConnectionPoint ** ppCP)
  704. {
  705. if (!ppCP)
  706. return E_POINTER;
  707. if (riid == IID_IWinHttpRequestEvents)
  708. {
  709. return _CP.QueryInterface(IID_IConnectionPoint, (void **)ppCP);
  710. }
  711. else
  712. return CONNECT_E_NOCONNECTION;
  713. }
  714. STDMETHODIMP
  715. CHttpRequest::CHttpRequestEventsCP::QueryInterface(REFIID riid, void ** ppvObject)
  716. {
  717. if (!ppvObject)
  718. return E_INVALIDARG;
  719. if (riid == IID_IUnknown || riid == IID_IConnectionPoint)
  720. {
  721. *ppvObject = static_cast<IUnknown *>(static_cast<IConnectionPoint *>(this));
  722. AddRef();
  723. return NOERROR;
  724. }
  725. return E_NOINTERFACE;
  726. }
  727. ULONG STDMETHODCALLTYPE
  728. CHttpRequest::CHttpRequestEventsCP::AddRef()
  729. {
  730. return Px()->AddRef();
  731. }
  732. ULONG STDMETHODCALLTYPE
  733. CHttpRequest::CHttpRequestEventsCP::Release()
  734. {
  735. return Px()->Release();
  736. }
  737. STDMETHODIMP
  738. CHttpRequest::CHttpRequestEventsCP::GetConnectionInterface(IID * pIID)
  739. {
  740. if (!pIID)
  741. return E_POINTER;
  742. *pIID = IID_IWinHttpRequestEvents;
  743. return NOERROR;
  744. }
  745. STDMETHODIMP
  746. CHttpRequest::CHttpRequestEventsCP::GetConnectionPointContainer
  747. (
  748. IConnectionPointContainer ** ppCPC
  749. )
  750. {
  751. if (!ppCPC)
  752. return E_POINTER;
  753. return Px()->QueryInterface(IID_IConnectionPointContainer, (void **)ppCPC);
  754. }
  755. STDMETHODIMP
  756. CHttpRequest::CHttpRequestEventsCP::Advise(IUnknown * pUnk, DWORD * pdwCookie)
  757. {
  758. if (!pUnk || !pdwCookie)
  759. {
  760. return E_POINTER;
  761. }
  762. IWinHttpRequestEvents * pIWinHttpRequestEvents;
  763. HRESULT hr;
  764. hr = pUnk->QueryInterface(IID_IWinHttpRequestEvents, (void **)&pIWinHttpRequestEvents);
  765. if (SUCCEEDED(hr))
  766. {
  767. *pdwCookie = _SinkArray.Add(static_cast<IUnknown *>(pIWinHttpRequestEvents));
  768. if (*pdwCookie)
  769. {
  770. _cConnections++;
  771. hr = NOERROR;
  772. }
  773. else
  774. {
  775. hr = E_OUTOFMEMORY;
  776. }
  777. }
  778. else
  779. hr = CONNECT_E_CANNOTCONNECT;
  780. return hr;
  781. }
  782. STDMETHODIMP
  783. CHttpRequest::CHttpRequestEventsCP::Unadvise(DWORD dwCookie)
  784. {
  785. IUnknown * pSink = _SinkArray.GetUnknown(dwCookie);
  786. if (pSink)
  787. {
  788. _SinkArray.Remove(dwCookie);
  789. pSink->Release();
  790. --_cConnections;
  791. }
  792. return NOERROR;
  793. }
  794. STDMETHODIMP
  795. CHttpRequest::CHttpRequestEventsCP::EnumConnections(IEnumConnections **)
  796. {
  797. return E_NOTIMPL;
  798. }
  799. void
  800. CHttpRequest::CHttpRequestEventsCP::FireOnResponseStart(long Status, BSTR ContentType)
  801. {
  802. if (_cConnections > 0 && !Px()->_bAborted)
  803. {
  804. GetSink()->OnResponseStart(Status, ContentType);
  805. }
  806. }
  807. void
  808. CHttpRequest::CHttpRequestEventsCP::FireOnResponseDataAvailable
  809. (
  810. const BYTE * rgbData,
  811. DWORD cbData
  812. )
  813. {
  814. if (_cConnections > 0 && !Px()->_bAborted)
  815. {
  816. VARIANT varData;
  817. HRESULT hr;
  818. VariantInit(&varData);
  819. hr = CreateVector(&varData, rgbData, cbData);
  820. if (SUCCEEDED(hr))
  821. {
  822. GetSink()->OnResponseDataAvailable(&V_ARRAY(&varData));
  823. }
  824. VariantClear(&varData);
  825. }
  826. }
  827. void
  828. CHttpRequest::CHttpRequestEventsCP::FireOnResponseFinished()
  829. {
  830. if (_cConnections > 0 && !Px()->_bAborted)
  831. {
  832. GetSink()->OnResponseFinished();
  833. }
  834. }
  835. HRESULT
  836. CHttpRequest::CHttpRequestEventsCP::CreateEventSinksMarshaller()
  837. {
  838. HRESULT hr = NOERROR;
  839. if (_cConnections > 0)
  840. {
  841. SafeRelease(_pSinkMarshaller);
  842. hr = CWinHttpRequestEventsMarshaller::Create(&_SinkArray, &_pSinkMarshaller);
  843. }
  844. return hr;
  845. }
  846. void
  847. CHttpRequest::CHttpRequestEventsCP::ShutdownEventSinksMarshaller()
  848. {
  849. if (_pSinkMarshaller)
  850. _pSinkMarshaller->Shutdown();
  851. }
  852. void
  853. CHttpRequest::CHttpRequestEventsCP::ReleaseEventSinksMarshaller()
  854. {
  855. SafeRelease(_pSinkMarshaller);
  856. }
  857. void
  858. CHttpRequest::CHttpRequestEventsCP::FreezeEvents()
  859. {
  860. if (_pSinkMarshaller)
  861. _pSinkMarshaller->FreezeEvents();
  862. }
  863. void
  864. CHttpRequest::CHttpRequestEventsCP::UnfreezeEvents()
  865. {
  866. if (_pSinkMarshaller)
  867. _pSinkMarshaller->UnfreezeEvents();
  868. }
  869. CHttpRequest::CHttpRequestEventsCP::~CHttpRequestEventsCP()
  870. {
  871. // If any connections are still alive, unadvise them.
  872. if (_cConnections > 0)
  873. {
  874. _SinkArray.ReleaseAll();
  875. _cConnections = 0;
  876. }
  877. }
  878. /*
  879. * CHttpRequest::Initialize
  880. *
  881. * Purpose:
  882. * Zero all data members
  883. *
  884. */
  885. void
  886. CHttpRequest::Initialize()
  887. {
  888. _cRefs = 0;
  889. _pTypeInfo = NULL;
  890. _bstrUserAgent = NULL;
  891. _dwProxySetting = INTERNET_OPEN_TYPE_PRECONFIG;
  892. _bstrProxyServer = NULL;
  893. _bstrBypassList = NULL;
  894. _eState = CHttpRequest::CREATED;
  895. _fAsync = FALSE;
  896. _hWorkerThread = NULL;
  897. _cRefsOnMainThread = 0;
  898. _dwMainThreadId = GetCurrentThreadId();
  899. _hrAsyncResult = NOERROR;
  900. _bAborted = false;
  901. _bSetTimeouts = false;
  902. _bDisableRedirects = false;
  903. _hInet = NULL;
  904. _hConnection = NULL;
  905. _hHTTP = NULL;
  906. _ResolveTimeout = 0;
  907. _ConnectTimeout = 0;
  908. _SendTimeout = 0;
  909. _ReceiveTimeout = 0;
  910. _cbRequestBody = 0;
  911. _szRequestBuffer = NULL;
  912. _dwCodePage = CP_UTF8;
  913. _dwEscapePercentFlag = 0;
  914. _szResponseBuffer = NULL;
  915. _cbResponseBuffer = 0;
  916. _cbResponseBody = 0;
  917. _hAbortedConnectObject = NULL;
  918. _hAbortedRequestObject = NULL;
  919. _bstrCertSubject = NULL;
  920. _dwSslIgnoreFlags = 0;
  921. }
  922. /*
  923. * CHttpRequest::ReleaseResources
  924. *
  925. * Purpose:
  926. * Release all handles, events, and buffers
  927. *
  928. */
  929. void
  930. CHttpRequest::ReleaseResources()
  931. {
  932. SafeRelease(_pTypeInfo);
  933. if (_hWorkerThread)
  934. {
  935. CloseHandle(_hWorkerThread);
  936. _hWorkerThread = NULL;
  937. }
  938. _CP.ReleaseEventSinksMarshaller();
  939. //
  940. // Derefence aborted handle objects (if any).
  941. //
  942. if (_hAbortedRequestObject != NULL)
  943. {
  944. DereferenceObject(_hAbortedRequestObject);
  945. _hAbortedRequestObject = NULL;
  946. }
  947. if (_hAbortedConnectObject != NULL)
  948. {
  949. DereferenceObject(_hAbortedConnectObject);
  950. _hAbortedConnectObject = NULL;
  951. }
  952. if (_hHTTP)
  953. {
  954. HINTERNET temp = _hHTTP;
  955. _hHTTP = NULL;
  956. WinHttpCloseHandle(temp);
  957. }
  958. if (_hConnection)
  959. {
  960. HINTERNET temp = _hConnection;
  961. _hConnection = NULL;
  962. WinHttpCloseHandle(temp);
  963. }
  964. if (_hInet)
  965. {
  966. HINTERNET temp = _hInet;
  967. _hInet = NULL;
  968. WinHttpCloseHandle(temp);
  969. }
  970. if (_szRequestBuffer)
  971. {
  972. delete [] _szRequestBuffer;
  973. _szRequestBuffer = NULL;
  974. }
  975. if (_szResponseBuffer)
  976. {
  977. delete [] _szResponseBuffer;
  978. _szResponseBuffer = NULL;
  979. }
  980. if (_bstrUserAgent)
  981. {
  982. SysFreeString(_bstrUserAgent);
  983. _bstrUserAgent = NULL;
  984. }
  985. if (_bstrProxyServer)
  986. {
  987. SysFreeString(_bstrProxyServer);
  988. _bstrProxyServer = NULL;
  989. }
  990. if (_bstrBypassList)
  991. {
  992. SysFreeString(_bstrBypassList);
  993. _bstrBypassList = NULL;
  994. }
  995. if (_bstrCertSubject)
  996. {
  997. SysFreeString(_bstrCertSubject);
  998. _bstrCertSubject = NULL;
  999. }
  1000. }
  1001. /*
  1002. * CHttpRequest::Reset
  1003. *
  1004. * Purpose:
  1005. * Release all resources and initialize data members
  1006. *
  1007. */
  1008. void
  1009. CHttpRequest::Reset()
  1010. {
  1011. ReleaseResources();
  1012. Initialize();
  1013. }
  1014. /*
  1015. * CHttpRequest::Recycle
  1016. *
  1017. * Purpose:
  1018. * Recycle object
  1019. *
  1020. */
  1021. void
  1022. CHttpRequest::Recycle()
  1023. {
  1024. //
  1025. // Wait for the worker thread to shut down. This shouldn't take long
  1026. // since the Abort will close the Request and Connection handles.
  1027. //
  1028. if (_hWorkerThread)
  1029. {
  1030. DWORD dwWaitResult;
  1031. for (;;)
  1032. {
  1033. dwWaitResult = MsgWaitForMultipleObjects(1, &_hWorkerThread,
  1034. FALSE,
  1035. INFINITE,
  1036. QS_ALLINPUT);
  1037. if (dwWaitResult == (WAIT_OBJECT_0 + 1))
  1038. {
  1039. // Message waiting in the message queue.
  1040. // Run message pump to clear queue.
  1041. MessageLoop();
  1042. }
  1043. else
  1044. {
  1045. break;
  1046. }
  1047. }
  1048. CloseHandle(_hWorkerThread);
  1049. _hWorkerThread = NULL;
  1050. }
  1051. _hConnection = NULL;
  1052. _hHTTP = NULL;
  1053. //
  1054. // Derefence aborted handle objects (if any).
  1055. //
  1056. if (_hAbortedRequestObject != NULL)
  1057. {
  1058. DereferenceObject(_hAbortedRequestObject);
  1059. _hAbortedRequestObject = NULL;
  1060. }
  1061. if (_hAbortedConnectObject != NULL)
  1062. {
  1063. DereferenceObject(_hAbortedConnectObject);
  1064. _hAbortedConnectObject = NULL;
  1065. }
  1066. _fAsync = FALSE;
  1067. _hrAsyncResult = NOERROR;
  1068. _bAborted = false;
  1069. // don't reset timeouts, keep any that were set.
  1070. _cbRequestBody = 0;
  1071. _cbResponseBuffer = 0;
  1072. _cbResponseBody = 0;
  1073. if (_szRequestBuffer)
  1074. {
  1075. delete [] _szRequestBuffer;
  1076. _szRequestBuffer = NULL;
  1077. }
  1078. if (_szResponseBuffer)
  1079. {
  1080. delete [] _szResponseBuffer;
  1081. _szResponseBuffer = NULL;
  1082. }
  1083. _CP.ShutdownEventSinksMarshaller();
  1084. _CP.ReleaseEventSinksMarshaller();
  1085. // Allow events to fire; Abort() would have frozen them from firing.
  1086. _CP.UnfreezeEvents();
  1087. SetState(CHttpRequest::CREATED);
  1088. }
  1089. /*
  1090. * CHttpRequest::ReadResponse
  1091. *
  1092. * Purpose:
  1093. * Read the response bits
  1094. *
  1095. * Parameters:
  1096. * None
  1097. *
  1098. * Errors:
  1099. * E_FAIL
  1100. * E_OUTOFMEMORY
  1101. */
  1102. HRESULT
  1103. CHttpRequest::ReadResponse()
  1104. {
  1105. HRESULT hr = NOERROR;
  1106. BOOL fRetCode;
  1107. long lStatus;
  1108. BSTR bstrContentType = NULL;
  1109. DWORD dwContentLength = 0;
  1110. // Determine the content length
  1111. DWORD cb = sizeof(dwContentLength);
  1112. fRetCode = HttpQueryInfoA(
  1113. _hHTTP,
  1114. HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
  1115. WINHTTP_HEADER_NAME_BY_INDEX,
  1116. &dwContentLength,
  1117. &cb,
  1118. 0);
  1119. hr = get_Status(&lStatus);
  1120. if (FAILED(hr))
  1121. goto Error;
  1122. hr = _GetResponseHeader(L"Content-Type", &bstrContentType);
  1123. if (FAILED(hr))
  1124. {
  1125. bstrContentType = SysAllocString(L"");
  1126. if (bstrContentType == NULL)
  1127. goto ErrorOutOfMemory;
  1128. hr = NOERROR;
  1129. }
  1130. _CP.FireOnResponseStart(lStatus, bstrContentType);
  1131. if (dwContentLength != 0)
  1132. {
  1133. _szResponseBuffer = New char[dwContentLength];
  1134. if (_szResponseBuffer)
  1135. {
  1136. _cbResponseBuffer = dwContentLength;
  1137. }
  1138. else
  1139. goto ErrorOutOfMemory;
  1140. }
  1141. else
  1142. {
  1143. _szResponseBuffer = NULL;
  1144. _cbResponseBuffer = 0;
  1145. }
  1146. //
  1147. // Read data until there is no more - we need to buffer the data
  1148. //
  1149. while (!_bAborted)
  1150. {
  1151. DWORD cbAvail = 0;
  1152. DWORD cbRead = 0;
  1153. fRetCode = WinHttpQueryDataAvailable(_hHTTP, &cbAvail);
  1154. if (!fRetCode)
  1155. {
  1156. goto ErrorFail;
  1157. }
  1158. // Check for buffer overflow - dynamically resize if neccessary
  1159. if (_cbResponseBody + cbAvail > _cbResponseBuffer)
  1160. {
  1161. ULONG cbNewSize = _cbResponseBody + cbAvail;
  1162. char * szNewBuf = New char[cbNewSize];
  1163. if (!szNewBuf)
  1164. goto ErrorOutOfMemory;
  1165. if (_szResponseBuffer)
  1166. {
  1167. ::memcpy(szNewBuf, _szResponseBuffer, _cbResponseBody);
  1168. delete [] _szResponseBuffer;
  1169. }
  1170. _cbResponseBuffer = cbNewSize;
  1171. _szResponseBuffer = szNewBuf;
  1172. }
  1173. fRetCode = WinHttpReadData(
  1174. _hHTTP,
  1175. &_szResponseBuffer[_cbResponseBody],
  1176. cbAvail,
  1177. &cbRead);
  1178. if (!fRetCode)
  1179. {
  1180. goto ErrorFail;
  1181. }
  1182. // No more data
  1183. if (cbRead == 0)
  1184. break;
  1185. _CP.FireOnResponseDataAvailable((const BYTE *)&_szResponseBuffer[_cbResponseBody],
  1186. cbRead);
  1187. _cbResponseBody += cbRead;
  1188. }
  1189. SetState(CHttpRequest::RESPONSE);
  1190. hr = NOERROR;
  1191. Cleanup:
  1192. if (bstrContentType)
  1193. SysFreeString(bstrContentType);
  1194. _CP.FireOnResponseFinished();
  1195. return hr;
  1196. ErrorOutOfMemory:
  1197. hr = E_OUTOFMEMORY;
  1198. goto Error;
  1199. ErrorFail:
  1200. hr = HRESULT_FROM_WIN32(GetLastError());
  1201. goto Cleanup;
  1202. Error:
  1203. goto Cleanup;
  1204. }
  1205. STDMETHODIMP
  1206. CHttpRequest::SetProxy(HTTPREQUEST_PROXY_SETTING ProxySetting,
  1207. VARIANT varProxyServer,
  1208. VARIANT varBypassList)
  1209. {
  1210. HRESULT hr = NOERROR;
  1211. if (!IsValidVariant(varProxyServer) || !IsValidVariant(varBypassList))
  1212. return E_INVALIDARG;
  1213. if (_bstrProxyServer)
  1214. {
  1215. SysFreeString(_bstrProxyServer);
  1216. _bstrProxyServer = NULL;
  1217. }
  1218. if (_bstrBypassList)
  1219. {
  1220. SysFreeString(_bstrBypassList);
  1221. _bstrBypassList = NULL;
  1222. }
  1223. switch (ProxySetting)
  1224. {
  1225. case HTTPREQUEST_PROXYSETTING_PRECONFIG:
  1226. _dwProxySetting = INTERNET_OPEN_TYPE_PRECONFIG;
  1227. break;
  1228. case HTTPREQUEST_PROXYSETTING_DIRECT:
  1229. _dwProxySetting = INTERNET_OPEN_TYPE_DIRECT;
  1230. break;
  1231. case HTTPREQUEST_PROXYSETTING_PROXY:
  1232. _dwProxySetting = INTERNET_OPEN_TYPE_PROXY;
  1233. _bstrProxyServer = GetBSTRFromVariant(varProxyServer);
  1234. _bstrBypassList = GetBSTRFromVariant(varBypassList);
  1235. break;
  1236. default:
  1237. hr = E_INVALIDARG;
  1238. break;
  1239. }
  1240. if (SUCCEEDED(hr))
  1241. {
  1242. if (_hInet)
  1243. {
  1244. WINHTTP_PROXY_INFOW ProxyInfo;
  1245. memset(&ProxyInfo, 0, sizeof(ProxyInfo));
  1246. ProxyInfo.dwAccessType = _dwProxySetting;
  1247. ProxyInfo.lpszProxy = _bstrProxyServer;
  1248. ProxyInfo.lpszProxyBypass = _bstrBypassList;
  1249. if (!WinHttpSetOption(_hInet, WINHTTP_OPTION_PROXY,
  1250. &ProxyInfo,
  1251. sizeof(ProxyInfo)))
  1252. {
  1253. hr = HRESULT_FROM_WIN32(GetLastError());
  1254. }
  1255. }
  1256. }
  1257. SetErrorInfo(hr);
  1258. return hr;
  1259. }
  1260. STDMETHODIMP
  1261. CHttpRequest::SetCredentials(
  1262. BSTR bstrUserName,
  1263. BSTR bstrPassword,
  1264. HTTPREQUEST_SETCREDENTIALS_FLAGS Flags)
  1265. {
  1266. HRESULT hr;
  1267. // Must call Open method before SetCredentials.
  1268. if (! _hHTTP)
  1269. {
  1270. goto ErrorCannotCallBeforeOpen;
  1271. }
  1272. if (!IsValidBstr(bstrUserName) || !IsValidBstr(bstrPassword))
  1273. return E_INVALIDARG;
  1274. if (Flags == HTTPREQUEST_SETCREDENTIALS_FOR_SERVER)
  1275. {
  1276. // Set Username and Password.
  1277. WinHttpSetOption(
  1278. _hHTTP,
  1279. WINHTTP_OPTION_USERNAME,
  1280. bstrUserName,
  1281. SysStringLen(bstrUserName));
  1282. WinHttpSetOption(
  1283. _hHTTP,
  1284. WINHTTP_OPTION_PASSWORD,
  1285. bstrPassword,
  1286. SysStringLen(bstrPassword));
  1287. }
  1288. else if (Flags == HTTPREQUEST_SETCREDENTIALS_FOR_PROXY)
  1289. {
  1290. // Set Username and Password.
  1291. WinHttpSetOption(
  1292. _hHTTP,
  1293. WINHTTP_OPTION_PROXY_USERNAME,
  1294. bstrUserName,
  1295. SysStringLen(bstrUserName));
  1296. WinHttpSetOption(
  1297. _hHTTP,
  1298. WINHTTP_OPTION_PROXY_PASSWORD,
  1299. bstrPassword,
  1300. SysStringLen(bstrPassword));
  1301. }
  1302. else
  1303. return E_INVALIDARG;
  1304. hr = NOERROR;
  1305. Cleanup:
  1306. SetErrorInfo(hr);
  1307. return hr;
  1308. ErrorCannotCallBeforeOpen:
  1309. hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN);
  1310. goto Cleanup;
  1311. }
  1312. /*
  1313. * CHttpRequest::Open
  1314. *
  1315. * Purpose:
  1316. * Open a logical HTTP connection
  1317. *
  1318. * Parameters:
  1319. * bstrMethod IN HTTP method (GET, PUT, ...)
  1320. * bstrUrl IN Target URL
  1321. *
  1322. * Errors:
  1323. * E_FAIL
  1324. * E_INVALIDARG
  1325. * E_OUTOFMEMORY
  1326. * E_ACCESSDENIED
  1327. * Errors from InternetOpenA and WinHttpCrackUrlA and InternetConnectA
  1328. * and HttpOpenRequestA
  1329. */
  1330. STDMETHODIMP
  1331. CHttpRequest::Open(
  1332. BSTR bstrMethod,
  1333. BSTR bstrUrl,
  1334. VARIANT varAsync)
  1335. {
  1336. HRESULT hr = NOERROR;
  1337. BSTR bstrHostName = NULL;
  1338. BSTR bstrUrlPath = NULL;
  1339. DWORD dwHttpOpenFlags = 0;
  1340. URL_COMPONENTSW url;
  1341. // Check for reinitialization
  1342. if (_eState != CHttpRequest::CREATED)
  1343. {
  1344. //
  1345. // Abort any request in progress.
  1346. // This will also recycle the object.
  1347. //
  1348. Abort();
  1349. }
  1350. // Validate parameters
  1351. if (!bstrMethod || !bstrUrl ||
  1352. !IsValidBstr(bstrMethod) ||
  1353. !IsValidBstr(bstrUrl) ||
  1354. !lstrlenW(bstrMethod) || // cannot have empty method
  1355. !lstrlenW(bstrUrl) || // cannot have empty url
  1356. !IsValidVariant(varAsync))
  1357. return E_INVALIDARG;
  1358. _fAsync = GetBoolFromVariant(varAsync, FALSE);
  1359. //
  1360. // Open an Internet Session if one does not already exist.
  1361. //
  1362. if (!_hInet)
  1363. {
  1364. _hInet = WinHttpOpen(
  1365. GetUserAgentString(),
  1366. _dwProxySetting,
  1367. _bstrProxyServer,
  1368. _bstrBypassList,
  1369. 0);
  1370. if (!_hInet)
  1371. goto ErrorFail;
  1372. // In winhttp5, this should be adjusted through an exposed option
  1373. // or flag, rather than going through the back door.
  1374. HINTERNET hSessionMapped = NULL;
  1375. if (ERROR_SUCCESS == MapHandleToAddress(_hInet,
  1376. (LPVOID *)&hSessionMapped,
  1377. FALSE) &&
  1378. hSessionMapped)
  1379. {
  1380. ((INTERNET_HANDLE_OBJECT *)hSessionMapped)->SetUseSslSessionCache(TRUE);
  1381. DereferenceObject(hSessionMapped);
  1382. }
  1383. }
  1384. //
  1385. // If any timeouts were set previously, apply them.
  1386. //
  1387. if (_bSetTimeouts)
  1388. {
  1389. if (!WinHttpSetTimeouts(_hInet, (int)_ResolveTimeout,
  1390. (int)_ConnectTimeout,
  1391. (int)_SendTimeout,
  1392. (int)_ReceiveTimeout))
  1393. goto ErrorFail;
  1394. }
  1395. //
  1396. // Set the code page on the Session handle; the Connect
  1397. // handle will also inherit this value.
  1398. //
  1399. if (!WinHttpSetOption(_hInet, WINHTTP_OPTION_CODEPAGE,
  1400. &_dwCodePage,
  1401. sizeof(_dwCodePage)))
  1402. goto ErrorFail;
  1403. // Break the URL into the required components
  1404. ZeroMemory(&url, sizeof(URL_COMPONENTSW));
  1405. url.dwStructSize = sizeof(URL_COMPONENTSW);
  1406. url.dwHostNameLength = 1;
  1407. url.dwUrlPathLength = 1;
  1408. url.dwExtraInfoLength = 1;
  1409. if (!WinHttpCrackUrl(bstrUrl, 0, 0, &url))
  1410. goto ErrorFail;
  1411. // Check for non-http schemes
  1412. if (url.nScheme != INTERNET_SCHEME_HTTP && url.nScheme != INTERNET_SCHEME_HTTPS)
  1413. goto ErrorUnsupportedScheme;
  1414. // IE6/Reno Bug #6236: if the client does not specify a resource path,
  1415. // then add the "/".
  1416. if (url.dwUrlPathLength == 0)
  1417. {
  1418. INET_ASSERT(url.dwExtraInfoLength == 1);
  1419. url.lpszUrlPath = L"/";
  1420. url.dwUrlPathLength = 1;
  1421. }
  1422. bstrHostName = SysAllocStringLen(url.lpszHostName, url.dwHostNameLength);
  1423. bstrUrlPath = SysAllocStringLen(url.lpszUrlPath, lstrlenW(url.lpszUrlPath));
  1424. if (!bstrHostName || !bstrUrlPath)
  1425. goto ErrorOutOfMemory;
  1426. INET_ASSERT(_hConnection == NULL);
  1427. INET_ASSERT(_hHTTP == NULL);
  1428. _hConnection = WinHttpConnect(
  1429. _hInet,
  1430. bstrHostName,
  1431. url.nPort,
  1432. 0);
  1433. if (!_hConnection)
  1434. goto ErrorFail;
  1435. if (url.nScheme == INTERNET_SCHEME_HTTPS)
  1436. {
  1437. dwHttpOpenFlags |= WINHTTP_FLAG_SECURE;
  1438. }
  1439. //
  1440. // Apply EscapePercentInURL option.
  1441. //
  1442. dwHttpOpenFlags |= _dwEscapePercentFlag;
  1443. _hHTTP = WinHttpOpenRequest(
  1444. _hConnection,
  1445. bstrMethod,
  1446. bstrUrlPath,
  1447. NULL,
  1448. NULL,
  1449. NULL,
  1450. dwHttpOpenFlags);
  1451. if (!_hHTTP)
  1452. goto ErrorFail;
  1453. // Set the SSL ignore flags through an undocumented front door
  1454. if (_dwSslIgnoreFlags)
  1455. {
  1456. WinHttpSetOption(_hHTTP,
  1457. WINHTTP_OPTION_SECURITY_FLAGS,
  1458. (LPVOID)&_dwSslIgnoreFlags,
  1459. sizeof(_dwSslIgnoreFlags));
  1460. }
  1461. SetState(CHttpRequest::OPENED);
  1462. hr = NOERROR;
  1463. Cleanup:
  1464. if (bstrHostName)
  1465. SysFreeString(bstrHostName);;
  1466. if (bstrUrlPath)
  1467. SysFreeString(bstrUrlPath);
  1468. SetErrorInfo(hr);
  1469. return hr;
  1470. ErrorUnsupportedScheme:
  1471. hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_UNRECOGNIZED_SCHEME);
  1472. goto Cleanup;
  1473. ErrorOutOfMemory:
  1474. hr = E_OUTOFMEMORY;
  1475. goto Error;
  1476. ErrorFail:
  1477. hr = HRESULT_FROM_WIN32(GetLastError());
  1478. goto Cleanup;
  1479. Error:
  1480. goto Cleanup;
  1481. }
  1482. /*
  1483. * CHttpRequest::SetRequestHeader
  1484. *
  1485. * Purpose:
  1486. * Set a request header
  1487. *
  1488. * Parameters:
  1489. * bstrHeader IN HTTP request header
  1490. * bstrValue IN Header value
  1491. *
  1492. * Errors:
  1493. * E_FAIL
  1494. * E_INVALIDARG
  1495. * E_UNEXPECTED
  1496. */
  1497. STDMETHODIMP
  1498. CHttpRequest::SetRequestHeader(BSTR bstrHeader, BSTR bstrValue)
  1499. {
  1500. WCHAR * wszHeaderValue = NULL;
  1501. DWORD cchHeaderValue;
  1502. DWORD dwModifiers = HTTP_ADDREQ_FLAG_ADD;
  1503. HRESULT hr = NOERROR;
  1504. // Validate header parameter (null or zero-length value is allowed)
  1505. if (!bstrHeader || !IsValidBstr(bstrHeader) ||
  1506. lstrlenW(bstrHeader)==0 ||
  1507. !IsValidBstr(bstrValue))
  1508. return E_INVALIDARG;
  1509. // Validate state
  1510. if (_eState < CHttpRequest::OPENED)
  1511. goto ErrorCannotCallBeforeOpen;
  1512. else if (_eState >= CHttpRequest::SENDING)
  1513. goto ErrorCannotCallAfterSend;
  1514. // Ignore attempts to set the Content-Length header; the
  1515. // content length is computed and sent automatically.
  1516. if (StrCmpIW(bstrHeader, L"Content-Length") == 0)
  1517. goto Cleanup;
  1518. cchHeaderValue = SysStringLen(bstrHeader) + SysStringLen(bstrValue)
  1519. + 2 /* wcslen(L": ") */
  1520. + 2 /* wcslen(L"\r\n") */;
  1521. wszHeaderValue = New WCHAR [cchHeaderValue + 1];
  1522. if (!wszHeaderValue)
  1523. goto ErrorOutOfMemory;
  1524. wcscpy(wszHeaderValue, bstrHeader);
  1525. wcscat(wszHeaderValue, L": ");
  1526. if (bstrValue)
  1527. wcscat(wszHeaderValue, bstrValue);
  1528. wcscat(wszHeaderValue, L"\r\n");
  1529. // For blank header values, erase the header by setting the
  1530. // REPLACE flag.
  1531. if (SysStringLen(bstrValue) == 0)
  1532. {
  1533. dwModifiers |= HTTP_ADDREQ_FLAG_REPLACE;
  1534. }
  1535. if (! WinHttpAddRequestHeaders(_hHTTP, wszHeaderValue,
  1536. -1L,
  1537. dwModifiers))
  1538. goto ErrorFail;
  1539. hr = NOERROR;
  1540. Cleanup:
  1541. if (wszHeaderValue)
  1542. delete [] wszHeaderValue;
  1543. SetErrorInfo(hr);
  1544. return hr;
  1545. ErrorOutOfMemory:
  1546. hr = E_OUTOFMEMORY;
  1547. goto Error;
  1548. ErrorCannotCallBeforeOpen:
  1549. hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN);
  1550. goto Error;
  1551. ErrorCannotCallAfterSend:
  1552. hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND);
  1553. goto Error;
  1554. ErrorFail:
  1555. hr = HRESULT_FROM_WIN32(GetLastError());
  1556. goto Error;
  1557. Error:
  1558. goto Cleanup;
  1559. }
  1560. /*
  1561. * CHttpRequest::SetRequiredRequestHeaders
  1562. *
  1563. * Purpose:
  1564. * Set implicit request headers
  1565. *
  1566. * Parameters:
  1567. * None
  1568. *
  1569. * Errors:
  1570. * E_FAIL
  1571. * E_UNEXPECTED
  1572. * E_OUTOFMEMORY
  1573. * Errors from WinHttpAddRequestHeaders and WinHttpSendRequest
  1574. */
  1575. HRESULT
  1576. CHttpRequest::SetRequiredRequestHeaders()
  1577. {
  1578. HRESULT hr = NOERROR;
  1579. char szContentLengthHeader[sizeof("Content-Length") +
  1580. sizeof(": ") +
  1581. 15 + // content-length value
  1582. sizeof("\r\n") + 1];
  1583. lstrcpy(szContentLengthHeader, "Content-Length: ");
  1584. _ltoa(_cbRequestBody,
  1585. szContentLengthHeader + 16, /* "Content-Length: " */
  1586. 10);
  1587. lstrcat(szContentLengthHeader, "\r\n");
  1588. if (! HttpAddRequestHeadersA(_hHTTP, szContentLengthHeader,
  1589. -1L,
  1590. HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE))
  1591. {
  1592. hr = E_FAIL;
  1593. }
  1594. // Add an Accept: */* header if no other Accept header
  1595. // has been set. Ignore any return code, since it is
  1596. // not fatal if this fails.
  1597. HttpAddRequestHeadersA(_hHTTP, "Accept: */*",
  1598. -1L,
  1599. HTTP_ADDREQ_FLAG_ADD_IF_NEW);
  1600. return hr;
  1601. }
  1602. DWORD WINAPI WinHttpRequestSendAsync(LPVOID lpParameter)
  1603. {
  1604. #ifdef WINHTTP_FOR_MSXML
  1605. //
  1606. // MSXML needs to initialize its thread local storage data.
  1607. // It does not do this during DLL_THREAD_ATTACH, so our
  1608. // worker thread must explicitly call into MSXML to initialize
  1609. // its TLS for this thread.
  1610. //
  1611. InitializeMsxmlTLS();
  1612. #endif
  1613. HRESULT hr;
  1614. DWORD dwExitCode;
  1615. CHttpRequest * pWinHttpRequest = reinterpret_cast<CHttpRequest *>(lpParameter);
  1616. INET_ASSERT(pWinHttpRequest != NULL);
  1617. dwExitCode = pWinHttpRequest->SendAsync();
  1618. pWinHttpRequest->Release();
  1619. // If this worker thread was impersonating, revert to the default
  1620. // process identity.
  1621. RevertToSelf();
  1622. return dwExitCode;
  1623. }
  1624. /*
  1625. * CHttpRequest::CreateAsyncWorkerThread
  1626. *
  1627. */
  1628. HRESULT
  1629. CHttpRequest::CreateAsyncWorkerThread()
  1630. {
  1631. DWORD dwWorkerThreadId;
  1632. HANDLE hThreadToken = NULL;
  1633. HRESULT hr;
  1634. hr = _CP.CreateEventSinksMarshaller();
  1635. if (FAILED(hr))
  1636. return hr;
  1637. hr = NOERROR;
  1638. //
  1639. // If the current thread is impersonating, then grab its access token
  1640. // and revert the current thread (so it is nolonger impersonating).
  1641. // After creating the worker thread, we will make the main thread
  1642. // impersonate again. Apparently you should not call CreateThread
  1643. // while impersonating.
  1644. //
  1645. if (OpenThreadToken(GetCurrentThread(), (TOKEN_IMPERSONATE | TOKEN_READ),
  1646. FALSE,
  1647. &hThreadToken))
  1648. {
  1649. INET_ASSERT(hThreadToken != 0);
  1650. RevertToSelf();
  1651. }
  1652. // Create the worker thread suspended.
  1653. _hWorkerThread = CreateThread(NULL, 0, WinHttpRequestSendAsync,
  1654. (void *)static_cast<CHttpRequest *>(this),
  1655. CREATE_SUSPENDED,
  1656. &dwWorkerThreadId);
  1657. // If CreateThread fails, grab the error code now.
  1658. if (!_hWorkerThread)
  1659. {
  1660. hr = HRESULT_FROM_WIN32(GetLastError());
  1661. }
  1662. //
  1663. // If the main thread was impersonating, then:
  1664. // (1) have the worker thread impersonate the same user, and
  1665. // (2) have the main thread resume impersonating the
  1666. // client too (since we called RevertToSelf above).
  1667. //
  1668. if (hThreadToken)
  1669. {
  1670. if (_hWorkerThread)
  1671. {
  1672. SetThreadToken(&_hWorkerThread, hThreadToken);
  1673. }
  1674. SetThreadToken(NULL, hThreadToken);
  1675. CloseHandle(hThreadToken);
  1676. }
  1677. // If the worker thread was created, start it running.
  1678. if (_hWorkerThread)
  1679. {
  1680. // The worker thread owns a ref count on the component.
  1681. // Don't call AddRef() as it will attribute the ref count
  1682. // to the main thread.
  1683. _cRefs++;
  1684. ResumeThread(_hWorkerThread);
  1685. }
  1686. else
  1687. {
  1688. _CP.ShutdownEventSinksMarshaller();
  1689. _CP.ReleaseEventSinksMarshaller();
  1690. }
  1691. return hr;
  1692. }
  1693. /*
  1694. * CHttpRequest::Send
  1695. *
  1696. * Purpose:
  1697. * Send the HTTP request
  1698. *
  1699. * Parameters:
  1700. * varBody IN Request body
  1701. *
  1702. * Errors:
  1703. * E_FAIL
  1704. * E_UNEXPECTED
  1705. * E_OUTOFMEMORY
  1706. * Errors from WinHttpAddRequestHeaders and WinHttpSendRequest
  1707. */
  1708. STDMETHODIMP
  1709. CHttpRequest::Send(VARIANT varBody)
  1710. {
  1711. HRESULT hr = NOERROR;
  1712. BOOL fRetCode = FALSE;
  1713. BOOL fRetryWithClientAuth = TRUE;
  1714. // Validate parameter
  1715. if (!IsValidVariant(varBody))
  1716. return E_INVALIDARG;
  1717. // Validate state
  1718. if (_eState < CHttpRequest::OPENED)
  1719. goto ErrorCannotCallBeforeOpen;
  1720. // Get the request body
  1721. hr = SetRequestBody(varBody);
  1722. if (FAILED(hr))
  1723. goto Error;
  1724. hr = SetRequiredRequestHeaders();
  1725. if (FAILED(hr))
  1726. goto Error;
  1727. if (_bDisableRedirects)
  1728. {
  1729. DWORD dwDisable = WINHTTP_DISABLE_REDIRECTS;
  1730. WinHttpSetOption(_hHTTP,
  1731. WINHTTP_OPTION_DISABLE_FEATURE,
  1732. (void *) &dwDisable,
  1733. sizeof(DWORD));
  1734. }
  1735. try_again:
  1736. SetState(CHttpRequest::SENDING);
  1737. if (_fAsync)
  1738. {
  1739. hr = CreateAsyncWorkerThread();
  1740. if (FAILED(hr))
  1741. goto Error;
  1742. }
  1743. else
  1744. {
  1745. // Send the HTTP request
  1746. fRetCode = WinHttpSendRequest(
  1747. _hHTTP,
  1748. NULL, 0, // No header info here
  1749. _szRequestBuffer,
  1750. _cbRequestBody,
  1751. _cbRequestBody,
  1752. 0);
  1753. if (!fRetCode)
  1754. {
  1755. goto ErrorFail;
  1756. }
  1757. fRetCode = WinHttpReceiveResponse(_hHTTP, NULL);
  1758. if (!fRetCode)
  1759. {
  1760. goto ErrorFail;
  1761. }
  1762. SetState(CHttpRequest::SENT);
  1763. // Read the response data
  1764. hr = ReadResponse();
  1765. if (FAILED(hr))
  1766. goto Error;
  1767. }
  1768. hr = NOERROR;
  1769. Cleanup:
  1770. SetErrorInfo(hr);
  1771. return hr;
  1772. ErrorCannotCallBeforeOpen:
  1773. hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN);
  1774. goto Error;
  1775. ErrorFail:
  1776. hr = HRESULT_FROM_WIN32(GetLastError());
  1777. if (!_fAsync &&
  1778. hr == HRESULT_FROM_WIN32(ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED) &&
  1779. fRetryWithClientAuth)
  1780. {
  1781. fRetryWithClientAuth = FALSE;
  1782. // Try to enumerate the first cert in the reverted user context,
  1783. // select the cert (per object sesssion, not global), and send
  1784. // the request again.
  1785. if (SelectCertificate())
  1786. goto try_again;
  1787. }
  1788. goto Cleanup;
  1789. Error:
  1790. goto Cleanup;
  1791. }
  1792. /*
  1793. * CHttpRequest::SendAsync
  1794. *
  1795. * Purpose:
  1796. * Send the HTTP request
  1797. *
  1798. * Parameters:
  1799. * varBody IN Request body
  1800. *
  1801. * Errors:
  1802. * E_FAIL
  1803. * E_UNEXPECTED
  1804. * E_OUTOFMEMORY
  1805. * Errors from WinHttpAddRequestHeaders and WinHttpSendRequest
  1806. */
  1807. DWORD
  1808. CHttpRequest::SendAsync()
  1809. {
  1810. DWORD dwLastError = 0;
  1811. DWORD fRetCode;
  1812. HRESULT hr;
  1813. BOOL fRetryWithClientAuth = TRUE;
  1814. try_again:
  1815. if (_bAborted || !_hHTTP)
  1816. goto ErrorUnexpected;
  1817. // Send the HTTP request
  1818. fRetCode = WinHttpSendRequest(
  1819. _hHTTP,
  1820. NULL, 0, // No header info here
  1821. _szRequestBuffer,
  1822. _cbRequestBody,
  1823. _cbRequestBody,
  1824. 0);
  1825. if (!fRetCode)
  1826. goto ErrorFail;
  1827. fRetCode = WinHttpReceiveResponse(_hHTTP, NULL);
  1828. if (!fRetCode)
  1829. goto ErrorFail;
  1830. if (!_bAborted)
  1831. {
  1832. SetState(CHttpRequest::SENT);
  1833. hr = ReadResponse();
  1834. if (FAILED(hr))
  1835. {
  1836. if (hr == E_OUTOFMEMORY)
  1837. goto ErrorOutOfMemory;
  1838. goto ErrorFail;
  1839. }
  1840. }
  1841. hr = NOERROR;
  1842. Cleanup:
  1843. _hrAsyncResult = hr;
  1844. return dwLastError;
  1845. ErrorUnexpected:
  1846. dwLastError = ERROR_WINHTTP_INTERNAL_ERROR;
  1847. hr = HRESULT_FROM_WIN32(dwLastError);
  1848. goto Cleanup;
  1849. ErrorFail:
  1850. dwLastError = GetLastError();
  1851. if (dwLastError == ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED &&
  1852. fRetryWithClientAuth)
  1853. {
  1854. fRetryWithClientAuth = FALSE;
  1855. // Try to enumerate the first cert in the reverted user context,
  1856. // select the cert (per object sesssion, not global), and send
  1857. // the request again.
  1858. if (SelectCertificate())
  1859. {
  1860. SetState(CHttpRequest::SENDING);
  1861. goto try_again;
  1862. }
  1863. }
  1864. hr = HRESULT_FROM_WIN32(dwLastError);
  1865. goto Cleanup;
  1866. ErrorOutOfMemory:
  1867. dwLastError = ERROR_NOT_ENOUGH_MEMORY;
  1868. hr = E_OUTOFMEMORY;
  1869. goto Cleanup;
  1870. }
  1871. STDMETHODIMP
  1872. CHttpRequest::WaitForResponse(VARIANT varTimeout, VARIANT_BOOL * pboolSucceeded)
  1873. {
  1874. HRESULT hr = NOERROR;
  1875. bool bSucceeded= true;
  1876. DWORD dwTimeout;
  1877. // Validate parameters; null pboolSucceeded pointer is Ok.
  1878. if (!IsValidVariant(varTimeout) ||
  1879. (pboolSucceeded &&
  1880. IsBadWritePtr(pboolSucceeded, sizeof(VARIANT_BOOL))))
  1881. return E_INVALIDARG;
  1882. // Get the timeout value. Disallow numbers
  1883. // less than -1 (which means INFINITE).
  1884. if (GetLongFromVariant(varTimeout, INFINITE) < -1L)
  1885. return E_INVALIDARG;
  1886. dwTimeout = GetDwordFromVariant(varTimeout, INFINITE);
  1887. // Validate state.
  1888. if (_eState < CHttpRequest::SENDING)
  1889. goto ErrorCannotCallBeforeSend;
  1890. //
  1891. // WaitForResponse is a no-op if we're not in async mode.
  1892. //
  1893. if (_fAsync && _hWorkerThread)
  1894. {
  1895. //
  1896. // Has the worker thread has already finished?
  1897. //
  1898. if (WaitForSingleObject(_hWorkerThread, 0) == WAIT_TIMEOUT)
  1899. {
  1900. //
  1901. // Convert Timeout from seconds to milliseconds. Any timeout
  1902. // value over 4 million seconds (~46 days) is "rounded up"
  1903. // to INFINITE. :)
  1904. //
  1905. if (dwTimeout > 4000000) // avoid overflow
  1906. {
  1907. dwTimeout = INFINITE;
  1908. }
  1909. else
  1910. {
  1911. // convert to milliseconds
  1912. dwTimeout *= 1000;
  1913. }
  1914. DWORD dwStartTime;
  1915. DWORD dwWaitResult;
  1916. bool bWaitAgain;
  1917. do
  1918. {
  1919. dwStartTime = GetTickCount();
  1920. dwWaitResult = MsgWaitForMultipleObjects(1, &_hWorkerThread,
  1921. FALSE,
  1922. dwTimeout,
  1923. QS_ALLINPUT);
  1924. bWaitAgain = false;
  1925. switch (dwWaitResult)
  1926. {
  1927. case WAIT_OBJECT_0:
  1928. // Thread exited.
  1929. MessageLoop();
  1930. hr = _hrAsyncResult;
  1931. bSucceeded = SUCCEEDED(hr);
  1932. break;
  1933. case WAIT_OBJECT_0 + 1:
  1934. // Message waiting in the message queue.
  1935. // Run message pump to clear queue.
  1936. MessageLoop();
  1937. bWaitAgain = true;
  1938. break;
  1939. case WAIT_TIMEOUT:
  1940. // Timeout.
  1941. bSucceeded = false;
  1942. break;
  1943. case (-1):
  1944. default:
  1945. // Error.
  1946. goto ErrorFail;
  1947. break;
  1948. }
  1949. // If we're going to continue waiting for the worker
  1950. // thread, decrease timeout appropriately.
  1951. if (bWaitAgain)
  1952. {
  1953. dwTimeout = UpdateTimeout(dwTimeout, dwStartTime);
  1954. }
  1955. } while (bWaitAgain);
  1956. }
  1957. else
  1958. {
  1959. // If the worker thread is already done, then pump messages
  1960. // to clear any events that it may have posted.
  1961. MessageLoop();
  1962. hr = _hrAsyncResult;
  1963. bSucceeded = SUCCEEDED(hr);
  1964. }
  1965. }
  1966. Cleanup:
  1967. if (pboolSucceeded)
  1968. {
  1969. *pboolSucceeded = (bSucceeded ? VARIANT_TRUE : VARIANT_FALSE);
  1970. }
  1971. SetErrorInfo(hr);
  1972. return hr;
  1973. ErrorFail:
  1974. hr = HRESULT_FROM_WIN32(GetLastError());
  1975. goto Error;
  1976. ErrorCannotCallBeforeSend:
  1977. hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND);
  1978. goto Error;
  1979. Error:
  1980. bSucceeded = false;
  1981. goto Cleanup;
  1982. }
  1983. STDMETHODIMP
  1984. CHttpRequest::Abort()
  1985. {
  1986. //
  1987. // Abort if not already aborted and not in the CREATED state,
  1988. // (meaning at least the Open method has been called).
  1989. //
  1990. if ((_eState > CHttpRequest::CREATED) && !_bAborted)
  1991. {
  1992. DWORD error;
  1993. _bAborted = true;
  1994. // Tell our connection point manager to abort any
  1995. // events "in flight"--i.e., abort any events that
  1996. // may have already been posted by the worker thread.
  1997. _CP.FreezeEvents();
  1998. if (_hHTTP)
  1999. {
  2000. //
  2001. // Add a ref count on the HTTP Request handle.
  2002. //
  2003. INET_ASSERT(_hAbortedRequestObject == NULL);
  2004. error = MapHandleToAddress(_hHTTP, (LPVOID *)&_hAbortedRequestObject, FALSE);
  2005. INET_ASSERT(error == 0);
  2006. WinHttpCloseHandle(_hHTTP);
  2007. }
  2008. if (_hConnection)
  2009. {
  2010. //
  2011. // Add a ref count on the Connection handle.
  2012. //
  2013. INET_ASSERT(_hAbortedConnectObject == NULL);
  2014. error = MapHandleToAddress(_hConnection, (LPVOID *)&_hAbortedConnectObject, FALSE);
  2015. INET_ASSERT(error == 0);
  2016. WinHttpCloseHandle(_hConnection);
  2017. }
  2018. // Recycle the object.
  2019. Recycle();
  2020. }
  2021. return NOERROR;
  2022. }
  2023. STDMETHODIMP
  2024. CHttpRequest::SetTimeouts(long ResolveTimeout, long ConnectTimeout, long SendTimeout, long ReceiveTimeout)
  2025. {
  2026. if ((ResolveTimeout < -1L) || (ConnectTimeout < -1L) ||
  2027. (SendTimeout < -1L) || (ReceiveTimeout < -1L))
  2028. {
  2029. return E_INVALIDARG;
  2030. }
  2031. HRESULT hr = NOERROR;
  2032. _ResolveTimeout = (DWORD) ResolveTimeout;
  2033. _ConnectTimeout = (DWORD) ConnectTimeout;
  2034. _SendTimeout = (DWORD) SendTimeout;
  2035. _ReceiveTimeout = (DWORD) ReceiveTimeout;
  2036. _bSetTimeouts = true;
  2037. if (_hHTTP)
  2038. {
  2039. DWORD fRetCode;
  2040. fRetCode = WinHttpSetTimeouts(_hHTTP, (int)_ResolveTimeout,
  2041. (int)_ConnectTimeout,
  2042. (int)_SendTimeout,
  2043. (int)_ReceiveTimeout);
  2044. if (!fRetCode)
  2045. hr = E_INVALIDARG;
  2046. }
  2047. return hr;
  2048. }
  2049. /*
  2050. * CHttpRequest::SetRequestBody
  2051. *
  2052. * Purpose:
  2053. * Set the request body
  2054. *
  2055. * Parameters:
  2056. * varBody IN Request body
  2057. *
  2058. * Errors:
  2059. * E_FAIL
  2060. * E_OUTOFMEMORY
  2061. * E_UNEXPECTED
  2062. */
  2063. HRESULT
  2064. CHttpRequest::SetRequestBody(VARIANT varBody)
  2065. {
  2066. HRESULT hr = NOERROR;
  2067. VARIANT varTemp;
  2068. BSTR bstrBody = NULL;
  2069. SAFEARRAY * psa = NULL;
  2070. VARIANT * pvarBody = NULL;
  2071. IStream * pStm = NULL;
  2072. // varBody is validated by CHttpRequest::Send().
  2073. VariantInit(&varTemp);
  2074. // Free a previously set body and its response
  2075. if (_szRequestBuffer)
  2076. {
  2077. delete [] _szRequestBuffer;
  2078. _szRequestBuffer = NULL;
  2079. _cbRequestBody = 0;
  2080. }
  2081. if (_szResponseBuffer)
  2082. {
  2083. delete [] _szResponseBuffer;
  2084. _szResponseBuffer = NULL;
  2085. _cbResponseBuffer = 0;
  2086. _cbResponseBody = 0;
  2087. }
  2088. if (V_ISBYREF(&varBody))
  2089. {
  2090. pvarBody = varBody.pvarVal;
  2091. }
  2092. else
  2093. {
  2094. pvarBody = &varBody;
  2095. }
  2096. // Check for an empty body
  2097. if (V_VT(pvarBody) == VT_EMPTY ||
  2098. V_VT(pvarBody) == VT_NULL ||
  2099. V_VT(pvarBody) == VT_ERROR)
  2100. goto Cleanup;
  2101. //
  2102. // Extract the body: BSTR or array of UI1
  2103. //
  2104. // We need to explicitly look for the byte array since it will be converted
  2105. // to a BSTR by VariantChangeType.
  2106. if (V_ISARRAY(pvarBody) && (V_VT(pvarBody) & VT_UI1))
  2107. {
  2108. BYTE * pb = NULL;
  2109. long lUBound = 0;
  2110. long lLBound = 0;
  2111. psa = V_ARRAY(pvarBody);
  2112. // We only handle 1 dimensional arrays
  2113. if (SafeArrayGetDim(psa) != 1)
  2114. goto ErrorFail;
  2115. // Get access to the blob
  2116. hr = SafeArrayAccessData(psa, (void **)&pb);
  2117. if (FAILED(hr))
  2118. goto Error;
  2119. // Compute the data size from the upper and lower array bounds
  2120. hr = SafeArrayGetLBound(psa, 1, &lLBound);
  2121. if (FAILED(hr))
  2122. goto Error;
  2123. hr = SafeArrayGetUBound(psa, 1, &lUBound);
  2124. if (FAILED(hr))
  2125. goto Error;
  2126. _cbRequestBody = lUBound - lLBound + 1;
  2127. if (_cbRequestBody > 0)
  2128. {
  2129. // Copy the data into the request buffer
  2130. _szRequestBuffer = New char [_cbRequestBody];
  2131. if (!_szRequestBuffer)
  2132. {
  2133. _cbRequestBody = 0;
  2134. goto ErrorOutOfMemory;
  2135. }
  2136. ::memcpy(_szRequestBuffer, pb, _cbRequestBody);
  2137. }
  2138. SafeArrayUnaccessData(psa);
  2139. psa = NULL;
  2140. }
  2141. else
  2142. {
  2143. // Try a BSTR
  2144. bstrBody = GetBSTRFromVariant(*pvarBody);
  2145. if (bstrBody)
  2146. {
  2147. hr = BSTRToUTF8(&_szRequestBuffer, &_cbRequestBody, bstrBody);
  2148. if (FAILED(hr))
  2149. goto Error;
  2150. }
  2151. else
  2152. {
  2153. // Try a Stream
  2154. if (V_VT(pvarBody) == VT_UNKNOWN || V_VT(pvarBody) == VT_DISPATCH)
  2155. {
  2156. IStream * pStm;
  2157. hr = pvarBody->punkVal->QueryInterface(
  2158. IID_IStream,
  2159. (void **)&pStm);
  2160. if (FAILED(hr))
  2161. goto Error;
  2162. hr = ReadFromStream(
  2163. &_szRequestBuffer,
  2164. &_cbRequestBody,
  2165. pStm);
  2166. pStm->Release();
  2167. if (FAILED(hr))
  2168. goto Error;
  2169. }
  2170. }
  2171. }
  2172. hr = NOERROR;
  2173. Cleanup:
  2174. VariantClear(&varTemp);
  2175. if (psa)
  2176. SafeArrayUnaccessData(psa);
  2177. if (bstrBody)
  2178. SysFreeString(bstrBody);
  2179. return hr;
  2180. ErrorOutOfMemory:
  2181. hr = E_OUTOFMEMORY;
  2182. goto Error;
  2183. ErrorFail:
  2184. hr = HRESULT_FROM_WIN32(GetLastError());
  2185. goto Error;
  2186. Error:
  2187. goto Cleanup;
  2188. }
  2189. /*
  2190. * CHttpRequest::GetResponseHeader
  2191. *
  2192. * Purpose:
  2193. * Get a response header
  2194. *
  2195. * Parameters:
  2196. * bstrHeader IN HTTP request header
  2197. * pbstrValue OUT Header value
  2198. *
  2199. * Errors:
  2200. * E_FAIL
  2201. * E_INVALIDARG
  2202. * E_OUTOFMEMORY
  2203. * E_UNEXPECTED
  2204. * Errors from WinHttpQueryHeaders
  2205. */
  2206. STDMETHODIMP
  2207. CHttpRequest::GetResponseHeader(BSTR bstrHeader, BSTR * pbstrValue)
  2208. {
  2209. return _GetResponseHeader(bstrHeader, pbstrValue);
  2210. }
  2211. HRESULT
  2212. CHttpRequest::_GetResponseHeader(OLECHAR * wszHeader, BSTR * pbstrValue)
  2213. {
  2214. HRESULT hr = NOERROR;
  2215. WCHAR * wszHeaderValue = NULL;
  2216. DWORD cb;
  2217. BOOL fRetCode;
  2218. // Validate parameters
  2219. if (IsBadReadPtr(wszHeader, 2) ||
  2220. IsBadWritePtr(pbstrValue, sizeof(BSTR)) ||
  2221. !lstrlenW(wszHeader))
  2222. return E_INVALIDARG;
  2223. // Validate state
  2224. if (_eState < CHttpRequest::SENDING)
  2225. goto ErrorCannotCallBeforeSend;
  2226. *pbstrValue = NULL;
  2227. cb = 64; // arbitrary size in which many header values will fit
  2228. wszHeaderValue = New WCHAR[cb];
  2229. if (!wszHeaderValue)
  2230. goto ErrorOutOfMemory;
  2231. RetryQuery:
  2232. // Determine length of requested header
  2233. fRetCode = WinHttpQueryHeaders(
  2234. _hHTTP,
  2235. HTTP_QUERY_CUSTOM,
  2236. wszHeader,
  2237. wszHeaderValue,
  2238. &cb,
  2239. 0);
  2240. // Check for ERROR_INSUFFICIENT_BUFFER - reallocate the buffer and retry
  2241. if (!fRetCode)
  2242. {
  2243. switch (GetLastError())
  2244. {
  2245. case ERROR_INSUFFICIENT_BUFFER:
  2246. {
  2247. delete [] wszHeaderValue;
  2248. wszHeaderValue = New WCHAR[cb]; // should this be cb/2?
  2249. if (!wszHeaderValue)
  2250. goto ErrorOutOfMemory;
  2251. goto RetryQuery;
  2252. }
  2253. case ERROR_HTTP_HEADER_NOT_FOUND:
  2254. goto ErrorFail;
  2255. default:
  2256. goto ErrorFail;
  2257. }
  2258. }
  2259. *pbstrValue = SysAllocString(wszHeaderValue);
  2260. if (!*pbstrValue)
  2261. goto ErrorOutOfMemory;
  2262. hr = NOERROR;
  2263. Cleanup:
  2264. if (wszHeaderValue)
  2265. delete [] wszHeaderValue;
  2266. SetErrorInfo(hr);
  2267. return hr;
  2268. ErrorOutOfMemory:
  2269. hr = E_OUTOFMEMORY;
  2270. goto Error;
  2271. ErrorFail:
  2272. hr = HRESULT_FROM_WIN32(GetLastError());
  2273. goto Error;
  2274. ErrorCannotCallBeforeSend:
  2275. hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND);
  2276. goto Error;
  2277. Error:
  2278. goto Cleanup;
  2279. }
  2280. /*
  2281. * CHttpRequest::GetAllResponseHeaders
  2282. *
  2283. * Purpose:
  2284. * Return the response headers
  2285. *
  2286. * Parameters:
  2287. * pbstrHeaders IN/OUT CRLF delimited headers
  2288. *
  2289. * Errors:
  2290. * E_FAIL
  2291. * E_INVALIDARG
  2292. * E_OUTOFMEMORY
  2293. * E_UNEXPECTED
  2294. */
  2295. STDMETHODIMP
  2296. CHttpRequest::GetAllResponseHeaders(BSTR * pbstrHeaders)
  2297. {
  2298. HRESULT hr = NOERROR;
  2299. BOOL fRetCode;
  2300. WCHAR * wszAllHeaders = NULL;
  2301. WCHAR * wszFirstHeader = NULL;
  2302. DWORD cb = 0;
  2303. // Validate parameter
  2304. if (IsBadWritePtr(pbstrHeaders, sizeof(BSTR)))
  2305. return E_INVALIDARG;
  2306. // Validate state
  2307. if (_eState < CHttpRequest::SENDING)
  2308. goto ErrorCannotCallBeforeSend;
  2309. *pbstrHeaders = NULL;
  2310. RetryQuery:
  2311. // Determine the length of all headers and then get all the headers
  2312. fRetCode = WinHttpQueryHeaders(
  2313. _hHTTP,
  2314. HTTP_QUERY_RAW_HEADERS_CRLF,
  2315. WINHTTP_HEADER_NAME_BY_INDEX,
  2316. wszAllHeaders,
  2317. &cb,
  2318. 0);
  2319. if (!fRetCode)
  2320. {
  2321. // Allocate a buffer large enough to hold all headers
  2322. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  2323. {
  2324. wszAllHeaders = New WCHAR[cb];
  2325. if (!wszAllHeaders)
  2326. goto ErrorOutOfMemory;
  2327. goto RetryQuery;
  2328. }
  2329. else
  2330. {
  2331. goto ErrorFail;
  2332. }
  2333. }
  2334. // Bypass status line - jump past first CRLF (0x13, 0x10)
  2335. wszFirstHeader = wcschr(wszAllHeaders, '\n');
  2336. if (*wszFirstHeader == '\n')
  2337. {
  2338. *pbstrHeaders = SysAllocString(wszFirstHeader + 1);
  2339. if (!*pbstrHeaders)
  2340. goto ErrorOutOfMemory;
  2341. }
  2342. hr = NOERROR;
  2343. Cleanup:
  2344. if (wszAllHeaders)
  2345. delete [] wszAllHeaders;
  2346. SetErrorInfo(hr);
  2347. return hr;
  2348. ErrorOutOfMemory:
  2349. hr = E_OUTOFMEMORY;
  2350. goto Error;
  2351. ErrorFail:
  2352. hr = HRESULT_FROM_WIN32(GetLastError());
  2353. goto Error;
  2354. ErrorCannotCallBeforeSend:
  2355. hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND);
  2356. goto Error;
  2357. Error:
  2358. if (pbstrHeaders)
  2359. {
  2360. SysFreeString(*pbstrHeaders);
  2361. *pbstrHeaders = NULL;
  2362. }
  2363. goto Cleanup;
  2364. }
  2365. /*
  2366. * CHttpRequest::get_status
  2367. *
  2368. * Purpose:
  2369. * Get the request status code
  2370. *
  2371. * Parameters:
  2372. * plStatus OUT HTTP request status code
  2373. *
  2374. * Errors:
  2375. * E_FAIL
  2376. * E_INVALIDARG
  2377. * E_UNEXPECTED
  2378. */
  2379. STDMETHODIMP
  2380. CHttpRequest::get_Status(long * plStatus)
  2381. {
  2382. HRESULT hr = NOERROR;
  2383. DWORD cb = sizeof(DWORD);
  2384. BOOL fRetCode;
  2385. DWORD dwStatus;
  2386. // Validate parameter
  2387. if (IsBadWritePtr(plStatus, sizeof(long)))
  2388. return E_INVALIDARG;
  2389. // Validate state
  2390. if (_eState < CHttpRequest::SENDING)
  2391. goto ErrorCannotCallBeforeSend;
  2392. fRetCode = HttpQueryInfoA(
  2393. _hHTTP,
  2394. HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
  2395. WINHTTP_HEADER_NAME_BY_INDEX,
  2396. &dwStatus,
  2397. &cb,
  2398. 0);
  2399. if (!fRetCode)
  2400. goto ErrorFail;
  2401. *plStatus = dwStatus;
  2402. hr = NOERROR;
  2403. Cleanup:
  2404. SetErrorInfo(hr);
  2405. return hr;
  2406. ErrorCannotCallBeforeSend:
  2407. hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND);
  2408. goto Error;
  2409. ErrorFail:
  2410. hr = HRESULT_FROM_WIN32(GetLastError());
  2411. goto Error;
  2412. Error:
  2413. goto Cleanup;
  2414. }
  2415. /*
  2416. * CHttpRequest::get_StatusText
  2417. *
  2418. * Purpose:
  2419. * Get the request status text
  2420. *
  2421. * Parameters:
  2422. * pbstrStatus OUT HTTP request status text
  2423. *
  2424. * Errors:
  2425. * E_FAIL
  2426. * E_INVALIDARG
  2427. * E_UNEXPECTED
  2428. */
  2429. STDMETHODIMP
  2430. CHttpRequest::get_StatusText(BSTR * pbstrStatus)
  2431. {
  2432. HRESULT hr = NOERROR;
  2433. WCHAR wszStatusText[32];
  2434. WCHAR * pwszStatusText = wszStatusText;
  2435. BOOL fFreeStatusString = FALSE;
  2436. DWORD cb;
  2437. BOOL fRetCode;
  2438. // Validate parameter
  2439. if (IsBadWritePtr(pbstrStatus, sizeof(BSTR)))
  2440. return E_INVALIDARG;
  2441. // Validate state
  2442. if (_eState < CHttpRequest::SENDING)
  2443. goto ErrorCannotCallBeforeSend;
  2444. *pbstrStatus = NULL;
  2445. cb = sizeof(wszStatusText) / sizeof(WCHAR);
  2446. RetryQuery:
  2447. fRetCode = WinHttpQueryHeaders(
  2448. _hHTTP,
  2449. HTTP_QUERY_STATUS_TEXT,
  2450. WINHTTP_HEADER_NAME_BY_INDEX,
  2451. pwszStatusText,
  2452. &cb,
  2453. 0);
  2454. if (!fRetCode)
  2455. {
  2456. // Check for ERROR_INSUFFICIENT_BUFFER error
  2457. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  2458. {
  2459. // Reallocate the status text buffer
  2460. if (fFreeStatusString)
  2461. delete pwszStatusText;
  2462. pwszStatusText = New WCHAR[cb];
  2463. if (!pwszStatusText)
  2464. goto ErrorOutOfMemory;
  2465. fFreeStatusString = TRUE;
  2466. goto RetryQuery;
  2467. }
  2468. else
  2469. {
  2470. goto ErrorFail;
  2471. }
  2472. }
  2473. // Convert the status text to a BSTR
  2474. *pbstrStatus = SysAllocString(pwszStatusText);
  2475. if (!*pbstrStatus)
  2476. goto ErrorOutOfMemory;
  2477. hr = NOERROR;
  2478. Cleanup:
  2479. if (fFreeStatusString)
  2480. delete [] pwszStatusText;
  2481. SetErrorInfo(hr);
  2482. return hr;
  2483. ErrorOutOfMemory:
  2484. hr = E_OUTOFMEMORY;
  2485. goto Error;
  2486. ErrorCannotCallBeforeSend:
  2487. hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND);
  2488. goto Error;
  2489. ErrorFail:
  2490. hr = HRESULT_FROM_WIN32(GetLastError());
  2491. goto Cleanup;
  2492. Error:
  2493. goto Cleanup;
  2494. }
  2495. /*
  2496. * CHttpRequest::get_ResponseBody
  2497. *
  2498. * Purpose:
  2499. * Retrieve the response body as a SAFEARRAY of UI1
  2500. *
  2501. * Parameters:
  2502. * pvarBody OUT Response blob
  2503. *
  2504. * Errors:
  2505. * E_INVALIDARG
  2506. * E_UNEXPECTED
  2507. * E_PENDING
  2508. */
  2509. STDMETHODIMP
  2510. CHttpRequest::get_ResponseBody(VARIANT * pvarBody)
  2511. {
  2512. HRESULT hr = NOERROR;
  2513. // Validate parameter
  2514. if (IsBadWritePtr(pvarBody, sizeof(VARIANT)))
  2515. return E_INVALIDARG;
  2516. // Validate state
  2517. if (_eState < CHttpRequest::SENDING)
  2518. goto ErrorCannotCallBeforeSend;
  2519. else if (_eState < CHttpRequest::RESPONSE)
  2520. goto ErrorPending;
  2521. VariantInit(pvarBody);
  2522. if (_szResponseBuffer)
  2523. {
  2524. hr = CreateVector(
  2525. pvarBody,
  2526. (const BYTE *)_szResponseBuffer,
  2527. _cbResponseBody);
  2528. if (FAILED(hr))
  2529. goto Error;
  2530. }
  2531. else
  2532. {
  2533. V_VT(pvarBody) = VT_EMPTY;
  2534. }
  2535. hr = NOERROR;
  2536. Cleanup:
  2537. SetErrorInfo(hr);
  2538. return hr;
  2539. ErrorCannotCallBeforeSend:
  2540. hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND);
  2541. goto Error;
  2542. ErrorPending:
  2543. hr = E_PENDING;
  2544. goto Error;
  2545. Error:
  2546. if (pvarBody)
  2547. VariantClear(pvarBody);
  2548. goto Cleanup;
  2549. }
  2550. /*
  2551. * CHttpRequest::get_ResponseText
  2552. *
  2553. * Purpose:
  2554. * Retrieve the response body as a BSTR
  2555. *
  2556. * Parameters:
  2557. * pbstrBody OUT response as a BSTR
  2558. *
  2559. * Errors:
  2560. * E_INVALIDARG
  2561. * E_OUTOFMEMORY
  2562. * E_UNEXPECTED
  2563. * E_PENDING
  2564. */
  2565. STDMETHODIMP
  2566. CHttpRequest::get_ResponseText(BSTR * pbstrBody)
  2567. {
  2568. HRESULT hr;
  2569. // Validate parameter
  2570. if (IsBadWritePtr(pbstrBody, sizeof(BSTR)))
  2571. return E_INVALIDARG;
  2572. // Validate state
  2573. if (_eState < CHttpRequest::SENDING)
  2574. goto ErrorCannotCallBeforeSend;
  2575. else if (_eState < CHttpRequest::RESPONSE)
  2576. goto ErrorPending;
  2577. hr = AsciiToBSTR(pbstrBody, _szResponseBuffer, (int)_cbResponseBody);
  2578. if (FAILED(hr))
  2579. goto Error;
  2580. hr = NOERROR;
  2581. Cleanup:
  2582. SetErrorInfo(hr);
  2583. return hr;
  2584. ErrorCannotCallBeforeSend:
  2585. hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND);
  2586. goto Error;
  2587. ErrorPending:
  2588. hr = E_PENDING;
  2589. goto Error;
  2590. Error:
  2591. goto Cleanup;
  2592. }
  2593. STDMETHODIMP
  2594. CHttpRequest::get_ResponseStream(VARIANT * pvarBody)
  2595. {
  2596. HRESULT hr = NOERROR;
  2597. IStream * pStm = NULL;
  2598. // Validate parameter
  2599. if (IsBadWritePtr(pvarBody, sizeof(VARIANT)))
  2600. return E_INVALIDARG;
  2601. // Validate state
  2602. if (_eState < CHttpRequest::SENDING)
  2603. goto ErrorCannotCallBeforeSend;
  2604. else if (_eState < CHttpRequest::RESPONSE)
  2605. goto ErrorPending;
  2606. VariantInit(pvarBody);
  2607. hr = CreateStreamOnResponse(&pStm);
  2608. if (FAILED(hr))
  2609. goto Error;
  2610. V_VT(pvarBody) = VT_UNKNOWN;
  2611. pvarBody->punkVal = pStm;
  2612. hr = NOERROR;
  2613. Cleanup:
  2614. SetErrorInfo(hr);
  2615. return hr;
  2616. ErrorCannotCallBeforeSend:
  2617. hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND);
  2618. goto Error;
  2619. ErrorPending:
  2620. hr = E_PENDING;
  2621. goto Error;
  2622. Error:
  2623. if (pvarBody)
  2624. VariantClear(pvarBody);
  2625. goto Cleanup;
  2626. }
  2627. void
  2628. CHttpRequest::SetState(State state)
  2629. {
  2630. if (_fAsync)
  2631. {
  2632. InterlockedExchange((long *)&_eState, state);
  2633. }
  2634. else
  2635. {
  2636. _eState = state;
  2637. }
  2638. }
  2639. /*
  2640. * CHttpRequest::CreateStreamOnResponse
  2641. *
  2642. * Purpose:
  2643. * Create a Stream containing the Response data
  2644. *
  2645. * Parameters:
  2646. * ppStm IN/OUT Stream
  2647. *
  2648. * Errors:
  2649. * E_INVALIDARG
  2650. * E_OUTOFMEMORY
  2651. */
  2652. HRESULT
  2653. CHttpRequest::CreateStreamOnResponse(IStream ** ppStm)
  2654. {
  2655. HRESULT hr = NOERROR;
  2656. ULONG cbWritten;
  2657. LARGE_INTEGER liReset = { 0 };
  2658. if (!ppStm)
  2659. return E_INVALIDARG;
  2660. *ppStm = NULL;
  2661. hr = CreateStreamOnHGlobal(NULL, TRUE, ppStm);
  2662. if (FAILED(hr))
  2663. goto ErrorOutOfMemory;
  2664. hr = (*ppStm)->Write(_szResponseBuffer, _cbResponseBody, &cbWritten);
  2665. if (FAILED(hr))
  2666. goto Error;
  2667. hr = (*ppStm)->Seek(liReset, STREAM_SEEK_SET, NULL);
  2668. if (FAILED(hr))
  2669. goto Error;
  2670. hr = NOERROR;
  2671. Cleanup:
  2672. return hr;
  2673. ErrorOutOfMemory:
  2674. hr = E_OUTOFMEMORY;
  2675. goto Error;
  2676. Error:
  2677. if (ppStm)
  2678. SafeRelease(*ppStm);
  2679. goto Cleanup;
  2680. }
  2681. STDMETHODIMP
  2682. CHttpRequest::get_Option(WinHttpRequestOption Option, VARIANT * Value)
  2683. {
  2684. HRESULT hr;
  2685. if (IsBadWritePtr(Value, sizeof(VARIANT)))
  2686. return E_INVALIDARG;
  2687. switch (Option)
  2688. {
  2689. case WinHttpRequestOption_UserAgentString:
  2690. {
  2691. V_BSTR(Value) = SysAllocString(GetUserAgentString());
  2692. if (V_BSTR(Value) == NULL)
  2693. goto ErrorOutOfMemory;
  2694. V_VT(Value) = VT_BSTR;
  2695. break;
  2696. }
  2697. case WinHttpRequestOption_URL:
  2698. {
  2699. WCHAR * pwszUrl = NULL;
  2700. DWORD dwBufferSize = 0;
  2701. if (_eState < CHttpRequest::OPENED)
  2702. goto ErrorCannotCallBeforeOpen;
  2703. if (!WinHttpQueryOption(_hHTTP, WINHTTP_OPTION_URL, NULL, &dwBufferSize) &&
  2704. (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
  2705. {
  2706. pwszUrl = New WCHAR[dwBufferSize + 1];
  2707. if (!pwszUrl)
  2708. goto ErrorOutOfMemory;
  2709. if (WinHttpQueryOption(_hHTTP, WINHTTP_OPTION_URL, pwszUrl, &dwBufferSize))
  2710. {
  2711. V_BSTR(Value) = SysAllocString(pwszUrl);
  2712. V_VT(Value) = VT_BSTR;
  2713. hr = NOERROR;
  2714. }
  2715. else
  2716. {
  2717. hr = E_FAIL;
  2718. }
  2719. delete [] pwszUrl;
  2720. if (FAILED(hr))
  2721. {
  2722. goto ErrorFail;
  2723. }
  2724. else if (V_BSTR(Value) == NULL)
  2725. {
  2726. goto ErrorOutOfMemory;
  2727. }
  2728. }
  2729. break;
  2730. }
  2731. case WinHttpRequestOption_URLCodePage:
  2732. V_I4(Value) = (long) _dwCodePage;
  2733. V_VT(Value) = VT_I4;
  2734. break;
  2735. case WinHttpRequestOption_EscapePercentInURL:
  2736. V_BOOL(Value) = (_dwEscapePercentFlag==WINHTTP_FLAG_ESCAPE_PERCENT) ? VARIANT_TRUE : VARIANT_FALSE;
  2737. V_VT(Value) = VT_BOOL;
  2738. break;
  2739. case WinHttpRequestOption_EnableRedirects:
  2740. V_BOOL(Value) = _bDisableRedirects ? VARIANT_FALSE : VARIANT_TRUE;
  2741. V_VT(Value) = VT_BOOL;
  2742. break;
  2743. default:
  2744. VariantInit(Value);
  2745. return E_INVALIDARG;
  2746. }
  2747. hr = NOERROR;
  2748. Cleanup:
  2749. SetErrorInfo(hr);
  2750. return hr;
  2751. ErrorCannotCallBeforeOpen:
  2752. hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN);
  2753. goto Error;
  2754. ErrorOutOfMemory:
  2755. hr = E_OUTOFMEMORY;
  2756. goto Error;
  2757. ErrorFail:
  2758. hr = HRESULT_FROM_WIN32(GetLastError());
  2759. goto Error;
  2760. Error:
  2761. goto Cleanup;
  2762. }
  2763. STDMETHODIMP
  2764. CHttpRequest::put_Option(WinHttpRequestOption Option, VARIANT Value)
  2765. {
  2766. HRESULT hr;
  2767. if (!IsValidVariant(Value))
  2768. return E_INVALIDARG;
  2769. switch (Option)
  2770. {
  2771. case WinHttpRequestOption_UserAgentString:
  2772. {
  2773. BSTR bstrUserAgent = GetBSTRFromVariant(Value);
  2774. if (!bstrUserAgent || (*bstrUserAgent == L'\0'))
  2775. return E_INVALIDARG;
  2776. if (_hInet)
  2777. {
  2778. if (!WinHttpSetOption(_hInet, WINHTTP_OPTION_USER_AGENT,
  2779. bstrUserAgent,
  2780. SysStringLen(bstrUserAgent)))
  2781. {
  2782. goto ErrorFail;
  2783. }
  2784. }
  2785. if (_hHTTP)
  2786. {
  2787. if (!WinHttpSetOption(_hHTTP, WINHTTP_OPTION_USER_AGENT,
  2788. bstrUserAgent,
  2789. SysStringLen(bstrUserAgent)))
  2790. {
  2791. goto ErrorFail;
  2792. }
  2793. }
  2794. if (_bstrUserAgent)
  2795. SysFreeString(_bstrUserAgent);
  2796. _bstrUserAgent = bstrUserAgent;
  2797. break;
  2798. }
  2799. case WinHttpRequestOption_URL:
  2800. // The URL cannot be set using the Option property.
  2801. return E_INVALIDARG;
  2802. case WinHttpRequestOption_URLCodePage:
  2803. {
  2804. _dwCodePage = GetDwordFromVariant(Value, CP_UTF8);
  2805. if (_hInet)
  2806. {
  2807. if (!WinHttpSetOption(_hInet, WINHTTP_OPTION_CODEPAGE,
  2808. &_dwCodePage,
  2809. sizeof(_dwCodePage)))
  2810. goto ErrorFail;
  2811. }
  2812. if (_hConnection)
  2813. {
  2814. if (!WinHttpSetOption(_hConnection, WINHTTP_OPTION_CODEPAGE,
  2815. &_dwCodePage,
  2816. sizeof(_dwCodePage)))
  2817. goto ErrorFail;
  2818. }
  2819. break;
  2820. }
  2821. case WinHttpRequestOption_EscapePercentInURL:
  2822. {
  2823. BOOL fEscapePercent = GetBoolFromVariant(Value, FALSE);
  2824. _dwEscapePercentFlag = (fEscapePercent ? WINHTTP_FLAG_ESCAPE_PERCENT : 0);
  2825. break;
  2826. }
  2827. case WinHttpRequestOption_SslErrorIgnoreFlags:
  2828. {
  2829. DWORD dwSslIgnoreFlags = GetDwordFromVariant(Value, _dwSslIgnoreFlags);
  2830. if (dwSslIgnoreFlags & ~SECURITY_INTERNET_MASK)
  2831. {
  2832. return E_INVALIDARG;
  2833. }
  2834. // Break automatically, so we don't need the overhead of a callback.
  2835. dwSslIgnoreFlags |= SECURITY_FLAG_BREAK_ON_STATUS_SECURE_FAILURE;
  2836. if (_hHTTP)
  2837. {
  2838. // Set the SSL ignore flags through an undocumented front door
  2839. if (!WinHttpSetOption(_hHTTP,
  2840. WINHTTP_OPTION_SECURITY_FLAGS,
  2841. (LPVOID)&dwSslIgnoreFlags,
  2842. sizeof(dwSslIgnoreFlags)))
  2843. goto ErrorFail;
  2844. }
  2845. _dwSslIgnoreFlags = dwSslIgnoreFlags;
  2846. break;
  2847. }
  2848. case WinHttpRequestOption_SelectCertificate:
  2849. {
  2850. BSTR bstrCertSubject = GetBSTRFromVariant(Value);
  2851. if (!bstrCertSubject)
  2852. {
  2853. // Allow all empty calls to be interpreted as "first enum"
  2854. bstrCertSubject = SysAllocString(L"");
  2855. if (!bstrCertSubject)
  2856. return E_OUTOFMEMORY;
  2857. }
  2858. if (_bstrCertSubject)
  2859. SysFreeString(_bstrCertSubject);
  2860. _bstrCertSubject = bstrCertSubject;
  2861. break;
  2862. }
  2863. case WinHttpRequestOption_EnableRedirects:
  2864. {
  2865. BOOL fEnableRedirects = GetBoolFromVariant(Value, TRUE);
  2866. _bDisableRedirects = fEnableRedirects ? false : true;
  2867. break;
  2868. }
  2869. default:
  2870. return E_INVALIDARG;
  2871. }
  2872. hr = NOERROR;
  2873. Cleanup:
  2874. SetErrorInfo(hr);
  2875. return hr;
  2876. ErrorFail:
  2877. hr = HRESULT_FROM_WIN32(GetLastError());
  2878. goto Error;
  2879. Error:
  2880. goto Cleanup;
  2881. }
  2882. void
  2883. CHttpRequest::SetErrorInfo(HRESULT hr)
  2884. {
  2885. if (SUCCEEDED(hr))
  2886. return;
  2887. ICreateErrorInfo * pCErrInfo = NULL;
  2888. IErrorInfo * pErrInfo = NULL;
  2889. DWORD error = hr;
  2890. DWORD dwFmtMsgFlag = FORMAT_MESSAGE_FROM_SYSTEM;
  2891. HMODULE hModule = NULL;
  2892. DWORD rc;
  2893. LPWSTR pwszMessage = NULL;
  2894. const DWORD dwSize = 512;
  2895. pwszMessage = New WCHAR[dwSize];
  2896. if (pwszMessage == NULL)
  2897. return;
  2898. if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
  2899. {
  2900. DWORD errcode = HRESULT_CODE(hr);
  2901. if ((errcode > WINHTTP_ERROR_BASE) && (errcode <= WINHTTP_ERROR_LAST))
  2902. {
  2903. dwFmtMsgFlag = FORMAT_MESSAGE_FROM_HMODULE;
  2904. hModule = GetModuleHandle("WINHTTP5.DLL");
  2905. error = errcode;
  2906. }
  2907. }
  2908. rc = ::FormatMessageW(dwFmtMsgFlag, hModule,
  2909. error,
  2910. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  2911. pwszMessage,
  2912. dwSize,
  2913. NULL);
  2914. if (rc != 0)
  2915. {
  2916. if (SUCCEEDED(::CreateErrorInfo(&pCErrInfo)))
  2917. {
  2918. if (SUCCEEDED(pCErrInfo->QueryInterface(IID_IErrorInfo, (void **) &pErrInfo)))
  2919. {
  2920. pCErrInfo->SetSource(L"WinHttp.WinHttpRequest");
  2921. pCErrInfo->SetGUID(IID_IWinHttpRequest);
  2922. pCErrInfo->SetDescription(pwszMessage);
  2923. ::SetErrorInfo(0, pErrInfo);
  2924. pErrInfo->Release();
  2925. }
  2926. pCErrInfo->Release();
  2927. }
  2928. }
  2929. delete [] pwszMessage;
  2930. }
  2931. BOOL
  2932. CHttpRequest::SelectCertificate()
  2933. {
  2934. HCERTSTORE hMyStore = NULL;
  2935. BOOL fRet = FALSE;
  2936. HANDLE hThreadToken = NULL;
  2937. PCCERT_CONTEXT pCertContext = NULL;
  2938. // If impersonating, revert while trying to obtain the cert
  2939. if (OpenThreadToken(GetCurrentThread(), (TOKEN_IMPERSONATE | TOKEN_READ),
  2940. FALSE,
  2941. &hThreadToken))
  2942. {
  2943. INET_ASSERT(hThreadToken != 0);
  2944. RevertToSelf();
  2945. }
  2946. // For now, just pick the first enum available
  2947. // based on a simple CERT_ FIND_SUBJECT_STR criteria
  2948. // If the user selected an empty string, then simply
  2949. // pick the first enum.
  2950. hMyStore = CertOpenSystemStore(NULL, "MY");
  2951. if (!hMyStore)
  2952. {
  2953. goto Cleanup;
  2954. }
  2955. if (_bstrCertSubject && _bstrCertSubject[0])
  2956. {
  2957. pCertContext = CertFindCertificateInStore(hMyStore,
  2958. X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  2959. 0,
  2960. CERT_FIND_SUBJECT_STR,
  2961. (LPVOID) _bstrCertSubject,
  2962. NULL);
  2963. }
  2964. else
  2965. {
  2966. pCertContext = CertFindCertificateInStore(hMyStore,
  2967. X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  2968. 0,
  2969. CERT_FIND_ANY,
  2970. NULL,
  2971. NULL);
  2972. }
  2973. // Winhttp5 will add a couple of new options.
  2974. // One will be an option to request that client auth
  2975. // certs are not cached, so they can be session based.
  2976. // Another will be for flushing the SSL cache on demand.
  2977. //
  2978. // For now, set this via a private method off of the
  2979. // request object.
  2980. if (pCertContext)
  2981. {
  2982. fRet = WinHttpSetOption(_hHTTP,
  2983. WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
  2984. (LPVOID) pCertContext,
  2985. sizeof(CERT_CONTEXT));
  2986. }
  2987. Cleanup:
  2988. if (pCertContext)
  2989. CertFreeCertificateContext(pCertContext);
  2990. if (hMyStore)
  2991. CertCloseStore(hMyStore, 0);
  2992. // Restore the impersonating state for the current thread.
  2993. if (hThreadToken)
  2994. {
  2995. SetThreadToken(NULL, hThreadToken);
  2996. CloseHandle(hThreadToken);
  2997. }
  2998. return fRet;
  2999. }
  3000. /*
  3001. * BSTRToUTF8
  3002. *
  3003. * Purpose:
  3004. * Convert a BSTR to UTF-8
  3005. *
  3006. */
  3007. static
  3008. HRESULT
  3009. BSTRToUTF8(char ** psz, DWORD * pcbUTF8, BSTR bstr)
  3010. {
  3011. UINT cch = lstrlenW(bstr);
  3012. *pcbUTF8 = 0;
  3013. *psz = NULL;
  3014. if (cch == 0)
  3015. return NOERROR;
  3016. // Allocate the maximum buffer the UTF-8 string could be
  3017. *psz = New char [(cch * 3) + 1];
  3018. if (!*psz)
  3019. return E_OUTOFMEMORY;
  3020. *pcbUTF8 = cch * 3;
  3021. WideCharToUtf8(bstr, cch, (BYTE *)*psz, (UINT *)pcbUTF8);
  3022. (*psz)[*pcbUTF8] = 0;
  3023. return NOERROR;
  3024. }
  3025. /**
  3026. * Scans buffer and translates Unicode characters into UTF8 characters
  3027. */
  3028. static
  3029. void
  3030. WideCharToUtf8(
  3031. WCHAR * buffer,
  3032. UINT cch,
  3033. BYTE * bytebuffer,
  3034. UINT * cb)
  3035. {
  3036. UINT count = 0, m1 = *cb, m2 = m1 - 1, m3 = m2 - 1, m4 = m3 - 1;
  3037. DWORD dw1;
  3038. bool surrogate = false;
  3039. for (UINT i = cch; i > 0; i--)
  3040. {
  3041. DWORD dw = *buffer;
  3042. if (surrogate) // is it the second char of a surrogate pair?
  3043. {
  3044. if (dw >= 0xdc00 && dw <= 0xdfff)
  3045. {
  3046. // four bytes 0x11110xxx 0x10xxxxxx 0x10xxxxxx 0x10xxxxxx
  3047. if (count < m4)
  3048. count += 4;
  3049. else
  3050. break;
  3051. ULONG ucs4 = (dw1 - 0xd800) * 0x400 + (dw - 0xdc00) + 0x10000;
  3052. *bytebuffer++ = (byte)(( ucs4 >> 18) | 0xF0);
  3053. *bytebuffer++ = (byte)((( ucs4 >> 12) & 0x3F) | 0x80);
  3054. *bytebuffer++ = (byte)((( ucs4 >> 6) & 0x3F) | 0x80);
  3055. *bytebuffer++ = (byte)(( ucs4 & 0x3F) | 0x80);
  3056. surrogate = false;
  3057. buffer++;
  3058. continue;
  3059. }
  3060. else // Then dw1 must be a three byte character
  3061. {
  3062. if (count < m3)
  3063. count += 3;
  3064. else
  3065. break;
  3066. *bytebuffer++ = (byte)(( dw1 >> 12) | 0xE0);
  3067. *bytebuffer++ = (byte)((( dw1 >> 6) & 0x3F) | 0x80);
  3068. *bytebuffer++ = (byte)(( dw1 & 0x3F) | 0x80);
  3069. }
  3070. surrogate = false;
  3071. }
  3072. if (dw < 0x80) // one byte, 0xxxxxxx
  3073. {
  3074. if (count < m1)
  3075. count++;
  3076. else
  3077. break;
  3078. *bytebuffer++ = (byte)dw;
  3079. }
  3080. else if ( dw < 0x800) // two WORDS, 110xxxxx 10xxxxxx
  3081. {
  3082. if (count < m2)
  3083. count += 2;
  3084. else
  3085. break;
  3086. *bytebuffer++ = (byte)((dw >> 6) | 0xC0);
  3087. *bytebuffer++ = (byte)((dw & 0x3F) | 0x80);
  3088. }
  3089. else if (dw >= 0xd800 && dw <= 0xdbff) // Assume that it is the first char of surrogate pair
  3090. {
  3091. if (i == 1) // last wchar in buffer
  3092. break;
  3093. dw1 = dw;
  3094. surrogate = true;
  3095. }
  3096. else // three bytes, 1110xxxx 10xxxxxx 10xxxxxx
  3097. {
  3098. if (count < m3)
  3099. count += 3;
  3100. else
  3101. break;
  3102. *bytebuffer++ = (byte)(( dw >> 12) | 0xE0);
  3103. *bytebuffer++ = (byte)((( dw >> 6) & 0x3F) | 0x80);
  3104. *bytebuffer++ = (byte)(( dw & 0x3F) | 0x80);
  3105. }
  3106. buffer++;
  3107. }
  3108. *cb = count;
  3109. }
  3110. /*
  3111. * AsciiToBSTR
  3112. *
  3113. * Purpose:
  3114. * Convert an ascii string to a BSTR
  3115. *
  3116. */
  3117. static
  3118. HRESULT
  3119. AsciiToBSTR(BSTR * pbstr, char * sz, int cch)
  3120. {
  3121. int cwch;
  3122. // Determine how big the ascii string will be
  3123. cwch = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, sz, cch,
  3124. NULL, 0);
  3125. *pbstr = SysAllocStringLen(NULL, cwch);
  3126. if (!*pbstr)
  3127. return E_OUTOFMEMORY;
  3128. cch = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, sz, cch,
  3129. *pbstr, cwch);
  3130. return NOERROR;
  3131. }
  3132. /*
  3133. * BSTRToAscii
  3134. *
  3135. * Purpose:
  3136. * Convert a BSTR to an ascii string
  3137. *
  3138. */
  3139. static
  3140. HRESULT
  3141. BSTRToAscii(char ** psz, BSTR bstr)
  3142. {
  3143. int cch;
  3144. *psz = NULL;
  3145. if (!bstr)
  3146. return S_OK;
  3147. // Determine how big the ascii string will be
  3148. cch = WideCharToMultiByte(CP_ACP, 0, bstr, SysStringLen(bstr),
  3149. NULL, 0, NULL, NULL);
  3150. *psz = New char[cch + 1];
  3151. if (!*psz)
  3152. return E_OUTOFMEMORY;
  3153. // Determine how big the ascii string will be
  3154. cch = WideCharToMultiByte(CP_ACP, 0, bstr, SysStringLen(bstr),
  3155. *psz, cch, NULL, NULL);
  3156. (*psz)[cch] = 0;
  3157. return S_OK;
  3158. }
  3159. /*
  3160. * GetBSTRFromVariant
  3161. *
  3162. * Purpose:
  3163. * Convert a VARIANT to a BSTR
  3164. *
  3165. */
  3166. static BSTR GetBSTRFromVariant(VARIANT varVariant)
  3167. {
  3168. VARIANT varTemp;
  3169. HRESULT hr;
  3170. BSTR bstrResult = NULL;
  3171. if (V_VT(&varVariant) != VT_EMPTY && V_VT(&varVariant) != VT_NULL &&
  3172. V_VT(&varVariant) != VT_ERROR)
  3173. {
  3174. VariantInit(&varTemp);
  3175. hr = VariantChangeType(
  3176. &varTemp,
  3177. &varVariant,
  3178. 0,
  3179. VT_BSTR);
  3180. if (FAILED(hr))
  3181. goto Error;
  3182. bstrResult = V_BSTR(&varTemp); // take over ownership of BSTR
  3183. }
  3184. hr = NOERROR;
  3185. Cleanup:
  3186. // DON'T clear the variant because we stole the BSTR
  3187. //VariantClear(&varTemp);
  3188. return bstrResult;
  3189. Error:
  3190. goto Cleanup;
  3191. }
  3192. /*
  3193. * GetBoolFromVariant
  3194. *
  3195. * Purpose:
  3196. * Convert a VARIANT to a Boolean
  3197. *
  3198. */
  3199. static BOOL GetBoolFromVariant(VARIANT varVariant, BOOL fDefault)
  3200. {
  3201. HRESULT hr;
  3202. BOOL fResult = fDefault;
  3203. if (V_VT(&varVariant) != VT_EMPTY && V_VT(&varVariant) != VT_NULL &&
  3204. V_VT(&varVariant) != VT_ERROR)
  3205. {
  3206. VARIANT varTemp;
  3207. VariantInit(&varTemp);
  3208. hr = VariantChangeType(
  3209. &varTemp,
  3210. &varVariant,
  3211. 0,
  3212. VT_BOOL);
  3213. if (FAILED(hr))
  3214. goto Cleanup;
  3215. fResult = V_BOOL(&varTemp) == VARIANT_TRUE ? TRUE : FALSE;
  3216. }
  3217. hr = NOERROR;
  3218. Cleanup:
  3219. return fResult;
  3220. }
  3221. /*
  3222. * GetDwordFromVariant
  3223. *
  3224. * Purpose:
  3225. * Convert a VARIANT to a DWORD
  3226. *
  3227. */
  3228. static DWORD GetDwordFromVariant(VARIANT varVariant, DWORD dwDefault)
  3229. {
  3230. HRESULT hr;
  3231. DWORD dwResult = dwDefault;
  3232. if (V_VT(&varVariant) != VT_EMPTY && V_VT(&varVariant) != VT_NULL &&
  3233. V_VT(&varVariant) != VT_ERROR)
  3234. {
  3235. VARIANT varTemp;
  3236. VariantInit(&varTemp);
  3237. hr = VariantChangeType(
  3238. &varTemp,
  3239. &varVariant,
  3240. 0,
  3241. VT_UI4);
  3242. if (FAILED(hr))
  3243. goto Cleanup;
  3244. dwResult = V_UI4(&varTemp);
  3245. }
  3246. hr = NOERROR;
  3247. Cleanup:
  3248. return dwResult;
  3249. }
  3250. /*
  3251. * GetDwordFromVariant
  3252. *
  3253. * Purpose:
  3254. * Convert a VARIANT to a DWORD
  3255. *
  3256. */
  3257. static long GetLongFromVariant(VARIANT varVariant, long lDefault)
  3258. {
  3259. HRESULT hr;
  3260. long lResult = lDefault;
  3261. if (V_VT(&varVariant) != VT_EMPTY && V_VT(&varVariant) != VT_NULL &&
  3262. V_VT(&varVariant) != VT_ERROR)
  3263. {
  3264. VARIANT varTemp;
  3265. VariantInit(&varTemp);
  3266. hr = VariantChangeType(
  3267. &varTemp,
  3268. &varVariant,
  3269. 0,
  3270. VT_I4);
  3271. if (FAILED(hr))
  3272. goto Cleanup;
  3273. lResult = V_I4(&varTemp);
  3274. }
  3275. hr = NOERROR;
  3276. Cleanup:
  3277. return lResult;
  3278. }
  3279. /**
  3280. * Helper to create a char safearray from a string
  3281. */
  3282. static
  3283. HRESULT
  3284. CreateVector(VARIANT * pVar, const BYTE * pData, DWORD cElems)
  3285. {
  3286. HRESULT hr;
  3287. BYTE * pB;
  3288. SAFEARRAY * psa = SafeArrayCreateVector(VT_UI1, 0, cElems);
  3289. if (!psa)
  3290. {
  3291. hr = E_OUTOFMEMORY;
  3292. goto Cleanup;
  3293. }
  3294. hr = SafeArrayAccessData(psa, (void **)&pB);
  3295. if (hr)
  3296. goto Error;
  3297. memcpy(pB, pData, cElems);
  3298. SafeArrayUnaccessData(psa);
  3299. INET_ASSERT((pVar->vt == VT_EMPTY) || (pVar->vt == VT_NULL));
  3300. V_ARRAY(pVar) = psa;
  3301. pVar->vt = VT_ARRAY | VT_UI1;
  3302. hr = NOERROR;
  3303. Cleanup:
  3304. return hr;
  3305. Error:
  3306. if (psa)
  3307. SafeArrayDestroy(psa);
  3308. goto Cleanup;
  3309. }
  3310. /*
  3311. * ReadFromStream
  3312. *
  3313. * Purpose:
  3314. * Extract the contents of a stream into a buffer.
  3315. *
  3316. * Parameters:
  3317. * ppBuf IN/OUT Buffer
  3318. * pStm IN Stream
  3319. *
  3320. * Errors:
  3321. * E_INVALIDARG
  3322. * E_OUTOFMEMORY
  3323. */
  3324. static
  3325. HRESULT
  3326. ReadFromStream(char ** ppData, ULONG * pcbData, IStream * pStm)
  3327. {
  3328. HRESULT hr = NOERROR;
  3329. char * pBuffer = NULL; // Buffer
  3330. ULONG cbBuffer = 0; // Bytes in buffer
  3331. ULONG cbData = 0; // Bytes of data in buffer
  3332. ULONG cbRead = 0; // Bytes read from stream
  3333. ULONG cbNewSize = 0;
  3334. char * pNewBuf = NULL;
  3335. if (!ppData || !pStm)
  3336. return E_INVALIDARG;
  3337. *ppData = NULL;
  3338. *pcbData = 0;
  3339. while (TRUE)
  3340. {
  3341. if (cbData + 512 > cbBuffer)
  3342. {
  3343. cbNewSize = (cbData ? cbData*2 : 4096);
  3344. pNewBuf = New char[cbNewSize+1];
  3345. if (!pNewBuf)
  3346. goto ErrorOutOfMemory;
  3347. if (cbData)
  3348. ::memcpy(pNewBuf, pBuffer, cbData);
  3349. cbBuffer = cbNewSize;
  3350. delete[] pBuffer;
  3351. pBuffer = pNewBuf;
  3352. pBuffer[cbData] = 0;
  3353. }
  3354. hr = pStm->Read(
  3355. &pBuffer[cbData],
  3356. cbBuffer - cbData,
  3357. &cbRead);
  3358. if (FAILED(hr))
  3359. goto Error;
  3360. cbData += cbRead;
  3361. pBuffer[cbData] = 0;
  3362. // No more data
  3363. if (cbRead == 0)
  3364. break;
  3365. }
  3366. *ppData = pBuffer;
  3367. *pcbData = cbData;
  3368. hr = NOERROR;
  3369. Cleanup:
  3370. return hr;
  3371. ErrorOutOfMemory:
  3372. hr = E_OUTOFMEMORY;
  3373. goto Error;
  3374. Error:
  3375. if (pBuffer)
  3376. delete [] pBuffer;
  3377. goto Cleanup;
  3378. }
  3379. static
  3380. void
  3381. MessageLoop()
  3382. {
  3383. MSG msg;
  3384. // There is a window message available. Dispatch it.
  3385. while (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))
  3386. {
  3387. TranslateMessage(&msg);
  3388. DispatchMessage(&msg);
  3389. }
  3390. }
  3391. static
  3392. DWORD
  3393. UpdateTimeout(DWORD dwTimeout, DWORD dwStartTime)
  3394. {
  3395. if (dwTimeout != INFINITE)
  3396. {
  3397. DWORD dwTimeNow = GetTickCount();
  3398. DWORD dwElapsedTime;
  3399. if (dwTimeNow >= dwStartTime)
  3400. {
  3401. dwElapsedTime = dwTimeNow - dwStartTime;
  3402. }
  3403. else
  3404. {
  3405. dwElapsedTime = dwTimeNow + (0xFFFFFFFF - dwStartTime);
  3406. }
  3407. if (dwElapsedTime < dwTimeout)
  3408. {
  3409. dwTimeout -= dwElapsedTime;
  3410. }
  3411. else
  3412. {
  3413. dwTimeout = 0;
  3414. }
  3415. }
  3416. return dwTimeout;
  3417. }
  3418. DWORD CSinkArray::Add(IUnknown * pUnk)
  3419. {
  3420. ULONG iIndex;
  3421. IUnknown** pp = NULL;
  3422. if (_nSize == 0) // no connections
  3423. {
  3424. _pUnk = pUnk;
  3425. _nSize = 1;
  3426. return 1;
  3427. }
  3428. else if (_nSize == 1)
  3429. {
  3430. // create array
  3431. pp = (IUnknown **)ALLOCATE_FIXED_MEMORY(sizeof(IUnknown*)* _DEFAULT_VECTORLENGTH);
  3432. if (pp == NULL)
  3433. return 0;
  3434. memset(pp, 0, sizeof(IUnknown *) * _DEFAULT_VECTORLENGTH);
  3435. *pp = _pUnk;
  3436. _ppUnk = pp;
  3437. _nSize = _DEFAULT_VECTORLENGTH;
  3438. }
  3439. for (pp = begin(); pp < end(); pp++)
  3440. {
  3441. if (*pp == NULL)
  3442. {
  3443. *pp = pUnk;
  3444. iIndex = ULONG(pp-begin());
  3445. return iIndex+1;
  3446. }
  3447. }
  3448. int nAlloc = _nSize*2;
  3449. pp = (IUnknown **)REALLOCATE_MEMORY(_ppUnk, sizeof(IUnknown*)*nAlloc, LMEM_ZEROINIT);
  3450. if (pp == NULL)
  3451. return 0;
  3452. _ppUnk = pp;
  3453. memset(&_ppUnk[_nSize], 0, sizeof(IUnknown *)*_nSize);
  3454. _ppUnk[_nSize] = pUnk;
  3455. iIndex = _nSize;
  3456. _nSize = nAlloc;
  3457. return iIndex+1;
  3458. }
  3459. BOOL CSinkArray::Remove(DWORD dwCookie)
  3460. {
  3461. ULONG iIndex;
  3462. if (dwCookie == NULL)
  3463. return FALSE;
  3464. if (_nSize == 0)
  3465. return FALSE;
  3466. iIndex = dwCookie-1;
  3467. if (iIndex >= (ULONG)_nSize)
  3468. return FALSE;
  3469. if (_nSize == 1)
  3470. {
  3471. _nSize = 0;
  3472. return TRUE;
  3473. }
  3474. begin()[iIndex] = NULL;
  3475. return TRUE;
  3476. }
  3477. void CSinkArray::ReleaseAll()
  3478. {
  3479. for (IUnknown ** pp = begin(); pp < end(); pp++)
  3480. {
  3481. if (*pp != NULL)
  3482. {
  3483. SafeRelease(*pp);
  3484. }
  3485. }
  3486. }
  3487. HRESULT STDMETHODCALLTYPE
  3488. CSinkArray::QueryInterface(REFIID, void **)
  3489. {
  3490. return E_NOTIMPL;
  3491. }
  3492. ULONG STDMETHODCALLTYPE
  3493. CSinkArray::AddRef()
  3494. {
  3495. return 2;
  3496. }
  3497. ULONG STDMETHODCALLTYPE
  3498. CSinkArray::Release()
  3499. {
  3500. return 1;
  3501. }
  3502. void STDMETHODCALLTYPE
  3503. CSinkArray::OnResponseStart(long Status, BSTR bstrContentType)
  3504. {
  3505. for (IUnknown ** pp = begin(); pp < end(); pp++)
  3506. {
  3507. if (*pp != NULL)
  3508. {
  3509. IWinHttpRequestEvents * pSink;
  3510. pSink = static_cast<IWinHttpRequestEvents *>(*pp);
  3511. if (((*(DWORD_PTR **)pSink)[3]) != NULL)
  3512. {
  3513. pSink->OnResponseStart(Status, bstrContentType);
  3514. }
  3515. }
  3516. }
  3517. }
  3518. void STDMETHODCALLTYPE
  3519. CSinkArray::OnResponseDataAvailable(SAFEARRAY ** ppsaData)
  3520. {
  3521. for (IUnknown ** pp = begin(); pp < end(); pp++)
  3522. {
  3523. if (*pp != NULL)
  3524. {
  3525. IWinHttpRequestEvents * pSink;
  3526. pSink = static_cast<IWinHttpRequestEvents *>(*pp);
  3527. if (((*(DWORD_PTR **)pSink)[4]) != NULL)
  3528. {
  3529. pSink->OnResponseDataAvailable(ppsaData);
  3530. }
  3531. }
  3532. }
  3533. }
  3534. void STDMETHODCALLTYPE
  3535. CSinkArray::OnResponseFinished(void)
  3536. {
  3537. for (IUnknown ** pp = begin(); pp < end(); pp++)
  3538. {
  3539. if (*pp != NULL)
  3540. {
  3541. IWinHttpRequestEvents * pSink;
  3542. pSink = static_cast<IWinHttpRequestEvents *>(*pp);
  3543. if (((*(DWORD_PTR **)pSink)[5]) != NULL)
  3544. {
  3545. pSink->OnResponseFinished();
  3546. }
  3547. }
  3548. }
  3549. }
  3550. CWinHttpRequestEventsMarshaller::CWinHttpRequestEventsMarshaller
  3551. (
  3552. CSinkArray * pSinkArray,
  3553. HWND hWnd
  3554. )
  3555. {
  3556. INET_ASSERT((pSinkArray != NULL) && (hWnd != NULL));
  3557. _pSinkArray = pSinkArray;
  3558. _hWnd = hWnd;
  3559. _cRefs = 0;
  3560. _bFireEvents = true;
  3561. _cs.Init();
  3562. }
  3563. CWinHttpRequestEventsMarshaller::~CWinHttpRequestEventsMarshaller()
  3564. {
  3565. INET_ASSERT(_pSinkArray == NULL);
  3566. INET_ASSERT(_hWnd == NULL);
  3567. INET_ASSERT(_cRefs == 0);
  3568. }
  3569. HRESULT
  3570. CWinHttpRequestEventsMarshaller::Create
  3571. (
  3572. CSinkArray * pSinkArray,
  3573. CWinHttpRequestEventsMarshaller ** ppSinkMarshaller
  3574. )
  3575. {
  3576. CWinHttpRequestEventsMarshaller * pSinkMarshaller = NULL;
  3577. HWND hWnd = NULL;
  3578. HRESULT hr = NOERROR;
  3579. if (!RegisterWinHttpEventMarshallerWndClass())
  3580. goto ErrorFail;
  3581. hWnd = CreateWindowEx(0, s_szWinHttpEventMarshallerWndClass, NULL,
  3582. 0, 0, 0, 0, 0,
  3583. (IsPlatformWinNT() && GlobalPlatformVersion5) ? HWND_MESSAGE : NULL,
  3584. NULL, GlobalDllHandle, NULL);
  3585. if (!hWnd)
  3586. goto ErrorFail;
  3587. pSinkMarshaller = New CWinHttpRequestEventsMarshaller(pSinkArray, hWnd);
  3588. if (!pSinkMarshaller)
  3589. goto ErrorOutOfMemory;
  3590. SetLastError(0);
  3591. SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) pSinkMarshaller);
  3592. if (GetLastError() != 0)
  3593. goto ErrorFail;
  3594. pSinkMarshaller->AddRef();
  3595. *ppSinkMarshaller = pSinkMarshaller;
  3596. Exit:
  3597. if (FAILED(hr))
  3598. {
  3599. if (pSinkMarshaller)
  3600. {
  3601. delete pSinkMarshaller;
  3602. }
  3603. else if (hWnd)
  3604. {
  3605. DestroyWindow(hWnd);
  3606. }
  3607. }
  3608. return hr;
  3609. ErrorFail:
  3610. hr = HRESULT_FROM_WIN32(GetLastError());
  3611. goto Exit;
  3612. ErrorOutOfMemory:
  3613. hr = E_OUTOFMEMORY;
  3614. goto Exit;
  3615. }
  3616. void
  3617. CWinHttpRequestEventsMarshaller::Shutdown()
  3618. {
  3619. if (_cs.Lock())
  3620. {
  3621. FreezeEvents();
  3622. if (_hWnd)
  3623. {
  3624. MessageLoop();
  3625. DestroyWindow(_hWnd);
  3626. _hWnd = NULL;
  3627. }
  3628. _pSinkArray = NULL;
  3629. _cs.Unlock();
  3630. }
  3631. }
  3632. LRESULT CALLBACK
  3633. CWinHttpRequestEventsMarshaller::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  3634. {
  3635. if (msg >= WHREM_MSG_ON_RESPONSE_START && msg <= WHREM_MSG_ON_RESPONSE_FINISHED)
  3636. {
  3637. CWinHttpRequestEventsMarshaller * pMarshaller;
  3638. CSinkArray * pSinkArray;
  3639. bool bOkToFireEvents = false;
  3640. pMarshaller = (CWinHttpRequestEventsMarshaller *) GetWindowLongPtr(hWnd, GWLP_USERDATA);
  3641. if (pMarshaller)
  3642. {
  3643. pSinkArray = pMarshaller->GetSinkArray();
  3644. bOkToFireEvents = pMarshaller->OkToFireEvents();
  3645. }
  3646. switch (msg)
  3647. {
  3648. case WHREM_MSG_ON_RESPONSE_START:
  3649. {
  3650. BSTR bstrContentType = (BSTR) lParam;
  3651. if (bOkToFireEvents)
  3652. {
  3653. pSinkArray->OnResponseStart((long) wParam, bstrContentType);
  3654. }
  3655. if (bstrContentType)
  3656. {
  3657. SysFreeString(bstrContentType);
  3658. }
  3659. }
  3660. break;
  3661. case WHREM_MSG_ON_RESPONSE_DATA_AVAILABLE:
  3662. {
  3663. SAFEARRAY * psaData = (SAFEARRAY *) wParam;
  3664. if (bOkToFireEvents)
  3665. {
  3666. pSinkArray->OnResponseDataAvailable(&psaData);
  3667. }
  3668. if (psaData)
  3669. {
  3670. SafeArrayDestroy(psaData);
  3671. }
  3672. }
  3673. break;
  3674. case WHREM_MSG_ON_RESPONSE_FINISHED:
  3675. if (bOkToFireEvents)
  3676. {
  3677. pSinkArray->OnResponseFinished();
  3678. }
  3679. break;
  3680. }
  3681. return 0;
  3682. }
  3683. else
  3684. {
  3685. return DefWindowProc(hWnd, msg, wParam, lParam);
  3686. }
  3687. }
  3688. HRESULT STDMETHODCALLTYPE
  3689. CWinHttpRequestEventsMarshaller::QueryInterface(REFIID riid, void ** ppv)
  3690. {
  3691. HRESULT hr = NOERROR;
  3692. if (ppv == NULL)
  3693. {
  3694. hr = E_INVALIDARG;
  3695. }
  3696. else if (riid == IID_IWinHttpRequestEvents || riid == IID_IUnknown)
  3697. {
  3698. *ppv = static_cast<IWinHttpRequestEvents *>(this);
  3699. AddRef();
  3700. }
  3701. else
  3702. hr = E_NOINTERFACE;
  3703. return hr;
  3704. }
  3705. ULONG STDMETHODCALLTYPE
  3706. CWinHttpRequestEventsMarshaller::AddRef()
  3707. {
  3708. return InterlockedIncrement(&_cRefs);
  3709. }
  3710. ULONG STDMETHODCALLTYPE
  3711. CWinHttpRequestEventsMarshaller::Release()
  3712. {
  3713. DWORD cRefs = InterlockedDecrement(&_cRefs);
  3714. if (cRefs == 0)
  3715. {
  3716. delete this;
  3717. return 0;
  3718. }
  3719. else
  3720. return cRefs;
  3721. }
  3722. void STDMETHODCALLTYPE
  3723. CWinHttpRequestEventsMarshaller::OnResponseStart(long Status, BSTR bstrContentType)
  3724. {
  3725. if (_cs.Lock())
  3726. {
  3727. if (OkToFireEvents())
  3728. {
  3729. BSTR bstrContentTypeCopy;
  3730. bstrContentTypeCopy = SysAllocString(bstrContentType);
  3731. PostMessage(_hWnd, WHREM_MSG_ON_RESPONSE_START,
  3732. (WPARAM) Status,
  3733. (LPARAM) bstrContentTypeCopy);
  3734. // Note: ownership of bstrContentType is transferred to the
  3735. // message window, so the string is not freed here.
  3736. }
  3737. _cs.Unlock();
  3738. }
  3739. }
  3740. void STDMETHODCALLTYPE
  3741. CWinHttpRequestEventsMarshaller::OnResponseDataAvailable(SAFEARRAY ** ppsaData)
  3742. {
  3743. if (_cs.Lock())
  3744. {
  3745. if (OkToFireEvents())
  3746. {
  3747. SAFEARRAY * psaDataCopy = NULL;
  3748. if (SUCCEEDED(SafeArrayCopy(*ppsaData, &psaDataCopy)))
  3749. {
  3750. PostMessage(_hWnd, WHREM_MSG_ON_RESPONSE_DATA_AVAILABLE,
  3751. (WPARAM) psaDataCopy, 0);
  3752. }
  3753. // Note: ownership of psaDataCopy is transferred to the
  3754. // message window, so the array is not freed here.
  3755. }
  3756. _cs.Unlock();
  3757. }
  3758. }
  3759. void STDMETHODCALLTYPE
  3760. CWinHttpRequestEventsMarshaller::OnResponseFinished(void)
  3761. {
  3762. if (_cs.Lock())
  3763. {
  3764. if (OkToFireEvents())
  3765. {
  3766. PostMessage(_hWnd, WHREM_MSG_ON_RESPONSE_FINISHED, 0, 0);
  3767. }
  3768. _cs.Unlock();
  3769. }
  3770. }
  3771. BOOL RegisterWinHttpEventMarshallerWndClass()
  3772. {
  3773. if (s_fWndClassRegistered)
  3774. return TRUE;
  3775. // only one thread should be here
  3776. if (!GeneralInitCritSec.Lock())
  3777. return FALSE;
  3778. if (s_fWndClassRegistered == FALSE)
  3779. {
  3780. WNDCLASS wndclass;
  3781. wndclass.style = 0;
  3782. wndclass.lpfnWndProc = &CWinHttpRequestEventsMarshaller::WndProc;
  3783. wndclass.cbClsExtra = 0;
  3784. wndclass.cbWndExtra = 0;
  3785. wndclass.hInstance = GlobalDllHandle;
  3786. wndclass.hIcon = NULL;
  3787. wndclass.hCursor = NULL;;
  3788. wndclass.hbrBackground = (HBRUSH)NULL;
  3789. wndclass.lpszMenuName = NULL;
  3790. wndclass.lpszClassName = s_szWinHttpEventMarshallerWndClass;
  3791. // Register the window class
  3792. if (RegisterClass(&wndclass))
  3793. {
  3794. s_fWndClassRegistered = TRUE;
  3795. }
  3796. }
  3797. GeneralInitCritSec.Unlock();
  3798. return s_fWndClassRegistered;
  3799. }
  3800. void CleanupWinHttpRequestGlobals()
  3801. {
  3802. if (s_fWndClassRegistered)
  3803. {
  3804. // Register the window class
  3805. if (UnregisterClass(s_szWinHttpEventMarshallerWndClass, GlobalDllHandle))
  3806. {
  3807. s_fWndClassRegistered = FALSE;
  3808. }
  3809. }
  3810. }
  3811. static
  3812. BOOL
  3813. IsValidVariant(VARIANT v)
  3814. {
  3815. BOOL fOk = TRUE;
  3816. if (V_ISBYREF(&v))
  3817. {
  3818. if (IsBadReadPtr(v.pvarVal, sizeof(VARIANT)))
  3819. {
  3820. fOk = FALSE;
  3821. goto Exit;
  3822. }
  3823. else
  3824. v = *(v.pvarVal);
  3825. }
  3826. switch (v.vt)
  3827. {
  3828. case VT_BSTR:
  3829. fOk = IsValidBstr(v.bstrVal);
  3830. break;
  3831. case (VT_BYREF | VT_BSTR):
  3832. fOk = !IsBadReadPtr(v.pbstrVal, sizeof(BSTR));
  3833. break;
  3834. case (VT_BYREF | VT_VARIANT):
  3835. fOk = !IsBadReadPtr(v.pvarVal, sizeof(VARIANT)) &&
  3836. IsValidVariant(*(v.pvarVal));
  3837. break;
  3838. case VT_UNKNOWN:
  3839. case VT_DISPATCH:
  3840. fOk = !IsBadReadPtr(v.punkVal, sizeof(void *));
  3841. break;
  3842. }
  3843. Exit:
  3844. return fOk;
  3845. }