Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3282 lines
86 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1994 **/
  4. /**********************************************************************/
  5. /*
  6. doget.cxx
  7. This module contains the code for the GET and HEAD verb
  8. FILE HISTORY:
  9. Johnl 23-Aug-1994 Created
  10. Phillich 24-Jan-1996 Added support for NCSA map files
  11. Phillich 20-Feb-1996 Added support for byte ranges
  12. */
  13. #include "w3p.hxx"
  14. //
  15. // Private constants.
  16. //
  17. //
  18. // Computes the square of a number. Used for circle image maps
  19. //
  20. #define SQR(x) ((x) * (x))
  21. //
  22. // Maximum number of vertices in image map polygon
  23. //
  24. #define MAXVERTS 160
  25. //
  26. // Point offset of x and y
  27. //
  28. #define X 0
  29. #define Y 1
  30. //
  31. // Private globals.
  32. //
  33. #define BOUNDARY_STRING_DEFINITION "[lka9uw3et5vxybtp87ghq23dpu7djv84nhls9p]"
  34. // The boundary string is preceded by a line delimiter ( cf RFC 1521 )
  35. // This can be set to "\n" instead of "\r\n" as Navigator 2.0 apparently handles
  36. // all bytes before the "\n" as part of the reply body.
  37. #define BOUNDARY_STRING "\r\n--" BOUNDARY_STRING_DEFINITION "\r\n"
  38. #define LAST_BOUNDARY_STRING "\r\n--" BOUNDARY_STRING_DEFINITION "--\r\n\r\n"
  39. // addition to 1st delimiter of boundary string ( can be "\r" if not included
  40. // in BOUNDARY_STRING )
  41. #define DELIMIT_FIRST ""
  42. #define ADJ_FIRST (2-(sizeof(DELIMIT_FIRST)-1))
  43. #define MMIME_TYPE_1 "Content-Type: "
  44. #define MMIME_TYPE_2 "\r\n"
  45. #define MMIME_TYPE MMIME_TYPE_1 \
  46. "%s" \
  47. MMIME_TYPE_2
  48. char g_achMMimeTypeFmt[] = MMIME_TYPE_1 MMIME_TYPE_2;
  49. #define MMIME_RANGE_1 "Content-Range: bytes "
  50. #define MMIME_RANGE_2 "-"
  51. #define MMIME_RANGE_3 "/"
  52. #define MMIME_RANGE_4 "\r\n\r\n"
  53. #define MMIME_RANGE MMIME_RANGE_1 \
  54. "%u" \
  55. MMIME_RANGE_2 \
  56. "%u" \
  57. MMIME_RANGE_3 \
  58. "%u" \
  59. MMIME_RANGE_4
  60. char g_achMMimeRangeFmt[] = MMIME_RANGE_1
  61. MMIME_RANGE_2
  62. MMIME_RANGE_3
  63. MMIME_RANGE_4;
  64. #define MIN_ADDL_BUF_HDR_SIZE_CACHED (sizeof("Date: ") - 1 + \
  65. sizeof("Mon, 00 Jan 0000 00:00:00 GMT\r\n") - 1 +\
  66. MAX_SIZE_HTTP_INFO)
  67. #define MIN_ADDL_BUF_HDR_SIZE (sizeof("Content-Type: \r\n") - 1 + \
  68. sizeof("Accept-Ranges: bytes\r\n") - 1 + \
  69. sizeof("Last-Modified: ") - 1 +\
  70. sizeof("Mon, 00 Jan 0000 00:00:00 GMT\r\n") - 1 +\
  71. sizeof("ETag: W/\r\n") - 1 + \
  72. MAX_ETAG_BUFFER_LENGTH + \
  73. sizeof("Content-Length: 4294967295\r\n\r\n") - 1)
  74. #define RANGE_ADDL_BUF_HDR_SIZE (sizeof("Content-Type: multipart/x-byteranges; boundary=")\
  75. - 1 + sizeof(BOUNDARY_STRING) - 1 + \
  76. sizeof("Date: ") - 1 + \
  77. sizeof("Mon, 00 Jan 0000 00:00:00 GMT\r\n") - 1 +\
  78. sizeof("Accept-Ranges: bytes\r\n") - 1 + \
  79. sizeof("Last-Modified: ") - 1 +\
  80. sizeof("Mon, 00 Jan 0000 00:00:00 GMT\r\n") - 1 +\
  81. sizeof("ETag: W/\r\n") - 1 + \
  82. MAX_ETAG_BUFFER_LENGTH + \
  83. sizeof("Content-Length: 4294967295\r\n\r\n") - 1)
  84. //
  85. // Private prototypes.
  86. //
  87. BOOL SearchMapFile( LPTS_OPEN_FILE_INFO gFile,
  88. CHAR * pchFile,
  89. TSVC_CACHE * pTsvcCache,
  90. HANDLE hToken,
  91. INT x,
  92. INT y,
  93. STR * pstrURL,
  94. BOOL * pfFound,
  95. BOOL fmayCacheAccessToken );
  96. int pointinpoly(int x, int y, double pgon[MAXVERTS][2]);
  97. INT GetNumber( CHAR * * ppch );
  98. DWORD NbDigit( DWORD dw );
  99. //
  100. // Public functions.
  101. //
  102. //
  103. // Private functions.
  104. //
  105. // Forward references.
  106. extern BOOL DisposeOpenURIFileInfo(IN PVOID pvOldBlock);
  107. DWORD NbDigit( DWORD dw )
  108. {
  109. if ( dw < 10 )
  110. {
  111. return 1;
  112. }
  113. else if ( dw < 100 )
  114. {
  115. return 2;
  116. }
  117. else if ( dw < 1000 )
  118. {
  119. return 3;
  120. }
  121. else if ( dw < 10000 )
  122. {
  123. return 4;
  124. }
  125. else if ( dw < 100000 )
  126. {
  127. return 5;
  128. }
  129. else if ( dw < 1000000 )
  130. {
  131. return 6;
  132. }
  133. DWORD cD = 7;
  134. for ( dw /= 10000000 ; dw ; ++cD )
  135. {
  136. dw /= 10;
  137. }
  138. return cD;
  139. }
  140. LPSYSTEMTIME
  141. MinSystemTime(
  142. LPSYSTEMTIME Now,
  143. LPSYSTEMTIME Other
  144. )
  145. /*++
  146. Routine Descriptions:
  147. Compare to systemtime dates, and return a pointer to the earlier one.
  148. Arguments:
  149. Now - The current date.
  150. Other - Other date to compare.
  151. Return Value:
  152. Pointer to the earlier of Now and Other.
  153. --*/
  154. {
  155. if (Now->wYear > Other->wYear)
  156. {
  157. return Other;
  158. }
  159. else
  160. {
  161. if (Now->wYear == Other->wYear)
  162. {
  163. if (Now->wMonth > Other->wMonth)
  164. {
  165. return Other;
  166. }
  167. else
  168. {
  169. if (Now->wMonth == Other->wMonth)
  170. {
  171. if (Now->wDay > Other->wDay)
  172. {
  173. return Other;
  174. }
  175. else
  176. {
  177. if (Now->wDay == Other->wDay)
  178. {
  179. if (Now->wHour > Other->wHour)
  180. {
  181. return Other;
  182. }
  183. else
  184. {
  185. if (Now->wHour == Other->wHour)
  186. {
  187. if (Now->wMinute > Other->wMinute)
  188. {
  189. return Other;
  190. }
  191. else
  192. {
  193. if (Now->wMinute == Other->wMinute)
  194. {
  195. if (Now->wSecond > Other->wSecond)
  196. {
  197. return Other;
  198. }
  199. else
  200. {
  201. if (Now->wSecond == Other->wSecond)
  202. {
  203. if (Now->wMilliseconds >= Other->wMilliseconds)
  204. {
  205. return Other;
  206. }
  207. else
  208. {
  209. return Now;
  210. }
  211. }
  212. else
  213. {
  214. return Now;
  215. }
  216. }
  217. }
  218. else
  219. {
  220. return Now;
  221. }
  222. }
  223. }
  224. else
  225. {
  226. return Now;
  227. }
  228. }
  229. }
  230. else
  231. {
  232. return Now;
  233. }
  234. }
  235. }
  236. else
  237. {
  238. return Now;
  239. }
  240. }
  241. }
  242. else
  243. {
  244. return Now;
  245. }
  246. }
  247. }
  248. BOOL
  249. HTTP_REQUEST::DoTraceCk(
  250. BOOL * pfFinished
  251. )
  252. /*++
  253. Routine Description:
  254. Handle a TRACECK request ( used by WolfPack as non-logged TRACE request )
  255. Basically echo the client request as a message body
  256. Arguments:
  257. None
  258. Return Value:
  259. TRUE if success, else FALSE
  260. --*/
  261. {
  262. return DoTrace( pfFinished );
  263. }
  264. BOOL
  265. HTTP_REQUEST::DoTrace(
  266. BOOL * pfFinished
  267. )
  268. /*++
  269. Routine Description:
  270. Handle a TRACE request ( cf HTTP 1.1 spec )
  271. Basically echo the client request as a message body
  272. Arguments:
  273. None
  274. Return Value:
  275. TRUE if success, else FALSE
  276. --*/
  277. {
  278. BUFFER buHeader;
  279. BOOL fHandled = FALSE;
  280. LPSTR pszResp;
  281. LPSTR pszTail;
  282. UINT cHeaderSize;
  283. UINT cMsgSize;
  284. DWORD dwWrt;
  285. DWORD cbContentLength;
  286. //
  287. // build response header
  288. //
  289. if ( !buHeader.Resize( MIN_BUFFER_SIZE_FOR_HEADERS ) )
  290. {
  291. return FALSE;
  292. }
  293. if ( !BuildBaseResponseHeader( &buHeader,
  294. &fHandled,
  295. NULL,
  296. 0 ))
  297. {
  298. return FALSE;
  299. }
  300. if ( fHandled )
  301. {
  302. return TRUE;
  303. }
  304. cHeaderSize = strlen( (PSTR)buHeader.QueryPtr() );
  305. if (!buHeader.Resize(cHeaderSize + sizeof("Content-Type: message/http\r\n")
  306. - 1 + sizeof("Content-Length: 4294967295\r\n\r\n") - 1))
  307. {
  308. return FALSE;
  309. }
  310. pszResp = (PSTR) buHeader.QueryPtr();
  311. pszTail = pszResp + cHeaderSize;
  312. cMsgSize = _cbClientRequest + _cbContentLength;
  313. APPEND_STRING( pszTail, "Content-Type: message/http\r\n" );
  314. pszTail += wsprintf( pszTail,
  315. "Content-Length: %lu\r\n\r\n",
  316. cMsgSize );
  317. SetState( HTR_DONE, HT_OK, NO_ERROR );
  318. //
  319. // send response header
  320. //
  321. if ( !SendHeader( (CHAR *) buHeader.QueryPtr(),
  322. (DWORD) (pszTail - (PSTR)buHeader.QueryPtr()),
  323. IO_FLAG_SYNC,
  324. pfFinished ))
  325. {
  326. return FALSE;
  327. }
  328. if ( *pfFinished )
  329. return TRUE;
  330. //
  331. // send echo of request headers
  332. // NOTE : we're using the original client buffer because a filter may have
  333. // modified the server response buffer
  334. //
  335. cbContentLength = _cbContentLength;
  336. if (! WriteFile( (PSTR) _bufClientRequest.QueryPtr(),
  337. _cbClientRequest,
  338. &dwWrt,
  339. cbContentLength ? IO_FLAG_SYNC : IO_FLAG_ASYNC ) )
  340. {
  341. return FALSE;
  342. }
  343. //
  344. // send echo of request message body
  345. //
  346. if ( cbContentLength &&
  347. !WriteFile( (PSTR)_bufClientRequest.QueryPtr()
  348. + _cbClientRequest,
  349. cbContentLength,
  350. NULL,
  351. IO_FLAG_ASYNC ) )
  352. {
  353. return FALSE;
  354. }
  355. return TRUE;
  356. }
  357. /*******************************************************************
  358. NAME: HTTP_REQUEST::DoGet
  359. SYNOPSIS: Transmits a the specified file or directory in response
  360. to a Get or Head request
  361. RETURNS: TRUE if successful, FALSE on error
  362. FALSE should be returned for fatal errors (memory etc),
  363. a server error response will be sent with error text from
  364. GetLastError()
  365. For other errors (access denied, path not found etc)
  366. disconnect with status should be called and TRUE should be
  367. returned.
  368. If no further processing is needed (and the caller needs to
  369. setup for the next request), set *pfFinished to TRUE
  370. NOTES: The file handle gets closed during destruction
  371. We never retrieve a hidden file or directory. We will process
  372. hidden map files however.
  373. HISTORY:
  374. Johnl 29-Aug-1994 Created
  375. ********************************************************************/
  376. BOOL
  377. HTTP_REQUEST::DoGet(
  378. BOOL * pfFinished
  379. )
  380. {
  381. BOOL fHandled = FALSE;
  382. DWORD cbSizeLow;
  383. DWORD cbSizeHigh;
  384. BOOL fHidden;
  385. BOOL fMatches;
  386. DWORD dwMask;
  387. BOOL fSendFile = (QueryVerb() == HTV_GET);
  388. PW3_SERVER_INSTANCE pInstance = QueryW3Instance();
  389. DWORD err;
  390. //
  391. // Open the file (or directory)
  392. //
  393. IF_DEBUG( REQUEST )
  394. {
  395. DBGPRINTF((DBG_CONTEXT,
  396. "SendFileOrDir: Opening %s\n",
  397. _strPhysicalPath.QueryStr()));
  398. }
  399. if ( !IS_ACCESS_ALLOWED(READ) && !_fPossibleDefaultExecute )
  400. {
  401. DBGPRINTF((DBG_CONTEXT,
  402. "ACCESS_DENIED. No Read Permission for URL %s (Physical Path: %s)\n",
  403. _strURL.QueryStr(),
  404. _strPhysicalPath.QueryStr()));
  405. SetState( HTR_DONE, HT_FORBIDDEN, ERROR_ACCESS_DENIED );
  406. Disconnect( HT_FORBIDDEN,
  407. IDS_READ_ACCESS_DENIED,
  408. FALSE,
  409. pfFinished );
  410. return TRUE;
  411. }
  412. if (*_strURL.QueryStr() == '*')
  413. {
  414. // Don't allow GETs on the server URL.
  415. SetState( HTR_DONE, HT_BAD_REQUEST, ERROR_INVALID_PARAMETER );
  416. Disconnect( HT_BAD_REQUEST,
  417. NULL,
  418. FALSE,
  419. pfFinished );
  420. return TRUE;
  421. }
  422. if( _strURL.QueryCCH() > MAX_URI_LENGTH )
  423. {
  424. SetState( HTR_DONE, HT_URL_TOO_LONG, ERROR_INSUFFICIENT_BUFFER);
  425. Disconnect( HT_URL_TOO_LONG, IDS_URL_TOO_LONG, FALSE, pfFinished);
  426. return TRUE;
  427. }
  428. if ( _pURIInfo == NULL )
  429. {
  430. if ( !CacheUri( QueryW3Instance(),
  431. &_pURIInfo,
  432. _pMetaData,
  433. _strURL.QueryStr(),
  434. _strURL.QueryCCH(),
  435. &_strPhysicalPath,
  436. &_strUnmappedPhysicalPath ) )
  437. {
  438. return FALSE;
  439. }
  440. }
  441. if ( _pMetaData->QueryDoCache() && !_pMetaData->QueryVrError() )
  442. {
  443. _pGetFile = TsCreateFileFromURI(pInstance->GetTsvcCache(),
  444. _pURIInfo,
  445. QueryImpersonationHandle( FALSE ),
  446. TS_NOT_IMPERSONATED
  447. | ((_fClearTextPass || _fAnonymous) ? 0 :
  448. TS_DONT_CACHE_ACCESS_TOKEN)
  449. | (DoAccessCheckOnUrl()
  450. ? 0 : TS_NO_ACCESS_CHECK ),
  451. &err
  452. );
  453. }
  454. else
  455. {
  456. _pGetFile = TsCreateFile( pInstance->GetTsvcCache(),
  457. _strPhysicalPath.QueryStr(),
  458. QueryImpersonationHandle(),
  459. TS_NOT_IMPERSONATED );
  460. if (_pGetFile == NULL) {
  461. err = GetLastError();
  462. }
  463. }
  464. if ( _pGetFile == NULL ) {
  465. IF_DEBUG(ERROR) {
  466. DBGPRINTF((DBG_CONTEXT,
  467. "DoGetHeadAux: Failed to open %s, error %d\n",
  468. _strURL.QueryStr(),
  469. err ));
  470. }
  471. if ( err == ERROR_FILE_NOT_FOUND ||
  472. err == ERROR_PATH_NOT_FOUND ||
  473. err == ERROR_INVALID_NAME )
  474. {
  475. SetState( HTR_DONE, HT_NOT_FOUND, err );
  476. Disconnect( HT_NOT_FOUND, NO_ERROR, FALSE, pfFinished );
  477. return TRUE;
  478. }
  479. if (err == ERROR_INSUFFICIENT_BUFFER)
  480. {
  481. // Really means the file name was too long.
  482. SetState(HTR_DONE, HT_URL_TOO_LONG, err);
  483. Disconnect(HT_URL_TOO_LONG, IDS_URL_TOO_LONG, FALSE, pfFinished );
  484. return TRUE;
  485. }
  486. if ( err == ERROR_ACCESS_DENIED )
  487. {
  488. DBGPRINTF((DBG_CONTEXT,
  489. "ACCESS_DENIED. ACLs restricting acess to URL %s (Physical Path: %s)\n",
  490. _strURL.QueryStr(), _strPhysicalPath.QueryStr()));
  491. DBGPRINTF((DBG_CONTEXT,
  492. "User: %s, AuthorizationType: %s\n",
  493. _strUserName.QueryStr(), _strAuthType.QueryStr()));
  494. SetDeniedFlags( SF_DENIED_RESOURCE );
  495. }
  496. return FALSE;
  497. }
  498. fHidden = ((_pGetFile->QueryAttributes() & FILE_ATTRIBUTE_HIDDEN) != 0);
  499. //
  500. // If the file is a directory, then we may need to do a directory listing
  501. //
  502. if ( _pGetFile->QueryAttributes() & FILE_ATTRIBUTE_DIRECTORY &&
  503. (QueryVerb() == HTV_GET || QueryVerb() == HTV_HEAD) )
  504. {
  505. DWORD dirBrowFlags;
  506. if ( fHidden )
  507. {
  508. SetState( HTR_DONE, HT_NOT_FOUND, ERROR_PATH_NOT_FOUND );
  509. Disconnect( HT_NOT_FOUND, NO_ERROR, FALSE, pfFinished );
  510. return TRUE;
  511. }
  512. //
  513. // If a default file is in the directory and the feature is enabled,
  514. // then return the default file to the user
  515. //
  516. dirBrowFlags = QueryDirBrowseFlags();
  517. if ( dirBrowFlags & DIRBROW_LOADDEFAULT )
  518. {
  519. if ( !CheckDefaultLoad( &_strPhysicalPath, &fHandled, pfFinished ))
  520. {
  521. if ( GetLastError() == ERROR_ACCESS_DENIED )
  522. {
  523. SetDeniedFlags( SF_DENIED_RESOURCE );
  524. }
  525. return FALSE;
  526. }
  527. if ( fHandled || *pfFinished )
  528. {
  529. return TRUE;
  530. }
  531. }
  532. //
  533. // We're doing a directory listing, so send the directory list
  534. // with the response headers. The request is finished at that
  535. // point.
  536. //
  537. SetState( HTR_DONE, HT_OK, NO_ERROR );
  538. if ( dirBrowFlags & DIRBROW_ENABLED )
  539. {
  540. return DoDirList( _strPhysicalPath, QueryRespBuf(), pfFinished );
  541. }
  542. DBGPRINTF((DBG_CONTEXT,
  543. "[DoDirList] Denying request for directory browsing\n"));
  544. SetLogStatus( HT_FORBIDDEN, ERROR_ACCESS_DENIED );
  545. Disconnect( HT_FORBIDDEN, IDS_DIR_LIST_DENIED, FALSE, pfFinished );
  546. return TRUE;
  547. }
  548. //
  549. // We're dealing with a file. Is it an ismap request?
  550. //
  551. if ( _GatewayType == GATEWAY_MAP )
  552. {
  553. BOOL fFound = FALSE;
  554. //
  555. // This may be an ISMAP request so check the parameters and process
  556. // the map file if it is. _hGetFile is a map file if it is.
  557. //
  558. if ( !ProcessISMAP( _pGetFile,
  559. _strPhysicalPath.QueryStr(),
  560. QueryRespBuf(),
  561. &fFound,
  562. &fHandled ))
  563. {
  564. return FALSE;
  565. }
  566. if ( fHandled )
  567. {
  568. return TRUE;
  569. }
  570. if ( fFound )
  571. {
  572. SetState( HTR_DONE, HT_OK, NO_ERROR );
  573. return SendHeader( QueryRespBufPtr(),
  574. (DWORD) -1,
  575. IsKeepConnSet() ?
  576. (IO_FLAG_ASYNC | IO_FLAG_AND_RECV) :
  577. IO_FLAG_ASYNC,
  578. pfFinished );
  579. }
  580. }
  581. if ( fHidden )
  582. {
  583. SetState( HTR_DONE, HT_NOT_FOUND, ERROR_FILE_NOT_FOUND );
  584. Disconnect( HT_NOT_FOUND, NO_ERROR, FALSE, pfFinished );
  585. return TRUE;
  586. }
  587. //
  588. // At this point we know the user wants to retrieve the file
  589. //
  590. // If we're currently using a weak ETag for this file, try to make it
  591. // strong.
  592. if (_pGetFile->WeakETag())
  593. {
  594. _pGetFile->MakeStrongETag();
  595. }
  596. //
  597. // If the client sent an If-* modifier, check it now. Skip if Custom Error
  598. //
  599. if ( _fIfModifier && !_bProcessingCustomError)
  600. {
  601. BOOL bReturn;
  602. if (CheckPreconditions(_pGetFile, pfFinished, &bReturn))
  603. {
  604. return bReturn;
  605. }
  606. }
  607. TCP_REQUIRE( _pGetFile->QuerySize( &cbSizeLow,
  608. &cbSizeHigh ));
  609. // check if range requested
  610. DWORD dwOffset;
  611. DWORD dwSizeToSend;
  612. BOOL fIsNxRange;
  613. _fAcceptRange = pInstance->IsAcceptByteRanges();
  614. if ( _fAcceptRange && !_strRange.IsEmpty() && fSendFile )
  615. {
  616. ProcessRangeRequest( &_strPhysicalPath,
  617. &dwOffset,
  618. &dwSizeToSend,
  619. &fIsNxRange );
  620. //
  621. // Error HT_RANGE_NOT_SATISFIABLE if we saw no valid ranges and we saw an
  622. // unsatisfiable byte range and no If-Range header was sent.
  623. //
  624. if ( (!_fProcessByteRange) && _fUnsatisfiableByteRange &&
  625. ( NULL == _HeaderList.FastMapQueryValue(HM_IFR)))
  626. {
  627. CHAR ach[17];
  628. STR * pstrAdditionalHeader = QueryAdditionalRespHeaders();
  629. _itoa( cbSizeLow, ach, 10 );
  630. pstrAdditionalHeader->Append("Content-Range: bytes */");
  631. pstrAdditionalHeader->Append(ach);
  632. pstrAdditionalHeader->Append("\r\n");
  633. SetState( HTR_DONE, HT_RANGE_NOT_SATISFIABLE, NO_ERROR );
  634. Disconnect( HT_RANGE_NOT_SATISFIABLE, NO_ERROR, FALSE, pfFinished );
  635. return TRUE;
  636. }
  637. if ( !BuildResponseHeader( QueryRespBuf(),
  638. &_strPhysicalPath,
  639. _pGetFile,
  640. &fHandled,
  641. NULL,
  642. pfFinished ))
  643. {
  644. return FALSE;
  645. }
  646. }
  647. //
  648. // Build the header response based on file type and client
  649. // requests
  650. //
  651. // It's possible we may not want to send the file (if the client
  652. // doesn't have a needed MIME type for example)
  653. //
  654. else if ( !BuildFileResponseHeader( QueryRespBuf(),
  655. &_strPhysicalPath,
  656. _pGetFile,
  657. &fHandled,
  658. pfFinished ))
  659. {
  660. return FALSE;
  661. }
  662. if ( fHandled )
  663. {
  664. return TRUE;
  665. }
  666. //
  667. // Send the header response and file and cleanup the request
  668. //
  669. if ( !fSendFile )
  670. {
  671. SetState( HTR_DONE, HT_OK, NO_ERROR );
  672. return SendHeader( QueryRespBufPtr(),
  673. QueryRespBufCB(),
  674. (IsKeepConnSet() &&
  675. (_cbBytesReceived == _cbClientRequest)) ?
  676. (IO_FLAG_ASYNC | IO_FLAG_AND_RECV) :
  677. IO_FLAG_ASYNC,
  678. pfFinished );
  679. }
  680. //
  681. // Refuse requests for files greater then four gigs
  682. //
  683. if ( cbSizeHigh )
  684. {
  685. SetState( HTR_DONE, HT_NOT_SUPPORTED, ERROR_NOT_SUPPORTED );
  686. Disconnect( HT_NOT_SUPPORTED, NO_ERROR, FALSE, pfFinished );
  687. return TRUE;
  688. }
  689. //
  690. // Notify filters of the headers we're about to send
  691. //
  692. if ( _Filter.IsNotificationNeeded( SF_NOTIFY_SEND_RESPONSE,
  693. IsSecurePort() ))
  694. {
  695. if ( !_Filter.NotifySendHeaders( QueryRespBufPtr(),
  696. pfFinished,
  697. NULL,
  698. QueryRespBuf() ))
  699. {
  700. return FALSE;
  701. }
  702. if ( *pfFinished )
  703. {
  704. return TRUE;
  705. }
  706. }
  707. if ( _fProcessByteRange )
  708. {
  709. if ( fIsNxRange )
  710. {
  711. SetState( HTR_RANGE, HT_RANGE, NO_ERROR );
  712. }
  713. return SendRange( QueryRespBufCB(), dwOffset, dwSizeToSend, !fIsNxRange );
  714. }
  715. else
  716. {
  717. DWORD dwIOFlags = IO_FLAG_ASYNC;
  718. //
  719. // If a filter needs a send response notification, keep the socket open
  720. // and make sure we receive the TransmitFile completion
  721. // otherwise if this is not a keep-alive connection, tell ATQ to
  722. // disconnect after sending the file
  723. //
  724. if ( _Filter.IsNotificationNeeded( SF_NOTIFY_END_OF_REQUEST,
  725. IsSecurePort() ))
  726. {
  727. dwIOFlags |= IO_FLAG_NO_RECV;
  728. }
  729. else if ( !IsKeepConnSet() )
  730. {
  731. dwIOFlags |= TF_DISCONNECT | TF_REUSE_SOCKET;
  732. }
  733. SetState( HTR_DONE, HT_OK, NO_ERROR );
  734. //
  735. // If we've still got unprocessed data left in the buffer, don't
  736. // allow a receive after the transmit file.
  737. //
  738. if (_cbBytesReceived > _cbClientRequest)
  739. {
  740. dwIOFlags |= IO_FLAG_NO_RECV;
  741. }
  742. if ( !TransmitFile( _pGetFile,
  743. NULL,
  744. 0,
  745. cbSizeLow,
  746. dwIOFlags,
  747. QueryRespBufPtr(),
  748. QueryRespBufCB() ,
  749. QueryMetaData()->QueryFooter(),
  750. QueryMetaData()->QueryFooterLength())
  751. )
  752. {
  753. DBGPRINTF((DBG_CONTEXT,
  754. "DoGetHeadAux: TransmitFile failed sending header, error %d\n",
  755. GetLastError() ));
  756. return FALSE;
  757. }
  758. }
  759. return TRUE;
  760. }
  761. DWORD AToDW(
  762. LPSTR *ppRng,
  763. BOOL *pfIsB
  764. )
  765. /*++
  766. Routine Description:
  767. Convert ASCII to DWORD, set flag stating presence
  768. of a numeric value, update pointer to character stream
  769. Returns:
  770. DWORD value converted from ASCII
  771. Arguments:
  772. ppRng PSTR to numeric value, updated on return
  773. pfIsB flag set to TRUE if numeric value present on return
  774. History:
  775. Phillich 08-Feb-1996 Created
  776. --*/
  777. {
  778. LPSTR pRng = *ppRng;
  779. DWORD dwV = 0;
  780. if ( isdigit( (UCHAR)(*pRng) ) )
  781. {
  782. int c;
  783. while ( (c = *pRng) && isdigit( (UCHAR)c ) )
  784. {
  785. dwV = dwV * 10 + c - '0';
  786. ++pRng;
  787. }
  788. *pfIsB = TRUE;
  789. *ppRng = pRng;
  790. }
  791. else
  792. {
  793. *pfIsB = FALSE;
  794. }
  795. return dwV;
  796. }
  797. void
  798. HTTP_REQUEST::ProcessRangeRequest(
  799. STR * pstrPath,
  800. DWORD * pdwOffset,
  801. DWORD * pdwSizeToSend,
  802. BOOL * pfIsNxRange )
  803. /*++
  804. Routine Description:
  805. Process a range request, updating member variables
  806. Returns:
  807. VOID
  808. Arguments:
  809. pstrPath File being requested
  810. pdwOffset Range offset
  811. pdwSizeToSend Range size
  812. pfIsNxRange TRUE if valid next range exists
  813. History:
  814. Phillich 08-Feb-1996 Created
  815. --*/
  816. {
  817. DWORD cbSizeLow;
  818. DWORD cbSizeHigh;
  819. FILETIME tm;
  820. BOOL fEntireFile; // Indicates: Skip all Range headers. Send Entire File
  821. BOOL fIsLastRange; // Indicates: No valid range after this.
  822. //
  823. // Check range specified & optional UnlessModifiedSince
  824. //
  825. TCP_REQUIRE( _pGetFile->QueryLastWriteTime( &tm ));
  826. TCP_REQUIRE( _pGetFile->QuerySize( &cbSizeLow,
  827. &cbSizeHigh ));
  828. if ( !_liUnlessModifiedSince.QuadPart ||
  829. *(LONGLONG*)&tm <= _liUnlessModifiedSince.QuadPart )
  830. {
  831. //
  832. // Run through all valid ranges
  833. //
  834. DWORD cRanges = 0;
  835. DWORD dwContentLength = 0;
  836. DWORD dwFirstRangeEnd = 0;
  837. DWORD dwFx = 0;
  838. while ( ScanRange( &_dwRgNxOffset,
  839. &_dwRgNxSizeToSend,
  840. &fEntireFile,
  841. &fIsLastRange ))
  842. {
  843. _fProcessByteRange = TRUE; // At least one range to process
  844. if (fEntireFile)
  845. {
  846. //
  847. // Ignore Range headers. Send Full File.
  848. //
  849. _fUnsatisfiableByteRange = FALSE;
  850. _fProcessByteRange = FALSE;
  851. return;
  852. }
  853. cRanges++;
  854. if ( 1 == cRanges)
  855. {
  856. //
  857. // First valid range. Special processing
  858. //
  859. *pdwOffset = _dwRgNxOffset;
  860. *pdwSizeToSend = _cbMimeMultipart = _dwRgNxSizeToSend;
  861. if (!fIsLastRange)
  862. {
  863. //
  864. // There are more ranges left in the header. However they may or
  865. // may not be syntactically valid or satisfiable.
  866. //
  867. SelectMimeMapping( &_strReturnMimeType,
  868. pstrPath->QueryStr(),
  869. _pMetaData);
  870. dwFirstRangeEnd = _iRangeIdx; // Save this for restoring later.
  871. //
  872. // For each segment in the MIME multipart message the size of the MIME type
  873. // and the number of digits in document total size will be constant,
  874. // so compute them now.
  875. //
  876. dwFx = strlen( _strReturnMimeType.QueryStr() ) + NbDigit( cbSizeLow );
  877. dwContentLength = sizeof(BOUNDARY_STRING) - 1
  878. + _dwRgNxSizeToSend
  879. + sizeof(g_achMMimeTypeFmt) - 1
  880. + sizeof(g_achMMimeRangeFmt) - 1
  881. + NbDigit( _dwRgNxOffset )
  882. + NbDigit( _dwRgNxOffset + _dwRgNxSizeToSend - 1 )
  883. + dwFx;
  884. }
  885. else
  886. {
  887. break;
  888. }
  889. }
  890. else
  891. {
  892. //
  893. // We need to send a multipart response.
  894. //
  895. dwContentLength += sizeof(BOUNDARY_STRING) - 1
  896. + _dwRgNxSizeToSend
  897. + sizeof(g_achMMimeTypeFmt) - 1
  898. + sizeof(g_achMMimeRangeFmt) - 1
  899. + NbDigit( _dwRgNxOffset )
  900. + NbDigit( _dwRgNxOffset + _dwRgNxSizeToSend - 1 )
  901. + dwFx;
  902. }
  903. }
  904. if (cRanges > 1)
  905. {
  906. //
  907. // adjust Content-Length because initial delimiter is part of the header delimiter
  908. //
  909. dwContentLength += sizeof(LAST_BOUNDARY_STRING) - ADJ_FIRST - 1;
  910. _fMimeMultipart = TRUE;
  911. _cbMimeMultipart = dwContentLength;
  912. *pfIsNxRange = TRUE;
  913. //
  914. // Restore the end of Range for subsequent processing
  915. //
  916. _iRangeIdx = dwFirstRangeEnd;
  917. }
  918. else
  919. {
  920. *pfIsNxRange = FALSE;
  921. }
  922. }
  923. }
  924. BOOL
  925. HTTP_REQUEST::ScanRange(
  926. LPDWORD pdwOffset,
  927. LPDWORD pdwSizeToSend,
  928. BOOL *pfEntireFile,
  929. BOOL *pfIsLastRange
  930. )
  931. /*++
  932. Routine Description:
  933. Scan the next range in strRange
  934. Returns:
  935. TRUE if a range was found, else FALSE
  936. Arguments:
  937. pdwOffset update range offset on return
  938. pdwSizeToSend update range size on return
  939. pfEntireFile set to TRUE on return if entire file to be send
  940. pfIsLastRange set to TRUE on return if this is the last range
  941. History:
  942. Phillich 08-Feb-1996 Created
  943. --*/
  944. {
  945. DWORD cbSizeLow;
  946. DWORD cbSizeHigh;
  947. DWORD dwOffset = 0;
  948. DWORD cbSizeToSend = 0;
  949. BOOL fEndOfRange = FALSE;
  950. BOOL fInvalidRange;
  951. BOOL fEntireFile = FALSE;
  952. int c;
  953. TCP_REQUIRE( _pGetFile->QuerySize( &cbSizeLow,
  954. &cbSizeHigh ));
  955. LPSTR pRng = _strRange.QueryStr() + _iRangeIdx;
  956. //
  957. // Rules for processing ranges
  958. //
  959. // If there is any Syntactically Invalid Byte-Range in the request, then the header
  960. // must be ignored. Return code is 200 with entire body (i.e. *pfEntireFile = TRUE).
  961. //
  962. // If the request is Syntactically Valid & any element is satisfiable, the partial
  963. // data for the satisfiable portions should be sent with return code HT_PARTIAL_CONTENT.
  964. //
  965. // If the request is Syntactically Valid & all elements are unsatisfiable and there
  966. // is no If-Range header the return error code is HT_RANGE_NOT_SATISFIABLE (416)
  967. do
  968. {
  969. fInvalidRange = FALSE;
  970. //
  971. // Skip to begining of next range
  972. //
  973. while ( (c=*pRng) && (' '==c) )
  974. {
  975. ++pRng;
  976. }
  977. //
  978. // Test for no range
  979. //
  980. if ( *pRng == '\0' )
  981. {
  982. _iRangeIdx = (DWORD)(pRng - _strRange.QueryStr());
  983. *pfEntireFile = fEntireFile;
  984. *pfIsLastRange = TRUE;
  985. return FALSE;
  986. }
  987. // determine Offset & Size to send
  988. DWORD dwB, dwE;
  989. BOOL fIsB, fIsE;
  990. dwB = AToDW( &pRng, &fIsB );
  991. if ( *pRng == '-' )
  992. {
  993. ++pRng;
  994. dwE = AToDW( &pRng, &fIsE );
  995. if ( *pRng == '-' || (!fIsB && !fIsE) )
  996. {
  997. //
  998. // Syntactically Invalid Range. Skip RANGE Header
  999. //
  1000. fEntireFile = TRUE;
  1001. break;
  1002. }
  1003. if ( fIsB )
  1004. {
  1005. if ( fIsE )
  1006. {
  1007. if ( dwB <= dwE )
  1008. {
  1009. if ( dwE < cbSizeLow )
  1010. {
  1011. dwOffset = dwB;
  1012. cbSizeToSend = dwE - dwB + 1;
  1013. }
  1014. else if (dwE < (cbSizeLow + QueryMetaData()->QueryFooterLength()) )
  1015. {
  1016. //
  1017. // Asking for part of footer, send entire file.
  1018. //
  1019. dwOffset = 0;
  1020. cbSizeToSend = cbSizeLow;
  1021. }
  1022. else
  1023. {
  1024. if ( dwB < cbSizeLow )
  1025. {
  1026. dwOffset = dwB;
  1027. cbSizeToSend = cbSizeLow - dwB;
  1028. }
  1029. else if (dwB < (cbSizeLow + QueryMetaData()->QueryFooterLength()) )
  1030. {
  1031. //
  1032. // Asking for part of footer,send entire file.
  1033. //
  1034. dwOffset = 0;
  1035. cbSizeToSend = cbSizeLow;
  1036. }
  1037. else
  1038. {
  1039. //
  1040. // Syntactically Valid but Unsatisfiable range. Zap this range and
  1041. // skip to the next range.
  1042. //
  1043. _fUnsatisfiableByteRange = TRUE;
  1044. fInvalidRange = TRUE;
  1045. memmove( _strRange.QueryStr()+_iRangeIdx, pRng,
  1046. _strRange.QueryCCH() - (size_t)(pRng - _strRange.QueryStr())+1);
  1047. pRng = _strRange.QueryStr()+_iRangeIdx;
  1048. }
  1049. }
  1050. }
  1051. else
  1052. {
  1053. //
  1054. // E < B : Syntactically Invalid Range. Skip RANGE Header
  1055. //
  1056. fEntireFile = TRUE;
  1057. break;
  1058. }
  1059. }
  1060. else
  1061. {
  1062. //
  1063. // Starting at B until end.
  1064. //
  1065. DWORD dwFooter = QueryMetaData()->QueryFooterLength() ;
  1066. if ( dwB < cbSizeLow + dwFooter)
  1067. {
  1068. if ( 0 != dwFooter)
  1069. {
  1070. //
  1071. // There's a footer on the file, send the whole thing.
  1072. //
  1073. dwOffset = 0;
  1074. cbSizeToSend = cbSizeLow;
  1075. }
  1076. else
  1077. {
  1078. dwOffset = dwB;
  1079. cbSizeToSend = cbSizeLow - dwB;
  1080. }
  1081. }
  1082. else
  1083. {
  1084. //
  1085. // Syntactically Valid but Unsatisfiable range. Zap this range and
  1086. // skip to the next range.
  1087. //
  1088. _fUnsatisfiableByteRange = TRUE;
  1089. fInvalidRange = TRUE;
  1090. memmove( _strRange.QueryStr()+_iRangeIdx, pRng,
  1091. _strRange.QueryCCH() - (size_t)(pRng - _strRange.QueryStr())+1);
  1092. pRng = _strRange.QueryStr()+_iRangeIdx;
  1093. }
  1094. }
  1095. }
  1096. else
  1097. {
  1098. //
  1099. // E last bytes
  1100. //
  1101. if ( 0 != dwE &&
  1102. dwE < cbSizeLow &&
  1103. QueryMetaData()->QueryFooterLength() == 0)
  1104. {
  1105. dwOffset = cbSizeLow - dwE;
  1106. cbSizeToSend = dwE;
  1107. }
  1108. else if ( 0 == dwE )
  1109. {
  1110. //
  1111. // Syntactically Valid but Unsatisfiable range. Zap this range and
  1112. // skip to the next range.
  1113. //
  1114. _fUnsatisfiableByteRange = TRUE;
  1115. fInvalidRange = TRUE;
  1116. memmove( _strRange.QueryStr()+_iRangeIdx, pRng,
  1117. _strRange.QueryCCH() - (size_t)(pRng - _strRange.QueryStr())+1);
  1118. pRng = _strRange.QueryStr()+_iRangeIdx;
  1119. }
  1120. else
  1121. {
  1122. //
  1123. // Return entire file
  1124. //
  1125. dwOffset = 0;
  1126. cbSizeToSend = cbSizeLow;
  1127. }
  1128. }
  1129. }
  1130. else
  1131. {
  1132. //
  1133. // Syntactically Invalid Range. Skip RANGE Header
  1134. //
  1135. fEntireFile = TRUE;
  1136. break;
  1137. }
  1138. //
  1139. // Skip to begining of next range
  1140. //
  1141. while ( (c=*pRng) && c!=',' )
  1142. {
  1143. ++pRng;
  1144. }
  1145. if ( c == ',' )
  1146. {
  1147. ++pRng;
  1148. }
  1149. }
  1150. while ( fInvalidRange );
  1151. _iRangeIdx = (DWORD)(pRng - _strRange.QueryStr());
  1152. *pfEntireFile = fEntireFile;
  1153. *pfIsLastRange = (*pRng == '\0');
  1154. *pdwOffset = dwOffset;
  1155. *pdwSizeToSend = cbSizeToSend;
  1156. return TRUE;
  1157. }
  1158. BOOL
  1159. HTTP_REQUEST::SendRange(
  1160. DWORD dwBufLen,
  1161. DWORD dwOffset,
  1162. DWORD dwSizeToSend,
  1163. BOOL fIsLast
  1164. )
  1165. /*++
  1166. Routine Description:
  1167. Send a byte range to the client
  1168. Returns:
  1169. TRUE if TransmitFile OK, FALSE on error
  1170. Arguments:
  1171. dwBufLen length of the header already created in pbufResponse
  1172. dwOffset range offset in file
  1173. dwSizeToSend range size
  1174. fIsLast TRUE if this is the last range to send
  1175. History:
  1176. Phillich 08-Feb-1996 Created
  1177. --*/
  1178. {
  1179. CHAR * pszResp;
  1180. CHAR * pszTail;
  1181. BUFFER * pbufResponse = QueryRespBuf();
  1182. DWORD cbSizeLow;
  1183. DWORD cbSizeHigh;
  1184. DWORD dwFlags = IO_FLAG_ASYNC | (fIsLast ? 0 : IO_FLAG_NO_RECV);
  1185. pszTail = pszResp = (CHAR *) pbufResponse->QueryPtr() + dwBufLen;
  1186. if ( _fMimeMultipart )
  1187. {
  1188. pszTail += wsprintf( pszTail,
  1189. MMIME_TYPE,
  1190. _strReturnMimeType.QueryStr() );
  1191. }
  1192. if ( fIsLast )
  1193. {
  1194. SetState( HTR_DONE, HT_RANGE, NO_ERROR );
  1195. if (_cbBytesReceived > _cbClientRequest)
  1196. {
  1197. dwFlags |= IO_FLAG_NO_RECV;
  1198. }
  1199. }
  1200. TCP_REQUIRE( _pGetFile->QuerySize( &cbSizeLow,
  1201. &cbSizeHigh ));
  1202. pszTail += wsprintf( pszTail,
  1203. MMIME_RANGE,
  1204. dwOffset, dwOffset + dwSizeToSend - 1,
  1205. cbSizeLow
  1206. );
  1207. if ( fIsLast && (!IsKeepConnSet() &&
  1208. !_Filter.IsNotificationNeeded( SF_NOTIFY_END_OF_REQUEST,
  1209. IsSecurePort() )))
  1210. {
  1211. dwFlags |= TF_DISCONNECT | TF_REUSE_SOCKET;
  1212. }
  1213. if ( !TransmitFile( _pGetFile,
  1214. NULL,
  1215. dwOffset,
  1216. dwSizeToSend,
  1217. dwFlags,
  1218. QueryRespBufPtr(),
  1219. QueryRespBufCB(),
  1220. _fMimeMultipart ? (fIsLast ? LAST_BOUNDARY_STRING : BOUNDARY_STRING) : NULL,
  1221. (DWORD)(_fMimeMultipart ? (fIsLast ? sizeof(LAST_BOUNDARY_STRING)-1 : sizeof(BOUNDARY_STRING)-1 ) : 0 ) ) )
  1222. {
  1223. SetState( HTR_DONE );
  1224. return FALSE;
  1225. }
  1226. return TRUE;
  1227. }
  1228. /*******************************************************************
  1229. NAME: HTTP_REQ_BASE::BuildResponseHeader
  1230. SYNOPSIS: Builds a successful response header
  1231. ENTRY: pstrResponse - Receives reply headers
  1232. pstrPath - Fully qualified path to file, may be NULL
  1233. pFile - File information about pstrPath, may be NULL
  1234. pfHandled - Does processing need to continue? Will be
  1235. set to TRUE if no further processing is needed by
  1236. the caller
  1237. pstrStatus - Alternate status string to use
  1238. RETURNS: TRUE if successful, FALSE on error
  1239. NOTES: if pstrPath is NULL, then the base header is put into
  1240. pstrResponse without the header termination or file
  1241. information
  1242. HISTORY:
  1243. Johnl 30-Aug-1994 Created
  1244. ********************************************************************/
  1245. BOOL
  1246. HTTP_REQUEST::BuildResponseHeader(
  1247. BUFFER * pbufResponse,
  1248. STR * pstrPath,
  1249. TS_OPEN_FILE_INFO * pFile,
  1250. BOOL * pfHandled,
  1251. STR * pstrStatus,
  1252. LPBOOL pfFinished
  1253. )
  1254. {
  1255. FILETIME FileTime;
  1256. DWORD cbSizeLow;
  1257. SYSTEMTIME SysTime;
  1258. SYSTEMTIME SysFileTime;
  1259. LPSYSTEMTIME pMinSysTime;
  1260. CHAR * pszResp;
  1261. CHAR * pszTail;
  1262. CHAR ach[64];
  1263. DWORD cb;
  1264. DWORD dwHdrLength;
  1265. if ( pfHandled )
  1266. {
  1267. *pfHandled = FALSE;
  1268. }
  1269. //
  1270. // HTTP 0.9 clients don't use MIME headers, they just expect the data
  1271. //
  1272. if ( IsPointNine() )
  1273. {
  1274. IF_DEBUG( PARSING )
  1275. {
  1276. DBGPRINTF((DBG_CONTEXT,
  1277. "[BuildResponseHeader] Skipping headers, 0.9 client\n"));
  1278. }
  1279. *((CHAR *)pbufResponse->QueryPtr()) = '\0';
  1280. return TRUE;
  1281. }
  1282. if ( !BuildBaseResponseHeader( pbufResponse,
  1283. pfHandled,
  1284. pstrStatus,
  1285. (HTTPH_SEND_GLOBAL_EXPIRE |
  1286. HTTPH_NO_DATE) ))
  1287. {
  1288. return FALSE;
  1289. }
  1290. //
  1291. // If no file, then don't add the content type headers
  1292. //
  1293. if ( !pstrPath )
  1294. {
  1295. return TRUE;
  1296. }
  1297. //
  1298. // "Content-Type: xxx/xxx"
  1299. //
  1300. // We check to make sure the client can accept the type we
  1301. // want to send
  1302. //
  1303. if ( !::SelectMimeMapping( &_strReturnMimeType,
  1304. pstrPath->QueryStr(),
  1305. _pMetaData) )
  1306. {
  1307. return FALSE;
  1308. }
  1309. dwHdrLength = strlen((CHAR *) pbufResponse->QueryPtr());
  1310. if (!pbufResponse->Resize(dwHdrLength + RANGE_ADDL_BUF_HDR_SIZE +
  1311. _strReturnMimeType.QueryCB()))
  1312. {
  1313. return FALSE;
  1314. }
  1315. pszResp = (CHAR *) pbufResponse->QueryPtr();
  1316. // build Date: uses Date/Time cache
  1317. dwHdrLength += g_pDateTimeCache->GetFormattedCurrentDateTime(
  1318. pszResp + dwHdrLength );
  1319. pszTail = pszResp + dwHdrLength;
  1320. if ( !DoesClientAccept( _strReturnMimeType.QueryStr() ) )
  1321. {
  1322. IF_DEBUG( PARSING )
  1323. {
  1324. DBGPRINTF((DBG_CONTEXT,
  1325. "[BuildResponseHeader] Client doesn't accept %s\n",
  1326. _strReturnMimeType.QueryStr() ));
  1327. }
  1328. SetState( HTR_DONE, HT_NONE_ACCEPTABLE, NO_ERROR );
  1329. Disconnect( HT_NONE_ACCEPTABLE, NO_ERROR, FALSE, pfFinished );
  1330. //
  1331. // No further processing is needed
  1332. //
  1333. if ( pfHandled )
  1334. {
  1335. *pfHandled = TRUE;
  1336. }
  1337. return TRUE;
  1338. }
  1339. if ( _fMimeMultipart && _fProcessByteRange )
  1340. {
  1341. if ( _VersionMajor > 1 || (_VersionMajor == 1 && _VersionMinor > 0) )
  1342. {
  1343. APPEND_STRING( pszTail, "Content-Type: multipart/byteranges; boundary="
  1344. BOUNDARY_STRING_DEFINITION "\r\n" );
  1345. }
  1346. else
  1347. {
  1348. APPEND_STRING( pszTail, "Content-Type: multipart/x-byteranges; boundary="
  1349. BOUNDARY_STRING_DEFINITION "\r\n" );
  1350. }
  1351. }
  1352. else
  1353. {
  1354. if ( _fAcceptRange && !_fProcessByteRange )
  1355. {
  1356. APPEND_STRING( pszTail, "Accept-Ranges: bytes\r\n" );
  1357. }
  1358. APPEND_STR_HEADER( pszTail, "Content-Type: ", _strReturnMimeType, "\r\n" );
  1359. }
  1360. if ( !pFile )
  1361. {
  1362. return TRUE;
  1363. }
  1364. if ( !QueryNoCache() )
  1365. {
  1366. //
  1367. // "Last-Modified: <GMT time>". Only do this if we're not
  1368. // satisfying an If-Range: request.
  1369. //
  1370. if (!_fProcessByteRange ||
  1371. (_HeaderList.FastMapQueryValue(HM_IFR) == NULL) )
  1372. {
  1373. if ( !pFile->QueryLastWriteTime( &FileTime ) ||
  1374. !::FileTimeToSystemTime( &FileTime, &SysFileTime ))
  1375. {
  1376. return FALSE;
  1377. }
  1378. IISGetCurrentTimeAsSystemTime(&SysTime);
  1379. pMinSysTime = MinSystemTime(&SysTime, &SysFileTime);
  1380. if (!::SystemTimeToGMT( *pMinSysTime, ach, sizeof(ach) ))
  1381. {
  1382. return FALSE;
  1383. }
  1384. APPEND_PSZ_HEADER( pszTail, "Last-Modified: ", ach, "\r\n" );
  1385. }
  1386. //
  1387. // ETag: <Etag>
  1388. //
  1389. if (pFile->WeakETag())
  1390. {
  1391. APPEND_PSZ_HEADER(pszTail, "ETag: W/", pFile->QueryETag(), "\r\n");
  1392. } else
  1393. {
  1394. APPEND_PSZ_HEADER(pszTail, "ETag: ", pFile->QueryETag(), "\r\n");
  1395. }
  1396. }
  1397. //
  1398. // "Content-Length: nnnn" and end of headers
  1399. //
  1400. if ( _fMimeMultipart )
  1401. {
  1402. if ( _cbMimeMultipart )
  1403. {
  1404. APPEND_NUMERIC_HEADER( pszTail,
  1405. "Content-Length: ",
  1406. _cbMimeMultipart,
  1407. "\r\n" );
  1408. }
  1409. //
  1410. // first boundary string
  1411. //
  1412. APPEND_STRING( pszTail, DELIMIT_FIRST BOUNDARY_STRING );
  1413. }
  1414. else
  1415. {
  1416. if ( _fProcessByteRange )
  1417. {
  1418. cbSizeLow = _cbMimeMultipart;
  1419. }
  1420. else
  1421. {
  1422. TCP_REQUIRE( pFile->QuerySize( &cbSizeLow ));
  1423. cbSizeLow += QueryMetaData()->QueryFooterLength();
  1424. }
  1425. APPEND_NUMERIC_HEADER_TAILVAR( pszTail,
  1426. "Content-Length: ",
  1427. cbSizeLow,
  1428. (_fProcessByteRange ? "\r\n" : "\r\n\r\n") );
  1429. }
  1430. IF_DEBUG( REQUEST )
  1431. {
  1432. DBGPRINTF((DBG_CONTEXT,
  1433. "BuildResponseHeader: Built the following header:\n%s",
  1434. pszResp ));
  1435. }
  1436. return TRUE;
  1437. }
  1438. BOOL
  1439. HTTP_REQUEST::BuildFileResponseHeader(
  1440. BUFFER * pbufResponse,
  1441. STR * pstrPath,
  1442. TS_OPEN_FILE_INFO * pFile,
  1443. BOOL * pfHandled,
  1444. LPBOOL pfFinished
  1445. )
  1446. /*++
  1447. Routine Description:
  1448. Builds a successful response header for a file request
  1449. without byte ranges.
  1450. Returns:
  1451. TRUE if successful, FALSE on error
  1452. Arguments:
  1453. pstrResponse - Receives reply headers
  1454. pstrPath - Fully qualified path to file, may be NULL
  1455. pFile - File information about pstrPath, may be NULL
  1456. pfHandled - Does processing need to continue? Will be
  1457. set to TRUE if no further processing is needed by
  1458. the caller
  1459. History:
  1460. Phillich 26-Feb-1996 Created
  1461. --*/
  1462. {
  1463. FILETIME FileTime;
  1464. DWORD cbSizeLow;
  1465. SYSTEMTIME SysTime;
  1466. SYSTEMTIME SysFileTime;
  1467. LPSYSTEMTIME pMinSysTime;
  1468. CHAR * pszResp;
  1469. CHAR * pszTail;
  1470. CHAR * pszVariant;
  1471. CHAR ach[64];
  1472. int cMod;
  1473. DWORD dwCurrentHeaderLength;
  1474. if ( pfHandled )
  1475. {
  1476. *pfHandled = FALSE;
  1477. }
  1478. //
  1479. // HTTP 0.9 clients don't use MIME headers, they just expect the data
  1480. //
  1481. if ( IsPointNine() )
  1482. {
  1483. IF_DEBUG( PARSING )
  1484. {
  1485. DBGPRINTF((DBG_CONTEXT,
  1486. "[BuildResponseHeader] Skipping headers, 0.9 client\n"));
  1487. }
  1488. *((CHAR *)pbufResponse->QueryPtr()) = '\0';
  1489. return TRUE;
  1490. }
  1491. if ( !BuildBaseResponseHeader( pbufResponse,
  1492. pfHandled,
  1493. NULL,
  1494. HTTPH_SEND_GLOBAL_EXPIRE
  1495. |HTTPH_NO_DATE ))
  1496. {
  1497. return FALSE;
  1498. }
  1499. //
  1500. // Make sure that we have enough room left in the buffer. Note
  1501. // that right here we only account for enough space for the Date:
  1502. // header and the max. amount of cached info. If there is more
  1503. // uncached info that may be inserted here, make sure to change the
  1504. // MIN_ADDL_BUF_HDR_SIZE_CACHED define above.
  1505. //
  1506. dwCurrentHeaderLength = strlen((CHAR *) pbufResponse->QueryPtr());
  1507. if (!pbufResponse->Resize(dwCurrentHeaderLength +
  1508. MIN_ADDL_BUF_HDR_SIZE_CACHED) )
  1509. {
  1510. return FALSE;
  1511. }
  1512. pszResp = (CHAR *) pbufResponse->QueryPtr();
  1513. // build Date: uses Date/Time cache
  1514. dwCurrentHeaderLength += g_pDateTimeCache->GetFormattedCurrentDateTime(
  1515. pszResp + dwCurrentHeaderLength );
  1516. pszTail = pszResp + dwCurrentHeaderLength;
  1517. if ( !QueryNoCache() && pFile->RetrieveHttpInfo( pszTail, &cMod ) )
  1518. {
  1519. // 1st line is Content-Type:, check client accepts it
  1520. PSTR pDelim = strchr( pszTail, '\r' );
  1521. if ( pDelim )
  1522. {
  1523. *pDelim = '\0';
  1524. }
  1525. if ( !DoesClientAccept( pszTail + sizeof( "Content-Type: " ) - sizeof(CHAR) ) )
  1526. {
  1527. goto no_match_type;
  1528. }
  1529. if ( pDelim )
  1530. {
  1531. *pDelim = '\r';
  1532. }
  1533. return TRUE;
  1534. }
  1535. //
  1536. // "Content-Type: xxx/xxx"
  1537. //
  1538. // We check to make sure the client can accept the type we
  1539. // want to send
  1540. //
  1541. if ( !::SelectMimeMapping( &_strReturnMimeType,
  1542. pstrPath->QueryStr(),
  1543. _pMetaData) )
  1544. {
  1545. return FALSE;
  1546. }
  1547. if (!pbufResponse->Resize(dwCurrentHeaderLength +
  1548. _strReturnMimeType.QueryCB() +
  1549. MIN_ADDL_BUF_HDR_SIZE))
  1550. {
  1551. return FALSE;
  1552. }
  1553. pszResp = (CHAR *) pbufResponse->QueryPtr();
  1554. pszTail = pszResp + dwCurrentHeaderLength;
  1555. pszVariant = pszTail;
  1556. if ( !DoesClientAccept( _strReturnMimeType.QueryStr() ) )
  1557. {
  1558. no_match_type:
  1559. IF_DEBUG( PARSING )
  1560. {
  1561. DBGPRINTF((DBG_CONTEXT,
  1562. "[BuildResponseHeader] Client doesn't accept %s\n",
  1563. _strReturnMimeType.QueryStr() ));
  1564. }
  1565. SetState( HTR_DONE, HT_NONE_ACCEPTABLE, NO_ERROR );
  1566. Disconnect( HT_NONE_ACCEPTABLE, NO_ERROR, FALSE, pfFinished );
  1567. //
  1568. // No further processing is needed
  1569. //
  1570. if ( pfHandled )
  1571. {
  1572. *pfHandled = TRUE;
  1573. }
  1574. return TRUE;
  1575. }
  1576. //
  1577. // Content-Type: MUST be the 1st header, or we get messed up when
  1578. // we use the cached headers and try to pass the mime type to
  1579. // DoesClientAccept().
  1580. //
  1581. APPEND_STR_HEADER( pszTail, "Content-Type: ", _strReturnMimeType, "\r\n" );
  1582. if ( _fAcceptRange )
  1583. {
  1584. APPEND_STRING( pszTail, "Accept-Ranges: bytes\r\n" );
  1585. }
  1586. if ( !QueryNoCache() )
  1587. {
  1588. //
  1589. // "Last-Modified: <GMT time>"
  1590. //
  1591. if ( !pFile->QueryLastWriteTime( &FileTime ) ||
  1592. !::FileTimeToSystemTime( &FileTime, &SysFileTime ))
  1593. {
  1594. return FALSE;
  1595. }
  1596. IISGetCurrentTimeAsSystemTime(&SysTime);
  1597. pMinSysTime = MinSystemTime(&SysTime, &SysFileTime);
  1598. if (!::SystemTimeToGMT( *pMinSysTime, ach, sizeof(ach) ))
  1599. {
  1600. return FALSE;
  1601. }
  1602. APPEND_PSZ_HEADER( pszTail, "Last-Modified: ", ach, "\r\n" );
  1603. //
  1604. // ETag: <Etag>
  1605. //
  1606. if (pFile->WeakETag())
  1607. {
  1608. APPEND_PSZ_HEADER(pszTail, "ETag: W/", pFile->QueryETag(), "\r\n");
  1609. } else
  1610. {
  1611. APPEND_PSZ_HEADER(pszTail, "ETag: ", pFile->QueryETag(), "\r\n");
  1612. }
  1613. }
  1614. //
  1615. // "Content-Length: nnnn" and end of headers
  1616. //
  1617. TCP_REQUIRE( pFile->QuerySize( &cbSizeLow ));
  1618. cbSizeLow += QueryMetaData()->QueryFooterLength();
  1619. APPEND_NUMERIC_HEADER( pszTail,
  1620. "Content-Length: ",
  1621. cbSizeLow,
  1622. "\r\n\r\n" );
  1623. if ( !QueryNoCache() )
  1624. {
  1625. pFile->SetHttpInfo( pszVariant, DIFF(pszTail - pszVariant) );
  1626. }
  1627. IF_DEBUG( REQUEST )
  1628. {
  1629. DBGPRINTF((DBG_CONTEXT,
  1630. "BuildResponseHeader: Built the following header:\n%s",
  1631. pszResp ));
  1632. }
  1633. return TRUE;
  1634. }
  1635. /*******************************************************************
  1636. NAME: HTTP_REQUEST::ProcessISMAP
  1637. SYNOPSIS: Checks of the URL and passed parameters specify an
  1638. image mapping file
  1639. RETURNS: TRUE if successful, FALSE on error
  1640. NOTES: gFile - Opened file Info
  1641. pchFile - Fully qualified name of file
  1642. pstrResp - Response to send if *pfHandled is FALSE
  1643. pfFound - TRUE if a mapping was found
  1644. pfHandled - Set to TRUE if no further processing is needed,
  1645. FALSE if pstrResp should be sent to the client
  1646. HISTORY:
  1647. Johnl 17-Sep-1994 Created
  1648. ********************************************************************/
  1649. BOOL HTTP_REQUEST::ProcessISMAP( LPTS_OPEN_FILE_INFO gFile,
  1650. CHAR * pchFile,
  1651. BUFFER * pbufResp,
  1652. BOOL * pfFound,
  1653. BOOL * pfHandled )
  1654. {
  1655. INT x, y;
  1656. TCHAR * pch = _strURLParams.QueryStr();
  1657. PW3_SERVER_INSTANCE pInstance = QueryW3Instance();
  1658. STACK_STR( strURL, MAX_PATH);
  1659. *pfHandled = FALSE;
  1660. *pfFound = FALSE;
  1661. //
  1662. // Get the x and y cooridinates of the mouse click on the image
  1663. //
  1664. x = _tcstoul( pch,
  1665. NULL,
  1666. 10 );
  1667. //
  1668. // Move past x and any intervening delimiters
  1669. //
  1670. while ( isdigit( (UCHAR)(*pch) ))
  1671. pch++;
  1672. while ( *pch && !isdigit( (UCHAR)(*pch) ))
  1673. pch++;
  1674. y = _tcstoul( pch,
  1675. NULL,
  1676. 10 );
  1677. if ( !ImpersonateUser() )
  1678. {
  1679. return FALSE;
  1680. }
  1681. if ( !SearchMapFile( gFile,
  1682. pchFile,
  1683. &pInstance->GetTsvcCache(),
  1684. QueryImpersonationHandle(),
  1685. x,
  1686. y,
  1687. &strURL,
  1688. pfFound,
  1689. IsAnonymous()||IsClearTextPassword() ))
  1690. {
  1691. RevertUser();
  1692. return FALSE;
  1693. }
  1694. RevertUser();
  1695. if ( !*pfFound )
  1696. {
  1697. if ( (pch = (TCHAR * ) _HeaderList.FastMapQueryValue( HM_REF )) )
  1698. {
  1699. #if 0
  1700. // handle relative URL ( i.e. not fully qualified
  1701. // and not specifying an absolute path ).
  1702. // disabled for now.
  1703. PSTR pColon = strchr( pch, ':' );
  1704. PSTR pDelim = strchr( pch, '/' );
  1705. BOOL fValid;
  1706. // check for relative URL
  1707. if ( *pch != '/' && (pColon == NULL || pDelim < pColon) )
  1708. {
  1709. strURL.Copy( _strURL.QueryStr() );
  1710. PSTR pL = strURL.QueryStr();
  1711. // look for last '/'
  1712. int iL = strlen( pL );
  1713. while ( iL && pL[iL-1] != '/' )
  1714. --iL;
  1715. // combine request URL & relative referer URL
  1716. strURL.Resize( iL + strlen( pch ) + 1 );
  1717. strcpy( strURL.QueryStr() + iL, pch );
  1718. fValid = TRUE;
  1719. CanonURL( strURL.QueryStr());
  1720. CanonURL.SetLen( strlen( strURL.QueryStr() ));
  1721. }
  1722. else
  1723. #endif
  1724. strURL.Copy( (TCHAR *) pch );
  1725. }
  1726. else
  1727. return TRUE;
  1728. }
  1729. CloseGetFile();
  1730. //
  1731. // If the found URL starts with a forward slash ("/foo/bar/doc.htm")
  1732. // and it doesn't contain a bookmark ('#')
  1733. // then the URL is local and we build a fully qualified URL to send
  1734. // back to the client.
  1735. // we assume it's a fully qualified URL ("http://foo/bar/doc.htm")
  1736. // and send the client a redirection notice to the mapped URL
  1737. //
  1738. if ( *strURL.QueryStr() == TEXT('/') )
  1739. {
  1740. #if 0
  1741. // disabled :
  1742. // we now always send a redirect
  1743. TCHAR * pch = strURL.QueryStr();
  1744. //
  1745. // Make sure there's no bookmark
  1746. //
  1747. if ( !(strchr( strURL.QueryStr(), '#' )) )
  1748. {
  1749. //
  1750. // Call OnURL to reparse the URL and set the members then
  1751. // call the verb again
  1752. //
  1753. if ( !ReprocessURL( pch ) )
  1754. {
  1755. return FALSE;
  1756. }
  1757. *pfHandled = TRUE;
  1758. }
  1759. else
  1760. #endif
  1761. {
  1762. CHAR achPort[32];
  1763. STACK_STR( strOldURL, MAX_PATH );
  1764. //
  1765. // fully qualify the URL and send a
  1766. // redirect. Some browsers (emosaic) don't like doc relative
  1767. // URLs with bookmarks
  1768. //
  1769. if ( !strOldURL.Copy( strURL ) ||
  1770. !strURL.Resize( strOldURL.QueryCB() + 128 ))
  1771. {
  1772. return FALSE;
  1773. }
  1774. //
  1775. // NOTE: We fully qualify the URL with the protocol (http or
  1776. // https) based on the port this request came in on. This means
  1777. // you cannot have a partial URL with a bookmark (which is how
  1778. // we got here) go from a secure part of the server to a
  1779. // nonsecure part of the server.
  1780. //
  1781. strURL.Copy( (IsSecurePort() ? "https://" : "http://" ));
  1782. strURL.Append( QueryHostAddr() );
  1783. if ( IsSecurePort() ? (INT) QueryClientConn()->QueryPort()
  1784. != HTTP_SSL_PORT
  1785. : (INT) QueryClientConn()->QueryPort() != 80 )
  1786. {
  1787. strURL.Append( ":" );
  1788. _itoa( (INT) QueryClientConn()->QueryPort(), achPort, 10 );
  1789. strURL.Append( achPort );
  1790. }
  1791. strURL.Append( strOldURL );
  1792. if ( !BuildURLMovedResponse( pbufResp, &strURL, HT_REDIRECT, FALSE ))
  1793. return FALSE;
  1794. }
  1795. }
  1796. else
  1797. {
  1798. if ( !BuildURLMovedResponse( pbufResp, &strURL, HT_REDIRECT, FALSE ))
  1799. return FALSE;
  1800. }
  1801. *pfFound = TRUE;
  1802. return TRUE;
  1803. }
  1804. /*******************************************************************
  1805. NAME: EncodeStringToHTML
  1806. SYNOPSIS: Enode string in HTML format
  1807. ENTRY: szSrc - Source string to be encoded, in system codepage
  1808. szDest - Space to output HTML (in system codepage)
  1809. cbDest - Size of space in szDest (number of bytes)
  1810. RETURNS: number of bytes required for HTML text
  1811. NOTES: If cbDest is less than target HTML, then we fit
  1812. as much characters as possible into szDest. But the
  1813. return value remains the same and is greater than
  1814. cbDest. szDest is not zero terminated in this case.
  1815. HISTORY:
  1816. markzh 17-Jan-2002 Created
  1817. ********************************************************************/
  1818. UINT EncodeStringToHTML(CHAR *szSrc, CHAR *szDest, UINT cbDest)
  1819. {
  1820. CHAR *pSrc = szSrc;
  1821. CHAR *pDest = szDest;
  1822. BOOL isSecondByte=FALSE;
  1823. do {
  1824. CHAR *szAppend = pSrc;
  1825. UINT cbAppend = 1;
  1826. // We may be running on DBCS. Ideally we should encode them to &#nnnnn
  1827. // but we dont want that many code here
  1828. if (isSecondByte)
  1829. {
  1830. isSecondByte = FALSE;
  1831. }
  1832. else if (IsDBCSLeadByte(*pSrc))
  1833. {
  1834. isSecondByte = TRUE;
  1835. }
  1836. else switch (*pSrc)
  1837. {
  1838. #define SetAppend(s) (szAppend=(s), cbAppend=sizeof(s)-1)
  1839. case '&': SetAppend("&amp;");
  1840. break;
  1841. case '"': SetAppend("&quot;");
  1842. break;
  1843. case '<': SetAppend("&lt;");
  1844. break;
  1845. case '>': SetAppend("&gt;");
  1846. break;
  1847. default:
  1848. break;
  1849. #undef SetAppend
  1850. }
  1851. if (pDest - szDest + cbAppend <= cbDest)
  1852. {
  1853. memcpy(pDest, szAppend, cbAppend);
  1854. }
  1855. pDest += cbAppend;
  1856. }while (*pSrc++);
  1857. return DIFF(pDest - szDest);
  1858. }
  1859. /*******************************************************************
  1860. NAME: HTTP_REQ_BASE::BuildURLMovedResponse
  1861. SYNOPSIS: Builds a full request indicating an object has moved to
  1862. the location specified by URL
  1863. ENTRY: pbufResp - String to receive built response
  1864. pstrURL - New location of object, gets escaped
  1865. dwServerCode - Server response code
  1866. (either HT_REDIRECT or HT_MOVED)
  1867. fIncludeParams - TRUE to include params from original request
  1868. in redirect
  1869. RETURNS: TRUE if successful, FALSE on error
  1870. NOTES: This routine doesn't support sending a Unicode doc moved
  1871. message
  1872. HISTORY:
  1873. Johnl 17-Sep-1994 Created
  1874. ********************************************************************/
  1875. BOOL HTTP_REQ_BASE::BuildURLMovedResponse( BUFFER * pbufResp,
  1876. STR * pstrURL,
  1877. DWORD dwServerCode,
  1878. BOOL fIncludeParams )
  1879. {
  1880. STACK_STR(strMovedMessage, 512);
  1881. STR* pstrMovedMessage;
  1882. STACK_STR(strUrlWithParams, 512);
  1883. UINT cb;
  1884. UINT cbA;
  1885. CHAR * pszTail;
  1886. CHAR * pszResp;
  1887. DWORD cbURL;
  1888. BOOL fDone;
  1889. BOOL bHaveCustom;
  1890. DWORD dwMsgSize;
  1891. DWORD dwRedirHeaderSize;
  1892. DBG_ASSERT(dwServerCode == HT_REDIRECT || dwServerCode == HT_MOVED);
  1893. if (CheckCustomError(&strMovedMessage, HT_REDIRECT, 0, &fDone, &dwMsgSize, FALSE))
  1894. {
  1895. DBG_ASSERT(!fDone);
  1896. strMovedMessage.SetLen(strlen((CHAR *)strMovedMessage.QueryPtr()));
  1897. bHaveCustom = TRUE;
  1898. pstrMovedMessage = &strMovedMessage;
  1899. }
  1900. else
  1901. {
  1902. pstrMovedMessage = g_pstrMovedMessage;
  1903. DBG_ASSERT( pstrMovedMessage->QueryCB() );
  1904. bHaveCustom = FALSE;
  1905. }
  1906. //
  1907. // Make sure the response buffer is large enough
  1908. //
  1909. #define CB_FIXED_RESP 500
  1910. //
  1911. // Technically we should escape more characters then just the spaces but
  1912. // that would probably break some number of client apps
  1913. //
  1914. if ( !pstrURL->EscapeSpaces() )
  1915. {
  1916. return FALSE;
  1917. }
  1918. if ( fIncludeParams &&
  1919. !_strURLParams.IsEmpty() )
  1920. {
  1921. if ( !strUrlWithParams.Copy( *pstrURL ) ||
  1922. !strUrlWithParams.Append( "?" ) ||
  1923. !strUrlWithParams.Append( _strURLParams ) )
  1924. {
  1925. return FALSE;
  1926. }
  1927. pstrURL = &strUrlWithParams;
  1928. }
  1929. cbURL = pstrURL->QueryCB();
  1930. // WinSE 24915
  1931. // Enode string in HTML format
  1932. STACK_STR(strUrlInHTML, 512);
  1933. UINT cbUrlInHTML;
  1934. cbUrlInHTML = EncodeStringToHTML(pstrURL->QueryStr(),
  1935. strUrlInHTML.QueryStr(),
  1936. strUrlInHTML.QuerySize());
  1937. if ( cbUrlInHTML > strUrlInHTML.QuerySize() )
  1938. {
  1939. if (! strUrlInHTML.Resize(cbUrlInHTML))
  1940. {
  1941. return FALSE;
  1942. }
  1943. cbUrlInHTML = EncodeStringToHTML(pstrURL->QueryStr(),
  1944. strUrlInHTML.QueryStr(),
  1945. strUrlInHTML.QuerySize());
  1946. }
  1947. if ( (cbUrlInHTML + cbURL + CB_FIXED_RESP) > pbufResp->QuerySize() )
  1948. {
  1949. if ( !pbufResp->Resize( cbUrlInHTML + cbURL + CB_FIXED_RESP ))
  1950. return FALSE;
  1951. }
  1952. //
  1953. // "HTTP/<ver> 302 Redirect" or
  1954. // "HTTP/<ver> 301 Redirect"
  1955. //
  1956. if ( !BuildStatusLine( pbufResp,
  1957. dwServerCode,
  1958. NO_ERROR ))
  1959. {
  1960. return FALSE;
  1961. }
  1962. //
  1963. // Set the status to log here
  1964. //
  1965. SetLogStatus( dwServerCode, NO_ERROR );
  1966. pszResp = (CHAR *) pbufResp->QueryPtr();
  1967. pszTail = pszResp + strlen( pszResp );
  1968. //
  1969. // "Location: <URL>"
  1970. //
  1971. APPEND_STR_HEADER( pszTail, "Location: ", *pstrURL, "\r\n" );
  1972. //
  1973. // "Server: <Server>/<version>
  1974. //
  1975. APPEND_VER_STR( pszTail );
  1976. //
  1977. // Content-Type, it's OK to assume all clients accept text/html
  1978. //
  1979. if (!bHaveCustom)
  1980. {
  1981. APPEND_STRING( pszTail, "Content-Type: text/html\r\n" );
  1982. }
  1983. //
  1984. // Calculate the bytes in the body of the message for Content-Length
  1985. //
  1986. // cbUrlInHTML is total bytes in the string (-1 for the null)
  1987. //
  1988. // pstrMovedMessage->QueryCB does not include the null, but
  1989. // it may be a format string that will contain the url, so -2 for %s
  1990. //
  1991. // Wait to subtract the %s away, though since pstrMovedMessage
  1992. // my be custom error file data - This code is so @#$#%@%$.
  1993. // Of course none of these calculations are right if %s is actually
  1994. // in the custom error file, and if it isn't then the error is
  1995. // useless. Whoever wrote this deserves a beating.
  1996. //
  1997. cb = ( cbUrlInHTML - 1 ) +
  1998. ( pstrMovedMessage->QueryCB() );
  1999. if (!bHaveCustom)
  2000. {
  2001. //
  2002. // Now compensate for %s. It is okay that cb is a little too
  2003. // big in this case dwMsgSize will be what determines the value
  2004. // of the content length header.
  2005. //
  2006. dwMsgSize = cb - 2;
  2007. }
  2008. if ( IsKeepConnSet() )
  2009. {
  2010. if (!IsOneOne())
  2011. {
  2012. APPEND_STRING( pszTail, "Connection: keep-alive\r\n" );
  2013. }
  2014. } else
  2015. {
  2016. if (IsOneOne())
  2017. {
  2018. APPEND_STRING( pszTail, "Connection: close\r\n" );
  2019. }
  2020. }
  2021. //
  2022. // If we have any redirection specific headers, copy them in now.
  2023. //
  2024. dwRedirHeaderSize = QueryMetaData()->QueryRedirectHeaders()->QueryCCH();
  2025. if (dwRedirHeaderSize != 0)
  2026. {
  2027. if (!pbufResp->Resize(DIFF(pszTail - pszResp) + dwRedirHeaderSize ))
  2028. {
  2029. return FALSE;
  2030. }
  2031. pszTail = (CHAR *) pbufResp->QueryPtr() + (pszTail - pszResp);
  2032. pszResp = (CHAR *) pbufResp->QueryPtr();
  2033. memcpy(pszTail, QueryMetaData()->QueryRedirectHeaders()->QueryStr(),
  2034. dwRedirHeaderSize);
  2035. pszTail += dwRedirHeaderSize;
  2036. }
  2037. //
  2038. // Append any headers specified by filters
  2039. //
  2040. if ( cbA = QueryAdditionalRespHeaders()->QueryCB() )
  2041. {
  2042. if (!pbufResp->Resize(DIFF(pszTail - pszResp) + cbA ))
  2043. {
  2044. return FALSE;
  2045. }
  2046. pszTail = (CHAR *) pbufResp->QueryPtr() + (pszTail - pszResp);
  2047. pszResp = (CHAR *) pbufResp->QueryPtr();
  2048. memcpy( pszTail, QueryAdditionalRespHeaders()->QueryStr(), cbA );
  2049. pszTail += cbA;
  2050. }
  2051. //
  2052. // Figure out the total length to see if we have enough room for the "Content-Length"
  2053. // header
  2054. //
  2055. cb += DIFF(pszTail - pszResp) +
  2056. sizeof("Content-Length: ") - 1 +
  2057. 30 + // [magic #] max length of string containing numeric value of Content-Length
  2058. ( bHaveCustom ? sizeof("\r\n") - 1 : sizeof("\r\n\r\n") - 1 ) +
  2059. sizeof(TCHAR);
  2060. if ( !pbufResp->Resize( cb ))
  2061. {
  2062. return FALSE;
  2063. }
  2064. if ( pbufResp->QueryPtr() != pszResp )
  2065. {
  2066. pszTail = (CHAR *) pbufResp->QueryPtr() + (pszTail - pszResp);
  2067. pszResp = (CHAR *) pbufResp->QueryPtr();
  2068. }
  2069. //
  2070. // "Content-Length: <length>"
  2071. //
  2072. APPEND_NUMERIC_HEADER_TAILVAR( pszTail, "Content-Length: ", dwMsgSize, bHaveCustom ? "\r\n" : "\r\n\r\n" );
  2073. //
  2074. // pstrMovedMessage is something along the lines of "This object can be found at %s" and
  2075. // pstrURL is the URL that replaces the %s in pstrMovedMessage, so the max length of the
  2076. // actual HTML doc is the sum of the lengths of pstrMovedMessage and pstrURL
  2077. //
  2078. if (HTV_HEAD != QueryVerb())
  2079. {
  2080. DWORD cbMaxMsg = pstrMovedMessage->QueryCB() + strUrlInHTML.QueryCB();
  2081. if ( !pbufResp->Resize( DIFF(pszTail - pszResp) + cbMaxMsg ) )
  2082. {
  2083. return FALSE;
  2084. }
  2085. //
  2086. // Watch for pointer shift
  2087. //
  2088. if ( pbufResp->QueryPtr() != pszResp )
  2089. {
  2090. pszTail = (CHAR*) pbufResp->QueryPtr() + (pszTail - pszResp);
  2091. pszResp = (CHAR *) pbufResp->QueryPtr();
  2092. }
  2093. //
  2094. // Add the short HTML doc indicating the new location of the URL,
  2095. // making sure we have enough space for it
  2096. // Note we've already added the message body length. Add in the terminator.
  2097. //
  2098. ::sprintf( pszTail,
  2099. pstrMovedMessage->QueryStr(),
  2100. strUrlInHTML.QueryStr() );
  2101. }
  2102. return TRUE;
  2103. }
  2104. /*******************************************************************
  2105. NAME: SearchMapFile
  2106. SYNOPSIS: Searches the given mapfile for a shape that contains
  2107. the passed cooridinates
  2108. ENTRY: gFile - Open file Info
  2109. pchFile - Fully qualified path to file
  2110. pTsvcCache - Cache ID
  2111. hToken - Impersonation token
  2112. x - x cooridinate
  2113. y - y cooridinate
  2114. pstrURL - receives URL indicated in the map file
  2115. pfFound - Set to TRUE if a mapping was found
  2116. fMayCacheAccessToken - TRUE access token may be cached
  2117. RETURNS: TRUE if successful, FALSE on error
  2118. NOTES: This routine will attempt to cache the file. You must call
  2119. this function while impersonating the appropriate user
  2120. HISTORY:
  2121. Johnl 19-Sep-1994 Created
  2122. ********************************************************************/
  2123. #define SKIPNONWHITE( pch ) while ( *pch && \
  2124. !ISWHITEA( *pch )) \
  2125. pch++;
  2126. #define SKIPWHITE( pch ) while ( ISWHITE( *pch ) || \
  2127. *pch == ')' || \
  2128. *pch == '(' ) \
  2129. pch++;
  2130. BOOL SearchMapFile( LPTS_OPEN_FILE_INFO gFile,
  2131. CHAR * pchFile,
  2132. TSVC_CACHE * pTsvcCache,
  2133. HANDLE hToken,
  2134. INT x,
  2135. INT y,
  2136. STR * pstrURL,
  2137. BOOL * pfFound,
  2138. BOOL fMayCacheAccessToken )
  2139. {
  2140. DWORD BytesRead;
  2141. CHAR * pch;
  2142. CACHE_FILE_INFO CacheFileInfo;
  2143. CHAR * pchDefault = NULL;
  2144. CHAR * pchPoint = NULL;
  2145. CHAR * pchStart;
  2146. BOOL fRet = TRUE;
  2147. DWORD cchUrl;
  2148. UINT dis;
  2149. UINT bdis = UINT(-1);
  2150. STACK_STR( strDefaultURL, MAX_PATH );
  2151. *pfFound = FALSE;
  2152. //
  2153. // Retrieve the '\0' terminated map file
  2154. //
  2155. if ( !CheckOutCachedFileFromURI( (PVOID)gFile,
  2156. pchFile,
  2157. pTsvcCache,
  2158. hToken,
  2159. (BYTE **) &pch,
  2160. &BytesRead,
  2161. fMayCacheAccessToken,
  2162. &CacheFileInfo,
  2163. 0 )) // no code conversion
  2164. {
  2165. if ( !CheckOutCachedFile( pchFile,
  2166. pTsvcCache,
  2167. hToken,
  2168. (BYTE **) &pch,
  2169. &BytesRead,
  2170. fMayCacheAccessToken,
  2171. &CacheFileInfo,
  2172. 0 )) // no code conversion
  2173. {
  2174. return FALSE;
  2175. }
  2176. }
  2177. //
  2178. // Loop through the contents of the buffer and see what we've got
  2179. //
  2180. BOOL fComment = FALSE;
  2181. BOOL fIsNCSA = FALSE;
  2182. LPSTR pURL; // valid only if fIsNCSA is TRUE
  2183. while ( *pch )
  2184. {
  2185. fIsNCSA = FALSE;
  2186. //
  2187. // note: _tolower doesn't check case (tolower does)
  2188. //
  2189. switch ( (*pch >= 'A' && *pch <= 'Z') ? _tolower( *pch ) : *pch )
  2190. {
  2191. case '#':
  2192. fComment = TRUE;
  2193. break;
  2194. case '\r':
  2195. case '\n':
  2196. fComment = FALSE;
  2197. break;
  2198. //
  2199. // Rectangle
  2200. //
  2201. case 'r':
  2202. case 'o':
  2203. if ( !fComment &&
  2204. (!_strnicmp( "rect", pch, 4 )
  2205. // handles oval as a rect, as they are using
  2206. // the same specification format. Should do better.
  2207. || !_strnicmp( "oval", pch, 4 )) )
  2208. {
  2209. INT x1, y1, x2, y2;
  2210. SKIPNONWHITE( pch );
  2211. pURL = pch;
  2212. SKIPWHITE( pch );
  2213. if ( !isdigit((UCHAR)(*pch)) && *pch!='(' )
  2214. {
  2215. fIsNCSA = TRUE;
  2216. SKIPNONWHITE( pch );
  2217. }
  2218. x1 = GetNumber( &pch );
  2219. y1 = GetNumber( &pch );
  2220. x2 = GetNumber( &pch );
  2221. y2 = GetNumber( &pch );
  2222. if ( x >= x1 && x < x2 &&
  2223. y >= y1 && y < y2 )
  2224. {
  2225. if ( fIsNCSA )
  2226. pch = pURL;
  2227. goto Found;
  2228. }
  2229. //
  2230. // Skip the URL
  2231. //
  2232. if ( !fIsNCSA )
  2233. {
  2234. SKIPWHITE( pch );
  2235. SKIPNONWHITE( pch );
  2236. }
  2237. continue;
  2238. }
  2239. break;
  2240. //
  2241. // Circle
  2242. //
  2243. case 'c':
  2244. if ( !fComment &&
  2245. !_strnicmp( "circ", pch, 4 ))
  2246. {
  2247. INT xCenter, yCenter, xEdge, yEdge;
  2248. INT r1, r2;
  2249. SKIPNONWHITE( pch );
  2250. pURL = pch;
  2251. SKIPWHITE( pch );
  2252. if ( !isdigit((UCHAR)(*pch)) && *pch!='(' )
  2253. {
  2254. fIsNCSA = TRUE;
  2255. SKIPNONWHITE( pch );
  2256. }
  2257. //
  2258. // Get the center and edge of the circle
  2259. //
  2260. xCenter = GetNumber( &pch );
  2261. yCenter = GetNumber( &pch );
  2262. xEdge = GetNumber( &pch );
  2263. yEdge = GetNumber( &pch );
  2264. //
  2265. // If there's a yEdge, then we have the NCSA format, otherwise
  2266. // we have the CERN format, which specifies a radius
  2267. //
  2268. if ( yEdge != -1 )
  2269. {
  2270. r1 = ((yCenter - yEdge) * (yCenter - yEdge)) +
  2271. ((xCenter - xEdge) * (xCenter - xEdge));
  2272. r2 = ((yCenter - y) * (yCenter - y)) +
  2273. ((xCenter - x) * (xCenter - x));
  2274. if ( r2 <= r1 )
  2275. {
  2276. if ( fIsNCSA )
  2277. pch = pURL;
  2278. goto Found;
  2279. }
  2280. }
  2281. else
  2282. {
  2283. INT radius;
  2284. //
  2285. // CERN format, third param is the radius
  2286. //
  2287. radius = xEdge;
  2288. if ( SQR( xCenter - x ) + SQR( yCenter - y ) <=
  2289. SQR( radius ))
  2290. {
  2291. if ( fIsNCSA )
  2292. pch = pURL;
  2293. goto Found;
  2294. }
  2295. }
  2296. //
  2297. // Skip the URL
  2298. //
  2299. if ( !fIsNCSA )
  2300. {
  2301. SKIPWHITE( pch );
  2302. SKIPNONWHITE( pch );
  2303. }
  2304. continue;
  2305. }
  2306. break;
  2307. //
  2308. // Polygon
  2309. //
  2310. case 'p':
  2311. if ( !fComment &&
  2312. !_strnicmp( "poly", pch, 4 ))
  2313. {
  2314. double pgon[MAXVERTS][2];
  2315. DWORD i = 0;
  2316. CHAR * pchLast;
  2317. BOOL fOverflow = FALSE;
  2318. SKIPNONWHITE( pch );
  2319. pURL = pch;
  2320. SKIPWHITE( pch );
  2321. if ( !isdigit((UCHAR)(*pch)) && *pch!='(' )
  2322. {
  2323. fIsNCSA = TRUE;
  2324. SKIPNONWHITE( pch );
  2325. }
  2326. //
  2327. // Build the array of points
  2328. //
  2329. while ( *pch && *pch != '\r' && *pch != '\n' )
  2330. {
  2331. pgon[i][0] = GetNumber( &pch );
  2332. //
  2333. // Did we hit the end of the line (and go past the URL)?
  2334. //
  2335. if ( pgon[i][0] != -1 )
  2336. {
  2337. pgon[i][1] = GetNumber( &pch );
  2338. }
  2339. else
  2340. {
  2341. break;
  2342. }
  2343. if ( i < MAXVERTS-1 )
  2344. {
  2345. i++;
  2346. }
  2347. else
  2348. {
  2349. fOverflow = TRUE;
  2350. }
  2351. }
  2352. pgon[i][X] = -1;
  2353. if ( !fOverflow && pointinpoly( x, y, pgon ))
  2354. {
  2355. if ( fIsNCSA )
  2356. pch = pURL;
  2357. goto Found;
  2358. }
  2359. //
  2360. // Skip the URL
  2361. //
  2362. if ( !fIsNCSA )
  2363. {
  2364. SKIPWHITE( pch );
  2365. SKIPNONWHITE( pch );
  2366. }
  2367. continue;
  2368. }
  2369. else if ( !fComment &&
  2370. !_strnicmp( "point", pch, 5 ))
  2371. {
  2372. INT x1,y1;
  2373. SKIPNONWHITE( pch );
  2374. pURL = pch;
  2375. SKIPWHITE( pch );
  2376. SKIPNONWHITE( pch );
  2377. x1 = GetNumber( &pch );
  2378. y1 = GetNumber( &pch );
  2379. x1 -= x;
  2380. y1 -= y;
  2381. dis = x1*x1 + y1*y1;
  2382. if ( dis < bdis )
  2383. {
  2384. pchPoint = pURL;
  2385. bdis = dis;
  2386. }
  2387. }
  2388. break;
  2389. //
  2390. // Default URL
  2391. //
  2392. case 'd':
  2393. if ( !fComment &&
  2394. !_strnicmp( "def", pch, 3 ) )
  2395. {
  2396. //
  2397. // Skip "default" (don't skip white space)
  2398. //
  2399. SKIPNONWHITE( pch );
  2400. pchDefault = pch;
  2401. //
  2402. // Skip URL
  2403. //
  2404. SKIPWHITE( pch );
  2405. SKIPNONWHITE( pch );
  2406. continue;
  2407. }
  2408. break;
  2409. }
  2410. pch++;
  2411. SKIPWHITE( pch );
  2412. }
  2413. //
  2414. // If we didn't find a mapping and a default was specified, use
  2415. // the default URL
  2416. //
  2417. if ( pchPoint )
  2418. {
  2419. pch = pchPoint;
  2420. goto Found;
  2421. }
  2422. if ( pchDefault )
  2423. {
  2424. pch = pchDefault;
  2425. goto Found;
  2426. }
  2427. IF_DEBUG( PARSING )
  2428. {
  2429. DBGPRINTF(( DBG_CONTEXT,
  2430. "[SearchMapFile] No mapping found for (%d,%d)\n",
  2431. x,
  2432. y ));
  2433. }
  2434. goto Exit;
  2435. Found:
  2436. //
  2437. // pch should point to the white space immediately before the URL
  2438. //
  2439. SKIPWHITE( pch );
  2440. pchStart = pch;
  2441. SKIPNONWHITE( pch );
  2442. //
  2443. // Determine the length of the URL and copy it out
  2444. //
  2445. cchUrl = DIFF(pch - pchStart);
  2446. if ( !pstrURL->Resize( cchUrl + 1 ))
  2447. {
  2448. fRet = FALSE;
  2449. goto Exit;
  2450. }
  2451. memcpy( pstrURL->QueryStr(),
  2452. pchStart,
  2453. cchUrl );
  2454. pstrURL->SetLen(cchUrl);
  2455. if ( !pstrURL->Unescape() )
  2456. {
  2457. fRet = FALSE;
  2458. goto Exit;
  2459. }
  2460. IF_DEBUG( PARSING )
  2461. {
  2462. DBGPRINTF((DBG_CONTEXT,
  2463. "[SearchMapFile] Mapping for (%d,%d) is %s\n",
  2464. x,
  2465. y,
  2466. pstrURL->QueryStr() ));
  2467. }
  2468. *pfFound = TRUE;
  2469. Exit:
  2470. TCP_REQUIRE( CheckInCachedFile( pTsvcCache,
  2471. &CacheFileInfo ));
  2472. return fRet;
  2473. }
  2474. int pointinpoly(int point_x, int point_y, double pgon[MAXVERTS][2])
  2475. {
  2476. int i, numverts, inside_flag, xflag0;
  2477. int crossings;
  2478. double *p, *stop;
  2479. double tx, ty, y;
  2480. for (i = 0; pgon[i][X] != -1 && i < MAXVERTS; i++)
  2481. ;
  2482. numverts = i;
  2483. crossings = 0;
  2484. tx = (double) point_x;
  2485. ty = (double) point_y;
  2486. y = pgon[numverts - 1][Y];
  2487. p = (double *) pgon + 1;
  2488. if ((y >= ty) != (*p >= ty))
  2489. {
  2490. if ((xflag0 = (pgon[numverts - 1][X] >= tx)) == (*(double *) pgon >= tx))
  2491. {
  2492. if (xflag0)
  2493. crossings++;
  2494. }
  2495. else
  2496. {
  2497. crossings += (pgon[numverts - 1][X] - (y - ty) *
  2498. (*(double *) pgon - pgon[numverts - 1][X]) /
  2499. (*p - y)) >= tx;
  2500. }
  2501. }
  2502. stop = pgon[numverts];
  2503. for (y = *p, p += 2; p < stop; y = *p, p += 2)
  2504. {
  2505. if (y >= ty)
  2506. {
  2507. while ((p < stop) && (*p >= ty))
  2508. p += 2;
  2509. if (p >= stop)
  2510. break;
  2511. if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx))
  2512. {
  2513. if (xflag0)
  2514. crossings++;
  2515. }
  2516. else
  2517. {
  2518. crossings += (*(p - 3) - (*(p - 2) - ty) *
  2519. (*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx;
  2520. }
  2521. }
  2522. else
  2523. {
  2524. while ((p < stop) && (*p < ty))
  2525. p += 2;
  2526. if (p >= stop)
  2527. break;
  2528. if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx))
  2529. {
  2530. if (xflag0)
  2531. crossings++;
  2532. }
  2533. else
  2534. {
  2535. crossings += (*(p - 3) - (*(p - 2) - ty) *
  2536. (*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx;
  2537. }
  2538. }
  2539. }
  2540. inside_flag = crossings & 0x01;
  2541. return (inside_flag);
  2542. }
  2543. /*******************************************************************
  2544. NAME: GetNumber
  2545. SYNOPSIS: Scans for the beginning of a number and places the
  2546. pointer after the found number
  2547. ENTRY: ppch - Place to begin. Will be set to character after
  2548. the last digit of the found number
  2549. RETURNS: Integer value of found number (or -1 if not found)
  2550. HISTORY:
  2551. Johnl 19-Sep-1994 Created
  2552. ********************************************************************/
  2553. INT GetNumber( CHAR * * ppch )
  2554. {
  2555. CHAR * pch = *ppch;
  2556. INT n;
  2557. //
  2558. // Make sure we don't get into the URL
  2559. //
  2560. while ( *pch &&
  2561. !isdigit( (UCHAR)(*pch) ) &&
  2562. !isalpha( (UCHAR)(*pch) ) &&
  2563. *pch != '/' &&
  2564. *pch != '\r' &&
  2565. *pch != '\n' )
  2566. {
  2567. pch++;
  2568. }
  2569. if ( !isdigit( (UCHAR)(*pch) ) )
  2570. return -1;
  2571. n = atoi( pch );
  2572. while ( isdigit( (UCHAR)(*pch) ))
  2573. pch++;
  2574. *ppch = pch;
  2575. return n;
  2576. }
  2577. BOOL
  2578. DisposeOpenURIFileInfo(
  2579. IN PVOID pvOldBlock
  2580. )
  2581. /*++
  2582. Routine Description
  2583. Close an open URI file information block. This involves closing the
  2584. handle if the file information is valid and freeing and associated
  2585. structures.
  2586. Arguments
  2587. pvOldBlock - pointer to the URI file information block.
  2588. Returns
  2589. TRUE if operation successful.
  2590. --*/
  2591. {
  2592. PW3_URI_INFO pURIInfo;
  2593. PVOID pvBlob;
  2594. BOOL bSuccess;
  2595. pURIInfo = (PW3_URI_INFO ) pvOldBlock;
  2596. if (pURIInfo->pszName != NULL) {
  2597. LocalFree(pURIInfo->pszName);
  2598. }
  2599. if (pURIInfo->pszUnmappedName != NULL) {
  2600. LocalFree(pURIInfo->pszUnmappedName);
  2601. }
  2602. if( pURIInfo->pMetaData != NULL ) {
  2603. TsFreeMetaData(pURIInfo->pMetaData->QueryCacheInfo() );
  2604. }
  2605. if ( pURIInfo->pOpenFileInfo != NULL ) {
  2606. TsDerefURIFile(pURIInfo->pOpenFileInfo);
  2607. }
  2608. return( TRUE );
  2609. } // DisposeOpenURIFileInfo