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