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