From 968f41ba511f8f74377b1a1c7b61ff759ebe3078 Mon Sep 17 00:00:00 2001 From: faketruth Date: Tue, 31 Jan 2012 20:56:42 +0000 Subject: Changed how Lua handles the (Post)Params in the HTTPRequest of a WebPlugin It should now be theoretically possible to upload files through WebAdmin git-svn-id: http://mc-server.googlecode.com/svn/trunk@203 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- WebServer/WebServer.cpp | 230 +++++++++++++++++++++++++++++++++++++++++++++--- WebServer/WebServer.h | 9 ++ 2 files changed, 226 insertions(+), 13 deletions(-) (limited to 'WebServer') diff --git a/WebServer/WebServer.cpp b/WebServer/WebServer.cpp index 5de1385a2..3a2b6fbef 100644 --- a/WebServer/WebServer.cpp +++ b/WebServer/WebServer.cpp @@ -49,6 +49,206 @@ webserver::request_func webserver::request_func_=0; +static std::vector< std::string > StringSplit(std::string str, std::string delim) +{ + std::vector< std::string > results; + size_t cutAt; + while( (cutAt = str.find_first_of(delim)) != str.npos ) + { + if(cutAt > 0) + { + results.push_back(str.substr(0,cutAt)); + } + str = str.substr(cutAt+1); + } + if(str.length() > 0) + { + results.push_back(str); + } + return results; +} + +static std::string EatLine( std::string& a_String ) +{ + std::string RetVal = ""; + unsigned int StringSize = a_String.size(); + const char* c = a_String.c_str(); + + for( unsigned int i = 0; i < StringSize; ++i, ++c) + { + if( *c == '\n' ) + { + RetVal += *c; +// ++i; ++c; +// if( i < StringSize ) +// { +// if( *c == '\r' ) +// { +// RetVal += *c; +// } +// } + break; + } + RetVal += *c; + } + a_String = a_String.substr( RetVal.size() ); + return RetVal; +} + +// Turns +// "blabla my string with \"quotes\"!" +// into +// blabla my string with "quotes"! +static std::string GetQuotedString( const std::string& a_String ) +{ + std::string RetVal; + + bool bGotFirstQuote = false; + bool bIgnoreNext = false; + unsigned int StrLen = a_String.size(); + for( unsigned int i = 0; i < StrLen; ++i ) + { + if( bIgnoreNext == false ) + { + if( a_String[i] == '\"' ) + { + if( bGotFirstQuote == false ) + { + bGotFirstQuote = true; + } + else + { + break; + } + continue; + } + else if( a_String[i] == '\\' ) // Escape character + { + bIgnoreNext = true; + continue; + } + } + RetVal.push_back( a_String[i] ); + bIgnoreNext = false; + } + + return RetVal; +} + +void ParseMultipartFormData( webserver::http_request& req, Socket* s) +{ + static const std::string multipart_form_data = "multipart/form-data"; + if(req.content_type_.substr(0, multipart_form_data.size()) == multipart_form_data) // Difficult data... :( + { + typedef std::vector< std::string > StringVector; + StringVector ContentTypeData = StringSplit( req.content_type_, "; " ); + + std::string boundary; + // Find boundary + for( unsigned int i = 0; i < ContentTypeData.size(); ++i ) + { + static const std::string boundary_ = "boundary="; + if( ContentTypeData[i].substr(0, boundary_.size()) == boundary_ ) // Found boundary + { + boundary = ContentTypeData[i].substr( boundary_.size() ); + } + } + + //LOGINFO("Boundary: %s", boundary.c_str() ); + std::string boundary_start = "--" + boundary; + std::string boundary_end = boundary_start + "--"; + + std::string Content = s->ReceiveBytes( req.content_length_ ); + + //printf("Total content: \n%s\n", Content.c_str() ); + + // Should start with boundary! + std::string line = EatLine( Content ); + if( line.substr(0, boundary_start.size() ) != boundary_start ) + { + // Something was wrong! :( + Content.clear(); + } + + while( !Content.empty() ) + { + webserver::formdata FormData; + + static const std::string content_disposition = "Content-Disposition: "; + static const std::string content_type = "Content-Type: "; + + std::string f_disposition; + + while( 1 ) + { + std::string line = EatLine( Content ); + if( line.empty() ) + break; + + unsigned int pos_cr_lf = line.find_first_of("\x0a\x0d"); + if (pos_cr_lf == 0) break; // Empty line, indicates end of mime thingy + + if( line.substr(0, content_disposition.size() ) == content_disposition ) + { + f_disposition = line.substr(content_disposition.size()); + LOGINFO("Disposition: %s", f_disposition.c_str() ); + } + else if( line.substr(0, content_type.size() ) == content_type ) + { + FormData.content_type_ = line.substr(content_type.size()); + } + + //LOGINFO("Got line: '%s'", line.c_str() ); + } + + // Check if we got the proper headers + if( !f_disposition.empty() ) + { + static const std::string disp_name = "name="; + static const std::string disp_filename = "filename="; + + // Parse the disposition + StringVector DispositionData = StringSplit( f_disposition, "; " ); + for( unsigned int i = 0; i < DispositionData.size(); ++i ) + { + if( DispositionData[i].substr(0, disp_name.size()) == disp_name ) + { + FormData.name_ = GetQuotedString( DispositionData[i].substr(disp_name.size()) ); + } + else if( DispositionData[i].substr(0, disp_filename.size()) == disp_filename ) + { + FormData.filename_ = GetQuotedString( DispositionData[i].substr(disp_filename.size()) ); + } + } + + std::string ContentValue; + // Parse until boundary_end is found + while( 1 ) + { + std::string line = EatLine( Content ); + if( line.empty() ) + break; + + if( line.substr(0, boundary_end.size() ) == boundary_end ) + { + break; + } + else if( line.substr(0, boundary_start.size() ) == boundary_start ) + { + break; + } + ContentValue.append( line.c_str(), line.size() ); + } + + + FormData.value_ = ContentValue; + } + + req.multipart_formdata_.push_back( FormData ); + } + } +} + #ifdef _WIN32 unsigned webserver::Request(void* ptr_s) #else @@ -99,7 +299,10 @@ void* webserver::Request(void* ptr_s) while(1) { line=s->ReceiveLine(); - if (line.empty()) break; + if (line.empty()) + { + break; + } unsigned int pos_cr_lf = line.find_first_of("\x0a\x0d"); if (pos_cr_lf == 0) break; @@ -143,22 +346,23 @@ void* webserver::Request(void* ptr_s) } } - if( req.method_.compare("POST") == 0 ) + if( (req.method_.compare("POST") == 0) && (req.content_length_ > 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 ) { - // 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 + std::string Content = s->ReceiveBytes( req.content_length_ ); + Content.insert( 0, "/ ?" ); // Little hack, inserts dummy URL so that SplitGetReq() can work with this content - std::string dummy; - std::map post_params; - SplitGetReq(Content, dummy, post_params); + std::string dummy; + std::map post_params; + SplitGetReq(Content, dummy, post_params); - req.params_post_ = post_params; - } + req.params_post_ = post_params; + } + else + { + ParseMultipartFormData( req, s ); } } diff --git a/WebServer/WebServer.h b/WebServer/WebServer.h index a78fc44e4..90b38d407 100644 --- a/WebServer/WebServer.h +++ b/WebServer/WebServer.h @@ -36,6 +36,14 @@ class Socket; class SocketServer; class webserver { public: + struct formdata + { + std::string name_; + std::string filename_; + std::string content_type_; + std::string value_; + }; + struct http_request { http_request() @@ -56,6 +64,7 @@ public: std::string user_agent_; int content_length_; std::string content_type_; + std::vector< formdata > multipart_formdata_; /* status_: used to transmit server's error status, such as o 202 OK -- cgit v1.2.3