/*!
	更新暦

	2005/05/20 CheckRange,GetETagの例外処理を修正
*/



#include "StdAfx.h"
#include "fileresponse.h"
#include "connection.h"
#include "DateUtility.h"
#include "SystemResponse.h"

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

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

CFileResponse::~CFileResponse(void)
{
	try
	{
		m_file.Close();
	}
	catch(CFileException *err)
	{
		err->Delete();
	}
}



/*
	レスポンス生成
*/
void CFileResponse::CreateResponse()
{
	//	レスポンスコード
	SetResponseCode(200);	//	OK

	//	ファイルエラースコープ
	try
	{
		TRACE("File : %s\n",m_alias.GetTarget());
		if(!m_file.Open(m_alias.GetTarget(),	CFile::modeRead | CFile::shareDenyWrite))
			throw CSystemResponse(m_server, m_context, "ファイルのオープンに失敗しました、サーバー上のファイルは、使用中のためロックされている可能性があります", 500);

		//	更新のチェック
		CFileStatus	stat;
		m_file.GetStatus(stat);
		int update = CheckUpdate(stat.m_mtime);

		//	ETagチェック
		int update2 = CheckETag(m_file);

		if(update == 1 || update2 == 1 || (update == -1 && update2 == -1))
		{
			//	レジュームする
			CheckRange();

			//	mimetypeの検索
			CMimeTypeMgr	mgr = m_server->GetCMimeTypeMgr();
			m_responseOptions.SetConfig("Content-Type",mgr.GetMimeTypeByName(m_alias.GetTarget()));										//	タイプ
		}
		else
			throw new CSystemResponse(m_server,m_context,"ターゲットは更新されていません",304);
	}
	catch(CFileException *err)
	{
		TRACE("File error : %s\n",err->m_strFileName);
		err->Delete();
		throw CSystemResponse(m_server, m_context, "ファイルの操作に失敗しました、システムエラーです", 500);
	}
}


/*!
	レスポンスサイズの取得
*/
__int64 CFileResponse::GetResponseSize()
{
	try
	{
		return(m_file.GetLength() - m_file.GetPosition());
	}
	catch(CFileException *err)
	{
		TRACE("File error : %s\n",err->m_strFileName);
		err->Delete();
		throw CSystemResponse(m_server, m_context, "ファイルの操作に失敗しました、システムエラーです", 500);
	}
}

/*!
	レスポンスの取得
*/
int CFileResponse::GetNextData(CBuffer &data)
{
	try
	{
		int size = m_file.Read(data.GetPtr(),data.GetSize());
		data.ReSize(size);
		return(size);
	}
	catch(CFileException *err) 
	{
		TRACE("File error : %s\n",err->m_strFileName);
		err->Delete();
		throw CServerFatalException("ファイルの操作に失敗しました、システムエラーです");	//	送信後なので、致命的エラーに
	}
}

/*!
 *	レジューム処理
 *
 *	@return		0:正常
 */
int CFileResponse::CheckRange()
{
	try
	{
		//	Rangeヘッダは？
		CString range = m_requestOptions.GetConfig("Range","");
		if(range != "")
		{
			//	サポートしてるのか？(bytesか？)
			//	Range: bytes=xxxx-
			if(range.Left(6).CompareNoCase("bytes=")==0)
			{
				range = range.Mid(6);
				int index = range.Find("-");
				if(index != -1)
					range = range.Left(index);

				__int64	start = _atoi64(range);
				if(start < 0 || (unsigned)start >= m_file.GetLength())
					return(-1);

				//	レジューム
				m_file.Seek(start,CFile::begin);
				SetResponseCode(206);
				CString	str;
				m_responseOptions.SetConfig("Accept-Ranges","bytes");
				str.Format("bytes %I64d-%I64d/%I64d",start,m_file.GetLength(),m_file.GetLength());
				m_responseOptions.SetConfig("Content-Range",str);
			}
		}
	}
	catch(CFileException *err) 
	{
		TRACE("File error : %s\n",err->m_strFileName);
		err->Delete();
		throw CSystemResponse(m_server, m_context, "ファイルの操作に失敗しました、システムエラーです", 500);
	}
			
	return(0);
}


/*!
 *	更新の確認とLast-modifiedの設定
 *
 *	@param	date	ターゲットの更新日時
 *	@return			1:更新されている
 */
int CFileResponse::CheckUpdate(CTime date)
{
	//	更新日付の出力
	m_responseOptions.SetConfig("Last-modified",CDateUtility::GetGMTString(date));

	//	更新チェック
	CString modified = m_requestOptions.GetConfig("If-Modified-Since","");
	if(modified.IsEmpty())
		return -1;

	CTime lm = CDateUtility::StringToCTime(modified);
	if(date <= lm && lm != 0)
		return(0);

	return 1;
}


/*!
 *	ETagの確認とETagの設定
 *
 *	@param	tag		ターゲットの情報
 *	@return			1:更新されている
 */
int CFileResponse::CheckETag(CFile &file)
{
	//	ETagの出力
	CString tag = GetETag(file);
	m_responseOptions.SetConfig("ETag",tag);

	//	ETagチェック
	CString modified = m_requestOptions.GetConfig("If-None-Match","");
	if(modified.IsEmpty())
		return(-1);

	if(modified == tag)
		return(0);

	return(1);
}


/*!
	ETgaの取得
*/
CString	CFileResponse::GetETag(CFile &file)
{
	try
	{
		CFileStatus	stat;
		m_file.GetStatus(stat);

		//	04WebServerのETagは、ファイルサイズ、ファイルパス、ファイル日付より取得
		__int64 size = m_file.GetLength();
		CTime	time = stat.m_mtime;
		CString	path = m_file.GetFilePath();

		int	calc=0;
		unsigned char *buf = (unsigned char *)path.GetBuffer();
		for(int i=0;i<path.GetLength();i++)
			calc += buf[i];

		CString str;
		str.Format("\"%I64x-%I64x:%04d\"",size,time.GetTime(),calc);

		return(str);
	}
	catch(CFileException *err) 
	{
		TRACE("File error : %s\n",err->m_strFileName);
		err->Delete();
		throw CSystemResponse(m_server, m_context, "ファイルの操作に失敗しました、システムエラーです", 500);
	}
}



