zaela-Server/world/eqw_http_handler.cpp

336 lines
8.6 KiB
C++
Raw Permalink Normal View History

2013-05-09 10:44:08 -04:00
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2006 EQEMu Development Team (http://eqemulator.net)
2013-02-16 16:14:39 -08:00
2013-05-09 10:44:08 -04:00
This program 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; version 2 of the License.
2013-05-06 13:07:41 -04:00
2013-05-09 10:44:08 -04:00
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
2013-02-16 16:14:39 -08:00
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
2013-05-09 10:44:08 -04:00
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
2013-05-06 13:07:41 -04:00
2013-05-09 10:44:08 -04:00
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2013-02-16 16:14:39 -08:00
*/
#include "../common/global_define.h"
2014-08-21 23:30:09 -07:00
#include "eqw_http_handler.h"
2013-02-16 16:14:39 -08:00
#include "../common/SocketLib/Base64.h"
2014-08-21 23:30:09 -07:00
#include "eqw_parser.h"
#include "eqw.h"
#include "http_request.h"
2015-01-18 04:03:45 -06:00
2013-02-16 16:14:39 -08:00
#include "worlddb.h"
#include "console.h"
Mime EQWHTTPHandler::s_mime;
#ifdef EMBPERL
2013-05-04 18:06:58 -07:00
EQWParser *EQWHTTPHandler::s_parser = nullptr;
2013-02-16 16:14:39 -08:00
#endif
const int EQWHTTPHandler::READ_BUFFER_LEN = 1024; //for page IO, was a static const member, but VC6 got mad.
EQWHTTPHandler::EQWHTTPHandler(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort)
2013-05-09 10:44:08 -04:00
: HttpdSocket(ID,in_socket,irIP,irPort),
m_closeOnFinish(false)
2013-02-16 16:14:39 -08:00
{
}
EQWHTTPHandler::~EQWHTTPHandler() {
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
}
#ifdef EMBPERL
EQWParser *EQWHTTPHandler::GetParser() {
2013-05-04 18:06:58 -07:00
if(s_parser == nullptr) {
2013-02-16 16:14:39 -08:00
EQW::Singleton()->ClearOutput();
s_parser = new EQWParser();
const std::string &res = EQW::Singleton()->GetOutput();
2013-02-16 16:14:39 -08:00
if(!res.empty()) {
printf("EQWParser Init output:\n%s\n\n", res.c_str());
EQW::Singleton()->ClearOutput();
}
}
return(s_parser);
}
#endif
/*void EQWHTTPHandler::OnWrite() {
HttpdSocket::OnWrite();
if(m_closeOnFinish && GetOutputLength() == 0) {
// printf("CLOSING\n");
Close();
}
}*/
void EQWHTTPHandler::Exec() {
m_sentHeaders = false;
m_responseCode = "200";
// printf("Request: %s, %s, %s, %s.\n", GetMethod().c_str(), GetUrl().c_str(), GetUri().c_str(), GetQueryString().c_str());
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
SetHttpVersion("HTTP/1.0");
AddResponseHeader("Connection", "close");
2013-05-06 13:07:41 -04:00
if(GetUri().find("..") != std::string::npos) {
2013-02-16 16:14:39 -08:00
SendResponse("403", "Forbidden");
printf("%s is forbidden.\n", GetUri().c_str());
return;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if(!CheckAuth()) {
AddResponseHeader("Content-type", "text/plain");
AddResponseHeader("WWW-Authenticate", "Basic realm=\"EQEmulator\"");
SendResponse("401", "Authorization Required");
SendString("Gotta Authenticate.");
} else {
std::string::size_type start = GetUri().find_first_not_of('/');
std::string page;
if(start != std::string::npos)
2013-02-16 16:14:39 -08:00
page = GetUri().substr(start);
else
page = "index.html";
SendPage(page);
}
/* if (!Detach()) {
printf("Unable to detach...\n");
}
if(GetOutputLength() > 0) {
//we cannot close yet
m_closeOnFinish = true;
} else {
Close();
}*/
Free(); //the "app" side (us) is done with this connection too...
Disconnect();
}
void EQWHTTPHandler::OnHeader(const std::string& key,const std::string& value) {
HttpdSocket::OnHeader(key, value);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if (!strcasecmp(key.c_str(),"Authorization")) {
if(strncasecmp(value.c_str(), "Basic ", 6)) {
printf("Invalid auth type. Expected Basic: %s\n", value.c_str());
return;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
std::string dec;
Base64::decode(value.c_str() + 6, dec);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
std::string::size_type cpos;
cpos = dec.find_first_of(':');
if(cpos == std::string::npos) {
2013-02-16 16:14:39 -08:00
printf("Invalid auth string: %s\n", dec.c_str());
return;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
m_username = dec.substr(0, cpos);
m_password = dec.substr(cpos+1);
}
}
//we should prolly cache login info here... if we load a fresh page, we could be checking
//their auth dozens of times rather quickly...
bool EQWHTTPHandler::CheckAuth() const {
if(m_username.length() < 1)
return(false);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
int16 status = 0;
uint32 acctid = database.CheckLogin(m_username.c_str(), m_password.c_str(), &status);
if(acctid == 0) {
LogInfo("Login autentication failed for [{}] with [{}]", m_username.c_str(), m_password.c_str());
2013-02-16 16:14:39 -08:00
return(false);
}
if(status < httpLoginStatus) {
LogInfo("Login of [{}] failed: status too low", m_username.c_str());
2013-02-16 16:14:39 -08:00
return(false);
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
return(true);
}
void EQWHTTPHandler::SendPage(const std::string &file) {
2013-05-06 13:07:41 -04:00
std::string path = "templates/";
2013-02-16 16:14:39 -08:00
path += file;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
FILE *f = fopen(path.c_str(), "rb");
2013-05-04 18:06:58 -07:00
if(f == nullptr) {
2013-02-16 16:14:39 -08:00
SendResponse("404", "Not Found");
SendString("Not found.");
printf("%s not found.\n", file.c_str());
return;
}
2013-05-06 13:07:41 -04:00
std::string type = s_mime.GetMimeFromFilename(file);
2013-02-16 16:14:39 -08:00
AddResponseHeader("Content-type", type);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
bool process = false;
#ifdef EMBPERL
if(type == "text/html")
process = true;
else {
//not processing, send headers right away
#endif
SendResponse("200", "OK");
#ifdef EMBPERL
}
#endif
auto buffer = new char[READ_BUFFER_LEN + 1];
2013-02-16 16:14:39 -08:00
size_t len;
std::string to_process;
2013-02-16 16:14:39 -08:00
while((len = fread(buffer, 1, READ_BUFFER_LEN, f)) > 0) {
buffer[len] = '\0';
if(process)
to_process += buffer;
else
SendBuf(buffer, len);
}
delete[] buffer;
2013-05-09 10:44:08 -04:00
fclose(f);
2013-02-16 16:14:39 -08:00
#ifdef EMBPERL
if(process) {
//convert the base form into a useful perl exportable form
HTTPRequest req(this, GetHttpForm());
GetParser()->SetHTTPRequest("testing", &req);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
//parse out the page and potentially pass some stuff on to perl.
ProcessAndSend(to_process);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
//clear out the form, just in case (since it gets destroyed next)
2013-05-04 18:06:58 -07:00
GetParser()->SetHTTPRequest("testing", nullptr);
2013-02-16 16:14:39 -08:00
}
#endif
}
bool EQWHTTPHandler::LoadMimeTypes(const char *filename) {
return(s_mime.LoadMimeFile(filename));
}
#ifdef EMBPERL
void EQWHTTPHandler::ProcessAndSend(const std::string &str) {
std::string::size_type len = str.length();
std::string::size_type start = 0;
std::string::size_type pos, end;
2013-05-06 13:07:41 -04:00
while((pos = str.find("<?", start)) != std::string::npos) {
2013-02-16 16:14:39 -08:00
//send all the crap leading up to the script block
if(pos != start) {
ProcessText(str.c_str() + start, pos-start);
}
//look for the end of this script block...
end = str.find("?>", pos+2);
if(end == std::string::npos) {
2013-02-16 16:14:39 -08:00
//terminal ?> not found... should issue a warning or something...
std::string scriptBody = str.substr(pos+2);
2013-02-16 16:14:39 -08:00
ProcessScript(scriptBody);
start = len;
break;
} else {
//script only consumes some of this buffer...
std::string scriptBody = str.substr(pos+2, end-pos-2);
2013-02-16 16:14:39 -08:00
ProcessScript(scriptBody);
start = end + 2;
}
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
//send whatever is left over
if(start != len)
ProcessText(str.c_str() + start, len-start);
}
void EQWHTTPHandler::ProcessScript(const std::string &script_body) {
const char *script = script_body.c_str();
if(strcmp("perl", script) == 0)
script += 4; //allow <?perl
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
// printf("Script: ''''%s''''\n\n", script_body.c_str());
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
GetParser()->EQW_eval("testing", script_body.c_str());
const std::string &res = EQW::Singleton()->GetOutput();
2013-02-16 16:14:39 -08:00
if(!res.empty()) {
ProcessText(res.c_str(), res.length());
EQW::Singleton()->ClearOutput();
}
}
void EQWHTTPHandler::ProcessText(const char *txt, int len) {
if(!m_sentHeaders) {
SendResponse(m_responseCode, "OK");
m_sentHeaders = true;
}
SendBuf(txt, len);
}
#endif
EQWHTTPServer::EQWHTTPServer()
: m_port(0)
{
}
void EQWHTTPServer::CreateNewConnection(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort) {
auto conn = new EQWHTTPHandler(ID, in_socket, irIP, irPort);
2013-02-16 16:14:39 -08:00
AddConnection(conn);
}
void EQWHTTPServer::Stop() {
LogInfo("Requesting that HTTP Service stop");
2013-02-16 16:14:39 -08:00
m_running = false;
Close();
}
bool EQWHTTPServer::Start(uint16 port, const char *mime_file) {
if(m_running) {
LogInfo("HTTP Service is already running on port [{}]", m_port);
2013-02-16 16:14:39 -08:00
return(false);
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
//load up our nice mime types
if(!EQWHTTPHandler::LoadMimeTypes(mime_file)) {
LogInfo("Failed to load mime types from [{}]", mime_file);
2013-02-16 16:14:39 -08:00
return(false);
} else {
LogInfo("Loaded mime types from [{}]", mime_file);
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
//fire up the server thread
char errbuf[TCPServer_ErrorBufferSize];
if(!Open(port, errbuf)) {
LogInfo("Unable to bind to port [{}] for HTTP service: [{}]", port, errbuf);
2013-02-16 16:14:39 -08:00
return(false);
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
m_running = true;
m_port = port;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
/*
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
#ifdef _WINDOWS
_beginthread(ThreadProc, 0, this);
#else
2013-05-04 18:06:58 -07:00
pthread_create(&m_thread, nullptr, ThreadProc, this);
2013-02-16 16:14:39 -08:00
#endif*/
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
return(true);
}
/*
void EQWHTTPServer::Run() {
Log.LogDebugType(Logs::Detail, Logs::World_Server, "HTTP Processing thread started on port %d", m_port);
2013-02-16 16:14:39 -08:00
do {
#warning DELETE THIS IF YOU DONT USE IT
Sleep(10);
} while(m_running);
Log.LogDebugType(Logs::Detail, Logs::World_Server, "HTTP Processing thread terminating on port %d", m_port);
2013-02-16 16:14:39 -08:00
}
ThreadReturnType EQWHTTPServer::ThreadProc(void *data) {
((EQWHTTPServer *) data)->Run();
2013-05-04 18:06:58 -07:00
THREAD_RETURN(nullptr);
2013-02-16 16:14:39 -08:00
}*/