#include "StdAfx.h"
#include "ssiresponse.h"
#include "Dir.h"

CSSIResponse::CSSIResponse(CServer *server,CWorkspaceAccess context,CRequest request,CAlias alias,CConnectionInterrupt interrupt)
: CNormalResponse(server,context,request,alias),
  m_interrupt(interrupt), m_parser(interrupt), m_done(0),
  m_currentTimeFormat("%Y/%m/%d %H:%M:%S"), m_currentSizeFormat("abbrev"), m_prevIf(0)
{
	//	実は無いとか
	if(alias.IsExist()==0)
		throw new CSystemResponse(server,context,"指定されたファイルは存在しません",404);

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

CSSIResponse::~CSSIResponse(void)
{
}

/*!
	レスポンスサイズの取得
*/
__int64 CSSIResponse::GetResponseSize()
{
	return(m_body.GetLength());
}


/*!
	レスポンスの取得
*/
int CSSIResponse::GetNextData(CBuffer &data)
{
	if(m_done)
	{
		return(0);
	}
	else
	{
		if(m_body.GetLength()==0)
			throw CServerFatalException("HTMLデータのロードに失敗しました、システムエラーです");

		m_done = 1;
		data = m_body;
		return(data.GetSize());
	}
}



/*
	レスポンス生成
*/
void CSSIResponse::CreateResponse()
{
	//	ファイルロード、構文を解析
	if(m_parser.ParseFile(m_alias.GetTarget()))
		throw CServerFatalException("ファイルの操作に失敗しました");


	//	最初のタグまで出力
	m_body += m_parser[0].m_prevHtml;

	//	各タグを処理
	for(int i=0;i<m_parser.GetTagCount() - 1;i++)
	{
		TRACE("%s %s\n",m_parser[i].m_tagName,m_parser[i].m_tagData);
		int textOut = TRUE;

		if(m_parser[i].m_tagName == "config")
			textOut = DoConfig(m_parser[i]);
		else if(m_parser[i].m_tagName == "flastmod" || m_parser[i].m_tagName == "include" || m_parser[i].m_tagName == "fsize")
			textOut = DoFile(m_parser[i]);
		else if(m_parser[i].m_tagName == "echo")
			textOut = DoEcho(m_parser[i]);
		else if(m_parser[i].m_tagName == "if")
			textOut = DoIf(m_parser[i]);
		else if(m_parser[i].m_tagName == "exec")
			textOut = DoExec(m_parser[i]);
	
		if(textOut)
			m_body += m_parser[i + 1].m_prevHtml;
	}
}



//	Configを処理
int CSSIResponse::DoConfig(CSSIParserItem &item)
{
	//	TimFormat
	if(item.m_dataKey == "timefmt")
		m_currentTimeFormat = item.m_dataData;
	else if(item.m_dataKey == "sizefmt")
		m_currentSizeFormat = item.m_dataData;

	return(TRUE);
}


//	ファイル関連を処理
int CSSIResponse::DoFile(CSSIParserItem &item)
{
	//	ファイルパス取得
	CString	path;

	if(item.m_dataKey == "file")
		path = NameToLocalPath(item.m_dataData);
	else
		path = ServerPathToLocalPath(item.m_dataData);

	//	ファイルはあるか
	if(path.IsEmpty())
	{
		m_body += "ファイルが開けません";
		return(TRUE);
	}

	//	flastmod
	if(item.m_tagName == "flastmod")
	{
		CFileStatus	stat;
		CFile::GetStatus(path,stat);

		m_body += stat.m_mtime.Format(m_currentTimeFormat);
		return(TRUE);
	}

	//	include
	if(item.m_tagName == "include")
	{
		m_body += CSSIParser::LoadFile(path);
		return(TRUE);
	}

	//	fsize
	if(item.m_tagName == "fsize")
	{
		CFileStatus	stat;
		CFile::GetStatus(path,stat);

		CString	str;
		if(m_currentSizeFormat == "abbrev")
		{
			if(stat.m_size < 10240)
			{
				str.Format("%I64d",stat.m_size);
			}
			else if(stat.m_size < 1048576 * 10)
			{
				str.Format("%I64dK",stat.m_size / 1024);
			}
			else
			{
				str.Format("%I64dM",stat.m_size / 1048576);
			}
		}
		else
		{
			str.Format("%I64d",stat.m_size);
		}


		m_body += str;
		return(TRUE);
	}

	return(TRUE);
}


//	Echoを処理
int CSSIResponse::DoEcho(CSSIParserItem &item)
{
	CWorkspace			env;
	CWorkspaceAccess	envAccess = env.GetAccess("Env");

	//	環境取得
	GetEnvironment(envAccess);

	m_body += envAccess.GetConfig(item.m_dataData,"",TRUE);

	return(TRUE);
}

//	Ifを処理
int CSSIResponse::DoIf(CSSIParserItem &item)
{
	m_body += "SSIの制御構文はサポートしていません。";
	return(TRUE);
}

//	Execを処理
int CSSIResponse::DoExec(CSSIParserItem &item)
{
	if(item.m_dataKey == "cmd")
	{
		//	ファイル名・オプション
		CString command = item.m_dataData;
		CString	option;
		CString	threadID = m_context.GetConfig("ThreadName","");

		int index = command.Find(" ");
		if(index != -1)
		{
			option = command.Mid(index);
			command = command.Left(index);
		}

		CString	path = NameToLocalPath(command);
		CString	workDir = CDir::PathToDir(path);

		CSubProcess	subProcess(m_server,m_interrupt);
		subProcess.AddCGIServerEnvironment(m_server,m_context,m_request,m_alias);

		try
		{
			//	プロセス実行
			subProcess.RunSubProcess(path + option,workDir,threadID);
		}
		catch(CSubProcessException err)
		{
			m_body += "コマンドが実行できませんでした";
		}

		m_body += (CString)subProcess.m_stdout;

		return(TRUE);
	}
	else if(item.m_dataKey == "cgi")
	{
		CString	path = NameToLocalPath(item.m_dataData);
		CString	workDir = CDir::PathToDir(path);
		CString command = CSubProcess::GetCGICommand(m_server,path,"");
		CString	threadID = m_context.GetConfig("ThreadName","");

		if(command.IsEmpty())
		{
			m_body += "コマンドが実行できませんでした";
			return(TRUE);
		}


		CSubProcess	subProcess(m_server,m_interrupt);
		subProcess.AddCGIServerEnvironment(m_server,m_context,m_request,m_alias);

		try
		{
			//	プロセス実行
			subProcess.RunSubProcess(command,workDir,threadID);
		}
		catch(CSubProcessException err)
		{
			m_body += "コマンドが実行できませんでした";
		}

		m_body += RemoveCGIHeader(subProcess.m_stdout);
	}

	return(TRUE);
}

//	環境取得
void CSSIResponse::GetEnvironment(CWorkspaceAccess envAccess)
{
	CSubProcess::GetCGIServerEnvironment(envAccess,m_server,m_context,m_request,m_alias);

	envAccess.SetConfig("DOCUMENT_NAME",CDir::PathToName(m_alias.GetTarget()));
	envAccess.SetConfig("DOCUMENT_URI",m_request.GetURI());
	envAccess.SetConfig("QUERY_STRING_UNESCAPED",m_request.GetQuery());

	CTime			local = CTime::GetCurrentTime();
	envAccess.SetConfig("DATE_LOCAL",local.Format(m_currentTimeFormat));
	envAccess.SetConfig("DATE_GMT",local.FormatGmt(m_currentTimeFormat));

	CFileStatus	stat;
	CFile::GetStatus(m_alias.GetTarget(),stat);
	envAccess.SetConfig("LAST_MODIFIED",stat.m_mtime.Format(m_currentTimeFormat));
}



//	名前から物理パスを取得
CString CSSIResponse::NameToLocalPath(CString name)
{
	CString	workdir = CDir::PathToDir(m_alias.GetTarget());

	// ## 2005/08/24 "/" に対応
	if(name.Left(2) == "./")
	{
		name.Replace("/","\\");
		name = workdir + name.Mid(2);						//	./xxx.exe
	}
    else if(name.GetLength() < 3 || name.Mid(1,2) != ":\\")
	{
		name.Replace("/","\\");
		name = workdir + name;								//	xxx.exe
	}

	if(CDir::FileExist(name) == CDir::FILE_TYPE_FILE)
		return(name);

	return("");
}

//	サーバパスから物理パスを取得
CString CSSIResponse::ServerPathToLocalPath(CString path)
{
	if(path.Left(1) != "/")
	{
		CString dir = m_request.GetURIObject();
		int index = dir.ReverseFind('/');
		if(index != -1)
			dir = dir.Left(index + 1);
		path = dir + path;
	}

	CAlias	alias(path,m_alias.GetTargetHost(), m_server->m_setting);

	if(alias.IsExist())
		return(alias.GetTarget());

	return("");
}

//	CGIヘッダを削除
CString CSSIResponse::RemoveCGIHeader(CString text)
{
	//	ヘッダー部分検索
	int index = text.Find("\n\n");
	if(index != -1)
	{
		return(text.Mid(index + 2));
	}
	
	index = text.Find("\r\n\r\n");
	if(index != -1)
	{
		return(text.Mid(index + 4));
	}

	return(text);
}



