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-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 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
31 #include "editor_file_server.h"
32
33 #include "../editor_settings.h"
34 #include "core/io/marshalls.h"
35
36 //#define DEBUG_PRINT(m_p) print_line(m_p)
37 //#define DEBUG_TIME(m_what) printf("MS: %s - %lu\n", m_what, OS::get_singleton()->get_ticks_usec());
38
39 #define DEBUG_PRINT(m_what)
40 #define DEBUG_TIME(m_what)
41
_close_client(ClientData * cd)42 void EditorFileServer::_close_client(ClientData *cd) {
43
44 cd->connection->disconnect_from_host();
45 cd->efs->wait_mutex->lock();
46 cd->efs->to_wait.insert(cd->thread);
47 cd->efs->wait_mutex->unlock();
48 while (cd->files.size()) {
49 memdelete(cd->files.front()->get());
50 cd->files.erase(cd->files.front());
51 }
52 memdelete(cd);
53 }
54
_subthread_start(void * s)55 void EditorFileServer::_subthread_start(void *s) {
56
57 ClientData *cd = (ClientData *)s;
58
59 cd->connection->set_no_delay(true);
60 uint8_t buf4[8];
61 Error err = cd->connection->get_data(buf4, 4);
62 if (err != OK) {
63 _close_client(cd);
64 ERR_FAIL_COND(err != OK);
65 }
66
67 int passlen = decode_uint32(buf4);
68
69 if (passlen > 512) {
70
71 _close_client(cd);
72 ERR_FAIL_COND(passlen > 512);
73 } else if (passlen > 0) {
74
75 Vector<char> passutf8;
76 passutf8.resize(passlen + 1);
77 err = cd->connection->get_data((uint8_t *)passutf8.ptr(), passlen);
78 if (err != OK) {
79 _close_client(cd);
80 ERR_FAIL_COND(err != OK);
81 }
82 passutf8.write[passlen] = 0;
83 String s2;
84 s2.parse_utf8(passutf8.ptr());
85 if (s2 != cd->efs->password) {
86 encode_uint32(ERR_INVALID_DATA, buf4);
87 cd->connection->put_data(buf4, 4);
88 OS::get_singleton()->delay_usec(1000000);
89 _close_client(cd);
90 ERR_PRINT("CLIENT PASSWORD MISMATCH");
91 ERR_FAIL();
92 }
93 } else {
94 if (cd->efs->password != "") {
95 encode_uint32(ERR_INVALID_DATA, buf4);
96 cd->connection->put_data(buf4, 4);
97 OS::get_singleton()->delay_usec(1000000);
98 _close_client(cd);
99 ERR_PRINT("CLIENT PASSWORD MISMATCH (should be empty!)");
100 ERR_FAIL();
101 }
102 }
103
104 encode_uint32(OK, buf4);
105 cd->connection->put_data(buf4, 4);
106
107 while (!cd->quit) {
108
109 //wait for ID
110 err = cd->connection->get_data(buf4, 4);
111 DEBUG_TIME("get_data")
112
113 if (err != OK) {
114 _close_client(cd);
115 ERR_FAIL_COND(err != OK);
116 }
117 int id = decode_uint32(buf4);
118
119 //wait for command
120 err = cd->connection->get_data(buf4, 4);
121 if (err != OK) {
122 _close_client(cd);
123 ERR_FAIL_COND(err != OK);
124 }
125 int cmd = decode_uint32(buf4);
126
127 switch (cmd) {
128
129 case FileAccessNetwork::COMMAND_FILE_EXISTS:
130 case FileAccessNetwork::COMMAND_GET_MODTIME:
131 case FileAccessNetwork::COMMAND_OPEN_FILE: {
132
133 DEBUG_TIME("open_file")
134 err = cd->connection->get_data(buf4, 4);
135 if (err != OK) {
136 _close_client(cd);
137 ERR_FAIL_COND(err != OK);
138 }
139
140 int namelen = decode_uint32(buf4);
141 Vector<char> fileutf8;
142 fileutf8.resize(namelen + 1);
143 err = cd->connection->get_data((uint8_t *)fileutf8.ptr(), namelen);
144 if (err != OK) {
145 _close_client(cd);
146 ERR_FAIL_COND(err != OK);
147 }
148 fileutf8.write[namelen] = 0;
149 String s2;
150 s2.parse_utf8(fileutf8.ptr());
151
152 if (cmd == FileAccessNetwork::COMMAND_FILE_EXISTS) {
153 print_verbose("FILE EXISTS: " + s2);
154 }
155 if (cmd == FileAccessNetwork::COMMAND_GET_MODTIME) {
156 print_verbose("MOD TIME: " + s2);
157 }
158 if (cmd == FileAccessNetwork::COMMAND_OPEN_FILE) {
159 print_verbose("OPEN: " + s2);
160 }
161
162 if (!s2.begins_with("res://")) {
163
164 _close_client(cd);
165 ERR_FAIL_COND(!s2.begins_with("res://"));
166 }
167 ERR_CONTINUE(cd->files.has(id));
168
169 if (cmd == FileAccessNetwork::COMMAND_FILE_EXISTS) {
170
171 encode_uint32(id, buf4);
172 cd->connection->put_data(buf4, 4);
173 encode_uint32(FileAccessNetwork::RESPONSE_FILE_EXISTS, buf4);
174 cd->connection->put_data(buf4, 4);
175 encode_uint32(FileAccess::exists(s2), buf4);
176 cd->connection->put_data(buf4, 4);
177 DEBUG_TIME("open_file_end")
178 break;
179 }
180
181 if (cmd == FileAccessNetwork::COMMAND_GET_MODTIME) {
182
183 encode_uint32(id, buf4);
184 cd->connection->put_data(buf4, 4);
185 encode_uint32(FileAccessNetwork::RESPONSE_GET_MODTIME, buf4);
186 cd->connection->put_data(buf4, 4);
187 encode_uint64(FileAccess::get_modified_time(s2), buf4);
188 cd->connection->put_data(buf4, 8);
189 DEBUG_TIME("open_file_end")
190 break;
191 }
192
193 FileAccess *fa = FileAccess::open(s2, FileAccess::READ);
194 if (!fa) {
195 //not found, continue
196 encode_uint32(id, buf4);
197 cd->connection->put_data(buf4, 4);
198 encode_uint32(FileAccessNetwork::RESPONSE_OPEN, buf4);
199 cd->connection->put_data(buf4, 4);
200 encode_uint32(ERR_FILE_NOT_FOUND, buf4);
201 cd->connection->put_data(buf4, 4);
202 DEBUG_TIME("open_file_end")
203 break;
204 }
205
206 encode_uint32(id, buf4);
207 cd->connection->put_data(buf4, 4);
208 encode_uint32(FileAccessNetwork::RESPONSE_OPEN, buf4);
209 cd->connection->put_data(buf4, 4);
210 encode_uint32(OK, buf4);
211 cd->connection->put_data(buf4, 4);
212 encode_uint64(fa->get_len(), buf4);
213 cd->connection->put_data(buf4, 8);
214
215 cd->files[id] = fa;
216 DEBUG_TIME("open_file_end")
217
218 } break;
219 case FileAccessNetwork::COMMAND_READ_BLOCK: {
220
221 err = cd->connection->get_data(buf4, 8);
222 if (err != OK) {
223 _close_client(cd);
224 ERR_FAIL_COND(err != OK);
225 }
226
227 ERR_CONTINUE(!cd->files.has(id));
228
229 uint64_t offset = decode_uint64(buf4);
230
231 err = cd->connection->get_data(buf4, 4);
232 if (err != OK) {
233 _close_client(cd);
234 ERR_FAIL_COND(err != OK);
235 }
236
237 int blocklen = decode_uint32(buf4);
238 ERR_CONTINUE(blocklen > (16 * 1024 * 1024));
239
240 cd->files[id]->seek(offset);
241 Vector<uint8_t> buf;
242 buf.resize(blocklen);
243 int read = cd->files[id]->get_buffer(buf.ptrw(), blocklen);
244 ERR_CONTINUE(read < 0);
245
246 print_verbose("GET BLOCK - offset: " + itos(offset) + ", blocklen: " + itos(blocklen));
247
248 //not found, continue
249 encode_uint32(id, buf4);
250 cd->connection->put_data(buf4, 4);
251 encode_uint32(FileAccessNetwork::RESPONSE_DATA, buf4);
252 cd->connection->put_data(buf4, 4);
253 encode_uint64(offset, buf4);
254 cd->connection->put_data(buf4, 8);
255 encode_uint32(read, buf4);
256 cd->connection->put_data(buf4, 4);
257 cd->connection->put_data(buf.ptr(), read);
258
259 } break;
260 case FileAccessNetwork::COMMAND_CLOSE: {
261
262 print_verbose("CLOSED");
263 ERR_CONTINUE(!cd->files.has(id));
264 memdelete(cd->files[id]);
265 cd->files.erase(id);
266 } break;
267 }
268 }
269
270 _close_client(cd);
271 }
272
_thread_start(void * s)273 void EditorFileServer::_thread_start(void *s) {
274
275 EditorFileServer *self = (EditorFileServer *)s;
276 while (!self->quit) {
277
278 if (self->cmd == CMD_ACTIVATE) {
279 self->server->listen(self->port);
280 self->active = true;
281 self->cmd = CMD_NONE;
282 } else if (self->cmd == CMD_STOP) {
283 self->server->stop();
284 self->active = false;
285 self->cmd = CMD_NONE;
286 }
287
288 if (self->active) {
289 if (self->server->is_connection_available()) {
290 ClientData *cd = memnew(ClientData);
291 cd->connection = self->server->take_connection();
292 cd->efs = self;
293 cd->quit = false;
294 cd->thread = Thread::create(_subthread_start, cd);
295 }
296 }
297
298 self->wait_mutex->lock();
299 while (self->to_wait.size()) {
300 Thread *w = self->to_wait.front()->get();
301 self->to_wait.erase(w);
302 self->wait_mutex->unlock();
303 Thread::wait_to_finish(w);
304 memdelete(w);
305 self->wait_mutex->lock();
306 }
307 self->wait_mutex->unlock();
308
309 OS::get_singleton()->delay_usec(100000);
310 }
311 }
312
start()313 void EditorFileServer::start() {
314
315 stop();
316 port = EDITOR_DEF("filesystem/file_server/port", 6010);
317 password = EDITOR_DEF("filesystem/file_server/password", "");
318 cmd = CMD_ACTIVATE;
319 }
320
is_active() const321 bool EditorFileServer::is_active() const {
322
323 return active;
324 }
325
stop()326 void EditorFileServer::stop() {
327
328 cmd = CMD_STOP;
329 }
330
EditorFileServer()331 EditorFileServer::EditorFileServer() {
332
333 server.instance();
334 wait_mutex = Mutex::create();
335 quit = false;
336 active = false;
337 cmd = CMD_NONE;
338 thread = Thread::create(_thread_start, this);
339
340 EDITOR_DEF("filesystem/file_server/port", 6010);
341 EDITOR_DEF("filesystem/file_server/password", "");
342 }
343
~EditorFileServer()344 EditorFileServer::~EditorFileServer() {
345
346 quit = true;
347 Thread::wait_to_finish(thread);
348 memdelete(thread);
349 memdelete(wait_mutex);
350 }
351