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
61 typedef 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
74 typedef 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
127 typedef enum {
128 PL_WINNT_DATE = 0,
129 PL_WINNT_TIME,
130 PL_WINNT_DIRORSIZE,
131 PL_WINNT_FILENAME
132 } pl_winNT_mainstate;
133
134 typedef 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 */
151 struct 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
fileinfo_dtor(void * user,void * element)184 static void fileinfo_dtor(void *user, void *element)
185 {
186 (void)user;
187 Curl_fileinfo_cleanup(element);
188 }
189
Curl_wildcard_init(struct WildcardData * wc)190 CURLcode 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
Curl_wildcard_dtor(struct WildcardData ** wcp)198 void 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
Curl_ftp_parselist_data_alloc(void)221 struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void)
222 {
223 return calloc(1, sizeof(struct ftp_parselist_data));
224 }
225
226
Curl_ftp_parselist_data_free(struct ftp_parselist_data ** parserp)227 void 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
Curl_ftp_parselist_geterror(struct ftp_parselist_data * pl_data)237 CURLcode 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
ftp_pl_get_permission(const char * str)245 static 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
ftp_pl_insert_finfo(struct Curl_easy * data,struct fileinfo * infop)310 static 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 /* move finfo pointers to b_data */
322 char *str = finfo->b_data;
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
Curl_ftp_parselist(char * buffer,size_t size,size_t nmemb,void * connptr)365 size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
366 void *connptr)
367 {
368 size_t bufflen = size*nmemb;
369 struct Curl_easy *data = (struct Curl_easy *)connptr;
370 struct ftp_wc *ftpwc = data->wildcard->ftpwc;
371 struct ftp_parselist_data *parser = ftpwc->parser;
372 struct fileinfo *infop;
373 struct curl_fileinfo *finfo;
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 hadled 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 = (buffer[0] >= '0' && buffer[0] <= '9') ?
391 OS_TYPE_WIN_NT : OS_TYPE_UNIX;
392 }
393
394 while(i < bufflen) { /* FSM */
395
396 char c = buffer[i];
397 if(!parser->file_data) { /* tmp file data is not allocated yet */
398 parser->file_data = Curl_fileinfo_alloc();
399 if(!parser->file_data) {
400 parser->error = CURLE_OUT_OF_MEMORY;
401 goto fail;
402 }
403 parser->file_data->info.b_data = malloc(FTP_BUFFER_ALLOCSIZE);
404 if(!parser->file_data->info.b_data) {
405 parser->error = CURLE_OUT_OF_MEMORY;
406 goto fail;
407 }
408 parser->file_data->info.b_size = FTP_BUFFER_ALLOCSIZE;
409 parser->item_offset = 0;
410 parser->item_length = 0;
411 }
412
413 infop = parser->file_data;
414 finfo = &infop->info;
415 finfo->b_data[finfo->b_used++] = c;
416
417 if(finfo->b_used >= finfo->b_size - 1) {
418 /* if it is important, extend buffer space for file data */
419 char *tmp = realloc(finfo->b_data,
420 finfo->b_size + FTP_BUFFER_ALLOCSIZE);
421 if(tmp) {
422 finfo->b_size += FTP_BUFFER_ALLOCSIZE;
423 finfo->b_data = tmp;
424 }
425 else {
426 Curl_fileinfo_cleanup(parser->file_data);
427 parser->file_data = NULL;
428 parser->error = CURLE_OUT_OF_MEMORY;
429 goto fail;
430 }
431 }
432
433 switch(parser->os_type) {
434 case OS_TYPE_UNIX:
435 switch(parser->state.UNIX.main) {
436 case PL_UNIX_TOTALSIZE:
437 switch(parser->state.UNIX.sub.total_dirsize) {
438 case PL_UNIX_TOTALSIZE_INIT:
439 if(c == 't') {
440 parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
441 parser->item_length++;
442 }
443 else {
444 parser->state.UNIX.main = PL_UNIX_FILETYPE;
445 /* start FSM again not considering size of directory */
446 finfo->b_used = 0;
447 continue;
448 }
449 break;
450 case PL_UNIX_TOTALSIZE_READING:
451 parser->item_length++;
452 if(c == '\r') {
453 parser->item_length--;
454 finfo->b_used--;
455 }
456 else if(c == '\n') {
457 finfo->b_data[parser->item_length - 1] = 0;
458 if(strncmp("total ", finfo->b_data, 6) == 0) {
459 char *endptr = finfo->b_data + 6;
460 /* here we can deal with directory size, pass the leading
461 whitespace and then the digits */
462 while(ISBLANK(*endptr))
463 endptr++;
464 while(ISDIGIT(*endptr))
465 endptr++;
466 if(*endptr) {
467 parser->error = CURLE_FTP_BAD_FILE_LIST;
468 goto fail;
469 }
470 parser->state.UNIX.main = PL_UNIX_FILETYPE;
471 finfo->b_used = 0;
472 }
473 else {
474 parser->error = CURLE_FTP_BAD_FILE_LIST;
475 goto fail;
476 }
477 }
478 break;
479 }
480 break;
481 case PL_UNIX_FILETYPE:
482 switch(c) {
483 case '-':
484 finfo->filetype = CURLFILETYPE_FILE;
485 break;
486 case 'd':
487 finfo->filetype = CURLFILETYPE_DIRECTORY;
488 break;
489 case 'l':
490 finfo->filetype = CURLFILETYPE_SYMLINK;
491 break;
492 case 'p':
493 finfo->filetype = CURLFILETYPE_NAMEDPIPE;
494 break;
495 case 's':
496 finfo->filetype = CURLFILETYPE_SOCKET;
497 break;
498 case 'c':
499 finfo->filetype = CURLFILETYPE_DEVICE_CHAR;
500 break;
501 case 'b':
502 finfo->filetype = CURLFILETYPE_DEVICE_BLOCK;
503 break;
504 case 'D':
505 finfo->filetype = CURLFILETYPE_DOOR;
506 break;
507 default:
508 parser->error = CURLE_FTP_BAD_FILE_LIST;
509 goto fail;
510 }
511 parser->state.UNIX.main = PL_UNIX_PERMISSION;
512 parser->item_length = 0;
513 parser->item_offset = 1;
514 break;
515 case PL_UNIX_PERMISSION:
516 parser->item_length++;
517 if(parser->item_length <= 9) {
518 if(!strchr("rwx-tTsS", c)) {
519 parser->error = CURLE_FTP_BAD_FILE_LIST;
520 goto fail;
521 }
522 }
523 else if(parser->item_length == 10) {
524 unsigned int perm;
525 if(c != ' ') {
526 parser->error = CURLE_FTP_BAD_FILE_LIST;
527 goto fail;
528 }
529 finfo->b_data[10] = 0; /* terminate permissions */
530 perm = ftp_pl_get_permission(finfo->b_data + parser->item_offset);
531 if(perm & FTP_LP_MALFORMATED_PERM) {
532 parser->error = CURLE_FTP_BAD_FILE_LIST;
533 goto fail;
534 }
535 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM;
536 parser->file_data->info.perm = perm;
537 parser->offsets.perm = parser->item_offset;
538
539 parser->item_length = 0;
540 parser->state.UNIX.main = PL_UNIX_HLINKS;
541 parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
542 }
543 break;
544 case PL_UNIX_HLINKS:
545 switch(parser->state.UNIX.sub.hlinks) {
546 case PL_UNIX_HLINKS_PRESPACE:
547 if(c != ' ') {
548 if(c >= '0' && c <= '9') {
549 parser->item_offset = finfo->b_used - 1;
550 parser->item_length = 1;
551 parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
552 }
553 else {
554 parser->error = CURLE_FTP_BAD_FILE_LIST;
555 goto fail;
556 }
557 }
558 break;
559 case PL_UNIX_HLINKS_NUMBER:
560 parser->item_length ++;
561 if(c == ' ') {
562 char *p;
563 long int hlinks;
564 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
565 hlinks = strtol(finfo->b_data + parser->item_offset, &p, 10);
566 if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) {
567 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
568 parser->file_data->info.hardlinks = hlinks;
569 }
570 parser->item_length = 0;
571 parser->item_offset = 0;
572 parser->state.UNIX.main = PL_UNIX_USER;
573 parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
574 }
575 else if(c < '0' || c > '9') {
576 parser->error = CURLE_FTP_BAD_FILE_LIST;
577 goto fail;
578 }
579 break;
580 }
581 break;
582 case PL_UNIX_USER:
583 switch(parser->state.UNIX.sub.user) {
584 case PL_UNIX_USER_PRESPACE:
585 if(c != ' ') {
586 parser->item_offset = finfo->b_used - 1;
587 parser->item_length = 1;
588 parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
589 }
590 break;
591 case PL_UNIX_USER_PARSING:
592 parser->item_length++;
593 if(c == ' ') {
594 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
595 parser->offsets.user = parser->item_offset;
596 parser->state.UNIX.main = PL_UNIX_GROUP;
597 parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
598 parser->item_offset = 0;
599 parser->item_length = 0;
600 }
601 break;
602 }
603 break;
604 case PL_UNIX_GROUP:
605 switch(parser->state.UNIX.sub.group) {
606 case PL_UNIX_GROUP_PRESPACE:
607 if(c != ' ') {
608 parser->item_offset = finfo->b_used - 1;
609 parser->item_length = 1;
610 parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
611 }
612 break;
613 case PL_UNIX_GROUP_NAME:
614 parser->item_length++;
615 if(c == ' ') {
616 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
617 parser->offsets.group = parser->item_offset;
618 parser->state.UNIX.main = PL_UNIX_SIZE;
619 parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
620 parser->item_offset = 0;
621 parser->item_length = 0;
622 }
623 break;
624 }
625 break;
626 case PL_UNIX_SIZE:
627 switch(parser->state.UNIX.sub.size) {
628 case PL_UNIX_SIZE_PRESPACE:
629 if(c != ' ') {
630 if(c >= '0' && c <= '9') {
631 parser->item_offset = finfo->b_used - 1;
632 parser->item_length = 1;
633 parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
634 }
635 else {
636 parser->error = CURLE_FTP_BAD_FILE_LIST;
637 goto fail;
638 }
639 }
640 break;
641 case PL_UNIX_SIZE_NUMBER:
642 parser->item_length++;
643 if(c == ' ') {
644 char *p;
645 curl_off_t fsize;
646 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
647 if(!curlx_strtoofft(finfo->b_data + parser->item_offset,
648 &p, 10, &fsize)) {
649 if(p[0] == '\0' && fsize != CURL_OFF_T_MAX &&
650 fsize != CURL_OFF_T_MIN) {
651 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
652 parser->file_data->info.size = fsize;
653 }
654 parser->item_length = 0;
655 parser->item_offset = 0;
656 parser->state.UNIX.main = PL_UNIX_TIME;
657 parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
658 }
659 }
660 else if(!ISDIGIT(c)) {
661 parser->error = CURLE_FTP_BAD_FILE_LIST;
662 goto fail;
663 }
664 break;
665 }
666 break;
667 case PL_UNIX_TIME:
668 switch(parser->state.UNIX.sub.time) {
669 case PL_UNIX_TIME_PREPART1:
670 if(c != ' ') {
671 if(ISALNUM(c)) {
672 parser->item_offset = finfo->b_used -1;
673 parser->item_length = 1;
674 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
675 }
676 else {
677 parser->error = CURLE_FTP_BAD_FILE_LIST;
678 goto fail;
679 }
680 }
681 break;
682 case PL_UNIX_TIME_PART1:
683 parser->item_length++;
684 if(c == ' ') {
685 parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
686 }
687 else if(!ISALNUM(c) && c != '.') {
688 parser->error = CURLE_FTP_BAD_FILE_LIST;
689 goto fail;
690 }
691 break;
692 case PL_UNIX_TIME_PREPART2:
693 parser->item_length++;
694 if(c != ' ') {
695 if(ISALNUM(c)) {
696 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
697 }
698 else {
699 parser->error = CURLE_FTP_BAD_FILE_LIST;
700 goto fail;
701 }
702 }
703 break;
704 case PL_UNIX_TIME_PART2:
705 parser->item_length++;
706 if(c == ' ') {
707 parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
708 }
709 else if(!ISALNUM(c) && c != '.') {
710 parser->error = CURLE_FTP_BAD_FILE_LIST;
711 goto fail;
712 }
713 break;
714 case PL_UNIX_TIME_PREPART3:
715 parser->item_length++;
716 if(c != ' ') {
717 if(ISALNUM(c)) {
718 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
719 }
720 else {
721 parser->error = CURLE_FTP_BAD_FILE_LIST;
722 goto fail;
723 }
724 }
725 break;
726 case PL_UNIX_TIME_PART3:
727 parser->item_length++;
728 if(c == ' ') {
729 finfo->b_data[parser->item_offset + parser->item_length -1] = 0;
730 parser->offsets.time = parser->item_offset;
731 /*
732 if(ftp_pl_gettime(parser, finfo->b_data + parser->item_offset)) {
733 parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME;
734 }
735 */
736 if(finfo->filetype == CURLFILETYPE_SYMLINK) {
737 parser->state.UNIX.main = PL_UNIX_SYMLINK;
738 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
739 }
740 else {
741 parser->state.UNIX.main = PL_UNIX_FILENAME;
742 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
743 }
744 }
745 else if(!ISALNUM(c) && c != '.' && c != ':') {
746 parser->error = CURLE_FTP_BAD_FILE_LIST;
747 goto fail;
748 }
749 break;
750 }
751 break;
752 case PL_UNIX_FILENAME:
753 switch(parser->state.UNIX.sub.filename) {
754 case PL_UNIX_FILENAME_PRESPACE:
755 if(c != ' ') {
756 parser->item_offset = finfo->b_used - 1;
757 parser->item_length = 1;
758 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
759 }
760 break;
761 case PL_UNIX_FILENAME_NAME:
762 parser->item_length++;
763 if(c == '\r') {
764 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
765 }
766 else if(c == '\n') {
767 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
768 parser->offsets.filename = parser->item_offset;
769 parser->state.UNIX.main = PL_UNIX_FILETYPE;
770 result = ftp_pl_insert_finfo(data, infop);
771 if(result) {
772 parser->error = result;
773 goto fail;
774 }
775 }
776 break;
777 case PL_UNIX_FILENAME_WINDOWSEOL:
778 if(c == '\n') {
779 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
780 parser->offsets.filename = parser->item_offset;
781 parser->state.UNIX.main = PL_UNIX_FILETYPE;
782 result = ftp_pl_insert_finfo(data, infop);
783 if(result) {
784 parser->error = result;
785 goto fail;
786 }
787 }
788 else {
789 parser->error = CURLE_FTP_BAD_FILE_LIST;
790 goto fail;
791 }
792 break;
793 }
794 break;
795 case PL_UNIX_SYMLINK:
796 switch(parser->state.UNIX.sub.symlink) {
797 case PL_UNIX_SYMLINK_PRESPACE:
798 if(c != ' ') {
799 parser->item_offset = finfo->b_used - 1;
800 parser->item_length = 1;
801 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
802 }
803 break;
804 case PL_UNIX_SYMLINK_NAME:
805 parser->item_length++;
806 if(c == ' ') {
807 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
808 }
809 else if(c == '\r' || c == '\n') {
810 parser->error = CURLE_FTP_BAD_FILE_LIST;
811 goto fail;
812 }
813 break;
814 case PL_UNIX_SYMLINK_PRETARGET1:
815 parser->item_length++;
816 if(c == '-') {
817 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
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_PRETARGET2:
828 parser->item_length++;
829 if(c == '>') {
830 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
831 }
832 else if(c == '\r' || c == '\n') {
833 parser->error = CURLE_FTP_BAD_FILE_LIST;
834 goto fail;
835 }
836 else {
837 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
838 }
839 break;
840 case PL_UNIX_SYMLINK_PRETARGET3:
841 parser->item_length++;
842 if(c == ' ') {
843 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
844 /* now place where is symlink following */
845 finfo->b_data[parser->item_offset + parser->item_length - 4] = 0;
846 parser->offsets.filename = parser->item_offset;
847 parser->item_length = 0;
848 parser->item_offset = 0;
849 }
850 else if(c == '\r' || c == '\n') {
851 parser->error = CURLE_FTP_BAD_FILE_LIST;
852 goto fail;
853 }
854 else {
855 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
856 }
857 break;
858 case PL_UNIX_SYMLINK_PRETARGET4:
859 if(c != '\r' && c != '\n') {
860 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
861 parser->item_offset = finfo->b_used - 1;
862 parser->item_length = 1;
863 }
864 else {
865 parser->error = CURLE_FTP_BAD_FILE_LIST;
866 goto fail;
867 }
868 break;
869 case PL_UNIX_SYMLINK_TARGET:
870 parser->item_length++;
871 if(c == '\r') {
872 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
873 }
874 else if(c == '\n') {
875 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
876 parser->offsets.symlink_target = parser->item_offset;
877 result = ftp_pl_insert_finfo(data, infop);
878 if(result) {
879 parser->error = result;
880 goto fail;
881 }
882 parser->state.UNIX.main = PL_UNIX_FILETYPE;
883 }
884 break;
885 case PL_UNIX_SYMLINK_WINDOWSEOL:
886 if(c == '\n') {
887 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
888 parser->offsets.symlink_target = parser->item_offset;
889 result = ftp_pl_insert_finfo(data, infop);
890 if(result) {
891 parser->error = result;
892 goto fail;
893 }
894 parser->state.UNIX.main = PL_UNIX_FILETYPE;
895 }
896 else {
897 parser->error = CURLE_FTP_BAD_FILE_LIST;
898 goto fail;
899 }
900 break;
901 }
902 break;
903 }
904 break;
905 case OS_TYPE_WIN_NT:
906 switch(parser->state.NT.main) {
907 case PL_WINNT_DATE:
908 parser->item_length++;
909 if(parser->item_length < 9) {
910 if(!strchr("0123456789-", c)) { /* only simple control */
911 parser->error = CURLE_FTP_BAD_FILE_LIST;
912 goto fail;
913 }
914 }
915 else if(parser->item_length == 9) {
916 if(c == ' ') {
917 parser->state.NT.main = PL_WINNT_TIME;
918 parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE;
919 }
920 else {
921 parser->error = CURLE_FTP_BAD_FILE_LIST;
922 goto fail;
923 }
924 }
925 else {
926 parser->error = CURLE_FTP_BAD_FILE_LIST;
927 goto fail;
928 }
929 break;
930 case PL_WINNT_TIME:
931 parser->item_length++;
932 switch(parser->state.NT.sub.time) {
933 case PL_WINNT_TIME_PRESPACE:
934 if(!ISBLANK(c)) {
935 parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
936 }
937 break;
938 case PL_WINNT_TIME_TIME:
939 if(c == ' ') {
940 parser->offsets.time = parser->item_offset;
941 finfo->b_data[parser->item_offset + parser->item_length -1] = 0;
942 parser->state.NT.main = PL_WINNT_DIRORSIZE;
943 parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE;
944 parser->item_length = 0;
945 }
946 else if(!strchr("APM0123456789:", c)) {
947 parser->error = CURLE_FTP_BAD_FILE_LIST;
948 goto fail;
949 }
950 break;
951 }
952 break;
953 case PL_WINNT_DIRORSIZE:
954 switch(parser->state.NT.sub.dirorsize) {
955 case PL_WINNT_DIRORSIZE_PRESPACE:
956 if(c != ' ') {
957 parser->item_offset = finfo->b_used - 1;
958 parser->item_length = 1;
959 parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
960 }
961 break;
962 case PL_WINNT_DIRORSIZE_CONTENT:
963 parser->item_length ++;
964 if(c == ' ') {
965 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
966 if(strcmp("<DIR>", finfo->b_data + parser->item_offset) == 0) {
967 finfo->filetype = CURLFILETYPE_DIRECTORY;
968 finfo->size = 0;
969 }
970 else {
971 char *endptr;
972 if(curlx_strtoofft(finfo->b_data +
973 parser->item_offset,
974 &endptr, 10, &finfo->size)) {
975 parser->error = CURLE_FTP_BAD_FILE_LIST;
976 goto fail;
977 }
978 /* correct file type */
979 parser->file_data->info.filetype = CURLFILETYPE_FILE;
980 }
981
982 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
983 parser->item_length = 0;
984 parser->state.NT.main = PL_WINNT_FILENAME;
985 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
986 }
987 break;
988 }
989 break;
990 case PL_WINNT_FILENAME:
991 switch(parser->state.NT.sub.filename) {
992 case PL_WINNT_FILENAME_PRESPACE:
993 if(c != ' ') {
994 parser->item_offset = finfo->b_used -1;
995 parser->item_length = 1;
996 parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
997 }
998 break;
999 case PL_WINNT_FILENAME_CONTENT:
1000 parser->item_length++;
1001 if(c == '\r') {
1002 parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
1003 finfo->b_data[finfo->b_used - 1] = 0;
1004 }
1005 else if(c == '\n') {
1006 parser->offsets.filename = parser->item_offset;
1007 finfo->b_data[finfo->b_used - 1] = 0;
1008 result = ftp_pl_insert_finfo(data, infop);
1009 if(result) {
1010 parser->error = result;
1011 goto fail;
1012 }
1013 parser->state.NT.main = PL_WINNT_DATE;
1014 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
1015 }
1016 break;
1017 case PL_WINNT_FILENAME_WINEOL:
1018 if(c == '\n') {
1019 parser->offsets.filename = parser->item_offset;
1020 result = ftp_pl_insert_finfo(data, infop);
1021 if(result) {
1022 parser->error = result;
1023 goto fail;
1024 }
1025 parser->state.NT.main = PL_WINNT_DATE;
1026 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
1027 }
1028 else {
1029 parser->error = CURLE_FTP_BAD_FILE_LIST;
1030 goto fail;
1031 }
1032 break;
1033 }
1034 break;
1035 }
1036 break;
1037 default:
1038 retsize = bufflen + 1;
1039 goto fail;
1040 }
1041
1042 i++;
1043 }
1044 return retsize;
1045
1046 fail:
1047
1048 /* Clean up any allocated memory. */
1049 if(parser->file_data) {
1050 Curl_fileinfo_cleanup(parser->file_data);
1051 parser->file_data = NULL;
1052 }
1053
1054 return retsize;
1055 }
1056
1057 #endif /* CURL_DISABLE_FTP */
1058