1 /*************************************************************************/
2 /*  editor_file_server.cpp                                               */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
10 /*                                                                       */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the       */
13 /* "Software"), to deal in the Software without restriction, including   */
14 /* without limitation the rights to use, copy, modify, merge, publish,   */
15 /* distribute, sublicense, and/or sell copies of the Software, and to    */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions:                                             */
18 /*                                                                       */
19 /* The above copyright notice and this permission notice shall be        */
20 /* included in all copies or substantial portions of the Software.       */
21 /*                                                                       */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29 /*************************************************************************/
30 #include "editor_file_server.h"
31 #include "../editor_settings.h"
32 #include "io/marshalls.h"
33 
34 //#define DEBUG_PRINT(m_p) print_line(m_p)
35 #define DEBUG_TIME(m_what) printf("MS: %s - %lli\n", m_what, OS::get_singleton()->get_ticks_usec());
36 
37 //#define DEBUG_TIME(m_what)
38 
_close_client(ClientData * cd)39 void EditorFileServer::_close_client(ClientData *cd) {
40 
41 	cd->connection->disconnect();
42 	cd->efs->wait_mutex->lock();
43 	cd->efs->to_wait.insert(cd->thread);
44 	cd->efs->wait_mutex->unlock();
45 	while (cd->files.size()) {
46 		memdelete(cd->files.front()->get());
47 		cd->files.erase(cd->files.front());
48 	}
49 	memdelete(cd);
50 }
51 
_subthread_start(void * s)52 void EditorFileServer::_subthread_start(void *s) {
53 
54 	ClientData *cd = (ClientData *)s;
55 
56 	cd->connection->set_nodelay(true);
57 	uint8_t buf4[8];
58 	Error err = cd->connection->get_data(buf4, 4);
59 	if (err != OK) {
60 		_close_client(cd);
61 		ERR_FAIL_COND(err != OK);
62 	}
63 
64 	int passlen = decode_uint32(buf4);
65 
66 	if (passlen > 512) {
67 
68 		_close_client(cd);
69 		ERR_FAIL_COND(passlen > 512);
70 	} else if (passlen > 0) {
71 
72 		Vector<char> passutf8;
73 		passutf8.resize(passlen + 1);
74 		err = cd->connection->get_data((uint8_t *)passutf8.ptr(), passlen);
75 		if (err != OK) {
76 			_close_client(cd);
77 			ERR_FAIL_COND(err != OK);
78 		}
79 		passutf8[passlen] = 0;
80 		String s;
81 		s.parse_utf8(passutf8.ptr());
82 		if (s != cd->efs->password) {
83 			encode_uint32(ERR_INVALID_DATA, buf4);
84 			cd->connection->put_data(buf4, 4);
85 			OS::get_singleton()->delay_usec(1000000);
86 			_close_client(cd);
87 			ERR_PRINT("CLIENT PASSWORD MISMATCH");
88 			ERR_FAIL();
89 		}
90 	} else {
91 		if (cd->efs->password != "") {
92 			encode_uint32(ERR_INVALID_DATA, buf4);
93 			cd->connection->put_data(buf4, 4);
94 			OS::get_singleton()->delay_usec(1000000);
95 			_close_client(cd);
96 			ERR_PRINT("CLIENT PASSWORD MISMATCH (should be empty!)");
97 			ERR_FAIL();
98 		}
99 	}
100 
101 	encode_uint32(OK, buf4);
102 	cd->connection->put_data(buf4, 4);
103 
104 	while (!cd->quit) {
105 
106 		//wait for ID
107 		err = cd->connection->get_data(buf4, 4);
108 		//#define DEBUG_PRINT(m_p) print_line(m_p)
109 		DEBUG_TIME("get_data")
110 
111 		if (err != OK) {
112 			_close_client(cd);
113 			ERR_FAIL_COND(err != OK);
114 		}
115 		int id = decode_uint32(buf4);
116 
117 		//wait for command
118 		err = cd->connection->get_data(buf4, 4);
119 		if (err != OK) {
120 			_close_client(cd);
121 			ERR_FAIL_COND(err != OK);
122 		}
123 		int cmd = decode_uint32(buf4);
124 
125 		switch (cmd) {
126 
127 			case FileAccessNetwork::COMMAND_FILE_EXISTS:
128 			case FileAccessNetwork::COMMAND_GET_MODTIME:
129 			case FileAccessNetwork::COMMAND_OPEN_FILE: {
130 
131 				DEBUG_TIME("open_file")
132 				err = cd->connection->get_data(buf4, 4);
133 				if (err != OK) {
134 					_close_client(cd);
135 					ERR_FAIL_COND(err != OK);
136 				}
137 
138 				int namelen = decode_uint32(buf4);
139 				Vector<char> fileutf8;
140 				fileutf8.resize(namelen + 1);
141 				err = cd->connection->get_data((uint8_t *)fileutf8.ptr(), namelen);
142 				if (err != OK) {
143 					_close_client(cd);
144 					ERR_FAIL_COND(err != OK);
145 				}
146 				fileutf8[namelen] = 0;
147 				String s;
148 				s.parse_utf8(fileutf8.ptr());
149 
150 				if (cmd == FileAccessNetwork::COMMAND_FILE_EXISTS) {
151 					print_line("FILE EXISTS: " + s);
152 				}
153 				if (cmd == FileAccessNetwork::COMMAND_GET_MODTIME) {
154 					print_line("MOD TIME: " + s);
155 				}
156 				if (cmd == FileAccessNetwork::COMMAND_OPEN_FILE) {
157 					print_line("OPEN: " + s);
158 				}
159 
160 				if (!s.begins_with("res://")) {
161 
162 					_close_client(cd);
163 					ERR_FAIL_COND(!s.begins_with("res://"));
164 				}
165 				ERR_CONTINUE(cd->files.has(id));
166 
167 				if (cmd == FileAccessNetwork::COMMAND_FILE_EXISTS) {
168 
169 					encode_uint32(id, buf4);
170 					cd->connection->put_data(buf4, 4);
171 					encode_uint32(FileAccessNetwork::RESPONSE_FILE_EXISTS, buf4);
172 					cd->connection->put_data(buf4, 4);
173 					encode_uint32(FileAccess::exists(s), buf4);
174 					cd->connection->put_data(buf4, 4);
175 					DEBUG_TIME("open_file_end")
176 					break;
177 				}
178 
179 				if (cmd == FileAccessNetwork::COMMAND_GET_MODTIME) {
180 
181 					encode_uint32(id, buf4);
182 					cd->connection->put_data(buf4, 4);
183 					encode_uint32(FileAccessNetwork::RESPONSE_GET_MODTIME, buf4);
184 					cd->connection->put_data(buf4, 4);
185 					encode_uint64(FileAccess::get_modified_time(s), buf4);
186 					cd->connection->put_data(buf4, 8);
187 					DEBUG_TIME("open_file_end")
188 					break;
189 				}
190 
191 				FileAccess *fa = FileAccess::open(s, FileAccess::READ);
192 				if (!fa) {
193 					//not found, continue
194 					encode_uint32(id, buf4);
195 					cd->connection->put_data(buf4, 4);
196 					encode_uint32(FileAccessNetwork::RESPONSE_OPEN, buf4);
197 					cd->connection->put_data(buf4, 4);
198 					encode_uint32(ERR_FILE_NOT_FOUND, buf4);
199 					cd->connection->put_data(buf4, 4);
200 					DEBUG_TIME("open_file_end")
201 					break;
202 				}
203 
204 				encode_uint32(id, buf4);
205 				cd->connection->put_data(buf4, 4);
206 				encode_uint32(FileAccessNetwork::RESPONSE_OPEN, buf4);
207 				cd->connection->put_data(buf4, 4);
208 				encode_uint32(OK, buf4);
209 				cd->connection->put_data(buf4, 4);
210 				encode_uint64(fa->get_len(), buf4);
211 				cd->connection->put_data(buf4, 8);
212 
213 				cd->files[id] = fa;
214 				DEBUG_TIME("open_file_end")
215 
216 			} break;
217 			case FileAccessNetwork::COMMAND_READ_BLOCK: {
218 
219 				err = cd->connection->get_data(buf4, 8);
220 				if (err != OK) {
221 					_close_client(cd);
222 					ERR_FAIL_COND(err != OK);
223 				}
224 
225 				ERR_CONTINUE(!cd->files.has(id));
226 
227 				uint64_t offset = decode_uint64(buf4);
228 
229 				err = cd->connection->get_data(buf4, 4);
230 				if (err != OK) {
231 					_close_client(cd);
232 					ERR_FAIL_COND(err != OK);
233 				}
234 
235 				int blocklen = decode_uint32(buf4);
236 				ERR_CONTINUE(blocklen > (16 * 1024 * 1024));
237 
238 				cd->files[id]->seek(offset);
239 				Vector<uint8_t> buf;
240 				buf.resize(blocklen);
241 				int read = cd->files[id]->get_buffer(buf.ptr(), blocklen);
242 				ERR_CONTINUE(read < 0);
243 
244 				print_line("GET BLOCK - offset: " + itos(offset) + ", blocklen: " + itos(blocklen));
245 
246 				//not found, continue
247 				encode_uint32(id, buf4);
248 				cd->connection->put_data(buf4, 4);
249 				encode_uint32(FileAccessNetwork::RESPONSE_DATA, buf4);
250 				cd->connection->put_data(buf4, 4);
251 				encode_uint64(offset, buf4);
252 				cd->connection->put_data(buf4, 8);
253 				encode_uint32(read, buf4);
254 				cd->connection->put_data(buf4, 4);
255 				cd->connection->put_data(buf.ptr(), read);
256 
257 			} break;
258 			case FileAccessNetwork::COMMAND_CLOSE: {
259 
260 				print_line("CLOSED");
261 				ERR_CONTINUE(!cd->files.has(id));
262 				memdelete(cd->files[id]);
263 				cd->files.erase(id);
264 			} break;
265 		}
266 	}
267 
268 	_close_client(cd);
269 }
270 
_thread_start(void * s)271 void EditorFileServer::_thread_start(void *s) {
272 
273 	EditorFileServer *self = (EditorFileServer *)s;
274 	while (!self->quit) {
275 
276 		if (self->cmd == CMD_ACTIVATE) {
277 			self->server->listen(self->port);
278 			self->active = true;
279 			self->cmd = CMD_NONE;
280 		} else if (self->cmd == CMD_STOP) {
281 			self->server->stop();
282 			self->active = false;
283 			self->cmd = CMD_NONE;
284 		}
285 
286 		if (self->active) {
287 			if (self->server->is_connection_available()) {
288 				ClientData *cd = memnew(ClientData);
289 				cd->connection = self->server->take_connection();
290 				cd->efs = self;
291 				cd->quit = false;
292 				cd->thread = Thread::create(_subthread_start, cd);
293 			}
294 		}
295 
296 		self->wait_mutex->lock();
297 		while (self->to_wait.size()) {
298 			Thread *w = self->to_wait.front()->get();
299 			self->to_wait.erase(w);
300 			self->wait_mutex->unlock();
301 			Thread::wait_to_finish(w);
302 			memdelete(w);
303 			self->wait_mutex->lock();
304 		}
305 		self->wait_mutex->unlock();
306 
307 		OS::get_singleton()->delay_usec(100000);
308 	}
309 }
310 
start()311 void EditorFileServer::start() {
312 
313 	stop();
314 	port = EDITOR_DEF("file_server/port", 6010);
315 	password = EDITOR_DEF("file_server/password", "");
316 	cmd = CMD_ACTIVATE;
317 }
318 
is_active() const319 bool EditorFileServer::is_active() const {
320 
321 	return active;
322 }
323 
stop()324 void EditorFileServer::stop() {
325 
326 	cmd = CMD_STOP;
327 }
328 
EditorFileServer()329 EditorFileServer::EditorFileServer() {
330 
331 	server = TCP_Server::create_ref();
332 	wait_mutex = Mutex::create();
333 	quit = false;
334 	active = false;
335 	cmd = CMD_NONE;
336 	thread = Thread::create(_thread_start, this);
337 
338 	EDITOR_DEF("file_server/port", 6010);
339 	EDITOR_DEF("file_server/password", "");
340 }
341 
~EditorFileServer()342 EditorFileServer::~EditorFileServer() {
343 
344 	quit = true;
345 	Thread::wait_to_finish(thread);
346 	memdelete(thread);
347 	memdelete(wait_mutex);
348 }
349