1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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
184static void fileinfo_dtor(void *user, void *element)
185{
186 (void)user;
187 Curl_fileinfo_cleanup(element);
188}
189
190CURLcode Curl_wildcard_init(struct WildcardData *wc)
191{
192 Curl_llist_init(&wc->filelist, fileinfo_dtor);
193 wc->state = CURLWC_INIT;
194
195 return CURLE_OK;
196}
197
198void Curl_wildcard_dtor(struct WildcardData **wcp)
199{
200 struct WildcardData *wc = *wcp;
201 if(!wc)
202 return;
203
204 if(wc->dtor) {
205 wc->dtor(wc->ftpwc);
206 wc->dtor = ZERO_NULL;
207 wc->ftpwc = NULL;
208 }
209 DEBUGASSERT(wc->ftpwc == NULL);
210
211 Curl_llist_destroy(&wc->filelist, NULL);
212 free(wc->path);
213 wc->path = NULL;
214 free(wc->pattern);
215 wc->pattern = NULL;
216 wc->state = CURLWC_INIT;
217 free(wc);
218 *wcp = NULL;
219}
220
221struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void)
222{
223 return calloc(1, sizeof(struct ftp_parselist_data));
224}
225
226
227void Curl_ftp_parselist_data_free(struct ftp_parselist_data **parserp)
228{
229 struct ftp_parselist_data *parser = *parserp;
230 if(parser)
231 Curl_fileinfo_cleanup(parser->file_data);
232 free(parser);
233 *parserp = NULL;
234}
235
236
237CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data)
238{
239 return pl_data->error;
240}
241
242
243#define FTP_LP_MALFORMATED_PERM 0x01000000
244
245static unsigned int ftp_pl_get_permission(const char *str)
246{
247 unsigned int permissions = 0;
248 /* USER */
249 if(str[0] == 'r')
250 permissions |= 1 << 8;
251 else if(str[0] != '-')
252 permissions |= FTP_LP_MALFORMATED_PERM;
253 if(str[1] == 'w')
254 permissions |= 1 << 7;
255 else if(str[1] != '-')
256 permissions |= FTP_LP_MALFORMATED_PERM;
257
258 if(str[2] == 'x')
259 permissions |= 1 << 6;
260 else if(str[2] == 's') {
261 permissions |= 1 << 6;
262 permissions |= 1 << 11;
263 }
264 else if(str[2] == 'S')
265 permissions |= 1 << 11;
266 else if(str[2] != '-')
267 permissions |= FTP_LP_MALFORMATED_PERM;
268 /* GROUP */
269 if(str[3] == 'r')
270 permissions |= 1 << 5;
271 else if(str[3] != '-')
272 permissions |= FTP_LP_MALFORMATED_PERM;
273 if(str[4] == 'w')
274 permissions |= 1 << 4;
275 else if(str[4] != '-')
276 permissions |= FTP_LP_MALFORMATED_PERM;
277 if(str[5] == 'x')
278 permissions |= 1 << 3;
279 else if(str[5] == 's') {
280 permissions |= 1 << 3;
281 permissions |= 1 << 10;
282 }
283 else if(str[5] == 'S')
284 permissions |= 1 << 10;
285 else if(str[5] != '-')
286 permissions |= FTP_LP_MALFORMATED_PERM;
287 /* others */
288 if(str[6] == 'r')
289 permissions |= 1 << 2;
290 else if(str[6] != '-')
291 permissions |= FTP_LP_MALFORMATED_PERM;
292 if(str[7] == 'w')
293 permissions |= 1 << 1;
294 else if(str[7] != '-')
295 permissions |= FTP_LP_MALFORMATED_PERM;
296 if(str[8] == 'x')
297 permissions |= 1;
298 else if(str[8] == 't') {
299 permissions |= 1;
300 permissions |= 1 << 9;
301 }
302 else if(str[8] == 'T')
303 permissions |= 1 << 9;
304 else if(str[8] != '-')
305 permissions |= FTP_LP_MALFORMATED_PERM;
306
307 return permissions;
308}
309
310static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data,
311 struct fileinfo *infop)
312{
313 curl_fnmatch_callback compare;
314 struct WildcardData *wc = data->wildcard;
315 struct ftp_wc *ftpwc = wc->ftpwc;
316 struct Curl_llist *llist = &wc->filelist;
317 struct ftp_parselist_data *parser = ftpwc->parser;
318 bool add = TRUE;
319 struct curl_fileinfo *finfo = &infop->info;
320
321 /* set the finfo pointers */
322 char *str = Curl_dyn_ptr(&infop->buf);
323 finfo->filename = str + parser->offsets.filename;
324 finfo->strings.group = parser->offsets.group ?
325 str + parser->offsets.group : NULL;
326 finfo->strings.perm = parser->offsets.perm ?
327 str + parser->offsets.perm : NULL;
328 finfo->strings.target = parser->offsets.symlink_target ?
329 str + parser->offsets.symlink_target : NULL;
330 finfo->strings.time = str + parser->offsets.time;
331 finfo->strings.user = parser->offsets.user ?
332 str + parser->offsets.user : NULL;
333
334 /* get correct fnmatch callback */
335 compare = data->set.fnmatch;
336 if(!compare)
337 compare = Curl_fnmatch;
338
339 /* filter pattern-corresponding filenames */
340 Curl_set_in_callback(data, true);
341 if(compare(data->set.fnmatch_data, wc->pattern,
342 finfo->filename) == 0) {
343 /* discard symlink which is containing multiple " -> " */
344 if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target &&
345 (strstr(finfo->strings.target, " -> "))) {
346 add = FALSE;
347 }
348 }
349 else {
350 add = FALSE;
351 }
352 Curl_set_in_callback(data, false);
353
354 if(add) {
355 Curl_llist_insert_next(llist, llist->tail, finfo, &infop->list);
356 }
357 else {
358 Curl_fileinfo_cleanup(infop);
359 }
360
361 ftpwc->parser->file_data = NULL;
362 return CURLE_OK;
363}
364
365#define MAX_FTPLIST_BUFFER 10000 /* arbitrarily set */
366
367size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
368 void *connptr)
369{
370 size_t bufflen = size*nmemb;
371 struct Curl_easy *data = (struct Curl_easy *)connptr;
372 struct ftp_wc *ftpwc = data->wildcard->ftpwc;
373 struct ftp_parselist_data *parser = ftpwc->parser;
374 size_t i = 0;
375 CURLcode result;
376 size_t retsize = bufflen;
377
378 if(parser->error) { /* error in previous call */
379 /* scenario:
380 * 1. call => OK..
381 * 2. call => OUT_OF_MEMORY (or other error)
382 * 3. (last) call => is skipped RIGHT HERE and the error is handled later
383 * in wc_statemach()
384 */
385 goto fail;
386 }
387
388 if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) {
389 /* considering info about FILE response format */
390 parser->os_type = ISDIGIT(buffer[0]) ? OS_TYPE_WIN_NT : OS_TYPE_UNIX;
391 }
392
393 while(i < bufflen) { /* FSM */
394 char *mem;
395 size_t len; /* number of bytes of data in the dynbuf */
396 char c = buffer[i];
397 struct fileinfo *infop;
398 struct curl_fileinfo *finfo;
399 if(!parser->file_data) { /* tmp file data is not allocated yet */
400 parser->file_data = Curl_fileinfo_alloc();
401 if(!parser->file_data) {
402 parser->error = CURLE_OUT_OF_MEMORY;
403 goto fail;
404 }
405 parser->item_offset = 0;
406 parser->item_length = 0;
407 Curl_dyn_init(&parser->file_data->buf, MAX_FTPLIST_BUFFER);
408 }
409
410 infop = parser->file_data;
411 finfo = &infop->info;
412
413 if(Curl_dyn_addn(&infop->buf, &c, 1)) {
414 parser->error = CURLE_OUT_OF_MEMORY;
415 goto fail;
416 }
417 len = Curl_dyn_len(&infop->buf);
418 mem = Curl_dyn_ptr(&infop->buf);
419
420 switch(parser->os_type) {
421 case OS_TYPE_UNIX:
422 switch(parser->state.UNIX.main) {
423 case PL_UNIX_TOTALSIZE:
424 switch(parser->state.UNIX.sub.total_dirsize) {
425 case PL_UNIX_TOTALSIZE_INIT:
426 if(c == 't') {
427 parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
428 parser->item_length++;
429 }
430 else {
431 parser->state.UNIX.main = PL_UNIX_FILETYPE;
432 /* start FSM again not considering size of directory */
433 Curl_dyn_reset(&infop->buf);
434 continue;
435 }
436 break;
437 case PL_UNIX_TOTALSIZE_READING:
438 parser->item_length++;
439 if(c == '\r') {
440 parser->item_length--;
441 Curl_dyn_setlen(&infop->buf, --len);
442 }
443 else if(c == '\n') {
444 mem[parser->item_length - 1] = 0;
445 if(!strncmp("total ", mem, 6)) {
446 char *endptr = mem + 6;
447 /* here we can deal with directory size, pass the leading
448 whitespace and then the digits */
449 while(ISBLANK(*endptr))
450 endptr++;
451 while(ISDIGIT(*endptr))
452 endptr++;
453 if(*endptr) {
454 parser->error = CURLE_FTP_BAD_FILE_LIST;
455 goto fail;
456 }
457 parser->state.UNIX.main = PL_UNIX_FILETYPE;
458 Curl_dyn_reset(&infop->buf);
459 }
460 else {
461 parser->error = CURLE_FTP_BAD_FILE_LIST;
462 goto fail;
463 }
464 }
465 break;
466 }
467 break;
468 case PL_UNIX_FILETYPE:
469 switch(c) {
470 case '-':
471 finfo->filetype = CURLFILETYPE_FILE;
472 break;
473 case 'd':
474 finfo->filetype = CURLFILETYPE_DIRECTORY;
475 break;
476 case 'l':
477 finfo->filetype = CURLFILETYPE_SYMLINK;
478 break;
479 case 'p':
480 finfo->filetype = CURLFILETYPE_NAMEDPIPE;
481 break;
482 case 's':
483 finfo->filetype = CURLFILETYPE_SOCKET;
484 break;
485 case 'c':
486 finfo->filetype = CURLFILETYPE_DEVICE_CHAR;
487 break;
488 case 'b':
489 finfo->filetype = CURLFILETYPE_DEVICE_BLOCK;
490 break;
491 case 'D':
492 finfo->filetype = CURLFILETYPE_DOOR;
493 break;
494 default:
495 parser->error = CURLE_FTP_BAD_FILE_LIST;
496 goto fail;
497 }
498 parser->state.UNIX.main = PL_UNIX_PERMISSION;
499 parser->item_length = 0;
500 parser->item_offset = 1;
501 break;
502 case PL_UNIX_PERMISSION:
503 parser->item_length++;
504 if(parser->item_length <= 9) {
505 if(!strchr("rwx-tTsS", c)) {
506 parser->error = CURLE_FTP_BAD_FILE_LIST;
507 goto fail;
508 }
509 }
510 else if(parser->item_length == 10) {
511 unsigned int perm;
512 if(c != ' ') {
513 parser->error = CURLE_FTP_BAD_FILE_LIST;
514 goto fail;
515 }
516 mem[10] = 0; /* terminate permissions */
517 perm = ftp_pl_get_permission(mem + parser->item_offset);
518 if(perm & FTP_LP_MALFORMATED_PERM) {
519 parser->error = CURLE_FTP_BAD_FILE_LIST;
520 goto fail;
521 }
522 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM;
523 parser->file_data->info.perm = perm;
524 parser->offsets.perm = parser->item_offset;
525
526 parser->item_length = 0;
527 parser->state.UNIX.main = PL_UNIX_HLINKS;
528 parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
529 }
530 break;
531 case PL_UNIX_HLINKS:
532 switch(parser->state.UNIX.sub.hlinks) {
533 case PL_UNIX_HLINKS_PRESPACE:
534 if(c != ' ') {
535 if(ISDIGIT(c)) {
536 parser->item_offset = len - 1;
537 parser->item_length = 1;
538 parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
539 }
540 else {
541 parser->error = CURLE_FTP_BAD_FILE_LIST;
542 goto fail;
543 }
544 }
545 break;
546 case PL_UNIX_HLINKS_NUMBER:
547 parser->item_length ++;
548 if(c == ' ') {
549 char *p;
550 long int hlinks;
551 mem[parser->item_offset + parser->item_length - 1] = 0;
552 hlinks = strtol(mem + parser->item_offset, &p, 10);
553 if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) {
554 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
555 parser->file_data->info.hardlinks = hlinks;
556 }
557 parser->item_length = 0;
558 parser->item_offset = 0;
559 parser->state.UNIX.main = PL_UNIX_USER;
560 parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
561 }
562 else if(!ISDIGIT(c)) {
563 parser->error = CURLE_FTP_BAD_FILE_LIST;
564 goto fail;
565 }
566 break;
567 }
568 break;
569 case PL_UNIX_USER:
570 switch(parser->state.UNIX.sub.user) {
571 case PL_UNIX_USER_PRESPACE:
572 if(c != ' ') {
573 parser->item_offset = len - 1;
574 parser->item_length = 1;
575 parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
576 }
577 break;
578 case PL_UNIX_USER_PARSING:
579 parser->item_length++;
580 if(c == ' ') {
581 mem[parser->item_offset + parser->item_length - 1] = 0;
582 parser->offsets.user = parser->item_offset;
583 parser->state.UNIX.main = PL_UNIX_GROUP;
584 parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
585 parser->item_offset = 0;
586 parser->item_length = 0;
587 }
588 break;
589 }
590 break;
591 case PL_UNIX_GROUP:
592 switch(parser->state.UNIX.sub.group) {
593 case PL_UNIX_GROUP_PRESPACE:
594 if(c != ' ') {
595 parser->item_offset = len - 1;
596 parser->item_length = 1;
597 parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
598 }
599 break;
600 case PL_UNIX_GROUP_NAME:
601 parser->item_length++;
602 if(c == ' ') {
603 mem[parser->item_offset + parser->item_length - 1] = 0;
604 parser->offsets.group = parser->item_offset;
605 parser->state.UNIX.main = PL_UNIX_SIZE;
606 parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
607 parser->item_offset = 0;
608 parser->item_length = 0;
609 }
610 break;
611 }
612 break;
613 case PL_UNIX_SIZE:
614 switch(parser->state.UNIX.sub.size) {
615 case PL_UNIX_SIZE_PRESPACE:
616 if(c != ' ') {
617 if(ISDIGIT(c)) {
618 parser->item_offset = len - 1;
619 parser->item_length = 1;
620 parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
621 }
622 else {
623 parser->error = CURLE_FTP_BAD_FILE_LIST;
624 goto fail;
625 }
626 }
627 break;
628 case PL_UNIX_SIZE_NUMBER:
629 parser->item_length++;
630 if(c == ' ') {
631 char *p;
632 curl_off_t fsize;
633 mem[parser->item_offset + parser->item_length - 1] = 0;
634 if(!curlx_strtoofft(mem + parser->item_offset,
635 &p, 10, &fsize)) {
636 if(p[0] == '\0' && fsize != CURL_OFF_T_MAX &&
637 fsize != CURL_OFF_T_MIN) {
638 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
639 parser->file_data->info.size = fsize;
640 }
641 parser->item_length = 0;
642 parser->item_offset = 0;
643 parser->state.UNIX.main = PL_UNIX_TIME;
644 parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
645 }
646 }
647 else if(!ISDIGIT(c)) {
648 parser->error = CURLE_FTP_BAD_FILE_LIST;
649 goto fail;
650 }
651 break;
652 }
653 break;
654 case PL_UNIX_TIME:
655 switch(parser->state.UNIX.sub.time) {
656 case PL_UNIX_TIME_PREPART1:
657 if(c != ' ') {
658 if(ISALNUM(c)) {
659 parser->item_offset = len -1;
660 parser->item_length = 1;
661 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
662 }
663 else {
664 parser->error = CURLE_FTP_BAD_FILE_LIST;
665 goto fail;
666 }
667 }
668 break;
669 case PL_UNIX_TIME_PART1:
670 parser->item_length++;
671 if(c == ' ') {
672 parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
673 }
674 else if(!ISALNUM(c) && c != '.') {
675 parser->error = CURLE_FTP_BAD_FILE_LIST;
676 goto fail;
677 }
678 break;
679 case PL_UNIX_TIME_PREPART2:
680 parser->item_length++;
681 if(c != ' ') {
682 if(ISALNUM(c)) {
683 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
684 }
685 else {
686 parser->error = CURLE_FTP_BAD_FILE_LIST;
687 goto fail;
688 }
689 }
690 break;
691 case PL_UNIX_TIME_PART2:
692 parser->item_length++;
693 if(c == ' ') {
694 parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
695 }
696 else if(!ISALNUM(c) && c != '.') {
697 parser->error = CURLE_FTP_BAD_FILE_LIST;
698 goto fail;
699 }
700 break;
701 case PL_UNIX_TIME_PREPART3:
702 parser->item_length++;
703 if(c != ' ') {
704 if(ISALNUM(c)) {
705 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
706 }
707 else {
708 parser->error = CURLE_FTP_BAD_FILE_LIST;
709 goto fail;
710 }
711 }
712 break;
713 case PL_UNIX_TIME_PART3:
714 parser->item_length++;
715 if(c == ' ') {
716 mem[parser->item_offset + parser->item_length -1] = 0;
717 parser->offsets.time = parser->item_offset;
718 /*
719 if(ftp_pl_gettime(parser, finfo->mem + parser->item_offset)) {
720 parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME;
721 }
722 */
723 if(finfo->filetype == CURLFILETYPE_SYMLINK) {
724 parser->state.UNIX.main = PL_UNIX_SYMLINK;
725 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
726 }
727 else {
728 parser->state.UNIX.main = PL_UNIX_FILENAME;
729 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
730 }
731 }
732 else if(!ISALNUM(c) && c != '.' && c != ':') {
733 parser->error = CURLE_FTP_BAD_FILE_LIST;
734 goto fail;
735 }
736 break;
737 }
738 break;
739 case PL_UNIX_FILENAME:
740 switch(parser->state.UNIX.sub.filename) {
741 case PL_UNIX_FILENAME_PRESPACE:
742 if(c != ' ') {
743 parser->item_offset = len - 1;
744 parser->item_length = 1;
745 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
746 }
747 break;
748 case PL_UNIX_FILENAME_NAME:
749 parser->item_length++;
750 if(c == '\r') {
751 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
752 }
753 else if(c == '\n') {
754 mem[parser->item_offset + parser->item_length - 1] = 0;
755 parser->offsets.filename = parser->item_offset;
756 parser->state.UNIX.main = PL_UNIX_FILETYPE;
757 result = ftp_pl_insert_finfo(data, infop);
758 if(result) {
759 parser->error = result;
760 goto fail;
761 }
762 }
763 break;
764 case PL_UNIX_FILENAME_WINDOWSEOL:
765 if(c == '\n') {
766 mem[parser->item_offset + parser->item_length - 1] = 0;
767 parser->offsets.filename = parser->item_offset;
768 parser->state.UNIX.main = PL_UNIX_FILETYPE;
769 result = ftp_pl_insert_finfo(data, infop);
770 if(result) {
771 parser->error = result;
772 goto fail;
773 }
774 }
775 else {
776 parser->error = CURLE_FTP_BAD_FILE_LIST;
777 goto fail;
778 }
779 break;
780 }
781 break;
782 case PL_UNIX_SYMLINK:
783 switch(parser->state.UNIX.sub.symlink) {
784 case PL_UNIX_SYMLINK_PRESPACE:
785 if(c != ' ') {
786 parser->item_offset = len - 1;
787 parser->item_length = 1;
788 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
789 }
790 break;
791 case PL_UNIX_SYMLINK_NAME:
792 parser->item_length++;
793 if(c == ' ') {
794 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
795 }
796 else if(c == '\r' || c == '\n') {
797 parser->error = CURLE_FTP_BAD_FILE_LIST;
798 goto fail;
799 }
800 break;
801 case PL_UNIX_SYMLINK_PRETARGET1:
802 parser->item_length++;
803 if(c == '-') {
804 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
805 }
806 else if(c == '\r' || c == '\n') {
807 parser->error = CURLE_FTP_BAD_FILE_LIST;
808 goto fail;
809 }
810 else {
811 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
812 }
813 break;
814 case PL_UNIX_SYMLINK_PRETARGET2:
815 parser->item_length++;
816 if(c == '>') {
817 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
818 }
819 else if(c == '\r' || c == '\n') {
820 parser->error = CURLE_FTP_BAD_FILE_LIST;
821 goto fail;
822 }
823 else {
824 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
825 }
826 break;
827 case PL_UNIX_SYMLINK_PRETARGET3:
828 parser->item_length++;
829 if(c == ' ') {
830 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
831 /* now place where is symlink following */
832 mem[parser->item_offset + parser->item_length - 4] = 0;
833 parser->offsets.filename = parser->item_offset;
834 parser->item_length = 0;
835 parser->item_offset = 0;
836 }
837 else if(c == '\r' || c == '\n') {
838 parser->error = CURLE_FTP_BAD_FILE_LIST;
839 goto fail;
840 }
841 else {
842 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
843 }
844 break;
845 case PL_UNIX_SYMLINK_PRETARGET4:
846 if(c != '\r' && c != '\n') {
847 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
848 parser->item_offset = len - 1;
849 parser->item_length = 1;
850 }
851 else {
852 parser->error = CURLE_FTP_BAD_FILE_LIST;
853 goto fail;
854 }
855 break;
856 case PL_UNIX_SYMLINK_TARGET:
857 parser->item_length++;
858 if(c == '\r') {
859 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
860 }
861 else if(c == '\n') {
862 mem[parser->item_offset + parser->item_length - 1] = 0;
863 parser->offsets.symlink_target = parser->item_offset;
864 result = ftp_pl_insert_finfo(data, infop);
865 if(result) {
866 parser->error = result;
867 goto fail;
868 }
869 parser->state.UNIX.main = PL_UNIX_FILETYPE;
870 }
871 break;
872 case PL_UNIX_SYMLINK_WINDOWSEOL:
873 if(c == '\n') {
874 mem[parser->item_offset + parser->item_length - 1] = 0;
875 parser->offsets.symlink_target = parser->item_offset;
876 result = ftp_pl_insert_finfo(data, infop);
877 if(result) {
878 parser->error = result;
879 goto fail;
880 }
881 parser->state.UNIX.main = PL_UNIX_FILETYPE;
882 }
883 else {
884 parser->error = CURLE_FTP_BAD_FILE_LIST;
885 goto fail;
886 }
887 break;
888 }
889 break;
890 }
891 break;
892 case OS_TYPE_WIN_NT:
893 switch(parser->state.NT.main) {
894 case PL_WINNT_DATE:
895 parser->item_length++;
896 if(parser->item_length < 9) {
897 if(!strchr("0123456789-", c)) { /* only simple control */
898 parser->error = CURLE_FTP_BAD_FILE_LIST;
899 goto fail;
900 }
901 }
902 else if(parser->item_length == 9) {
903 if(c == ' ') {
904 parser->state.NT.main = PL_WINNT_TIME;
905 parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE;
906 }
907 else {
908 parser->error = CURLE_FTP_BAD_FILE_LIST;
909 goto fail;
910 }
911 }
912 else {
913 parser->error = CURLE_FTP_BAD_FILE_LIST;
914 goto fail;
915 }
916 break;
917 case PL_WINNT_TIME:
918 parser->item_length++;
919 switch(parser->state.NT.sub.time) {
920 case PL_WINNT_TIME_PRESPACE:
921 if(!ISBLANK(c)) {
922 parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
923 }
924 break;
925 case PL_WINNT_TIME_TIME:
926 if(c == ' ') {
927 parser->offsets.time = parser->item_offset;
928 mem[parser->item_offset + parser->item_length -1] = 0;
929 parser->state.NT.main = PL_WINNT_DIRORSIZE;
930 parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE;
931 parser->item_length = 0;
932 }
933 else if(!strchr("APM0123456789:", c)) {
934 parser->error = CURLE_FTP_BAD_FILE_LIST;
935 goto fail;
936 }
937 break;
938 }
939 break;
940 case PL_WINNT_DIRORSIZE:
941 switch(parser->state.NT.sub.dirorsize) {
942 case PL_WINNT_DIRORSIZE_PRESPACE:
943 if(c != ' ') {
944 parser->item_offset = len - 1;
945 parser->item_length = 1;
946 parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
947 }
948 break;
949 case PL_WINNT_DIRORSIZE_CONTENT:
950 parser->item_length ++;
951 if(c == ' ') {
952 mem[parser->item_offset + parser->item_length - 1] = 0;
953 if(strcmp("<DIR>", mem + parser->item_offset) == 0) {
954 finfo->filetype = CURLFILETYPE_DIRECTORY;
955 finfo->size = 0;
956 }
957 else {
958 char *endptr;
959 if(curlx_strtoofft(mem +
960 parser->item_offset,
961 &endptr, 10, &finfo->size)) {
962 parser->error = CURLE_FTP_BAD_FILE_LIST;
963 goto fail;
964 }
965 /* correct file type */
966 parser->file_data->info.filetype = CURLFILETYPE_FILE;
967 }
968
969 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
970 parser->item_length = 0;
971 parser->state.NT.main = PL_WINNT_FILENAME;
972 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
973 }
974 break;
975 }
976 break;
977 case PL_WINNT_FILENAME:
978 switch(parser->state.NT.sub.filename) {
979 case PL_WINNT_FILENAME_PRESPACE:
980 if(c != ' ') {
981 parser->item_offset = len -1;
982 parser->item_length = 1;
983 parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
984 }
985 break;
986 case PL_WINNT_FILENAME_CONTENT:
987 parser->item_length++;
988 if(c == '\r') {
989 parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
990 mem[len - 1] = 0;
991 }
992 else if(c == '\n') {
993 parser->offsets.filename = parser->item_offset;
994 mem[len - 1] = 0;
995 result = ftp_pl_insert_finfo(data, infop);
996 if(result) {
997 parser->error = result;
998 goto fail;
999 }
1000 parser->state.NT.main = PL_WINNT_DATE;
1001 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
1002 }
1003 break;
1004 case PL_WINNT_FILENAME_WINEOL:
1005 if(c == '\n') {
1006 parser->offsets.filename = parser->item_offset;
1007 result = ftp_pl_insert_finfo(data, infop);
1008 if(result) {
1009 parser->error = result;
1010 goto fail;
1011 }
1012 parser->state.NT.main = PL_WINNT_DATE;
1013 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
1014 }
1015 else {
1016 parser->error = CURLE_FTP_BAD_FILE_LIST;
1017 goto fail;
1018 }
1019 break;
1020 }
1021 break;
1022 }
1023 break;
1024 default:
1025 retsize = bufflen + 1;
1026 goto fail;
1027 }
1028
1029 i++;
1030 }
1031 return retsize;
1032
1033fail:
1034
1035 /* Clean up any allocated memory. */
1036 if(parser->file_data) {
1037 Curl_fileinfo_cleanup(parser->file_data);
1038 parser->file_data = NULL;
1039 }
1040
1041 return retsize;
1042}
1043
1044#endif /* CURL_DISABLE_FTP */
1045