HttpDirectory.cpp
上传用户:dengkfang
上传日期:2008-12-30
资源大小:5233k
文件大小:16k
源码类别:

CA认证

开发平台:

Visual C++

  1. /*
  2. Module : HttpDirectory.cpp
  3. Purpose: Implementation for the CHttpDirectory class
  4. Created: PJN / 21-02-2003
  5. History: None
  6. Copyright (c) 2003 - 2005 by PJ Naughter.  (Web: www.naughter.com, Email: pjna@naughter.com)
  7. All rights reserved.
  8. Copyright / Usage Details:
  9. You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise) 
  10. when your product is released in binary form. You are allowed to modify the source code in any way you want 
  11. except you cannot modify the copyright details at the top of each module. If you want to distribute source 
  12. code with your application, then you are only allowed to distribute versions released by the author. This is 
  13. to maintain a single distribution point for the source code. 
  14. */
  15. //////////////// Includes ////////////////////////////////////////////
  16. #include "stdafx.h"
  17. #include "..resource.h"
  18. #include "HttpDirectory.h"
  19. #include "HttpClient.h"
  20. //////////////// Macros //////////////////////////////////////////////
  21. #ifdef _DEBUG
  22. #define new DEBUG_NEW
  23. #undef THIS_FILE
  24. static char THIS_FILE[] = __FILE__;
  25. #endif
  26. //////////////// Implementation //////////////////////////////////////
  27. CHttpDirectory::CHttpDirectory()
  28. {
  29.   m_bDirectoryListing = FALSE;
  30.   m_bScript = FALSE;
  31.   m_bWritable = FALSE;
  32. }
  33. CHttpDirectory::~CHttpDirectory()
  34. {
  35. }
  36. void CHttpDirectory::SetAlias(const CString& sAlias)
  37. {
  38.   //Must be some alias
  39.   ASSERT(sAlias.GetLength());
  40.   //Ensure the virtual directory begins with a ""
  41.   if (sAlias.Find(_T('\')) == -1)
  42.   {
  43.     if (sAlias.Find(_T('/')) == 0)
  44.       m_sAlias = _T('\') + sAlias.Mid(1);
  45.     else
  46.       m_sAlias = _T('\') + sAlias;
  47.   }  
  48.   else
  49.     m_sAlias = sAlias;
  50.   //Ensure there is a  on the end of the directory if 
  51.   //length is greater than 1
  52.   int nLen = m_sAlias.GetLength();
  53.   if (nLen > 1 && (m_sAlias.GetAt(nLen - 1) != _T('\')))
  54.     m_sAlias += _T('\');
  55. }
  56. void CHttpDirectory::SetDirectory(const CString& sDirectory)
  57. {
  58.   m_sDirectory = sDirectory;  
  59. }
  60. void CHttpDirectory::SetDefaultFile(const CString& sDefaultFile)
  61. {
  62.   m_sDefaultFile = sDefaultFile;
  63. }
  64. BOOL CHttpDirectory::HandleDirectoryAuthorization(CHttpClient* pClient)
  65. {
  66.   //Validate our parameters
  67.   ASSERT(pClient);
  68.   ASSERT(pClient->m_pServer);
  69.   CHttpServerSettings* pSettings = pClient->m_pServer->GetSettings();
  70.   ASSERT(pSettings);
  71.   //Do we need to do virtual directory authentication
  72.   if (m_sUsername.GetLength())
  73.   {
  74.     //If the username and password matches up
  75.     if (m_sUsername == pClient->m_Request.m_sUsername && m_sPassword == pClient->m_Request.m_sPassword)
  76.       return TRUE;
  77.     else
  78.     {
  79.       if (pSettings->m_bAllowBasicAuthentication || pSettings->m_bAllowNTLMAuthentication)
  80.       {
  81.         //Fail the request with the challenge sent back to the client
  82.         pClient->ReturnUnauthorizedMessage(m_sRealm.GetLength() ? m_sRealm : m_sAlias);
  83.       }
  84.       else
  85.       {
  86.         //Report the error
  87.         CString sError;
  88.         sError.Format(_T("CHttpDirectory::HandleDirectoryAuthorization, Directory is protected but all authentication mechanisms are disabled, All requests for this directory will fail!!, %s"), m_sAlias);
  89.         pClient->m_pServer->OnError(sError);
  90.         pClient->ReturnErrorMessage(500); //Internal server error
  91.       }    
  92.       return FALSE;
  93.     }
  94.   }
  95.   else
  96.     return TRUE;
  97. }
  98. void CHttpDirectory::TransmitDirectory(CHttpClient* pClient)
  99. {
  100.   //Validate our parameters
  101.   ASSERT(pClient);
  102.   ASSERT(pClient->m_pServer);
  103.   CHttpServerSettings* pSettings = pClient->m_pServer->GetSettings();
  104.   ASSERT(pSettings);
  105. //For correct operation of the T2A macro, see MFC Tech Note 59
  106.   USES_CONVERSION;
  107.   //Look for all files in the specified directory
  108.   CFileFind finder;
  109.   CString strWildcard(pClient->m_Request.m_sLocalFile);
  110.   strWildcard += _T("\*.*");
  111.   BOOL bWorking = finder.FindFile(strWildcard);
  112.   if (bWorking)
  113.   {
  114.     //Load up the template of the body
  115.     CString sTemp;
  116.     sTemp.LoadString(IDS_DIRECTORY_LISTING_HEADER);
  117.     CString sBody(sTemp);
  118.     FILETIME ftLastModified;
  119.     ZeroMemory(&ftLastModified, sizeof(FILETIME));
  120.     BOOL bHaveLastModified = FALSE;
  121.     TCHAR cLastChar = pClient->m_Request.m_sURL.GetAt(pClient->m_Request.m_sURL.GetLength()-1);
  122.     CString sDirectoryURL;
  123.     if (cLastChar != _T('\') && (cLastChar != _T('/')))
  124.       sDirectoryURL = pClient->m_Request.m_sURL + _T("/");
  125.     else
  126.       sDirectoryURL = pClient->m_Request.m_sURL;
  127.     //Iterate through all the files in this directory
  128.     sBody += _T("<table>rn");
  129.     while (bWorking)
  130.     {
  131.       bWorking = finder.FindNextFile();
  132.       if (!finder.IsDots())
  133.       {
  134.         //Get the last modified time for the file
  135.         CString sLine;
  136.         FILETIME ft;
  137.         finder.GetLastWriteTime(&ft);
  138.         if (CompareFileTime(&ft, &ftLastModified) > 0)
  139.         {
  140.           CopyMemory(&ftLastModified, &ft, sizeof(FILETIME));
  141.           bHaveLastModified = TRUE;
  142.         }
  143.         //Form the URL of the file
  144.         CString sFilename = finder.GetFileName();
  145.         CString sEncodedFilename = CHttpClient::URLEncode(sFilename);
  146.         CString sURL = sDirectoryURL + sEncodedFilename;
  147.         //Get the last modified date as a string
  148.         TCHAR sDate[20];
  149.         SYSTEMTIME st;
  150.         FileTimeToSystemTime(&ft, &st);
  151.         GetDateFormat(LOCALE_SYSTEM_DEFAULT, LOCALE_NOUSEROVERRIDE, &st, NULL, sDate, 20);
  152.         //Get the last modified time as a string
  153.         TCHAR sTod[20];
  154.         GetTimeFormat(LOCALE_SYSTEM_DEFAULT, LOCALE_NOUSEROVERRIDE, &st, NULL, sTod, 20);
  155.         //Form all the info into a row of the table
  156.         sLine.Format(_T("<tr>rn<td><a href=%s>%s</a></td><td>%dKB</td><td>%s</td><td>%s</td>rn</tr>"), 
  157.                      sURL, sFilename, (finder.GetLength()+1023)/1024, sDate, sTod);
  158.         sBody += sLine;
  159.       }
  160.     }
  161.     finder.Close();
  162.     sBody += _T("</table>rn");
  163.     sTemp.LoadString(IDS_DIRECTORY_LISTING_FOOTER);
  164.     sBody += sTemp;
  165.     //replace any "%1"'s with m_Request.m_sURL
  166.     sBody.Replace(_T("%1"), pClient->m_Request.m_sURL);
  167.     //Make a local copy of the field we are going to parse
  168.     char* pszBody = T2A((LPTSTR) (LPCTSTR) sBody);
  169.     //Get the current system time in UTC
  170.     SYSTEMTIME stCurTime;
  171.     FILETIME ftCurTime;
  172.     ::GetSystemTime(&stCurTime);
  173.     ::SystemTimeToFileTime(&stCurTime, &ftCurTime);
  174.     //Form the header of the response
  175.     CHttpResponseHeader responseHdr;
  176.     responseHdr.AddStatusCode(200);
  177.     responseHdr.AddDate(stCurTime);
  178.     responseHdr.AddServer(pSettings->m_sServerName);
  179.     responseHdr.AddW3MfcAllowFields(pSettings->m_bAllowDeleteRequest);
  180.     SYSTEMTIME stLastModified;
  181.     if (bHaveLastModified && ::FileTimeToSystemTime(&ftLastModified, &stLastModified))
  182.       responseHdr.AddLastModified(stLastModified);
  183.     if (pClient->m_bResponseKeepAlive)
  184.       responseHdr.AddKeepAlive();
  185.     int nBodyLength = strlen(pszBody);
  186.     responseHdr.AddContentLength(nBodyLength);
  187.     responseHdr.AddContentType(_T("text/html"));
  188.     //Send the header and body all in one
  189.     pClient->TransmitBuffer(pClient->m_Socket, responseHdr, (BYTE*)pszBody, nBodyLength, pSettings->m_dwWritableTimeout);
  190.     //Log the information
  191.     pClient->PostLog(200, nBodyLength);
  192.   }
  193.   else
  194.   {
  195.     DWORD dwSize = pClient->ReturnErrorMessage(500); //Internal server error
  196.     //Log the information
  197.     pClient->PostLog(500, dwSize);
  198.   }
  199. }
  200. void CHttpDirectory::TransmitFile(CHttpClient* pClient)
  201. {
  202.   //Validate our settings
  203.   ASSERT(pClient);
  204.   ASSERT(pClient->m_pServer);
  205.   CHttpServerSettings* pSettings = pClient->m_pServer->GetSettings();
  206.   ASSERT(pSettings);
  207.   ASSERT(pSettings->m_pMimeManager);
  208.   //Variable used to generate the header response
  209.   CHttpResponseHeader responseHdr;
  210.   //Open the file (use the FILE_FLAG_SEQUENTIAL_SCAN to improve performance)
  211.   HANDLE hFile = ::CreateFile(pClient->m_Request.m_sLocalFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
  212.   if (hFile != INVALID_HANDLE_VALUE)
  213.   {
  214.     //Get the last modified time for the file / directory
  215.     FILETIME ftFile;
  216.     FILETIME ftRequest;
  217.     SYSTEMTIME stFile;
  218.     BOOL bHaveFileTime = ::GetFileTime(hFile, NULL, NULL, &ftFile);
  219.     if (bHaveFileTime)
  220.     {
  221.       ::FileTimeToSystemTime(&ftFile, &stFile);
  222.       //deliberately remove the milliseconds as they do not appear in HTTP responses.
  223.       stFile.wMilliseconds = 0;
  224.       SystemTimeToFileTime(&stFile, &ftFile);
  225.     }
  226.     //Get the current system time in UTC
  227.     SYSTEMTIME stCurTime;
  228.     FILETIME ftCurTime;
  229.     ::GetSystemTime(&stCurTime);
  230.     ::SystemTimeToFileTime(&stCurTime, &ftCurTime);
  231.     //Ensure that the file time is not past the server time
  232.     if (CompareFileTime(&ftFile, &ftCurTime) == 1)
  233.     {
  234.       CopyMemory(&ftFile, &ftCurTime, sizeof(FILETIME));
  235.       CopyMemory(&stFile, &stCurTime, sizeof(SYSTEMTIME));
  236.     }
  237.     //Handle conditional GET of the file
  238.     if (pClient->m_Request.m_Verb == CHttpRequest::HTTP_VERB_GET && 
  239.         bHaveFileTime && pClient->m_Request.m_bIfModifiedSincePresent &&
  240.         ::SystemTimeToFileTime(&pClient->m_Request.m_IfModifiedSince, &ftRequest) &&
  241.         ::CompareFileTime(&ftFile, &ftRequest) != 1)
  242.     {
  243.       //Form the header
  244.       responseHdr.AddStatusCode(304); //File Not modified
  245.       responseHdr.AddDate(stCurTime);
  246.       responseHdr.AddServer(pSettings->m_sServerName);
  247.       responseHdr.AddW3MfcAllowFields(pSettings->m_bAllowDeleteRequest);
  248.     if (pSettings->m_bAutoExpire)
  249.     responseHdr.AddExpires(stFile);
  250.       if (pClient->m_bResponseKeepAlive)
  251.         responseHdr.AddKeepAlive();
  252.       responseHdr.AddLastModified(stFile);
  253.       responseHdr.AddContentLength(0);
  254.       responseHdr.AddContentType(_T("text/html"));
  255.       //Send the header
  256. #ifdef W3MFC_SSL_SUPPORT
  257.       responseHdr.Send(pClient->m_Socket, pSettings->m_dwWritableTimeout, pClient->m_SSL);
  258. #else
  259.       responseHdr.Send(pClient->m_Socket, pSettings->m_dwWritableTimeout);
  260. #endif
  261.       //No body is sent for a 304 status
  262.       //Log the information
  263.       pClient->PostLog(304, 0);
  264.     }
  265.     else
  266.     {
  267.       //Get the length of the file
  268.       DWORD dwFileLength = GetFileSize(hFile, NULL);
  269.       if (pClient->m_Request.m_dwHttpVersion > MAKELONG(9, 0))
  270.       {
  271.         CString sMime = pSettings->m_pMimeManager->GetMimeType(pClient->m_Request);
  272.         //Form the header of the response
  273.         responseHdr.AddStatusCode(200);
  274.         responseHdr.AddDate(stCurTime);
  275.         responseHdr.AddServer(pSettings->m_sServerName);
  276.         responseHdr.AddW3MfcAllowFields(pSettings->m_bAllowDeleteRequest);
  277.       if (pSettings->m_bAutoExpire)
  278.       responseHdr.AddExpires(stFile);
  279.         if (pClient->m_bResponseKeepAlive)
  280.           responseHdr.AddKeepAlive();
  281.         if (bHaveFileTime)
  282.           responseHdr.AddLastModified(stFile);
  283.         responseHdr.AddContentLength(dwFileLength);
  284.         responseHdr.AddContentType(sMime);
  285.         //Send back the file contents (if not a HEAD request)
  286.         if (pClient->m_Request.m_Verb == CHttpRequest::HTTP_VERB_HEAD)
  287.         {
  288.           //Just send the header
  289. #ifdef W3MFC_SSL_SUPPORT
  290.           responseHdr.Send(pClient->m_Socket, pSettings->m_dwWritableTimeout, pClient->m_SSL);
  291. #else
  292.           responseHdr.Send(pClient->m_Socket, pSettings->m_dwWritableTimeout);
  293. #endif
  294.         }
  295.         else
  296.         {
  297.           //Send the header and body all in one
  298.           pClient->TransmitFile(pClient->m_Socket, responseHdr, hFile, dwFileLength, pSettings->m_dwWritableTimeout);
  299.         }
  300.       }
  301.       else
  302.       {
  303.         //No header sent for Http 0.9
  304.         //Send back the file contents (if not a HEAD request)
  305.         if (pClient->m_Request.m_Verb != CHttpRequest::HTTP_VERB_HEAD)
  306.         {
  307.           try
  308.           {
  309.             char sBuf[4096];
  310.             DWORD dwBytesRead = 0;
  311.             do 
  312.             {
  313.               if (::ReadFile(hFile, sBuf, 4096, &dwBytesRead, NULL) && dwBytesRead)
  314. #ifdef W3MFC_SSL_SUPPORT
  315.                 pClient->m_Socket.SendWithRetry(sBuf, dwBytesRead, pSettings->m_dwWritableTimeout, pClient->m_SSL);
  316. #else
  317.                 pClient->m_Socket.SendWithRetry(sBuf, dwBytesRead, pSettings->m_dwWritableTimeout);
  318. #endif
  319.             } 
  320.             while (dwBytesRead);
  321.           }
  322.           catch(CWSocketException* pEx)
  323.           {
  324.             //Report the error
  325.             CString sError;
  326.             sError.Format(_T("CHttpDirectory::TransmitFile, Failed to send to socket, Error:%d"), pEx->m_nError);
  327.             pClient->m_pServer->OnError(sError);
  328.             pEx->Delete();  
  329.           }
  330.         }
  331.       }
  332.       //Log the information
  333.       pClient->PostLog(200, dwFileLength);
  334.     }
  335.     //Don't forget to close the file
  336.     CloseHandle(hFile);
  337.   }
  338.   else
  339.   {
  340.     DWORD dwLastError = ::GetLastError();
  341.     if (dwLastError == ERROR_ACCESS_DENIED && ((GetFileAttributes(pClient->m_Request.m_sLocalFile) & FILE_ATTRIBUTE_DIRECTORY) == 0) && 
  342.         (pSettings->m_bAllowBasicAuthentication || 
  343.          pSettings->m_bAllowNTLMAuthentication))
  344.       pClient->ReturnUnauthorizedMessage(pClient->m_Request.m_sURL);
  345.     else
  346.       pClient->ReturnErrorMessage(404); //File not found
  347.   }
  348. }
  349. void CHttpDirectory::HandleDirectory(CHttpClient* pClient, BOOL bDirectory)
  350. {
  351.   //Validate the parameters
  352.   ASSERT(pClient);
  353.   ASSERT(pClient->m_pServer);
  354.   CHttpServerSettings* pSettings = pClient->m_pServer->GetSettings();
  355.   ASSERT(pSettings);
  356.   //Do the directory level authentication
  357.   if (!HandleDirectoryAuthorization(pClient))
  358.     return;
  359.   if (m_bScript)
  360.   {
  361.     BOOL bSentSomething = FALSE;
  362.   #ifndef W3MFC_NO_ISAPI_SUPPORT
  363.     CString sDLL = pSettings->m_pISAPIManager->GetISAPIExtension(pClient->m_Request);
  364.     if (sDLL.GetLength())
  365.     {
  366.       bSentSomething = TRUE;
  367.       TransmitISAPIRequest(pClient, sDLL); //Do our ISAPI implementation
  368.     }
  369.   #endif  
  370.   #ifndef W3MFC_NO_CGI_SUPPORT
  371.     if (!bSentSomething)
  372.     {
  373.       bSentSomething = TRUE;
  374.       pSettings->m_pCGI->TransmitCGIResponse(pClient); //Do our CGI implementation
  375.     }
  376.   #endif
  377.     if (!bSentSomething)
  378.     {
  379.       //Report the error
  380.       CString sError;
  381.       sError.Format(_T("CHttpDirectory::HandleDirectoryAuthorization, Directory is a script directory but ISAPI and / or CGI is not supported in this configuration, All requests for this directory will fail!!, %s"), m_sAlias);
  382.       pClient->m_pServer->OnError(sError);
  383.       pClient->ReturnErrorMessage(500); //Internal server error
  384.     }
  385.   }
  386.   else if (bDirectory)
  387.     TransmitDirectory(pClient); //Return a directory listing back to the client
  388.   else
  389.     TransmitFile(pClient); //Return the file back to the client
  390. }
  391. #ifndef W3MFC_NO_ISAPI_SUPPORT
  392. void CHttpDirectory::TransmitISAPIRequest(CHttpClient* pClient, const CString& sDLL)
  393. {
  394.   //Validate the parameters
  395.   ASSERT(pClient);
  396.   ASSERT(pClient->m_pServer);
  397.   CHttpServerSettings* pSettings = pClient->m_pServer->GetSettings();
  398.   ASSERT(pSettings);
  399.   ASSERT(pSettings->m_pISAPI);
  400.   //Reset the data counter
  401.   pClient->m_dwDataSentViaWriteClient = 0;
  402.   pClient->m_nHttpStatusCodeSent = 0;
  403.   pClient->m_bResponseKeepAlive = FALSE;
  404.   //Assume the worst
  405.   BOOL bSuccess = FALSE;
  406.   if (pSettings->m_bCacheISAPI)
  407.   {
  408.     CHttpISAPIExtension* pISAPI = pSettings->m_pISAPI->CachedLoad(sDLL);
  409.     if (pISAPI)
  410.       bSuccess = pSettings->m_pISAPI->CallHttpExtensionProc(pClient, *pISAPI);
  411.   }
  412.   else
  413.   {
  414.     CHttpISAPIExtension isapi;
  415.     if (pSettings->m_pISAPI->UncachedLoad(sDLL, isapi))
  416.       bSuccess = pSettings->m_pISAPI->CallHttpExtensionProc(pClient, isapi);
  417.   }
  418.   if (!bSuccess)
  419.   {
  420.     //Report the error
  421.     CString sError;
  422.     sError.Format(_T("CHttpDirectory::TransmitISAPIRequest, Failed calling the function HttpExtensionProc in the ISAPI extension %s"), sDLL);
  423.     pClient->m_pServer->OnError(sError);
  424.     pClient->ReturnErrorMessage(500); //Internal server error
  425.   }
  426.   else
  427.     pClient->PostLog(pClient->m_nHttpStatusCodeSent, pClient->m_dwDataSentViaWriteClient);
  428. }
  429. #endif