ServerWebServer.cpp

Go to the documentation of this file.
00001 ////////////////////////////////////////////////////////////////////////////////
00002 //    Scorched3D (c) 2000-2003
00003 //
00004 //    This file is part of Scorched3D.
00005 //
00006 //    Scorched3D is free software; you can redistribute it and/or modify
00007 //    it under the terms of the GNU General Public License as published by
00008 //    the Free Software Foundation; either version 2 of the License, or
00009 //    (at your option) any later version.
00010 //
00011 //    Scorched3D is distributed in the hope that it will be useful,
00012 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 //    GNU General Public License for more details.
00015 //
00016 //    You should have received a copy of the GNU General Public License
00017 //    along with Scorched3D; if not, write to the Free Software
00018 //    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 ////////////////////////////////////////////////////////////////////////////////
00020 
00021 #include <stdlib.h>
00022 #include <string.h>
00023 #include <time.h>
00024 #include <webserver/ServerWebServer.h>
00025 #include <webserver/ServerWebHandler.h>
00026 #include <webserver/ServerWebSettingsHandler.h>
00027 #include <webserver/ServerWebAppletHandler.h>
00028 #include <server/ServerChannelManager.h>
00029 #include <server/ServerCommon.h>
00030 #include <webserver/ServerWebServerUtil.h>
00031 #include <server/ScorchedServer.h>
00032 #include <net/NetMessagePool.h>
00033 #include <common/OptionsScorched.h>
00034 #include <common/Logger.h>
00035 #include <common/LoggerI.h>
00036 #include <common/Defines.h>
00037 
00038 ServerWebServer *ServerWebServer::instance_ = 0;
00039 
00040 ServerWebServer *ServerWebServer::instance()
00041 {
00042         if (!instance_)
00043         {
00044                 instance_ = new ServerWebServer();
00045         }
00046         return instance_;
00047 }
00048 
00049 ServerWebServer::ServerWebServer() : 
00050         netServer_(new NetServerHTTPProtocolRecv),
00051         logger_(0), asyncTimer_(0)
00052 {
00053         sendThread_ = SDL_CreateThread(ServerWebServer::sendThreadFunc, 0);
00054         if (sendThread_ == 0)
00055         {
00056                 Logger::log(S3D::formatStringBuffer("ServerWebServer: Failed to create thread"));
00057         }       
00058 
00059         addRequestHandler("/players", new ServerWebHandler::PlayerHandler());
00060         addThrededRequestHandler("/playersthreaded", new ServerWebHandler::PlayerHandlerThreaded());
00061         addRequestHandler("/logs", new ServerWebHandler::LogHandler());
00062         addRequestHandler("/logfile", new ServerWebHandler::LogFileHandler());
00063         addRequestHandler("/game", new ServerWebHandler::GameHandler());
00064         addRequestHandler("/server", new ServerWebHandler::ServerHandler());
00065         addRequestHandler("/talk", new ServerWebAppletHandler::AppletHtmlHandler());
00066         addRequestHandler("/banned", new ServerWebHandler::BannedHandler());
00067         addRequestHandler("/mods", new ServerWebHandler::ModsHandler());
00068         addRequestHandler("/sessions", new ServerWebHandler::SessionsHandler());
00069         addRequestHandler("/account", new ServerWebHandler::AccountHandler());
00070         addRequestHandler("/stats", new ServerWebHandler::StatsHandler());
00071         addRequestHandler("/settingsmain", new ServerWebSettingsHandler::SettingsMainHandler());
00072         addRequestHandler("/settingsall", new ServerWebSettingsHandler::SettingsAllHandler());
00073         addRequestHandler("/settingslandscape", new ServerWebSettingsHandler::SettingsLandscapeHandler());
00074         addRequestHandler("/settingsplayers", new ServerWebSettingsHandler::SettingsPlayersHandler());
00075         addRequestHandler("/settingsmod", new ServerWebSettingsHandler::SettingsModHandler());
00076         addRequestHandler("/Applet.jar", new ServerWebAppletHandler::AppletFileHandler());
00077         addAsyncRequestHandler("/appletstream", new ServerWebAppletHandler::AppletAsyncHandler());
00078         addRequestHandler("/action", new ServerWebAppletHandler::AppletActionHandler());
00079 }
00080 
00081 ServerWebServer::~ServerWebServer()
00082 {
00083 }
00084 
00085 int ServerWebServer::sendThreadFunc(void *)
00086 {
00087         while (true)
00088         {
00089                 SDL_Delay(100);
00090 
00091                 // Process the threaded queue
00092                 instance_->processQueue(instance_->threadedQueue_, false);              
00093         }
00094         return 1;
00095 }
00096 
00097 void ServerWebServer::start(int port)
00098 {
00099         Logger::log(S3D::formatStringBuffer("Starting management web server on port %i", port));
00100         netServer_.setMessageHandler(this);
00101         netServer_.start(port);
00102 
00103         if (0 != strcmp(ScorchedServer::instance()->getOptionsGame().
00104                 getServerFileLogger(), "none"))
00105         {
00106                 logger_ = new FileLogger(
00107                         S3D::formatStringBuffer("ServerWeb-%i-",
00108                                 ScorchedServer::instance()->getOptionsGame().getPortNo()));
00109         }
00110 }
00111 
00112 void ServerWebServer::addRequestHandler(const char *url,
00113         ServerWebServerI *handler)
00114 {
00115         HandlerEntry entry = { handler, 0 };
00116         handlers_[url] = entry;
00117 }
00118 
00119 void ServerWebServer::addThrededRequestHandler(const char *url,
00120         ServerWebServerI *handler)
00121 {
00122         HandlerEntry entry = { handler, HandlerEntry::eThreaded };
00123         handlers_[url] = entry;
00124 }
00125 
00126 void ServerWebServer::addAsyncRequestHandler(const char *url,
00127         ServerWebServerI *handler)
00128 {
00129         HandlerEntry entry = { handler, HandlerEntry::eAsync };
00130         handlers_[url] = entry;
00131 }
00132 
00133 void ServerWebServer::processMessages()
00134 {
00135         // Check if any delayed messages should be sent
00136         while (!delayedMessages_.empty())
00137         {
00138                 unsigned int theTime = (unsigned int) time(0);
00139                 std::pair<unsigned int, NetMessage *> &delayedMessage =
00140                         delayedMessages_.front();
00141                 if (delayedMessage.first <= theTime)
00142                 {
00143                         // Get the message
00144                         NetMessage *message = delayedMessage.second;
00145                         delayedMessages_.pop_front();
00146 
00147                         // Send this message now
00148                         netServer_.sendMessageDest(message->getBuffer(), message->getDestinationId());
00149                         NetMessagePool::instance()->addToPool(message);
00150                 }
00151                 else break;
00152         }
00153 
00154         // Check if any non-delayed messages should be processed
00155         netServer_.processMessages();
00156 
00157         // Check if anything needs to be done for the async processing
00158         unsigned int theTime = (unsigned int) time(0);
00159         if (theTime != asyncTimer_)
00160         {
00161                 asyncTimer_ = theTime;
00162                 processQueue(asyncQueue_, true);
00163         }
00164 }
00165 
00166 void ServerWebServer::processMessage(NetMessage &message)
00167 {
00168         if (message.getMessageType() == NetMessage::BufferMessage)
00169         {
00170                 // We have received a request for the web server
00171 
00172                 // Add a NULL to the end of the buffer so we can 
00173                 // do string searches on it and they dont run out of
00174                 // the buffer.
00175                 message.getBuffer().addToBuffer("");
00176                 const char *buffer = message.getBuffer().getBuffer();
00177                 
00178                 // Check it is a GET
00179                 bool ok = false;
00180                 bool get = (strstr(buffer, "GET ") == buffer);
00181                 bool post = (strstr(buffer, "POST ") == buffer);
00182                 if (get || post)
00183                 {
00184                         std::map<std::string, std::string> fields;
00185                         std::map<std::string, NetMessage *> parts;
00186                 
00187                         // Get POST query fields if any
00188                         if (post)
00189                         {
00190                                 // Find the end of the header
00191                                 char *headerend = (char *) strstr(buffer, "\r\n\r\n");
00192                                 if (headerend)
00193                                 {
00194                                         // Try to find the multipart POST information
00195                                         // in the header only
00196                                         // (So make the headerend a null to bound the search)
00197                                         headerend[0] = '\0';
00198                                         const char *findStr = "Content-Type: multipart/form-data; boundary=";
00199                                         const char *multipart = strstr(buffer, findStr);
00200                                         headerend[0] = '\r';
00201                                         if (multipart)
00202                                         {
00203                                                 // We have a multipart message
00204                                                 // Get the boundry type
00205                                                 const char *boundryStart = multipart + strlen(findStr);
00206                                                 char *boundrysep = (char *) strstr(boundryStart, "\r\n");
00207                                                 if (boundrysep)
00208                                                 {
00209                                                         // Get the multi-part boundry
00210                                                         boundrysep[0] = '\0';
00211                                                         std::string boundry = boundryStart;
00212                                                         boundrysep[0] = '\r';
00213 
00214                                                         // Extract the multi-part data from after the header
00215                                                         headerend += 4; // Skip past /r/n/r/n
00216                                                         int headersize = headerend - buffer;
00217                                                         int sizeleft = message.getBuffer().getBufferUsed() - headersize;
00218 
00219                                                         ServerWebServerUtil::extractMultiPartPost(headerend, boundry.c_str(), sizeleft, parts);
00220                                                 }
00221                                         }
00222                                         else
00223                                         {
00224                                                 // Extract the query fields from after the header
00225                                                 headerend += 4; // Skip past /r/n/r/n
00226                                                 ServerWebServerUtil::extractQueryFields(fields, headerend);
00227                                         }
00228                                 }
00229                         }
00230                 
00231                         // Check it has a url
00232                         const char *url = buffer + (get?4:5);
00233                         char *eol = (char *) strchr(url, ' ');
00234                         if (eol)
00235                         {
00236                                 *eol = '\0';
00237                                 if (*url)
00238                                 {
00239                                         // Get GET query fields if any
00240                                         char *sep = (char *) strchr(url, '?');
00241                                         if (sep)
00242                                         {
00243                                                 *sep = '\0'; sep++;
00244                                                 ServerWebServerUtil::extractQueryFields(fields, sep);
00245                                         }
00246 
00247                                         // Add ip address into fields
00248                                         fields["ipaddress"] = 
00249                                                 NetInterface::getIpName(message.getIpAddress());
00250 
00251                                         // Log info
00252                                         if (logger_)
00253                                         {
00254                                                 time_t t = time(0);
00255                                                 std::string f;
00256                                                 std::map<std::string, std::string>::iterator itor;
00257                                                 for (itor = fields.begin();
00258                                                         itor != fields.end();
00259                                                         itor++)
00260                                                 {
00261                                                         if (0 != strcmp((*itor).first.c_str(), "password"))
00262                                                         {
00263                                                                 f += S3D::formatStringBuffer("%s=%s ",
00264                                                                         (*itor).first.c_str(),
00265                                                                         (*itor).second.c_str());
00266                                                         }
00267                                                 }
00268 
00269                                                 std::string username;
00270                                                 if (fields.find("sid") != fields.end())
00271                                                 {
00272                                                         unsigned int sid = (unsigned int) atoi(fields["sid"].c_str());
00273                                                         ServerAdminSessions::SessionParams *session =
00274                                                                 ServerAdminSessions::instance()->getSession(sid);
00275                                                         if (session)
00276                                                         {
00277                                                                 username = session->credentials.username;
00278                                                         }
00279                                                 }
00280 
00281                                                 LoggerInfo info(
00282                                                         S3D::formatStringBuffer("%u %s http://%s [%s]", 
00283                                                         message.getDestinationId(), 
00284                                                         username.c_str(), url, f.c_str()),
00285                                                         ctime(&t));
00286                                                 logger_->logMessage(info);
00287                                         }
00288                                         
00289                                         // Process request
00290                                         const char *ipaddress = NetInterface::getIpName(message.getIpAddress());
00291                                         ok = processRequest(message.getDestinationId(), ipaddress, url, fields, parts);
00292                                 }
00293                         }
00294 
00295                         // Add any message parts back to the pool
00296                         std::map<std::string, NetMessage *>::iterator partitor;
00297                         for (partitor = parts.begin();
00298                                 partitor != parts.end();
00299                                 partitor++)
00300                         {
00301                                 NetMessage *newMessage = (*partitor).second;
00302                                 NetMessagePool::instance()->addToPool(newMessage);
00303                         }
00304                 }
00305 
00306                 if (!ok)
00307                 {
00308                         // Disconnect the client
00309                         netServer_.disconnectClient(message.getDestinationId());
00310                 }
00311         }
00312         else if (message.getMessageType() == NetMessage::SentMessage)
00313         {
00314                 // Check if this is a sync or async destination
00315                 if (asyncQueue_.hasEntry(message.getDestinationId()))
00316                 {
00317                         // Its an async destination do nothing
00318                 }
00319                 else
00320                 {
00321                         // Its a sync destination
00322                         // Once a sync message has been fully sent close the connection
00323                         netServer_.disconnectClient(message.getDestinationId());
00324                 }
00325         }
00326         else if (message.getMessageType() == NetMessage::DisconnectMessage)
00327         {
00328                 // Remove any async processes we may be processing for this 
00329                 // destination
00330                 asyncQueue_.removeEntry(message.getDestinationId());
00331         }
00332 }
00333 
00334 bool ServerWebServer::processRequest(
00335         unsigned int destinationId,
00336         const char *ip,
00337         const char *url,
00338         std::map<std::string, std::string> &fields,
00339         std::map<std::string, NetMessage *> &parts)
00340 {
00341         bool delayed = false; // Set delayed on authentication failure
00342         std::string text;
00343         if (0 == strcmp(url, "/"))
00344         {
00345                 // We have requested the login page
00346                 // Have the login credentials been supplied
00347                 if (validateUser(ip, url, fields))
00348                 {
00349                         // Yes, and credentials are correct
00350                         // Show the starting (players) page
00351                         ServerWebServerUtil::getHtmlRedirect(
00352                                 S3D::formatStringBuffer("/players?sid=%s", fields["sid"].c_str()), text);
00353                 }
00354                 else
00355                 {
00356                         // No, or credentials are not correct
00357                         // Show the login page after a delay
00358                         if (!ServerWebServerUtil::getHtmlTemplate(
00359                                 0, "login.html", fields, text)) return false;
00360                         delayed = true;
00361                 }
00362         }
00363         else
00364         {
00365                 // A "normal" page has been requested
00366                 // Check the session is valid
00367                 unsigned int sid = validateSession(ip, url, fields);
00368                 if (sid)
00369                 {
00370                         // The session is valid, show the page
00371                         std::map<std::string, HandlerEntry>::iterator itor = 
00372                                 handlers_.find(url);
00373                         if (itor == handlers_.end())
00374                         {
00375                                 ServerWebServerUtil::getHtmlNotFound(text);
00376                         }
00377                         else
00378                         {
00379                                 ServerWebServerI *handler = itor->second.handler->createCopy();
00380                                 ServerWebServerQueueEntry *entry = new ServerWebServerQueueEntry(
00381                                                 destinationId, sid, url, handler, fields, parts);
00382 
00383                                 if (itor->second.flags == HandlerEntry::eAsync)
00384                                 {
00385                                         asyncQueue_.addEntry(entry);
00386                                 }
00387                                 else if (itor->second.flags == HandlerEntry::eThreaded)
00388                                 {
00389                                         threadedQueue_.addEntry(entry);
00390                                 }
00391                                 else 
00392                                 {
00393                                         normalQueue_.addEntry(entry);
00394                                         if (!processQueue(normalQueue_, false)) return false;
00395                                 }
00396                                 return true;
00397                         }
00398                 }
00399                 else
00400                 {
00401                         if (handlers_.find(url) == handlers_.end())
00402                         {
00403                                 // The session is invalid,
00404                                 // but the page does not exist show the 404 page.
00405                                 // This is for cases where the browser asks for stupid files
00406                                 // from the webserver that must fail for the browser to continue.
00407                                 ServerWebServerUtil::getHtmlNotFound(text);
00408                         }
00409                         else
00410                         {
00411                                 // The session is invalid show the login page after a delay
00412                                 ServerWebServerUtil::getHtmlRedirect("/", text);
00413                                 delayed = true;
00414                         }
00415                 }
00416         }
00417 
00418         // Check the text is not empty
00419         if (text.empty()) return false;
00420 
00421         // Generate the message to send
00422         NetMessage *message = NetMessagePool::instance()->getFromPool(
00423                 NetMessage::BufferMessage, destinationId, 0, 0);
00424         message->getBuffer().addDataToBuffer(text.data(), text.size()); // No null
00425         if (delayed)
00426         {
00427                 // Generate an outgoing message, that will be sent after a time delay
00428                 unsigned int delayedTime = (unsigned int) time(0) + 5;
00429                 std::pair<unsigned int, NetMessage *> delayedMessage(delayedTime, message);
00430                 delayedMessages_.push_back(delayedMessage);
00431         }
00432         else
00433         {
00434                 // Send this message now
00435                 netServer_.sendMessageDest(message->getBuffer(), message->getDestinationId());
00436                 NetMessagePool::instance()->addToPool(message);
00437         }
00438         
00439         return true;
00440 }
00441 
00442 unsigned int ServerWebServer::validateSession(
00443         const char *ip,
00444         const char *url,
00445         std::map<std::string, std::string> &fields)
00446 {
00447         // Hack for silly java 6.0 applets
00448         if (strcmp(url, "/Applet.jar") == 0)
00449         {
00450                 ServerAdminSessions::SessionParams *session =
00451                         ServerAdminSessions::instance()->getFirstSession();
00452                 if (session)
00453                 {
00454                         return session->sid;
00455                 }
00456         }
00457 
00458         // Check this sid is valid
00459         if (fields.find("sid") != fields.end())
00460         {
00461                 unsigned int sid = (unsigned int) atoi(fields["sid"].c_str());
00462                 ServerAdminSessions::SessionParams *params =
00463                         ServerAdminSessions::instance()->getSession(sid);
00464                 if (params) return sid;
00465         }
00466 
00467         return 0;
00468 }
00469 
00470 bool ServerWebServer::validateUser(
00471         const char *ip,
00472         const char *url,
00473         std::map<std::string, std::string> &fields)
00474 {
00475         // Create a session for the user
00476         unsigned int sid = ServerAdminSessions::instance()->login(
00477                 fields["name"].c_str(),
00478                 fields["password"].c_str(),
00479                 ip);
00480         if (sid != 0)
00481         {
00482                 // Set the sid for use in the html templates
00483                 fields["sid"] = S3D::formatStringBuffer("%u", sid);
00484 
00485                 ServerAdminSessions::SessionParams *adminSession =
00486                         ServerAdminSessions::instance()->getSession(sid);
00487 
00488                 ServerChannelManager::instance()->sendText(
00489                         ChannelText("info",
00490                                 "ADMIN_WEB_LOGIN",
00491                                 "server admin \"{0}\" logged in",
00492                                 adminSession->credentials.username.c_str()),
00493                         true);
00494 
00495                 return true;
00496         }
00497         else
00498         {
00499                 Logger::log(S3D::formatStringBuffer("Failed login for server admin \"%s\", via web, ip \"%s\"",
00500                         fields["name"].c_str(), ip));
00501         }
00502 
00503         return false;
00504 }
00505 
00506 bool ServerWebServer::processQueue(ServerWebServerQueue &queue, bool keepEntries)
00507 {
00508         bool result = true;
00509         std::list<ServerWebServerQueueEntry *> keptEntries;
00510 
00511         // Process queue
00512         ServerWebServerQueueEntry *entry = 0;
00513         while ((entry = queue.getEntry()) != 0)
00514         {
00515                 bool keepEntry = keepEntries;
00516 
00517                 // Get the session for the user
00518                 ServerAdminSessions::SessionParams *session = ServerAdminSessions::instance()->getSession(
00519                         entry->getSid());
00520                 entry->getRequest().setSession(session);
00521 
00522                 // Call handler
00523                 std::string resultText;
00524                 if (session &&
00525                         entry->getHandler()->processRequest(
00526                         entry->getRequest(), resultText))
00527                 {
00528                         if (!resultText.empty())
00529                         {
00530                                 // It has generated some text
00531                                 // Generate the message to send
00532                                 NetMessage *message = NetMessagePool::instance()->getFromPool(
00533                                         NetMessage::BufferMessage, entry->getDestinationId(), 0, 0);
00534                                 message->getBuffer().addDataToBuffer(resultText.data(), resultText.size()); // No null
00535 
00536                                 // Send this message now
00537                                 netServer_.sendMessageDest(message->getBuffer(), message->getDestinationId());
00538                                 NetMessagePool::instance()->addToPool(message);
00539 
00540                                 // Update the session time so we don't timeout
00541                                 ServerAdminSessions::instance()->getSession(entry->getSid());
00542                         }
00543                         else
00544                         {
00545                                 result = false;
00546                         }
00547                 }
00548                 else
00549                 {
00550                         keepEntry = false;
00551                         result = false;
00552                 }
00553 
00554                 // Tidy queue entry
00555                 if (keepEntry) keptEntries.push_back(entry);
00556                 else delete entry;
00557         }
00558 
00559         // Keep entries
00560         while (!keptEntries.empty())
00561         {
00562                 entry = keptEntries.front();
00563                 keptEntries.pop_front();
00564                 queue.addEntry(entry);
00565         }
00566 
00567         return result;
00568 }

Generated on Mon Feb 16 15:14:53 2009 for Scorched3D by  doxygen 1.5.3