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