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