/*!
	## 2005/08/24 D:resourcetype
*/

#include "StdAfx.h"
#include "davpropfindresponse.h"
#include "connection.h"
#include "JISUtility.h"
#include "UrlUtility.h"
#include "Dir.h"
#include "DAVPropfindReqParser.h"

//	#define	_WEB_DAV_DEBUG_DUMP

CDAVPropfindResponse::CDAVPropfindResponse(CServer *server,CWorkspaceAccess context,CRequest request,CAlias alias)
: CNormalResponse(server,context,request,alias)
{
	//	実は無いとか
	if(alias.IsExist()==0)
		throw new CSystemResponse(server,context,"指定されたディレクトリは存在しません",404);

	m_propList = m_propRoot.GetAccess("DAV");
}

CDAVPropfindResponse::~CDAVPropfindResponse(void)
{
}

void CDAVPropfindResponse::SetNextData(CBuffer &data)
{

#ifdef	_WEB_DAV_DEBUG_DUMP
	FILE *test = fopen("test.xml","at");
	if(test)
	{
		fwrite(data.GetPtr(),data.GetSize(),1,test);
		fclose(test);
	}
#endif
	//	追加
	m_requestBody.Append(data.GetPtr(), data.GetSize());
}

/*
	レスポンス生成
*/
void CDAVPropfindResponse::CreateResponse()
{
	//	要求は来てるか？
	if(m_requestBody.GetSize() > 0)
	{
		//	リクエストを解析
		CDAVPropfindReqParser	parser;
		if(parser.ParsePropfind(m_requestBody, m_propList))
			throw new CSystemResponse(m_server, m_context, "リクエストが異常です", 500);
	}

	//	全てを指定？
	if(m_requestBody.GetSize() == 0 || m_propList.KeyIsExist(_T("allprop")))
	{
		m_propList.SetKeyInt(_T("DAV:getcontentlanguage"), 0);
		m_propList.SetKeyInt(_T("DAV:creationdate"), 0);
		m_propList.SetKeyInt(_T("DAV:displayname"), 0);
		m_propList.SetKeyInt(_T("DAV:getcontentlength"), 0);
		m_propList.SetKeyInt(_T("DAV:getcontenttype"), 0);
		m_propList.SetKeyInt(_T("DAV:getcontentlanguage"), 0);
		m_propList.SetKeyInt(_T("DAV:getlastmodified"), 0);
		m_propList.SetKeyInt(_T("DAV:resourcetype"), 0);
		m_propList.SetKeyInt(_T("DAV:iscollection"), 0);
		m_propList.SetKeyInt(_T("DAV:ishidden"), 0);
	}
	m_propList.GetAllKey(m_propListNames);

	//	レスポンスコード
	SetResponseCode(207);	//Multi status

	//	タイプ
	m_responseOptions.SetConfig("Content-Type","text/xml; charset=\"utf-8\"");
	m_responseOptions.SetConfig("DAV","1");

	try
	{
		//	MSXML2 インスタンスの生成
		MSXML2::IXMLDOMDocumentPtr pXMLDom(CLSID_DOMDocument);

		//	ProcessingInstruction の追加
		MSXML2::IXMLDOMProcessingInstructionPtr pi;
		pi = pXMLDom->createProcessingInstruction("xml", "version=\"1.0\"");

		pXMLDom->appendChild(pi);
		pi.Release();

		// ルートを追加.
		MSXML2::IXMLDOMElementPtr pe;
		pe = pXMLDom->createElement("D:multistatus");
		pXMLDom->appendChild(pe);

		//	アトリビュート
		MSXML2::IXMLDOMAttributePtr pa;
		pa = pXMLDom->createAttribute("xmlns:D");
		pa->value = "DAV:";
		pe->setAttributeNode(pa);
		pa.Release();

		FindAll(pXMLDom,pe);

		//	UTF8に変換
		CBuffer	unicode;
		_bstr_t	bstr(pXMLDom->xml);
		unicode.ReSize(bstr.length() * sizeof(wchar_t));
		unicode.BlockCopy(0,(char *)((wchar_t*)bstr),bstr.length() * sizeof(wchar_t));
		unicode += (char)0;
		unicode += (char)0;
		m_body = CJISUtility::UnicodeToUTF8(unicode);

#ifdef	_WEB_DAV_DEBUG_DUMP
		CBinaryData	test;
		test.AppendString(m_body);
		test.SaveToFile("out.xml");
#endif
	}
	catch(const _com_error& err)
	{
		CString str;
		str.Format("XMLの操作に失敗しました、システムエラーです [ComError %s]",err.ErrorMessage());
		throw CServerFatalException(str);
	}
}

void CDAVPropfindResponse::FindAll(MSXML2::IXMLDOMDocumentPtr doc,MSXML2::IXMLDOMElementPtr res)
{
	CString url = m_request.GetURIObject();
	CString	depth = m_request.GetOptionAccess().GetConfig("Depth","1",TRUE);
	int		showHidden = m_alias.GetPathOptions().GetConfig("EnableHiddenFile",0);

	int				done=1;
	CFileFind		find;
	
	if(m_alias.IsDirectory())
	{
		//	"/"が無い？
		url = CDir::AddSlash(url);

		//	ドライブを指定した場合、"."が無いため、正常に動作しない
		CString target = m_alias.GetTarget();
		target = target.Left(target.GetLength() - 1);
		if(target.Find("\\") == -1)
		{
			AddVolumeInfo(doc,res,m_alias.GetTarget(),url);
		}

		if(find.FindFile(m_alias.GetTarget() + "*.*"))
		{
			while(done)
			{
				done=find.FindNextFile();

				if(find.GetFileName() == ".")
				{
					AddInfo(doc,res,find,url);
				}
				else if(depth != "0")
				{
					if(find.IsDots())
						continue;

					if(find.IsHidden() && showHidden == 0)
						continue;

					if(find.IsDirectory())
					{
						AddInfo(doc,res,find,url + find.GetFileName() + "/");
						if(depth == "infinity")
							FindFiles(doc,res,url + find.GetFileName() + "/",m_alias.GetTarget() + find.GetFileName());
					}
					else
					{
						AddInfo(doc,res,find,url + find.GetFileName());
					}
				}
			}
		}
		find.Close();
	}
	else
	{
		if(find.FindFile(m_alias.GetTarget()))
		{
			while(done)
			{
				done=find.FindNextFile();

				if(find.IsDots())
					continue;

				if(find.IsHidden() && showHidden == 0)
					continue;

				AddInfo(doc,res,find,url + find.GetFileName());
				break;
			}
		}
		find.Close();
	}

	m_responseOptions.SetConfig("Content-Location",url);

}

//	下層の検索
void CDAVPropfindResponse::FindFiles(MSXML2::IXMLDOMDocumentPtr doc,MSXML2::IXMLDOMElementPtr res,CString url,CString path)
{
	int				showHidden = m_alias.GetPathOptions().GetConfig("EnableHiddenFile",0);

	int				done=1;
	CFileFind		find;


	if(find.FindFile(path + "*.*"))
	{
		while(done)
		{
			done=find.FindNextFile();

			if(find.IsDots())
				continue;

			if(find.IsHidden() && showHidden == 0)
				continue;

			if(find.IsDirectory())
			{
				AddInfo(doc,res,find,url + find.GetFileName() + "/");
				FindFiles(doc,res,url + find.GetFileName() + "/",m_alias.GetTarget() + find.GetFileName());
				continue;
			}

			AddInfo(doc,res,find,url + find.GetFileName());
		}
	}
	find.Close();
}


void CDAVPropfindResponse::AddInfo(MSXML2::IXMLDOMDocumentPtr doc,MSXML2::IXMLDOMElementPtr res,CFileFind &find,CString url)
{
	CString			str;

	//	フラグクリア
	ClearPropFlag();

	//	段下げ
	res->appendChild(doc->createTextNode(_bstr_t("\r\n")));

	// D:responseを追加.
	MSXML2::IXMLDOMElementPtr response;
	response = doc->createElement("D:response");
	res->appendChild(response);

	//	href追加
	MSXML2::IXMLDOMElementPtr href;
	href = doc->createElement("D:href");
	href->text = _bstr_t(EncodeUrl(url));
	response->appendChild(href);

	//	D:propstat追加
	MSXML2::IXMLDOMElementPtr propstat;
	propstat = doc->createElement("D:propstat");
	response->appendChild(propstat);

	//	D:status
	AddAttr(doc,propstat,"D:status","HTTP/1.1 "+GetHTTPResponseString(200));

	//	D:prop追加
	MSXML2::IXMLDOMElementPtr prop;
	prop = doc->createElement("D:prop");
	propstat->appendChild(prop);

	//	D:getcontentlanguage
	if(PropExistSetFlag(_T("DAV:getcontentlanguage")))
	{
		AddAttr(doc,prop,"D:getcontentlanguage", _T("jp"));
	}

	//	D:creationdate
	if(PropExistSetFlag(_T("DAV:creationdate")))
	{
		CTime	time;
		find.GetCreationTime(time);
		AddAttr(doc,prop,"D:creationdate",time.FormatGmt("%Y-%m-%dT%H:%M:%SZ"));
	}

	//	D:displayname
	if(PropExistSetFlag(_T("DAV:displayname")))
	{
		AddAttr(doc,prop,"D:displayname",find.GetFileName());
	}

	//	D:getcontentlength
	if(PropExistSetFlag(_T("DAV:getcontentlength")))
	{
		str.Format("%I64d",find.GetLength());
		AddAttr(doc,prop,"D:getcontentlength",str);
	}

	//	D:getcontenttype
	if(PropExistSetFlag(_T("DAV:getcontenttype")))
	{
		if(find.IsDirectory())
			AddAttr(doc,prop,"D:getcontenttype", _T("application/octet-stream"));
		else
			AddAttr(doc,prop,"D:getcontenttype",m_server->GetCMimeTypeMgr().GetMimeTypeByName(find.GetFileName()));
	}

	//	D:getlastmodified
	if(PropExistSetFlag(_T("DAV:getlastmodified")))
	{
		CTime	time;
		find.GetLastWriteTime(time);
		AddAttr(doc,prop,"D:getlastmodified",time.FormatGmt("%a, %d %b %Y %H:%M:%S GMT"));
	}

	// ## 2005/08/24
	//	D:resourcetype
	if(PropExistSetFlag(_T("DAV:resourcetype")))
	{
		if(find.IsDirectory())
		{
			MSXML2::IXMLDOMElementPtr resourcetype;
			resourcetype = doc->createElement("D:resourcetype");
			prop->appendChild(resourcetype);
			AddAttr(doc,resourcetype,"D:collection","");
		}
		else
		{
			AddAttr(doc,prop,"D:resourcetype","");
		}
	}

	//	D:iscollection
	if(PropExistSetFlag(_T("DAV:iscollection")))
	{
		if(find.IsDirectory())
			AddAttr(doc,prop,"D:iscollection","1");
		else
			AddAttr(doc,prop,"D:iscollection","0");
	}

	//	D:ishidden
	if(PropExistSetFlag(_T("DAV:ishidden")))
	{
		AddAttr(doc,prop,"D:ishidden","0");
	}

	//	D:getetag
	if(PropExistSetFlag(_T("DAV:getcontenttype")))
	{
		AddAttr(doc,prop,"D:getetag",GetETag(find));
		m_propList.SetKeyInt(_T("DAV:getcontenttype"), 1);
	}

	//	見つからなかった
	AddNotFound(doc, response);
}

void CDAVPropfindResponse::AddVolumeInfo(MSXML2::IXMLDOMDocumentPtr doc,MSXML2::IXMLDOMElementPtr res,CString volume,CString url)
{
	CString str;

	//	フラグクリア
	ClearPropFlag();

	// D:responseを追加.
	MSXML2::IXMLDOMElementPtr response;
	response = doc->createElement("D:response");
	res->appendChild(response);

	//	href追加
	MSXML2::IXMLDOMElementPtr href;
	href = doc->createElement("D:href");
	href->text = _bstr_t(url);
	response->appendChild(href);

	//	D:propstat追加
	MSXML2::IXMLDOMElementPtr propstat;
	propstat = doc->createElement("D:propstat");
	response->appendChild(propstat);

	//	D:status
	AddAttr(doc,propstat,"D:status","HTTP/1.1 "+GetHTTPResponseString(200));

	//	D:prop追加
	MSXML2::IXMLDOMElementPtr prop;
	prop = doc->createElement("D:prop");
	propstat->appendChild(prop);

	/*
	//	D:creationdate
	AddAttr(doc,prop,"D:creationdate","");

	//	D:getcontentlength
	AddAttr(doc,prop,"D:getcontentlength","");

	//	D:getetag(Not support)
	AddAttr(doc,prop,"D:getetag","");

	//	D:getlastmodified
	AddAttr(doc,prop,"D:getlastmodified","");
	*/

	//	D:getcontentlanguage
	if(PropExistSetFlag(_T("DAV:getcontentlanguage")))
	{
		AddAttr(doc,prop,"D:getcontentlanguage", _T("jp"));
	}

	//	D:creationdate
	if(PropExistSetFlag(_T("DAV:creationdate")))
	{
		CTime	time(0);
		AddAttr(doc,prop,"D:creationdate",time.FormatGmt("%Y-%m-%dT%H:%M:%SZ"));
	}

	//	D:displayname
	if(PropExistSetFlag(_T("DAV:displayname")))
	{
		AddAttr(doc,prop,"D:displayname",".");
	}

	//	D:getlastmodified
	if(PropExistSetFlag(_T("DAV:getlastmodified")))
	{
		CTime	time(0);
		AddAttr(doc,prop,"D:getlastmodified",time.FormatGmt("%a, %d %b %Y %H:%M:%S GMT"));
	}

	//	D:getcontentlength
	if(PropExistSetFlag(_T("DAV:getcontentlength")))
	{
		AddAttr(doc,prop,"D:getcontenttype", _T("0"));
	}

	//	D:getcontenttype
	if(PropExistSetFlag(_T("DAV:getcontenttype")))
	{
		AddAttr(doc,prop,"D:getcontenttype", _T("application/octet-stream"));
	}

	//	D:getetag(Not support)
	if(PropExistSetFlag(_T("DAV:getcontenttype")))
	{
		AddAttr(doc,prop,"D:getetag","0000-0000-0000");
		m_propList.SetKeyInt(_T("DAV:getcontenttype"), 1);
	}

	//	D:resourcetype
	if(PropExistSetFlag(_T("DAV:resourcetype")))
	{
		MSXML2::IXMLDOMElementPtr resourcetype;
		resourcetype = doc->createElement("D:resourcetype");
		prop->appendChild(resourcetype);
		AddAttr(doc,resourcetype,"D:collection","");
	}

	//	D:iscollection
	if(PropExistSetFlag(_T("DAV:iscollection")))
	{
		AddAttr(doc,prop,"D:iscollection","1");
	}

	//	D:ishidden
	if(PropExistSetFlag(_T("DAV:ishidden")))
	{
		AddAttr(doc,prop,"D:ishidden","0");
	}

	//	見つからなかった
	AddNotFound(doc, response);
}


void CDAVPropfindResponse::AddAttr(MSXML2::IXMLDOMDocumentPtr doc,MSXML2::IXMLDOMElementPtr res,CString attr,CString text)
{
	MSXML2::IXMLDOMElementPtr attrleaf;
	attrleaf = doc->createElement(_bstr_t(attr));
	attrleaf->text = _bstr_t(text);
	res->appendChild(attrleaf);
}


/*!
	見つからなかった項目をNotFoundに
*/
void CDAVPropfindResponse::AddNotFound(MSXML2::IXMLDOMDocumentPtr doc, MSXML2::IXMLDOMElementPtr response)
{
	//	必要はあるか？
	int	notFoundCount = 0;
	for(int i=0;i<m_propListNames.GetCount();i++)
		if(m_propList.GetKeyInt(m_propListNames[i], 0) == 0)
			notFoundCount++;
	if(notFoundCount == 0)
		return;

	//	D:propstat追加
	MSXML2::IXMLDOMElementPtr propstat;
	propstat = doc->createElement("D:propstat");
	response->appendChild(propstat);

	//	D:status
	AddAttr(doc,propstat,"D:status", "HTTP/1.1 " + GetHTTPResponseString(404));

	//	D:prop追加
	MSXML2::IXMLDOMElementPtr prop;
	prop = doc->createElement("D:prop");
	propstat->appendChild(prop);

	//	見つからなかったものをNotFoundへ追加
	for(int i=0;i<m_propListNames.GetCount();i++)
	{
		if(m_propList.GetKeyInt(m_propListNames[i], 0) == 0)
		{
			CString name = m_propListNames[i];

			//	ネームスペース変換
			if(name.Left(4) == _T("DAV:"))
			{
				name = _T("D:") + name.Mid(4);
				AddAttr(doc,prop,name,_T(""));
			}
		}
	}
}


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

/*!
	レスポンスの取得
*/
int CDAVPropfindResponse::GetNextData(CBuffer &data)
{
	int len = m_body.GetLength();

	data = m_body;
	m_body = "";

	return(len);
}

/*!
	URLエンコード
*/
CString CDAVPropfindResponse::EncodeUrl(CString url)
{
	CString ret = CUrlUtility::EncodeUrl8Bit(CJISUtility::SjisToUTF8(url));

	ret.Replace("&","%26");

	return(ret);
}



/////////////////////////////////////////////////////////////////////////////////////////////
//	ツール関数
/////////////////////////////////////////////////////////////////////////////////////////////
/*!
	プロパティフラグの全削除
*/
void CDAVPropfindResponse::ClearPropFlag()
{
	for(int i=0;i<m_propListNames.GetCount();i++)
		m_propList.SetKeyInt(m_propListNames[i], 0);
}

/*!
	指定されたプロパティがあるか取得し、あればフラグを立てる
*/
int CDAVPropfindResponse::PropExistSetFlag(CString prop)
{
	if(m_propList.KeyIsExist(prop))
	{
		m_propList.SetKeyInt(prop, 1);
		return 1;
	}
	return 0;
}


/*!
	ETgaの取得
*/
CString	CDAVPropfindResponse::GetETag(CFileFind &find)
{
	//	04WebServerのETagは、ファイルサイズ、ファイルパス、ファイル日付より取得
	__int64 size = find.GetLength();
	CString	path = find.GetFilePath();
	CTime	time;
	find.GetLastWriteTime(time);

	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);
}


