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