From c142424571a3744e8b68ec9746eaa05505c03af5 Mon Sep 17 00:00:00 2001 From: faketruth Date: Tue, 31 Jan 2012 00:38:18 +0000 Subject: [PATCH] Can now receive POST data in WebPlugins! Fixed Debug With optimized Noise in VS2010 by having it run the correct MCServer_debug.exe instead of MCServer.exe Changed winsock.h to Winsock2.h in Globals.h so sockets can be graciously closed (See webserver Socket::Close() ) git-svn-id: http://mc-server.googlecode.com/svn/trunk@197 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- Plugins/Core/web_reload.lua | 4 +- Plugins/Core/web_whitelist.lua | 16 +-- VC2010/MCServer.vcxproj.user | 7 ++ WebServer/Globals.h | 2 +- WebServer/Socket.cpp | 30 ++++- WebServer/Socket.h | 12 +- WebServer/WebServer.cpp | 217 +++++++++++++++++++-------------- WebServer/WebServer.h | 4 + source/Bindings.cpp | 33 ++++- source/Bindings.h | 2 +- source/cPlayer.cpp | 3 +- source/cWebAdmin.cpp | 2 + source/cWebAdmin.h | 1 + source/cWebPlugin_Lua.cpp | 8 +- 14 files changed, 224 insertions(+), 117 deletions(-) diff --git a/Plugins/Core/web_reload.lua b/Plugins/Core/web_reload.lua index f78297fcb..45ed1a40b 100644 --- a/Plugins/Core/web_reload.lua +++ b/Plugins/Core/web_reload.lua @@ -1,12 +1,12 @@ function HandleRequest_Reload( Request ) local Content = "" - if( Request.Params:get("reload") ~= "" ) then + if( Request.PostParams:get("reload") ~= "" ) then Content = Content .. "" Content = Content .. "

Reloading plugins... This can take a while depending on the plugins you're using.

" cRoot:Get():GetPluginManager():ReloadPlugins() else - Content = Content .. "
" + Content = Content .. "" Content = Content .. "

Click the reload button to reload all plugins!
" Content = Content .. "

" Content = Content .. "
" diff --git a/Plugins/Core/web_whitelist.lua b/Plugins/Core/web_whitelist.lua index b7a220515..1a99a9f95 100644 --- a/Plugins/Core/web_whitelist.lua +++ b/Plugins/Core/web_whitelist.lua @@ -1,11 +1,11 @@ local function HTMLDeleteButton( name ) - return "
" + return "
" end function HandleRequest_WhiteList( Request ) local UpdateMessage = "" - if( Request.Params:get("whitelist-add") ~= "" ) then - local PlayerName = Request.Params:get("whitelist-add") + if( Request.PostParams:get("whitelist-add") ~= "" ) then + local PlayerName = Request.PostParams:get("whitelist-add") if( WhiteListIni:GetValueB("WhiteList", PlayerName, false) == true ) then UpdateMessage = "".. PlayerName.." is already on the whitelist" @@ -14,12 +14,12 @@ function HandleRequest_WhiteList( Request ) UpdateMessage = "Added " .. PlayerName .. " to whitelist." WhiteListIni:WriteFile() end - elseif( Request.Params:get("whitelist-delete") ~= "" ) then - local PlayerName = Request.Params:get("whitelist-delete") + elseif( Request.PostParams:get("whitelist-delete") ~= "" ) then + local PlayerName = Request.PostParams:get("whitelist-delete") WhiteListIni:DeleteValue( "WhiteList", PlayerName ) UpdateMessage = "Removed " .. PlayerName .. " from whitelist." WhiteListIni:WriteFile() - elseif( Request.Params:get("whitelist-reload") ~= "" ) then + elseif( Request.PostParams:get("whitelist-reload") ~= "" ) then WhiteListIni:Erase() -- Empty entire loaded ini first, otherwise weird shit goes down WhiteListIni:ReadFile() UpdateMessage = "Loaded from disk" @@ -63,10 +63,10 @@ function HandleRequest_WhiteList( Request ) end Content = Content .. "" Content = Content .. "

Add player to whitelist

" - Content = Content .. "
" + Content = Content .. "" Content = Content .. "" Content = Content .. "
" - Content = Content .. "
" + Content = Content .. "" Content = Content .. "" Content = Content .. "
" Content = Content .. "
"..UpdateMessage diff --git a/VC2010/MCServer.vcxproj.user b/VC2010/MCServer.vcxproj.user index 1fb9362b4..61612b15b 100644 --- a/VC2010/MCServer.vcxproj.user +++ b/VC2010/MCServer.vcxproj.user @@ -9,4 +9,11 @@ $(OutDir) WindowsLocalDebugger + + $(TargetDir)\$(TargetName)_debug$(TargetExt) + + + $(OutDir) + WindowsLocalDebugger + \ No newline at end of file diff --git a/WebServer/Globals.h b/WebServer/Globals.h index 027b988ef..9e74f351d 100644 --- a/WebServer/Globals.h +++ b/WebServer/Globals.h @@ -12,7 +12,7 @@ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include - #include + #include #else #include #include // for mkdir diff --git a/WebServer/Socket.cpp b/WebServer/Socket.cpp index ec3c9a5c7..e2a5179f3 100644 --- a/WebServer/Socket.cpp +++ b/WebServer/Socket.cpp @@ -118,11 +118,20 @@ Socket& Socket::operator=(Socket& o) { return *this; } -void Socket::Close() { +void Socket::Close( bool a_WaitSend /* = false */ ) +{ if( s_ ) { - closesocket(s_); - s_ = 0; + if( a_WaitSend ) + { + assert( shutdown(s_, SD_SEND ) == 0 ); + char c; + while( recv(s_, &c, 1, 0 ) != 0 ) + {} + } + + closesocket(s_); + s_ = 0; } } @@ -135,12 +144,25 @@ std::string Socket::ReceiveLine() { { return ""; } - ret += r; if (r == '\n') return ret; } } +std::string Socket::ReceiveBytes( unsigned int a_Length ) { + std::string ret; + while( ret.size() < a_Length ) { + char r; + + if (recv(s_, &r, 1, 0) <= 0 ) + { + return ""; + } + ret += r; + } + return ret; +} + void Socket::SendLine(std::string s) { s += '\n'; if( send(s_,s.c_str(),s.length(),MSG_NOSIGNAL) <= 0 ) diff --git a/WebServer/Socket.h b/WebServer/Socket.h index c26822e34..339dae188 100644 --- a/WebServer/Socket.h +++ b/WebServer/Socket.h @@ -31,11 +31,13 @@ #ifndef SOCKET_H #define SOCKET_H - + #include "../source/MCSocket.h" // #ifdef _WIN32 -// #include -// #endif +// #include + +// #endif + #include @@ -49,9 +51,9 @@ public: Socket& operator=(Socket&); std::string ReceiveLine(); - std::string ReceiveBytes(); + std::string ReceiveBytes( unsigned int a_Length ); - void Close(); + void Close( bool a_WaitSend = false ); // The parameter of SendLine is not a const reference // because SendLine modifes the std::string passed. diff --git a/WebServer/WebServer.cpp b/WebServer/WebServer.cpp index 466093fd3..5de1385a2 100644 --- a/WebServer/WebServer.cpp +++ b/WebServer/WebServer.cpp @@ -12,12 +12,12 @@ freely, subject to the following restrictions: 1. The origin of this source code must not be misrepresented; you must not - claim that you wrote the original source code. If you use this source code - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original source code. + misrepresented as being the original source code. 3. This notice may not be removed or altered from any source distribution. @@ -55,121 +55,160 @@ unsigned webserver::Request(void* ptr_s) void* webserver::Request(void* ptr_s) #endif { - Socket* s = (reinterpret_cast(ptr_s)); + Socket* s = (reinterpret_cast(ptr_s)); - std::string line = s->ReceiveLine(); - if (line.empty()) { - s->Close(); - delete s; - return 0; - } + std::string line = s->ReceiveLine(); + if (line.empty()) + { + s->Close(); + delete s; + return 0; + } - http_request req; + http_request req; + std::string path; + std::map params; + size_t posStartPath = 0; - if (line.find("GET") == 0) { - req.method_="GET"; - } - else if (line.find("POST") == 0) { - req.method_="POST"; - } + if (line.find("GET") == 0) + { + req.method_="GET"; + posStartPath = line.find_first_not_of(" ",3); + } + else if (line.find("POST") == 0) + { + req.method_="POST"; + posStartPath = line.find_first_not_of(" ",4); + } - std::string path; - std::map params; + SplitGetReq(line.substr(posStartPath), path, params); - size_t posStartPath = line.find_first_not_of(" ",3); + req.status_ = "202 OK"; + req.s_ = s; + req.path_ = path; + req.params_ = params; - SplitGetReq(line.substr(posStartPath), path, params); + static const std::string authorization = "Authorization: Basic "; + static const std::string accept = "Accept: " ; + static const std::string accept_language = "Accept-Language: " ; + static const std::string accept_encoding = "Accept-Encoding: " ; + static const std::string user_agent = "User-Agent: " ; + static const std::string content_length = "Content-Length: " ; + static const std::string content_type = "Content-Type: " ; - req.status_ = "202 OK"; - req.s_ = s; - req.path_ = path; - req.params_ = params; + while(1) + { + line=s->ReceiveLine(); + if (line.empty()) break; - static const std::string authorization = "Authorization: Basic "; - static const std::string accept = "Accept: " ; - static const std::string accept_language = "Accept-Language: " ; - static const std::string accept_encoding = "Accept-Encoding: " ; - static const std::string user_agent = "User-Agent: " ; + unsigned int pos_cr_lf = line.find_first_of("\x0a\x0d"); + if (pos_cr_lf == 0) break; - while(1) { - line=s->ReceiveLine(); + line = line.substr(0,pos_cr_lf); - if (line.empty()) break; + if (line.substr(0, authorization.size()) == authorization) + { + req.authentication_given_ = true; + std::string encoded = line.substr(authorization.size()); + std::string decoded = base64_decode(encoded); - unsigned int pos_cr_lf = line.find_first_of("\x0a\x0d"); - if (pos_cr_lf == 0) break; + unsigned int pos_colon = decoded.find(":"); - line = line.substr(0,pos_cr_lf); + req.username_ = decoded.substr(0, pos_colon); + req.password_ = decoded.substr(pos_colon+1 ); + } + else if (line.substr(0, accept.size()) == accept) + { + req.accept_ = line.substr(accept.size()); + } + else if (line.substr(0, accept_language.size()) == accept_language) + { + req.accept_language_ = line.substr(accept_language.size()); + } + else if (line.substr(0, accept_encoding.size()) == accept_encoding) + { + req.accept_encoding_ = line.substr(accept_encoding.size()); + } + else if (line.substr(0, user_agent.size()) == user_agent) + { + req.user_agent_ = line.substr(user_agent.size()); + } + else if (line.substr(0, content_length.size()) == content_length) + { + req.content_length_ = atoi( line.substr(content_length.size()).c_str() ); + } + else if (line.substr(0, content_type.size()) == content_type) + { + req.content_type_ = line.substr(content_type.size()); + } + } - if (line.substr(0, authorization.size()) == authorization) { - req.authentication_given_ = true; - std::string encoded = line.substr(authorization.size()); - std::string decoded = base64_decode(encoded); + if( req.method_.compare("POST") == 0 ) + { + if( req.content_length_ > 0 ) + { + // The only content type we can parse at the moment, the default HTML post data + if( req.content_type_.compare( "application/x-www-form-urlencoded" ) == 0 ) + { + std::string Content = s->ReceiveBytes( req.content_length_ ); + Content.insert( 0, "/ ?" ); // Little hack, inserts dummy URL so that SplitGetReq() can work with this content - unsigned int pos_colon = decoded.find(":"); + std::string dummy; + std::map post_params; + SplitGetReq(Content, dummy, post_params); - req.username_ = decoded.substr(0, pos_colon); - req.password_ = decoded.substr(pos_colon+1 ); - } - else if (line.substr(0, accept.size()) == accept) { - req.accept_ = line.substr(accept.size()); - } - else if (line.substr(0, accept_language.size()) == accept_language) { - req.accept_language_ = line.substr(accept_language.size()); - } - else if (line.substr(0, accept_encoding.size()) == accept_encoding) { - req.accept_encoding_ = line.substr(accept_encoding.size()); - } - else if (line.substr(0, user_agent.size()) == user_agent) { - req.user_agent_ = line.substr(user_agent.size()); - } - } + req.params_post_ = post_params; + } + } + } - request_func_(&req); + request_func_(&req); - std::stringstream str_str; - str_str << req.answer_.size(); + std::stringstream str_str; + str_str << req.answer_.size(); - time_t ltime; - time(<ime); - tm* gmt= gmtime(<ime); + time_t ltime; + time(<ime); + tm* gmt= gmtime(<ime); #ifdef _WIN32 - static std::string const serverName = "MCServerWebAdmin (Windows)"; + static std::string const serverName = "MCServerWebAdmin (Windows)"; #elif __APPLE__ - static std::string const serverName = "MCServerWebAdmin (MacOSX)"; + static std::string const serverName = "MCServerWebAdmin (MacOSX)"; #else - static std::string const serverName = "MCServerWebAdmin (Linux)"; + static std::string const serverName = "MCServerWebAdmin (Linux)"; #endif - char* asctime_remove_nl = std::asctime(gmt); - asctime_remove_nl[24] = 0; + char* asctime_remove_nl = std::asctime(gmt); + asctime_remove_nl[24] = 0; - s->SendBytes("HTTP/1.1 "); + s->SendBytes("HTTP/1.1 "); - if (! req.auth_realm_.empty() ) { - s->SendLine("401 Unauthorized"); - s->SendBytes("WWW-Authenticate: Basic Realm=\""); - s->SendBytes(req.auth_realm_); - s->SendLine("\""); - } - else { - s->SendLine(req.status_); - } - s->SendLine(std::string("Date: ") + asctime_remove_nl + " GMT"); - s->SendLine(std::string("Server: ") +serverName); - s->SendLine("Connection: close"); - s->SendLine("Content-Type: text/html; charset=ISO-8859-1"); - s->SendLine("Content-Length: " + str_str.str()); - s->SendLine(""); - s->SendLine(req.answer_); + if (! req.auth_realm_.empty() ) + { + s->SendLine("401 Unauthorized"); + s->SendBytes("WWW-Authenticate: Basic Realm=\""); + s->SendBytes(req.auth_realm_); + s->SendLine("\""); + } + else + { + s->SendLine(req.status_); + } + s->SendLine(std::string("Date: ") + asctime_remove_nl + " GMT"); + s->SendLine(std::string("Server: ") +serverName); + s->SendLine("Connection: close"); + s->SendLine("Content-Type: text/html; charset=ISO-8859-1"); + s->SendLine("Content-Length: " + str_str.str()); + s->SendLine(""); + s->SendLine(req.answer_); - s->Close(); - delete s; + s->Close( true ); // true = wait for all data to be sent before closing + delete s; - return 0; + return 0; } void webserver::Stop() diff --git a/WebServer/WebServer.h b/WebServer/WebServer.h index 844af0c46..a78fc44e4 100644 --- a/WebServer/WebServer.h +++ b/WebServer/WebServer.h @@ -41,17 +41,21 @@ public: http_request() : s_( 0 ) , authentication_given_(false) + , content_length_( 0 ) {} Socket* s_; std::string method_; std::string path_; std::map params_; + std::map params_post_; std::string accept_; std::string accept_language_; std::string accept_encoding_; std::string user_agent_; + int content_length_; + std::string content_type_; /* status_: used to transmit server's error status, such as o 202 OK diff --git a/source/Bindings.cpp b/source/Bindings.cpp index 8d484fa1f..6462036be 100644 --- a/source/Bindings.cpp +++ b/source/Bindings.cpp @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 01/30/12 17:26:14. +** Generated automatically by tolua++-1.0.92 on 01/31/12 01:23:11. */ #ifndef __cplusplus @@ -10453,6 +10453,36 @@ static int tolua_set_HTTPRequest_Params_ptr(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* get function: PostParams of class HTTPRequest */ +#ifndef TOLUA_DISABLE_tolua_get_HTTPRequest_PostParams_ptr +static int tolua_get_HTTPRequest_PostParams_ptr(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'PostParams'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)self->PostParams,"cStringMap"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: PostParams of class HTTPRequest */ +#ifndef TOLUA_DISABLE_tolua_set_HTTPRequest_PostParams_ptr +static int tolua_set_HTTPRequest_PostParams_ptr(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'PostParams'",NULL); + if (!tolua_isusertype(tolua_S,2,"cStringMap",0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->PostParams = ((cStringMap*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + /* get function: Username of class HTTPRequest */ #ifndef TOLUA_DISABLE_tolua_get_HTTPRequest_Username static int tolua_get_HTTPRequest_Username(lua_State* tolua_S) @@ -17001,6 +17031,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_variable(tolua_S,"Method",tolua_get_HTTPRequest_Method,tolua_set_HTTPRequest_Method); tolua_variable(tolua_S,"Path",tolua_get_HTTPRequest_Path,tolua_set_HTTPRequest_Path); tolua_variable(tolua_S,"Params",tolua_get_HTTPRequest_Params_ptr,tolua_set_HTTPRequest_Params_ptr); + tolua_variable(tolua_S,"PostParams",tolua_get_HTTPRequest_PostParams_ptr,tolua_set_HTTPRequest_PostParams_ptr); tolua_variable(tolua_S,"Username",tolua_get_HTTPRequest_Username,tolua_set_HTTPRequest_Username); tolua_endmodule(tolua_S); #ifdef __cplusplus diff --git a/source/Bindings.h b/source/Bindings.h index b2ff3c1c8..6e1a7b46c 100644 --- a/source/Bindings.h +++ b/source/Bindings.h @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 01/30/12 17:26:15. +** Generated automatically by tolua++-1.0.92 on 01/31/12 01:23:11. */ /* Exported function */ diff --git a/source/cPlayer.cpp b/source/cPlayer.cpp index 8e161900c..23c43ae25 100644 --- a/source/cPlayer.cpp +++ b/source/cPlayer.cpp @@ -767,8 +767,7 @@ void cPlayer::LoadPermissionsFromDisk() - -bool cPlayer::LoadFromDisk() // TODO - This should also get/set/whatever the correct world for this player +bool cPlayer::LoadFromDisk() { LoadPermissionsFromDisk(); diff --git a/source/cWebAdmin.cpp b/source/cWebAdmin.cpp index 70f89528c..005a85424 100644 --- a/source/cWebAdmin.cpp +++ b/source/cWebAdmin.cpp @@ -136,6 +136,7 @@ void cWebAdmin::Request_Handler(webserver::http_request* r) Request.Username = r->username_; Request.Method = r->method_; Request.Params = new cStringMap(r->params_); + Request.PostParams = new cStringMap(r->params_post_); Request.Path = r->path_; if( Split.size() > 1 ) @@ -158,6 +159,7 @@ void cWebAdmin::Request_Handler(webserver::http_request* r) } delete Request.Params; + delete Request.PostParams; if( FoundPlugin.empty() ) // Default page { diff --git a/source/cWebAdmin.h b/source/cWebAdmin.h index 5a43b4877..da60a5492 100644 --- a/source/cWebAdmin.h +++ b/source/cWebAdmin.h @@ -10,6 +10,7 @@ struct HTTPRequest std::string Method; std::string Path; cStringMap* Params; + cStringMap* PostParams; std::string Username; }; //tolua_end diff --git a/source/cWebPlugin_Lua.cpp b/source/cWebPlugin_Lua.cpp index 40e8fcb76..d1446ba24 100644 --- a/source/cWebPlugin_Lua.cpp +++ b/source/cWebPlugin_Lua.cpp @@ -76,13 +76,13 @@ std::string cWebPlugin_Lua::HandleRequest( HTTPRequest* a_Request ) if( Tab ) { - LOGINFO("1. Stack size: %i", lua_gettop(LuaState) ); + //LOGINFO("1. Stack size: %i", lua_gettop(LuaState) ); lua_rawgeti( LuaState, LUA_REGISTRYINDEX, Tab->Reference); // same as lua_getref() - LOGINFO("2. Stack size: %i", lua_gettop(LuaState) ); + //LOGINFO("2. Stack size: %i", lua_gettop(LuaState) ); // Push HTTPRequest tolua_pushusertype( LuaState, a_Request, "HTTPRequest" ); - LOGINFO("Calling bound function! :D"); + //LOGINFO("Calling bound function! :D"); int s = lua_pcall( LuaState, 1, 1, 0); if ( s != 0 ) @@ -103,7 +103,7 @@ std::string cWebPlugin_Lua::HandleRequest( HTTPRequest* a_Request ) RetVal += tolua_tostring(LuaState, -1, 0); lua_pop(LuaState, 1); // Pop return value - LOGINFO("ok. Stack size: %i", lua_gettop(LuaState) ); + //LOGINFO("ok. Stack size: %i", lua_gettop(LuaState) ); } return RetVal;