1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #define FORBIDDEN_SYMBOL_ALLOW_ALL
24 
25 #include "backends/networking/sdl_net/localwebserver.h"
26 #include "backends/networking/sdl_net/getclienthandler.h"
27 #include "common/memstream.h"
28 #include "common/str.h"
29 #include "common/system.h"
30 #include "common/timer.h"
31 #include "common/translation.h"
32 #include <SDL_net.h>
33 #include <common/config-manager.h>
34 
35 #ifdef POSIX
36 #include <errno.h>
37 #include <unistd.h>
38 #include <arpa/inet.h>
39 #include <net/if.h>
40 #include <netinet/in.h>
41 #include <sys/ioctl.h>
42 #include <sys/socket.h>
43 #include <sys/types.h>
44 
45 #ifndef SIOCGIFCONF
46 #include <sys/sockio.h>
47 #endif
48 
49 #ifndef _SIZEOF_ADDR_IFREQ
50 #define _SIZEOF_ADDR_IFREQ sizeof
51 #endif
52 
53 #define LSSDP_BUFFER_LEN 2048
54 #endif
55 
56 namespace Common {
57 class MemoryReadWriteStream;
58 
59 DECLARE_SINGLETON(Networking::LocalWebserver);
60 
61 }
62 
63 namespace Networking {
64 
LocalWebserver()65 LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerStarted(false),
66 	_stopOnIdle(false), _minimalMode(false), _clients(0), _idlingFrames(0), _serverPort(DEFAULT_SERVER_PORT) {
67 	addPathHandler("/", &_indexPageHandler);
68 	addPathHandler("/files", &_filesPageHandler);
69 	addPathHandler("/create", &_createDirectoryHandler);
70 	addPathHandler("/download", &_downloadFileHandler);
71 	addPathHandler("/upload", &_uploadFileHandler);
72 	addPathHandler("/list", &_listAjaxHandler);
73 	addPathHandler("/filesAJAX", &_filesAjaxPageHandler);
74 	_defaultHandler = &_resourceHandler;
75 }
76 
~LocalWebserver()77 LocalWebserver::~LocalWebserver() {
78 	stop();
79 }
80 
localWebserverTimer(void * ignored)81 void localWebserverTimer(void *ignored) {
82 	LocalServer.handle();
83 }
84 
startTimer(int interval)85 void LocalWebserver::startTimer(int interval) {
86 	Common::TimerManager *manager = g_system->getTimerManager();
87 	if (manager->installTimerProc(localWebserverTimer, interval, 0, "Networking::LocalWebserver's Timer")) {
88 		_timerStarted = true;
89 	} else {
90 		warning("Failed to install Networking::LocalWebserver's timer");
91 	}
92 }
93 
stopTimer()94 void LocalWebserver::stopTimer() {
95 	Common::TimerManager *manager = g_system->getTimerManager();
96 	manager->removeTimerProc(localWebserverTimer);
97 	_timerStarted = false;
98 }
99 
start(bool useMinimalMode)100 void LocalWebserver::start(bool useMinimalMode) {
101 	_handleMutex.lock();
102 	_serverPort = getPort();
103 	_stopOnIdle = false;
104 	if (_timerStarted) {
105 		_handleMutex.unlock();
106 		return;
107 	}
108 	_minimalMode = useMinimalMode;
109 	startTimer();
110 
111 	// Create a listening TCP socket
112 	IPaddress ip;
113 	if (SDLNet_ResolveHost(&ip, NULL, _serverPort) == -1) {
114 		error("LocalWebserver: SDLNet_ResolveHost: %s\n", SDLNet_GetError());
115 	}
116 
117 	resolveAddress(&ip);
118 
119 	_serverSocket = SDLNet_TCP_Open(&ip);
120 	if (!_serverSocket) {
121 		warning("LocalWebserver: SDLNet_TCP_Open: %s", SDLNet_GetError());
122 		stopTimer();
123 		g_system->displayMessageOnOSD(_("Failed to start local webserver.\nCheck whether selected port is not used by another application and try again."));
124 		_handleMutex.unlock();
125 		return;
126 	}
127 
128 	// Create a socket set
129 	_set = SDLNet_AllocSocketSet(MAX_CONNECTIONS + 1); //one more for our server socket
130 	if (!_set) {
131 		error("LocalWebserver: SDLNet_AllocSocketSet: %s\n", SDLNet_GetError());
132 	}
133 
134 	int numused = SDLNet_TCP_AddSocket(_set, _serverSocket);
135 	if (numused == -1) {
136 		error("LocalWebserver: SDLNet_AddSocket: %s\n", SDLNet_GetError());
137 	}
138 	_handleMutex.unlock();
139 }
140 
stop()141 void LocalWebserver::stop() {
142 	_handleMutex.lock();
143 	if (_timerStarted)
144 		stopTimer();
145 
146 	if (_serverSocket) {
147 		SDLNet_TCP_Close(_serverSocket);
148 		_serverSocket = nullptr;
149 	}
150 
151 	for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
152 		_client[i].close();
153 
154 	_clients = 0;
155 
156 	if (_set) {
157 		SDLNet_FreeSocketSet(_set);
158 		_set = nullptr;
159 	}
160 	_handleMutex.unlock();
161 }
162 
stopOnIdle()163 void LocalWebserver::stopOnIdle() { _stopOnIdle = true; }
164 
addPathHandler(Common::String path,BaseHandler * handler)165 void LocalWebserver::addPathHandler(Common::String path, BaseHandler *handler) {
166 	if (_pathHandlers.contains(path))
167 		warning("LocalWebserver::addPathHandler: path already had a handler");
168 	_pathHandlers[path] = handler;
169 }
170 
getAddress()171 Common::String LocalWebserver::getAddress() { return _address;  }
172 
indexPageHandler()173 IndexPageHandler &LocalWebserver::indexPageHandler() { return _indexPageHandler; }
174 
isRunning()175 bool LocalWebserver::isRunning() {
176 	bool result = false;
177 	_handleMutex.lock();
178 	result = _timerStarted;
179 	_handleMutex.unlock();
180 	return result;
181 }
182 
getPort()183 uint32 LocalWebserver::getPort() {
184 #ifdef NETWORKING_LOCALWEBSERVER_ENABLE_PORT_OVERRIDE
185 	if (ConfMan.hasKey("local_server_port"))
186 		return ConfMan.getInt("local_server_port");
187 #endif
188 	return DEFAULT_SERVER_PORT;
189 }
190 
handle()191 void LocalWebserver::handle() {
192 	_handleMutex.lock();
193 	int numready = SDLNet_CheckSockets(_set, 0);
194 	if (numready == -1) {
195 		error("LocalWebserver: SDLNet_CheckSockets: %s\n", SDLNet_GetError());
196 	} else if (numready) {
197 		acceptClient();
198 	}
199 
200 	for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
201 		handleClient(i);
202 
203 	_clients = 0;
204 	for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
205 		if (_client[i].state() != INVALID)
206 			++_clients;
207 
208 	if (_clients == 0)
209 		++_idlingFrames;
210 	else
211 		_idlingFrames = 0;
212 
213 	if (_idlingFrames > FRAMES_PER_SECOND && _stopOnIdle) {
214 		_handleMutex.unlock();
215 		stop();
216 		return;
217 	}
218 
219 	_handleMutex.unlock();
220 }
221 
handleClient(uint32 i)222 void LocalWebserver::handleClient(uint32 i) {
223 	switch (_client[i].state()) {
224 	case INVALID:
225 		return;
226 	case READING_HEADERS:
227 		_client[i].readHeaders();
228 		break;
229 	case READ_HEADERS: {
230 		// decide what to do next with that client
231 		// check whether we know a handler for such URL
232 		BaseHandler *handler = nullptr;
233 		if (_pathHandlers.contains(_client[i].path())) {
234 			handler = _pathHandlers[_client[i].path()];
235 		} else {
236 			// try default handler
237 			handler = _defaultHandler;
238 		}
239 
240 		// if server's in "minimal mode", only handlers which support it are used
241 		if (handler && (!_minimalMode || handler->minimalModeSupported()))
242 			handler->handle(_client[i]);
243 
244 		if (_client[i].state() == BEING_HANDLED || _client[i].state() == INVALID)
245 			break;
246 
247 		// if no handler, answer with default BAD REQUEST
248 	}
249 	// fall through
250 
251 	case BAD_REQUEST:
252 		setClientGetHandler(_client[i], "<html><head><title>ScummVM - Bad Request</title></head><body>BAD REQUEST</body></html>", 400);
253 		break;
254 	case BEING_HANDLED:
255 		_client[i].handle();
256 		break;
257 	}
258 }
259 
acceptClient()260 void LocalWebserver::acceptClient() {
261 	if (!SDLNet_SocketReady(_serverSocket))
262 		return;
263 
264 	TCPsocket client = SDLNet_TCP_Accept(_serverSocket);
265 	if (!client)
266 		return;
267 
268 	if (_clients == MAX_CONNECTIONS) { //drop the connection
269 		SDLNet_TCP_Close(client);
270 		return;
271 	}
272 
273 	++_clients;
274 	for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
275 		if (_client[i].state() == INVALID) {
276 			_client[i].open(_set, client);
277 			break;
278 		}
279 }
280 
resolveAddress(void * ipAddress)281 void LocalWebserver::resolveAddress(void *ipAddress) {
282 	IPaddress *ip = (IPaddress *)ipAddress;
283 
284 	// not resolved
285 	_address = Common::String::format("http://127.0.0.1:%u/ (unresolved)", _serverPort);
286 
287 	// default way (might work everywhere, surely works on Windows)
288 	const char *name = SDLNet_ResolveIP(ip);
289 	if (name == NULL) {
290 		warning("LocalWebserver: SDLNet_ResolveIP: %s", SDLNet_GetError());
291 	} else {
292 		IPaddress localIp;
293 		if (SDLNet_ResolveHost(&localIp, name, _serverPort) == -1) {
294 			warning("LocalWebserver: SDLNet_ResolveHost: %s", SDLNet_GetError());
295 		} else {
296 			_address = Common::String::format(
297 				"http://%u.%u.%u.%u:%u/",
298 				localIp.host & 0xFF, (localIp.host >> 8) & 0xFF, (localIp.host >> 16) & 0xFF, (localIp.host >> 24) & 0xFF,
299 				_serverPort
300 			);
301 		}
302 	}
303 
304 	// check that our trick worked
305 	if (_address.contains("/127.0.0.1:") || _address.contains("localhost") || _address.contains("/0.0.0.0:"))
306 		warning("LocalWebserver: Failed to resolve IP with the default way");
307 	else
308 		return;
309 
310 	// if not - try platform-specific
311 #ifdef POSIX
312 	void *tmpAddrPtr = NULL;
313 
314 	int fd = socket(AF_INET, SOCK_DGRAM, 0);
315 	if (fd < 0) {
316 		warning("LocalWebserver: failed to create socket: %s (%d)", strerror(errno), errno);
317 	} else {
318 		// get ifconfig
319 		char buffer[LSSDP_BUFFER_LEN] = {};
320 		struct ifconf ifc;
321 		ifc.ifc_len = sizeof(buffer);
322 		ifc.ifc_buf = (caddr_t) buffer;
323 
324 		if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
325 		    warning("LocalWebserver: ioctl SIOCGIFCONF failed: %s (%d)", strerror(errno), errno);
326 		} else {
327 			struct ifreq *i;
328 			for (size_t index = 0; index < (size_t)ifc.ifc_len; index += _SIZEOF_ADDR_IFREQ(*i)) {
329 				i = (struct ifreq *)(buffer + index);
330 
331 				Common::String addr;
332 
333 				// IPv4
334 				if (i->ifr_addr.sa_family == AF_INET) {
335 					tmpAddrPtr = &((struct sockaddr_in *)&i->ifr_addr)->sin_addr;
336 					char addressBuffer[INET_ADDRSTRLEN];
337 					inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
338 					debug(9, "%s IP Address %s", i->ifr_name, addressBuffer);
339 					addr = addressBuffer;
340 				}
341 
342 				// IPv6
343 				/*
344 				if (i->ifr_addr.sa_family == AF_INET6) {
345 					tmpAddrPtr = &((struct sockaddr_in6 *)&i->ifr_addr)->sin6_addr;
346 					char addressBuffer[INET6_ADDRSTRLEN];
347 					inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
348 					debug(9, "%s IP Address %s", i->ifr_name, addressBuffer);
349 					addr = addressBuffer;
350 				}
351 				*/
352 
353 				if (addr.empty())
354 					continue;
355 
356 				// ignored IPv4 addresses
357 				if (addr.equals("127.0.0.1") || addr.equals("0.0.0.0") || addr.equals("localhost"))
358 					continue;
359 
360 				// ignored IPv6 addresses
361 				/*
362 				if (addr.equals("::1"))
363 					continue;
364 				*/
365 
366 				// use the address found
367 				_address = "http://" + addr + Common::String::format(":%u/", _serverPort);
368 			}
369 		}
370 
371 		// close socket
372 		if (close(fd) != 0) {
373 			warning("LocalWebserver: failed to close socket [fd %d]: %s (%d)", fd, strerror(errno), errno);
374 		}
375 	}
376 #endif
377 }
378 
setClientGetHandler(Client & client,Common::String response,long code,const char * mimeType)379 void LocalWebserver::setClientGetHandler(Client &client, Common::String response, long code, const char *mimeType) {
380 	byte *data = new byte[response.size()];
381 	memcpy(data, response.c_str(), response.size());
382 	Common::MemoryReadStream *stream = new Common::MemoryReadStream(data, response.size(), DisposeAfterUse::YES);
383 	setClientGetHandler(client, stream, code, mimeType);
384 }
385 
setClientGetHandler(Client & client,Common::SeekableReadStream * responseStream,long code,const char * mimeType)386 void LocalWebserver::setClientGetHandler(Client &client, Common::SeekableReadStream *responseStream, long code, const char *mimeType) {
387 	GetClientHandler *handler = new GetClientHandler(responseStream);
388 	handler->setResponseCode(code);
389 	if (mimeType)
390 		handler->setHeader("Content-Type", mimeType);
391 	client.setHandler(handler);
392 }
393 
setClientRedirectHandler(Client & client,Common::String response,Common::String location,const char * mimeType)394 void LocalWebserver::setClientRedirectHandler(Client &client, Common::String response, Common::String location, const char *mimeType) {
395 	byte *data = new byte[response.size()];
396 	memcpy(data, response.c_str(), response.size());
397 	Common::MemoryReadStream *stream = new Common::MemoryReadStream(data, response.size(), DisposeAfterUse::YES);
398 	setClientRedirectHandler(client, stream, location, mimeType);
399 }
400 
setClientRedirectHandler(Client & client,Common::SeekableReadStream * responseStream,Common::String location,const char * mimeType)401 void LocalWebserver::setClientRedirectHandler(Client &client, Common::SeekableReadStream *responseStream, Common::String location, const char *mimeType) {
402 	GetClientHandler *handler = new GetClientHandler(responseStream);
403 	handler->setResponseCode(302); //redirect
404 	handler->setHeader("Location", location);
405 	if (mimeType)
406 		handler->setHeader("Content-Type", mimeType);
407 	client.setHandler(handler);
408 }
409 
410 namespace {
hexDigit(char c)411 int hexDigit(char c) {
412 	if ('0' <= c && c <= '9') return c - '0';
413 	if ('A' <= c && c <= 'F') return c - 'A' + 10;
414 	if ('a' <= c && c <= 'f') return c - 'a' + 10;
415 	return -1;
416 }
417 }
418 
urlDecode(Common::String value)419 Common::String LocalWebserver::urlDecode(Common::String value) {
420 	Common::String result = "";
421 	uint32 size = value.size();
422 	for (uint32 i = 0; i < size; ++i) {
423 		if (value[i] == '+') {
424 			result += ' ';
425 			continue;
426 		}
427 
428 		if (value[i] == '%' && i + 2 < size) {
429 			int d1 = hexDigit(value[i + 1]);
430 			int d2 = hexDigit(value[i + 2]);
431 			if (0 <= d1 && d1 < 16 && 0 <= d2 && d2 < 16) {
432 				result += (char)(d1 * 16 + d2);
433 				i = i + 2;
434 				continue;
435 			}
436 		}
437 
438 		result += value[i];
439 	}
440 	return result;
441 }
442 
443 namespace {
isQueryUnreserved(char c)444 bool isQueryUnreserved(char c) {
445 	return (
446 		('0' <= c && c <= '9') ||
447 		('A' <= c && c <= 'Z') ||
448 		('a' <= c && c <= 'z') ||
449 		c == '-' || c == '_' || c == '.' || c == '!' ||
450 		c == '~' || c == '*' || c == '\'' || c == '(' || c == ')'
451 	);
452 }
453 }
454 
urlEncodeQueryParameterValue(Common::String value)455 Common::String LocalWebserver::urlEncodeQueryParameterValue(Common::String value) {
456 	//OK chars = alphanum | "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
457 	//reserved for query are ";", "/", "?", ":", "@", "&", "=", "+", ","
458 	//that means these must be encoded too or otherwise they could malform the query
459 	Common::String result = "";
460 	char hexChar[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
461 	for (uint32 i = 0; i < value.size(); ++i) {
462 		char c = value[i];
463 		if (isQueryUnreserved(c))
464 			result += c;
465 		else {
466 			result += '%';
467 			result += hexChar[(c >> 4) & 0xF];
468 			result += hexChar[c & 0xF];
469 		}
470 	}
471 	return result;
472 }
473 
474 } // End of namespace Networking
475