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

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

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

CDAVPropfindResponse::~CDAVPropfindResponse(void)
{
}

void CDAVPropfindResponse::SetNextData(CBuffer &data)
{
	/*
	## 2005/8/24
	FILE *test = fopen("test.txt","at");
	if(test)
	{
		fwrite(data.GetPtr(),data.GetSize(),1,test);
		fclose(test);
	}
	*/
}

/*
	レスポンス生成
*/
void CDAVPropfindResponse::CreateResponse()
{
	//	レスポンスコード
	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);

	}
	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.m_uriObject;
	CString	depth = m_request.m_options.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;

	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:creationdate
	CTime	time;
	find.GetCreationTime(time);
	AddAttr(doc,prop,"D:creationdate",time.FormatGmt("%Y-%m-%dT%H:%M:%SZ"));

	//	D:displayname
	AddAttr(doc,prop,"D:displayname",find.GetFileName());

	//	D:getcontentlength
	str.Format("%I64d",find.GetLength());
	AddAttr(doc,prop,"D:getcontentlength",str);

	//	D:getcontenttype
	AddAttr(doc,prop,"D:getcontenttype",m_server->GetCMimeTypeMgr().GetMimeTypeByName(find.GetFileName()));

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

	//	D:getlastmodified
	find.GetLastWriteTime(time);
	AddAttr(doc,prop,"D:getlastmodified",time.FormatGmt("%a, %d %b %Y %H:%M:%S GMT"));

	// ## 2005/08/24
	//	D: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(find.IsDirectory())
		AddAttr(doc,prop,"D:iscollection","1");
	else
		AddAttr(doc,prop,"D:iscollection","0");

	//	D:ishidden
	AddAttr(doc,prop,"D:ishidden","0");

}

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

	// 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:displayname
	AddAttr(doc,prop,"D:displayname","");

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

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

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

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

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

	//	D:iscollection
	AddAttr(doc,prop,"D:iscollection","1");

	//	D:ishidden
	AddAttr(doc,prop,"D:ishidden","0");
}


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


/*!
	レスポンスサイズの取得
*/
__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);
}
