blob: c59ea7e162274a3395016d2f0b845781a2eb722a [file] [log] [blame]
Lucas Eckels9bd90e62012-08-06 15:07:02 -07001/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2010, 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 "setup.h"
39
40#include <time.h>
41
42#include "ftplistparser.h"
43#include "curl_fnmatch.h"
44
45#include "urldata.h"
46#include "ftp.h"
47#include "fileinfo.h"
48#include "llist.h"
49#include "strtoofft.h"
50#include "rawstr.h"
51#include "ftp.h"
52
53#define _MPRINTF_REPLACE /* use our functions only */
54#include <curl/mprintf.h>
55
56#include "curl_memory.h"
57/* The last #include file should be: */
58#include "memdebug.h"
59
60/* allocs buffer which will contain one line of LIST command response */
61#define FTP_BUFFER_ALLOCSIZE 160
62
63typedef enum {
64 PL_UNIX_TOTALSIZE = 0,
65 PL_UNIX_FILETYPE,
66 PL_UNIX_PERMISSION,
67 PL_UNIX_HLINKS,
68 PL_UNIX_USER,
69 PL_UNIX_GROUP,
70 PL_UNIX_SIZE,
71 PL_UNIX_TIME,
72 PL_UNIX_FILENAME,
73 PL_UNIX_SYMLINK
74} pl_unix_mainstate;
75
76typedef union {
77 enum {
78 PL_UNIX_TOTALSIZE_INIT = 0,
79 PL_UNIX_TOTALSIZE_READING
80 } total_dirsize;
81
82 enum {
83 PL_UNIX_HLINKS_PRESPACE = 0,
84 PL_UNIX_HLINKS_NUMBER
85 } hlinks;
86
87 enum {
88 PL_UNIX_USER_PRESPACE = 0,
89 PL_UNIX_USER_PARSING
90 } user;
91
92 enum {
93 PL_UNIX_GROUP_PRESPACE = 0,
94 PL_UNIX_GROUP_NAME
95 } group;
96
97 enum {
98 PL_UNIX_SIZE_PRESPACE = 0,
99 PL_UNIX_SIZE_NUMBER
100 } size;
101
102 enum {
103 PL_UNIX_TIME_PREPART1 = 0,
104 PL_UNIX_TIME_PART1,
105 PL_UNIX_TIME_PREPART2,
106 PL_UNIX_TIME_PART2,
107 PL_UNIX_TIME_PREPART3,
108 PL_UNIX_TIME_PART3
109 } time;
110
111 enum {
112 PL_UNIX_FILENAME_PRESPACE = 0,
113 PL_UNIX_FILENAME_NAME,
114 PL_UNIX_FILENAME_WINDOWSEOL
115 } filename;
116
117 enum {
118 PL_UNIX_SYMLINK_PRESPACE = 0,
119 PL_UNIX_SYMLINK_NAME,
120 PL_UNIX_SYMLINK_PRETARGET1,
121 PL_UNIX_SYMLINK_PRETARGET2,
122 PL_UNIX_SYMLINK_PRETARGET3,
123 PL_UNIX_SYMLINK_PRETARGET4,
124 PL_UNIX_SYMLINK_TARGET,
125 PL_UNIX_SYMLINK_WINDOWSEOL
126 } symlink;
127} pl_unix_substate;
128
129typedef enum {
130 PL_WINNT_DATE = 0,
131 PL_WINNT_TIME,
132 PL_WINNT_DIRORSIZE,
133 PL_WINNT_FILENAME
134} pl_winNT_mainstate;
135
136typedef union {
137 enum {
138 PL_WINNT_TIME_PRESPACE = 0,
139 PL_WINNT_TIME_TIME
140 } time;
141 enum {
142 PL_WINNT_DIRORSIZE_PRESPACE = 0,
143 PL_WINNT_DIRORSIZE_CONTENT
144 } dirorsize;
145 enum {
146 PL_WINNT_FILENAME_PRESPACE = 0,
147 PL_WINNT_FILENAME_CONTENT,
148 PL_WINNT_FILENAME_WINEOL
149 } filename;
150} pl_winNT_substate;
151
152/* This struct is used in wildcard downloading - for parsing LIST response */
153struct ftp_parselist_data {
154 enum {
155 OS_TYPE_UNKNOWN = 0,
156 OS_TYPE_UNIX,
157 OS_TYPE_WIN_NT
158 } os_type;
159
160 union {
161 struct {
162 pl_unix_mainstate main;
163 pl_unix_substate sub;
164 } UNIX;
165
166 struct {
167 pl_winNT_mainstate main;
168 pl_winNT_substate sub;
169 } NT;
170 } state;
171
172 CURLcode error;
173 struct curl_fileinfo *file_data;
174 unsigned int item_length;
175 size_t item_offset;
176 struct {
177 size_t filename;
178 size_t user;
179 size_t group;
180 size_t time;
181 size_t perm;
182 size_t symlink_target;
183 } offsets;
184};
185
186struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void)
187{
188 return calloc(1, sizeof(struct ftp_parselist_data));
189}
190
191
192void Curl_ftp_parselist_data_free(struct ftp_parselist_data **pl_data)
193{
194 if(*pl_data)
195 free(*pl_data);
196 *pl_data = NULL;
197}
198
199
200CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data)
201{
202 return pl_data->error;
203}
204
205
206#define FTP_LP_MALFORMATED_PERM 0x01000000
207
208static int ftp_pl_get_permission(const char *str)
209{
210 int permissions = 0;
211 /* USER */
212 if(str[0] == 'r')
213 permissions |= 1 << 8;
214 else if(str[0] != '-')
215 permissions |= FTP_LP_MALFORMATED_PERM;
216 if(str[1] == 'w')
217 permissions |= 1 << 7;
218 else if(str[1] != '-')
219 permissions |= FTP_LP_MALFORMATED_PERM;
220
221 if(str[2] == 'x')
222 permissions |= 1 << 6;
223 else if(str[2] == 's') {
224 permissions |= 1 << 6;
225 permissions |= 1 << 11;
226 }
227 else if(str[2] == 'S')
228 permissions |= 1 << 11;
229 else if(str[2] != '-')
230 permissions |= FTP_LP_MALFORMATED_PERM;
231 /* GROUP */
232 if(str[3] == 'r')
233 permissions |= 1 << 5;
234 else if(str[3] != '-')
235 permissions |= FTP_LP_MALFORMATED_PERM;
236 if(str[4] == 'w')
237 permissions |= 1 << 4;
238 else if(str[4] != '-')
239 permissions |= FTP_LP_MALFORMATED_PERM;
240 if(str[5] == 'x')
241 permissions |= 1 << 3;
242 else if(str[5] == 's') {
243 permissions |= 1 << 3;
244 permissions |= 1 << 10;
245 }
246 else if(str[5] == 'S')
247 permissions |= 1 << 10;
248 else if(str[5] != '-')
249 permissions |= FTP_LP_MALFORMATED_PERM;
250 /* others */
251 if(str[6] == 'r')
252 permissions |= 1 << 2;
253 else if(str[6] != '-')
254 permissions |= FTP_LP_MALFORMATED_PERM;
255 if(str[7] == 'w')
256 permissions |= 1 << 1;
257 else if(str[7] != '-')
258 permissions |= FTP_LP_MALFORMATED_PERM;
259 if(str[8] == 'x')
260 permissions |= 1;
261 else if(str[8] == 't') {
262 permissions |= 1;
263 permissions |= 1 << 9;
264 }
265 else if(str[8] == 'T')
266 permissions |= 1 << 9;
267 else if(str[8] != '-')
268 permissions |= FTP_LP_MALFORMATED_PERM;
269
270 return permissions;
271}
272
273static void PL_ERROR(struct connectdata *conn, CURLcode err)
274{
275 struct ftp_wc_tmpdata *tmpdata = conn->data->wildcard.tmp;
276 struct ftp_parselist_data *parser = tmpdata->parser;
277 if(parser->file_data)
278 Curl_fileinfo_dtor(NULL, parser->file_data);
279 parser->file_data = NULL;
280 parser->error = err;
281}
282
283static bool ftp_pl_gettime(struct ftp_parselist_data *parser, char *string)
284{
285 (void)parser;
286 (void)string;
287 /* TODO
288 * There could be possible parse timestamp from server. Leaving unimplemented
289 * for now.
290 * If you want implement this, please add CURLFINFOFLAG_KNOWN_TIME flag to
291 * parser->file_data->flags
292 *
293 * Ftp servers are giving usually these formats:
294 * Apr 11 1998 (unknown time.. set it to 00:00:00?)
295 * Apr 11 12:21 (unknown year -> set it to NOW() time?)
296 * 08-05-09 02:49PM (ms-dos format)
297 * 20100421092538 -> for MLST/MLSD response
298 */
299
300 return FALSE;
301}
302
303static CURLcode ftp_pl_insert_finfo(struct connectdata *conn,
304 struct curl_fileinfo *finfo)
305{
306 curl_fnmatch_callback compare;
307 struct WildcardData *wc = &conn->data->wildcard;
308 struct ftp_wc_tmpdata *tmpdata = wc->tmp;
309 struct curl_llist *llist = wc->filelist;
310 struct ftp_parselist_data *parser = tmpdata->parser;
311 bool add = TRUE;
312
313 /* move finfo pointers to b_data */
314 char *str = finfo->b_data;
315 finfo->filename = str + parser->offsets.filename;
316 finfo->strings.group = parser->offsets.group ?
317 str + parser->offsets.group : NULL;
318 finfo->strings.perm = parser->offsets.perm ?
319 str + parser->offsets.perm : NULL;
320 finfo->strings.target = parser->offsets.symlink_target ?
321 str + parser->offsets.symlink_target : NULL;
322 finfo->strings.time = str + parser->offsets.time;
323 finfo->strings.user = parser->offsets.user ?
324 str + parser->offsets.user : NULL;
325
326 /* get correct fnmatch callback */
327 compare = conn->data->set.fnmatch;
328 if(!compare)
329 compare = Curl_fnmatch;
330
331 /* filter pattern-corresponding filenames */
332 if(compare(conn->data->set.fnmatch_data, wc->pattern, finfo->filename) == 0) {
333 /* discard symlink which is containing multiple " -> " */
334 if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target &&
335 (strstr(finfo->strings.target, " -> "))) {
336 add = FALSE;
337 }
338 }
339 else {
340 add = FALSE;
341 }
342
343 if(add) {
344 if(!Curl_llist_insert_next(llist, llist->tail, finfo)) {
345 Curl_fileinfo_dtor(NULL, finfo);
346 tmpdata->parser->file_data = NULL;
347 return CURLE_OUT_OF_MEMORY;
348 }
349 }
350 else {
351 Curl_fileinfo_dtor(NULL, finfo);
352 }
353
354 tmpdata->parser->file_data = NULL;
355 return CURLE_OK;
356}
357
358size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
359 void *connptr)
360{
361 size_t bufflen = size*nmemb;
362 struct connectdata *conn = (struct connectdata *)connptr;
363 struct ftp_wc_tmpdata *tmpdata = conn->data->wildcard.tmp;
364 struct ftp_parselist_data *parser = tmpdata->parser;
365 struct curl_fileinfo *finfo;
366 unsigned long i = 0;
367 CURLcode rc;
368
369 if(parser->error) { /* error in previous call */
370 /* scenario:
371 * 1. call => OK..
372 * 2. call => OUT_OF_MEMORY (or other error)
373 * 3. (last) call => is skipped RIGHT HERE and the error is hadled later
374 * in wc_statemach()
375 */
376 return bufflen;
377 }
378
379 if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) {
380 /* considering info about FILE response format */
381 parser->os_type = (buffer[0] >= '0' && buffer[0] <= '9') ?
382 OS_TYPE_WIN_NT : OS_TYPE_UNIX;
383 }
384
385 while(i < bufflen) { /* FSM */
386
387 char c = buffer[i];
388 if(!parser->file_data) { /* tmp file data is not allocated yet */
389 parser->file_data = Curl_fileinfo_alloc();
390 if(!parser->file_data) {
391 parser->error = CURLE_OUT_OF_MEMORY;
392 return bufflen;
393 }
394 parser->file_data->b_data = malloc(FTP_BUFFER_ALLOCSIZE);
395 if(!parser->file_data->b_data) {
396 PL_ERROR(conn, CURLE_OUT_OF_MEMORY);
397 return bufflen;
398 }
399 parser->file_data->b_size = FTP_BUFFER_ALLOCSIZE;
400 parser->item_offset = 0;
401 parser->item_length = 0;
402 }
403
404 finfo = parser->file_data;
405 finfo->b_data[finfo->b_used++] = c;
406
407 if(finfo->b_used >= finfo->b_size - 1) {
408 /* if it is important, extend buffer space for file data */
409 char *tmp = realloc(finfo->b_data,
410 finfo->b_size + FTP_BUFFER_ALLOCSIZE);
411 if(tmp) {
412 finfo->b_size += FTP_BUFFER_ALLOCSIZE;
413 finfo->b_data = tmp;
414 }
415 else {
416 Curl_fileinfo_dtor(NULL, parser->file_data);
417 parser->file_data = NULL;
418 parser->error = CURLE_OUT_OF_MEMORY;
419 PL_ERROR(conn, CURLE_OUT_OF_MEMORY);
420 return bufflen;
421 }
422 }
423
424 switch (parser->os_type) {
425 case OS_TYPE_UNIX:
426 switch (parser->state.UNIX.main) {
427 case PL_UNIX_TOTALSIZE:
428 switch(parser->state.UNIX.sub.total_dirsize) {
429 case PL_UNIX_TOTALSIZE_INIT:
430 if(c == 't') {
431 parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
432 parser->item_length++;
433 }
434 else {
435 parser->state.UNIX.main = PL_UNIX_FILETYPE;
436 /* start FSM again not considering size of directory */
437 finfo->b_used = 0;
438 i--;
439 }
440 break;
441 case PL_UNIX_TOTALSIZE_READING:
442 parser->item_length++;
443 if(c == '\r') {
444 parser->item_length--;
445 finfo->b_used--;
446 }
447 else if(c == '\n') {
448 finfo->b_data[parser->item_length - 1] = 0;
449 if(strncmp("total ", finfo->b_data, 6) == 0) {
450 char *endptr = NULL;
451 /* here we can deal with directory size */
452 curlx_strtoofft(finfo->b_data+6, &endptr, 10);
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 rc = ftp_pl_insert_finfo(conn, finfo);
757 if(rc) {
758 PL_ERROR(conn, rc);
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 rc = ftp_pl_insert_finfo(conn, finfo);
769 if(rc) {
770 PL_ERROR(conn, rc);
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 rc = ftp_pl_insert_finfo(conn, finfo);
865 if(rc) {
866 PL_ERROR(conn, rc);
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 rc = ftp_pl_insert_finfo(conn, finfo);
877 if(rc) {
878 PL_ERROR(conn, rc);
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 + parser->item_offset,
963 &endptr, 10);
964 if(!*endptr) {
965 if(finfo->size == CURL_OFF_T_MAX ||
966 finfo->size == CURL_OFF_T_MIN) {
967 if(errno == ERANGE) {
968 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
969 return bufflen;
970 }
971 }
972 }
973 else {
974 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
975 return bufflen;
976 }
977 /* correct file type */
978 parser->file_data->filetype = CURLFILETYPE_FILE;
979 }
980
981 parser->file_data->flags |= CURLFINFOFLAG_KNOWN_SIZE;
982 parser->item_length = 0;
983 parser->state.NT.main = PL_WINNT_FILENAME;
984 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
985 }
986 break;
987 }
988 break;
989 case PL_WINNT_FILENAME:
990 switch (parser->state.NT.sub.filename) {
991 case PL_WINNT_FILENAME_PRESPACE:
992 if(c != ' ') {
993 parser->item_offset = finfo->b_used -1;
994 parser->item_length = 1;
995 parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
996 }
997 break;
998 case PL_WINNT_FILENAME_CONTENT:
999 parser->item_length++;
1000 if(c == '\r') {
1001 parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
1002 finfo->b_data[finfo->b_used - 1] = 0;
1003 }
1004 else if(c == '\n') {
1005 parser->offsets.filename = parser->item_offset;
1006 finfo->b_data[finfo->b_used - 1] = 0;
1007 parser->offsets.filename = parser->item_offset;
1008 rc = ftp_pl_insert_finfo(conn, finfo);
1009 if(rc) {
1010 PL_ERROR(conn, rc);
1011 return bufflen;
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 rc = ftp_pl_insert_finfo(conn, finfo);
1021 if(rc) {
1022 PL_ERROR(conn, rc);
1023 return bufflen;
1024 }
1025 parser->state.NT.main = PL_WINNT_DATE;
1026 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
1027 }
1028 else {
1029 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
1030 return bufflen;
1031 }
1032 break;
1033 }
1034 break;
1035 }
1036 break;
1037 default:
1038 return bufflen+1;
1039 }
1040
1041 i++;
1042 }
1043
1044 return bufflen;
1045}