summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMattes D <github@xoft.cz>2016-03-02 10:05:10 +0100
committerMattes D <github@xoft.cz>2016-06-27 20:51:48 +0200
commitaf8c96026d458481543ead176bd15651857826bd (patch)
tree1635300b69ceed70abe1195cf7e23c51b2546b73 /src
parentAdded cLuaState::cCallback for representing (resettable) Lua callbacks. (diff)
downloadcuberite-af8c96026d458481543ead176bd15651857826bd.tar
cuberite-af8c96026d458481543ead176bd15651857826bd.tar.gz
cuberite-af8c96026d458481543ead176bd15651857826bd.tar.bz2
cuberite-af8c96026d458481543ead176bd15651857826bd.tar.lz
cuberite-af8c96026d458481543ead176bd15651857826bd.tar.xz
cuberite-af8c96026d458481543ead176bd15651857826bd.tar.zst
cuberite-af8c96026d458481543ead176bd15651857826bd.zip
Diffstat (limited to '')
-rw-r--r--src/Bindings/AllToLua.pkg1
-rw-r--r--src/Bindings/CMakeLists.txt3
-rw-r--r--src/Bindings/ManualBindings.cpp282
-rw-r--r--src/Bindings/PluginLua.cpp64
-rw-r--r--src/Bindings/PluginLua.h15
-rw-r--r--src/Bindings/PluginManager.cpp17
-rw-r--r--src/Bindings/PluginManager.h3
-rw-r--r--src/Bindings/WebPlugin.cpp152
-rw-r--r--src/Bindings/WebPlugin.h80
-rw-r--r--src/WebAdmin.cpp415
-rw-r--r--src/WebAdmin.h158
11 files changed, 584 insertions, 606 deletions
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg
index 991ed0ddd..6ca9c8658 100644
--- a/src/Bindings/AllToLua.pkg
+++ b/src/Bindings/AllToLua.pkg
@@ -38,7 +38,6 @@ $cfile "LuaFunctions.h"
$cfile "PluginManager.h"
$cfile "Plugin.h"
$cfile "PluginLua.h"
-$cfile "WebPlugin.h"
$cfile "LuaWindow.h"
$cfile "../BlockID.h"
diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt
index 10cda1efb..4f25f2cf4 100644
--- a/src/Bindings/CMakeLists.txt
+++ b/src/Bindings/CMakeLists.txt
@@ -23,7 +23,6 @@ SET (SRCS
Plugin.cpp
PluginLua.cpp
PluginManager.cpp
- WebPlugin.cpp
)
SET (HDRS
@@ -44,7 +43,6 @@ SET (HDRS
Plugin.h
PluginLua.h
PluginManager.h
- WebPlugin.h
tolua++.h
)
@@ -66,7 +64,6 @@ set(BINDING_DEPENDENCIES
../Bindings/Plugin.h
../Bindings/PluginLua.h
../Bindings/PluginManager.h
- ../Bindings/WebPlugin.h
../BiomeDef.h
../BlockArea.h
../BlockEntities/BeaconEntity.h
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index 9945929d0..bd781f766 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -1702,47 +1702,71 @@ static int tolua_SetObjectCallback(lua_State * tolua_S)
-static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S)
+// Callback class used for the WebTab:
+class cWebTabCallback:
+ public cWebAdmin::cWebTabCallback
{
- cLuaState LuaState(tolua_S);
- cPluginLua * self = nullptr;
+public:
+ /** The Lua callback to call to generate the page contents. */
+ cLuaState::cCallback m_Callback;
- if (!LuaState.GetStackValue(1, self))
+ virtual bool Call(
+ const HTTPRequest & a_Request,
+ const AString & a_UrlPath,
+ AString & a_Content,
+ AString & a_ContentType
+ ) override
{
- LOGWARNING("cPluginLua:AddWebTab: invalid self as first argument");
- return 0;
+ AString content, contentType;
+ return m_Callback.Call(&a_Request, a_UrlPath, cLuaState::Return, a_Content, a_ContentType);
}
+};
- tolua_Error tolua_err;
- tolua_err.array = 0;
- tolua_err.index = 3;
- tolua_err.type = "function";
- std::string Title;
- int Reference = LUA_REFNIL;
- if (LuaState.CheckParamString(2) && LuaState.CheckParamFunction(3))
+
+
+static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S)
+{
+ // OBSOLETE, use cWebAdmin:AddWebTab() instead!
+ // Function signature:
+ // cPluginLua:AddWebTab(Title, CallbackFn, [UrlPath])
+
+ // TODO: Warn about obsolete API usage
+ // Only implement after merging the new API change and letting some time for changes in the plugins
+
+ // Check params:
+ cLuaState LuaState(tolua_S);
+ cPluginLua * self = cManualBindings::GetLuaPlugin(tolua_S);
+ if (self == nullptr)
{
- Reference = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
- LuaState.GetStackValue(2, Title);
+ return 0;
}
- else
+ if (
+ !LuaState.CheckParamString(2) ||
+ !LuaState.CheckParamFunction(3) ||
+ // Optional string as param 4
+ !LuaState.CheckParamEnd(5)
+ )
{
- return cManualBindings::tolua_do_error(tolua_S, "#ferror calling function '#funcname#'", &tolua_err);
+ return 0;
}
- if (Reference != LUA_REFNIL)
+ // Read the params:
+ AString title, urlPath;
+ auto callback = std::make_shared<cWebTabCallback>();
+ if (!LuaState.GetStackValues(2, title, callback->m_Callback))
{
- if (!self->AddWebTab(Title.c_str(), tolua_S, Reference))
- {
- luaL_unref(tolua_S, LUA_REGISTRYINDEX, Reference);
- }
+ LOGWARNING("cPlugin:AddWebTab(): Cannot read required parameters");
+ return 0;
}
- else
+ if (!LuaState.GetStackValue(4, urlPath))
{
- LOGWARNING("cPluginLua:AddWebTab: invalid function reference in 2nd argument (Title: \"%s\")", Title.c_str());
+ urlPath = cWebAdmin::GetURLEncodedString(title);
}
+ cRoot::Get()->GetWebAdmin()->AddWebTab(title, urlPath, self->GetName(), callback);
+
return 0;
}
@@ -2107,22 +2131,68 @@ static int tolua_cUrlParser_ParseAuthorityPart(lua_State * a_LuaState)
-static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S)
+static int tolua_cWebAdmin_AddWebTab(lua_State * tolua_S)
+{
+ // Function signatures:
+ // cWebAdmin:AddWebTab(Title, UrlPath, CallbackFn)
+
+ // Check params:
+ cLuaState LuaState(tolua_S);
+ cPluginLua * self = cManualBindings::GetLuaPlugin(tolua_S);
+ if (self == nullptr)
+ {
+ return 0;
+ }
+ if (
+ // Don't care whether the first param is a cWebAdmin instance or class
+ !LuaState.CheckParamString(2, 3) ||
+ !LuaState.CheckParamFunction(4) ||
+ !LuaState.CheckParamEnd(5)
+ )
+ {
+ return 0;
+ }
+
+ // Read the params:
+ AString title, urlPath;
+ auto callback = std::make_shared<cWebTabCallback>();
+ if (!LuaState.GetStackValues(2, title, urlPath, callback->m_Callback))
+ {
+ LOGWARNING("cWebAdmin:AddWebTab(): Cannot read required parameters");
+ return 0;
+ }
+
+ cRoot::Get()->GetWebAdmin()->AddWebTab(title, urlPath, self->GetName(), callback);
+
+ return 0;
+}
+
+
+
+
+
+static int tolua_cWebAdmin_GetAllWebTabs(lua_State * tolua_S)
{
- cWebAdmin * self = reinterpret_cast<cWebAdmin *>(tolua_tousertype(tolua_S, 1, nullptr));
+ // Function signature:
+ // cWebAdmin:GetAllWebTabs() -> { {"PluginName", "UrlPath", "Title"}, {"PluginName", "UrlPath", "Title"}, ...}
- const cWebAdmin::PluginList & AllPlugins = self->GetPlugins();
+ // Don't care about params at all
- lua_createtable(tolua_S, static_cast<int>(AllPlugins.size()), 0);
+ auto webTabs = cRoot::Get()->GetWebAdmin()->GetAllWebTabs();
+ lua_createtable(tolua_S, static_cast<int>(webTabs.size()), 0);
int newTable = lua_gettop(tolua_S);
int index = 1;
- cWebAdmin::PluginList::const_iterator iter = AllPlugins.begin();
- while (iter != AllPlugins.end())
- {
- const cWebPlugin * Plugin = *iter;
- tolua_pushusertype(tolua_S, reinterpret_cast<void *>(const_cast<cWebPlugin*>(Plugin)), "const cWebPlugin");
+ cLuaState L(tolua_S);
+ for (const auto & wt: webTabs)
+ {
+ lua_createtable(tolua_S, 0, 3);
+ L.Push(wt->m_PluginName);
+ lua_setfield(tolua_S, -2, "PluginName");
+ L.Push(wt->m_UrlPath);
+ lua_setfield(tolua_S, -2, "UrlPath");
+ L.Push(wt->m_Title);
+ lua_setfield(tolua_S, -2, "Title");
lua_rawseti(tolua_S, newTable, index);
- ++iter;
++index;
}
return 1;
@@ -2132,14 +2202,14 @@ static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S)
-/** Binding for cWebAdmin::GetHTMLEscapedString.
+/** Binding for cWebAdmin::GetBaseURL.
Manual code required because ToLua generates an extra return value */
-static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S)
+static int tolua_cWebAdmin_GetBaseURL(lua_State * tolua_S)
{
// Check the param types:
cLuaState S(tolua_S);
if (
- !S.CheckParamUserTable(1, "cWebAdmin") ||
+ // Don't care whether the first param is a cWebAdmin instance or class
!S.CheckParamString(2) ||
!S.CheckParamEnd(3)
)
@@ -2152,7 +2222,7 @@ static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S)
S.GetStackValue(2, Input);
// Convert and return:
- S.Push(cWebAdmin::GetHTMLEscapedString(Input));
+ S.Push(cWebAdmin::GetBaseURL(Input));
return 1;
}
@@ -2160,14 +2230,14 @@ static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S)
-/** Binding for cWebAdmin::GetURLEncodedString.
+/** Binding for cWebAdmin::GetContentTypeFromFileExt.
Manual code required because ToLua generates an extra return value */
-static int tolua_AllToLua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S)
+static int tolua_cWebAdmin_GetContentTypeFromFileExt(lua_State * tolua_S)
{
// Check the param types:
cLuaState S(tolua_S);
if (
- !S.CheckParamUserTable(1, "cWebAdmin") ||
+ // Don't care whether the first param is a cWebAdmin instance or class
!S.CheckParamString(2) ||
!S.CheckParamEnd(3)
)
@@ -2180,7 +2250,7 @@ static int tolua_AllToLua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S)
S.GetStackValue(2, Input);
// Convert and return:
- S.Push(cWebAdmin::GetURLEncodedString(Input));
+ S.Push(cWebAdmin::GetContentTypeFromFileExt(Input));
return 1;
}
@@ -2188,20 +2258,112 @@ static int tolua_AllToLua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S)
-static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S)
+/** Binding for cWebAdmin::GetHTMLEscapedString.
+Manual code required because ToLua generates an extra return value */
+static int tolua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S)
{
- // Returns a map of (SafeTitle -> Title) for the plugin's web tabs.
- auto self = reinterpret_cast<cWebPlugin *>(tolua_tousertype(tolua_S, 1, nullptr));
- auto TabNames = self->GetTabNames();
- lua_newtable(tolua_S);
- int index = 1;
- for (auto itr = TabNames.cbegin(), end = TabNames.cend(); itr != end; ++itr)
+ // Check the param types:
+ cLuaState S(tolua_S);
+ if (
+ // Don't care whether the first param is a cWebAdmin instance or class
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
{
- tolua_pushstring(tolua_S, itr->second.c_str()); // Because the SafeTitle is supposed to be unique, use it as key
- tolua_pushstring(tolua_S, itr->first.c_str());
- lua_rawset(tolua_S, -3);
- ++index;
+ return 0;
}
+
+ // Get the parameters:
+ AString Input;
+ S.GetStackValue(2, Input);
+
+ // Convert and return:
+ S.Push(cWebAdmin::GetHTMLEscapedString(Input));
+ return 1;
+}
+
+
+
+
+
+/** Binding for cWebAdmin::GetPage. */
+static int tolua_cWebAdmin_GetPage(lua_State * tolua_S)
+{
+ /*
+ Function signature:
+ cWebAdmin:GetPage(a_HTTPRequest) ->
+ {
+ Content = "", // Content generated by the plugin
+ ContentType = "", // Content type generated by the plugin (default: "text/html")
+ UrlPath = "", // URL path of the tab
+ TabTitle = "", // Tab's title, as register via cWebAdmin:AddWebTab()
+ PluginName = "", // Plugin's API name
+ PluginFolder = "", // Plugin's folder name (display name)
+ }
+ */
+
+ // Check the param types:
+ cLuaState S(tolua_S);
+ if (
+ // Don't care about first param, whether it's cWebAdmin instance or class
+ !S.CheckParamUserType(2, "HTTPRequest") ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the parameters:
+ HTTPRequest * request = nullptr;
+ if (!S.GetStackValue(2, request))
+ {
+ LOGWARNING("cWebAdmin:GetPage(): Cannot read the HTTPRequest parameter.");
+ return 0;
+ }
+
+ // Generate the page and push the results as a dictionary-table:
+ auto page = cRoot::Get()->GetWebAdmin()->GetPage(*request);
+ lua_createtable(S, 0, 6);
+ S.Push(page.Content);
+ lua_setfield(S, -2, "Content");
+ S.Push(page.ContentType);
+ lua_setfield(S, -2, "ContentType");
+ S.Push(page.TabUrlPath);
+ lua_setfield(S, -2, "UrlPath");
+ S.Push(page.TabTitle);
+ lua_setfield(S, -2, "TabTitle");
+ S.Push(page.PluginName);
+ lua_setfield(S, -2, "PluginName");
+ S.Push(cPluginManager::Get()->GetPluginFolderName(page.PluginName));
+ lua_setfield(S, -2, "PluginFolder");
+ return 1;
+}
+
+
+
+
+
+/** Binding for cWebAdmin::GetURLEncodedString.
+Manual code required because ToLua generates an extra return value */
+static int tolua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S)
+{
+ // Check the param types:
+ cLuaState S(tolua_S);
+ if (
+ // Don't care whether the first param is a cWebAdmin instance or class
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the parameters:
+ AString Input;
+ S.GetStackValue(2, Input);
+
+ // Convert and return:
+ S.Push(cWebAdmin::GetURLEncodedString(Input));
return 1;
}
@@ -3655,13 +3817,13 @@ void cManualBindings::Bind(lua_State * tolua_S)
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cWebAdmin");
- tolua_function(tolua_S, "GetHTMLEscapedString", tolua_AllToLua_cWebAdmin_GetHTMLEscapedString);
- tolua_function(tolua_S, "GetPlugins", tolua_cWebAdmin_GetPlugins);
- tolua_function(tolua_S, "GetURLEncodedString", tolua_AllToLua_cWebAdmin_GetURLEncodedString);
- tolua_endmodule(tolua_S);
-
- tolua_beginmodule(tolua_S, "cWebPlugin");
- tolua_function(tolua_S, "GetTabNames", tolua_cWebPlugin_GetTabNames);
+ tolua_function(tolua_S, "AddWebTab", tolua_cWebAdmin_AddWebTab);
+ tolua_function(tolua_S, "GetAllWebTabs", tolua_cWebAdmin_GetAllWebTabs);
+ tolua_function(tolua_S, "GetBaseURL", tolua_cWebAdmin_GetBaseURL);
+ tolua_function(tolua_S, "GetContentTypeFromFileExt", tolua_cWebAdmin_GetContentTypeFromFileExt);
+ tolua_function(tolua_S, "GetHTMLEscapedString", tolua_cWebAdmin_GetHTMLEscapedString);
+ tolua_function(tolua_S, "GetPage", tolua_cWebAdmin_GetPage);
+ tolua_function(tolua_S, "GetURLEncodedString", tolua_cWebAdmin_GetURLEncodedString);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "HTTPRequest");
diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp
index a266e6223..9b2d48b33 100644
--- a/src/Bindings/PluginLua.cpp
+++ b/src/Bindings/PluginLua.cpp
@@ -15,6 +15,8 @@
#include "../CommandOutput.h"
#include "PluginManager.h"
#include "../Item.h"
+#include "../Root.h"
+#include "../WebAdmin.h"
extern "C"
{
@@ -66,7 +68,7 @@ void cPluginLua::Close(void)
// Remove the command bindings and web tabs:
ClearCommands();
ClearConsoleCommands();
- ClearTabs();
+ ClearWebTabs();
// Notify and remove all m_Resettables (unlock the m_CriticalSection while resetting them):
cResettablePtrs resettables;
@@ -205,7 +207,7 @@ bool cPluginLua::Load(void)
void cPluginLua::Unload(void)
{
- ClearTabs();
+ ClearWebTabs();
super::Unload();
Close();
}
@@ -2126,51 +2128,6 @@ void cPluginLua::AddResettable(cPluginLua::cResettablePtr a_Resettable)
-AString cPluginLua::HandleWebRequest(const HTTPRequest & a_Request)
-{
- // Find the tab to use for the request:
- auto TabName = GetTabNameForRequest(a_Request);
- AString SafeTabTitle = TabName.second;
- if (SafeTabTitle.empty())
- {
- return "";
- }
- auto Tab = GetTabBySafeTitle(SafeTabTitle);
- if (Tab == nullptr)
- {
- return "";
- }
-
- // Get the page content from the plugin:
- cCSLock Lock(m_CriticalSection);
- AString Contents = Printf("WARNING: WebPlugin tab '%s' did not return a string!", Tab->m_Title.c_str());
- if (!m_LuaState.Call(Tab->m_UserData, &a_Request, cLuaState::Return, Contents))
- {
- return "Lua encountered error while processing the page request";
- }
- return Contents;
-}
-
-
-
-
-
-bool cPluginLua::AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference)
-{
- cCSLock Lock(m_CriticalSection);
- if (a_LuaState != m_LuaState)
- {
- LOGERROR("Only allowed to add a tab to a WebPlugin of your own Plugin!");
- return false;
- }
- AddNewWebTab(a_Title, a_FunctionReference);
- return true;
-}
-
-
-
-
-
void cPluginLua::BindCommand(const AString & a_Command, int a_FnRef)
{
ASSERT(m_Commands.find(a_Command) == m_Commands.end());
@@ -2227,6 +2184,19 @@ void cPluginLua::CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int
+void cPluginLua::ClearWebTabs(void)
+{
+ auto webAdmin = cRoot::Get()->GetWebAdmin();
+ if (webAdmin != nullptr) // can be nullptr when shutting down the server
+ {
+ webAdmin->RemoveAllPluginWebTabs(m_Name);
+ }
+}
+
+
+
+
+
////////////////////////////////////////////////////////////////////////////////
// cPluginLua::cResettable:
diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h
index db6612671..dac782a43 100644
--- a/src/Bindings/PluginLua.h
+++ b/src/Bindings/PluginLua.h
@@ -10,7 +10,6 @@
#pragma once
#include "Plugin.h"
-#include "WebPlugin.h"
#include "LuaState.h"
// Names for the global variables through which the plugin is identified in its LuaState
@@ -29,8 +28,7 @@ class cWindow;
// tolua_begin
class cPluginLua :
- public cPlugin,
- public cWebPlugin
+ public cPlugin
{
typedef cPlugin super;
@@ -181,14 +179,6 @@ public:
/** Returns true if the plugin contains the function for the specified hook type, using the old-style registration (#121) */
bool CanAddOldStyleHook(int a_HookType);
- // cWebPlugin overrides
- virtual const AString GetWebTitle(void) const override {return GetName(); }
- virtual AString HandleWebRequest(const HTTPRequest & a_Request) override;
-
- /** Adds a new web tab to webadmin.
- Displaying the tab calls the referenced function. */
- bool AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference); // Exported in ManualBindings.cpp
-
/** Binds the command to call the function specified by a Lua function reference. Simply adds to CommandMap. */
void BindCommand(const AString & a_Command, int a_FnRef);
@@ -270,6 +260,9 @@ protected:
/** Releases all Lua references, notifies and removes all m_Resettables[] and closes the m_LuaState. */
void Close(void);
+
+ /** Removes all WebTabs currently registered for this plugin from the WebAdmin. */
+ void ClearWebTabs(void);
} ; // tolua_export
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index 5b3ef7803..203450505 100644
--- a/src/Bindings/PluginManager.cpp
+++ b/src/Bindings/PluginManager.cpp
@@ -1965,6 +1965,23 @@ bool cPluginManager::ForEachPlugin(cPluginCallback & a_Callback)
+AString cPluginManager::GetPluginFolderName(const AString & a_PluginName)
+{
+ // TODO: Implement locking for plugins
+ for (auto & plugin: m_Plugins)
+ {
+ if (plugin->GetName() == a_PluginName)
+ {
+ return plugin->GetFolderName();
+ }
+ }
+ return AString();
+}
+
+
+
+
+
void cPluginManager::AddHook(cPlugin * a_Plugin, int a_Hook)
{
if (a_Plugin == nullptr)
diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h
index f3f0b6d0b..a97582fbe 100644
--- a/src/Bindings/PluginManager.h
+++ b/src/Bindings/PluginManager.h
@@ -332,6 +332,9 @@ public:
Returns true if all plugins have been reported, false if the callback has aborted the enumeration by returning true. */
bool ForEachPlugin(cPluginCallback & a_Callback);
+ /** Returns the name of the folder (cPlugin::GetFolderName()) from which the specified plugin was loaded. */
+ AString GetPluginFolderName(const AString & a_PluginName); // tolua_export
+
/** Returns the path where individual plugins' folders are expected.
The path doesn't end in a slash. */
static AString GetPluginsPath(void) { return FILE_IO_PREFIX + AString("Plugins"); } // tolua_export
diff --git a/src/Bindings/WebPlugin.cpp b/src/Bindings/WebPlugin.cpp
deleted file mode 100644
index 1eca7de93..000000000
--- a/src/Bindings/WebPlugin.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "WebPlugin.h"
-#include "../WebAdmin.h"
-#include "../Root.h"
-
-
-
-
-
-cWebPlugin::cWebPlugin()
-{
- cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin();
- if (WebAdmin != nullptr)
- {
- WebAdmin->AddPlugin(this);
- }
-}
-
-
-
-
-
-cWebPlugin::~cWebPlugin()
-{
- ASSERT(m_Tabs.empty()); // Has ClearTabs() been called?
-
- // Remove from WebAdmin:
- cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin();
- if (WebAdmin != nullptr)
- {
- WebAdmin->RemovePlugin(this);
- }
-}
-
-
-
-
-
-cWebPlugin::cTabNames cWebPlugin::GetTabNames(void) const
-{
- std::list< std::pair<AString, AString>> NameList;
- for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr)
- {
- NameList.push_back(std::make_pair((*itr)->m_Title, (*itr)->m_SafeTitle));
- }
- return NameList;
-}
-
-
-
-
-
-cWebPlugin::cTabPtr cWebPlugin::GetTabBySafeTitle(const AString & a_SafeTitle) const
-{
- cCSLock Lock(m_CSTabs);
- for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr)
- {
- if ((*itr)->m_SafeTitle == a_SafeTitle)
- {
- return *itr;
- }
- }
- return nullptr;
-}
-
-
-
-
-
-std::pair<AString, AString> cWebPlugin::GetTabNameForRequest(const HTTPRequest & a_Request)
-{
- AStringVector Split = StringSplit(a_Request.Path, "/");
- if (Split.empty())
- {
- return std::make_pair(AString(), AString());
- }
-
- cCSLock Lock(m_CSTabs);
- cTabPtr Tab;
- if (Split.size() > 2) // If we got the tab name, show that page
- {
- for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr)
- {
- if ((*itr)->m_SafeTitle.compare(Split[2]) == 0) // This is the one!
- {
- return std::make_pair((*itr)->m_Title, (*itr)->m_SafeTitle);
- }
- }
- // Tab name not found, display an "empty" page:
- return std::make_pair(AString(), AString());
- }
-
- // Show the first tab:
- if (!m_Tabs.empty())
- {
- return std::make_pair(m_Tabs.front()->m_SafeTitle, m_Tabs.front()->m_SafeTitle);
- }
-
- // No tabs at all:
- return std::make_pair(AString(), AString());
-}
-
-
-
-
-AString cWebPlugin::SafeString(const AString & a_String)
-{
- AString RetVal;
- auto len = a_String.size();
- RetVal.reserve(len);
- for (size_t i = 0; i < len; ++i)
- {
- char c = a_String[i];
- if (c == ' ')
- {
- c = '_';
- }
- RetVal.push_back(c);
- }
- return RetVal;
-}
-
-
-
-
-
-void cWebPlugin::AddNewWebTab(const AString & a_Title, int a_UserData)
-{
- auto Tab = std::make_shared<cTab>(a_Title, a_UserData);
- cCSLock Lock(m_CSTabs);
- m_Tabs.push_back(Tab);
-}
-
-
-
-
-
-void cWebPlugin::ClearTabs(void)
-{
- // Remove the webadmin tabs:
- cTabPtrs Tabs;
- {
- cCSLock Lock(m_CSTabs);
- std::swap(Tabs, m_Tabs);
- }
-}
-
-
-
-
diff --git a/src/Bindings/WebPlugin.h b/src/Bindings/WebPlugin.h
deleted file mode 100644
index 6dc8db801..000000000
--- a/src/Bindings/WebPlugin.h
+++ /dev/null
@@ -1,80 +0,0 @@
-
-#pragma once
-
-struct HTTPRequest;
-
-
-
-
-
-// tolua_begin
-class cWebPlugin
-{
-public:
- // tolua_end
-
- struct cTab
- {
- AString m_Title;
- AString m_SafeTitle;
- int m_UserData;
-
- cTab(const AString & a_Title, int a_UserData):
- m_Title(a_Title),
- m_SafeTitle(cWebPlugin::SafeString(a_Title)),
- m_UserData(a_UserData)
- {
- }
- };
-
- typedef SharedPtr<cTab> cTabPtr;
- typedef std::list<cTabPtr> cTabPtrs;
- typedef std::list<std::pair<AString, AString>> cTabNames;
-
-
- cWebPlugin();
-
- virtual ~cWebPlugin();
-
- // tolua_begin
-
- /** Returns the title of the plugin, as it should be presented in the webadmin's pages tree. */
- virtual const AString GetWebTitle(void) const = 0;
-
- /** Sanitizes the input string, replacing spaces with underscores. */
- static AString SafeString(const AString & a_String);
-
- // tolua_end
-
- virtual AString HandleWebRequest(const HTTPRequest & a_Request) = 0;
-
- /** Adds a new web tab with the specified contents. */
- void AddNewWebTab(const AString & a_Title, int a_UserData);
-
- /** Removes all the tabs. */
- void ClearTabs(void);
-
- /** Returns all the tabs that this plugin has registered. */
- const cTabPtrs & GetTabs(void) const { return m_Tabs; }
-
- /** Returns all of the tabs that this plugin has registered. */
- cTabNames GetTabNames(void) const; // Exported in ManualBindings.cpp
-
- /** Returns the tab that has the specified SafeTitle.
- Returns nullptr if no such tab. */
- cTabPtr GetTabBySafeTitle(const AString & a_SafeTitle) const;
-
- std::pair<AString, AString> GetTabNameForRequest(const HTTPRequest & a_Request);
-
-private:
- /** All tabs that this plugin has registered.
- Protected against multithreaded access by m_CSTabs. */
- cTabPtrs m_Tabs;
-
- /** Protects m_Tabs against multithreaded access. */
- mutable cCriticalSection m_CSTabs;
-}; // tolua_export
-
-
-
-
diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp
index e43d749dd..5c08deb0d 100644
--- a/src/WebAdmin.cpp
+++ b/src/WebAdmin.cpp
@@ -2,10 +2,6 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "WebAdmin.h"
-#include "Bindings/WebPlugin.h"
-
-#include "Bindings/PluginManager.h"
-#include "Bindings/Plugin.h"
#include "World.h"
#include "Entities/Player.h"
@@ -87,9 +83,9 @@ public:
// cWebAdmin:
cWebAdmin::cWebAdmin(void) :
+ m_TemplateScript("<webadmin_template>"),
m_IsInitialized(false),
- m_IsRunning(false),
- m_TemplateScript("<webadmin_template>")
+ m_IsRunning(false)
{
}
@@ -106,40 +102,9 @@ cWebAdmin::~cWebAdmin()
-void cWebAdmin::AddPlugin(cWebPlugin * a_Plugin)
-{
- m_Plugins.remove(a_Plugin);
- m_Plugins.push_back(a_Plugin);
-}
-
-
-
-
-
-void cWebAdmin::RemovePlugin(cWebPlugin * a_Plugin)
-{
- m_Plugins.remove(a_Plugin);
-}
-
-
-
-
-
bool cWebAdmin::Init(void)
{
- if (!m_IniFile.ReadFile("webadmin.ini"))
- {
- LOGWARN("Regenerating webadmin.ini, all settings will be reset");
- m_IniFile.AddHeaderComment(" This file controls the webadmin feature of Cuberite");
- m_IniFile.AddHeaderComment(" Username format: [User:*username*]");
- m_IniFile.AddHeaderComment(" Password format: Password=*password*; for example:");
- m_IniFile.AddHeaderComment(" [User:admin]");
- m_IniFile.AddHeaderComment(" Password=admin");
- m_IniFile.SetValue("WebAdmin", "Ports", DEFAULT_WEBADMIN_PORTS);
- m_IniFile.WriteFile("webadmin.ini");
- }
-
- if (!m_IniFile.GetValueSetB("WebAdmin", "Enabled", true))
+ if (!LoadIniFile())
{
// WebAdmin is disabled, bail out faking a success
return true;
@@ -147,31 +112,7 @@ bool cWebAdmin::Init(void)
LOGD("Initialising WebAdmin...");
- // Initialize the WebAdmin template script and load the file
- m_TemplateScript.Create();
- m_TemplateScript.RegisterAPILibs();
- if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua"))
- {
- LOGWARN("Could not load WebAdmin template \"%s\". WebAdmin disabled!", FILE_IO_PREFIX "webadmin/template.lua");
- m_TemplateScript.Close();
- m_HTTPServer.Stop();
- return false;
- }
-
- // Load the login template, provide a fallback default if not found:
- if (!LoadLoginTemplate())
- {
- LOGWARN("Could not load WebAdmin login template \"%s\", using fallback template.", FILE_IO_PREFIX "webadmin/login_template.html");
-
- // Sets the fallback template:
- m_LoginTemplate = \
- "<h1>Cuberite WebAdmin</h1>" \
- "<center>" \
- "<form method='get' action='webadmin/'>" \
- "<input type='submit' value='Log in'>" \
- "</form>" \
- "</center>";
- }
+ Reload();
// Read the ports to be used:
// Note that historically the ports were stored in the "Port" and "PortsIPv6" values
@@ -224,7 +165,7 @@ void cWebAdmin::Stop(void)
-bool cWebAdmin::LoadLoginTemplate(void)
+bool cWebAdmin::LoadLoginPage(void)
{
cFile File(FILE_IO_PREFIX "webadmin/login_template.html", cFile::fmRead);
if (!File.IsOpen())
@@ -238,7 +179,8 @@ bool cWebAdmin::LoadLoginTemplate(void)
return false;
}
- m_LoginTemplate = TemplateContent;
+ cCSLock Lock(m_CS);
+ m_LoginPage = TemplateContent;
return true;
}
@@ -246,6 +188,89 @@ bool cWebAdmin::LoadLoginTemplate(void)
+void cWebAdmin::RemoveAllPluginWebTabs(const AString & a_PluginName)
+{
+ cCSLock lock(m_CS);
+ m_WebTabs.erase(std::remove_if(m_WebTabs.begin(), m_WebTabs.end(), [=](cWebTabPtr a_CBWebTab)
+ {
+ return (a_CBWebTab->m_PluginName == a_PluginName);
+ }),
+ m_WebTabs.end()
+ );
+}
+
+
+
+
+
+void cWebAdmin::Reload(void)
+{
+ cCSLock lock(m_CS);
+ if (!LoadIniFile())
+ {
+ // We are asked to disable the webadmin, cannot do that, so warn the admin:
+ LOGWARNING(
+ "WebAdmin was previously enabled and now the settings say to disable it."
+ " This will not take effect until you restart the server."
+ );
+ }
+
+ // Initialize the WebAdmin template script and reload the file:
+ if (m_TemplateScript.IsValid())
+ {
+ m_TemplateScript.Close();
+ }
+ m_TemplateScript.Create();
+ m_TemplateScript.RegisterAPILibs();
+ if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua"))
+ {
+ LOGWARN("Could not load WebAdmin template \"%s\". WebAdmin will not work properly!", FILE_IO_PREFIX "webadmin/template.lua");
+ m_TemplateScript.Close();
+ }
+
+ // Load the login template, provide a fallback default if not found:
+ if (!LoadLoginPage())
+ {
+ LOGWARN("Could not load WebAdmin login page \"%s\", using fallback template.", FILE_IO_PREFIX "webadmin/login_template.html");
+
+ // Set the fallback:
+ m_LoginPage = \
+ "<h1>Cuberite WebAdmin</h1>" \
+ "<center>" \
+ "<form method='get' action='webadmin/'>" \
+ "<input type='submit' value='Log in'>" \
+ "</form>" \
+ "</center>";
+ }
+}
+
+
+
+
+
+bool cWebAdmin::LoadIniFile(void)
+{
+ m_IniFile.Clear();
+ if (!m_IniFile.ReadFile("webadmin.ini"))
+ {
+ LOGWARN("Regenerating webadmin.ini, all settings will be reset");
+ m_IniFile.AddHeaderComment(" This file controls the webadmin feature of Cuberite");
+ m_IniFile.AddHeaderComment(" It specifies whether webadmin is enabled, and what logins are allowed. ");
+ m_IniFile.AddHeaderComment(" Username format: [User:*username*]");
+ m_IniFile.AddHeaderComment(" Password format: Password=*password*; for example:");
+ m_IniFile.AddHeaderComment(" [User:admin]");
+ m_IniFile.AddHeaderComment(" Password=admin");
+ m_IniFile.SetValue("WebAdmin", "Ports", DEFAULT_WEBADMIN_PORTS);
+ m_IniFile.WriteFile("webadmin.ini");
+ }
+
+ return m_IniFile.GetValueSetB("WebAdmin", "Enabled", true);
+}
+
+
+
+
+
void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTTPIncomingRequest & a_Request)
{
if (!a_Request.HasAuth())
@@ -255,17 +280,20 @@ void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTT
}
// Check auth:
- AString UserPassword = m_IniFile.GetValue("User:" + a_Request.GetAuthUsername(), "Password", "");
- if ((UserPassword == "") || (a_Request.GetAuthPassword() != UserPassword))
{
- a_Connection.SendNeedAuth("Cuberite WebAdmin - bad username or password");
- return;
+ cCSLock Lock(m_CS);
+ AString UserPassword = m_IniFile.GetValue("User:" + a_Request.GetAuthUsername(), "Password", "");
+ if ((UserPassword == "") || (a_Request.GetAuthPassword() != UserPassword))
+ {
+ a_Connection.SendNeedAuth("Cuberite WebAdmin - bad username or password");
+ return;
+ }
}
// Check if the contents should be wrapped in the template:
auto BareURL = a_Request.GetURLPath();
ASSERT(BareURL.length() > 0);
- bool ShouldWrapInTemplate = ((BareURL.length() > 1) && (BareURL[1] != '~'));
+ bool ShouldWrapInTemplate = (!BareURL.empty() && (BareURL[1] != '~'));
// Retrieve the request data:
auto Data = std::static_pointer_cast<cWebadminRequestData>(a_Request.GetUserData());
@@ -312,6 +340,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTT
// Try to get the template from the Lua template script
if (ShouldWrapInTemplate)
{
+ cCSLock Lock(m_CS);
if (m_TemplateScript.Call("ShowPage", this, &TemplateRequest, cLuaState::Return, Template))
{
cHTTPOutgoingResponse Resp;
@@ -325,59 +354,12 @@ void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTT
return;
}
- AString BaseURL = GetBaseURL(BareURL);
- AString Menu;
- Template = "{CONTENT}";
- AString FoundPlugin;
-
- for (PluginList::iterator itr = m_Plugins.begin(); itr != 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 = GetPage(TemplateRequest.Request);
- AString Content = Page.Content;
- FoundPlugin = Page.PluginName;
- if (!Page.TabName.empty())
- {
- FoundPlugin += " - " + Page.TabName;
- }
-
- if (FoundPlugin.empty()) // Default page
- {
- Content = GetDefaultPage();
- }
-
- int MemUsageKiB = cRoot::GetPhysicalRAMUsage();
- if (MemUsageKiB > 0)
- {
- ReplaceString(Template, "{MEM}", Printf("%.02f", static_cast<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}", "Cuberite");
-
- AString NumChunks;
- Printf(NumChunks, "%d", cRoot::Get()->GetTotalChunkCount());
- ReplaceString(Template, "{NUMCHUNKS}", NumChunks);
-
- cHTTPOutgoingResponse Resp;
- Resp.SetContentType("text/html");
- a_Connection.Send(Resp);
- a_Connection.Send(Template.c_str(), Template.length());
+ // Send the un-decorated page content:
+ auto page = GetPage(TemplateRequest.Request);
+ cHTTPOutgoingResponse resp;
+ resp.SetContentType(page.ContentType);
+ a_Connection.Send(resp);
+ a_Connection.Send(page.Content.c_str(), page.Content.length());
a_Connection.FinishResponse();
}
@@ -392,7 +374,7 @@ void cWebAdmin::HandleRootRequest(cHTTPServerConnection & a_Connection, cHTTPInc
cHTTPOutgoingResponse Resp;
Resp.SetContentType("text/html");
a_Connection.Send(Resp);
- a_Connection.Send(m_LoginTemplate);
+ a_Connection.Send(m_LoginPage);
a_Connection.FinishResponse();
}
@@ -406,7 +388,7 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc
std::replace(FileURL.begin(), FileURL.end(), '\\', '/');
// Remove all leading backslashes:
- if (FileURL[0] == '/')
+ if (!FileURL.empty() && (FileURL[0] == '/'))
{
size_t FirstCharToRead = FileURL.find_first_not_of('/');
if (FirstCharToRead != AString::npos)
@@ -418,8 +400,9 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc
// Remove all "../" strings:
ReplaceString(FileURL, "../", "");
- bool LoadedSuccessfull = false;
+ // Read the file contents and guess its mime-type, based on the extension:
AString Content = "<h2>404 Not Found</h2>";
+ AString ContentType;
AString Path = Printf(FILE_IO_PREFIX "webadmin/files/%s", FileURL.c_str());
if (cFile::IsFile(Path))
{
@@ -427,18 +410,17 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc
AString FileContent;
if (File.IsOpen() && (File.ReadRestOfFile(FileContent) != -1))
{
- LoadedSuccessfull = true;
- Content = FileContent;
+ std::swap(Content, FileContent);
+ size_t LastPointPosition = Path.find_last_of('.');
+ if (LastPointPosition != AString::npos)
+ {
+ ContentType = GetContentTypeFromFileExt(Path.substr(LastPointPosition + 1));
+ }
}
}
-
- // Find content type (The currently method is very bad. We should change it later)
- AString ContentType = "text/html";
- size_t LastPointPosition = Path.find_last_of('.');
- if (LoadedSuccessfull && (LastPointPosition != AString::npos) && (LastPointPosition < Path.length()))
+ if (ContentType.empty())
{
- AString FileExtension = Path.substr(LastPointPosition + 1);
- ContentType = GetContentTypeFromFileExt(FileExtension);
+ ContentType = "application/unknown";
}
// Send the response:
@@ -456,32 +438,36 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc
AString cWebAdmin::GetContentTypeFromFileExt(const AString & a_FileExtension)
{
static bool IsInitialized = false;
- static std::map<AString, AString> ContentTypeMap;
+ static AStringMap ContentTypeMap;
if (!IsInitialized)
{
// Initialize the ContentTypeMap:
- ContentTypeMap["png"] = "image/png";
- ContentTypeMap["fif"] = "image/fif";
- ContentTypeMap["gif"] = "image/gif";
- ContentTypeMap["jpeg"] = "image/jpeg";
- ContentTypeMap["jpg"] = "image/jpeg";
- ContentTypeMap["jpe"] = "image/jpeg";
- ContentTypeMap["tiff"] = "image/tiff";
- ContentTypeMap["ico"] = "image/ico";
- ContentTypeMap["csv"] = "image/comma-separated-values";
- ContentTypeMap["css"] = "text/css";
- ContentTypeMap["js"] = "text/javascript";
- ContentTypeMap["txt"] = "text/plain";
- ContentTypeMap["rtx"] = "text/richtext";
- ContentTypeMap["xml"] = "text/xml";
- }
-
- AString FileExtension = StrToLower(a_FileExtension);
- if (ContentTypeMap.find(a_FileExtension) == ContentTypeMap.end())
- {
- return "text/html";
- }
- return ContentTypeMap[FileExtension];
+ ContentTypeMap["png"] = "image/png";
+ ContentTypeMap["fif"] = "image/fif";
+ ContentTypeMap["gif"] = "image/gif";
+ ContentTypeMap["jpeg"] = "image/jpeg";
+ ContentTypeMap["jpg"] = "image/jpeg";
+ ContentTypeMap["jpe"] = "image/jpeg";
+ ContentTypeMap["tiff"] = "image/tiff";
+ ContentTypeMap["ico"] = "image/ico";
+ ContentTypeMap["csv"] = "text/csv";
+ ContentTypeMap["css"] = "text/css";
+ ContentTypeMap["js"] = "text/javascript";
+ ContentTypeMap["txt"] = "text/plain";
+ ContentTypeMap["rtx"] = "text/richtext";
+ ContentTypeMap["rtf"] = "text/richtext";
+ ContentTypeMap["xml"] = "text/xml";
+ ContentTypeMap["html"] = "text/html";
+ ContentTypeMap["htm"] = "text/html";
+ ContentTypeMap["xhtml"] = "application/xhtml+xml"; // Not recomended for IE6, but no-one uses that anymore
+ }
+
+ auto itr = ContentTypeMap.find(StrToLower(a_FileExtension));
+ if (itr == ContentTypeMap.end())
+ {
+ return AString();
+ }
+ return itr->second;
}
@@ -490,86 +476,93 @@ AString cWebAdmin::GetContentTypeFromFileExt(const AString & a_FileExtension)
sWebAdminPage cWebAdmin::GetPage(const HTTPRequest & a_Request)
{
- sWebAdminPage Page;
- AStringVector Split = StringSplit(a_Request.Path, "/");
+ sWebAdminPage page;
+ auto split = StringSplit(a_Request.Path, "/");
+
+ // If no specific page was requested, return an empty object:
+ if (split.size() <= 2)
+ {
+ return page;
+ }
- // Find the plugin that corresponds to the requested path
- AString FoundPlugin;
- if (Split.size() > 1)
+ // Find the WebTab handler responsible for the request:
+ cWebTabPtr tab;
{
- for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr)
+ cCSLock Lock(m_CS);
+ for (auto & wt: m_WebTabs)
{
- if ((*itr)->GetWebTitle() == Split[1])
+ if (
+ (wt->m_PluginName == split[1]) &&
+ (wt->m_UrlPath == split[2])
+ )
{
- Page.Content = (*itr)->HandleWebRequest(a_Request);
- cWebPlugin * WebPlugin = *itr;
- FoundPlugin = WebPlugin->GetWebTitle();
- AString TabName = WebPlugin->GetTabNameForRequest(a_Request).first;
- Page.PluginName = FoundPlugin;
- Page.TabName = TabName;
+ tab = wt;
break;
}
+ } // for wt - m_WebTabs[]
+ }
+
+ // If a WebTab handler was found, call it:
+ if (tab != nullptr)
+ {
+ page.ContentType = "text/html"; // Default to HTML content type, unless overridden by a plugin
+ if (!tab->m_Callback->Call(a_Request, split[1], page.Content, page.ContentType))
+ {
+ page.Content = GetHTMLEscapedString(Printf(
+ "WebTab callback for plugin %s, page %s has failed.",
+ tab->m_PluginName.c_str(), tab->m_Title.c_str()
+ ));
}
+ page.PluginName = tab->m_PluginName;
+ page.TabTitle = tab->m_Title;
+ page.TabUrlPath = split[1];
}
- // Return the page contents
- return Page;
+ return page;
}
-AString cWebAdmin::GetDefaultPage(void)
+AString cWebAdmin::GetBaseURL(const AString & a_URL)
{
- AString Content;
- Content += "<h4>Server Name:</h4>";
- Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID()) + "</p>";
+ return GetBaseURL(StringSplit(a_URL, "/"));
+}
- // Display a list of all plugins:
- Content += "<h4>Plugins:</h4><ul>";
- struct cPluginCallback:
- public cPluginManager::cPluginCallback
- {
- AString & m_Content;
- cPluginCallback(AString & a_Content):
- m_Content(a_Content)
- {
- }
- virtual bool Item(cPlugin * a_Plugin) override
- {
- if (a_Plugin->IsLoaded())
- {
- AppendPrintf(m_Content, "<li>%s V.%i</li>", a_Plugin->GetName().c_str(), a_Plugin->GetVersion());
- }
- return false;
- }
- } Callback(Content);
- cPluginManager::Get()->ForEachPlugin(Callback);
- Content += "</ul>";
- // Display a list of all players:
- Content += "<h4>Players:</h4><ul>";
- cPlayerAccum PlayerAccum;
- cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players
- if (World != nullptr)
- {
- World->ForEachPlayer(PlayerAccum);
- Content.append(PlayerAccum.m_Contents);
- }
- Content += "</ul><br>";
- return Content;
+
+void cWebAdmin::AddWebTab(
+ const AString & a_Title,
+ const AString & a_UrlPath,
+ const AString & a_PluginName,
+ SharedPtr<cWebAdmin::cWebTabCallback> a_Callback
+)
+{
+ cCSLock lock(m_CS);
+ m_WebTabs.emplace_back(std::make_shared<cWebTab>(a_Title, a_UrlPath, a_PluginName, a_Callback));
}
-AString cWebAdmin::GetBaseURL(const AString & a_URL)
+bool cWebAdmin::DelWebTab(const AString & a_UrlPath)
{
- return GetBaseURL(StringSplit(a_URL, "/"));
+ cCSLock lock(m_CS);
+ for (auto itr = m_WebTabs.begin(), end = m_WebTabs.end(); itr != end; ++itr)
+ {
+ if ((*itr)->m_UrlPath == a_UrlPath)
+ {
+ m_WebTabs.erase(itr);
+ return true;
+ }
+ } // for itr - m_WebTabs[]
+
+ // Not found:
+ return false;
}
diff --git a/src/WebAdmin.h b/src/WebAdmin.h
index 5e48f597c..b76ca6df8 100644
--- a/src/WebAdmin.h
+++ b/src/WebAdmin.h
@@ -89,14 +89,14 @@ struct HTTPTemplateRequest
-// tolua_begin
struct sWebAdminPage
{
AString Content;
AString PluginName;
- AString TabName;
+ AString TabTitle;
+ AString TabUrlPath;
+ AString ContentType;
};
-// tolua_end
@@ -111,7 +111,49 @@ class cWebAdmin :
public:
// tolua_end
- typedef std::list< cWebPlugin* > PluginList;
+ /** Interface for getting the content of a single WebTab. */
+ class cWebTabCallback abstract
+ {
+ public:
+ // Force a virtual destructor in descendants
+ virtual ~cWebTabCallback() {}
+
+ /** Returns the contents for the specified request.
+ Returns true if the call was successful, false on an error.
+ a_Request is the full HTTP request object, as received from the client.
+ a_UrlPath is the UrlPath of the WebTab registered for this request, as parsed from a_Request.
+ Descendants should fill a_Content with the page contents
+ and optionally set a_ContentType [defaults to "text/html"] */
+ virtual bool Call(
+ const HTTPRequest & a_Request,
+ const AString & a_UrlPath,
+ AString & a_Content,
+ AString & a_ContentType
+ ) = 0;
+ };
+
+
+ /** Container for a single web tab.
+ Each web tab has a title, URL path and an associated plugin's name.
+ Each web tab is registered with a callback to provide the content. */
+ class cWebTab
+ {
+ public:
+ AString m_Title;
+ AString m_UrlPath;
+ AString m_PluginName;
+ SharedPtr<cWebTabCallback> m_Callback;
+
+ cWebTab(const AString & a_Title, const AString & a_UrlPath, const AString & a_PluginName, SharedPtr<cWebTabCallback> a_Callback):
+ m_Title(a_Title),
+ m_UrlPath(a_UrlPath),
+ m_PluginName(a_PluginName),
+ m_Callback(a_Callback)
+ {
+ }
+ };
+ typedef SharedPtr<cWebTab> cWebTabPtr;
+ typedef std::vector<cWebTabPtr> cWebTabPtrs;
cWebAdmin(void);
@@ -120,81 +162,115 @@ public:
/** 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 */
+ /** Starts the HTTP server taking care of the webadmin. Returns true if successful */
bool Start(void);
/** Stops the HTTP server, if it was started. */
void Stop(void);
- /** Loads the login template. Returns true if the loading succeeds, false if not. */
- bool LoadLoginTemplate(void);
-
- void AddPlugin(cWebPlugin * a_Plugin);
- void RemovePlugin(cWebPlugin * a_Plugin);
+ /** Loads the login template into m_LoginPage.
+ Returns true if the loading succeeds, false if not. */
+ bool LoadLoginPage(void);
- // 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 <<
+ /** Returns a copy of all the registered web tabs.
+ Exported to Lua in ManualBindings.cpp. */
+ cWebTabPtrs GetAllWebTabs(void) { return m_WebTabs; }
- // tolua_begin
+ /** Removes all WebTabs registered by the specified plugin. */
+ void RemoveAllPluginWebTabs(const AString & a_PluginName);
+ /** Returns the (inner) page contents for the specified request.
+ Calls the appropriate WebTab handler to get the contents.
+ Exported to Lua in ManualBindings.cpp. */
sWebAdminPage GetPage(const HTTPRequest & a_Request);
- /** Returns the contents of the default page - the list of plugins and players */
- AString GetDefaultPage(void);
+ // tolua_begin
- /** Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) */
- AString GetBaseURL(const AString & a_URL);
+ /** Reloads m_IniFile, m_LoginPage and m_TemplateScript.
+ Note that reloading will not change the "enabled" state of the server, and it will not update listening ports. */
+ void Reload(void);
- /** Returns the list of ports used for the webadmin. */
+ /** Returns the list of ports on which the webadmin is configured to listen. */
AString GetPorts(void) const { return StringsConcat(m_Ports, ','); }
-
- /** OBSOLETE: Returns the list of IPv4 ports used for the webadmin.
- Currently there is no distinction between IPv4 and IPv6; use GetPorts() instead. */
- AString GetIPv4Ports(void) const { return GetPorts(); }
-
- /** OBSOLETE: Returns the list of IPv6 ports used for the webadmin.
- Currently there is no distinction between IPv4 and IPv6; use GetPorts() instead. */
- AString GetIPv6Ports(void) const { return GetPorts(); }
-
// tolua_end
- /** Escapes text passed into it, so it can be embedded into html. */
+ /** Adds a new WebTab handler.
+ a_Title is the display title of the tab
+ a_UrlPath is the part of the URL that uniquely identifies this tab.
+ a_PluginName is the display name of the plugin creating this tab.
+ a_Callback is used to provide the actual WebTab contents, when requested.
+ Exported in ManualBindings.cpp. */
+ void AddWebTab(
+ const AString & a_Title,
+ const AString & a_UrlPath,
+ const AString & a_PluginName,
+ SharedPtr<cWebTabCallback> a_Callback
+ );
+
+ /** Removes the WebTab with the specified URL path.
+ Returns true if WebTab was found and removed, false if not found.
+ Exported in ManualBindings.cpp */
+ bool DelWebTab(const AString & a_UrlPath);
+
+ /** Escapes text passed into it, so it can be embedded into html.
+ Exported to Lua in ManualBindings.cpp. */
static AString GetHTMLEscapedString(const AString & a_Input);
- /** Escapes the string for use in an URL */
+ /** Escapes the string for use in an URL
+ Exported to Lua in ManualBindings.cpp. */
static AString GetURLEncodedString(const AString & a_Input);
+ /** Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style).
+ Exported to Lua in ManualBindings.cpp. */
+ static AString GetBaseURL(const AString & a_URL);
+
/** Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) */
static AString GetBaseURL(const AStringVector & a_URLSplit);
- /** Returns the content type from the file extension. If the extension isn't in the list, the function returns "text/html" */
+ /** Returns the content type from the file extension.
+ If the extension isn't in the list, the function returns an empty string.
+ Exported to Lua in ManualBindings.cpp. */
static AString GetContentTypeFromFileExt(const AString & a_FileExtension);
protected:
+ /** Protects m_WebTabs, m_TemplateScript, m_LoginTemplate and m_IniFile against multithreaded access. */
+ cCriticalSection m_CS;
+
+ /** All registered WebTab handlers.
+ Protected against multithreaded access by m_CS. */
+ cWebTabPtrs m_WebTabs;
+
+ /** The Lua template script to provide templates.
+ Protected against multithreaded access by m_CS. */
+ cLuaState m_TemplateScript;
+
+ /** The HTML page that provides the login.
+ Protected against multithreaded access by m_CS. */
+ AString m_LoginPage;
+
+ /** The webadmin.ini file, used for the settings and allowed logins.
+ Protected against multithreaded access by m_CS. */
+ cIniFile m_IniFile;
+
/** Set to true if Init() succeeds and the webadmin isn't to be disabled */
bool m_IsInitialized;
/** Set to true if Start() succeeds in starting the server, reset back to false in Stop(). */
bool m_IsRunning;
- /** The webadmin.ini file, used for the settings and allowed logins */
- cIniFile m_IniFile;
-
- PluginList m_Plugins;
-
/** The ports on which the webadmin is running. */
AStringVector m_Ports;
- /** The Lua template script to provide templates: */
- cLuaState m_TemplateScript;
-
- /** The template that provides the login site: */
- AString m_LoginTemplate;
-
/** The HTTP server which provides the underlying HTTP parsing, serialization and events */
cHTTPServer m_HTTPServer;
+
+ /** Loads webadmin.ini into m_IniFile.
+ Creates a default file if it doesn't exist.
+ Returns true if webadmin is enabled, false if disabled. */
+ bool LoadIniFile(void);
+
/** Handles requests coming to the "/webadmin" or "/~webadmin" URLs */
void HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTTPIncomingRequest & a_Request);