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.

948 lines
31 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. cachelogic.cxx
  5. Abstract:
  6. This file contains the implementation of the HTTPCACHE request object public interface and a few
  7. miscellaneous global variables and classes
  8. Author:
  9. Revision History:
  10. --*/
  11. #include <wininetp.h>
  12. #define __CACHE_INCLUDE__
  13. #include "..\urlcache\cache.hxx"
  14. #include "..\urlcache\hndlmgr.hxx"
  15. #include "cachelogic.hxx"
  16. #include "internalapi.hxx"
  17. /////////////////////////////////////////////////////////////////////////
  18. //
  19. // Global variables specific to HttpCache
  20. //
  21. //
  22. /////////////////////////////////////////////////////////////////////////
  23. #define CACHE_ENTRY_INFOEX_SIZE 1024 * 5 // we need this much to prevent buffer overrun
  24. /////////////////////////////////////////////////////////////////////////
  25. //
  26. // HTTPCACHE_REQUEST Constructors and destructors
  27. //
  28. //
  29. /////////////////////////////////////////////////////////////////////////
  30. HTTPCACHE_REQUEST::HTTPCACHE_REQUEST(HINTERNET hRequest)
  31. {
  32. _hRequest = hRequest;
  33. InternalQueryOptionA(_hRequest, WINHTTP_OPTION_CACHE_FLAGS, &_dwCacheFlags);
  34. InternalQueryOptionA(_hRequest, WINHTTP_OPTION_REQUEST_FLAGS, &_dwRequestFlags);
  35. _lpszFileName = NULL;
  36. _lpszFileExtension = NULL;
  37. _lpszCacheWriteLocalFilename = NULL;
  38. _fIsPartialCache = FALSE;
  39. _fCacheWriteInProgress = FALSE;
  40. _fDeleteWriteFile = FALSE;
  41. _fCacheReadInProgress = FALSE;
  42. _fLazyUpdate = FALSE;
  43. _fHasExpiry = FALSE;
  44. _fHasLastModTime = FALSE;
  45. _fHasPostCheck = FALSE;
  46. _fMustRevalidate = FALSE;
  47. _pCacheEntryInfo = (CACHE_ENTRY_INFOEX *) ALLOCATE_FIXED_MEMORY(CACHE_ENTRY_INFOEX_SIZE);
  48. // Set the URL for this object
  49. // if you drill down to the defn of GetURL(), you'll see that it's returning
  50. // _CacheUrlName from the class. This variable has NOTHING to do with the
  51. // caching layer. They just have a misleading variable name there!
  52. DWORD dwSize = INTERNET_MAX_URL_LENGTH;
  53. InternetQueryOptionA(_hRequest, WINHTTP_OPTION_URL, _szUrl, &dwSize);
  54. // This is where _nextState is set
  55. _nextState = CHECK_IF_IN_CACHE;
  56. if (_dwCacheFlags & CACHE_FLAG_DISABLE_CACHE_READ)
  57. _nextState = PREPARE_DOWNLOAD_FROM_INET;
  58. }
  59. HTTPCACHE_REQUEST::~HTTPCACHE_REQUEST()
  60. {
  61. // If there's a file that we're using to write to the cache,
  62. // but fails to commit to the cache index, then we entirely
  63. // get rid of the file here
  64. if (_fDeleteWriteFile == TRUE)
  65. {
  66. CloseHandle(_hCacheWriteFile);
  67. DeleteFile(_lpszCacheWriteLocalFilename);
  68. }
  69. if (_lpszFileName)
  70. FREE_MEMORY(_lpszFileName);
  71. if (_lpszFileExtension)
  72. FREE_MEMORY(_lpszFileExtension);
  73. if (_lpszCacheWriteLocalFilename)
  74. FREE_MEMORY(_lpszCacheWriteLocalFilename);
  75. if (_pCacheEntryInfo)
  76. FREE_MEMORY(_pCacheEntryInfo);
  77. }
  78. /////////////////////////////////////////////////////////////////////////
  79. //
  80. // HTTPCACHE_REQUEST Public interface:
  81. // SendRequest
  82. // ReceiveResponse
  83. // QueryDataAvailable
  84. // ReadData
  85. //
  86. // Essentially these public interfaces keep track of a state variable (_nextState)
  87. // and manipulate the states based on the results returned from the private
  88. // functions.
  89. //
  90. // It is structured so that ONLY the public interface should manipulate the state
  91. // variables
  92. //
  93. // The lpszHeader and lpOptional parameters are being ignored
  94. // by the cache, unless the cache fails (cache lookup fails or IMS request
  95. // returns 200 OK), in which case we call the net SendRequest with
  96. // the passed-in parameters
  97. PUBLIC BOOL HTTPCACHE_REQUEST::SendRequest(
  98. IN LPCWSTR lpszHeaders,
  99. IN DWORD dwHeadersLength,
  100. IN LPVOID lpOptional,
  101. IN DWORD dwOptionalLength
  102. )
  103. {
  104. DEBUG_ENTER((DBG_CACHE,
  105. Bool,
  106. "HTTPCACHE_REQUEST::SendRequest",
  107. NULL
  108. ));
  109. BOOL fResult = FALSE;
  110. BOOL fFinish = FALSE;
  111. do
  112. {
  113. switch(_nextState)
  114. {
  115. case CHECK_IF_IN_CACHE:
  116. DEBUG_PRINT(CACHE,
  117. INFO,
  118. ("CHECK_IF_IN_CACHE state in HTTPCACHE_REQUEST::SendRequest\n"
  119. ));
  120. fResult = OpenCacheReadStream();
  121. if (fResult)
  122. {
  123. _fCacheReadInProgress = TRUE;
  124. DEBUG_PRINT(CACHE, INFO, ("%s coming from the cache\n", GetUrl()));
  125. if (IsPartialCacheEntry())
  126. _nextState = ADD_PARTIAL_CONTENT_UMS_AND_RANGE_HEADER;
  127. else
  128. _nextState = ADD_NORMAL_CONTENT_IMS_HEADER;
  129. }
  130. else
  131. {
  132. _fCacheReadInProgress = FALSE;
  133. _nextState = PREPARE_DOWNLOAD_FROM_INET;
  134. }
  135. break;
  136. case ADD_PARTIAL_CONTENT_UMS_AND_RANGE_HEADER:
  137. DEBUG_PRINT(CACHE,
  138. INFO,
  139. ("ADD_PARTIAL_CONTENT_UMS_AND_RANGE_HEADER state in HTTPCACHE_REQUEST::SendRequest\n"
  140. ));
  141. // we don't need to check for expiry if it's a partial content
  142. LockPartialCacheEntry();
  143. AddRangeRequestHeaders();
  144. _nextState = SEND_REQUEST;
  145. break;
  146. case ADD_NORMAL_CONTENT_IMS_HEADER:
  147. DEBUG_PRINT(CACHE,
  148. INFO,
  149. ("ADD_NORMAL_CONTENT_IMS_HEADER state in HTTPCACHE_REQUEST::SendRequest\n"
  150. ));
  151. if (_dwCacheFlags & CACHE_FLAG_ALWAYS_RESYNCHRONIZE ||
  152. IsExpired() == TRUE)
  153. {
  154. AddIfModifiedSinceHeaders();
  155. _nextState = SEND_REQUEST;
  156. }
  157. else
  158. {
  159. _nextState = PREPARE_READ_FROM_CACHE;
  160. }
  161. break;
  162. case SEND_REQUEST:
  163. DEBUG_PRINT(CACHE,
  164. INFO,
  165. ("SEND_REQUEST state in HTTPCACHE_REQUEST::SendRequest\n"
  166. ));
  167. DWORD dwStatusCode;
  168. DWORD dwAction;
  169. fResult = TransmitRequest(&dwStatusCode);
  170. if (fResult)
  171. {
  172. CheckResponseAfterIMS(dwStatusCode);
  173. // If I get a 304 back, then it means it's not modified, and I can grab it
  174. // from the cache
  175. if (dwStatusCode == HTTP_STATUS_NOT_MODIFIED)
  176. _nextState = PREPARE_READ_FROM_CACHE;
  177. // If I get back a 200 then I can start reading data from the net;
  178. // but I don't have to do a SendRequets again caz it's already done in
  179. // the Transmit request call
  180. else if (dwStatusCode == HTTP_STATUS_OK)
  181. _nextState = PREPARE_READ_FROM_INET_AFTER_200_RESPONSE;
  182. // If we get a 206, then do the partial read
  183. else if (dwStatusCode == HTTP_STATUS_PARTIAL_CONTENT &&
  184. _pCacheEntryInfo->CacheEntryType & SPARSE_CACHE_ENTRY)
  185. _nextState = PARTIAL_READ;
  186. // Otherwise, I'll have to clear all the headers, reset the request object
  187. // and redo the full SendRequest again (CACHE_SEND_ERROR state)
  188. }
  189. else
  190. {
  191. _nextState = CACHE_SEND_ERROR;
  192. }
  193. break;
  194. case PARTIAL_READ:
  195. DEBUG_PRINT(CACHE,
  196. INFO,
  197. ("PARTIAL_READ state in HTTPCACHE_REQUEST::SendRequest\n"
  198. ));
  199. fResult = FakePartialCacheResponseHeaders();
  200. _nextState = PREPARE_READ_FROM_CACHE;
  201. fFinish = TRUE;
  202. break;
  203. case PREPARE_READ_FROM_CACHE:
  204. DEBUG_PRINT(CACHE,
  205. INFO,
  206. ("PREPARE_READ_FROM_CACHE state in HTTPCACHE_REQUEST::SendRequest\n"
  207. ));
  208. // If the request can be satisifed by the cache, we complete the SendRequest
  209. // transaction by recovering the response headers from the cache so that the
  210. // user is not aware that the content is coming from the cache
  211. FakeCacheResponseHeaders();
  212. fResult = TRUE;
  213. fFinish = TRUE;
  214. break;
  215. case PREPARE_READ_FROM_INET_AFTER_200_RESPONSE:
  216. DEBUG_PRINT(CACHE,
  217. INFO,
  218. ("PREPARE_READ_FROM_INET_AFTER_200_RESPONSE state in HTTPCACHE_REQUEST::SendRequest\n"
  219. ));
  220. if (IsPartialCacheEntry())
  221. DeletePartialCacheFile();
  222. CloseCacheReadStream();
  223. _nextState = PREPARE_DOWNLOAD_FROM_INET;
  224. fFinish = TRUE;
  225. break;
  226. // somehow adding the request header fails, so we fall back to downloading from inet
  227. case CACHE_SEND_ERROR:
  228. DEBUG_PRINT(CACHE,
  229. INFO,
  230. ("CACHE_SEND_ERROR state in HTTPCACHE_REQUEST::SendRequest\n"
  231. ));
  232. if (IsPartialCacheEntry())
  233. DeletePartialCacheFile();
  234. CloseCacheReadStream();
  235. // Reset the request handle object (clear any previous request headers, etc...)
  236. // so it can be used to send new requests again
  237. InternalReuseHTTP_Request_Handle_Object(_hRequest);
  238. _nextState = PREPARE_DOWNLOAD_FROM_INET;
  239. break;
  240. case PREPARE_DOWNLOAD_FROM_INET:
  241. DEBUG_PRINT(CACHE,
  242. INFO,
  243. ("PREPARE_DOWNLOAD_FROM_INET state in HTTPCACHE_REQUEST::SendRequest\n"
  244. ));
  245. fResult = WinHttpSendRequest(
  246. _hRequest,
  247. lpszHeaders,
  248. dwHeadersLength,
  249. lpOptional,
  250. dwOptionalLength,
  251. 0,
  252. 0);
  253. fFinish = TRUE;
  254. break;
  255. default:
  256. fResult = FALSE;
  257. fFinish = TRUE;
  258. break;
  259. }
  260. } while (!fFinish);
  261. DEBUG_LEAVE(fResult);
  262. return fResult;
  263. }
  264. PUBLIC BOOL HTTPCACHE_REQUEST::ReceiveResponse(LPVOID lpBuffersOut)
  265. {
  266. DEBUG_ENTER((DBG_CACHE,
  267. Bool,
  268. "HTTPCACHE_REQUEST::ReceiveResponse",
  269. NULL
  270. ));
  271. BOOL fResult = FALSE;
  272. switch (_nextState)
  273. {
  274. case PREPARE_READ_FROM_CACHE:
  275. _nextState = BEGIN_CACHE_READ;
  276. fResult = TRUE;
  277. break;
  278. case PREPARE_DOWNLOAD_FROM_INET:
  279. _nextState = BEGIN_DOWNLOAD_FROM_INET;
  280. fResult = WinHttpReceiveResponse(_hRequest, lpBuffersOut);
  281. break;
  282. default:
  283. fResult = FALSE;
  284. break;
  285. }
  286. DEBUG_LEAVE(fResult);
  287. return fResult;
  288. }
  289. PUBLIC BOOL HTTPCACHE_REQUEST::QueryDataAvailable(LPDWORD lpdwNumberOfBytesAvailable)
  290. {
  291. DEBUG_ENTER((DBG_CACHE,
  292. Bool,
  293. "HTTPCACHE_REQUEST::QueryDataAvailable",
  294. NULL
  295. ));
  296. BOOL fResult = FALSE;
  297. switch (_nextState)
  298. {
  299. case END_CACHE_READ:
  300. case END_READ_DATA:
  301. *lpdwNumberOfBytesAvailable = 0;
  302. fResult = TRUE;
  303. break;
  304. case BEGIN_CACHE_READ:
  305. // We assume that the cached file size is less than 4 GB (= 2^32). If the cached file is
  306. // really that big we might as well not cache it
  307. *lpdwNumberOfBytesAvailable = _pCacheEntryInfo->dwSizeLow;
  308. fResult = TRUE;
  309. break;
  310. case DOWNLOAD_FROM_INET_WITH_CACHE_WRITE:
  311. case DOWNLOAD_FROM_INET_WITHOUT_CACHE_WRITE:
  312. case BEGIN_DOWNLOAD_FROM_INET:
  313. fResult = WinHttpQueryDataAvailable(_hRequest, lpdwNumberOfBytesAvailable);
  314. break;
  315. default:
  316. break;
  317. }
  318. DEBUG_LEAVE(fResult);
  319. return fResult;
  320. }
  321. PUBLIC BOOL HTTPCACHE_REQUEST::ReadData(LPVOID lpBuffer,
  322. DWORD dwNumberOfBytesToRead,
  323. LPDWORD lpdwNumberOfBytesRead)
  324. {
  325. DEBUG_ENTER((DBG_CACHE,
  326. Bool,
  327. "HTTPCACHE_REQUEST::ReadData",
  328. NULL
  329. ));
  330. BOOL fFinish = FALSE;
  331. BOOL fResult = FALSE;
  332. do
  333. {
  334. switch (_nextState)
  335. {
  336. case BEGIN_CACHE_READ:
  337. DEBUG_PRINT(CACHE,
  338. INFO,
  339. ("BEGIN_CACHE_READ state in HTTPCACHE_REQUEST::ReadData\n"
  340. ));
  341. fResult = ReadDataFromCache(lpBuffer, dwNumberOfBytesToRead, lpdwNumberOfBytesRead);
  342. if (fResult == FALSE)
  343. _nextState = BEGIN_DOWNLOAD_FROM_INET;
  344. else if (*lpdwNumberOfBytesRead == 0)
  345. {
  346. _nextState = END_CACHE_READ;
  347. }
  348. else
  349. {
  350. _nextState = BEGIN_CACHE_READ; // just to be more clear
  351. fFinish = TRUE;
  352. }
  353. break;
  354. case BEGIN_DOWNLOAD_FROM_INET:
  355. DEBUG_PRINT(CACHE,
  356. INFO,
  357. ("BEGIN_DOWNLOAD_FROM_INET state in HTTPCACHE_REQUEST::ReadData\n"
  358. ));
  359. if (_dwCacheFlags & CACHE_FLAG_DISABLE_CACHE_WRITE)
  360. _nextState = DOWNLOAD_FROM_INET_WITHOUT_CACHE_WRITE;
  361. else
  362. _nextState = PREP_FOR_CACHE_WRITE;
  363. if (GetScheme() == INTERNET_SCHEME_HTTPS &&
  364. _dwCacheFlags & CACHE_FLAG_DISABLE_SSL_CACHING)
  365. {
  366. _nextState = DOWNLOAD_FROM_INET_WITHOUT_CACHE_WRITE; // SetPerUserItem(TRUE) ??;
  367. }
  368. break;
  369. case DOWNLOAD_FROM_INET_WITH_CACHE_WRITE:
  370. DEBUG_PRINT(CACHE,
  371. INFO,
  372. ("DOWNLOAD_FROM_INET_WITH_CACHE_WRITE state in HTTPCACHE_REQUEST::ReadData\n"
  373. ));
  374. fResult = WinHttpReadData(_hRequest,
  375. lpBuffer,
  376. dwNumberOfBytesToRead,
  377. lpdwNumberOfBytesRead);
  378. if (fResult)
  379. _nextState = WRITE_TO_CACHE_ENTRY;
  380. else
  381. {
  382. _nextState = COMMIT_PARTIAL_CACHE_ENTRY;
  383. fFinish = TRUE;
  384. }
  385. break;
  386. case DOWNLOAD_FROM_INET_WITHOUT_CACHE_WRITE:
  387. DEBUG_PRINT(CACHE,
  388. INFO,
  389. ("DOWNLOAD_FROM_INET_WITHOUT_CACHE_WRITE state in HTTPCACHE_REQUEST::ReadData\n"
  390. ));
  391. fResult = WinHttpReadData(_hRequest,
  392. lpBuffer,
  393. dwNumberOfBytesToRead,
  394. lpdwNumberOfBytesRead);
  395. fFinish = TRUE;
  396. break;
  397. case PREP_FOR_CACHE_WRITE:
  398. DEBUG_PRINT(CACHE,
  399. INFO,
  400. ("PREP_FOR_CACHE_WRITE state in HTTPCACHE_REQUEST::ReadData\n"
  401. ));
  402. INET_ASSERT((_dwCacheFlags & CACHE_FLAG_DISABLE_CACHE_WRITE) == FALSE);
  403. BOOL fNoCache;
  404. if (FCanWriteHTTP1_1ResponseToCache(&fNoCache))
  405. {
  406. // fNoCache indicates that a cache-control: no-store
  407. // or cache-control: no-cache is present, so we need
  408. // to make sure that the cache does not keep any
  409. // previous copies of the file as well
  410. if (fNoCache)
  411. DeleteUrlCacheEntryA(GetUrl());
  412. SetFilenameAndExtForCacheWrite();
  413. _RealCacheFileSize = 0;
  414. if (CreateCacheWriteFile())
  415. {
  416. _fCacheWriteInProgress = TRUE;
  417. _nextState = DOWNLOAD_FROM_INET_WITH_CACHE_WRITE;
  418. }
  419. else
  420. {
  421. _nextState = DOWNLOAD_FROM_INET_WITHOUT_CACHE_WRITE;
  422. }
  423. }
  424. else
  425. {
  426. _nextState = DOWNLOAD_FROM_INET_WITHOUT_CACHE_WRITE;
  427. }
  428. break;
  429. case WRITE_TO_CACHE_ENTRY:
  430. DEBUG_PRINT(CACHE,
  431. INFO,
  432. ("WRITE_TO_CACHE_ENTRY state in HTTPCACHE_REQUEST::ReadData\n"
  433. ));
  434. if (*lpdwNumberOfBytesRead == 0)
  435. _nextState = END_CACHE_WRITE;
  436. else
  437. {
  438. if ((fResult = WriteToCacheFile((LPBYTE) lpBuffer, dwNumberOfBytesToRead, lpdwNumberOfBytesRead)) == TRUE)
  439. {
  440. _nextState = DOWNLOAD_FROM_INET_WITH_CACHE_WRITE;
  441. fFinish = TRUE;
  442. }
  443. else
  444. {
  445. _nextState = COMMIT_PARTIAL_CACHE_ENTRY;
  446. }
  447. }
  448. break;
  449. case COMMIT_PARTIAL_CACHE_ENTRY:
  450. DEBUG_PRINT(CACHE,
  451. INFO,
  452. ("COMMIT_PARTIAL_CACHE_ENTRY state in HTTPCACHE_REQUEST::ReadData\n"
  453. ));
  454. if (!CommitCacheFileEntry(FALSE))
  455. // The partial entry cannot be committed to the cache, so let's
  456. // delete it in the destructor so it'll not become a stale entry
  457. _fDeleteWriteFile = TRUE;
  458. else
  459. _fDeleteWriteFile = FALSE;
  460. _fCacheWriteInProgress = FALSE;
  461. _nextState = END_READ_DATA;
  462. fResult = TRUE;
  463. fFinish = TRUE;
  464. break;
  465. case END_CACHE_WRITE:
  466. DEBUG_PRINT(CACHE,
  467. INFO,
  468. ("END_CACHE_WRITE state in HTTPCACHE_REQUEST::ReadData\n"
  469. ));
  470. if (!CommitCacheFileEntry(TRUE))
  471. // The file cannot be committed to the cache, so let's
  472. // delete it in the destructor so it'll not become a stale entry
  473. _fDeleteWriteFile = TRUE;
  474. else
  475. _fDeleteWriteFile = FALSE;
  476. _fCacheWriteInProgress = FALSE;
  477. _nextState = END_READ_DATA;
  478. fResult = TRUE;
  479. fFinish = TRUE;
  480. break;
  481. case END_CACHE_READ:
  482. DEBUG_PRINT(CACHE,
  483. INFO,
  484. ("END_CACHE_READ state in HTTPCACHE_REQUEST::ReadData\n"
  485. ));
  486. // Close the cache read file handle
  487. CloseCacheReadStream();
  488. _fCacheReadInProgress = FALSE;
  489. if (_fIsPartialCache == TRUE)
  490. {
  491. _nextState = BEGIN_DOWNLOAD_FROM_INET;
  492. _fIsPartialCache = FALSE;
  493. }
  494. else
  495. _nextState = END_READ_DATA;
  496. fResult = TRUE;
  497. fFinish = TRUE;
  498. break;
  499. case END_READ_DATA:
  500. DEBUG_PRINT(CACHE,
  501. INFO,
  502. ("END_READ_DATA state\n in HTTPCACHE_REQUEST::ReadData"
  503. ));
  504. lpBuffer = (LPVOID)'\0';
  505. *lpdwNumberOfBytesRead = 0;
  506. fResult = TRUE;
  507. fFinish = TRUE;
  508. break;
  509. // If we ever got here, we REALLY SHOULD PANIC!! Fix this later
  510. default:
  511. DEBUG_PRINT(CACHE,
  512. INFO,
  513. ("HTTPCACHE_REQUEST::ReadData FSM is in bogus state\n"
  514. ));
  515. fResult = WinHttpReadData(_hRequest, lpBuffer, dwNumberOfBytesToRead, lpdwNumberOfBytesRead);
  516. fFinish = TRUE;
  517. break;
  518. }
  519. } while (!fFinish);
  520. // Before you exit this loop, make sure you set fResult to the intended value
  521. DEBUG_LEAVE(fResult);
  522. return fResult;
  523. }
  524. PUBLIC BOOL HTTPCACHE_REQUEST::CloseRequestHandle()
  525. {
  526. DEBUG_ENTER((DBG_CACHE,
  527. Bool,
  528. "HTTPCACHE_REQUEST::CloseHandle",
  529. NULL
  530. ));
  531. BOOL fResult = FALSE;
  532. switch (_nextState)
  533. {
  534. case DOWNLOAD_FROM_INET_WITH_CACHE_WRITE:
  535. // The idea here is that if the user calls WinHttpCloseHandle
  536. // before the full content has been downloaded, then we
  537. // should try to commit it as a partial entry for later retrieval
  538. if (!CommitCacheFileEntry(FALSE))
  539. // The file cannot be committed to the cache, so let's
  540. // delete it in the destructor so it'll not become a stale entry
  541. _fDeleteWriteFile = TRUE;
  542. else
  543. _fDeleteWriteFile = FALSE;
  544. _fCacheWriteInProgress = FALSE;
  545. // intentional fall through;
  546. case END_READ_DATA:
  547. default:
  548. fResult = WinHttpCloseHandle(_hRequest);
  549. break;
  550. }
  551. DEBUG_LEAVE(fResult);
  552. return fResult;
  553. }
  554. ////////////////////////////////////////////////////////////////////////
  555. //
  556. // Miscelleneous utility functions
  557. //
  558. //
  559. //
  560. /***
  561. *char *StrTokEx(pstring, control) - tokenize string with delimiter in control
  562. *
  563. *Purpose:
  564. * StrTokEx considers the string to consist of a sequence of zero or more
  565. * text tokens separated by spans of one or more control chars. the first
  566. * call, with string specified, returns a pointer to the first char of the
  567. * first token, and will write a null char into pstring immediately
  568. * following the returned token. when no tokens remain
  569. * in pstring a NULL pointer is returned. remember the control chars with a
  570. * bit map, one bit per ascii char. the null char is always a control char.
  571. *
  572. *Entry:
  573. * char **pstring - ptr to ptr to string to tokenize
  574. * char *control - string of characters to use as delimiters
  575. *
  576. *Exit:
  577. * returns pointer to first token in string,
  578. * returns NULL when no more tokens remain.
  579. * pstring points to the beginning of the next token.
  580. *
  581. *WARNING!!!
  582. * upon exit, the first delimiter in the input string will be replaced with '\0'
  583. *
  584. *******************************************************************************/
  585. char * StrTokExA (char ** pstring, const char * control)
  586. {
  587. unsigned char *str;
  588. const unsigned char *ctrl = (const unsigned char *)control;
  589. unsigned char map[32];
  590. int count;
  591. char *tokenstr;
  592. if(*pstring == NULL)
  593. return NULL;
  594. /* Clear control map */
  595. for (count = 0; count < 32; count++)
  596. map[count] = 0;
  597. /* Set bits in delimiter table */
  598. do
  599. {
  600. map[*ctrl >> 3] |= (1 << (*ctrl & 7));
  601. } while (*ctrl++);
  602. /* Initialize str. */
  603. str = (unsigned char *)*pstring;
  604. /* Find beginning of token (skip over leading delimiters). Note that
  605. * there is no token if this loop sets str to point to the terminal
  606. * null (*str == '\0') */
  607. while ( (map[*str >> 3] & (1 << (*str & 7))) && *str )
  608. str++;
  609. tokenstr = (char *)str;
  610. /* Find the end of the token. If it is not the end of the string,
  611. * put a null there. */
  612. for ( ; *str ; str++ )
  613. {
  614. if ( map[*str >> 3] & (1 << (*str & 7)) )
  615. {
  616. *str++ = '\0';
  617. break;
  618. }
  619. }
  620. /* string now points to beginning of next token */
  621. *pstring = (char *)str;
  622. /* Determine if a token has been found. */
  623. if ( tokenstr == (char *)str )
  624. return NULL;
  625. else
  626. return tokenstr;
  627. }
  628. #define EXE_EXTENSION TEXT(".exe")
  629. #define DLL_EXTENSION TEXT(".dll")
  630. #define CGI_EXTENSION TEXT(".cgi")
  631. LPSTR GetFileExtensionFromUrl(
  632. IN LPSTR lpszUrl,
  633. IN OUT LPDWORD lpdwLength)
  634. /*++
  635. Routine Description:
  636. This routine returns a possible file extension from a URL
  637. It does this by walking back from the end till the first dot.
  638. Arguments:
  639. lpszUrl Url to derive the extension from
  640. lpdwLength max length of the extension expected
  641. Returns:
  642. NULL if no dot within the passed in length or a forward slash or a
  643. backward slash encountered before the dot. Otherwise returns a pointer
  644. pointing past the dot in the url string
  645. Comments:
  646. --*/
  647. {
  648. const char vszInvalidFilenameChars[] = "<>\\\"/:|?*";
  649. INET_ASSERT(lpszUrl && lpdwLength);
  650. if (!lpszUrl)
  651. {
  652. *lpdwLength = 0;
  653. return NULL;
  654. }
  655. LPSTR pszPeriod = NULL;
  656. BOOL fContinue = TRUE;
  657. // Scanning from left to right, note where we last saw a period.
  658. // If we see a character that cannot be in an extension, and we've seen a period, forget
  659. // about the period.
  660. // Repeat this until we've reached the end of the url, a question mark (query) or hash (fragment)
  661. // 1.6.98: _However_, if the file extension we've discovered is either .dll or .exe,
  662. // we'll continue to scan beyond the query mark for a file extension.
  663. // 1.20.98: And if we find no extension before the question mark, we'll look after it, then.
  664. while (fContinue)
  665. {
  666. switch (*lpszUrl)
  667. {
  668. case TEXT('.'):
  669. pszPeriod = lpszUrl;
  670. break;
  671. case TEXT('?'):
  672. if (pszPeriod)
  673. {
  674. if ((!StrCmpNI(pszPeriod, EXE_EXTENSION, ARRAY_ELEMENTS(EXE_EXTENSION)-1))
  675. || (!StrCmpNI(pszPeriod, DLL_EXTENSION, ARRAY_ELEMENTS(DLL_EXTENSION)-1))
  676. || (!StrCmpNI(pszPeriod, CGI_EXTENSION, ARRAY_ELEMENTS(CGI_EXTENSION)-1)))
  677. {
  678. pszPeriod = NULL;
  679. break;
  680. }
  681. }
  682. else
  683. {
  684. break;
  685. }
  686. case TEXT('#'):
  687. case TEXT('\0'):
  688. fContinue = FALSE;
  689. break;
  690. default:
  691. if (pszPeriod && strchr(vszInvalidFilenameChars, *lpszUrl))
  692. {
  693. pszPeriod = NULL;
  694. }
  695. }
  696. lpszUrl++;
  697. }
  698. // This will be off by one
  699. lpszUrl--;
  700. if (pszPeriod)
  701. {
  702. if (*lpdwLength < (DWORD)(lpszUrl-pszPeriod))
  703. {
  704. pszPeriod = NULL;
  705. }
  706. else
  707. {
  708. pszPeriod++;
  709. *lpdwLength = (DWORD)(lpszUrl-pszPeriod);
  710. }
  711. }
  712. return pszPeriod;
  713. }
  714. // This function and the #define should be moved to registry.cxx
  715. #define MIME_TO_FILE_EXTENSION_KEY "MIME\\Database\\Content Type\\"
  716. #define EXTENSION_VALUE "Extension"
  717. PRIVATE BOOL GetFileExtensionFromMimeType(
  718. LPCSTR lpszMimeType,
  719. DWORD dwMimeLen,
  720. LPSTR lpszFileExtension,
  721. LPDWORD lpdwExtLen
  722. )
  723. {
  724. HKEY hKey = NULL;
  725. LPSTR lpszMimeKey = (LPSTR)_alloca(sizeof(MIME_TO_FILE_EXTENSION_KEY)+dwMimeLen);
  726. memcpy(lpszMimeKey, MIME_TO_FILE_EXTENSION_KEY,
  727. sizeof(MIME_TO_FILE_EXTENSION_KEY)-1);
  728. memcpy(lpszMimeKey + sizeof(MIME_TO_FILE_EXTENSION_KEY) - 1, lpszMimeType,
  729. dwMimeLen);
  730. lpszMimeKey[sizeof(MIME_TO_FILE_EXTENSION_KEY) + dwMimeLen - 1] = '\0';
  731. if (REGOPENKEYEX(HKEY_CLASSES_ROOT,
  732. lpszMimeKey,
  733. 0,
  734. KEY_QUERY_VALUE,
  735. &hKey)==ERROR_SUCCESS)
  736. {
  737. DWORD dwType, dwError = RegQueryValueEx(hKey,
  738. EXTENSION_VALUE,
  739. NULL,
  740. &dwType,
  741. (LPBYTE)lpszFileExtension,
  742. lpdwExtLen);
  743. REGCLOSEKEY(hKey);
  744. return (dwError==ERROR_SUCCESS);
  745. }
  746. return FALSE;
  747. }
  748. PRIVATE BOOL FExcludedMimeType(
  749. IN LPSTR lpszMimeType,
  750. IN DWORD dwMimeTypeSize
  751. )
  752. {
  753. LPCSTR rgszExcludedMimeTypes[] = {
  754. "multipart/mixed",
  755. "multipart/x-mixed-replace",
  756. "multipart/x-byteranges"
  757. };
  758. const DWORD rgdwExcludedMimeTypeSizes[] = {
  759. sizeof("multipart/mixed") - 1,
  760. sizeof("multipart/x-mixed-replace") - 1,
  761. sizeof("multipart/x-byteranges") - 1
  762. };
  763. DWORD i;
  764. LPCSTR * lprgszMimeExcludeTable = rgszExcludedMimeTypes;
  765. DWORD dwMimeExcludeCount = (sizeof(rgszExcludedMimeTypes)/sizeof(LPSTR));
  766. const DWORD *lprgdwMimeExcludeTableOfSizes = rgdwExcludedMimeTypeSizes;
  767. for (i = 0; i < dwMimeExcludeCount; ++i) {
  768. if ((dwMimeTypeSize == lprgdwMimeExcludeTableOfSizes[i]) &&
  769. !strnicmp(lpszMimeType,
  770. lprgszMimeExcludeTable[i],
  771. lprgdwMimeExcludeTableOfSizes[i])) {
  772. return TRUE;
  773. }
  774. }
  775. return FALSE;
  776. }