#include "StdAfx.h"
#include "cgiresponse.h"
#include "connection.h"
#include "Dir.h"
#include "intervaltimer.h"

CCGIResponse::CCGIResponse(CServer *server,CWorkspaceAccess context,CRequest request,CAlias alias,CConnectionInterrupt interrupt)
: CNormalResponse(server,context,request,alias),
  m_subProcess(server,interrupt), m_stdOutStartIndex(0),
  m_interrupt(interrupt)
{
	//	実は無いとか
	if(alias.IsExist()==0 && alias.IsExtarExist()==0)
		throw new CSystemResponse(server,context,"指定されたファイルは存在しません",404);

	//	メソッドはGET/POST/HEAD
	if(request.GetMethod() != "GET" && request.GetMethod() != "HEAD" && request.GetMethod() != "POST")
		throw new CSystemResponse(server,context,"対応していないメソッドです",405);

	if(alias.IsExtarExist())
		m_target = alias.GetExtraTarget();
	else
		m_target = alias.GetTarget();
}

CCGIResponse::~CCGIResponse(void)
{
}

/*
	1:準備

*/
void CCGIResponse::PreCreateResponse()
{
	__int64	recvLen = m_request.GetOptionAccess().GetConfig("Content-length",0,TRUE);

	//	受信サイズを設定
	if(recvLen > 65536)
		m_subProcess.m_stdin.SetGrowSize((int)recvLen);
}

/*
	2:受信データの設定(繰り返し)

*/
void CCGIResponse::SetNextData(CBuffer &data)
{
	//	StdInバッファに追加
	m_subProcess.m_stdin += data;
}


//	3:レスポンスの生成
void CCGIResponse::CreateResponse()
{
	CString	threadID = m_context.GetConfig("ThreadName","");

	//	コマンド取得
	CString	command = GetCommandString();

	//	環境をCGIに
	m_subProcess.AddCGIServerEnvironment(m_server,m_context,m_request,m_alias);

	//	REDIRECT_STATUS
	if(command.Left(m_target.GetLength()).CompareNoCase(m_target) != 0)
		m_subProcess.AddEnvironment("REDIRECT_STATUS=200");

	try
	{
		//	プロセス実行
		m_subProcess.RunSubProcess(command,CDir::PathToDir(m_target),threadID);
	}
	catch(CSubProcessException err)
	{
		ThrowCGIError("CGI実行エラー : " + err.m_info);
	}

	//	最初の１行を取得
	CString	header = GetHeader();

	//	解析
	CWorkspace			option;
	CWorkspaceAccess	optionAccess = option.GetAccess("Option");
	ParseHeader(header,optionAccess);

	if(optionAccess.GetKeyCount() == 0)
		ThrowCGIError("CGIエラー : CGIヘッダーが出力されませんでした");

	//	ステータスコードがある？
	if(optionAccess.GetConfig("Status","",TRUE) != "")
	{
		CString status = optionAccess.GetConfig("Status","",TRUE);
		
		if(!status.IsEmpty())
		{
			int	temp = 0;
			CString code = status.Tokenize(" ",temp);

			//	ステータスコードの設定
			if(atoi(code) >= 100)
				SetResponseCode(atoi(code));
		}
	}
	
	//	タイプは？
	if(optionAccess.GetConfig("Location","",TRUE) != "")
	{
		//	拡張オプションを設定
		m_responseInfo.SetConfig("CGIHeader",optionAccess.GetConfig("CGIHeader",""));

		//	移動
		CString location = optionAccess.GetConfig("Location","",TRUE);
		CSystemResponse *move = new CSystemResponse(m_server,m_context,location + " に移動してください",301);
		move->m_responseOptions.SetConfig("Location",location);
		throw move;
	}
	else if(optionAccess.GetConfig("Content-Type","",TRUE) != "")
	{
		m_responseOptions.SetConfig("Content-Type",optionAccess.GetConfig("Content-Type","",TRUE));
	}
	else
		ThrowCGIError("CGIエラー : CGIヘッダーが出力されませんでした");

	//	拡張オプションを設定
	m_responseInfo.SetConfig("CGIHeader",optionAccess.GetConfig("CGIHeader",""));
}

//	4:レスポンスサイズの取得
__int64 CCGIResponse::GetResponseSize()
{
	//	StdOutサイズ
	return(m_subProcess.m_stdout.GetSize());
}


//	6:レスポンスの取得
int CCGIResponse::GetNextData(CBuffer &data)
{
	int left = m_subProcess.m_stdout.GetSize() - m_stdOutStartIndex;

	if(left <= 0)
		return(0);

	if(left < data.GetSize())
		data.ReSize(left);

	data.BlockCopy(0,m_subProcess.m_stdout.GetPtr() + m_stdOutStartIndex,data.GetSize());
	m_stdOutStartIndex += data.GetSize();

	return(data.GetSize());
}

//	ヘッダー領域取得
CString CCGIResponse::GetHeader()
{
	int	maxHeader = 4096;
	if(maxHeader > m_subProcess.m_stdout.GetSize())
		maxHeader = m_subProcess.m_stdout.GetSize();

	//	ヘッダーを文字列に
	CString	header;
	memcpy(header.GetBuffer(maxHeader + 1),m_subProcess.m_stdout.GetPtr(),maxHeader);
	header.ReleaseBufferSetLength(maxHeader);

	//	ヘッダー部分検索
	int index1 = header.Find("\r\n\r\n");
	int index2 = header.Find("\n\n");
	if(index1 != -1 && (index2 == -1 || index2 > index1))
	{
		header = header.Left(index1);
		m_subProcess.m_stdout.Remove(0,index1+4);
		return(header);
	}

	if(index2 != -1)
	{
		header = header.Left(index2);
		m_subProcess.m_stdout.Remove(0,index2+2);
		return(header);
	}

	return("");
}

//	ヘッダー解析
void CCGIResponse::ParseHeader(CString header,CWorkspaceAccess option)
{
	CString	line;
	int		counter = 0;

	while(TRUE)
	{
		line = header.Tokenize("\n",counter);
		if(line.IsEmpty())
			break;

		int index = line.Find(":");
		if(index == -1)
			continue;

		CString _key = line.Left(index);
		CString _data = line.Mid(index + 1);
		_key.Trim("\r\n");
		_data.Trim("\r\n");

		if(_key.CompareNoCase("Status") == 0)
			option.SetConfig("Status",_data);
		else if(_key.CompareNoCase("Content-Type") == 0)
			option.SetConfig("Content-Type",_data);
		else if(_key.CompareNoCase("Location") == 0)
			option.SetConfig("Location",_data);
		else
		{
			//	CGI-Extension header
			CString header = option.GetConfig("CGIHeader","");
			header += _key;
			header += ": ";
			header += _data;
			header += "\r\n";
			option.SetConfig("CGIHeader",header);
		}
	}
}


/*
	コマンド文字列取得
*/
CString	CCGIResponse::GetCommandString()
{
	CString file = m_context.GetConfig("ScriptCommand","");

	return(file);
}

//	エラー
void CCGIResponse::ThrowCGIError(CString detail)
{
	//	エラー情報がある？
	if(m_subProcess.m_stderr.GetSize() > 0)
	{
		CString	str;
		str.Format("-- 以下、CGIプログラムによるエラー情報 --\n\n%s",(CString)m_subProcess.m_stderr);
		throw new CSystemResponse(m_server,m_context,detail,500,str);
	}
#ifdef DEBUG
	else if(m_subProcess.m_stdout.GetSize() > 0)
	{
		CString	str;
		str.Format("-- 以下、CGIプログラムによるエラー情報 --\n\n%s",(CString)m_subProcess.m_stdout);
		throw new CSystemResponse(m_server,m_context,detail,500,str);
	}
#endif
	else
		throw new CSystemResponse(m_server,m_context,detail,500);
}

