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