⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 httpcgi.cpp

📁 MiniCA V2.0版本源码。《小型CA系统V2.1含源码》发表以来
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/*
Module : HttpCGI.cpp
Purpose: Implementation for the CHttpCGI class
Created: PJN / 26-02-2003
History: None

Copyright (c) 2003 - 2005 by PJ Naughter.  (Web: www.naughter.com, Email: pjna@naughter.com)

All rights reserved.

Copyright / Usage Details:

You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise) 
when your product is released in binary form. You are allowed to modify the source code in any way you want 
except you cannot modify the copyright details at the top of each module. If you want to distribute source 
code with your application, then you are only allowed to distribute versions released by the author. This is 
to maintain a single distribution point for the source code. 

*/



//////////////// Includes ////////////////////////////////////////////

#include "stdafx.h"
#include "HttpCGI.h"
#include "Win32Handle.h"
#include "..\resource.h"
#include "HttpClient.h"



//////////////// Macros //////////////////////////////////////////////

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif



//////////////// Implementation //////////////////////////////////////

CHttpCGI::CHttpCGI()
{
}

CHttpCGI::~CHttpCGI()
{
}

void CHttpCGI::TransmitCGIResponse(CHttpClient* pClient)
{
  //Validate our parameters
  ASSERT(pClient);
  ASSERT(pClient->m_pServer);

  //Check to make sure the file exists become we try to run it
  CFileStatus fs;
  if (!CFile::GetStatus(pClient->m_Request.m_sLocalFile, fs))
  {
    //Report the error
    CString sError;
    sError.Format(_T("CHttpCGI::TransmitCGIResponse, Requested file %s does not exist"), pClient->m_Request.m_sLocalFile);
    pClient->m_pServer->OnError(sError);

    //Handle the error
    pClient->ReturnErrorMessage(500);
    return;
  }

  //First need to create an anonymous pipe for the child process's redirected 
  //STDOUT
  SECURITY_ATTRIBUTES sa;
  sa.nLength= sizeof(SECURITY_ATTRIBUTES);
  sa.lpSecurityDescriptor = NULL;
  sa.bInheritHandle = TRUE;
  CW32Handle ChildStdoutReadTemp;
  CW32Handle ChildStdoutWrite;
  if (!CreatePipe(&ChildStdoutReadTemp, &ChildStdoutWrite, &sa, 0)) 
  {
    //Report the error
    CString sError;
    sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to create anonymous pipes for STDOUT for CGI child process, Error:%d"), GetLastError());
    pClient->m_pServer->OnError(sError);

    //Handle the error
    pClient->ReturnErrorMessage(500);
    return;
  }

  //Next need to create an anonymous pipe for the child process's redirected 
  //STDIN
  CW32Handle ChildStdinRead;
  CW32Handle ChildStdinWriteTemp;
  if (pClient->m_Request.m_dwRawEntitySize)
  {
    if (!CreatePipe(&ChildStdinRead, &ChildStdinWriteTemp, &sa, 0)) 
    {
      //Report the error
      CString sError;
      sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to create anonymous pipes for STDIN for CGI child process, Error:%d"), GetLastError());
      pClient->m_pServer->OnError(sError);

      //Handle the error
      pClient->ReturnErrorMessage(500);
      return;
    }
  }

  //Create a duplicate of the child's STDOUT write handle for the STDERR write handle.
  //This is necessary in case the child application closes one of its std output handles
  CW32Handle ChildStderrWrite;
  if (!DuplicateHandle(GetCurrentProcess(), ChildStdoutWrite, GetCurrentProcess(), &ChildStderrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS))
  {
    //Report the error
    CString sError;
    sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to duplicate STDOUT handle for STDERR handle CGI child process, Error:%d"), GetLastError());
    pClient->m_pServer->OnError(sError);

    //Handle the error
    pClient->ReturnErrorMessage(500);
    return;
  }

  //Create new STDOUT read handle (which is non-inheritable). if we did not do this then the child inherits the 
  //properties and, as a result non-closeable handles to the pipes are created
  CW32Handle ChildStdoutRead;
  if (!DuplicateHandle(GetCurrentProcess(), ChildStdoutReadTemp, GetCurrentProcess(), &ChildStdoutRead, 0, FALSE, DUPLICATE_SAME_ACCESS))
  {
    //Report the error
    CString sError;
    sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to duplicate STDOUT read handle, Error:%d"), GetLastError());
    pClient->m_pServer->OnError(sError);

    //Handle the error
    pClient->ReturnErrorMessage(500);
    return;
  }

  //Close the temp STDOUT read handle now that we have duplicated it successfully
  ChildStdoutReadTemp.Close();

  //Create new write handle (if we need to which is non-inheritable). if we did not do this then the child inherits the 
  //properties and, as a result non-closeable handles to the pipes are created
  CW32Handle ChildStdinWrite;
  if (pClient->m_Request.m_dwRawEntitySize)
  {
    if (!DuplicateHandle(GetCurrentProcess(), ChildStdinWriteTemp, GetCurrentProcess(), &ChildStdinWrite, 0, FALSE, DUPLICATE_SAME_ACCESS))
    {
      //Report the error
      CString sError;
      sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to duplicate STDIN write handle, Error:%d"), GetLastError());
      pClient->m_pServer->OnError(sError);

      //Handle the error
      pClient->ReturnErrorMessage(500);
      return;
    }

    //Close the temp STDIN write handle now that we have duplicated it successfully
    ChildStdinWriteTemp.Close();
  }

  //Now setup the structures for a call to CreateProcess
  PROCESS_INFORMATION pi;
  STARTUPINFO si;
  ZeroMemory(&si,sizeof(STARTUPINFO));
  si.cb = sizeof(STARTUPINFO);
  si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
  si.hStdOutput = ChildStdoutWrite;
  si.hStdInput  = ChildStdinRead;
  si.hStdError  = ChildStderrWrite;
  si.wShowWindow = SW_HIDE;

  //Get the environment variables we will be sending down to the CGI process
  CString sEnvironment = FormCGIEnvironment(pClient);

  //Convert the "sEnvironment" CString into a MULTI_SZ suitable for calling CreateProcess with
  DWORD dwEnvironmentSize = (sEnvironment.GetLength() + 2);
  TCHAR* pszEnvironment = new TCHAR[dwEnvironmentSize];
  _tcscpy(pszEnvironment, sEnvironment);
  pszEnvironment[dwEnvironmentSize-1] = _T('\0'); //Double NULL terminate the data
  //Replace all '\n' with '\0'
  for (DWORD i=0; i<dwEnvironmentSize; i++)
  {
    if (pszEnvironment[i] == _T('\n'))
      pszEnvironment[i] = _T('\0');
  }
 
  //Setup the creation flags
  DWORD dwCreationFlags = CREATE_NEW_CONSOLE;
  #ifdef _UNICODE
  dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
  #endif

  //Get the working directory of the script
  TCHAR sDrive[_MAX_DRIVE];
  TCHAR sDir[_MAX_DIR];
  _tsplitpath(pClient->m_Request.m_sLocalFile, sDrive, sDir, NULL, NULL);
  TCHAR sCWD[_MAX_PATH];
  _tmakepath(sCWD, sDrive, sDir, NULL, NULL);

  //Form the correct command line for the call to CreateProcess
  CString sCommandLine = GetCGICommandLine(pClient);

  //Launch the process that we want to redirect
  BOOL bSuccess = CreateProcess(NULL, sCommandLine.GetBuffer(sCommandLine.GetLength()), NULL, NULL, TRUE, dwCreationFlags, pszEnvironment, sCWD, &si, &pi);
  DWORD dwError = GetLastError();
  sCommandLine.ReleaseBuffer();

  //Tidy up the temp heap memory we have used
  delete [] pszEnvironment;

  //Close the STDOUT write handle as we have no use for it now
  ChildStdoutWrite.Close();

  //Close the STDIN read handle as we have no use for it now
  if (pClient->m_Request.m_dwRawEntitySize)
    ChildStdinRead.Close();

  //Close the STDERR write handle as we have no use for it now
  ChildStderrWrite.Close();

  //Handle the error if we could not run the child process
  if (!bSuccess)
  {
    //Report the error
    CString sError;
    sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to run the CGI child process, Error:%d"), GetLastError());
    pClient->m_pServer->OnError(sError);

    //Return an appropriate error page
    if (dwError == ERROR_FILE_NOT_FOUND)
      pClient->ReturnErrorMessage(404);
    else
      pClient->ReturnErrorMessage(500);
    return;
  }

  //Write to stdin of the client
  if (pClient->m_Request.m_dwRawEntitySize)
  {
    WriteToChildStdin(pClient, ChildStdinWrite);

    //Close the pipe handle now that we are finished with it
    ChildStdinWrite.Close();
  }

  //Read from stdout of the client and send the data back down the socket
  DWORD dwDataSent = ReadFromClientStdout(pClient, ChildStdoutRead, pClient->m_bResponseKeepAlive);
  if (dwDataSent == 0)
    pClient->ReturnErrorMessage(500);
  else
    pClient->PostLog(200, dwDataSent);

  //Close the anonymous pipe handles
  ChildStdoutRead.Close();

  //Wait until the child process exits
  WaitForSingleObject(pi.hProcess, INFINITE);

  // Close process and thread handles.     
  CloseHandle(pi.hProcess);
  CloseHandle(pi.hThread);
}

CString CHttpCGI::FormCGIEnvironment(CHttpClient* pClient)
{
  //Validate our parameters
  ASSERT(pClient);
  ASSERT(pClient->m_pServer);
  CHttpServerSettings* pSettings = pClient->m_pServer->GetSettings();
  ASSERT(pSettings);

  //Form the environment string to send to the child process
  CString sEnvironment;
  CString sLine;

  //Add all the environment variables W3MFC has before we add the custom ones
  for (TCHAR** szVariable = _tenviron; *szVariable; szVariable++ )
  {
    sEnvironment += *szVariable;
    sEnvironment += _T("\n");
  }    

  if (pClient->m_Request.m_AuthorizationType == CHttpRequest::HTTP_AUTHORIZATION_PLAINTEXT)
  {
    sEnvironment += _T("AUTH_TYPE=Basic\n");

    sLine.Format(_T("AUTH_REALM=%s\n"), pClient->m_Request.m_sURL);
    sEnvironment += sLine;

    sLine.Format(_T("REMOTE_USER=%s\n"), pClient->m_Request.m_sUsername);
    sEnvironment += sLine;

    sLine.Format(_T("AUTH_USER=%s\n"), pClient->m_Request.m_sUsername);
    sEnvironment += sLine;

    sLine.Format(_T("REMOTE_PASSWORD=%s\n"), pClient->m_Request.m_sPassword);
    sEnvironment += sLine;

    sLine.Format(_T("AUTH_PASSWORD=%s\n"), pClient->m_Request.m_sPassword);
    sEnvironment += sLine;
  }
  else if (pClient->m_Request.m_AuthorizationType == CHttpRequest::HTTP_AUTHORIZATION_NTLM)
  {
    sEnvironment += _T("AUTH_TYPE=NTLM\n");

    sLine.Format(_T("REMOTE_USER=%s\n"), pClient->m_Request.m_sUsername);
    sEnvironment += sLine;

    sLine.Format(_T("AUTH_USER=%s\n"), pClient->m_Request.m_sUsername);
    sEnvironment += sLine;
  }

  sEnvironment += _T("GATEWAY_INTERFACE=CGI/1.1\n");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -