diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Bindings/AllToLua.pkg | 1 | ||||
-rw-r--r-- | src/Bindings/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/Bindings/ManualBindings.cpp | 282 | ||||
-rw-r--r-- | src/Bindings/PluginLua.cpp | 64 | ||||
-rw-r--r-- | src/Bindings/PluginLua.h | 15 | ||||
-rw-r--r-- | src/Bindings/PluginManager.cpp | 17 | ||||
-rw-r--r-- | src/Bindings/PluginManager.h | 3 | ||||
-rw-r--r-- | src/Bindings/WebPlugin.cpp | 152 | ||||
-rw-r--r-- | src/Bindings/WebPlugin.h | 80 | ||||
-rw-r--r-- | src/WebAdmin.cpp | 415 | ||||
-rw-r--r-- | src/WebAdmin.h | 158 |
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); |