//
// DataRetriever.cpp
//
// $Id: //poco/Main/WebWidgets/src/DataRetriever.cpp#1 $
//
//
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
// 
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//


#include "Poco/WebWidgets/DataRetriever.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Net/HTTPServerResponse.h"
#include "Poco/Logger.h"
#include "Poco/StringTokenizer.h"
#include "Poco/StreamCopier.h"
#include <fstream>


namespace Poco {
namespace WebWidgets {


const std::string DataRetriever::INDEX_HTM("/index.htm");
const std::string DataRetriever::INDEX_HTML("/index.html");
const std::string DataRetriever::MSG_LISTING_NOT_ALLOWED("<HTML><HEAD><TITLE>Not Allowed</TITLE></HEAD><BODY><H1>Browsing of directories not allowed</H1></BODY></HTML>");
const std::string DataRetriever::MSG_NOT_FOUND("<HTML><HEAD><TITLE>Not Allowed</TITLE></HEAD><BODY><H1>File Not Found</H1></BODY></HTML>");
std::map<std::string, std::string> DataRetriever::EXTENSIONMAPPING(DataRetriever::initExtensions());

DataRetriever::DataRetriever(const DataRetriever::Aliases& alias, Poco::Logger* pLogger):
	_aliases(alias),
	_pLogger(pLogger)
{
	// a root must exist!
	poco_assert_dbg (_aliases.find("") != _aliases.end());
}


DataRetriever::~DataRetriever()
{
}


void DataRetriever::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response)
{
	std::string path = request.getURI();
	if (path.empty() || path == "/")
	{
		path = INDEX_HTML;
	}
	if (!path.empty() && path[0] == '/')
	{
		path = path.substr(1);
	}

	path = resolveAliases(path);

	Poco::File aFile(path);
	if (!aFile.exists())
	{
		if (_pLogger)
			_pLogger->warning("File not found: " + path);
		handleNotFound(response);
	}
	else if (aFile.isDirectory())
	{
		if (_pLogger)
			_pLogger->warning("Illegal directory access: " + path);
		handleBrowsingNotAllowed(response);
	}
	else
	{
		sendFile(path, response);
	}
}


std::string DataRetriever::resolveAliases(const std::string& path)
{
	Poco::StringTokenizer tok(path, "/", Poco::StringTokenizer::TOK_IGNORE_EMPTY | Poco::StringTokenizer::TOK_TRIM);
	// the path is either only / : tok.count == 0
	// or /file : note if we have an alias for the same name we assume it is a directory that is requested
	// or /dir
	// or /dir/subdir/
	Aliases::const_iterator itRoot = _aliases.find("");
	if(tok.count() > 0)
	{
		Poco::StringTokenizer::Iterator itTok = tok.begin();
		Poco::StringTokenizer::Iterator itTokEnd = tok.end();
		Aliases::const_iterator it = _aliases.find(*tok.begin());
		if (it != _aliases.end())
		{
			// set the new root
			itRoot = it;
			// remove the resolved first path
			++itTok;
		}
		Poco::Path aPath(itRoot->second);
		aPath.makeDirectory();
		for (; itTok != itTokEnd; ++itTok)
		{
			aPath.pushDirectory(*itTok);
		}
		std::string result = aPath.toString();
		if (path[path.size()-1] != '/' && path[path.size()-1] != '\\')
			return result.substr(0, result.size() - 1);
		return result;
	}

	return itRoot->second.toString()+path;
}


void DataRetriever::handleBrowsingNotAllowed(Poco::Net::HTTPServerResponse& response)
{
	response.setChunkedTransferEncoding(true);
	response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_FORBIDDEN);
	std::ostream& out = response.send();
	out << MSG_LISTING_NOT_ALLOWED;
}


void DataRetriever::handleNotFound(Poco::Net::HTTPServerResponse& response)
{
	response.setChunkedTransferEncoding(true);
	response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_NOT_FOUND);
	std::ostream& out = response.send();
	out << MSG_NOT_FOUND;
}


void DataRetriever::sendFile(const std::string& path, Poco::Net::HTTPServerResponse& response)
{
	//Poco::Util::Application::instance().logger().information("Sent file: " + path);
	Poco::Path aPath(path);
	std::string ext = Poco::toLower(aPath.getExtension());
	std::map<std::string, std::string>::const_iterator it = EXTENSIONMAPPING.find(ext);
	if (it != EXTENSIONMAPPING.end())
	{
		response.setContentType(it->second);
	}
	else
		response.setContentType("text/html");
	response.setChunkedTransferEncoding(true);
	std::ifstream file(path.c_str(), std::ios::binary);
	std::ostream& out = response.send();
	Poco::StreamCopier::copyStream(file, out);
}


std::map<std::string, std::string> DataRetriever::initExtensions()
{
	std::map<std::string, std::string> ext;
	ext.insert(std::make_pair("", "text/html"));
	ext.insert(std::make_pair("htm", "text/html"));
	ext.insert(std::make_pair("html", "text/html"));
	ext.insert(std::make_pair("bmp", "image/bmp"));
	ext.insert(std::make_pair("gif", "image/gif"));
	ext.insert(std::make_pair("jpe", "image/jpeg"));
	ext.insert(std::make_pair("jpg", "image/jpeg"));
	ext.insert(std::make_pair("jpeg", "image/jpeg"));
	ext.insert(std::make_pair("log", "text/plain"));
	ext.insert(std::make_pair("txt", "text/plain"));
	ext.insert(std::make_pair("png", "image/png"));
	ext.insert(std::make_pair("ico", "image/x-icon"));
	ext.insert(std::make_pair("xml", "text/xml"));
	ext.insert(std::make_pair("css", "text/css"));
	ext.insert(std::make_pair("rss", "application/rss+xml"));
	ext.insert(std::make_pair("js", "text/javascript"));
	return ext;
}


} } // namespace Poco::WebWidgets

