summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--source/Bindings.cpp46
-rw-r--r--source/Bindings.h2
-rw-r--r--source/HTTPServer/HTTPFormParser.cpp5
-rw-r--r--source/HTTPServer/HTTPMessage.cpp17
-rw-r--r--source/HTTPServer/HTTPMessage.h3
-rw-r--r--source/HTTPServer/HTTPServer.cpp15
-rw-r--r--source/HTTPServer/HTTPServer.h3
-rw-r--r--source/Root.cpp15
-rw-r--r--source/WebAdmin.cpp493
-rw-r--r--source/WebAdmin.h116
10 files changed, 414 insertions, 301 deletions
diff --git a/source/Bindings.cpp b/source/Bindings.cpp
index 24289865a..7895fe16b 100644
--- a/source/Bindings.cpp
+++ b/source/Bindings.cpp
@@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
-** Generated automatically by tolua++-1.0.92 on 09/21/13 17:37:22.
+** Generated automatically by tolua++-1.0.92 on 10/05/13 18:34:12.
*/
#ifndef __cplusplus
@@ -240,18 +240,19 @@ static void tolua_reg_types (lua_State* tolua_S)
tolua_usertype(tolua_S,"cCraftingRecipe");
tolua_usertype(tolua_S,"cPlugin");
tolua_usertype(tolua_S,"cItemGrid");
- tolua_usertype(tolua_S,"cBlockArea");
+ tolua_usertype(tolua_S,"cHTTPServer::cCallbacks");
tolua_usertype(tolua_S,"cLuaWindow");
tolua_usertype(tolua_S,"cInventory");
tolua_usertype(tolua_S,"cBoundingBox");
tolua_usertype(tolua_S,"cBlockEntityWithItems");
- tolua_usertype(tolua_S,"HTTPFormData");
tolua_usertype(tolua_S,"cTracer");
+ tolua_usertype(tolua_S,"HTTPFormData");
+ tolua_usertype(tolua_S,"cWindow");
tolua_usertype(tolua_S,"cArrowEntity");
tolua_usertype(tolua_S,"cDropSpenserEntity");
- tolua_usertype(tolua_S,"cWindow");
- tolua_usertype(tolua_S,"Vector3i");
+ tolua_usertype(tolua_S,"cBlockArea");
tolua_usertype(tolua_S,"cCraftingGrid");
+ tolua_usertype(tolua_S,"Vector3i");
tolua_usertype(tolua_S,"cGroup");
tolua_usertype(tolua_S,"cStringMap");
tolua_usertype(tolua_S,"cBlockEntity");
@@ -18666,38 +18667,6 @@ static int tolua_AllToLua_cWebAdmin_GetMemoryUsage00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* method: GetPort of class cWebAdmin */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetPort00
-static int tolua_AllToLua_cWebAdmin_GetPort00(lua_State* tolua_S)
-{
-#ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,2,&tolua_err)
- )
- goto tolua_lerror;
- else
-#endif
- {
- cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0);
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPort'", NULL);
-#endif
- {
- int tolua_ret = (int) self->GetPort();
- tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
- }
- }
- return 1;
-#ifndef TOLUA_RELEASE
- tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'GetPort'.",&tolua_err);
- return 0;
-#endif
-}
-#endif //#ifndef TOLUA_DISABLE
-
/* method: GetPage of class cWebAdmin */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetPage00
static int tolua_AllToLua_cWebAdmin_GetPage00(lua_State* tolua_S)
@@ -30233,10 +30202,9 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_variable(tolua_S,"PluginName",tolua_get_sWebAdminPage_PluginName,tolua_set_sWebAdminPage_PluginName);
tolua_variable(tolua_S,"TabName",tolua_get_sWebAdminPage_TabName,tolua_set_sWebAdminPage_TabName);
tolua_endmodule(tolua_S);
- tolua_cclass(tolua_S,"cWebAdmin","cWebAdmin","",NULL);
+ tolua_cclass(tolua_S,"cWebAdmin","cWebAdmin","cHTTPServer::cCallbacks",NULL);
tolua_beginmodule(tolua_S,"cWebAdmin");
tolua_function(tolua_S,"GetMemoryUsage",tolua_AllToLua_cWebAdmin_GetMemoryUsage00);
- tolua_function(tolua_S,"GetPort",tolua_AllToLua_cWebAdmin_GetPort00);
tolua_function(tolua_S,"GetPage",tolua_AllToLua_cWebAdmin_GetPage00);
tolua_function(tolua_S,"GetBaseURL",tolua_AllToLua_cWebAdmin_GetBaseURL00);
tolua_endmodule(tolua_S);
diff --git a/source/Bindings.h b/source/Bindings.h
index 0fa3665a3..933cd2ffd 100644
--- a/source/Bindings.h
+++ b/source/Bindings.h
@@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
-** Generated automatically by tolua++-1.0.92 on 09/21/13 17:37:22.
+** Generated automatically by tolua++-1.0.92 on 10/05/13 18:34:12.
*/
/* Exported function */
diff --git a/source/HTTPServer/HTTPFormParser.cpp b/source/HTTPServer/HTTPFormParser.cpp
index 85a789f7d..7db7b4e6d 100644
--- a/source/HTTPServer/HTTPFormParser.cpp
+++ b/source/HTTPServer/HTTPFormParser.cpp
@@ -32,7 +32,7 @@ cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callba
}
if ((a_Request.GetMethod() == "POST") || (a_Request.GetMethod() == "PUT"))
{
- if (a_Request.GetContentType() == "application/x-www-form-urlencoded")
+ if (strncmp(a_Request.GetContentType().c_str(), "application/x-www-form-urlencoded", 33) == 0)
{
m_Kind = fpkFormUrlEncoded;
return;
@@ -44,7 +44,8 @@ cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callba
return;
}
}
- ASSERT(!"Unhandled request method");
+ // Invalid method / content type combination, this is not a HTTP form
+ m_IsValid = false;
}
diff --git a/source/HTTPServer/HTTPMessage.cpp b/source/HTTPServer/HTTPMessage.cpp
index 6cf9464a3..ab23866e6 100644
--- a/source/HTTPServer/HTTPMessage.cpp
+++ b/source/HTTPServer/HTTPMessage.cpp
@@ -120,6 +120,23 @@ int cHTTPRequest::ParseHeaders(const char * a_Data, int a_Size)
+AString cHTTPRequest::GetBareURL(void) const
+{
+ size_t idxQM = m_URL.find('?');
+ if (idxQM != AString::npos)
+ {
+ return m_URL.substr(0, idxQM);
+ }
+ else
+ {
+ return m_URL;
+ }
+}
+
+
+
+
+
int cHTTPRequest::ParseRequestLine(const char * a_Data, int a_Size)
{
m_IncomingHeaderData.append(a_Data, a_Size);
diff --git a/source/HTTPServer/HTTPMessage.h b/source/HTTPServer/HTTPMessage.h
index 151eb468f..f5284c535 100644
--- a/source/HTTPServer/HTTPMessage.h
+++ b/source/HTTPServer/HTTPMessage.h
@@ -82,6 +82,9 @@ public:
/// Returns the URL used in the request
const AString & GetURL(void) const { return m_URL; }
+ /// Returns the URL used in the request, without any parameters
+ AString GetBareURL(void) const;
+
/// Sets the UserData pointer that is stored within this request. The request doesn't touch this data (doesn't delete it)!
void SetUserData(void * a_UserData) { m_UserData = a_UserData; }
diff --git a/source/HTTPServer/HTTPServer.cpp b/source/HTTPServer/HTTPServer.cpp
index 43bb751c4..7e216c629 100644
--- a/source/HTTPServer/HTTPServer.cpp
+++ b/source/HTTPServer/HTTPServer.cpp
@@ -127,25 +127,16 @@ cHTTPServer::cHTTPServer(void) :
-bool cHTTPServer::Initialize(cIniFile & a_IniFile)
+bool cHTTPServer::Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6)
{
- if (!a_IniFile.GetValueSetB("WebAdmin", "Enabled", false))
- {
- // The WebAdmin is disabled
- return true;
- }
bool HasAnyPort;
- HasAnyPort = m_ListenThreadIPv4.Initialize(a_IniFile.GetValueSet("WebAdmin", "Port", "8081"));
- HasAnyPort = m_ListenThreadIPv6.Initialize(a_IniFile.GetValueSet("WebAdmin", "PortsIPv6", "8082, 3300")) || HasAnyPort;
+ HasAnyPort = m_ListenThreadIPv4.Initialize(a_PortsIPv4);
+ HasAnyPort = m_ListenThreadIPv6.Initialize(a_PortsIPv6) || HasAnyPort;
if (!HasAnyPort)
{
- LOG("WebAdmin is disabled");
return false;
}
- // DEBUG:
- return Start(g_DebugCallbacks);
-
return true;
}
diff --git a/source/HTTPServer/HTTPServer.h b/source/HTTPServer/HTTPServer.h
index efe60809d..2ecb75fdd 100644
--- a/source/HTTPServer/HTTPServer.h
+++ b/source/HTTPServer/HTTPServer.h
@@ -51,7 +51,8 @@ public:
cHTTPServer(void);
- bool Initialize(cIniFile & a_IniFile);
+ /// Initializes the server on the specified ports
+ bool Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6);
/// Starts the server and assigns the callbacks to use for incoming requests
bool Start(cCallbacks & a_Callbacks);
diff --git a/source/Root.cpp b/source/Root.cpp
index 821dd0928..57e5dfcc4 100644
--- a/source/Root.cpp
+++ b/source/Root.cpp
@@ -130,15 +130,9 @@ void cRoot::Start(void)
}
IniFile.WriteFile();
- cIniFile WebIniFile("webadmin.ini");
- if (!WebIniFile.ReadFile())
- {
- LOGWARNING("webadmin.ini inaccessible, wabadmin is disabled");
- }
- else
- {
- m_HTTPServer.Initialize(WebIniFile);
- }
+ LOG("Initialising WebAdmin...");
+ m_WebAdmin = new cWebAdmin();
+ m_WebAdmin->Init();
LOG("Loading settings...");
m_GroupManager = new cGroupManager();
@@ -167,6 +161,9 @@ void cRoot::Start(void)
LOG("Starting server...");
m_Server->Start();
+
+ LOG("Starting WebAdmin...");
+ m_WebAdmin->Start();
#if !defined(ANDROID_NDK)
LOG("Starting InputThread...");
diff --git a/source/WebAdmin.cpp b/source/WebAdmin.cpp
index e53fb84e6..44ba38c8d 100644
--- a/source/WebAdmin.cpp
+++ b/source/WebAdmin.cpp
@@ -14,7 +14,8 @@
#include "Server.h"
#include "Root.h"
-#include "../iniFile/iniFile.h"
+#include "HTTPServer/HTTPMessage.h"
+#include "HTTPServer/HTTPConnection.h"
#ifdef _WIN32
#include <psapi.h>
@@ -55,14 +56,13 @@ cWebAdmin * WebAdmin = NULL;
-cWebAdmin::cWebAdmin( int a_Port /* = 8080 */ ) :
- m_Port(a_Port),
- m_bConnected(false),
- m_TemplateScript("<webadmin_template>")
+cWebAdmin::cWebAdmin(void) :
+ m_IsInitialized(false),
+ m_TemplateScript("<webadmin_template>"),
+ m_IniFile("webadmin.ini")
{
WebAdmin = this;
m_Event = new cEvent();
- Init( m_Port );
}
@@ -73,10 +73,6 @@ cWebAdmin::~cWebAdmin()
{
WebAdmin = 0;
- m_WebServer->Stop();
-
- delete m_WebServer;
- delete m_IniFile;
m_Event->Wait();
delete m_Event;
@@ -105,189 +101,36 @@ void cWebAdmin::RemovePlugin( cWebPlugin * a_Plugin )
-void cWebAdmin::Request_Handler(webserver::http_request* r)
+bool cWebAdmin::Init(void)
{
- if( WebAdmin == 0 ) return;
- LOG("Path: %s", r->path_.c_str() );
-
- if (r->path_ == "/")
- {
- r->answer_ += "<h1>MCServer WebAdmin</h1>";
- r->answer_ += "<center>";
- r->answer_ += "<form method='get' action='webadmin/'>";
- r->answer_ += "<input type='submit' value='Log in'>";
- r->answer_ += "</form>";
- r->answer_ += "</center>";
- return;
- }
-
- if (r->path_.empty() || r->path_[0] != '/')
+ if (!m_IniFile.ReadFile())
{
- r->answer_ += "<h1>Bad request</h1>";
- r->answer_ += "<p>";
- r->answer_ = r->path_; // TODO: Shouldn't we sanitize this? Possible security issue.
- r->answer_ += "</p>";
- return;
- }
-
- AStringVector Split = StringSplit(r->path_.substr(1), "/");
-
- if (Split.empty() || (Split[0] != "webadmin" && Split[0] != "~webadmin"))
- {
- r->answer_ += "<h1>Bad request</h1>";
- return;
+ return false;
}
- if (!r->authentication_given_)
- {
- r->answer_ += "no auth";
- r->auth_realm_ = "MCServer WebAdmin";
- }
-
- bool bDontShowTemplate = false;
- if (Split[0] == "~webadmin")
- {
- bDontShowTemplate = true;
- }
+ AString PortsIPv4 = m_IniFile.GetValue("WebAdmin", "Port", "8080");
+ AString PortsIPv6 = m_IniFile.GetValue("WebAdmin", "PortsIPv6", "");
- AString UserPassword = WebAdmin->m_IniFile->GetValue( "User:"+r->username_, "Password", "");
- if ((UserPassword != "") && (r->password_ == UserPassword))
- {
- AString Template;
-
- HTTPTemplateRequest TemplateRequest;
- TemplateRequest.Request.Username = r->username_;
- TemplateRequest.Request.Method = r->method_;
- TemplateRequest.Request.Params = r->params_;
- TemplateRequest.Request.PostParams = r->params_post_;
- TemplateRequest.Request.Path = r->path_.substr(1);
-
- for( unsigned int i = 0; i < r->multipart_formdata_.size(); ++i )
- {
- webserver::formdata& fd = r->multipart_formdata_[i];
-
- HTTPFormData HTTPfd;//( fd.value_ );
- HTTPfd.Value = fd.value_;
- HTTPfd.Type = fd.content_type_;
- HTTPfd.Name = fd.name_;
- LOGINFO("Form data name: %s", fd.name_.c_str() );
- TemplateRequest.Request.FormData[ fd.name_ ] = HTTPfd;
- }
-
- // Try to get the template from the Lua template script
- bool bLuaTemplateSuccessful = false;
- if (!bDontShowTemplate)
- {
- bLuaTemplateSuccessful = WebAdmin->m_TemplateScript.Call("ShowPage", WebAdmin, &TemplateRequest, cLuaState::Return, Template);
- }
-
- if (!bLuaTemplateSuccessful)
- {
- AString BaseURL = WebAdmin->GetBaseURL(Split);
- AString Menu;
- Template = bDontShowTemplate ? "{CONTENT}" : WebAdmin->GetTemplate();
- AString FoundPlugin;
-
- for (PluginList::iterator itr = WebAdmin->m_Plugins.begin(); itr != WebAdmin->m_Plugins.end(); ++itr)
- {
- cWebPlugin* WebPlugin = *itr;
- std::list< std::pair<AString, AString> > NameList = WebPlugin->GetTabNames();
- for( std::list< std::pair<AString, AString> >::iterator Names = NameList.begin(); Names != NameList.end(); ++Names )
- {
- Menu += "<li><a href='" + BaseURL + WebPlugin->GetWebTitle().c_str() + "/" + (*Names).second + "'>" + (*Names).first + "</a></li>";
- }
- }
-
- sWebAdminPage Page = WebAdmin->GetPage(TemplateRequest.Request);
- AString Content = Page.Content;
- FoundPlugin = Page.PluginName;
- if (!Page.TabName.empty())
- FoundPlugin += " - " + Page.TabName;
-
- if( FoundPlugin.empty() ) // Default page
- {
- Content.clear();
- FoundPlugin = "Current Game";
- Content += "<h4>Server Name:</h4>";
- Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID() ) + "</p>";
-
- Content += "<h4>Plugins:</h4><ul>";
- cPluginManager* PM = cRoot::Get()->GetPluginManager();
- if( PM )
- {
- const cPluginManager::PluginMap & List = PM->GetAllPlugins();
- for( cPluginManager::PluginMap::const_iterator itr = List.begin(); itr != List.end(); ++itr )
- {
- if( itr->second == NULL ) continue;
- AString VersionNum;
- AppendPrintf(Content, "<li>%s V.%i</li>", itr->second->GetName().c_str(), itr->second->GetVersion());
- }
- }
- Content += "</ul>";
- Content += "<h4>Players:</h4><ul>";
-
- cPlayerAccum PlayerAccum;
- cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players
- if( World != NULL )
- {
- World->ForEachPlayer(PlayerAccum);
- Content.append(PlayerAccum.m_Contents);
- }
- Content += "</ul><br>";
- }
-
-
-
- if (!bDontShowTemplate && (Split.size() > 1))
- {
- Content += "\n<p><a href='" + BaseURL + "'>Go back</a></p>";
- }
-
- int MemUsageKiB = GetMemoryUsage();
- if (MemUsageKiB > 0)
- {
- ReplaceString(Template, "{MEM}", Printf("%.02f", (double)MemUsageKiB / 1024));
- ReplaceString(Template, "{MEMKIB}", Printf("%d", MemUsageKiB));
- }
- else
- {
- ReplaceString(Template, "{MEM}", "unknown");
- ReplaceString(Template, "{MEMKIB}", "unknown");
- }
- ReplaceString(Template, "{USERNAME}", r->username_);
- ReplaceString(Template, "{MENU}", Menu);
- ReplaceString(Template, "{PLUGIN_NAME}", FoundPlugin);
- ReplaceString(Template, "{CONTENT}", Content);
- ReplaceString(Template, "{TITLE}", "MCServer");
-
- AString NumChunks;
- Printf(NumChunks, "%d", cRoot::Get()->GetTotalChunkCount());
- ReplaceString(Template, "{NUMCHUNKS}", NumChunks);
- }
-
- r->answer_ = Template;
- }
- else
+ if (!m_HTTPServer.Initialize(PortsIPv4, PortsIPv6))
{
- r->answer_ += "Wrong username/password";
- r->auth_realm_ = "MCServer WebAdmin";
+ return false;
}
+ m_IsInitialized = true;
+ return true;
}
-bool cWebAdmin::Init(int a_Port)
+bool cWebAdmin::Start(void)
{
- m_Port = a_Port;
-
- m_IniFile = new cIniFile("webadmin.ini");
- if (m_IniFile->ReadFile())
+ if (!m_IsInitialized)
{
- m_Port = m_IniFile->GetValueI("WebAdmin", "Port", 8080);
+ // Not initialized
+ return false;
}
-
+
// Initialize the WebAdmin template script and load the file
m_TemplateScript.Create();
if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua"))
@@ -296,75 +139,185 @@ bool cWebAdmin::Init(int a_Port)
m_TemplateScript.Close();
}
-
- LOG("Starting WebAdmin on port %i", m_Port);
-
-#ifdef _WIN32
- HANDLE hThread = CreateThread(
- NULL, // default security
- 0, // default stack size
- ListenThread, // name of the thread function
- this, // thread parameters
- 0, // default startup flags
- NULL);
- CloseHandle( hThread ); // Just close the handle immediately
-#else
- pthread_t LstnThread;
- pthread_create( &LstnThread, 0, ListenThread, this);
-#endif
-
- return true;
+ return m_HTTPServer.Start(*this);
}
-#ifdef _WIN32
-DWORD WINAPI cWebAdmin::ListenThread(LPVOID lpParam)
-#else
-void *cWebAdmin::ListenThread( void *lpParam )
-#endif
+AString cWebAdmin::GetTemplate()
{
- cWebAdmin* self = (cWebAdmin*)lpParam;
+ AString retVal = "";
- self->m_WebServer = new webserver(self->m_Port, Request_Handler );
- if (!self->m_WebServer->Begin())
+ char SourceFile[] = "webadmin/template.html";
+
+ cFile f;
+ if (!f.Open(SourceFile, cFile::fmRead))
{
- LOGWARN("WebServer failed to start! WebAdmin is disabled");
+ return "";
}
- self->m_Event->Set();
- return 0;
+ // copy the file into the buffer:
+ f.ReadRestOfFile(retVal);
+
+ return retVal;
}
-AString cWebAdmin::GetTemplate()
+void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
{
- AString retVal = "";
+ if (!a_Request.HasAuth())
+ {
+ a_Connection.SendNeedAuth("MCServer WebAdmin");
+ return;
+ }
- char SourceFile[] = "webadmin/template.html";
+ // Check auth:
+ AString UserPassword = m_IniFile.GetValue("User:" + a_Request.GetAuthUsername(), "Password", "");
+ if ((UserPassword == "") || (a_Request.GetAuthPassword() != UserPassword))
+ {
+ a_Connection.SendNeedAuth("MCServer WebAdmin - bad username or password");
+ return;
+ }
+
+ // Check if the contents should be wrapped in the template:
+ AString URL = a_Request.GetBareURL();
+ ASSERT(URL.length() > 0);
+ bool ShouldWrapInTemplate = ((URL.length() > 1) && (URL[1] != '~'));
+
+ // Retrieve the request data:
+ cWebadminRequestData * Data = (cWebadminRequestData *)(a_Request.GetUserData());
+ if (Data == NULL)
+ {
+ a_Connection.SendStatusAndReason(500, "Bad UserData");
+ return;
+ }
+
+ // Wrap it all up for the Lua call:
+ AString Template;
+ HTTPTemplateRequest TemplateRequest;
+ TemplateRequest.Request.Username = a_Request.GetAuthUsername();
+ TemplateRequest.Request.Method = a_Request.GetMethod();
+ TemplateRequest.Request.Path = URL.substr(1);
+
+ if (Data->m_Form.Finish())
+ {
+ for (cHTTPFormParser::const_iterator itr = Data->m_Form.begin(), end = Data->m_Form.end(); itr != end; ++itr)
+ {
+ HTTPFormData HTTPfd;
+ HTTPfd.Value = itr->second;
+ HTTPfd.Type = "";
+ HTTPfd.Name = itr->first;
+ TemplateRequest.Request.FormData[itr->first] = HTTPfd;
+ TemplateRequest.Request.PostParams[itr->first] = itr->second;
+ TemplateRequest.Request.Params[itr->first] = itr->second;
+ } // for itr - Data->m_Form[]
+ }
+
+ // Try to get the template from the Lua template script
+ if (ShouldWrapInTemplate)
+ {
+ if (WebAdmin->m_TemplateScript.Call("ShowPage", WebAdmin, &TemplateRequest, cLuaState::Return, Template))
+ {
+ cHTTPResponse Resp;
+ Resp.SetContentType("text/html");
+ a_Connection.Send(Resp);
+ a_Connection.Send(Template.c_str(), Template.length());
+ return;
+ }
+ a_Connection.SendStatusAndReason(500, "m_TemplateScript failed");
+ return;
+ }
+
+ AString BaseURL = GetBaseURL(URL);
+ AString Menu;
+ Template = "{CONTENT}";
+ AString FoundPlugin;
- cFile f;
- if (!f.Open(SourceFile, cFile::fmRead))
+ for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr)
{
- return "";
+ cWebPlugin * WebPlugin = *itr;
+ std::list< std::pair<AString, AString> > NameList = WebPlugin->GetTabNames();
+ for (std::list< std::pair<AString, AString> >::iterator Names = NameList.begin(); Names != NameList.end(); ++Names)
+ {
+ Menu += "<li><a href='" + BaseURL + WebPlugin->GetWebTitle().c_str() + "/" + (*Names).second + "'>" + (*Names).first + "</a></li>";
+ }
}
- // copy the file into the buffer:
- f.ReadRestOfFile(retVal);
+ sWebAdminPage Page = GetPage(TemplateRequest.Request);
+ AString Content = Page.Content;
+ FoundPlugin = Page.PluginName;
+ if (!Page.TabName.empty())
+ {
+ FoundPlugin += " - " + Page.TabName;
+ }
- return retVal;
+ if (FoundPlugin.empty()) // Default page
+ {
+ Content = GetDefaultPage();
+ }
+
+ if (ShouldWrapInTemplate && (URL.size() > 1))
+ {
+ Content += "\n<p><a href='" + BaseURL + "'>Go back</a></p>";
+ }
+
+ int MemUsageKiB = GetMemoryUsage();
+ if (MemUsageKiB > 0)
+ {
+ ReplaceString(Template, "{MEM}", Printf("%.02f", (double)MemUsageKiB / 1024));
+ ReplaceString(Template, "{MEMKIB}", Printf("%d", MemUsageKiB));
+ }
+ else
+ {
+ ReplaceString(Template, "{MEM}", "unknown");
+ ReplaceString(Template, "{MEMKIB}", "unknown");
+ }
+ ReplaceString(Template, "{USERNAME}", a_Request.GetAuthUsername());
+ ReplaceString(Template, "{MENU}", Menu);
+ ReplaceString(Template, "{PLUGIN_NAME}", FoundPlugin);
+ ReplaceString(Template, "{CONTENT}", Content);
+ ReplaceString(Template, "{TITLE}", "MCServer");
+
+ AString NumChunks;
+ Printf(NumChunks, "%d", cRoot::Get()->GetTotalChunkCount());
+ ReplaceString(Template, "{NUMCHUNKS}", NumChunks);
+
+ cHTTPResponse Resp;
+ Resp.SetContentType("text/html");
+ a_Connection.Send(Resp);
+ a_Connection.Send(Template.c_str(), Template.length());
+}
+
+
+
+
+
+void cWebAdmin::HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
+{
+ static const char LoginForm[] = \
+ "<h1>MCServer WebAdmin</h1>" \
+ "<center>" \
+ "<form method='get' action='webadmin/'>" \
+ "<input type='submit' value='Log in'>" \
+ "</form>" \
+ "</center>";
+ cHTTPResponse Resp;
+ Resp.SetContentType("text/html");
+ a_Connection.Send(Resp);
+ a_Connection.Send(LoginForm, sizeof(LoginForm) - 1);
+ a_Connection.FinishResponse();
}
-sWebAdminPage cWebAdmin::GetPage(const HTTPRequest& a_Request)
+sWebAdminPage cWebAdmin::GetPage(const HTTPRequest & a_Request)
{
sWebAdminPage Page;
AStringVector Split = StringSplit(a_Request.Path, "/");
@@ -396,6 +349,41 @@ sWebAdminPage cWebAdmin::GetPage(const HTTPRequest& a_Request)
+AString cWebAdmin::GetDefaultPage(void)
+{
+ AString Content;
+ Content += "<h4>Server Name:</h4>";
+ Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID() ) + "</p>";
+
+ Content += "<h4>Plugins:</h4><ul>";
+ cPluginManager * PM = cPluginManager::Get();
+ const cPluginManager::PluginMap & List = PM->GetAllPlugins();
+ for (cPluginManager::PluginMap::const_iterator itr = List.begin(); itr != List.end(); ++itr)
+ {
+ if (itr->second == NULL)
+ {
+ continue;
+ }
+ AString VersionNum;
+ AppendPrintf(Content, "<li>%s V.%i</li>", itr->second->GetName().c_str(), itr->second->GetVersion());
+ }
+ Content += "</ul>";
+ Content += "<h4>Players:</h4><ul>";
+
+ cPlayerAccum PlayerAccum;
+ cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players
+ if( World != NULL )
+ {
+ World->ForEachPlayer(PlayerAccum);
+ Content.append(PlayerAccum.m_Contents);
+ }
+ Content += "</ul><br>";
+ return Content;
+}
+
+
+
+
AString cWebAdmin::GetBaseURL( const AString& a_URL )
{
return GetBaseURL(StringSplit(a_URL, "/"));
@@ -474,3 +462,76 @@ int cWebAdmin::GetMemoryUsage(void)
+
+void cWebAdmin::OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
+{
+ const AString & URL = a_Request.GetURL();
+ if (
+ (strncmp(URL.c_str(), "/webadmin", 9) == 0) ||
+ (strncmp(URL.c_str(), "/~webadmin", 10) == 0)
+ )
+ {
+ a_Request.SetUserData(new cWebadminRequestData(a_Request));
+ return;
+ }
+ if (URL == "/")
+ {
+ // The root needs no body handler and is fully handled in the OnRequestFinished() call
+ return;
+ }
+ // TODO: Handle other requests
+}
+
+
+
+
+
+void cWebAdmin::OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size)
+{
+ cRequestData * Data = (cRequestData *)(a_Request.GetUserData());
+ if (Data == NULL)
+ {
+ return;
+ }
+ Data->OnBody(a_Data, a_Size);
+}
+
+
+
+
+
+void cWebAdmin::OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
+{
+ const AString & URL = a_Request.GetURL();
+ if (
+ (strncmp(URL.c_str(), "/webadmin", 9) == 0) ||
+ (strncmp(URL.c_str(), "/~webadmin", 10) == 0)
+ )
+ {
+ HandleWebadminRequest(a_Connection, a_Request);
+ return;
+ }
+ if (URL == "/")
+ {
+ // The root needs no body handler and is fully handled in the OnRequestFinished() call
+ HandleRootRequest(a_Connection, a_Request);
+ return;
+ }
+ // TODO: Handle other requests
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWebAdmin::cWebadminRequestData
+
+void cWebAdmin::cWebadminRequestData::OnBody(const char * a_Data, int a_Size)
+{
+ m_Form.Parse(a_Data, a_Size);
+}
+
+
+
+
diff --git a/source/WebAdmin.h b/source/WebAdmin.h
index 7b710bd3b..b85d8d059 100644
--- a/source/WebAdmin.h
+++ b/source/WebAdmin.h
@@ -1,8 +1,26 @@
+
+// WebAdmin.h
+
+// Declares the cWebAdmin class representing the admin interface over http protocol, and related services (API)
+
#pragma once
-#include "../WebServer/WebServer.h"
#include "OSSupport/Socket.h"
#include "LuaState.h"
+#include "../iniFile/iniFile.h"
+#include "HTTPServer/HTTPServer.h"
+#include "HTTPServer/HTTPFormParser.h"
+
+
+
+
+
+// Disable MSVC warnings:
+#if defined(_MSC_VER)
+ #pragma warning(push)
+ #pragma warning(disable:4355) // 'this' : used in base member initializer list
+#endif
+
@@ -10,7 +28,6 @@
// fwd:
class cStringMap;
class cEvent;
-class cIniFile;
class cWebPlugin;
@@ -73,7 +90,8 @@ struct sWebAdminPage
// tolua_begin
-class cWebAdmin
+class cWebAdmin :
+ public cHTTPServer::cCallbacks
{
public:
// tolua_end
@@ -81,49 +99,85 @@ public:
typedef std::list< cWebPlugin* > PluginList;
- cWebAdmin( int a_Port = 8080 );
+ cWebAdmin(void);
~cWebAdmin();
- bool Init( int a_Port );
+ /// Initializes the object. Returns true if successfully initialized and ready to start
+ bool Init(void);
+
+ /// Starts the HTTP server taking care of the admin. Returns true if successful
+ bool Start(void);
- void AddPlugin( cWebPlugin* a_Plugin );
- void RemovePlugin( cWebPlugin* a_Plugin );
+ void AddPlugin( cWebPlugin* a_Plugin );
+ void RemovePlugin( cWebPlugin* a_Plugin );
// TODO: Convert this to the auto-locking callback mechanism used for looping players in worlds and such
PluginList GetPlugins() const { return m_Plugins; } // >> EXPORTED IN MANUALBINDINGS <<
- static void Request_Handler(webserver::http_request* r);
-
// tolua_begin
/// Returns the amount of currently used memory, in KiB, or -1 if it cannot be queried
static int GetMemoryUsage(void);
- int GetPort() { return m_Port; }
-
sWebAdminPage GetPage(const HTTPRequest& a_Request);
+
+ /// Returns the contents of the default page - the list of plugins and players
+ AString GetDefaultPage(void);
+
AString GetBaseURL(const AString& a_URL);
// tolua_end
AString GetBaseURL(const AStringVector& a_URLSplit);
-
-private:
- int m_Port;
-
- bool m_bConnected;
- cSocket m_ListenSocket;
+protected:
+ /// Common base class for request body data handlers
+ class cRequestData
+ {
+ public:
+ /// Called when a new chunk of body data is received
+ virtual void OnBody(const char * a_Data, int a_Size) = 0;
+ } ;
+
+ /// The body handler for requests in the "/webadmin" and "/~webadmin" paths
+ class cWebadminRequestData :
+ public cRequestData,
+ public cHTTPFormParser::cCallbacks
+ {
+ public:
+ cHTTPFormParser m_Form;
+
+
+ cWebadminRequestData(cHTTPRequest & a_Request) :
+ m_Form(a_Request, *this)
+ {
+ }
+
+ // cRequestData overrides:
+ virtual void OnBody(const char * a_Data, int a_Size) override;
+
+ // cHTTPFormParser::cCallbacks overrides. Files are ignored:
+ virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) override {}
+ virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) override {}
+ virtual void OnFileEnd(cHTTPFormParser & a_Parser) override {}
+ } ;
+
+
+ /// Set to true if Init() succeeds and the webadmin isn't to be disabled
+ bool m_IsInitialized;
- cIniFile * m_IniFile;
+ /// The webadmin.ini file, used for the settings and allowed logins
+ cIniFile m_IniFile;
+
PluginList m_Plugins;
cEvent * m_Event;
- webserver * m_WebServer;
-
/// The Lua template script to provide templates:
cLuaState m_TemplateScript;
+
+ /// The HTTP server which provides the underlying HTTP parsing, serialization and events
+ cHTTPServer m_HTTPServer;
#ifdef _WIN32
@@ -132,9 +186,29 @@ private:
static void * ListenThread(void * lpParam);
#endif
- AString GetTemplate();
+ AString GetTemplate(void);
+
+ /// Handles requests coming to the "/webadmin" or "/~webadmin" URLs
+ void HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request);
+
+ /// Handles requests for the root page
+ void HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request);
+
+ // cHTTPServer::cCallbacks overrides:
+ virtual void OnRequestBegun (cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override;
+ virtual void OnRequestBody (cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) override;
+ virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override;
} ; // tolua_export
+
+// Revert MSVC warnings back to orignal state:
+#if defined(_MSC_VER)
+ #pragma warning(pop)
+#endif
+
+
+
+