#include "StdAfx.h"
#include "CGIProcess.h"

#include "TextTools.h"
#include "TextFormatter.h"
#include "ThreadSkeleton.h"
#include "IntervalTimer.h"

CCGIProcess::CCGIProcess(void)
{
	m_envAccess	= m_envRoot.GetAccess("Env");
}

CCGIProcess::~CCGIProcess(void)
{
}

///////////////////////////////////////////////////////////////////////////
//	実行
///////////////////////////////////////////////////////////////////////////
/*!
	CGIコマンドライン取得
*/
CString CCGIProcess::CreateCGICommandLine(CContext context, CCGICommand cgiCommand)
{
	CTextFormatter	formatter;

	//	置き換え追加
	formatter.AddKey("FilePath", context.GetTargetInfo().GetTarget());
	formatter.AddKey("Query", context.GetRequestInfo().GetQuery());

	//	取得
	return formatter.GetFormattedString(cgiCommand.GetCGICommand());
}



/*!
	ヘッダ取得

*/
CString CCGIProcess::GetHeaderBlock(CBinaryData &readBuffer)
{
	//	ヘッダ取得
	CString header = readBuffer.GetCString();

	//	ヘッダ部分検索
	int index1 = header.Find("\r\n\r\n");
	int index2 = header.Find("\n\n");

	if(index1 != -1 && (index2 == -1 || index2 > index1))
	{
		//	ヘッダ分削除
		readBuffer.Remove(0, index1+4);

		//	ヘッダ取得
		header = header.Left(index1);
		return(header);
	}

	if(index2 != -1)
	{
		//	ヘッダ分削除
		readBuffer.Remove(0, index2+2);

		//	ヘッダ取得
		header = header.Left(index2);
		header.Replace("\r\n", "\n");
		header.Replace("\n", "\r\n");
		return(header);
	}
	return("");
}


/*!
	ヘッダ解析
*/
void CCGIProcess::ParseCGIHeader(CString headerBlock, CCGIHeader headerOption)
{
	CString line;
	while(CTextTools::GetLine(headerBlock, line))
	{
		//	: 取得
		int index = line.Find(":");
		if(index == -1)
			continue;

		//	切り出し
		CString key = line.Left(index);
		CString data = line.Mid(index + 1);
		key.Trim();
		data.Trim();

		//	設定
		if(key.CompareNoCase("Status") == 0)
			headerOption.SetStatus(atoi(data));
		else if(key.CompareNoCase("Content-Type") == 0)
			headerOption.SetContentType(data);
		else if(key.CompareNoCase("Content-Length") == 0)
			headerOption.SetContentLength(CTextTools::strtoi64(data, 10));
		else if(key.CompareNoCase("Location") == 0)
			headerOption.SetLocation(data);
		else
		{
			//	CGI-Extension header
			headerOption.SetExtraHeader(headerOption.GetExtraHeader() + key + ": " + data + "\r\n");
		}
	}
}


///////////////////////////////////////////////////////////////////////////
//	環境変数
///////////////////////////////////////////////////////////////////////////
/*!
	サーバ情報環境変数を追加
*/
void CCGIProcess::AddServerInfoToEnv(CServerInfo serverInfo)
{

	//	CGI プログラム を起動したサーバソフトウエアの名前とバージョン情報。
	m_envAccess.SetKeyStr("SERVER_SOFTWARE", serverInfo.GetServerSignature());

	//	サーバのシグニチャ
	m_envAccess.SetKeyStr("SERVER_SIGNATURE", CTextTools::GetFormat("<address>%s</address>", serverInfo.GetServerSignature()));

	//	サーバのホスト名、ドメイン名、またはIPアドレス情報。 
	m_envAccess.SetKeyStr("SERVER_NAME", serverInfo.GetServerHostName());

	//	サーバが使用するCGIのレビジョン情報
	m_envAccess.SetKeyStr("GATEWAY_INTERFACE", "CGI/1.1");

	//	プロトコルの名前とレビジョン情報。
	m_envAccess.SetKeyStr("SERVER_PROTOCOL", "HTTP/1.1");
}

/*!
	要求情報環境変数を追加
*/
void CCGIProcess::AddRequestInfoToEnv(CContext context)
{
	/*
		クライアント基本情報
	*/
	//	サーバのアドレス
	m_envAccess.SetKeyStr("SERVER_ADDR", context.GetClientInfo().GetAcceptedAddress());

	//	サーバのポート番号。 
	m_envAccess.SetKeyInt("SERVER_PORT", context.GetClientInfo().GetClientPort());



	/*
		リクエスト基本情報
	*/
	CContextRequestInfo	requestInfo = context.GetRequestInfo();

	//	リクエストを送信した方法（メソッド）。GETやPOSTなど。 
	m_envAccess.SetKeyStr("REQUEST_METHOD", requestInfo.GetMethod());

	//	POSTから標準入力に送信されたデータのバイト数。 
	m_envAccess.SetKeyInt64("CONTENT_LENGTH", requestInfo.GetRequestOption().GetContentLength());
 
	//	POSTとPUTなどのクエリに添付されたデータのMIMEタイプ。
	m_envAccess.SetKeyStr("CONTENT_TYPE", requestInfo.GetRequestOption().GetContentType());

	//	クエリとして送られたURL情報のクエスチョン( ? )以降の情報。データは『URLエンコード』されています。 
	m_envAccess.SetKeyStr("QUERY_STRING", requestInfo.GetQuery());

	//	URI
	m_envAccess.SetKeyStr("REQUEST_URI", requestInfo.GetURL());


	/*
		パスに関する情報
	*/
	//	仮想パス
	m_envAccess.SetKeyStr("SCRIPT_NAME", requestInfo.GetObject());

	//	絶対パス。
	m_envAccess.SetKeyStr("SCRIPT_FILENAME", BackSlashToSlash(context.GetTargetInfo().GetTarget()));

	//	エクストラパス
	if(context.GetTargetInfo().ExtraPathIsSet())
	{
		CString	extraPath = context.GetTargetInfo().GetExtraPath();

		//	エクストラパス
		m_envAccess.SetKeyStr("PATH_INFO", extraPath);

		//	エクストラパスのローカルパス変換
		m_envAccess.SetKeyStr("PATH_TRANSLATED", BackSlashToSlash(context.GetTargetInfo().GetTarget()) + extraPath);

		//	スクリプト名修正
		CString	uri = requestInfo.GetURL();
		if(uri.GetLength() > extraPath.GetLength())
			uri = uri.Left(uri.GetLength() - extraPath.GetLength());
		m_envAccess.SetKeyStr("SCRIPT_NAME", uri);
	}

	/*
		クライアントに関する環境変数のリスト
	*/
	//	リモートホストのIPアドレス情報。 
	m_envAccess.SetKeyStr("REMOTE_ADDR", context.GetClientInfo().GetClientAddress());

	//	リモートホストのホスト名情報。 
	m_envAccess.SetKeyStr("REMOTE_HOST", context.GetClientInfo().GetClientHostName());

	//	リモートホストのポート。 
	m_envAccess.SetKeyInt("REMOTE_PORT", context.GetClientInfo().GetClientPort());



	/*
		ユーザに関する環境変数のリスト
	*/
	//	ユーザを認証するときに使用する認証メソッド。ウェブサーバとリモートホストがIDENTD認証デーモン（ユーザ認証プロトコル）を実行しているときだけ値がある。 
	m_envAccess.SetKeyStr("AUTH_TYPE", "basic");

	//	ユーザの認証名。ウェブサーバとリモートホストがIDENTD認証デーモン（ユーザ認証プロトコル）を実行しているときだけ値があります。 
	m_envAccess.SetKeyStr("REMOTE_USER", requestInfo.GetUser());

	//	ユーザの認証名。ウェブサーバとリモートホストがIDENTD認証デーモン（ユーザ認証プロトコル）を実行しているときだけ値があります。 
	m_envAccess.SetKeyStr("AUTH_USER", requestInfo.GetUser());

 	//	ユーザのパスワード。ウェブサーバとリモートホストがIDENTD認証デーモン（ユーザ認証プロトコル）を実行しているときだけ値があります。 
	m_envAccess.SetKeyStr("AUTH_PASSWORD", requestInfo.GetPasswd());

	//	ユーザの認証名 
	m_envAccess.SetKeyStr("PHP_AUTH_USER", requestInfo.GetUser());

 	//	ユーザのパスワード
	m_envAccess.SetKeyStr("PHP_AUTH_PW", requestInfo.GetPasswd());


	/*
		リクエストヘッダの変換
	*/
	CStringArray keyList;
	requestInfo.GetRequestOption().GetAllKey(keyList);

	//	変換
	for(int i=0;i<keyList.GetSize();i++)
	{
		CString	key = keyList[i];
		m_envAccess.SetKeyStr("HTTP_" + key.MakeUpper(), requestInfo.GetRequestOption().GetKeyStr(keyList[i], ""));
	}
}


/*!
	スクリプトパス情報を設定（SSI用）

	修正する関数のため、最後に呼び出す必要がある
*/
void CCGIProcess::ChangeScriptPath(CString virtualPath, CPathInfo pathInfo)
{
	//	仮想パス
	m_envAccess.SetKeyStr("SCRIPT_NAME", virtualPath);

	//	絶対パス。
	int	serverPathCount = pathInfo.GetServerPath().GetLength();
	m_envAccess.SetKeyStr("SCRIPT_FILENAME", BackSlashToSlash(pathInfo.GetLocalPath() + virtualPath.Mid(serverPathCount)));


	/*!
		無関係の変数を削除
	*/
	//	POSTから標準入力に送信されたデータのバイト数。 
	m_envAccess.DeleteKey("CONTENT_LENGTH");
 
	//	POSTとPUTなどのクエリに添付されたデータのMIMEタイプ。
	m_envAccess.DeleteKey("CONTENT_TYPE");

	//	クエリとして送られたURL情報のクエスチョン( ? )以降の情報。データは『URLエンコード』されています。 
	m_envAccess.DeleteKey("QUERY_STRING");

	//	エクストラパス
	m_envAccess.DeleteKey("PATH_INFO");

	//	エクストラパスのローカルパス変換
	m_envAccess.DeleteKey("PATH_TRANSLATED");
}

