GCC Code Coverage Report


Directory: src/gate/
File: src/gate/net/ftpclients.c
Date: 2025-12-12 23:40:09
Exec Total Coverage
Lines: 276 407 67.8%
Functions: 21 31 67.7%
Branches: 112 255 43.9%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger <sm@opengate.at> |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/net/ftpclients.h"
30 #include "gate/files.h"
31 #include "gate/debugging.h"
32
33 #if defined(GATE_SYS_WIN)
34 # if defined(GATE_SYS_WINSTORE) || defined(GATE_SYS_WIN16)
35 # define GATE_NET_FTPCLIENT_NO_IMPL
36 # else
37 # define GATE_NET_FTPCLIENT_USE_WININET
38 # endif
39 #elif defined(GATE_SYS_DOS) || defined(GATE_SYS_ANDROID)
40 # define GATE_NET_FTPCLIENT_NO_IMPL
41 #else
42 # define GATE_NET_FTPCLIENT_USE_CURL
43 #endif
44
45
46 /* generic implementation: */
47
48 gate_result_t gate_ftpclient_upload(gate_ftpclient_t* client, gate_stream_t* source_stream, gate_string_t const* dest_path)
49 {
50 gate_result_t ret;
51 gate_stream_t* ftp_stream = NULL;
52 do
53 {
54 ret = gate_ftpclient_open_stream(client, dest_path, GATE_STREAM_OPEN_WRITE, &ftp_stream);
55 GATE_BREAK_IF_FAILED(ret);
56
57 ret = gate_stream_transfer(source_stream, ftp_stream);
58 GATE_BREAK_IF_FAILED(ret);
59
60 gate_stream_flush(ftp_stream);
61
62 } while (0);
63
64 if (ftp_stream)
65 {
66 gate_object_release(ftp_stream);
67 }
68
69 return ret;
70 }
71
72 1 gate_result_t gate_ftpclient_download(gate_ftpclient_t* client, gate_string_t const* source_path, gate_stream_t* dest_stream)
73 {
74 gate_result_t ret;
75 1 gate_stream_t* ftp_stream = NULL;
76 1 ret = gate_ftpclient_open_stream(client, source_path, GATE_STREAM_OPEN_READ, &ftp_stream);
77
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
78 {
79 return ret;
80 }
81 1 ret = gate_stream_transfer(ftp_stream, dest_stream);
82 1 gate_object_release(ftp_stream);
83 1 return ret;
84 }
85
86 gate_result_t gate_ftpclient_upload_file(gate_ftpclient_t* client, gate_string_t const* source_file, gate_string_t const* dest_path)
87 {
88 gate_result_t ret;
89 gate_controlstream_t* source_stream = NULL;
90 ret = gate_file_openstream(source_file, GATE_STREAM_OPEN_READ, &source_stream);
91 if (GATE_SUCCEEDED(ret))
92 {
93 ret = gate_ftpclient_upload(client, (gate_stream_t*)source_stream, dest_path);
94 gate_object_release(source_stream);
95 }
96 return ret;
97 }
98
99 gate_result_t gate_ftpclient_download_file(gate_ftpclient_t* client, gate_string_t const* source_path, gate_string_t const* dest_file)
100 {
101 gate_result_t ret;
102 gate_controlstream_t* dest_stream = NULL;
103 ret = gate_file_openstream(dest_file, GATE_STREAM_OPEN_WRITE, &dest_stream);
104 if (GATE_SUCCEEDED(ret))
105 {
106 ret = gate_ftpclient_download(client, source_path, (gate_stream_t*)dest_stream);
107 gate_object_release(dest_stream);
108 }
109 return ret;
110 }
111
112
113
114
115 #if defined(GATE_NET_FTPCLIENT_USE_WININET)
116
117 #include "gate/net/platform/wininet_api.h"
118 #include "gate/platforms.h"
119
120 gate_result_t gate_ftpclient_get_error()
121 {
122 gate_result_t ret = GATE_RESULT_EXECUTIONFAILED;
123 DWORD last_error = gate_win32_getlasterror();
124 TCHAR error_msg[1024];
125 DWORD error_msg_len = sizeof(error_msg) / sizeof(error_msg[0]);
126
127 if (ERROR_INTERNET_EXTENDED_ERROR == last_error)
128 {
129 WinInet.InetGetLastResponseInfo(&last_error, error_msg, &error_msg_len);
130 }
131 return ret;
132 }
133
134 gate_result_t gate_ftpclient_create(gate_ftpclient_t* client, gate_string_t const* server, gate_uint16_t port,
135 gate_string_t const* user, gate_string_t const* password, gate_enumint_t flags)
136 {
137 static gate_size_t const config_len = sizeof(client->config);
138 gate_result_t ret = GATE_RESULT_FAILED;
139 HINTERNET hclient = NULL;
140 HINTERNET hsession = NULL;
141 TCHAR server_name[1024] = GATE_INIT_EMPTY;
142 TCHAR user_name[1024] = GATE_INIT_EMPTY;
143 TCHAR user_pass[1024] = GATE_INIT_EMPTY;
144 gate_size_t user_len;
145 gate_bool_t passive = GATE_FLAG_ENABLED(flags, GATE_FTPCLIENT_FLAG_PASSIVE);
146
147 do
148 {
149 gate_mem_clear(client, sizeof(gate_ftpclient_t));
150 gate_string_to_buffer(server, client->server, sizeof(client->server));
151 user_len = gate_string_to_buffer(user, &client->config[0], config_len);
152 gate_string_to_buffer(password, &client->config[user_len + 1], config_len - user_len - 1);
153
154 client->port = port ? port : 21;
155
156 ret = load_wininet_api();
157 GATE_BREAK_IF_FAILED(ret);
158
159 gate_win32_string_2_winstr(server, server_name, sizeof(server_name) / sizeof(server_name[0]));
160
161 gate_win32_string_2_winstr(user, user_name, sizeof(user_name) / sizeof(user_name[0]));
162 gate_win32_string_2_winstr(password, user_pass, sizeof(user_pass) / sizeof(user_pass[0]));
163
164 client->flags = flags;
165
166
167 hclient = WinInet.InetOpen(_T("GATE WinInet FTP Client"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
168 if (NULL == hclient)
169 {
170 ret = GATE_RESULT_NOTSUPPORTED;
171 break;
172 }
173
174 hsession = WinInet.InetConnect(hclient, server_name, (gate_uint16_t)(port == 0 ? 21 : port),
175 user_name, user_pass,
176 INTERNET_SERVICE_FTP,
177 passive ? INTERNET_FLAG_PASSIVE : 0,
178 (DWORD_PTR)(void*)client);
179 if (NULL == hsession)
180 {
181 gate_ftpclient_get_error();
182 ret = GATE_RESULT_NOTAVAILABLE;
183 break;
184 }
185
186 client->handles[0] = (void*)hclient;
187 client->handles[1] = (void*)hsession;
188 hclient = NULL;
189 hsession = NULL;
190
191 ret = GATE_RESULT_OK;
192
193 } while (0);
194
195 if (hsession != NULL)
196 {
197 WinInet.InetCloseHandle(hsession);
198 }
199 if (hclient != NULL)
200 {
201 WinInet.InetCloseHandle(hclient);
202 }
203 return ret;
204 }
205
206 static HINTERNET gate_ftpclient_new_session(gate_ftpclient_t* client)
207 {
208 HINTERNET hsession = NULL;
209
210 do
211 {
212 HINTERNET hclient = (HINTERNET)client->handles[0];
213 TCHAR server_name[1024] = GATE_INIT_EMPTY;
214 TCHAR user_name[1024] = GATE_INIT_EMPTY;
215 TCHAR user_pass[1024] = GATE_INIT_EMPTY;
216 gate_size_t user_len;
217 char const* ptr_pass;
218 gate_size_t pass_len;
219 gate_bool_t passive = GATE_FLAG_ENABLED(client->flags, GATE_FTPCLIENT_FLAG_PASSIVE);
220
221 gate_win32_utf8_2_winstr(client->server, gate_str_length(client->server),
222 server_name, sizeof(server_name) / sizeof(server_name[0]));
223 user_len = gate_str_length(client->config);
224 gate_win32_utf8_2_winstr(client->config, user_len,
225 user_name, sizeof(user_name) / sizeof(user_name[0]));
226 ptr_pass = &client->config[0] + user_len + 1;
227 pass_len = gate_str_length(ptr_pass);
228 gate_win32_utf8_2_winstr(ptr_pass, pass_len,
229 user_pass, sizeof(user_pass) / sizeof(user_pass[0]));
230
231 hsession = WinInet.InetConnect(hclient, server_name, client->port == 0 ? 21 : client->port,
232 user_name, user_pass,
233 INTERNET_SERVICE_FTP,
234 passive ? INTERNET_FLAG_PASSIVE : 0,
235 (DWORD_PTR)(void*)client);
236 } while (0);
237 return hsession;
238 }
239
240 gate_result_t gate_ftpclient_release(gate_ftpclient_t* client)
241 {
242 HINTERNET hclient = (HINTERNET)client->handles[0];
243 HINTERNET hsession = (HINTERNET)client->handles[1];
244 if (hsession != NULL)
245 {
246 WinInet.InetCloseHandle(hsession);
247 }
248 if (hclient != NULL)
249 {
250 WinInet.InetCloseHandle(hclient);
251 }
252 gate_mem_clear(client, sizeof(gate_ftpclient_t));
253 return GATE_RESULT_OK;
254 }
255
256 static gate_timestamp_t convert_filetime(FILETIME const* ft)
257 {
258 return (gate_timestamp_t)((((gate_uint64_t)ft->dwHighDateTime << 32) + (gate_uint64_t)ft->dwLowDateTime) / 10);
259 }
260
261 gate_result_t gate_ftpclient_list_files(gate_ftpclient_t* client, gate_string_t const* path,
262 gate_file_list_callback_t cb, void* user_param)
263 {
264 gate_result_t ret = GATE_RESULT_FAILED;
265 HINTERNET hsession = (HINTERNET)client->handles[1];
266 TCHAR str_path[GATE_MAX_FILEPATH_LENGTH];
267 gate_size_t str_path_len;
268 HINTERNET hfind = NULL;
269 WIN32_FIND_DATA find_data;
270 gate_file_entry_t entry;
271 gate_size_t cFileNameLen;
272 gate_size_t name_len;
273
274 do
275 {
276 str_path_len = gate_win32_string_2_winstr(path, str_path, sizeof(str_path) / sizeof(str_path[0]));
277 if (str_path_len == 0)
278 {
279 ret = GATE_RESULT_INVALIDARG;
280 break;
281 }
282 if (str_path[str_path_len - 1] != '/')
283 {
284 str_path[str_path_len] = '/';
285 ++str_path_len;
286 str_path[str_path_len] = '\0';
287 }
288
289 hfind = WinInet.FTPFindFirstFile(hsession, str_path, &find_data, INTERNET_FLAG_RELOAD, (DWORD_PTR)client);
290 if (hfind == NULL)
291 {
292 ret = GATE_RESULT_EXECUTIONFAILED;
293 break;
294 }
295
296 ret = GATE_RESULT_OK;
297 do
298 {
299 gate_mem_clear(&entry, sizeof(entry));
300
301 cFileNameLen = gate_win32_winstr_length(find_data.cFileName);
302 name_len = gate_win32_winstr_2_utf8(find_data.cFileName, cFileNameLen, entry.name, sizeof(entry.name));
303 if ((gate_str_compare(entry.name, name_len, "..", 2) == 0) || (gate_str_compare(entry.name, name_len, "..", 2) == 0))
304 {
305 continue;
306 }
307
308 entry.props.time_accessed = convert_filetime(&find_data.ftLastAccessTime);
309 entry.props.time_created = convert_filetime(&find_data.ftCreationTime);
310 entry.props.time_modified = convert_filetime(&find_data.ftLastWriteTime);
311
312 entry.props.size = ((((gate_uint64_t)find_data.nFileSizeHigh) << 32) + ((gate_uint64_t)find_data.nFileSizeLow));
313
314 if (GATE_FLAG_ENABLED(find_data.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY))
315 {
316 entry.props.attribs = GATE_FILEENTRY_ATTRIB_DIRECTORY;
317 }
318 else
319 {
320 entry.props.attribs = GATE_FILEENTRY_ATTRIB_FILE;
321 }
322 if (GATE_FLAG_ENABLED(find_data.dwFileAttributes, FILE_ATTRIBUTE_READONLY)) entry.props.attribs |= GATE_FILEENTRY_ATTRIB_READONLY;
323 if (GATE_FLAG_ENABLED(find_data.dwFileAttributes, FILE_ATTRIBUTE_HIDDEN)) entry.props.attribs |= GATE_FILEENTRY_ATTRIB_HIDDEN;
324 if (GATE_FLAG_ENABLED(find_data.dwFileAttributes, FILE_ATTRIBUTE_SYSTEM)) entry.props.attribs |= GATE_FILEENTRY_ATTRIB_SYSTEM;
325 if (GATE_FLAG_ENABLED(find_data.dwFileAttributes, FILE_ATTRIBUTE_ARCHIVE)) entry.props.attribs |= GATE_FILEENTRY_ATTRIB_ARCHIVE;
326
327 if (cb)
328 {
329 if (!cb(&entry, user_param))
330 {
331 break;
332 }
333 }
334
335 } while (WinInet.InetFindNextFile(hfind, &find_data));
336 } while (0);
337
338 if (hfind != NULL)
339 {
340 WinInet.InetCloseHandle(hfind);
341 }
342 return ret;
343 }
344
345 gate_result_t gate_ftpclient_create_directory(gate_ftpclient_t* client, gate_string_t const* parent_dir,
346 gate_string_t const* newname)
347 {
348 gate_result_t ret = GATE_RESULT_FAILED;
349 HINTERNET hsession = (HINTERNET)client->handles[1];
350 TCHAR str_dir[GATE_MAX_FILEPATH_LENGTH] = GATE_INIT_EMPTY;
351 TCHAR str_name[GATE_MAX_FILEPATH_LENGTH] = GATE_INIT_EMPTY;
352 do
353 {
354 if (!gate_string_is_empty(parent_dir))
355 {
356 gate_win32_string_2_winstr(parent_dir, str_dir, sizeof(str_dir) / sizeof(str_dir[0]));
357 if (!WinInet.FTPSetCurrentDirectory(hsession, str_dir))
358 {
359 ret = gate_ftpclient_get_error();
360 break;
361 }
362 }
363 gate_win32_string_2_winstr(newname, str_name, sizeof(str_name) / sizeof(str_name[0]));
364
365 if (WinInet.FTPCreateDirectory(hsession, str_name))
366 {
367 ret = GATE_RESULT_OK;
368 }
369 else
370 {
371 ret = gate_ftpclient_get_error();
372 }
373 } while (0);
374 return ret;
375 }
376
377 gate_result_t gate_ftpclient_delete_directory(gate_ftpclient_t* client, gate_string_t const* path)
378 {
379 gate_result_t ret = GATE_RESULT_FAILED;
380 HINTERNET hsession = (HINTERNET)client->handles[1];
381 TCHAR str_path[GATE_MAX_FILEPATH_LENGTH] = GATE_INIT_EMPTY;
382
383 do
384 {
385 gate_win32_string_2_winstr(path, str_path, sizeof(str_path) / sizeof(str_path[0]));
386 if (!WinInet.FTPRemoveDirectory(hsession, str_path))
387 {
388 ret = gate_ftpclient_get_error();
389 }
390 else
391 {
392 ret = GATE_RESULT_OK;
393 }
394 } while (0);
395 return ret;
396 }
397
398 gate_result_t gate_ftpclient_delete_file(gate_ftpclient_t* client, gate_string_t const* path)
399 {
400 gate_result_t ret = GATE_RESULT_FAILED;
401 HINTERNET hsession = (HINTERNET)client->handles[1];
402 TCHAR str_path[GATE_MAX_FILEPATH_LENGTH] = GATE_INIT_EMPTY;
403
404 do
405 {
406 gate_win32_string_2_winstr(path, str_path, sizeof(str_path) / sizeof(str_path[0]));
407 if (!WinInet.FTPDeleteFile(hsession, str_path))
408 {
409 ret = GATE_RESULT_EXECUTIONFAILED;
410 }
411 else
412 {
413 ret = GATE_RESULT_OK;
414 }
415 } while (0);
416 return ret;
417 }
418
419 gate_result_t gate_ftpclient_rename(gate_ftpclient_t* client,
420 gate_string_t const* from_path, gate_string_t const* to_path)
421 {
422 gate_result_t ret = GATE_RESULT_FAILED;
423 HINTERNET hsession = (HINTERNET)client->handles[1];
424 TCHAR str_path_from[GATE_MAX_FILEPATH_LENGTH] = GATE_INIT_EMPTY;
425 TCHAR str_path_to[GATE_MAX_FILEPATH_LENGTH] = GATE_INIT_EMPTY;
426
427 do
428 {
429 gate_win32_string_2_winstr(from_path, str_path_from, sizeof(str_path_from) / sizeof(str_path_from[0]));
430 gate_win32_string_2_winstr(to_path, str_path_to, sizeof(str_path_to) / sizeof(str_path_to[0]));
431 if (!WinInet.FTPRenameFile(hsession, str_path_from, str_path_to))
432 {
433 ret = gate_ftpclient_get_error();
434 }
435 else
436 {
437 ret = GATE_RESULT_OK;
438 }
439 } while (0);
440 return ret;
441 }
442
443 gate_result_t gate_ftpclient_get_current_directory(gate_ftpclient_t* client,
444 gate_string_t* path)
445 {
446 gate_result_t ret = GATE_RESULT_FAILED;
447 HINTERNET hsession = (HINTERNET)client->handles[1];
448 TCHAR str_path[GATE_MAX_FILEPATH_LENGTH] = GATE_INIT_EMPTY;
449 DWORD str_path_len = (sizeof(str_path) / sizeof(str_path[0])) - 1;
450
451 do
452 {
453 if (!WinInet.FTPGetCurrentDirectory(hsession, str_path, &str_path_len))
454 {
455 ret = gate_ftpclient_get_error();
456 }
457 else
458 {
459 if (NULL == gate_win32_winstr_2_utf8_string(str_path, str_path_len, path))
460 {
461 ret = GATE_RESULT_OUTOFMEMORY;
462 }
463 else
464 {
465 ret = GATE_RESULT_OK;
466 }
467 }
468 } while (0);
469 return ret;
470 }
471
472 gate_result_t gate_ftpclient_set_current_directory(gate_ftpclient_t* client,
473 gate_string_t const* path)
474 {
475 gate_result_t ret = GATE_RESULT_FAILED;
476 HINTERNET hsession = (HINTERNET)client->handles[1];
477 TCHAR str_path[GATE_MAX_FILEPATH_LENGTH] = GATE_INIT_EMPTY;
478
479 do
480 {
481 gate_win32_string_2_winstr(path, str_path, sizeof(str_path) / sizeof(str_path[0]));
482 if (!WinInet.FTPSetCurrentDirectory(hsession, str_path))
483 {
484 ret = gate_ftpclient_get_error();
485 }
486 else
487 {
488 ret = GATE_RESULT_OK;
489 }
490 } while (0);
491 return ret;
492 }
493
494
495 static char const* gate_ftpstream_get_interface_name(void* obj);
496 static void gate_ftpstream_release(void* obj);
497 static int gate_ftpstream_retain(void* obj);
498
499 static gate_result_t gate_ftpstream_read(void* obj, char* buffer, gate_size_t bufferlength, gate_size_t* returned);
500 static gate_result_t gate_ftpstream_peek(void* obj, char* buffer, gate_size_t bufferlength, gate_size_t* returned);
501 static gate_result_t gate_ftpstream_write(void* obj, char const* buffer, gate_size_t bufferlength, gate_size_t* written);
502 static gate_result_t gate_ftpstream_flush(void* obj);
503
504
505 static GATE_INTERFACE_VTBL(gate_stream) gate_ftpstream_vtbl;
506 static void gate_init_ftpstream_vtbl()
507 {
508 if (!gate_ftpstream_vtbl.get_interface_name)
509 {
510 GATE_INTERFACE_VTBL(gate_stream) const local_vtbl =
511 {
512 &gate_ftpstream_get_interface_name,
513 &gate_ftpstream_release,
514 &gate_ftpstream_retain,
515
516 &gate_ftpstream_read,
517 &gate_ftpstream_peek,
518 &gate_ftpstream_write,
519 &gate_ftpstream_flush
520 };
521 gate_ftpstream_vtbl = local_vtbl;
522 }
523 }
524
525 typedef struct gate_ftpstream_class
526 {
527 GATE_INTERFACE_VTBL(gate_stream) const* vtbl;
528
529 gate_atomic_int_t ref_counter;
530 HINTERNET session;
531 gate_bool_t upload;
532 } gate_ftpstream_t;
533
534 static char const* gate_ftpstream_get_interface_name(void* obj)
535 {
536 GATE_UNUSED_ARG(obj);
537 return GATE_INTERFACE_NAME_STREAM;
538 }
539 static void gate_ftpstream_release(void* obj)
540 {
541 gate_ftpstream_t* stream = (gate_ftpstream_t*)obj;
542 if (0 == gate_atomic_int_dec(&stream->ref_counter))
543 {
544 WinInet.InetCloseHandle(stream->session);
545 gate_mem_dealloc(obj);
546 }
547 }
548 static int gate_ftpstream_retain(void* obj)
549 {
550 gate_ftpstream_t* stream = (gate_ftpstream_t*)obj;
551 return gate_atomic_int_inc(&stream->ref_counter);
552 }
553
554 static gate_result_t gate_ftpstream_read(void* obj, char* buffer, gate_size_t bufferlength, gate_size_t* returned)
555 {
556 gate_result_t ret = GATE_RESULT_FAILED;
557 gate_ftpstream_t* stream = (gate_ftpstream_t*)obj;
558 DWORD bytes_returned;
559 do
560 {
561 if (stream->upload)
562 {
563 ret = GATE_RESULT_INVALIDSTATE;
564 break;
565 }
566 if (WinInet.InetReadFile(stream->session, buffer, (DWORD)bufferlength, &bytes_returned))
567 {
568 if (returned)
569 {
570 *returned = (gate_size_t)bytes_returned;
571 }
572 ret = GATE_RESULT_OK;
573 }
574 } while (0);
575 return ret;
576 }
577 static gate_result_t gate_ftpstream_peek(void* obj, char* buffer, gate_size_t bufferlength, gate_size_t* returned)
578 {
579 gate_ftpstream_t* stream = (gate_ftpstream_t*)obj;
580 GATE_UNUSED_ARG(stream);
581 GATE_UNUSED_ARG(buffer);
582 GATE_UNUSED_ARG(bufferlength);
583 GATE_UNUSED_ARG(returned);
584 return GATE_RESULT_NOTSUPPORTED;
585 }
586 static gate_result_t gate_ftpstream_write(void* obj, char const* buffer, gate_size_t bufferlength, gate_size_t* written)
587 {
588 gate_result_t ret = GATE_RESULT_FAILED;
589 gate_ftpstream_t* stream = (gate_ftpstream_t*)obj;
590 DWORD bytes_written;
591 do
592 {
593 if (!stream->upload)
594 {
595 ret = GATE_RESULT_INVALIDSTATE;
596 break;
597 }
598 if (WinInet.InetWriteFile(stream->session, buffer, (DWORD)bufferlength, &bytes_written))
599 {
600 if (written)
601 {
602 *written = (gate_size_t)bytes_written;
603 }
604 ret = GATE_RESULT_OK;
605 }
606 } while (0);
607 return ret;
608 }
609 static gate_result_t gate_ftpstream_flush(void* obj)
610 {
611 GATE_UNUSED_ARG(obj);
612 return GATE_RESULT_OK;
613 }
614
615
616 gate_result_t gate_ftpclient_open_stream(gate_ftpclient_t* client, gate_string_t const* path,
617 gate_enumint_t stream_open_flags, gate_stream_t** ptr_stream)
618 {
619 gate_result_t ret = GATE_RESULT_FAILED;
620 HINTERNET hsession = (HINTERNET)client->handles[1];
621 gate_bool_t is_upload = GATE_FLAG_ENABLED(stream_open_flags, GATE_STREAM_OPEN_WRITE);
622 DWORD dwAccess = is_upload ? GENERIC_WRITE : GENERIC_READ;
623 DWORD dwFlags = FTP_TRANSFER_TYPE_BINARY;
624 HINTERNET hfile = NULL;
625 gate_ftpstream_t* ftpstream = NULL;
626 do
627 {
628 TCHAR ftppath[GATE_MAX_FILEPATH_LENGTH];
629 gate_win32_string_2_winstr(path, ftppath, sizeof(ftppath) / sizeof(ftppath[0]));
630
631 hfile = WinInet.FTPOpenFile(hsession, ftppath, dwAccess, dwFlags, (DWORD_PTR)client);
632 if (hfile == NULL)
633 {
634 ret = GATE_RESULT_FAILED;
635 break;
636 }
637 ftpstream = gate_mem_alloc(sizeof(gate_ftpstream_t));
638 if (ftpstream == NULL)
639 {
640 ret = GATE_RESULT_OUTOFMEMORY;
641 break;
642 }
643 gate_mem_clear(ftpstream, sizeof(gate_ftpstream_t));
644 gate_init_ftpstream_vtbl();
645 ftpstream->vtbl = &gate_ftpstream_vtbl;
646 gate_atomic_int_init(&ftpstream->ref_counter, 1);
647 ftpstream->upload = is_upload;
648 ftpstream->session = hfile;
649 hfile = NULL;
650 *ptr_stream = (gate_stream_t*)ftpstream;
651
652 ret = GATE_RESULT_OK;
653 } while (0);
654
655 if (hfile != NULL)
656 {
657 WinInet.InetCloseHandle(hfile);
658 }
659
660 return ret;
661 }
662
663
664
665 #endif
666
667 #if defined(GATE_NET_FTPCLIENT_USE_CURL)
668
669 #include "gate/net/platform/libcurl_api.h"
670 #include "gate/uris.h"
671 #include "gate/times.h"
672
673 static gate_result_t gate_curl_build_ftp_url(gate_ftpclient_t const* client, gate_string_t const* svr_path, gate_string_t* uri_path)
674 {
675 gate_result_t ret = GATE_RESULT_FAILED;
676 gate_uri_t uri;
677
678 do
679 {
680 gate_uri_init(&uri);
681 gate_string_create_static(&uri.scheme, GATE_URI_SCHEME_FTP);
682 gate_string_create_static(&uri.user_info, &client->config[0]);
683 gate_string_create_static(&uri.host, &client->server[0]);
684 uri.port = (client->port == 0) ? GATE_FTPCLIENT_DEFAULT_PORT : client->port;
685 if (!gate_string_is_empty(svr_path))
686 {
687 gate_string_duplicate(&uri.absolute_path, svr_path);
688 }
689 else
690 {
691 gate_string_create_static(&uri.absolute_path, "/");
692 }
693 ret = gate_uri_to_string(&uri, uri_path, false);
694 } while (0);
695
696 gate_uri_destroy(&uri);
697
698 return ret;
699 }
700
701 3 static gate_result_t gate_curl_ftp_setup(CURL* ptr_curl, gate_string_t const* url, gate_bool_t passive,
702 gate_stream_t* input_stream, gate_stream_t* output_stream)
703 {
704 3 gate_result_t ret = GATE_RESULT_FAILED;
705 static char const port_range[] = "-";
706
707 do
708 {
709 3 ret = gate_curl_setup(ptr_curl, url, input_stream, output_stream);
710
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 GATE_BREAK_IF_FAILED(ret);
711
712
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!passive)
713 {
714 if (CURLE_OK != curl.easy_setopt(ptr_curl, CURLOPT_FTPPORT, &port_range[0]))
715 {
716 ret = GATE_RESULT_FAILED;
717 break;
718 }
719 }
720
721 3 curl.easy_setopt(ptr_curl, CURLOPT_DNS_CACHE_TIMEOUT, (long)60);
722 3 curl.easy_setopt(ptr_curl, CURLOPT_CONNECTTIMEOUT, (long)5);
723 3 curl.easy_setopt(ptr_curl, CURLOPT_FTP_RESPONSE_TIMEOUT, (long)5);
724 3 curl.easy_setopt(ptr_curl, CURLOPT_ACCEPTTIMEOUT_MS, (long)5000);
725
726 } while (0);
727
728 3 return ret;
729 }
730
731 3 static gate_result_t gate_curl_ftp_setup_str(CURL* ptr_curl, char const* url, gate_bool_t passive,
732 gate_stream_t* input_stream, gate_stream_t* output_stream)
733 {
734 gate_string_t urlstr;
735 3 gate_string_create_static(&urlstr, url);
736 3 return gate_curl_ftp_setup(ptr_curl, &urlstr, passive, input_stream, output_stream);
737 }
738
739
740 1 gate_result_t gate_ftpclient_create(gate_ftpclient_t* client, gate_string_t const* server, gate_uint16_t port,
741 gate_string_t const* user, gate_string_t const* password,
742 gate_enumint_t flags)
743 {
744 static gate_string_t default_anonymous_login = GATE_STRING_INIT_STATIC("anonymous:anonymous%40opengate.at");
745 1 gate_result_t ret = GATE_RESULT_FAILED;
746 1 CURL* pcurl = NULL;
747 1 gate_string_t tmp = GATE_STRING_INIT_EMPTY;
748 gate_size_t len;
749
750 do
751 {
752
753 1 ret = load_libcurl_functions();
754
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
755
756 1 gate_mem_clear(client, sizeof(gate_ftpclient_t));
757
758
759 /* store server and base-url */
760 1 gate_string_to_buffer(server, client->server, sizeof(client->server));
761
762
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 if (!gate_string_is_empty(user) || !gate_string_is_empty(password))
763 {
764 1 len = 0;
765
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (!gate_string_is_empty(user))
766 {
767 1 ret = gate_uri_escape(user, &tmp);
768
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
769 1 len = gate_string_to_buffer(&tmp, client->config, sizeof(client->config));
770 1 gate_string_release(&tmp);
771
772
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (!gate_string_is_empty(password))
773 {
774 1 client->config[len] = ':';
775 1 ++len;
776
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (len >= sizeof(client->config))
777 {
778 len = sizeof(client->config) - 1;
779 client->config[len] = 0;
780 }
781 else
782 {
783 1 ret = gate_uri_escape(password, &tmp);
784
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
785 1 len = gate_string_to_buffer(&tmp, &client->config[len], sizeof(client->config) - len);
786 1 gate_string_release(&tmp);
787 }
788 }
789 }
790 else
791 {
792 gate_string_to_buffer(&default_anonymous_login, client->config, sizeof(client->config));
793 }
794 }
795
796 1 pcurl = curl.easy_init();
797
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!pcurl)
798 {
799 ret = GATE_RESULT_NOTAVAILABLE;
800 break;
801 }
802
803 1 client->port = port;
804 1 client->handles[0] = (void*)pcurl;
805 1 client->flags = flags;
806
807 1 pcurl = NULL;
808 1 ret = GATE_RESULT_OK;
809 } while (0);
810
811
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (pcurl)
812 {
813 curl.easy_cleanup(pcurl);
814 }
815 1 gate_string_release(&tmp);
816
817 1 return ret;
818 }
819
820 1 gate_result_t gate_ftpclient_release(gate_ftpclient_t* client)
821 {
822 1 CURL* ptr_curl = NULL;
823
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (client)
824 {
825 1 ptr_curl = (CURL*)client->handles[0];
826
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr_curl)
827 {
828 1 curl.easy_cleanup(ptr_curl);
829 }
830 1 gate_mem_clear(client, sizeof(gate_ftpclient_t));
831 }
832 1 return GATE_RESULT_OK;
833 }
834
835 3 static gate_size_t build_ftp_path(gate_ftpclient_t* client, gate_string_t const* path, gate_string_t const* subelement,
836 char* buffer, gate_size_t buffer_capacity, gate_size_t buffer_used)
837 {
838 gate_strbuilder_t builder;
839 3 gate_strbuilder_create_static(&builder, buffer, buffer_capacity, buffer_used);
840 3 gate_bool_t server_path_ends_with_slash = false;
841
842
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (client != NULL)
843 {
844 3 buffer_used += gate_strbuilder_append_cstr(&builder, GATE_URI_SCHEME_FTP);
845 3 buffer_used += gate_strbuilder_append_cstr(&builder, GATE_URI_SCHEME_SEPARATOR);
846 3 buffer_used += gate_strbuilder_append_cstr(&builder, client->config);
847 3 buffer_used += gate_strbuilder_append_cstr(&builder, "@");
848 3 buffer_used += gate_strbuilder_append_cstr(&builder, client->server);
849
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3 if ((client->port != GATE_FTPCLIENT_DEFAULT_PORT) && (client->port != 0))
850 {
851 buffer_used += gate_strbuilder_append_cstr(&builder, ":");
852 buffer_used += gate_strbuilder_append_uint16(&builder, client->port);
853 }
854 3 server_path_ends_with_slash = (buffer[buffer_used - 1] == '/');
855
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (!server_path_ends_with_slash)
856 {
857 /* we need a path separator */
858 3 buffer_used += gate_strbuilder_append_chars(&builder, 1, '/');
859 3 server_path_ends_with_slash = true;
860 }
861 }
862
863
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
3 if (!gate_string_is_empty(path))
864 {
865 /* skip path separator, if server path ends with one */
866 2 const gate_size_t offset =
867
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 (server_path_ends_with_slash && gate_string_starts_with_char(path, '/')) ? 1 : 0;
868 2 buffer_used += gate_strbuilder_append_text(&builder, gate_string_ptr(path, offset), gate_string_length(path) - offset);
869 2 server_path_ends_with_slash = (buffer[buffer_used - 1] == '/');
870 }
871
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if (!gate_string_is_empty(subelement))
872 {
873 const gate_bool_t starts_with_slash = gate_string_starts_with_char(path, '/');
874 if (server_path_ends_with_slash && starts_with_slash)
875 {
876 /* we need to cut one path separator */
877 buffer_used += gate_strbuilder_append_text(&builder, gate_string_ptr(subelement, 1), gate_string_length(subelement) - 1);
878 }
879 else
880 {
881 const gate_bool_t has_path = (client != NULL) || !gate_string_is_empty(path);
882 if (has_path && !server_path_ends_with_slash && !starts_with_slash)
883 {
884 /* we need a path separator */
885 buffer_used += gate_strbuilder_append_chars(&builder, 1, '/');
886 }
887 buffer_used += gate_strbuilder_append_text(&builder, gate_string_ptr(subelement, 0), gate_string_length(subelement));
888 }
889 }
890 3 return gate_strbuilder_length(&builder);
891 }
892
893 3 static gate_result_t gate_curl_ftp_perform(CURL* ptr_curl)
894 {
895 3 char const* errmsg = "Unknown CURL error";
896 3 CURLcode curlcode = curl.easy_perform(ptr_curl);
897 3 gate_result_t ret = gate_curl_map_result(curlcode, &errmsg);
898
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 GATE_DEBUG_TRACE_FAILED(ret, errmsg);
899 3 return ret;
900 }
901
902 #define TRACE_CURL_ERROR(curlcode, msg) do { \
903 if(CURLE_OK != curlcode) { \
904 GATE_DEBUG_TRACE(msg); \
905 } \
906 } while (0)
907
908 1 static gate_result_t gate_curl_ftp_execute(CURL* ptr_curl, struct curl_slist* pre_cmd, struct curl_slist* cmd, struct curl_slist* post_cmd)
909 {
910 gate_result_t result;
911 CURLcode curlcode;
912 do
913 {
914
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!cmd)
915 {
916 result = GATE_RESULT_INVALIDARG;
917 break;
918 }
919
920 /* setup parameters */
921 1 curlcode = curl.easy_setopt(ptr_curl, CURLOPT_PREQUOTE, pre_cmd);
922
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 TRACE_CURL_ERROR(curlcode, "easy_setopt(PREQUOTE) failed");
923 1 curlcode = curl.easy_setopt(ptr_curl, CURLOPT_QUOTE, cmd);
924
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 TRACE_CURL_ERROR(curlcode, "easy_setopt(CURLOPT_QUOTE) failed");
925 1 curlcode = curl.easy_setopt(ptr_curl, CURLOPT_POSTQUOTE, post_cmd);
926
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 TRACE_CURL_ERROR(curlcode, "easy_setopt(CURLOPT_POSTQUOTE) failed");
927
928 /* execute FTP command */
929 1 result = gate_curl_ftp_perform(ptr_curl);
930
931 /* reset parameters */
932 1 curl.easy_setopt(ptr_curl, CURLOPT_POSTQUOTE, NULL);
933
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 TRACE_CURL_ERROR(curlcode, "easy_setopt(POSTQUOTE) reset failed");
934 1 curl.easy_setopt(ptr_curl, CURLOPT_QUOTE, NULL);
935
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 TRACE_CURL_ERROR(curlcode, "easy_setopt(CURLOPT_QUOTE) reset failed");
936 1 curl.easy_setopt(ptr_curl, CURLOPT_PREQUOTE, NULL);
937
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 TRACE_CURL_ERROR(curlcode, "easy_setopt(PREQUOTE) reset failed");
938
939 } while (0);
940 1 return result;
941 }
942
943 3 static gate_result_t gate_curl_ftp_setup_default(gate_ftpclient_t* client, gate_string_t const* path, gate_stream_t* in_stream, gate_stream_t* out_stream)
944 {
945 3 CURL* const ptr_curl = (CURL*)client->handles[0];
946 3 gate_bool_t const passive = GATE_FLAG_ENABLED(client->flags, GATE_FTPCLIENT_FLAG_PASSIVE);
947 3 char url[GATE_MAX_FILEPATH_LENGTH] = "";
948 3 gate_size_t url_used = build_ftp_path(client, path, NULL, url, sizeof(url), 0);
949
950 3 return gate_curl_ftp_setup_str(ptr_curl, url, passive, in_stream, out_stream);
951 }
952
953 1 static gate_result_t gate_curl_ftp_execute_command(gate_ftpclient_t* client, char const* pre_cmd, char const* cmd, char const* post_cmd)
954 {
955 gate_result_t result;
956 1 struct curl_slist* ptr_pre_list = NULL;
957 1 struct curl_slist* ptr_cmd_list = NULL;
958 1 struct curl_slist* ptr_post_list = NULL;
959
960
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if ((client == NULL) || (cmd == NULL))
961 {
962 return GATE_RESULT_INVALIDARG;
963 }
964
965 do
966 {
967 1 CURL* const ptr_curl = (CURL*)client->handles[0];
968
969 1 result = gate_curl_ftp_setup_default(client, NULL, NULL, NULL);
970
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(result);
971
972
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (pre_cmd)
973 {
974 ptr_pre_list = curl.slist_append(NULL, pre_cmd);
975 }
976 1 ptr_cmd_list = curl.slist_append(NULL, cmd);
977
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (post_cmd)
978 {
979 ptr_post_list = curl.slist_append(NULL, post_cmd);
980 }
981
982 1 result = gate_curl_ftp_execute(ptr_curl, ptr_pre_list, ptr_cmd_list, ptr_post_list);
983
984 1 gate_curl_reset(ptr_curl);
985 } while (0);
986
987
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ptr_pre_list != NULL) curl.slist_free_all(ptr_pre_list);
988
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr_cmd_list != NULL) curl.slist_free_all(ptr_cmd_list);
989
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ptr_post_list != NULL) curl.slist_free_all(ptr_post_list);
990
991 1 return result;
992 }
993
994 1073 static gate_bool_t is_whitespace(char chr)
995 {
996
6/8
✓ Branch 0 taken 845 times.
✓ Branch 1 taken 228 times.
✓ Branch 2 taken 845 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 845 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 16 times.
✓ Branch 7 taken 829 times.
1073 return (chr == ' ') || (chr == '\t') || (chr == '\r') || (chr == '\n');
997 }
998
999 16 static gate_size_t tokenize_directory_entry(char const* line, gate_size_t linelen, gate_string_t* tokens, gate_size_t tokens_capacity)
1000 {
1001 16 char const* ptr_start = NULL;
1002 16 char const* ptr = line;
1003 16 char const* ptr_end = line + linelen;
1004 16 gate_size_t tokens_used = 0;
1005
1006
3/4
✓ Branch 0 taken 1073 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 1073 times.
✗ Branch 3 not taken.
1089 for (; (ptr != ptr_end) && (tokens_used != tokens_capacity); ++ptr)
1007 {
1008 1073 const gate_bool_t at_space = is_whitespace(*ptr);
1009
4/4
✓ Branch 0 taken 244 times.
✓ Branch 1 taken 829 times.
✓ Branch 2 taken 144 times.
✓ Branch 3 taken 100 times.
1073 if (at_space && (ptr_start != NULL))
1010 {
1011 /* end of token */
1012 144 gate_string_create_static_len(&tokens[tokens_used], ptr_start, ptr - ptr_start);
1013 144 ++tokens_used;
1014 144 ptr_start = NULL;
1015 }
1016
4/4
✓ Branch 0 taken 244 times.
✓ Branch 1 taken 685 times.
✓ Branch 2 taken 144 times.
✓ Branch 3 taken 100 times.
929 else if ((ptr_start == NULL) && !at_space)
1017 {
1018 /* begin of new token */
1019 144 ptr_start = ptr;
1020 }
1021 }
1022
1023 /* add last entry, if one was started */
1024
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
16 if ((ptr_start != NULL) && (tokens_used != tokens_capacity))
1025 {
1026 gate_string_create_static_len(&tokens[tokens_used], ptr_start, ptr - ptr_start);
1027 ++tokens_used;
1028 }
1029
1030 16 return tokens_used;
1031 }
1032
1033
1034 16 static void parse_access_bits(gate_string_t const* bits, gate_enumint_t* ptr_attribs, gate_enumint_t* ptr_access)
1035 {
1036 16 gate_enumint_t attribs = 0;
1037 16 gate_enumint_t access = 0;
1038 16 char const* ptr = gate_string_ptr(bits, 0);
1039 16 gate_size_t len = gate_string_length(bits);
1040
1041
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 GATE_FLAG_SET(attribs, GATE_FILEENTRY_ATTRIB_DIRECTORY, (ptr[0] == 'd'));
1042
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 GATE_FLAG_SET(attribs, GATE_FILEENTRY_ATTRIB_LINK, (ptr[0] == 'l'));
1043
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 GATE_FLAG_SET(attribs, GATE_FILEENTRY_ATTRIB_DEVICE, (ptr[0] == 'c'));
1044
2/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 GATE_FLAG_SET(attribs, GATE_FILEENTRY_ATTRIB_FILE, (ptr[0] != 'd') && (ptr[0] != 'c'));
1045
1046
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (len >= 10)
1047 {
1048
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 GATE_FLAG_SET(access, GATE_FILEENTRY_ACCESS_OWNERREAD, (ptr[1] == 'r'));
1049
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 6 times.
16 GATE_FLAG_SET(access, GATE_FILEENTRY_ACCESS_OWNERWRITE, (ptr[2] == 'w'));
1050
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 GATE_FLAG_SET(access, GATE_FILEENTRY_ACCESS_OWNEREXECUTE, (ptr[3] == 'x'));
1051
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 GATE_FLAG_SET(access, GATE_FILEENTRY_ACCESS_GROUPREAD, (ptr[4] == 'r'));
1052
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 GATE_FLAG_SET(access, GATE_FILEENTRY_ACCESS_GROUPWRITE, (ptr[5] == 'w'));
1053
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 GATE_FLAG_SET(access, GATE_FILEENTRY_ACCESS_GROUPEXECUTE, (ptr[6] == 'x'));
1054
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 GATE_FLAG_SET(access, GATE_FILEENTRY_ACCESS_ALLREAD, (ptr[7] == 'r'));
1055
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 GATE_FLAG_SET(access, GATE_FILEENTRY_ACCESS_ALLWRITE, (ptr[8] == 'w'));
1056
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 GATE_FLAG_SET(access, GATE_FILEENTRY_ACCESS_ALLEXECUTE, (ptr[9] == 'x'));
1057
1058
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 GATE_FLAG_SET(access, GATE_FILEENTRY_ACCESS_OWNEREXECUTE | GATE_FILEENTRY_ACCESS_OWNERSETID, (ptr[3] == 's'));
1059
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 GATE_FLAG_SET(access, GATE_FILEENTRY_ACCESS_GROUPEXECUTE | GATE_FILEENTRY_ACCESS_GROUPSETID, (ptr[6] == 's'));
1060
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 GATE_FLAG_SET(access, GATE_FILEENTRY_ACCESS_ALLEXECUTE | GATE_FILEENTRY_ACCESS_NODELETE, (ptr[9] == 't'));
1061 }
1062
1063
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 10 times.
16 if ((access & (GATE_FILEENTRY_ACCESS_OWNERWRITE | GATE_FILEENTRY_ACCESS_GROUPWRITE | GATE_FILEENTRY_ACCESS_ALLWRITE)) == 0)
1064 {
1065 6 attribs |= GATE_FILEENTRY_ATTRIB_READONLY;
1066 }
1067
1068
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (ptr_attribs) *ptr_attribs = attribs;
1069
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (ptr_access) *ptr_access = access;
1070 16 }
1071
1072 16 static void parse_year_time(gate_string_t const* text, gate_uint16_t* year, gate_uint8_t* hour, gate_uint8_t* minute)
1073 {
1074 16 char const* ptr = gate_string_ptr(text, 0);
1075 16 gate_size_t len = gate_string_length(text);
1076 16 gate_size_t pos = gate_str_char_pos(ptr, len, ':', 0);
1077 16 gate_uint64_t num = 0;
1078
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (pos == GATE_STR_NPOS)
1079 {
1080 /* we have year */
1081 16 gate_str_parse_uint64(ptr, len, &num);
1082 16 *year = (gate_uint16_t)num;
1083
1084 16 *hour = 0;
1085 16 *minute = 0;
1086 }
1087 else
1088 {
1089 gate_time_t tm;
1090 gate_datetime_t dt;
1091 gate_time_now(&tm);
1092 gate_time_to_datetime(&tm, &dt);
1093
1094 /* set current year */
1095 *year = dt.date.year;
1096
1097 gate_str_parse_uint64(ptr, pos, &num);
1098 *hour = (gate_uint8_t)num;
1099
1100 gate_str_parse_uint64(ptr + pos + 1, len - pos - 1, &num);
1101 *minute = (gate_uint8_t)num;
1102 }
1103 16 }
1104
1105 /* /////////////// some unix directory format samples /////////////// */
1106 /* "-rw-r--r-- 1 root other 531 Jan 29 03:26 README" */
1107 /* "dr-xr-xr-x 2 root other 512 Apr 8 1994 etc" */
1108 /* "dr-xr-xr-x 2 root 512 Apr 8 1994 etc" */
1109 /* "lrwxrwxrwx 1 root other 7 Jan 25 00:17 bin -> usr/bin" */
1110
1111
1112 /* "---------- 1 owner group 1803128 Jul 10 10:18 ls-lR.Z" */
1113 /* "d--------- 1 owner group 0 May 9 19:45 Softlib" */
1114
1115 /* "-rwxrwxrwx 1 noone nogroup 322 Aug 19 1996 message.ftp" */
1116
1117 /* "d [R----F--] supervisor 512 Jan 16 18:53 login" */
1118 /* "- [R----F--] rhesus 214059 Oct 20 15:27 cx.exe" */
1119
1120 /* "-------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit" */
1121 /* "drwxrwxr-x folder 2 May 10 1996 network" */
1122
1123 16 static gate_bool_t parse_unix_directory_entry(char const* line, gate_size_t linelen, gate_file_entry_t* ptr_entry)
1124 {
1125 gate_string_t tokens[16];
1126 16 gate_size_t tokens_used = tokenize_directory_entry(line, linelen, &tokens[0], sizeof(tokens) / sizeof(tokens[0]));
1127
1128 16 gate_mem_clear(ptr_entry, sizeof(gate_file_entry_t));
1129
1130
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (tokens_used >= 9)
1131 {
1132 16 gate_string_t const* permbits = &tokens[0];
1133 /* gate_string_t const* linkcount = &tokens[1]; */
1134 /* gate_string_t const* owner = &tokens[2]; */
1135 /* gate_string_t const* group = &tokens[3]; */
1136 16 gate_string_t const* filesize = &tokens[4];
1137 16 gate_string_t const* month = &tokens[5];
1138 16 gate_string_t const* day = &tokens[6];
1139 16 gate_string_t const* year_time = &tokens[7];
1140 16 gate_string_t const* filename = &tokens[8];
1141
1142
1143 16 parse_access_bits(permbits, &ptr_entry->props.attribs, &ptr_entry->props.access);
1144 16 gate_str_parse_uint64(gate_string_ptr(filesize, 0), gate_string_length(filesize), &ptr_entry->props.size);
1145
1146 {
1147 /* modification date / time */
1148 16 gate_datetime_t dt = GATE_INIT_EMPTY;
1149 16 gate_time_t tm = GATE_INIT_EMPTY;
1150 16 gate_uint64_t num = 0;
1151
1152 16 parse_year_time(year_time, &dt.date.year, &dt.time.hour, &dt.time.minute);
1153 16 gate_date_parse_month(gate_string_ptr(month, 0), gate_string_length(month), &dt.date.month);
1154 16 gate_string_parse_uint(day, &num);
1155 16 dt.date.day = (gate_uint8_t)num;
1156
1157 16 gate_date_to_time(&dt, &tm);
1158
1159 16 ptr_entry->props.time_modified = tm.timestamp;
1160 }
1161
1162 {
1163 /* get file name */
1164 16 char const* name_start = gate_string_ptr(filename, 0);
1165 16 char const* name_end = line + linelen;
1166 gate_string_t name;
1167
1168
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 GATE_DEBUG_ASSERT(name_end >= name_start);
1169 16 gate_string_create_static_len(&name, name_start, name_end - name_start);
1170
1171
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (GATE_FLAG_ENABLED(ptr_entry->props.attribs, GATE_FILEENTRY_ATTRIB_LINK))
1172 {
1173 /* remove link target path */
1174 static gate_string_t const arrow = GATE_STRING_INIT_STATIC("->");
1175 gate_size_t pos = gate_string_pos(&name, &arrow, 0);
1176 if (pos != GATE_STR_NPOS)
1177 {
1178 gate_string_substr(&name, &name, 0, pos);
1179 }
1180 }
1181
1182 16 gate_string_trim(&name, &name);
1183 16 gate_string_to_buffer(&name, ptr_entry->name, sizeof(ptr_entry->name));
1184 }
1185 16 return true;
1186 }
1187 return false;
1188 }
1189
1190 16 static gate_bool_t parse_directory_entry(char const* line, gate_size_t linelen, gate_file_entry_t* ptr_entry)
1191 {
1192 16 return parse_unix_directory_entry(line, linelen, ptr_entry);
1193 }
1194
1195 1 static gate_result_t parse_directory_listing(gate_stream_t* listing, gate_string_t const* dirpath, gate_file_list_callback_t cb, void* user_param)
1196 {
1197 static gate_string_t const dot = GATE_STRING_INIT_STATIC(".");
1198 static gate_string_t const dotdot = GATE_STRING_INIT_STATIC("..");
1199
1200 gate_result_t result;
1201 1 gate_bool_t continue_parsing = true;
1202
1203 do
1204 {
1205 char line[4096];
1206 17 gate_size_t linelen = 0;
1207 gate_file_entry_t entry;
1208
1209 17 result = gate_stream_read_line(listing, line, sizeof(line), &linelen);
1210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
18 GATE_BREAK_IF_FAILED(result);
1211
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 16 times.
17 if (linelen == 0)
1212 {
1213 /* end of stream */
1214 1 break;
1215 }
1216
2/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
16 if (cb && parse_directory_entry(line, linelen, &entry))
1217 {
1218 16 const gate_bool_t is_filtered_entry =
1219 16 gate_string_equals_str(&dot, entry.name)
1220
2/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 16 times.
16 || gate_string_equals_str(&dotdot, entry.name);
1221
1222
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (!is_filtered_entry)
1223 {
1224 /* generate full FTP file path from parent dir + file name */
1225 16 gate_size_t pathlen = gate_string_to_buffer(dirpath, entry.path, sizeof(entry.path) - 1);
1226
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (entry.path[pathlen - 1] != '/')
1227 {
1228 entry.path[pathlen] = '/';
1229 ++pathlen;
1230 entry.path[pathlen] = '\0';
1231 }
1232 16 gate_str_print_text(&entry.path[pathlen], sizeof(entry.path) - pathlen, entry.name, gate_str_length(entry.name));
1233
1234 16 continue_parsing &= cb(&entry, user_param);
1235 }
1236 }
1237
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 } while (continue_parsing);
1238
1239 1 return result;
1240 }
1241
1242 1 gate_result_t gate_ftpclient_list_files(gate_ftpclient_t* client, gate_string_t const* path,
1243 gate_file_list_callback_t cb, void* user_param)
1244 {
1245 gate_result_t ret;
1246 1 gate_memstream_t* ptr_stream = NULL;
1247
1248
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 if (!client || gate_string_is_empty(path))
1249 {
1250 return GATE_RESULT_INVALIDARG;
1251 }
1252
1253 do
1254 {
1255 1 CURL* const ptr_curl = (CURL*)client->handles[0];
1256
1257 1 ptr_stream = gate_memstream_create(1024);
1258
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == ptr_stream)
1259 {
1260 ret = GATE_RESULT_OUTOFMEMORY;
1261 break;
1262 }
1263 1 ret = gate_curl_ftp_setup_default(client, path, NULL, (gate_stream_t*)ptr_stream);
1264
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
1265
1266 1 ret = gate_curl_ftp_perform(ptr_curl);
1267
1268 1 gate_curl_reset(ptr_curl);
1269
1270
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
1271
1272 1 ret = parse_directory_listing((gate_stream_t*)ptr_stream, path, cb, user_param);
1273 } while (0);
1274
1275
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr_stream != NULL)
1276 {
1277 1 gate_object_release(ptr_stream);
1278 }
1279
1280 1 return ret;
1281 }
1282
1283 gate_result_t gate_ftpclient_create_directory(gate_ftpclient_t* client, gate_string_t const* parent_dir,
1284 gate_string_t const* newname)
1285 {
1286 char cmd_buffer[GATE_MAX_FILEPATH_LENGTH] = "MKD ";
1287 gate_size_t cmd_buffer_used = build_ftp_path(NULL, parent_dir, newname, cmd_buffer, sizeof(cmd_buffer), 4);
1288 gate_result_t result = gate_curl_ftp_execute_command(client, NULL, cmd_buffer, NULL);
1289 GATE_DEBUG_TRACE_FAILED(result, "gate_curl_ftp_execute_command(MKD) failed");
1290 return result;
1291 }
1292
1293 gate_result_t gate_ftpclient_delete_directory(gate_ftpclient_t* client, gate_string_t const* path)
1294 {
1295 char cmd_buffer[GATE_MAX_FILEPATH_LENGTH] = "RMD ";
1296 gate_size_t cmd_buffer_used = build_ftp_path(NULL, path, NULL, cmd_buffer, sizeof(cmd_buffer), 4);
1297 gate_result_t result = gate_curl_ftp_execute_command(client, NULL, cmd_buffer, NULL);
1298 GATE_DEBUG_TRACE_FAILED(result, "gate_curl_ftp_execute_command(RMD) failed");
1299 return result;
1300 }
1301
1302 gate_result_t gate_ftpclient_delete_file(gate_ftpclient_t* client, gate_string_t const* path)
1303 {
1304 char cmd_buffer[GATE_MAX_FILEPATH_LENGTH] = "DELE ";
1305 gate_size_t cmd_buffer_used = build_ftp_path(NULL, path, NULL, cmd_buffer, sizeof(cmd_buffer), 5);
1306 gate_result_t result = gate_curl_ftp_execute_command(client, NULL, cmd_buffer, NULL);
1307 GATE_DEBUG_TRACE_FAILED(result, "gate_curl_ftp_execute_command(DELE) failed");
1308 return result;
1309 }
1310
1311 gate_result_t gate_ftpclient_rename(gate_ftpclient_t* client, gate_string_t const* oldpath,
1312 gate_string_t const* newname)
1313 {
1314 char cmd1_buffer[GATE_MAX_FILEPATH_LENGTH] = "RNFR ";
1315 char cmd2_buffer[GATE_MAX_FILEPATH_LENGTH] = "RNTO ";
1316 gate_result_t result;
1317 CURL* const ptr_curl = (CURL*)client->handles[0];
1318 gate_size_t cmd1_buffer_used = build_ftp_path(NULL, oldpath, NULL, cmd1_buffer, sizeof(cmd1_buffer), 5);
1319 gate_size_t cmd2_buffer_used = build_ftp_path(NULL, newname, NULL, cmd2_buffer, sizeof(cmd2_buffer), 5);
1320 struct curl_slist* ptr_cmd_list = NULL;
1321
1322 do
1323 {
1324 ptr_cmd_list = curl.slist_append(NULL, cmd1_buffer);
1325 ptr_cmd_list = curl.slist_append(ptr_cmd_list, cmd2_buffer);
1326
1327 result = gate_curl_ftp_setup_default(client, NULL, NULL, NULL);
1328 GATE_BREAK_IF_FAILED(result);
1329
1330 result = gate_curl_ftp_execute(ptr_curl, NULL, ptr_cmd_list, NULL);
1331
1332 gate_curl_reset(ptr_curl);
1333 } while (0);
1334
1335 if (ptr_cmd_list) curl.slist_free_all(ptr_cmd_list);
1336
1337 return result;
1338 }
1339
1340 1 gate_result_t gate_ftpclient_get_current_directory(gate_ftpclient_t* client, gate_string_t* path)
1341 {
1342 gate_result_t result;
1343 do
1344 {
1345 1 CURL* const ptr_curl = (CURL*)client->handles[0];
1346 CURLcode curlcode;
1347 1 char* entry_path = NULL;
1348
1349 1 result = gate_curl_ftp_execute_command(client, NULL, "PWD", NULL);
1350
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(result);
1351
1352 1 curlcode = curl.easy_getinfo(ptr_curl, CURLINFO_FTP_ENTRY_PATH, &entry_path);
1353
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (CURLE_OK != curlcode)
1354 {
1355 char const* errmsg = NULL;
1356 result = gate_curl_map_result(curlcode, &errmsg);
1357 GATE_DEBUG_TRACE_FAILED_RESULT(result);
1358 GATE_DEBUG_TRACE_FAILED(result, errmsg);
1359 break;
1360 }
1361
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (NULL == gate_string_create(path, entry_path, gate_str_length(entry_path)))
1362 {
1363 result = GATE_RESULT_OUTOFMEMORY;
1364 break;
1365 }
1366 /* success case reached */
1367 } while (0);
1368
1369 1 return result;
1370 }
1371
1372 gate_result_t gate_ftpclient_set_current_directory(gate_ftpclient_t* client, gate_string_t const* path)
1373 {
1374 return GATE_RESULT_NOTIMPLEMENTED;
1375 }
1376
1377 1 gate_result_t get_download_stream(gate_ftpclient_t* client, gate_string_t const* path, gate_stream_t** ptr_stream)
1378 {
1379 gate_result_t ret;
1380 1 gate_memstream_t* ptr_memstream = NULL;
1381
1382
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 if (!client || gate_string_is_empty(path))
1383 {
1384 return GATE_RESULT_INVALIDARG;
1385 }
1386
1387 do
1388 {
1389 1 CURL* const ptr_curl = (CURL*)client->handles[0];
1390
1391 1 ptr_memstream = gate_memstream_create(1024);
1392
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == ptr_memstream)
1393 {
1394 ret = GATE_RESULT_OUTOFMEMORY;
1395 break;
1396 }
1397 1 ret = gate_curl_ftp_setup_default(client, path, NULL, (gate_stream_t*)ptr_memstream);
1398
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
1399
1400 1 ret = gate_curl_ftp_perform(ptr_curl);
1401
1402 1 gate_curl_reset(ptr_curl);
1403
1404
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
1405
1406
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr_stream)
1407 {
1408 1 *ptr_stream = (gate_stream_t*)ptr_memstream;
1409 1 ptr_memstream = NULL;
1410 }
1411 1 ret = GATE_RESULT_OK;
1412 } while (0);
1413
1414
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ptr_memstream != NULL)
1415 {
1416 gate_object_release(ptr_memstream);
1417 }
1418
1419 1 return ret;
1420 }
1421
1422 gate_result_t get_upload_stream(gate_ftpclient_t* client, gate_string_t const* path, gate_stream_t** ptr_stream)
1423 {
1424 /* TODO */
1425 return GATE_RESULT_NOTSUPPORTED;
1426 }
1427
1428 1 gate_result_t gate_ftpclient_open_stream(gate_ftpclient_t* client, gate_string_t const* path,
1429 gate_enumint_t stream_open_flags, gate_stream_t** ptr_stream)
1430 {
1431
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 if (!client || gate_string_is_empty(path))
1432 {
1433 return GATE_RESULT_INVALIDARG;
1434 }
1435
1436
1/3
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
1 switch (stream_open_flags)
1437 {
1438 1 case GATE_STREAM_OPEN_READ: return get_download_stream(client, path, ptr_stream);
1439 case GATE_STREAM_OPEN_WRITE: return get_upload_stream(client, path, ptr_stream);
1440 default: return GATE_RESULT_NOTSUPPORTED;
1441 }
1442 }
1443
1444 #endif
1445
1446
1447 #if defined(GATE_NET_FTPCLIENT_NO_IMPL)
1448
1449 gate_result_t gate_ftpclient_create(gate_ftpclient_t* client, gate_string_t const* server, gate_uint16_t port,
1450 gate_string_t const* user, gate_string_t const* password,
1451 gate_enumint_t flags)
1452 {
1453 GATE_UNUSED_ARG(client);
1454 GATE_UNUSED_ARG(server);
1455 GATE_UNUSED_ARG(port);
1456 GATE_UNUSED_ARG(user);
1457 GATE_UNUSED_ARG(password);
1458 GATE_UNUSED_ARG(flags);
1459 return GATE_RESULT_NOTIMPLEMENTED;
1460 }
1461
1462 gate_result_t gate_ftpclient_release(gate_ftpclient_t* client)
1463 {
1464 GATE_UNUSED_ARG(client);
1465 return GATE_RESULT_NOTIMPLEMENTED;
1466 }
1467
1468 gate_result_t gate_ftpclient_list_files(gate_ftpclient_t* client, gate_string_t const* path,
1469 gate_file_list_callback_t cb, void* user_param)
1470 {
1471 GATE_UNUSED_ARG(client);
1472 GATE_UNUSED_ARG(path);
1473 GATE_UNUSED_ARG(cb);
1474 GATE_UNUSED_ARG(user_param);
1475 return GATE_RESULT_NOTIMPLEMENTED;
1476 }
1477
1478 gate_result_t gate_ftpclient_create_directory(gate_ftpclient_t* client, gate_string_t const* parent_dir,
1479 gate_string_t const* newname)
1480 {
1481 GATE_UNUSED_ARG(client);
1482 GATE_UNUSED_ARG(parent_dir);
1483 GATE_UNUSED_ARG(newname);
1484 return GATE_RESULT_NOTIMPLEMENTED;
1485 }
1486
1487 gate_result_t gate_ftpclient_delete_directory(gate_ftpclient_t* client, gate_string_t const* path)
1488 {
1489 GATE_UNUSED_ARG(client);
1490 GATE_UNUSED_ARG(path);
1491 return GATE_RESULT_NOTIMPLEMENTED;
1492 }
1493
1494 gate_result_t gate_ftpclient_delete_file(gate_ftpclient_t* client, gate_string_t const* path)
1495 {
1496 GATE_UNUSED_ARG(client);
1497 GATE_UNUSED_ARG(path);
1498 return GATE_RESULT_NOTIMPLEMENTED;
1499 }
1500
1501 gate_result_t gate_ftpclient_rename(gate_ftpclient_t* client, gate_string_t const* oldpath,
1502 gate_string_t const* newname)
1503 {
1504 GATE_UNUSED_ARG(client);
1505 GATE_UNUSED_ARG(oldpath);
1506 GATE_UNUSED_ARG(newname);
1507 return GATE_RESULT_NOTIMPLEMENTED;
1508 }
1509
1510 gate_result_t gate_ftpclient_get_current_directory(gate_ftpclient_t* client, gate_string_t* path)
1511 {
1512 GATE_UNUSED_ARG(client);
1513 GATE_UNUSED_ARG(path);
1514 return GATE_RESULT_NOTIMPLEMENTED;
1515 }
1516
1517 gate_result_t gate_ftpclient_set_current_directory(gate_ftpclient_t* client, gate_string_t const* path)
1518 {
1519 GATE_UNUSED_ARG(client);
1520 GATE_UNUSED_ARG(path);
1521 return GATE_RESULT_NOTIMPLEMENTED;
1522 }
1523
1524 gate_result_t gate_ftpclient_open_stream(gate_ftpclient_t* client, gate_string_t const* path,
1525 gate_enumint_t stream_open_flags, gate_stream_t** ptr_stream)
1526 {
1527 GATE_UNUSED_ARG(client);
1528 GATE_UNUSED_ARG(path);
1529 GATE_UNUSED_ARG(stream_open_flags);
1530 GATE_UNUSED_ARG(ptr_stream);
1531 return GATE_RESULT_NOTIMPLEMENTED;
1532 }
1533
1534 #endif /* GATE_NET_FTPCLIENT_NO_IMPL */
1535