1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2022, Daniel Stenberg, <[email protected]>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25/**
26 * Now implemented:
27 *
28 * 1) Unix version 1
29 * drwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog
30 * 2) Unix version 2
31 * drwxr-xr-x 1 user01 ftp 512 Jan 29 1997 prog
32 * 3) Unix version 3
33 * drwxr-xr-x 1 1 1 512 Jan 29 23:32 prog
34 * 4) Unix symlink
35 * lrwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog -> prog2000
36 * 5) DOS style
37 * 01-29-97 11:32PM <DIR> prog
38 */
39
40#include "curl_setup.h"
41
42#ifndef CURL_DISABLE_FTP
43
44#include <curl/curl.h>
45
46#include "urldata.h"
47#include "fileinfo.h"
48#include "llist.h"
49#include "strtoofft.h"
50#include "ftp.h"
51#include "ftplistparser.h"
52#include "curl_fnmatch.h"
53#include "curl_memory.h"
54#include "multiif.h"
55/* The last #include file should be: */
56#include "memdebug.h"
57
58/* allocs buffer which will contain one line of LIST command response */
59#define FTP_BUFFER_ALLOCSIZE 160
60
61typedef enum {
62 PL_UNIX_TOTALSIZE = 0,
63 PL_UNIX_FILETYPE,
64 PL_UNIX_PERMISSION,
65 PL_UNIX_HLINKS,
66 PL_UNIX_USER,
67 PL_UNIX_GROUP,
68 PL_UNIX_SIZE,
69 PL_UNIX_TIME,
70 PL_UNIX_FILENAME,
71 PL_UNIX_SYMLINK
72} pl_unix_mainstate;
73
74typedef union {
75 enum {
76 PL_UNIX_TOTALSIZE_INIT = 0,
77 PL_UNIX_TOTALSIZE_READING
78 } total_dirsize;
79
80 enum {
81 PL_UNIX_HLINKS_PRESPACE = 0,
82 PL_UNIX_HLINKS_NUMBER
83 } hlinks;
84
85 enum {
86 PL_UNIX_USER_PRESPACE = 0,
87 PL_UNIX_USER_PARSING
88 } user;
89
90 enum {
91 PL_UNIX_GROUP_PRESPACE = 0,
92 PL_UNIX_GROUP_NAME
93 } group;
94
95 enum {
96 PL_UNIX_SIZE_PRESPACE = 0,
97 PL_UNIX_SIZE_NUMBER
98 } size;
99
100 enum {
101 PL_UNIX_TIME_PREPART1 = 0,
102 PL_UNIX_TIME_PART1,
103 PL_UNIX_TIME_PREPART2,
104 PL_UNIX_TIME_PART2,
105 PL_UNIX_TIME_PREPART3,
106 PL_UNIX_TIME_PART3
107 } time;
108
109 enum {
110 PL_UNIX_FILENAME_PRESPACE = 0,
111 PL_UNIX_FILENAME_NAME,
112 PL_UNIX_FILENAME_WINDOWSEOL
113 } filename;
114
115 enum {
116 PL_UNIX_SYMLINK_PRESPACE = 0,
117 PL_UNIX_SYMLINK_NAME,
118 PL_UNIX_SYMLINK_PRETARGET1,
119 PL_UNIX_SYMLINK_PRETARGET2,
120 PL_UNIX_SYMLINK_PRETARGET3,
121 PL_UNIX_SYMLINK_PRETARGET4,
122 PL_UNIX_SYMLINK_TARGET,
123 PL_UNIX_SYMLINK_WINDOWSEOL
124 } symlink;
125} pl_unix_substate;
126
127typedef enum {
128 PL_WINNT_DATE = 0,
129 PL_WINNT_TIME,
130 PL_WINNT_DIRORSIZE,
131 PL_WINNT_FILENAME
132} pl_winNT_mainstate;
133
134typedef union {
135 enum {
136 PL_WINNT_TIME_PRESPACE = 0,
137 PL_WINNT_TIME_TIME
138 } time;
139 enum {
140 PL_WINNT_DIRORSIZE_PRESPACE = 0,
141 PL_WINNT_DIRORSIZE_CONTENT
142 } dirorsize;
143 enum {
144 PL_WINNT_FILENAME_PRESPACE = 0,
145 PL_WINNT_FILENAME_CONTENT,
146 PL_WINNT_FILENAME_WINEOL
147 } filename;
148} pl_winNT_substate;
149
150/* This struct is used in wildcard downloading - for parsing LIST response */
151struct ftp_parselist_data {
152 enum {
153 OS_TYPE_UNKNOWN = 0,
154 OS_TYPE_UNIX,
155 OS_TYPE_WIN_NT
156 } os_type;
157
158 union {
159 struct {
160 pl_unix_mainstate main;
161 pl_unix_substate sub;
162 } UNIX;
163
164 struct {
165 pl_winNT_mainstate main;
166 pl_winNT_substate sub;
167 } NT;
168 } state;
169
170 CURLcode error;
171 struct fileinfo *file_data;
172 unsigned int item_length;
173 size_t item_offset;
174 struct {
175 size_t filename;
176 size_t user;
177 size_t group;
178 size_t time;
179 size_t perm;
180 size_t symlink_target;
181 } offsets;
182};
183
184struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void)
185{
186 return calloc(1, sizeof(struct ftp_parselist_data));
187}
188
189
190void Curl_ftp_parselist_data_free(struct ftp_parselist_data **parserp)
191{
192 struct ftp_parselist_data *parser = *parserp;
193 if(parser)
194 Curl_fileinfo_cleanup(parser->file_data);
195 free(parser);
196 *parserp = NULL;
197}
198
199
200CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data)
201{
202 return pl_data->error;
203}
204
205
206#define FTP_LP_MALFORMATED_PERM 0x01000000
207
208static int ftp_pl_get_permission(const char *str)
209{
210 int permissions = 0;
211 /* USER */
212 if(str[0] == 'r')
213 permissions |= 1 << 8;
214 else if(str[0] != '-')
215 permissions |= FTP_LP_MALFORMATED_PERM;
216 if(str[1] == 'w')
217 permissions |= 1 << 7;
218 else if(str[1] != '-')
219 permissions |= FTP_LP_MALFORMATED_PERM;
220
221 if(str[2] == 'x')
222 permissions |= 1 << 6;
223 else if(str[2] == 's') {
224 permissions |= 1 << 6;
225 permissions |= 1 << 11;
226 }
227 else if(str[2] == 'S')
228 permissions |= 1 << 11;
229 else if(str[2] != '-')
230 permissions |= FTP_LP_MALFORMATED_PERM;
231 /* GROUP */
232 if(str[3] == 'r')
233 permissions |= 1 << 5;
234 else if(str[3] != '-')
235 permissions |= FTP_LP_MALFORMATED_PERM;
236 if(str[4] == 'w')
237 permissions |= 1 << 4;
238 else if(str[4] != '-')
239 permissions |= FTP_LP_MALFORMATED_PERM;
240 if(str[5] == 'x')
241 permissions |= 1 << 3;
242 else if(str[5] == 's') {
243 permissions |= 1 << 3;
244 permissions |= 1 << 10;
245 }
246 else if(str[5] == 'S')
247 permissions |= 1 << 10;
248 else if(str[5] != '-')
249 permissions |= FTP_LP_MALFORMATED_PERM;
250 /* others */
251 if(str[6] == 'r')
252 permissions |= 1 << 2;
253 else if(str[6] != '-')
254 permissions |= FTP_LP_MALFORMATED_PERM;
255 if(str[7] == 'w')
256 permissions |= 1 << 1;
257 else if(str[7] != '-')
258 permissions |= FTP_LP_MALFORMATED_PERM;
259 if(str[8] == 'x')
260 permissions |= 1;
261 else if(str[8] == 't') {
262 permissions |= 1;
263 permissions |= 1 << 9;
264 }
265 else if(str[8] == 'T')
266 permissions |= 1 << 9;
267 else if(str[8] != '-')
268 permissions |= FTP_LP_MALFORMATED_PERM;
269
270 return permissions;
271}
272
273static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data,
274 struct fileinfo *infop)
275{
276 curl_fnmatch_callback compare;
277 struct WildcardData *wc = &data->wildcard;
278 struct ftp_wc *ftpwc = wc->protdata;
279 struct Curl_llist *llist = &wc->filelist;
280 struct ftp_parselist_data *parser = ftpwc->parser;
281 bool add = TRUE;
282 struct curl_fileinfo *finfo = &infop->info;
283
284 /* move finfo pointers to b_data */
285 char *str = finfo->b_data;
286 finfo->filename = str + parser->offsets.filename;
287 finfo->strings.group = parser->offsets.group ?
288 str + parser->offsets.group : NULL;
289 finfo->strings.perm = parser->offsets.perm ?
290 str + parser->offsets.perm : NULL;
291 finfo->strings.target = parser->offsets.symlink_target ?
292 str + parser->offsets.symlink_target : NULL;
293 finfo->strings.time = str + parser->offsets.time;
294 finfo->strings.user = parser->offsets.user ?
295 str + parser->offsets.user : NULL;
296
297 /* get correct fnmatch callback */
298 compare = data->set.fnmatch;
299 if(!compare)
300 compare = Curl_fnmatch;
301
302 /* filter pattern-corresponding filenames */
303 Curl_set_in_callback(data, true);
304 if(compare(data->set.fnmatch_data, wc->pattern,
305 finfo->filename) == 0) {
306 /* discard symlink which is containing multiple " -> " */
307 if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target &&
308 (strstr(finfo->strings.target, " -> "))) {
309 add = FALSE;
310 }
311 }
312 else {
313 add = FALSE;
314 }
315 Curl_set_in_callback(data, false);
316
317 if(add) {
318 Curl_llist_insert_next(llist, llist->tail, finfo, &infop->list);
319 }
320 else {
321 Curl_fileinfo_cleanup(infop);
322 }
323
324 ftpwc->parser->file_data = NULL;
325 return CURLE_OK;
326}
327
328size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
329 void *connptr)
330{
331 size_t bufflen = size*nmemb;
332 struct Curl_easy *data = (struct Curl_easy *)connptr;
333 struct ftp_wc *ftpwc = data->wildcard.protdata;
334 struct ftp_parselist_data *parser = ftpwc->parser;
335 struct fileinfo *infop;
336 struct curl_fileinfo *finfo;
337 unsigned long i = 0;
338 CURLcode result;
339 size_t retsize = bufflen;
340
341 if(parser->error) { /* error in previous call */
342 /* scenario:
343 * 1. call => OK..
344 * 2. call => OUT_OF_MEMORY (or other error)
345 * 3. (last) call => is skipped RIGHT HERE and the error is hadled later
346 * in wc_statemach()
347 */
348 goto fail;
349 }
350
351 if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) {
352 /* considering info about FILE response format */
353 parser->os_type = (buffer[0] >= '0' && buffer[0] <= '9') ?
354 OS_TYPE_WIN_NT : OS_TYPE_UNIX;
355 }
356
357 while(i < bufflen) { /* FSM */
358
359 char c = buffer[i];
360 if(!parser->file_data) { /* tmp file data is not allocated yet */
361 parser->file_data = Curl_fileinfo_alloc();
362 if(!parser->file_data) {
363 parser->error = CURLE_OUT_OF_MEMORY;
364 goto fail;
365 }
366 parser->file_data->info.b_data = malloc(FTP_BUFFER_ALLOCSIZE);
367 if(!parser->file_data->info.b_data) {
368 parser->error = CURLE_OUT_OF_MEMORY;
369 goto fail;
370 }
371 parser->file_data->info.b_size = FTP_BUFFER_ALLOCSIZE;
372 parser->item_offset = 0;
373 parser->item_length = 0;
374 }
375
376 infop = parser->file_data;
377 finfo = &infop->info;
378 finfo->b_data[finfo->b_used++] = c;
379
380 if(finfo->b_used >= finfo->b_size - 1) {
381 /* if it is important, extend buffer space for file data */
382 char *tmp = realloc(finfo->b_data,
383 finfo->b_size + FTP_BUFFER_ALLOCSIZE);
384 if(tmp) {
385 finfo->b_size += FTP_BUFFER_ALLOCSIZE;
386 finfo->b_data = tmp;
387 }
388 else {
389 Curl_fileinfo_cleanup(parser->file_data);
390 parser->file_data = NULL;
391 parser->error = CURLE_OUT_OF_MEMORY;
392 goto fail;
393 }
394 }
395
396 switch(parser->os_type) {
397 case OS_TYPE_UNIX:
398 switch(parser->state.UNIX.main) {
399 case PL_UNIX_TOTALSIZE:
400 switch(parser->state.UNIX.sub.total_dirsize) {
401 case PL_UNIX_TOTALSIZE_INIT:
402 if(c == 't') {
403 parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
404 parser->item_length++;
405 }
406 else {
407 parser->state.UNIX.main = PL_UNIX_FILETYPE;
408 /* start FSM again not considering size of directory */
409 finfo->b_used = 0;
410 continue;
411 }
412 break;
413 case PL_UNIX_TOTALSIZE_READING:
414 parser->item_length++;
415 if(c == '\r') {
416 parser->item_length--;
417 finfo->b_used--;
418 }
419 else if(c == '\n') {
420 finfo->b_data[parser->item_length - 1] = 0;
421 if(strncmp("total ", finfo->b_data, 6) == 0) {
422 char *endptr = finfo->b_data + 6;
423 /* here we can deal with directory size, pass the leading
424 whitespace and then the digits */
425 while(ISSPACE(*endptr))
426 endptr++;
427 while(ISDIGIT(*endptr))
428 endptr++;
429 if(*endptr) {
430 parser->error = CURLE_FTP_BAD_FILE_LIST;
431 goto fail;
432 }
433 parser->state.UNIX.main = PL_UNIX_FILETYPE;
434 finfo->b_used = 0;
435 }
436 else {
437 parser->error = CURLE_FTP_BAD_FILE_LIST;
438 goto fail;
439 }
440 }
441 break;
442 }
443 break;
444 case PL_UNIX_FILETYPE:
445 switch(c) {
446 case '-':
447 finfo->filetype = CURLFILETYPE_FILE;
448 break;
449 case 'd':
450 finfo->filetype = CURLFILETYPE_DIRECTORY;
451 break;
452 case 'l':
453 finfo->filetype = CURLFILETYPE_SYMLINK;
454 break;
455 case 'p':
456 finfo->filetype = CURLFILETYPE_NAMEDPIPE;
457 break;
458 case 's':
459 finfo->filetype = CURLFILETYPE_SOCKET;
460 break;
461 case 'c':
462 finfo->filetype = CURLFILETYPE_DEVICE_CHAR;
463 break;
464 case 'b':
465 finfo->filetype = CURLFILETYPE_DEVICE_BLOCK;
466 break;
467 case 'D':
468 finfo->filetype = CURLFILETYPE_DOOR;
469 break;
470 default:
471 parser->error = CURLE_FTP_BAD_FILE_LIST;
472 goto fail;
473 }
474 parser->state.UNIX.main = PL_UNIX_PERMISSION;
475 parser->item_length = 0;
476 parser->item_offset = 1;
477 break;
478 case PL_UNIX_PERMISSION:
479 parser->item_length++;
480 if(parser->item_length <= 9) {
481 if(!strchr("rwx-tTsS", c)) {
482 parser->error = CURLE_FTP_BAD_FILE_LIST;
483 goto fail;
484 }
485 }
486 else if(parser->item_length == 10) {
487 unsigned int perm;
488 if(c != ' ') {
489 parser->error = CURLE_FTP_BAD_FILE_LIST;
490 goto fail;
491 }
492 finfo->b_data[10] = 0; /* terminate permissions */
493 perm = ftp_pl_get_permission(finfo->b_data + parser->item_offset);
494 if(perm & FTP_LP_MALFORMATED_PERM) {
495 parser->error = CURLE_FTP_BAD_FILE_LIST;
496 goto fail;
497 }
498 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM;
499 parser->file_data->info.perm = perm;
500 parser->offsets.perm = parser->item_offset;
501
502 parser->item_length = 0;
503 parser->state.UNIX.main = PL_UNIX_HLINKS;
504 parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
505 }
506 break;
507 case PL_UNIX_HLINKS:
508 switch(parser->state.UNIX.sub.hlinks) {
509 case PL_UNIX_HLINKS_PRESPACE:
510 if(c != ' ') {
511 if(c >= '0' && c <= '9') {
512 parser->item_offset = finfo->b_used - 1;
513 parser->item_length = 1;
514 parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
515 }
516 else {
517 parser->error = CURLE_FTP_BAD_FILE_LIST;
518 goto fail;
519 }
520 }
521 break;
522 case PL_UNIX_HLINKS_NUMBER:
523 parser->item_length ++;
524 if(c == ' ') {
525 char *p;
526 long int hlinks;
527 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
528 hlinks = strtol(finfo->b_data + parser->item_offset, &p, 10);
529 if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) {
530 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
531 parser->file_data->info.hardlinks = hlinks;
532 }
533 parser->item_length = 0;
534 parser->item_offset = 0;
535 parser->state.UNIX.main = PL_UNIX_USER;
536 parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
537 }
538 else if(c < '0' || c > '9') {
539 parser->error = CURLE_FTP_BAD_FILE_LIST;
540 goto fail;
541 }
542 break;
543 }
544 break;
545 case PL_UNIX_USER:
546 switch(parser->state.UNIX.sub.user) {
547 case PL_UNIX_USER_PRESPACE:
548 if(c != ' ') {
549 parser->item_offset = finfo->b_used - 1;
550 parser->item_length = 1;
551 parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
552 }
553 break;
554 case PL_UNIX_USER_PARSING:
555 parser->item_length++;
556 if(c == ' ') {
557 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
558 parser->offsets.user = parser->item_offset;
559 parser->state.UNIX.main = PL_UNIX_GROUP;
560 parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
561 parser->item_offset = 0;
562 parser->item_length = 0;
563 }
564 break;
565 }
566 break;
567 case PL_UNIX_GROUP:
568 switch(parser->state.UNIX.sub.group) {
569 case PL_UNIX_GROUP_PRESPACE:
570 if(c != ' ') {
571 parser->item_offset = finfo->b_used - 1;
572 parser->item_length = 1;
573 parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
574 }
575 break;
576 case PL_UNIX_GROUP_NAME:
577 parser->item_length++;
578 if(c == ' ') {
579 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
580 parser->offsets.group = parser->item_offset;
581 parser->state.UNIX.main = PL_UNIX_SIZE;
582 parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
583 parser->item_offset = 0;
584 parser->item_length = 0;
585 }
586 break;
587 }
588 break;
589 case PL_UNIX_SIZE:
590 switch(parser->state.UNIX.sub.size) {
591 case PL_UNIX_SIZE_PRESPACE:
592 if(c != ' ') {
593 if(c >= '0' && c <= '9') {
594 parser->item_offset = finfo->b_used - 1;
595 parser->item_length = 1;
596 parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
597 }
598 else {
599 parser->error = CURLE_FTP_BAD_FILE_LIST;
600 goto fail;
601 }
602 }
603 break;
604 case PL_UNIX_SIZE_NUMBER:
605 parser->item_length++;
606 if(c == ' ') {
607 char *p;
608 curl_off_t fsize;
609 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
610 if(!curlx_strtoofft(finfo->b_data + parser->item_offset,
611 &p, 10, &fsize)) {
612 if(p[0] == '\0' && fsize != CURL_OFF_T_MAX &&
613 fsize != CURL_OFF_T_MIN) {
614 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
615 parser->file_data->info.size = fsize;
616 }
617 parser->item_length = 0;
618 parser->item_offset = 0;
619 parser->state.UNIX.main = PL_UNIX_TIME;
620 parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
621 }
622 }
623 else if(!ISDIGIT(c)) {
624 parser->error = CURLE_FTP_BAD_FILE_LIST;
625 goto fail;
626 }
627 break;
628 }
629 break;
630 case PL_UNIX_TIME:
631 switch(parser->state.UNIX.sub.time) {
632 case PL_UNIX_TIME_PREPART1:
633 if(c != ' ') {
634 if(ISALNUM(c)) {
635 parser->item_offset = finfo->b_used -1;
636 parser->item_length = 1;
637 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
638 }
639 else {
640 parser->error = CURLE_FTP_BAD_FILE_LIST;
641 goto fail;
642 }
643 }
644 break;
645 case PL_UNIX_TIME_PART1:
646 parser->item_length++;
647 if(c == ' ') {
648 parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
649 }
650 else if(!ISALNUM(c) && c != '.') {
651 parser->error = CURLE_FTP_BAD_FILE_LIST;
652 goto fail;
653 }
654 break;
655 case PL_UNIX_TIME_PREPART2:
656 parser->item_length++;
657 if(c != ' ') {
658 if(ISALNUM(c)) {
659 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
660 }
661 else {
662 parser->error = CURLE_FTP_BAD_FILE_LIST;
663 goto fail;
664 }
665 }
666 break;
667 case PL_UNIX_TIME_PART2:
668 parser->item_length++;
669 if(c == ' ') {
670 parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
671 }
672 else if(!ISALNUM(c) && c != '.') {
673 parser->error = CURLE_FTP_BAD_FILE_LIST;
674 goto fail;
675 }
676 break;
677 case PL_UNIX_TIME_PREPART3:
678 parser->item_length++;
679 if(c != ' ') {
680 if(ISALNUM(c)) {
681 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
682 }
683 else {
684 parser->error = CURLE_FTP_BAD_FILE_LIST;
685 goto fail;
686 }
687 }
688 break;
689 case PL_UNIX_TIME_PART3:
690 parser->item_length++;
691 if(c == ' ') {
692 finfo->b_data[parser->item_offset + parser->item_length -1] = 0;
693 parser->offsets.time = parser->item_offset;
694 /*
695 if(ftp_pl_gettime(parser, finfo->b_data + parser->item_offset)) {
696 parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME;
697 }
698 */
699 if(finfo->filetype == CURLFILETYPE_SYMLINK) {
700 parser->state.UNIX.main = PL_UNIX_SYMLINK;
701 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
702 }
703 else {
704 parser->state.UNIX.main = PL_UNIX_FILENAME;
705 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
706 }
707 }
708 else if(!ISALNUM(c) && c != '.' && c != ':') {
709 parser->error = CURLE_FTP_BAD_FILE_LIST;
710 goto fail;
711 }
712 break;
713 }
714 break;
715 case PL_UNIX_FILENAME:
716 switch(parser->state.UNIX.sub.filename) {
717 case PL_UNIX_FILENAME_PRESPACE:
718 if(c != ' ') {
719 parser->item_offset = finfo->b_used - 1;
720 parser->item_length = 1;
721 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
722 }
723 break;
724 case PL_UNIX_FILENAME_NAME:
725 parser->item_length++;
726 if(c == '\r') {
727 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
728 }
729 else if(c == '\n') {
730 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
731 parser->offsets.filename = parser->item_offset;
732 parser->state.UNIX.main = PL_UNIX_FILETYPE;
733 result = ftp_pl_insert_finfo(data, infop);
734 if(result) {
735 parser->error = result;
736 goto fail;
737 }
738 }
739 break;
740 case PL_UNIX_FILENAME_WINDOWSEOL:
741 if(c == '\n') {
742 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
743 parser->offsets.filename = parser->item_offset;
744 parser->state.UNIX.main = PL_UNIX_FILETYPE;
745 result = ftp_pl_insert_finfo(data, infop);
746 if(result) {
747 parser->error = result;
748 goto fail;
749 }
750 }
751 else {
752 parser->error = CURLE_FTP_BAD_FILE_LIST;
753 goto fail;
754 }
755 break;
756 }
757 break;
758 case PL_UNIX_SYMLINK:
759 switch(parser->state.UNIX.sub.symlink) {
760 case PL_UNIX_SYMLINK_PRESPACE:
761 if(c != ' ') {
762 parser->item_offset = finfo->b_used - 1;
763 parser->item_length = 1;
764 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
765 }
766 break;
767 case PL_UNIX_SYMLINK_NAME:
768 parser->item_length++;
769 if(c == ' ') {
770 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
771 }
772 else if(c == '\r' || c == '\n') {
773 parser->error = CURLE_FTP_BAD_FILE_LIST;
774 goto fail;
775 }
776 break;
777 case PL_UNIX_SYMLINK_PRETARGET1:
778 parser->item_length++;
779 if(c == '-') {
780 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
781 }
782 else if(c == '\r' || c == '\n') {
783 parser->error = CURLE_FTP_BAD_FILE_LIST;
784 goto fail;
785 }
786 else {
787 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
788 }
789 break;
790 case PL_UNIX_SYMLINK_PRETARGET2:
791 parser->item_length++;
792 if(c == '>') {
793 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
794 }
795 else if(c == '\r' || c == '\n') {
796 parser->error = CURLE_FTP_BAD_FILE_LIST;
797 goto fail;
798 }
799 else {
800 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
801 }
802 break;
803 case PL_UNIX_SYMLINK_PRETARGET3:
804 parser->item_length++;
805 if(c == ' ') {
806 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
807 /* now place where is symlink following */
808 finfo->b_data[parser->item_offset + parser->item_length - 4] = 0;
809 parser->offsets.filename = parser->item_offset;
810 parser->item_length = 0;
811 parser->item_offset = 0;
812 }
813 else if(c == '\r' || c == '\n') {
814 parser->error = CURLE_FTP_BAD_FILE_LIST;
815 goto fail;
816 }
817 else {
818 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
819 }
820 break;
821 case PL_UNIX_SYMLINK_PRETARGET4:
822 if(c != '\r' && c != '\n') {
823 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
824 parser->item_offset = finfo->b_used - 1;
825 parser->item_length = 1;
826 }
827 else {
828 parser->error = CURLE_FTP_BAD_FILE_LIST;
829 goto fail;
830 }
831 break;
832 case PL_UNIX_SYMLINK_TARGET:
833 parser->item_length++;
834 if(c == '\r') {
835 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
836 }
837 else if(c == '\n') {
838 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
839 parser->offsets.symlink_target = parser->item_offset;
840 result = ftp_pl_insert_finfo(data, infop);
841 if(result) {
842 parser->error = result;
843 goto fail;
844 }
845 parser->state.UNIX.main = PL_UNIX_FILETYPE;
846 }
847 break;
848 case PL_UNIX_SYMLINK_WINDOWSEOL:
849 if(c == '\n') {
850 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
851 parser->offsets.symlink_target = parser->item_offset;
852 result = ftp_pl_insert_finfo(data, infop);
853 if(result) {
854 parser->error = result;
855 goto fail;
856 }
857 parser->state.UNIX.main = PL_UNIX_FILETYPE;
858 }
859 else {
860 parser->error = CURLE_FTP_BAD_FILE_LIST;
861 goto fail;
862 }
863 break;
864 }
865 break;
866 }
867 break;
868 case OS_TYPE_WIN_NT:
869 switch(parser->state.NT.main) {
870 case PL_WINNT_DATE:
871 parser->item_length++;
872 if(parser->item_length < 9) {
873 if(!strchr("0123456789-", c)) { /* only simple control */
874 parser->error = CURLE_FTP_BAD_FILE_LIST;
875 goto fail;
876 }
877 }
878 else if(parser->item_length == 9) {
879 if(c == ' ') {
880 parser->state.NT.main = PL_WINNT_TIME;
881 parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE;
882 }
883 else {
884 parser->error = CURLE_FTP_BAD_FILE_LIST;
885 goto fail;
886 }
887 }
888 else {
889 parser->error = CURLE_FTP_BAD_FILE_LIST;
890 goto fail;
891 }
892 break;
893 case PL_WINNT_TIME:
894 parser->item_length++;
895 switch(parser->state.NT.sub.time) {
896 case PL_WINNT_TIME_PRESPACE:
897 if(!ISSPACE(c)) {
898 parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
899 }
900 break;
901 case PL_WINNT_TIME_TIME:
902 if(c == ' ') {
903 parser->offsets.time = parser->item_offset;
904 finfo->b_data[parser->item_offset + parser->item_length -1] = 0;
905 parser->state.NT.main = PL_WINNT_DIRORSIZE;
906 parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE;
907 parser->item_length = 0;
908 }
909 else if(!strchr("APM0123456789:", c)) {
910 parser->error = CURLE_FTP_BAD_FILE_LIST;
911 goto fail;
912 }
913 break;
914 }
915 break;
916 case PL_WINNT_DIRORSIZE:
917 switch(parser->state.NT.sub.dirorsize) {
918 case PL_WINNT_DIRORSIZE_PRESPACE:
919 if(c != ' ') {
920 parser->item_offset = finfo->b_used - 1;
921 parser->item_length = 1;
922 parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
923 }
924 break;
925 case PL_WINNT_DIRORSIZE_CONTENT:
926 parser->item_length ++;
927 if(c == ' ') {
928 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
929 if(strcmp("<DIR>", finfo->b_data + parser->item_offset) == 0) {
930 finfo->filetype = CURLFILETYPE_DIRECTORY;
931 finfo->size = 0;
932 }
933 else {
934 char *endptr;
935 if(curlx_strtoofft(finfo->b_data +
936 parser->item_offset,
937 &endptr, 10, &finfo->size)) {
938 parser->error = CURLE_FTP_BAD_FILE_LIST;
939 goto fail;
940 }
941 /* correct file type */
942 parser->file_data->info.filetype = CURLFILETYPE_FILE;
943 }
944
945 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
946 parser->item_length = 0;
947 parser->state.NT.main = PL_WINNT_FILENAME;
948 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
949 }
950 break;
951 }
952 break;
953 case PL_WINNT_FILENAME:
954 switch(parser->state.NT.sub.filename) {
955 case PL_WINNT_FILENAME_PRESPACE:
956 if(c != ' ') {
957 parser->item_offset = finfo->b_used -1;
958 parser->item_length = 1;
959 parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
960 }
961 break;
962 case PL_WINNT_FILENAME_CONTENT:
963 parser->item_length++;
964 if(c == '\r') {
965 parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
966 finfo->b_data[finfo->b_used - 1] = 0;
967 }
968 else if(c == '\n') {
969 parser->offsets.filename = parser->item_offset;
970 finfo->b_data[finfo->b_used - 1] = 0;
971 result = ftp_pl_insert_finfo(data, infop);
972 if(result) {
973 parser->error = result;
974 goto fail;
975 }
976 parser->state.NT.main = PL_WINNT_DATE;
977 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
978 }
979 break;
980 case PL_WINNT_FILENAME_WINEOL:
981 if(c == '\n') {
982 parser->offsets.filename = parser->item_offset;
983 result = ftp_pl_insert_finfo(data, infop);
984 if(result) {
985 parser->error = result;
986 goto fail;
987 }
988 parser->state.NT.main = PL_WINNT_DATE;
989 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
990 }
991 else {
992 parser->error = CURLE_FTP_BAD_FILE_LIST;
993 goto fail;
994 }
995 break;
996 }
997 break;
998 }
999 break;
1000 default:
1001 retsize = bufflen + 1;
1002 goto fail;
1003 }
1004
1005 i++;
1006 }
1007 return retsize;
1008
1009fail:
1010
1011 /* Clean up any allocated memory. */
1012 if(parser->file_data) {
1013 Curl_fileinfo_cleanup(parser->file_data);
1014 parser->file_data = NULL;
1015 }
1016
1017 return retsize;
1018}
1019
1020#endif /* CURL_DISABLE_FTP */
1021