/*
	ZazouMiniWebServer

	Copyright (C) 2003-2011 Xavier Garreau <xavier@xgarreau.org>

	This file is part of ZazouMiniWebServer.

    ZazouMiniWebServer is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    ZazouMiniWebServer is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with ZazouMiniWebServer; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
*/
	
#include "stdafx.h"
#include "ZMWSConfFileParser.h"

BOOL ZMWSConfFileParser::HandleVar(ZMWSConfig& config, ZMWSMIME& mime_manager, std::string& var, std::string& val) {
	BOOL OptIsOK = FALSE;
	// curr_config_context can be Main, a domain name or MimeType
	std::string::size_type first_word_end = var.find(':');
	if (first_word_end == std::string::npos) return FALSE;
	
	std::string var_name;
	std::string extrainfo;
	std::string::size_type var_name_begin;

	if (var.substr(0, first_word_end)=="Main") {
		OptIsOK = TRUE;
		curr_config_context = "Main";
		var_name_begin = first_word_end+1;
	}
	if (var.substr(0, first_word_end)=="Include") {
		OptIsOK = TRUE;
		curr_config_context = "Include";
		var_name_begin = 0;
	}
	if (var.substr(0, first_word_end)=="MimeType") {
		OptIsOK = TRUE;
		curr_config_context = "Mime";
		var_name_begin = first_word_end+1;
	}
	if (var.substr(0, first_word_end)=="Handler") {
		OptIsOK = TRUE;
		curr_config_context = "Handler";
		var_name_begin = first_word_end+1;
	}
	if (var.substr(0, first_word_end)=="TRUECGIHandler") {
		OptIsOK = TRUE;
		curr_config_context = "TRUECGIHandler";
		var_name_begin = first_word_end+1;
	}
	if (var.substr(0, first_word_end)=="ZMWSSAPIHandler") {
		OptIsOK = TRUE;
		curr_config_context = "ZMWSSAPIHandler";
		var_name_begin = first_word_end+1;
	}
	if (var.substr(0, first_word_end)=="VirtualHost") {
		OptIsOK = TRUE;
		std::string::size_type vhost_name_begin = first_word_end+1;
		std::string::size_type vhost_name_end = var.find(':', vhost_name_begin);
		if (vhost_name_end == std::string::npos) return FALSE;

		curr_config_context = "VirtualHost";
		extrainfo = var.substr(vhost_name_begin, vhost_name_end-vhost_name_begin);

		var_name_begin = vhost_name_end+1;
	}
	if (var.substr(0, first_word_end)=="PerUrl") {
		OptIsOK = TRUE;
		std::string::size_type perurl_name_begin = first_word_end+1;
		std::string::size_type perurl_name_end = var.find(':', perurl_name_begin);
		if (perurl_name_end == std::string::npos) return FALSE;

		curr_config_context = "PerUrl";
		extrainfo = var.substr(perurl_name_begin, perurl_name_end-perurl_name_begin);

		var_name_begin = perurl_name_end+1;
	}
	if (!OptIsOK) return FALSE;

	if (!curr_config_context.length()) return FALSE;

	// Recherche du nom de variable
	var_name = var.substr(var_name_begin);
	if (!((curr_config_context == "Mime") || var_name.length())) return FALSE;

	// Config globale
	if (curr_config_context == "Main") {
		OptIsOK = MainApplyVar(config, var_name, val);
	} else 	if (curr_config_context == "Include") {
		ZMWSConfFileParser* cfp = new ZMWSConfFileParser();
		OptIsOK = cfp->Parse(config, mime_manager, val.c_str());
		delete cfp;
	} else 	if (curr_config_context == "Mime") {
		OptIsOK = MimeApplyVar(mime_manager, var_name, val);
	} else 	if (curr_config_context == "Handler") {
		OptIsOK = HandlerApplyVar(config, var_name, val);
	} else 	if (curr_config_context == "TRUECGIHandler") {
		OptIsOK = TRUECGIHandlerApplyVar(config, var_name, val);
	} else 	if (curr_config_context == "ZMWSSAPIHandler") {
		OptIsOK = ZMWSSAPIHandlerApplyVar(config, var_name, val);
	} else 	if (curr_config_context == "VirtualHost") {
		OptIsOK = VHostApplyVar(config, extrainfo, var_name, val);
	} else 	if (curr_config_context == "PerUrl") {
		OptIsOK = PerUrlApplyVar(config, extrainfo, var_name, val);
	}

	return OptIsOK;
}


BOOL ZMWSConfFileParser::GetConfigVar(std::string& var, std::string& val) {
	char c = 0;
	BOOL isVal = FALSE;
	BOOL ignoreSpace = TRUE;
	BOOL escapeOne = FALSE;
	BOOL isComment = FALSE;
	var = val = "";
	while ((conf_file.get(c)) && (c!='\n')) {
		if (isComment) continue;
		if (c=='\t') c=' ';
		if (c=='\r') c=' ';
		if (ignoreSpace && (c==' ')) continue;
		if (c=='\\' && !escapeOne) {
			escapeOne = TRUE;
			continue;
		}
		if (c=='\"' && !escapeOne) {
			ignoreSpace = !ignoreSpace;
			continue;
		}
		if ((c=='=') && !isVal && !escapeOne) {
			isVal = TRUE;
			continue;
		}
		if (c=='#' && !escapeOne) {
			isComment = TRUE;
			continue;
		}
		escapeOne = FALSE;
		if (isVal)
			val += c;
		else
			var += c;
	}

	// Cas de la ligne terminale avec val de longueur vide et \n final manquant
	if (conf_file.eof()) {
		conf_file.close();
		return TRUE;
	}

	return ((c=='\n') || (var.length() && val.length()));
}

BOOL ZMWSConfFileParser::Parse(ZMWSConfig& config, ZMWSMIME& mime_manager, const char* filepath)
{
	BOOL OptsOk = TRUE;
	std::string var, val, msg;	
	std::string fp;

	if (!filepath) {
		fp = config.GetPathToConfigFile();
	} else {
		fp = filepath;
	}

	conf_file.open(fp.c_str());
	if (!conf_file) {
		print_dbg(("No config file found. Tried "+fp+"\n").c_str());
		return TRUE;
	} else {
		print_dbg(("Using config file : "+fp+"\n").c_str());
	}

	while ((conf_file.is_open()) && (GetConfigVar(var, val))) {
		if (var.length()) {
			if (!HandleVar(config, mime_manager, var, val)) {
				print_dbg(("CONFIG SYNTAX ERROR : "+var+":"+val+"\n").c_str());
				OptsOk = FALSE;
			}
		}
	}
	return OptsOk;
}

void ZMWSConfFileParser::PrintError(const std::string& option, const std::string& error_text)
{
	std::string tmpS;
	tmpS  = "\n----------------------------------------\n";
	tmpS += "ERROR        : option ";
	tmpS += option;
	tmpS += "\nERROR MESSAGE: ";
	tmpS += error_text;
	tmpS += "\n----------------------------------------\n";
	print_dbg(tmpS.c_str());
}

void ZMWSConfFileParser::PrintWarning(const std::string& warning_text)
{
	std::string tmpS;
	tmpS  = "\n----------------------------------------\n";
	tmpS += "WARNING: ";
	tmpS += warning_text;
	tmpS += "\n----------------------------------------\n";
	print_dbg(tmpS.c_str());
}

BOOL ZMWSConfFileParser::PerUrlApplyVar(ZMWSConfig& config, const std::string& url, const std::string& var, const std::string& val) {
	BOOL OptIsOk = TRUE;
	ZMWSPerUrlConfig puc;
	config.GetPerUrlConfig(url, puc, TRUE);

	std::string varkey, varvar;
	std::string::size_type varkey_end = var.find(':');
	if (varkey_end == std::string::npos) return FALSE;

	varkey = var.substr(0, varkey_end);
	varvar = var.substr(varkey_end+1);
	print_dbg (("if url like "+url+"%, "+varkey+" "+val+" for "+varvar+"\n").c_str());
	if (varkey == "Handler") {
		ZMWSHandler handler;
		handler.handlerType = handler.DEFAULT;
		handler.handlerPath = val;
		puc.AddHandler(varvar, handler);
		config.SetPerUrlConfig(url, puc);
	}

	return OptIsOk;
}

BOOL ZMWSConfFileParser::VHostApplyVar(ZMWSConfig& config, const std::string& vhost, const std::string& var, const std::string& val) {
	BOOL OptIsOk = TRUE;
	ZMWSVHostConfig vhc;

	if (!config.GetVHostConfig(vhost, vhc)) {
		config.CreateDefaultVHost(vhost);
		if (!config.GetVHostConfig(vhost, vhc)) {
			print_dbg(("ERROR: Could not create default VHost " + vhost + "\n").c_str());
			return FALSE;
		}
	}

/*
* Indexing
*/
	if (var == "noindex") {
		if ((val=="1") || (val=="true"))
			vhc.canIndex = false;
		else
			vhc.canIndex = true;
		config.SetVHostConfig(vhc.domain, vhc);
		return OptIsOk;
	}

/*
* Reverse DNS
*/
	if (var == "norv") {
		if ((val=="1") || (val=="true"))
			vhc.reverseDNS = false;
		else
			vhc.reverseDNS = true;
		config.SetVHostConfig(vhc.domain, vhc);
		return OptIsOk;
	}

	if (var == "revdns") {
		if ((val=="1") || (val=="true"))
			vhc.reverseDNS = true;
		else
			vhc.reverseDNS = false;
		config.SetVHostConfig(vhc.domain, vhc);
		return OptIsOk;
	}

/*
* Web Dir
*/
	if (var == "webdir") {
		if (!config.SetDocumentRoot(val, vhc.domain)) {
			PrintError(var.c_str(), "Bad path !");
			OptIsOk = FALSE;
		}
		return OptIsOk;
	}

/*
* Logs Dir
*/
	if (var == "logsdir") {
		if (!config.SetLogsDir(val, vhc.domain)) {
			PrintError(var.c_str(), "Bad path !");
			OptIsOk = FALSE;
		}
		return OptIsOk;
	}

/*
* Alias
*/
	if (var == "alias") {
		config.AddVHostAlias(val, vhc.domain);
		print_dbg(("New alias for "+vhc.domain+" : "+val+"\n").c_str());
		return OptIsOk;
	}

/*
* Default charset
*/
	if (var == "defcharset") {
		config.SetDefaultCharset(val, vhc.domain);
		return OptIsOk;
	}

/*
* Allowed Client IP
*/
	if (var == "allowfrom") {
		config.SetAllowFrom(val, vhc.domain);
		return OptIsOk;
	}

/*
* Denied Client IP
*/
	if (var == "denyfrom") {
		config.SetDenyFrom(val, vhc.domain);
		return OptIsOk;
	}

/*
* Maximum kB per maxKBytesDTime
*/
	if (var == "maxKBytes") {
		DWORD maxKBytes = strtoul(val.c_str(), NULL, 10);
		vhc.maxKBytes=maxKBytes;
		config.SetVHostConfig(vhc.domain, vhc);
		return OptIsOk;
	}

/*
* Maximum kB per maxKBytesDTime
*/
	if (var == "maxKBytesDTime") {
		DWORD maxKBytesDTime = strtoul(val.c_str(), NULL, 10);
		vhc.maxKBytesDTime=maxKBytesDTime;
		config.SetVHostConfig(vhc.domain, vhc);
		return OptIsOk;
	}

/* complex options */
	std::string varkey, varvar;
	std::string::size_type varkey_end = var.find(':');
	if (varkey_end == std::string::npos)
		return OptIsOk;
	{
		varkey = var.substr(0, varkey_end);
		varvar = var.substr(varkey_end+1);

	/*
	* Directory Aliases
	*/
		if (varkey == "diralias") {
			config.AddDirAlias(varvar, val, vhc.domain);
			return OptIsOk;
		}

	/*
	* Handlers
	*/
		if (varkey == "Handler") {
			ZMWSHandler handler;
			handler.handlerType = handler.DEFAULT;
			handler.handlerPath = val;
			config.AddHandler(varvar, handler, vhc.domain);
		}
	}

	return OptIsOk;
}

BOOL ZMWSConfFileParser::MainApplyVar(ZMWSConfig& config, const std::string& var, const std::string& val) {
	BOOL OptIsOk = TRUE;
/*
 * Hide Console
 */
	if (var == "hide") {
		if ((val=="1") || (val=="true")) {
			config.SetHideConsole(true);
			config.SetBeQuiet(true);
		} else {
			config.SetHideConsole(false);
		}			
		return OptIsOk;
	}

/*
 * Be Quiet
 */
	if (var == "quiet") {
		if ((val=="1") || (val=="true")) {
			config.SetBeQuiet(true);
		} else {
			config.SetBeQuiet(false);
		}    
		return OptIsOk;
	}		

/*
 * Can Stop
 */
	if (var == "stop") {
		if ((val=="1") || (val=="true"))
			config.SetCanStop(true);
		else
			config.SetCanStop(false);
		return OptIsOk;
	}

/*
* Can Close Browser
*/
	if (var == "closebrowser") {
		if ((val=="1") || (val=="true"))
			config.SetCloseBrowser(true);
		else
			config.SetCloseBrowser(false);
		return OptIsOk;
	}

/*
* Browsing
*/
	if (var == "browse") {
		if ((val=="1") || (val=="true"))
			config.SetBrowseNow(true);
		else
			config.SetBrowseNow(false);
		return OptIsOk;
	}

/*
* Browser command
*/
	if (var == "browsercmd") {
		config.SetBrowserCmd(val);
		return OptIsOk;
	}

/*
* Drop Clients (immediate close)
*/
	if (var == "dropclients") {
		if ((val=="1") || (val=="true"))
			config.SetDropClients(true);
		else
			config.SetDropClients(false);
		return OptIsOk;
	}

/*
* Logging
*/
	if (var == "nolog") {
		if ((val=="1") || (val=="true"))
			config.SetWriteLogs(false);
		else
			config.SetWriteLogs(true);
		return OptIsOk;
	}

/*
* Reverse DNS
*/
	if (var == "norv") {
		if ((val=="1") || (val=="true"))
			config.SetReverseDNS(false);
		else
			config.SetReverseDNS(true);
		return OptIsOk;
	}

	if (var == "revdns") {
		if ((val=="1") || (val=="true"))
			config.SetReverseDNS(true);
		else
			config.SetReverseDNS(false);
		return OptIsOk;
	}

/*
* Indexing
*/
	if (var == "noindex") {
		if ((val=="1") || (val=="true"))
			config.SetCanIndex(false);
		else
			config.SetCanIndex(true);
		return OptIsOk;
	}

/*
 * Port
 */
	if (var == "port") {
		if (val=="httpsonly" || val=="0") {
			config.SetPort(0);
		} else {
			DWORD port = strtoul(val.c_str(), NULL, 10);
			if (port) {
				config.SetPort(port);
			} else {
				PrintError(var.c_str(), "port MUST be a valid port (a number) !\n");
				OptIsOk = FALSE;
			}
		}
		return OptIsOk;
	}

/*
 * SPort
 */
	if (var == "sport") {
		if (val=="httponly" || val=="0") {
			config.SetHTTPSPort(0);
		} else {
			DWORD port = strtoul(val.c_str(), NULL, 10);
			if (port) {
				config.SetHTTPSPort(port);
			} else {
				PrintError(var.c_str(), "sport MUST be a valid port (a number) !\n");
				OptIsOk = FALSE;
			}
		}
		return OptIsOk;
	}

/*
 * Don't try other ports
 */
	if (var == "unique-port") {
		if ((val=="1") || (val=="true"))
			config.SetTry808xPorts(false);
		else
			config.SetTry808xPorts(true);
		return OptIsOk;
	}

/*
* Max connections
*/
	if (var == "mc") {
		DWORD mc = strtoul(val.c_str(), NULL, 10);
		if (mc) {
			config.SetMaxClients(mc);
		} else {
			PrintError(var.c_str(), "maxConnections MUST be a number !\n");
			OptIsOk = FALSE;
		}
		return OptIsOk;
	}


/*
* Default charset
*/
	if (var == "defcharset") {
		config.SetDefaultCharset(val);
		return OptIsOk;
	}

/*
* IP to bind to
*/
	if (var == "bind") {
		config.SetBindAddr(val);
		return OptIsOk;
	}

/*
* PHP Path
*/
	if (var == "phppath") {
		if (!config.SetPathToPHP(val)) {
			PrintError(var.c_str(), "Bad path to php executable !\n");
			OptIsOk = FALSE;
		}
		return OptIsOk;
	}

/*
* Web Dir
*/
	if (var == "webdir") {
		if (!config.SetDocumentRoot(val)) {
			PrintError(var.c_str(), "Bad path !\n");
			OptIsOk = FALSE;
		}
		return OptIsOk;
	}

/*
* Logs Dir
*/
	if (var == "logsdir") {
		if (!config.SetLogsDir(val)) {
			PrintError(var.c_str(), "Bad path !\n");
			OptIsOk = FALSE;
		}
		return OptIsOk;
	}

/*
* Directory Index Files
* Start Page
*/
	if (var == "startpage") {
		config.SetStartPages(val);
		return OptIsOk;
	}

/*
* Allowed Client IP
*/
	if (var == "allowfrom") {
		config.SetAllowFrom(val);
		return OptIsOk;
	}

/*
* Denied Client IP
*/
	if (var == "denyfrom") {
		config.SetDenyFrom(val);
		return OptIsOk;
	}

/*
* Server Token
*/
	if (var == "pbtoken") {
		if (val == "noversion"){
			config.SetPBToken(PBTOKEN_NOVERSION);
		} else if (val == "off") {
			config.SetPBToken(PBTOKEN_OFF);
		} else {
			config.SetPBToken(PBTOKEN_ALL);
		}
		return OptIsOk;
	}

/* complex options */
	std::string varkey, varvar;
	std::string::size_type varkey_end = var.find(':');
	if (varkey_end != std::string::npos) {
		varkey = var.substr(0, varkey_end);
		varvar = var.substr(varkey_end+1);

	/*
	* Directory Aliases
	*/
		if (varkey == "diralias") {
			config.AddDirAlias(varvar, val);
			return OptIsOk;
		}
	}

/*
* Unknown option
*/
	PrintError(var, "Unknown option. Try fetching the Docs :-)");
	OptIsOk = FALSE;

	return OptIsOk;
}

BOOL ZMWSConfFileParser::MimeApplyVar(ZMWSMIME& mime_manager, const std::string& var, const std::string& val) {
    if (var=="ZMWSMIMEDEFAULT")
        mime_manager.setDefaultMimeType(val);
    else
    	mime_manager.addMimeType(var, val);
	print_dbg(("Added MIME Type "+val+" for ."+var+" extension.\n").c_str());
	return TRUE;
}

BOOL ZMWSConfFileParser::HandlerApplyVar(ZMWSConfig& config, const std::string& var, const std::string& val) {
    ZMWSHandler handler;    
    handler.handlerType = handler.DEFAULT;
    handler.handlerPath += val;
 	config.AddHandler(var, handler);
	print_dbg(("Added Handler "+val+" for ."+var+" extension.\n").c_str());
	return TRUE;
}

BOOL ZMWSConfFileParser::TRUECGIHandlerApplyVar(ZMWSConfig& config, const std::string& var, const std::string& val) {
    ZMWSHandler handler;
    handler.handlerType = handler.TRUECGI;
    handler.handlerPath = val;
	config.AddHandler(var, handler);
	print_dbg(("Added True CGI Handler "+val+" for ."+var+" extension.\n").c_str());
	return TRUE;
}

BOOL ZMWSConfFileParser::ZMWSSAPIHandlerApplyVar(ZMWSConfig& config, const std::string& var, const std::string& val) {
    ZMWSHandler handler;
    handler.handlerType = handler.ZMWSSAPI;
    handler.handlerPath = val;
	config.AddHandler(var, handler);
	print_dbg(("Added ZMWS SAPI Handler "+val+" for ."+var+" extension.\n").c_str());
	return TRUE;
}
