ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

localwebserver.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #define FORBIDDEN_SYMBOL_ALLOW_ALL
00024 
00025 #include "backends/networking/sdl_net/localwebserver.h"
00026 #include "backends/networking/sdl_net/getclienthandler.h"
00027 #include "common/memstream.h"
00028 #include "common/str.h"
00029 #include "common/system.h"
00030 #include "common/timer.h"
00031 #include "common/translation.h"
00032 #include <SDL_net.h>
00033 #include <common/config-manager.h>
00034 
00035 #ifdef POSIX
00036 #include <errno.h>
00037 #include <unistd.h>
00038 #include <arpa/inet.h>
00039 #include <net/if.h>
00040 #include <netinet/in.h>
00041 #include <sys/ioctl.h>
00042 #include <sys/socket.h>
00043 #include <sys/types.h>
00044 
00045 #ifndef SIOCGIFCONF
00046 #include <sys/sockio.h>
00047 #endif
00048 
00049 #ifndef _SIZEOF_ADDR_IFREQ
00050 #define _SIZEOF_ADDR_IFREQ sizeof
00051 #endif
00052 
00053 #define LSSDP_BUFFER_LEN 2048
00054 #endif
00055 
00056 namespace Common {
00057 class MemoryReadWriteStream;
00058 
00059 DECLARE_SINGLETON(Networking::LocalWebserver);
00060 
00061 }
00062 
00063 namespace Networking {
00064 
00065 LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerStarted(false),
00066     _stopOnIdle(false), _minimalMode(false), _clients(0), _idlingFrames(0), _serverPort(DEFAULT_SERVER_PORT) {
00067     addPathHandler("/", &_indexPageHandler);
00068     addPathHandler("/files", &_filesPageHandler);
00069     addPathHandler("/create", &_createDirectoryHandler);
00070     addPathHandler("/download", &_downloadFileHandler);
00071     addPathHandler("/upload", &_uploadFileHandler);
00072     addPathHandler("/list", &_listAjaxHandler);
00073     addPathHandler("/filesAJAX", &_filesAjaxPageHandler);
00074     _defaultHandler = &_resourceHandler;
00075 }
00076 
00077 LocalWebserver::~LocalWebserver() {
00078     stop();
00079 }
00080 
00081 void localWebserverTimer(void *ignored) {
00082     LocalServer.handle();
00083 }
00084 
00085 void LocalWebserver::startTimer(int interval) {
00086     Common::TimerManager *manager = g_system->getTimerManager();
00087     if (manager->installTimerProc(localWebserverTimer, interval, 0, "Networking::LocalWebserver's Timer")) {
00088         _timerStarted = true;
00089     } else {
00090         warning("Failed to install Networking::LocalWebserver's timer");
00091     }
00092 }
00093 
00094 void LocalWebserver::stopTimer() {
00095     Common::TimerManager *manager = g_system->getTimerManager();
00096     manager->removeTimerProc(localWebserverTimer);
00097     _timerStarted = false;
00098 }
00099 
00100 void LocalWebserver::start(bool useMinimalMode) {
00101     _handleMutex.lock();
00102     _serverPort = getPort();
00103     _stopOnIdle = false;
00104     if (_timerStarted) {
00105         _handleMutex.unlock();
00106         return;
00107     }
00108     _minimalMode = useMinimalMode;
00109     startTimer();
00110 
00111     // Create a listening TCP socket
00112     IPaddress ip;
00113     if (SDLNet_ResolveHost(&ip, NULL, _serverPort) == -1) {
00114         error("LocalWebserver: SDLNet_ResolveHost: %s\n", SDLNet_GetError());
00115     }
00116 
00117     resolveAddress(&ip);
00118 
00119     _serverSocket = SDLNet_TCP_Open(&ip);
00120     if (!_serverSocket) {
00121         warning("LocalWebserver: SDLNet_TCP_Open: %s", SDLNet_GetError());
00122         stopTimer();
00123         g_system->displayMessageOnOSD(_("Failed to start local webserver.\nCheck whether selected port is not used by another application and try again."));
00124         _handleMutex.unlock();
00125         return;
00126     }
00127 
00128     // Create a socket set
00129     _set = SDLNet_AllocSocketSet(MAX_CONNECTIONS + 1); //one more for our server socket
00130     if (!_set) {
00131         error("LocalWebserver: SDLNet_AllocSocketSet: %s\n", SDLNet_GetError());
00132     }
00133 
00134     int numused = SDLNet_TCP_AddSocket(_set, _serverSocket);
00135     if (numused == -1) {
00136         error("LocalWebserver: SDLNet_AddSocket: %s\n", SDLNet_GetError());
00137     }
00138     _handleMutex.unlock();
00139 }
00140 
00141 void LocalWebserver::stop() {
00142     _handleMutex.lock();
00143     if (_timerStarted)
00144         stopTimer();
00145 
00146     if (_serverSocket) {
00147         SDLNet_TCP_Close(_serverSocket);
00148         _serverSocket = nullptr;
00149     }
00150 
00151     for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
00152         _client[i].close();
00153 
00154     _clients = 0;
00155 
00156     if (_set) {
00157         SDLNet_FreeSocketSet(_set);
00158         _set = nullptr;
00159     }
00160     _handleMutex.unlock();
00161 }
00162 
00163 void LocalWebserver::stopOnIdle() { _stopOnIdle = true; }
00164 
00165 void LocalWebserver::addPathHandler(Common::String path, BaseHandler *handler) {
00166     if (_pathHandlers.contains(path))
00167         warning("LocalWebserver::addPathHandler: path already had a handler");
00168     _pathHandlers[path] = handler;
00169 }
00170 
00171 Common::String LocalWebserver::getAddress() { return _address;  }
00172 
00173 IndexPageHandler &LocalWebserver::indexPageHandler() { return _indexPageHandler; }
00174 
00175 bool LocalWebserver::isRunning() {
00176     bool result = false;
00177     _handleMutex.lock();
00178     result = _timerStarted;
00179     _handleMutex.unlock();
00180     return result;
00181 }
00182 
00183 uint32 LocalWebserver::getPort() {
00184 #ifdef NETWORKING_LOCALWEBSERVER_ENABLE_PORT_OVERRIDE
00185     if (ConfMan.hasKey("local_server_port"))
00186         return ConfMan.getInt("local_server_port");
00187 #endif
00188     return DEFAULT_SERVER_PORT;
00189 }
00190 
00191 void LocalWebserver::handle() {
00192     _handleMutex.lock();
00193     int numready = SDLNet_CheckSockets(_set, 0);
00194     if (numready == -1) {
00195         error("LocalWebserver: SDLNet_CheckSockets: %s\n", SDLNet_GetError());
00196     } else if (numready) {
00197         acceptClient();
00198     }
00199 
00200     for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
00201         handleClient(i);
00202 
00203     _clients = 0;
00204     for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
00205         if (_client[i].state() != INVALID)
00206             ++_clients;
00207 
00208     if (_clients == 0)
00209         ++_idlingFrames;
00210     else
00211         _idlingFrames = 0;
00212 
00213     if (_idlingFrames > FRAMES_PER_SECOND && _stopOnIdle) {
00214         _handleMutex.unlock();
00215         stop();
00216         return;
00217     }
00218 
00219     _handleMutex.unlock();
00220 }
00221 
00222 void LocalWebserver::handleClient(uint32 i) {
00223     switch (_client[i].state()) {
00224     case INVALID:
00225         return;
00226     case READING_HEADERS:
00227         _client[i].readHeaders();
00228         break;
00229     case READ_HEADERS: {
00230         // decide what to do next with that client
00231         // check whether we know a handler for such URL
00232         BaseHandler *handler = nullptr;
00233         if (_pathHandlers.contains(_client[i].path())) {
00234             handler = _pathHandlers[_client[i].path()];
00235         } else {
00236             // try default handler
00237             handler = _defaultHandler;
00238         }
00239 
00240         // if server's in "minimal mode", only handlers which support it are used
00241         if (handler && (!_minimalMode || handler->minimalModeSupported()))
00242             handler->handle(_client[i]);
00243 
00244         if (_client[i].state() == BEING_HANDLED || _client[i].state() == INVALID)
00245             break;
00246 
00247         // if no handler, answer with default BAD REQUEST
00248     }
00249     // fall through
00250 
00251     case BAD_REQUEST:
00252         setClientGetHandler(_client[i], "<html><head><title>ScummVM - Bad Request</title></head><body>BAD REQUEST</body></html>", 400);
00253         break;
00254     case BEING_HANDLED:
00255         _client[i].handle();
00256         break;
00257     }
00258 }
00259 
00260 void LocalWebserver::acceptClient() {
00261     if (!SDLNet_SocketReady(_serverSocket))
00262         return;
00263 
00264     TCPsocket client = SDLNet_TCP_Accept(_serverSocket);
00265     if (!client)
00266         return;
00267 
00268     if (_clients == MAX_CONNECTIONS) { //drop the connection
00269         SDLNet_TCP_Close(client);
00270         return;
00271     }
00272 
00273     ++_clients;
00274     for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
00275         if (_client[i].state() == INVALID) {
00276             _client[i].open(_set, client);
00277             break;
00278         }
00279 }
00280 
00281 void LocalWebserver::resolveAddress(void *ipAddress) {
00282     IPaddress *ip = (IPaddress *)ipAddress;
00283 
00284     // not resolved
00285     _address = Common::String::format("http://127.0.0.1:%u/ (unresolved)", _serverPort);
00286 
00287     // default way (might work everywhere, surely works on Windows)
00288     const char *name = SDLNet_ResolveIP(ip);
00289     if (name == NULL) {
00290         warning("LocalWebserver: SDLNet_ResolveIP: %s", SDLNet_GetError());
00291     } else {
00292         IPaddress localIp;
00293         if (SDLNet_ResolveHost(&localIp, name, _serverPort) == -1) {
00294             warning("LocalWebserver: SDLNet_ResolveHost: %s", SDLNet_GetError());
00295         } else {
00296             _address = Common::String::format(
00297                 "http://%u.%u.%u.%u:%u/",
00298                 localIp.host & 0xFF, (localIp.host >> 8) & 0xFF, (localIp.host >> 16) & 0xFF, (localIp.host >> 24) & 0xFF,
00299                 _serverPort
00300             );
00301         }
00302     }
00303 
00304     // check that our trick worked
00305     if (_address.contains("/127.0.0.1:") || _address.contains("localhost") || _address.contains("/0.0.0.0:"))
00306         warning("LocalWebserver: Failed to resolve IP with the default way");
00307     else
00308         return;
00309 
00310     // if not - try platform-specific
00311 #ifdef POSIX
00312     void *tmpAddrPtr = NULL;
00313 
00314     int fd = socket(AF_INET, SOCK_DGRAM, 0);
00315     if (fd < 0) {
00316         warning("LocalWebserver: failed to create socket: %s (%d)", strerror(errno), errno);
00317     } else {
00318         // get ifconfig
00319         char buffer[LSSDP_BUFFER_LEN] = {};
00320         struct ifconf ifc;
00321         ifc.ifc_len = sizeof(buffer);
00322         ifc.ifc_buf = (caddr_t) buffer;
00323 
00324         if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
00325             warning("LocalWebserver: ioctl SIOCGIFCONF failed: %s (%d)", strerror(errno), errno);
00326         } else {
00327             struct ifreq *i;
00328             for (size_t index = 0; index < (size_t)ifc.ifc_len; index += _SIZEOF_ADDR_IFREQ(*i)) {
00329                 i = (struct ifreq *)(buffer + index);
00330 
00331                 Common::String addr;
00332 
00333                 // IPv4
00334                 if (i->ifr_addr.sa_family == AF_INET) {
00335                     tmpAddrPtr = &((struct sockaddr_in *)&i->ifr_addr)->sin_addr;
00336                     char addressBuffer[INET_ADDRSTRLEN];
00337                     inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
00338                     debug(9, "%s IP Address %s", i->ifr_name, addressBuffer);
00339                     addr = addressBuffer;
00340                 }
00341 
00342                 // IPv6
00343                 /*
00344                 if (i->ifr_addr.sa_family == AF_INET6) {
00345                     tmpAddrPtr = &((struct sockaddr_in6 *)&i->ifr_addr)->sin6_addr;
00346                     char addressBuffer[INET6_ADDRSTRLEN];
00347                     inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
00348                     debug(9, "%s IP Address %s", i->ifr_name, addressBuffer);
00349                     addr = addressBuffer;
00350                 }
00351                 */
00352 
00353                 if (addr.empty())
00354                     continue;
00355 
00356                 // ignored IPv4 addresses
00357                 if (addr.equals("127.0.0.1") || addr.equals("0.0.0.0") || addr.equals("localhost"))
00358                     continue;
00359 
00360                 // ignored IPv6 addresses
00361                 /*
00362                 if (addr.equals("::1"))
00363                     continue;
00364                 */
00365 
00366                 // use the address found
00367                 _address = "http://" + addr + Common::String::format(":%u/", _serverPort);
00368             }
00369         }
00370 
00371         // close socket
00372         if (close(fd) != 0) {
00373             warning("LocalWebserver: failed to close socket [fd %d]: %s (%d)", fd, strerror(errno), errno);
00374         }
00375     }
00376 #endif
00377 }
00378 
00379 void LocalWebserver::setClientGetHandler(Client &client, Common::String response, long code, const char *mimeType) {
00380     byte *data = new byte[response.size()];
00381     memcpy(data, response.c_str(), response.size());
00382     Common::MemoryReadStream *stream = new Common::MemoryReadStream(data, response.size(), DisposeAfterUse::YES);
00383     setClientGetHandler(client, stream, code, mimeType);
00384 }
00385 
00386 void LocalWebserver::setClientGetHandler(Client &client, Common::SeekableReadStream *responseStream, long code, const char *mimeType) {
00387     GetClientHandler *handler = new GetClientHandler(responseStream);
00388     handler->setResponseCode(code);
00389     if (mimeType)
00390         handler->setHeader("Content-Type", mimeType);
00391     client.setHandler(handler);
00392 }
00393 
00394 void LocalWebserver::setClientRedirectHandler(Client &client, Common::String response, Common::String location, const char *mimeType) {
00395     byte *data = new byte[response.size()];
00396     memcpy(data, response.c_str(), response.size());
00397     Common::MemoryReadStream *stream = new Common::MemoryReadStream(data, response.size(), DisposeAfterUse::YES);
00398     setClientRedirectHandler(client, stream, location, mimeType);
00399 }
00400 
00401 void LocalWebserver::setClientRedirectHandler(Client &client, Common::SeekableReadStream *responseStream, Common::String location, const char *mimeType) {
00402     GetClientHandler *handler = new GetClientHandler(responseStream);
00403     handler->setResponseCode(302); //redirect
00404     handler->setHeader("Location", location);
00405     if (mimeType)
00406         handler->setHeader("Content-Type", mimeType);
00407     client.setHandler(handler);
00408 }
00409 
00410 namespace {
00411 int hexDigit(char c) {
00412     if ('0' <= c && c <= '9') return c - '0';
00413     if ('A' <= c && c <= 'F') return c - 'A' + 10;
00414     if ('a' <= c && c <= 'f') return c - 'a' + 10;
00415     return -1;
00416 }
00417 }
00418 
00419 Common::String LocalWebserver::urlDecode(Common::String value) {
00420     Common::String result = "";
00421     uint32 size = value.size();
00422     for (uint32 i = 0; i < size; ++i) {
00423         if (value[i] == '+') {
00424             result += ' ';
00425             continue;
00426         }
00427 
00428         if (value[i] == '%' && i + 2 < size) {
00429             int d1 = hexDigit(value[i + 1]);
00430             int d2 = hexDigit(value[i + 2]);
00431             if (0 <= d1 && d1 < 16 && 0 <= d2 && d2 < 16) {
00432                 result += (char)(d1 * 16 + d2);
00433                 i = i + 2;
00434                 continue;
00435             }
00436         }
00437 
00438         result += value[i];
00439     }
00440     return result;
00441 }
00442 
00443 namespace {
00444 bool isQueryUnreserved(char c) {
00445     return (
00446         ('0' <= c && c <= '9') ||
00447         ('A' <= c && c <= 'Z') ||
00448         ('a' <= c && c <= 'z') ||
00449         c == '-' || c == '_' || c == '.' || c == '!' ||
00450         c == '~' || c == '*' || c == '\'' || c == '(' || c == ')'
00451     );
00452 }
00453 }
00454 
00455 Common::String LocalWebserver::urlEncodeQueryParameterValue(Common::String value) {
00456     //OK chars = alphanum | "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
00457     //reserved for query are ";", "/", "?", ":", "@", "&", "=", "+", ","
00458     //that means these must be encoded too or otherwise they could malform the query
00459     Common::String result = "";
00460     char hexChar[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
00461     for (uint32 i = 0; i < value.size(); ++i) {
00462         char c = value[i];
00463         if (isQueryUnreserved(c))
00464             result += c;
00465         else {
00466             result += '%';
00467             result += hexChar[(c >> 4) & 0xF];
00468             result += hexChar[c & 0xF];
00469         }
00470     }
00471     return result;
00472 }
00473 
00474 } // End of namespace Networking


Generated on Sat Feb 16 2019 05:00:55 for ResidualVM by doxygen 1.7.1
curved edge   curved edge