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.

807 lines
26 KiB

  1. /*--
  2. Copyright (c) 1995-2000 Microsoft Corporation. All Rights Reserved.
  3. Module Name: request.CPP
  4. Abstract: per-Connection thread
  5. --*/
  6. #include "pch.h"
  7. #pragma hdrstop
  8. #include "httpd.h"
  9. BOOL IsLocalFile(PWSTR wszFile);
  10. void CHttpRequest::HandleRequest()
  11. {
  12. int err = 1;
  13. RESPONSESTATUS ret = STATUS_BADREQ;
  14. DWORD dwLength = 0;
  15. HANDLE hFile = INVALID_HANDLE_VALUE;
  16. DWORD dwAttrib;
  17. HRINPUT hi;
  18. BOOL fSendDirectoryList = FALSE;
  19. m_rs = STATUS_OK;
  20. TraceTag(ttidWebServer, "HandleRequest: entry. Current Authorization Level = %d",m_AuthLevelGranted);
  21. hi = m_bufRequest.RecvHeaders(m_socket);
  22. // Even if we get an error, continue processing, because we may have read in
  23. // binary data and have a filter installed that can convert it for us.
  24. if (hi == INPUT_TIMEOUT)
  25. {
  26. // Either we have no data, or there's no filter to read in data, return
  27. if (!g_pVars->m_fFilters || m_bufRequest.Count() == 0)
  28. {
  29. m_fKeepAlive = FALSE;
  30. return; // don't send any data across socket, just close it.
  31. }
  32. }
  33. if (hi == INPUT_ERROR)
  34. {
  35. if (!g_pVars->m_fFilters || m_bufRequest.Count() == 0)
  36. {
  37. m_fKeepAlive = FALSE;
  38. myretleave(m_rs = STATUS_BADREQ,61);
  39. }
  40. }
  41. if (g_pVars->m_fFilters &&
  42. ! CallFilter(SF_NOTIFY_READ_RAW_DATA))
  43. myleave(231);
  44. // parse the request headers
  45. if (!ParseHeaders())
  46. myleave(50);
  47. // There were numerous filter calls in ParseHeaders, make sure none requested end of connection
  48. if (m_pFInfo && m_pFInfo->m_fFAccept==FALSE)
  49. myleave(63); // don't change m_rs, filter set it as appropriate
  50. if (m_dwVersion >= MAKELONG(0, 2))
  51. myretleave(m_rs = STATUS_NOTSUPP, 111);
  52. // read body of request, if any
  53. if (!ReadPostData(g_pVars->m_dwPostReadSize,TRUE))
  54. myleave(233);
  55. // if we're in middle of authenticating, jump past other stuff to end
  56. if (m_NTLMState.m_Conversation == NTLM_PROCESSING)
  57. myretleave(m_rs = STATUS_UNAUTHORIZED, 65);
  58. // check if we successfully mapped the VRoot
  59. if (!m_wszPath)
  60. myretleave(m_rs = STATUS_NOTFOUND, 59);
  61. if (m_wszExt && wcsstr(m_wszExt, L"::"))
  62. {
  63. // infamous ::$DATA bug. Don't allow extensions with :: in them
  64. myretleave(m_rs = STATUS_NOTFOUND, 74);
  65. }
  66. if ( !CheckAuth())
  67. {
  68. if (g_pVars->m_fFilters)
  69. CallFilter(SF_NOTIFY_ACCESS_DENIED);
  70. myretleave(m_rs = STATUS_UNAUTHORIZED, 52);
  71. }
  72. if ((-1) == (dwAttrib = GetFileAttributes(m_wszPath)))
  73. myretleave(m_rs = GLEtoStatus(GetLastError()), 60);
  74. if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)
  75. {
  76. // if it doesn't end in '/' send a redirect
  77. int iLen = strlen(m_pszURL);
  78. if (m_pszURL[iLen-1]!='/' && m_pszURL[iLen-1]!='\\')
  79. {
  80. // SPECIAL HACK: we have allocated one extra char in m_pszURL already in case
  81. // we needed to send a redirect back (see parser.cpp)
  82. m_pszURL[iLen]='/';
  83. m_pszURL[iLen+1]=0;
  84. CHttpResponse resp(m_socket, STATUS_MOVED, GetConnHeader(), this);
  85. m_rs = STATUS_MOVED;
  86. resp.SendRedirect(m_pszURL); // send a special redirect body
  87. m_pszURL[iLen]=0; // restore m_pszURL
  88. err = 0;
  89. goto done;
  90. }
  91. // If there's no default page then we send dir list (later, after some
  92. // extra checking). If there is a default page match m_wszPath is
  93. // updated appropriatly. This must be done before script processing.
  94. fSendDirectoryList = !MapDirToDefaultPage();
  95. }
  96. // Set the socket to be a blocking one
  97. // Our web server implementaion for send and recv assumes it to be a blocking socket
  98. // This is only a temp fix...
  99. int nErrorVal = 0;
  100. u_long ulSockBlock = 0;
  101. if((nErrorVal = WSAEventSelect(m_socket, NULL, 0)) == SOCKET_ERROR)
  102. {
  103. TraceTag(ttidWebServer, "Disabling non blocking sock WSAEventSelect() failed - %d ",WSAGetLastError());
  104. }
  105. if(nErrorVal == 0 )
  106. {
  107. TraceTag(ttidWebServer, "Disabled non blocking sock WSAEventSelect() Succeeded");
  108. if ((nErrorVal = ioctlsocket(m_socket, FIONBIO, &ulSockBlock)) == SOCKET_ERROR)
  109. {
  110. TraceTag(ttidWebServer, "ioctlsocket() - Setting the blocking socket failed - %d ",WSAGetLastError());
  111. }
  112. else
  113. {
  114. TraceTag(ttidWebServer, "ioctlsocket() - Setting the blocking socket Succeeded");
  115. }
  116. }
  117. // Attempt to send even if its non blocking socket ;;; must be changed later !!
  118. // HandleScript returns true if page maps to ASP or ISAPI DLL, regardless of whether
  119. // we have correct permissions, component was included, there was an error, etc.
  120. // HandleScript sets its own errors.
  121. if ( !fSendDirectoryList && HandleScript() )
  122. {
  123. err = (m_rs != STATUS_OK); // Only send message on internal error.
  124. goto done;
  125. }
  126. // if it's not an ISAPI or ASP, we cant handle anything but GET and HEAD
  127. if (m_idMethod == TOK_UNKNOWN_VERB)
  128. myretleave(m_rs = STATUS_NOTIMPLEM, 54);
  129. // check permissions
  130. if (!(m_dwPermissions & HSE_URL_FLAGS_READ))
  131. {
  132. if (g_pVars->m_fFilters)
  133. CallFilter(SF_NOTIFY_ACCESS_DENIED);
  134. myretleave(m_rs = STATUS_FORBIDDEN, 55);
  135. }
  136. if (fSendDirectoryList)
  137. {
  138. // In this case there's no default page but directory browsing is turned
  139. // off. Return same error code IIS does.
  140. if (FALSE == g_pVars->m_fDirBrowse)
  141. {
  142. if (g_pVars->m_fFilters)
  143. CallFilter(SF_NOTIFY_ACCESS_DENIED);
  144. myretleave(m_rs = STATUS_FORBIDDEN,78);
  145. }
  146. if (!EmitDirListing())
  147. myretleave(m_rs = STATUS_INTERNALERR, 53);
  148. err=0;
  149. goto done;
  150. }
  151. // If we get to here then we're just sending a plain old static file.
  152. // try to open the file & get the length
  153. if (INVALID_HANDLE_VALUE == (hFile = MyOpenReadFile(m_wszPath)))
  154. myretleave(m_rs = GLEtoStatus(GetLastError()), 56);
  155. // get the size
  156. if (((DWORD)-1) == (dwLength = GetFileSize(hFile, 0)))
  157. myretleave(m_rs = GLEtoStatus(GetLastError()), 57);
  158. // if it's a GET check if-modified-since
  159. if ((m_idMethod==TOK_GET) && IsNotModified(hFile, dwLength))
  160. myretleave(m_rs = STATUS_NOTMODIFIED, 58);
  161. // if it's a HTTP/0.9 request, just send back the body. NO headers
  162. if (m_dwVersion <= MAKELONG(9, 0))
  163. {
  164. TraceTag(ttidWebServer, "Sending HTTP/0.9 response with NO headers");
  165. SendFile(m_socket, hFile, this);
  166. }
  167. else
  168. {
  169. // create a response object & send response
  170. // if it's a head request, skip the actual body
  171. CHttpResponse resp(m_socket, STATUS_OK, GetConnHeader(),this);
  172. m_rs = STATUS_OK;
  173. resp.SetBody(((m_idMethod==TOK_HEAD) ? NULL : hFile), m_wszExt, dwLength);
  174. resp.SendResponse();
  175. }
  176. TraceTag(ttidWebServer, "HTTP Request SUCCEEDED");
  177. err = 0;
  178. ret = m_rs = STATUS_OK;
  179. done:
  180. MyCloseHandle(hFile);
  181. if (err)
  182. {
  183. // end this session ASAP if we've encountered an error.
  184. if (m_rs == STATUS_INTERNALERR)
  185. {
  186. m_fKeepAlive = FALSE;
  187. }
  188. // if there's been an error but we're doing keep-alives, it's possible
  189. // there's POST data we haven't read in. We need to read this
  190. // before sending response, or next time we recv() HTTP headers we'll
  191. // start in middle of POST rather than in the new request.
  192. if (m_fKeepAlive)
  193. {
  194. DEBUGCHK(m_dwContentLength >= m_bufRequest.Count());
  195. TraceTag(ttidWebServer, "HTTP: HandleRequest: Error occured on keepalive, reading %d POST bytes now",m_dwContentLength - m_bufRequest.Count());
  196. ReadPostData(m_dwContentLength - m_bufRequest.Count(),FALSE);
  197. }
  198. CHttpResponse resp(m_socket, m_rs, GetConnHeader(),this);
  199. resp.SetDefaultBody();
  200. resp.SendResponse();
  201. TraceTag(ttidWebServer, "HTTP Request FAILED: GLE=%d err=%d status=%d (%d, %s)",GetLastError(), err, ret, rgStatus[ret].dwStatusNumber, rgStatus[ret].pszStatusText);
  202. }
  203. // if in middle of NTLM request, don't do this stuff
  204. if (m_NTLMState.m_fHaveCtxtHandle != NTLM_PROCESSING)
  205. {
  206. if (g_pVars->m_fFilters)
  207. {
  208. CallFilter(SF_NOTIFY_END_OF_REQUEST);
  209. CallFilter(SF_NOTIFY_LOG);
  210. }
  211. g_pVars->m_pLog->WriteLog(this);
  212. }
  213. return;
  214. }
  215. BOOL CHttpRequest::IsNotModified(HANDLE hFile, DWORD dwLength)
  216. {
  217. if (m_ftIfModifiedSince.dwLowDateTime || m_ftIfModifiedSince.dwHighDateTime)
  218. {
  219. FILETIME ftModified;
  220. int iTemp;
  221. if (!GetFileTime(hFile, NULL, NULL, &ftModified))
  222. {
  223. TraceTag(ttidWebServer, "GetFileTime(%08x) failed", hFile);
  224. return FALSE; // assume it is modified
  225. }
  226. iTemp = CompareFileTime(&m_ftIfModifiedSince, &ftModified);
  227. TraceTag(ttidWebServer, "IfModFT=%08x:%08x ModFT=%08x:%08x "
  228. "IfModLen=%d Len=%d Compare=%d",
  229. m_ftIfModifiedSince.dwHighDateTime,
  230. m_ftIfModifiedSince.dwLowDateTime ,
  231. ftModified.dwHighDateTime,
  232. ftModified.dwLowDateTime,
  233. m_dwIfModifiedLength, dwLength, iTemp);
  234. if ((iTemp >= 0) && (m_dwIfModifiedLength==0 || (dwLength==m_dwIfModifiedLength)))
  235. return TRUE; // not modified
  236. }
  237. return FALSE; // assume modified
  238. }
  239. RESPONSESTATUS CHttpRequest::GLEtoStatus(int iGLE)
  240. {
  241. switch (iGLE)
  242. {
  243. case ERROR_PATH_NOT_FOUND:
  244. case ERROR_FILE_NOT_FOUND:
  245. case ERROR_INVALID_NAME:
  246. return STATUS_NOTFOUND;
  247. case ERROR_ACCESS_DENIED:
  248. return STATUS_FORBIDDEN;
  249. default:
  250. return STATUS_INTERNALERR;
  251. }
  252. }
  253. BOOL CHttpRequest::MapDirToDefaultPage(void)
  254. {
  255. WCHAR wszTemp[MAX_PATH];
  256. // make temp copy of dir path. append \ if reqd
  257. wcscpy(wszTemp, m_wszPath);
  258. int iLen = wcslen(wszTemp);
  259. // if we get here we have a trailing \ or / (otherwise we sent a redirect instead)
  260. DEBUGCHK(wszTemp[iLen-1]=='/' || wszTemp[iLen-1]=='\\');
  261. if (!g_pVars->m_wszDefaultPages)
  262. return FALSE;
  263. for (PWSTR wszNext=g_pVars->m_wszDefaultPages; *wszNext; wszNext+=(1+wcslen(wszNext)))
  264. {
  265. wcscpy(wszTemp+iLen, wszNext);
  266. if ((-1) != GetFileAttributes(wszTemp))
  267. {
  268. PWSTR pwsz;
  269. // found something
  270. TraceTag(ttidWebServer, "Converting dir path (%s) to default page(%s)", m_wszPath, wszTemp);
  271. MyFree(m_wszPath);
  272. m_wszPath = MySzDupW(wszTemp);
  273. MyFree(m_wszExt);
  274. if (pwsz = wcsrchr(m_wszPath, '.'))
  275. m_wszExt = MySzDupW(pwsz);
  276. return TRUE;
  277. }
  278. }
  279. TraceTag(ttidWebServer, "No default page found in dir path (%s)", m_wszPath);
  280. return FALSE;
  281. }
  282. // const char cszDirHeader1[] = "<head><title>%s - %s</title></head><body><H1>%s - %s</H1><hr>";
  283. // const char cszDirHeader2[] = "<pre><A HREF=\"%s\">%S</A><br><br>";
  284. // const char cszDirEntry[] = "%12S %10S %12d <A HREF=\"%S\">%S</A><br>";
  285. const char cszDirFooter[] = "</pre><hr></body>";
  286. const char cszDirHeader3[] = "<head><title>";
  287. const char cszDirHeader4[] = "</title></head><body><H1>";
  288. const char cszDirHeader5[] = "</H1><hr><pre>";
  289. const char cszDirHeader6[] = "<A HREF=\"";
  290. const char cszDirHeader7[] = "</A><br>";
  291. const char cszDirEntry1[] = "<A HREF=\"";
  292. const char cszDirEntry2[] = "</A><br>";
  293. #define MAXENTRYSIZE 150+MAX_PATH+MAX_PATH
  294. BOOL CHttpRequest::EmitDirListing(void)
  295. {
  296. WCHAR wszBuf1[MAX_PATH+10];
  297. WCHAR wszBuf2[MAX_PATH];
  298. char szHostBuf[MAX_PATH];
  299. char szBuf[MAX_PATH+1]; // holds temp values
  300. // generate listing into a buffer
  301. int iSize = DIRBUFSIZE;
  302. int iLen;
  303. PSTR pszBuf = MyRgAllocNZ(CHAR, iSize);
  304. int iUsed = 0;
  305. PSTR pszTrav = pszBuf;
  306. if (!pszBuf)
  307. return FALSE;
  308. // we know have enough space for the headers
  309. if ( 0 != gethostname(szHostBuf, sizeof(szHostBuf)))
  310. szHostBuf[0] = '\0';
  311. // iNext += sprintf(pszBuf+iNext, cszDirHeader1, szHostBuf, m_pszURL, szHostBuf, m_pszURL);
  312. pszTrav = strcpyEx(pszTrav,cszDirHeader3);
  313. pszTrav = strcpyEx(pszTrav,szHostBuf);
  314. *pszTrav++ = ' '; *pszTrav++ = '-'; *pszTrav++ = ' ';
  315. pszTrav = strcpyEx(pszTrav,m_pszURL);
  316. pszTrav = strcpyEx(pszTrav,cszDirHeader4);
  317. pszTrav = strcpyEx(pszTrav,szHostBuf);
  318. *pszTrav++ = ' '; *pszTrav++ = '-'; *pszTrav++ = ' ';
  319. pszTrav = strcpyEx(pszTrav,m_pszURL);
  320. pszTrav = strcpyEx(pszTrav,cszDirHeader5);
  321. // end sprintf replacement
  322. // find the parent path ignore the trailing slash (always present)
  323. char chSave = 0;
  324. for (int i=strlen(m_pszURL)-2; i>=0; i--)
  325. {
  326. if (m_pszURL[i]=='/' || m_pszURL[i]=='\\')
  327. {
  328. // Holds the string [Link to parent directory], which is displayed to users
  329. // (who would probably like the message to come in their native language and not necessarily English)
  330. WCHAR wszParentDirectory[128];
  331. CHAR szParentDirectory[128];
  332. // save & restore one char to temporarily truncate the URL at the parent path (incl slash)
  333. char chSave=m_pszURL[i+1];
  334. m_pszURL[i+1] = 0;
  335. // iNext += sprintf(pszBuf+iNext, cszDirHeader2, m_pszURL, CELOADSZ(IDS_LINKTOPARENTDIR));
  336. pszTrav = strcpyEx(pszTrav,cszDirHeader6);
  337. pszTrav = strcpyEx(pszTrav,m_pszURL);
  338. *pszTrav++ = '"'; *pszTrav++ = '>';
  339. LoadString(g_hInst,IDS_LINKTOPARENTDIR,wszParentDirectory,celems(wszParentDirectory));
  340. MyW2A(wszParentDirectory,szParentDirectory,sizeof(szParentDirectory));
  341. pszTrav = strcpyEx(pszTrav,szParentDirectory);
  342. pszTrav = strcpyEx(pszTrav,cszDirHeader7);
  343. // End sprintf replacement
  344. m_pszURL[i+1] = chSave;
  345. break;
  346. }
  347. }
  348. // create Find pattern
  349. DEBUGCHK(m_wszPath[wcslen(m_wszPath)-1]=='/' || m_wszPath[wcslen(m_wszPath)-1]=='\\');
  350. WIN32_FIND_DATA fd;
  351. wcscpy(wszBuf1, m_wszPath);
  352. wcscat(wszBuf1, L"*");
  353. // now iterate the files & subdirs (if any)
  354. HANDLE hFile = FindFirstFile(wszBuf1, &fd);
  355. if (INVALID_HANDLE_VALUE != hFile)
  356. {
  357. do
  358. {
  359. // check for space
  360. iUsed = (int)((INT_PTR)(pszTrav - pszBuf));
  361. if ((iSize-iUsed) < MAXENTRYSIZE)
  362. {
  363. if (!(pszBuf = MyRgReAlloc(CHAR, pszBuf, iSize, iSize+DIRBUFSIZE)))
  364. return FALSE;
  365. iSize += DIRBUFSIZE;
  366. pszTrav = pszBuf + iUsed;
  367. }
  368. // convert date
  369. FILETIME ftLocal;
  370. SYSTEMTIME stLocal;
  371. FileTimeToLocalFileTime(&fd.ftLastAccessTime, &ftLocal);
  372. FileTimeToSystemTime(&ftLocal, &stLocal);
  373. // format date
  374. GetDateFormat(LOCALE_SYSTEM_DEFAULT, DATE_SHORTDATE, &stLocal, NULL, wszBuf1, CCHSIZEOF(wszBuf1));
  375. GetTimeFormat(LOCALE_SYSTEM_DEFAULT, TIME_NOSECONDS, &stLocal, NULL, wszBuf2, CCHSIZEOF(wszBuf2));
  376. // generate HTML entry
  377. // iNext += sprintf(pszBuf+iNext, cszDirEntry, wszBuf1, wszBuf2, fd.nFileSizeLow, fd.cFileName, fd.cFileName);
  378. // const char cszDirEntry[] = "%12S %10S %12d <A HREF=\"%S\">%S</A><br>";
  379. // Copy the Date. We right justify it and put 2 spaces between it and beginning of time info
  380. iLen = MyW2A(wszBuf1,szBuf,12) - 1;
  381. memset(pszTrav,' ',12 - iLen);
  382. memcpy(pszTrav + 12 - iLen,szBuf,iLen);
  383. memset(pszTrav+12,' ',2);
  384. pszTrav += 14;
  385. // Copy the time.
  386. iLen = MyW2A(wszBuf2,szBuf,10) - 1;
  387. memset(pszTrav,' ',10 - iLen);
  388. memcpy(pszTrav + 10 - iLen,szBuf,iLen);
  389. memset(pszTrav+10,' ',2);
  390. pszTrav += 12;
  391. // The file length.
  392. _itoa(fd.nFileSizeLow,szBuf,10);
  393. iLen = strlen(szBuf);
  394. memset(pszTrav,' ',12 - iLen);
  395. memcpy(pszTrav + 12 - iLen,szBuf,iLen);
  396. memset(pszTrav+12,' ',2);
  397. pszTrav += 14;
  398. pszTrav = strcpyEx(pszTrav,cszDirEntry1);
  399. // Copy the file name to be displayed
  400. MyW2A(fd.cFileName,szBuf,sizeof(szBuf));
  401. pszTrav = strcpyEx(pszTrav,szBuf);
  402. *pszTrav++ = '\"'; *pszTrav++ = '>';
  403. // Copy the file name again, this time in the <A HREF..> statement.
  404. pszTrav = strcpyEx(pszTrav,szBuf);
  405. pszTrav = strcpyEx(pszTrav,cszDirEntry2);
  406. // End sprintf replacement
  407. }
  408. while (FindNextFile(hFile, &fd));
  409. // CloseHandle(hFile); // This throws an exception on WinNT, use FindClose instead
  410. FindClose(hFile);
  411. }
  412. // emit footer
  413. pszTrav = strcpyEx(pszTrav, cszDirFooter);
  414. // create a response object & attach this body, then send headers & body
  415. CHttpResponse resp(m_socket, STATUS_OK, (m_fKeepAlive ? CONN_KEEP : CONN_CLOSE),this);
  416. m_rs = STATUS_OK;
  417. resp.SetBody(pszBuf, cszTextHtml);
  418. resp.SendResponse();
  419. // free the buffer
  420. MyFree(pszBuf);
  421. return TRUE;
  422. }
  423. void CHttpRequest::Init()
  424. {
  425. memset(this, 0, sizeof(*this));
  426. m_dwSig = CHTTPREQUEST_SIG;
  427. m_pFInfo = CreateCFilterInfo(); // even if we have 0 filters allocate this, filter stuff assume it exists
  428. }
  429. // Handle info must stay valid between net requests
  430. // Fcn is called if we successully authenticate (don't need them anymore) or
  431. // when the net session ends, to prevent a mem leak.
  432. CHttpRequest::~CHttpRequest()
  433. {
  434. TraceTag(ttidWebServer, "Calling CHttpRequest destructor");
  435. FreeHeaders();
  436. FreeAuth();
  437. if (m_pFInfo)
  438. {
  439. delete m_pFInfo;
  440. m_pFInfo = 0;
  441. }
  442. // only now do we free the NTLM library, assuming it made it past the
  443. // NTLM_NO_INIT_LIB stage.
  444. if (m_NTLMState.m_Conversation != NTLM_NO_INIT_LIB)
  445. {
  446. FreeNTLMHandles(&m_NTLMState);
  447. }
  448. }
  449. // Called right before each HTTP Request (multiple times for a persisted session)
  450. // Frees request specific data, like destructor but keeps session data
  451. // (Filter alloc'd mem, NTLM state) in place.
  452. BOOL CHttpRequest::ReInit()
  453. {
  454. TraceTag(ttidWebServer, "Calling CHttpRequest ReInit (between requests)");
  455. FreeHeaders();
  456. FreeAuth();
  457. m_bufRequest.Reset();
  458. m_bufRespHeaders.Reset();
  459. m_bufRespBody.Reset();
  460. if (m_pFInfo)
  461. {
  462. if ( !m_pFInfo->ReInit() )
  463. return FALSE;
  464. }
  465. // NTLM stuff. If we're in middle of conversation, don't delete NTLM state info
  466. // We never free the library here, only in the destructor.
  467. if (g_pVars->m_fNTLMAuth && m_NTLMState.m_Conversation == NTLM_DONE)
  468. {
  469. FreeNTLMHandles(&m_NTLMState);
  470. // Set the flags so that we know the context isn't initialized. This
  471. // would be relevent if user typed the wrong password.
  472. m_NTLMState.m_Conversation = NTLM_NO_INIT_CONTEXT;
  473. }
  474. // Certain values need to be re-zeroed
  475. m_dwContentLength = m_dwIfModifiedLength = m_dwVersion = 0;
  476. m_fKeepAlive = FALSE;
  477. return TRUE;
  478. }
  479. // If a filter makes a call to ServerSupportFunction to SEND_RESPONSE_HEADERS,
  480. // the http engine no longer directly display the requested page. In this
  481. // case the filter acts like an ISAPI extension, it's responsible for returning
  482. // it's own content. (Like IIS).
  483. // This isn't the same as ASP's concept of having sent headers. ASP's sent headers
  484. // stops script from doing other calls to send headers. If Filter wants to send
  485. // more headers (which will appear in client browser window) fine, we copy IIS.
  486. // This is small enough that it's not worth putting into a stub.
  487. BOOL CHttpRequest::FilterNoResponse(void)
  488. {
  489. if (m_pFInfo && m_pFInfo->m_fSentHeaders)
  490. {
  491. return TRUE;
  492. }
  493. return FALSE;
  494. }
  495. // Note: This has not been made part of the ISAPI component because we need
  496. // to do checking as to whether the requested operation is valid given our current
  497. // component set.
  498. BOOL CHttpRequest::HandleScript()
  499. {
  500. DEBUG_CODE_INIT;
  501. BOOL ret = TRUE; // Is page a ASP/ISAPI?
  502. CHAR szBuf[MAX_PATH + 1];
  503. DWORD dwLen = sizeof(szBuf);
  504. PWSTR wszPath;
  505. // Check if this path points at a VROOT with extension mapping
  506. // enabled. If so, we need to treat this as an extension, so we
  507. // update the script type for the VROOT and modify the dll path.
  508. if (wszPath = g_pVars->m_pVroots->MapExtToPath (m_pszURL, NULL))
  509. {
  510. if (m_wszPath != NULL)
  511. MyFree(m_wszPath);
  512. m_wszPath = MySzDupW(wszPath);
  513. m_VRootScriptType = SCRIPT_TYPE_EXTENSION;
  514. }
  515. // Set path translated here.
  516. if (m_pszPathInfo)
  517. strcpy(szBuf,m_pszPathInfo);
  518. else
  519. {
  520. // Fixes BUG 11270. On IIS, if no PATH_INFO variable is used,
  521. // PATH_TRANSLATED is set to the mapping of the "/" directory, no matter
  522. // what directory the isapi was called from.
  523. strcpy(szBuf,"/");
  524. }
  525. if ( !g_pVars->m_fExtensions || !ServerSupportFunction(HSE_REQ_MAP_URL_TO_PATH,szBuf,&dwLen,0))
  526. m_pszPathTranslated = NULL;
  527. else
  528. m_pszPathTranslated = MySzDupA(szBuf);
  529. // check if it's an executable ISAPI
  530. // If the VRoot was not executable we would download the dll, regardless of whether
  531. // Extensions are a component or not. This is the way IIS does it, so we follow suit.
  532. // If the file extension is .dll but the permissions flags don't have
  533. // HSE_URL_FLAGS_EXECUTE, we send the dll as a file. Like IIS.
  534. if (m_VRootScriptType == SCRIPT_TYPE_EXTENSION ||
  535. (m_dwPermissions & HSE_URL_FLAGS_EXECUTE) &&
  536. (m_wszExt && (0==_wcsicmp(m_wszExt, L".DLL"))))
  537. {
  538. if (FALSE == g_pVars->m_fExtensions)
  539. {
  540. m_rs = STATUS_NOTIMPLEM;
  541. myleave(88);
  542. }
  543. if (!ExecuteISAPI())
  544. {
  545. m_rs = STATUS_INTERNALERR;
  546. myleave(53);
  547. }
  548. }
  549. // check if it's an executable ASP. If the appropriate permissions aren't set,
  550. // we send an access denied message. Never download an ASP file's source
  551. // code under any conditions.
  552. else if (m_VRootScriptType == SCRIPT_TYPE_ASP ||
  553. m_wszExt && (0==_wcsicmp(m_wszExt, L".ASP")))
  554. {
  555. if ( ! (m_dwPermissions & (HSE_URL_FLAGS_EXECUTE | HSE_URL_FLAGS_SCRIPT)))
  556. {
  557. m_rs = STATUS_FORBIDDEN;
  558. if (g_pVars->m_fFilters)
  559. CallFilter(SF_NOTIFY_ACCESS_DENIED);
  560. myleave(79);
  561. }
  562. if (FALSE == g_pVars->m_fASP)
  563. {
  564. m_rs = STATUS_NOTIMPLEM;
  565. myleave(89);
  566. }
  567. if (!IsLocalFile(m_wszPath))
  568. {
  569. m_rs = STATUS_FORBIDDEN;
  570. if (g_pVars->m_fFilters)
  571. CallFilter(SF_NOTIFY_ACCESS_DENIED);
  572. myleave(87);
  573. }
  574. if (!ExecuteASP())
  575. {
  576. // ExecuteASP sets m_rs on error.
  577. myleave(92);
  578. }
  579. }
  580. else // Neither an ASP or ISAPI.
  581. {
  582. ret = FALSE;
  583. }
  584. done:
  585. TraceTag(ttidWebServer, "HandleScript returned:, err = %d, m_rs = %d",err,m_rs);
  586. return ret;
  587. }
  588. // wszFile is the physical file we're going to try to load. Function returns
  589. // true if file is local and false if it is on a network drive.
  590. // The only ways a file can be non-local on CE are if it has a UNC name
  591. // (\\machineshare\share\file) or if it is mapped under the NETWORK directory.
  592. // However, the Network folder doesn't have to be named "network", so we
  593. // use the offical method to get the name
  594. BOOL IsLocalFile(PWSTR wszFile)
  595. {
  596. // Are we requested a UNC name
  597. if ( wcslen(wszFile) >= 2)
  598. {
  599. if ( (wszFile[0] == '\\' || wszFile[0] == '/') &&
  600. (wszFile[1] == '\\' || wszFile[1] == '/'))
  601. {
  602. TraceTag(ttidWebServer, "Extension or ASP requested is not on local file system, access denied");
  603. return FALSE;
  604. }
  605. }
  606. #if defined (UNDER_CE) && !defined (OLD_CE_BUILD)
  607. CEOIDINFO ceOidInfo;
  608. DWORD dwNetworkLen;
  609. if (!CeOidGetInfo(OIDFROMAFS(AFS_ROOTNUM_NETWORK), &ceOidInfo))
  610. {
  611. return TRUE; // if we can't load it assume that it's not supported in general, so it is local file
  612. }
  613. dwNetworkLen = wcslen(ceOidInfo.infDirectory.szDirName);
  614. if (0 == wcsnicmp(ceOidInfo.infDirectory.szDirName,wszFile,dwNetworkLen))
  615. {
  616. TraceTag(ttidWebServer, "Extension or ASP requested is not on local file system, access denied");
  617. return FALSE;
  618. }
  619. #endif
  620. return TRUE;
  621. }
  622. // dwMaxSizeToRead is HKLM\Comm\Httpd\PostReadSize in typcilac case, or
  623. // is unread data if fInitialPostRead=0, which means that we're handling
  624. // an error condition on keep-alive and need to read remaining post data.
  625. // Note that we do NOT pull in remaining POST data if an ISAPI extension
  626. // ran and had more data than was initially read in because it's the ISAPI's
  627. // job to read all this data off the wire using ReadClient if they're going
  628. // to do a keep-alive; if they don't do this then HTTPD will get parse errors,
  629. // like IIS.
  630. BOOL CHttpRequest::ReadPostData(DWORD dwMaxSizeToRead, BOOL fInitialPostRead)
  631. {
  632. BOOL ret = TRUE;
  633. HRINPUT hi;
  634. if (m_dwContentLength && dwMaxSizeToRead)
  635. {
  636. DWORD dwRead;
  637. if (m_dwContentLength > dwMaxSizeToRead)
  638. dwRead = dwMaxSizeToRead;
  639. else
  640. dwRead = m_dwContentLength;
  641. hi = m_bufRequest.RecvBody(m_socket, dwRead, !fInitialPostRead);
  642. if (hi != INPUT_OK && hi != INPUT_NOCHANGE)
  643. {
  644. m_rs = STATUS_BADREQ;
  645. ret = FALSE;
  646. }
  647. // If no new data was read (hi = INPUT_NOCHANGE) don't call filter.
  648. else if (g_pVars->m_fFilters &&
  649. hi != INPUT_NOCHANGE &&
  650. ! CallFilter(SF_NOTIFY_READ_RAW_DATA))
  651. {
  652. // let filter set error code.
  653. ret = FALSE;
  654. }
  655. }
  656. return ret;
  657. }