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