//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*--
Module Name: asp.cpp
Abstract: ASP Handler
--*/

#include "httpd.h"


//****************************************************************
//  Called by httpd before running ASP
//****************************************************************


BOOL CGlobalVariables::InitASP(CReg *pWebsite) {
	CReg reg((HKEY) (*pWebsite), RK_ASP);
	PCWSTR pwszLanguage = NULL;		// don't free this, points to a buffer inside CReg

	if (NULL != (pwszLanguage = reg.ValueSZ(RV_ASP_LANGUAGE)) &&  
		(0 == _wcsicmp(pwszLanguage,L"JSCRIPT") ||
		 0 == _wcsicmp(pwszLanguage,L"JAVASCRIPT")))
	{
		m_ASPScriptLang = JSCRIPT;
	}
	else
		m_ASPScriptLang = VBSCRIPT;

	m_lASPCodePage             = (UINT) reg.ValueDW(RV_ASP_CODEPAGE,CP_ACP);
	m_ASPlcid                  = (LCID) reg.ValueDW(RV_ASP_LCID,LOCALE_SYSTEM_DEFAULT);
	m_fASPVerboseErrorMessages = reg.ValueDW(RV_ASP_VERBOSE_ERR,1);
	m_dwAspMaxFormSize         = reg.ValueDW(RV_ASP_MAX_FORM,0);

	DEBUGMSG(ZONE_ASP,(L"HTTPD: ASP Registry defaults -- language = %s, lcid = %d, codepage = %d\r\n",
						 (m_ASPScriptLang == JSCRIPT) ? L"JSCRIPT" : L"VBSCRIPT", m_ASPlcid, m_lASPCodePage));
	return TRUE;
}


// This is the only fcn that httpd calls to get the ASP.dll loaded and running.

// NOTE:  The ASP dll is responsible for reporting all it's own error messages.
// The only time httpd does this is if the ASP crashes or if the file wasn't found.
// ExecuteASP in asp.dll only returns FALSE if there was an exception.

BOOL CHttpRequest::ExecuteASP() {
	DEBUG_CODE_INIT;
	DWORD dwRet = HSE_STATUS_ERROR;
	ASP_CONTROL_BLOCK ACB;
	CISAPI *pISAPI = NULL;
	HINSTANCE hInst = 0;
	
	// First make sure file exists.  If it doesn't, don't bother making the call
	// to the dll.
	if (INVALID_HANDLE_VALUE == (HANDLE) GetFileAttributes(m_wszPath)) {
		DEBUGMSG(ZONE_ERROR,(L"HTTPD: GetFileAttributes fails on %s, GLE=0x%08x\r\n",m_wszPath,GetLastError()));
		m_rs = GLEtoStatus();
		myleave(620);
	}

	if (NULL==(hInst=g_pVars->m_pISAPICache->Load(ASP_DLL_NAME,&pISAPI,SCRIPT_TYPE_ASP))) {
		DEBUGMSG(ZONE_ERROR,(L"HTTPD: Unable to load %s, GLE=0x%08x\r\n",ASP_DLL_NAME,GetLastError()));
		m_rs = STATUS_INTERNALERR;
		myleave(621);
	}
	
	FillACB((void *) &ACB,hInst);

	__try {
		dwRet = pISAPI->CallASP(&ACB);
	}
	__except(ReportFault(GetExceptionInformation(),0), EXCEPTION_EXECUTE_HANDLER) {
		m_rs = STATUS_INTERNALERR;
		g_pVars->m_pLog->WriteEvent(IDS_HTTPD_EXT_EXCEPTION,L"asp.dll",GetExceptionCode(),"HttpExtensionProc",GetLastError());
		DEBUGMSG(ZONE_ERROR,(L"HTTPD: exception in asp.dll, exception=0x%08x\r\n",GetExceptionCode()));
		myleave(623);
	}	

	m_fKeepAlive = (dwRet==HSE_STATUS_SUCCESS_AND_KEEP_CONN);
	

done:
	// Don't free hInsnt.  The cache does this automatically.
	g_pVars->m_pISAPICache->Unload(pISAPI);

	return (dwRet!=HSE_STATUS_ERROR);
}

//**************************************************************
//  Callback fcns for ASP
//**************************************************************

//  Modifies headers in our extended header information for Response Put Methods.
BOOL WINAPI AddHeader (HCONN hConn, LPSTR lpszName, LPSTR lpszValue) {
	DEBUGCHK(CHECKSIG(hConn));
	return ((CHttpRequest*)hConn)->m_bufRespHeaders.AddHeader(lpszName, lpszValue,TRUE);
}

//  If there's a buffer, flush it to sock.
BOOL WINAPI Flush(HCONN hConn) {
	DEBUGCHK(CHECKSIG(hConn));
	CHttpRequest *pRequest = (CHttpRequest*)hConn;

	// all these rules straight from docs
	if (FALSE == pRequest->m_fBufferedResponse)
		return FALSE;

	return pRequest->m_bufRespBody.SendBuffer(pRequest);
}

//  If there's a buffer, clear it.
BOOL WINAPI Clear(HCONN hConn) {
	DEBUGCHK(CHECKSIG(hConn));
	CHttpRequest *pRequest = (CHttpRequest*)hConn;

	// all these rules straight from docs
	if (FALSE == pRequest->m_fBufferedResponse)
		return FALSE;

	pRequest->m_bufRespBody.Reset();		
	return TRUE;
}

//  Toggles buffer.  Error checking on this done in ASP calling fnc.
BOOL WINAPI SetBuffer(HCONN hConn, BOOL fBuffer) {
	DEBUGCHK(CHECKSIG(hConn));
	((CHttpRequest*)hConn)->m_fBufferedResponse = fBuffer;
	
	return TRUE;
}

// Reads any remaining bytes off the wire into the request buffer. 
// May need to update some pointers in 
BOOL WINAPI ReceiveCompleteRequest(struct _ASP_CONTROL_BLOCK *pcb) {
	DEBUGCHK(CHECKSIG(pcb->ConnID));
	CHttpRequest *pRequest = (CHttpRequest*)pcb->ConnID;

	return pRequest->ReceiveCompleteRequest(pcb);
}

// Refill buffer...
BOOL CHttpRequest::ReceiveCompleteRequest(PASP_CONTROL_BLOCK pcb) {
	DEBUGCHK(m_bufRequest.Count() < m_dwContentLength); // only call if we have extra remaining.

	if (m_pWebsite->m_dwAspMaxFormSize < m_dwContentLength) {
		// Check to make sure there's not a huge HTTP POST body, as if we tried
		// to read that in we would exhaust system memory.  Because ASP Request.Form
		// is a random access data structure, we need to have all the data read it 
		// in one block so hence if we're sent a huge block, must reject it.

		// Prior to this call we have read in m_dwPostReadSize (default 48KB) bytes
		DEBUGMSG(ZONE_ERROR,(L"HTTPD: ERROR: ASP requested ALL HTTP body, maxFormSize (%d) is < content length (%d)\r\n",
		                      m_pWebsite->m_dwAspMaxFormSize,m_dwContentLength));
		return FALSE;
	}

	HRINPUT hi = m_bufRequest.RecvBody(m_socket, m_dwContentLength - m_bufRequest.Count(),TRUE, FALSE, this,FALSE);

	if(hi != INPUT_OK && hi != INPUT_NOCHANGE) {
		// only change status if it's unset by lower layer.
		m_rs = (m_rs == STATUS_OK) ? STATUS_BADREQ : m_rs;
		return FALSE;
	}	

	if (g_pVars->m_fFilters  && hi != INPUT_NOCHANGE && 
		! CallFilter(SF_NOTIFY_READ_RAW_DATA)) 	{
		return FALSE;
	}	

	// We need to reset the read pointer because we may have had to reallocate the buffer.
	pcb->pszForm = (PSTR) m_bufRequest.Data();
	return TRUE;
}


//  Setup struct for ASP dll
void CHttpRequest::FillACB(void *p, HINSTANCE hInst) {
	PASP_CONTROL_BLOCK pcb = (PASP_CONTROL_BLOCK) p;
	pcb->cbSize = sizeof(ASP_CONTROL_BLOCK);
	pcb->ConnID = (HCONN) this;

	pcb->cbTotalBytes = m_dwContentLength;
	pcb->cbAvailable  = m_bufRequest.Count();
	pcb->wszFileName = m_wszPath;
	pcb->pszVirtualFileName = m_pszURL;

	if (m_dwContentLength)
		pcb->pszForm = (PSTR) m_bufRequest.Data();
	else
		pcb->pszForm = NULL;

	pcb->pszQueryString            = m_pszQueryString;
	pcb->pszCookie                 = m_pszCookie;
	pcb->scriptLang                = m_pWebsite->m_ASPScriptLang;
	pcb->lcid                      = m_pWebsite->m_ASPlcid;
	pcb->lCodePage                 = m_pWebsite->m_lASPCodePage;
	pcb->fASPVerboseErrorMessages  = m_pWebsite->m_fASPVerboseErrorMessages;
	pcb->hInst                     = hInst;

	pcb->GetServerVariable         = ::GetServerVariable;
	pcb->WriteClient               = ::WriteClient;
	pcb->ServerSupportFunction     = ::ServerSupportFunction;
	pcb->AddHeader                 = ::AddHeader;
	pcb->Flush                     = ::Flush;
	pcb->Clear                     = ::Clear;
	pcb->SetBuffer                 = ::SetBuffer;
	pcb->ReceiveCompleteRequest    = ::ReceiveCompleteRequest;
	pcb->ReadClient                = ::ReadClient;
}
