ftplistparser.c (30078B)
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 "ftp.h" 50 #include "ftplistparser.h" 51 #include "curl_fnmatch.h" 52 #include "multiif.h" 53 #include "curlx/strparse.h" 54 55 /* The last 3 #include files should be in this order */ 56 #include "curl_printf.h" 57 #include "curl_memory.h" 58 #include "memdebug.h" 59 60 typedef enum { 61 PL_UNIX_TOTALSIZE = 0, 62 PL_UNIX_FILETYPE, 63 PL_UNIX_PERMISSION, 64 PL_UNIX_HLINKS, 65 PL_UNIX_USER, 66 PL_UNIX_GROUP, 67 PL_UNIX_SIZE, 68 PL_UNIX_TIME, 69 PL_UNIX_FILENAME, 70 PL_UNIX_SYMLINK 71 } pl_unix_mainstate; 72 73 typedef union { 74 enum { 75 PL_UNIX_TOTALSIZE_INIT = 0, 76 PL_UNIX_TOTALSIZE_READING 77 } total_dirsize; 78 79 enum { 80 PL_UNIX_HLINKS_PRESPACE = 0, 81 PL_UNIX_HLINKS_NUMBER 82 } hlinks; 83 84 enum { 85 PL_UNIX_USER_PRESPACE = 0, 86 PL_UNIX_USER_PARSING 87 } user; 88 89 enum { 90 PL_UNIX_GROUP_PRESPACE = 0, 91 PL_UNIX_GROUP_NAME 92 } group; 93 94 enum { 95 PL_UNIX_SIZE_PRESPACE = 0, 96 PL_UNIX_SIZE_NUMBER 97 } size; 98 99 enum { 100 PL_UNIX_TIME_PREPART1 = 0, 101 PL_UNIX_TIME_PART1, 102 PL_UNIX_TIME_PREPART2, 103 PL_UNIX_TIME_PART2, 104 PL_UNIX_TIME_PREPART3, 105 PL_UNIX_TIME_PART3 106 } time; 107 108 enum { 109 PL_UNIX_FILENAME_PRESPACE = 0, 110 PL_UNIX_FILENAME_NAME, 111 PL_UNIX_FILENAME_WINDOWSEOL 112 } filename; 113 114 enum { 115 PL_UNIX_SYMLINK_PRESPACE = 0, 116 PL_UNIX_SYMLINK_NAME, 117 PL_UNIX_SYMLINK_PRETARGET1, 118 PL_UNIX_SYMLINK_PRETARGET2, 119 PL_UNIX_SYMLINK_PRETARGET3, 120 PL_UNIX_SYMLINK_PRETARGET4, 121 PL_UNIX_SYMLINK_TARGET, 122 PL_UNIX_SYMLINK_WINDOWSEOL 123 } symlink; 124 } pl_unix_substate; 125 126 typedef enum { 127 PL_WINNT_DATE = 0, 128 PL_WINNT_TIME, 129 PL_WINNT_DIRORSIZE, 130 PL_WINNT_FILENAME 131 } pl_winNT_mainstate; 132 133 typedef union { 134 enum { 135 PL_WINNT_TIME_PRESPACE = 0, 136 PL_WINNT_TIME_TIME 137 } time; 138 enum { 139 PL_WINNT_DIRORSIZE_PRESPACE = 0, 140 PL_WINNT_DIRORSIZE_CONTENT 141 } dirorsize; 142 enum { 143 PL_WINNT_FILENAME_PRESPACE = 0, 144 PL_WINNT_FILENAME_CONTENT, 145 PL_WINNT_FILENAME_WINEOL 146 } filename; 147 } pl_winNT_substate; 148 149 /* This struct is used in wildcard downloading - for parsing LIST response */ 150 struct ftp_parselist_data { 151 enum { 152 OS_TYPE_UNKNOWN = 0, 153 OS_TYPE_UNIX, 154 OS_TYPE_WIN_NT 155 } os_type; 156 157 union { 158 struct { 159 pl_unix_mainstate main; 160 pl_unix_substate sub; 161 } UNIX; 162 163 struct { 164 pl_winNT_mainstate main; 165 pl_winNT_substate sub; 166 } NT; 167 } state; 168 169 CURLcode error; 170 struct fileinfo *file_data; 171 unsigned int item_length; 172 size_t item_offset; 173 struct { 174 size_t filename; 175 size_t user; 176 size_t group; 177 size_t time; 178 size_t perm; 179 size_t symlink_target; 180 } offsets; 181 }; 182 183 static void fileinfo_dtor(void *user, void *element) 184 { 185 (void)user; 186 Curl_fileinfo_cleanup(element); 187 } 188 189 CURLcode Curl_wildcard_init(struct WildcardData *wc) 190 { 191 Curl_llist_init(&wc->filelist, fileinfo_dtor); 192 wc->state = CURLWC_INIT; 193 194 return CURLE_OK; 195 } 196 197 void Curl_wildcard_dtor(struct WildcardData **wcp) 198 { 199 struct WildcardData *wc = *wcp; 200 if(!wc) 201 return; 202 203 if(wc->dtor) { 204 wc->dtor(wc->ftpwc); 205 wc->dtor = ZERO_NULL; 206 wc->ftpwc = NULL; 207 } 208 DEBUGASSERT(wc->ftpwc == NULL); 209 210 Curl_llist_destroy(&wc->filelist, NULL); 211 free(wc->path); 212 wc->path = NULL; 213 free(wc->pattern); 214 wc->pattern = NULL; 215 wc->state = CURLWC_INIT; 216 free(wc); 217 *wcp = NULL; 218 } 219 220 struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void) 221 { 222 return calloc(1, sizeof(struct ftp_parselist_data)); 223 } 224 225 226 void Curl_ftp_parselist_data_free(struct ftp_parselist_data **parserp) 227 { 228 struct ftp_parselist_data *parser = *parserp; 229 if(parser) 230 Curl_fileinfo_cleanup(parser->file_data); 231 free(parser); 232 *parserp = NULL; 233 } 234 235 236 CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data) 237 { 238 return pl_data->error; 239 } 240 241 242 #define FTP_LP_MALFORMATED_PERM 0x01000000 243 244 static unsigned int ftp_pl_get_permission(const char *str) 245 { 246 unsigned int permissions = 0; 247 /* USER */ 248 if(str[0] == 'r') 249 permissions |= 1 << 8; 250 else if(str[0] != '-') 251 permissions |= FTP_LP_MALFORMATED_PERM; 252 if(str[1] == 'w') 253 permissions |= 1 << 7; 254 else if(str[1] != '-') 255 permissions |= FTP_LP_MALFORMATED_PERM; 256 257 if(str[2] == 'x') 258 permissions |= 1 << 6; 259 else if(str[2] == 's') { 260 permissions |= 1 << 6; 261 permissions |= 1 << 11; 262 } 263 else if(str[2] == 'S') 264 permissions |= 1 << 11; 265 else if(str[2] != '-') 266 permissions |= FTP_LP_MALFORMATED_PERM; 267 /* GROUP */ 268 if(str[3] == 'r') 269 permissions |= 1 << 5; 270 else if(str[3] != '-') 271 permissions |= FTP_LP_MALFORMATED_PERM; 272 if(str[4] == 'w') 273 permissions |= 1 << 4; 274 else if(str[4] != '-') 275 permissions |= FTP_LP_MALFORMATED_PERM; 276 if(str[5] == 'x') 277 permissions |= 1 << 3; 278 else if(str[5] == 's') { 279 permissions |= 1 << 3; 280 permissions |= 1 << 10; 281 } 282 else if(str[5] == 'S') 283 permissions |= 1 << 10; 284 else if(str[5] != '-') 285 permissions |= FTP_LP_MALFORMATED_PERM; 286 /* others */ 287 if(str[6] == 'r') 288 permissions |= 1 << 2; 289 else if(str[6] != '-') 290 permissions |= FTP_LP_MALFORMATED_PERM; 291 if(str[7] == 'w') 292 permissions |= 1 << 1; 293 else if(str[7] != '-') 294 permissions |= FTP_LP_MALFORMATED_PERM; 295 if(str[8] == 'x') 296 permissions |= 1; 297 else if(str[8] == 't') { 298 permissions |= 1; 299 permissions |= 1 << 9; 300 } 301 else if(str[8] == 'T') 302 permissions |= 1 << 9; 303 else if(str[8] != '-') 304 permissions |= FTP_LP_MALFORMATED_PERM; 305 306 return permissions; 307 } 308 309 static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data, 310 struct fileinfo *infop) 311 { 312 curl_fnmatch_callback compare; 313 struct WildcardData *wc = data->wildcard; 314 struct ftp_wc *ftpwc = wc->ftpwc; 315 struct Curl_llist *llist = &wc->filelist; 316 struct ftp_parselist_data *parser = ftpwc->parser; 317 bool add = TRUE; 318 struct curl_fileinfo *finfo = &infop->info; 319 320 /* set the finfo pointers */ 321 char *str = curlx_dyn_ptr(&infop->buf); 322 finfo->filename = str + parser->offsets.filename; 323 finfo->strings.group = parser->offsets.group ? 324 str + parser->offsets.group : NULL; 325 finfo->strings.perm = parser->offsets.perm ? 326 str + parser->offsets.perm : NULL; 327 finfo->strings.target = parser->offsets.symlink_target ? 328 str + parser->offsets.symlink_target : NULL; 329 finfo->strings.time = str + parser->offsets.time; 330 finfo->strings.user = parser->offsets.user ? 331 str + parser->offsets.user : NULL; 332 333 /* get correct fnmatch callback */ 334 compare = data->set.fnmatch; 335 if(!compare) 336 compare = Curl_fnmatch; 337 338 /* filter pattern-corresponding filenames */ 339 Curl_set_in_callback(data, TRUE); 340 if(compare(data->set.fnmatch_data, wc->pattern, 341 finfo->filename) == 0) { 342 /* discard symlink which is containing multiple " -> " */ 343 if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target && 344 (strstr(finfo->strings.target, " -> "))) { 345 add = FALSE; 346 } 347 } 348 else { 349 add = FALSE; 350 } 351 Curl_set_in_callback(data, FALSE); 352 353 if(add) { 354 Curl_llist_append(llist, finfo, &infop->list); 355 } 356 else { 357 Curl_fileinfo_cleanup(infop); 358 } 359 360 ftpwc->parser->file_data = NULL; 361 return CURLE_OK; 362 } 363 364 #define MAX_FTPLIST_BUFFER 10000 /* arbitrarily set */ 365 366 static CURLcode unix_filetype(const char c, curlfiletype *t) 367 { 368 switch(c) { 369 case '-': 370 *t = CURLFILETYPE_FILE; 371 break; 372 case 'd': 373 *t = CURLFILETYPE_DIRECTORY; 374 break; 375 case 'l': 376 *t = CURLFILETYPE_SYMLINK; 377 break; 378 case 'p': 379 *t = CURLFILETYPE_NAMEDPIPE; 380 break; 381 case 's': 382 *t = CURLFILETYPE_SOCKET; 383 break; 384 case 'c': 385 *t = CURLFILETYPE_DEVICE_CHAR; 386 break; 387 case 'b': 388 *t = CURLFILETYPE_DEVICE_BLOCK; 389 break; 390 case 'D': 391 *t = CURLFILETYPE_DOOR; 392 break; 393 default: 394 return CURLE_FTP_BAD_FILE_LIST; 395 } 396 return CURLE_OK; 397 } 398 399 static CURLcode parse_unix_totalsize(struct ftp_parselist_data *parser, 400 struct fileinfo *infop, 401 const char c) 402 { 403 size_t len = curlx_dyn_len(&infop->buf); 404 char *mem = curlx_dyn_ptr(&infop->buf); 405 switch(parser->state.UNIX.sub.total_dirsize) { 406 case PL_UNIX_TOTALSIZE_INIT: 407 if(c == 't') { 408 parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING; 409 parser->item_length++; 410 } 411 else { 412 parser->state.UNIX.main = PL_UNIX_FILETYPE; 413 /* continue to fall through */ 414 } 415 break; 416 case PL_UNIX_TOTALSIZE_READING: 417 parser->item_length++; 418 if(c == '\r') { 419 parser->item_length--; 420 if(len) 421 curlx_dyn_setlen(&infop->buf, --len); 422 } 423 else if(c == '\n') { 424 mem[parser->item_length - 1] = 0; 425 if(!strncmp("total ", mem, 6)) { 426 const char *endptr = mem + 6; 427 /* here we can deal with directory size, pass the leading 428 whitespace and then the digits */ 429 curlx_str_passblanks(&endptr); 430 while(ISDIGIT(*endptr)) 431 endptr++; 432 if(*endptr) { 433 return CURLE_FTP_BAD_FILE_LIST; 434 } 435 parser->state.UNIX.main = PL_UNIX_FILETYPE; 436 curlx_dyn_reset(&infop->buf); 437 } 438 else 439 return CURLE_FTP_BAD_FILE_LIST; 440 441 } 442 break; 443 } 444 return CURLE_OK; 445 } 446 447 static CURLcode parse_unix_permission(struct ftp_parselist_data *parser, 448 struct fileinfo *infop, 449 const char c) 450 { 451 char *mem = curlx_dyn_ptr(&infop->buf); 452 parser->item_length++; 453 if((parser->item_length <= 9) && !strchr("rwx-tTsS", c)) 454 return CURLE_FTP_BAD_FILE_LIST; 455 456 else if(parser->item_length == 10) { 457 unsigned int perm; 458 if(c != ' ') 459 return CURLE_FTP_BAD_FILE_LIST; 460 461 mem[10] = 0; /* terminate permissions */ 462 perm = ftp_pl_get_permission(mem + parser->item_offset); 463 if(perm & FTP_LP_MALFORMATED_PERM) 464 return CURLE_FTP_BAD_FILE_LIST; 465 466 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM; 467 parser->file_data->info.perm = perm; 468 parser->offsets.perm = parser->item_offset; 469 470 parser->item_length = 0; 471 parser->state.UNIX.main = PL_UNIX_HLINKS; 472 parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE; 473 } 474 return CURLE_OK; 475 } 476 477 static CURLcode parse_unix_hlinks(struct ftp_parselist_data *parser, 478 struct fileinfo *infop, 479 const char c) 480 { 481 size_t len = curlx_dyn_len(&infop->buf); 482 char *mem = curlx_dyn_ptr(&infop->buf); 483 484 switch(parser->state.UNIX.sub.hlinks) { 485 case PL_UNIX_HLINKS_PRESPACE: 486 if(c != ' ') { 487 if(ISDIGIT(c) && len) { 488 parser->item_offset = len - 1; 489 parser->item_length = 1; 490 parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER; 491 } 492 else 493 return CURLE_FTP_BAD_FILE_LIST; 494 } 495 break; 496 case PL_UNIX_HLINKS_NUMBER: 497 parser->item_length ++; 498 if(c == ' ') { 499 const char *p = &mem[parser->item_offset]; 500 curl_off_t hlinks; 501 mem[parser->item_offset + parser->item_length - 1] = 0; 502 503 if(!curlx_str_number(&p, &hlinks, LONG_MAX)) { 504 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT; 505 parser->file_data->info.hardlinks = (long)hlinks; 506 } 507 parser->item_length = 0; 508 parser->item_offset = 0; 509 parser->state.UNIX.main = PL_UNIX_USER; 510 parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE; 511 } 512 else if(!ISDIGIT(c)) 513 return CURLE_FTP_BAD_FILE_LIST; 514 515 break; 516 } 517 return CURLE_OK; 518 } 519 520 static CURLcode parse_unix_user(struct ftp_parselist_data *parser, 521 struct fileinfo *infop, 522 const char c) 523 { 524 size_t len = curlx_dyn_len(&infop->buf); 525 char *mem = curlx_dyn_ptr(&infop->buf); 526 switch(parser->state.UNIX.sub.user) { 527 case PL_UNIX_USER_PRESPACE: 528 if(c != ' ' && len) { 529 parser->item_offset = len - 1; 530 parser->item_length = 1; 531 parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING; 532 } 533 break; 534 case PL_UNIX_USER_PARSING: 535 parser->item_length++; 536 if(c == ' ') { 537 mem[parser->item_offset + parser->item_length - 1] = 0; 538 parser->offsets.user = parser->item_offset; 539 parser->state.UNIX.main = PL_UNIX_GROUP; 540 parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE; 541 parser->item_offset = 0; 542 parser->item_length = 0; 543 } 544 break; 545 } 546 return CURLE_OK; 547 } 548 549 static CURLcode parse_unix_group(struct ftp_parselist_data *parser, 550 struct fileinfo *infop, 551 const char c) 552 { 553 size_t len = curlx_dyn_len(&infop->buf); 554 char *mem = curlx_dyn_ptr(&infop->buf); 555 switch(parser->state.UNIX.sub.group) { 556 case PL_UNIX_GROUP_PRESPACE: 557 if(c != ' ' && len) { 558 parser->item_offset = len - 1; 559 parser->item_length = 1; 560 parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME; 561 } 562 break; 563 case PL_UNIX_GROUP_NAME: 564 parser->item_length++; 565 if(c == ' ') { 566 mem[parser->item_offset + parser->item_length - 1] = 0; 567 parser->offsets.group = parser->item_offset; 568 parser->state.UNIX.main = PL_UNIX_SIZE; 569 parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE; 570 parser->item_offset = 0; 571 parser->item_length = 0; 572 } 573 break; 574 } 575 return CURLE_OK; 576 } 577 578 static CURLcode parse_unix_size(struct ftp_parselist_data *parser, 579 struct fileinfo *infop, 580 const char c) 581 { 582 size_t len = curlx_dyn_len(&infop->buf); 583 char *mem = curlx_dyn_ptr(&infop->buf); 584 switch(parser->state.UNIX.sub.size) { 585 case PL_UNIX_SIZE_PRESPACE: 586 if(c != ' ') { 587 if(ISDIGIT(c) && len) { 588 parser->item_offset = len - 1; 589 parser->item_length = 1; 590 parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER; 591 } 592 else 593 return CURLE_FTP_BAD_FILE_LIST; 594 } 595 break; 596 case PL_UNIX_SIZE_NUMBER: 597 parser->item_length++; 598 if(c == ' ') { 599 const char *p = mem + parser->item_offset; 600 curl_off_t fsize; 601 mem[parser->item_offset + parser->item_length - 1] = 0; 602 if(!curlx_str_numblanks(&p, &fsize)) { 603 if(p[0] == '\0' && fsize != CURL_OFF_T_MAX) { 604 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE; 605 parser->file_data->info.size = fsize; 606 } 607 parser->item_length = 0; 608 parser->item_offset = 0; 609 parser->state.UNIX.main = PL_UNIX_TIME; 610 parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1; 611 } 612 } 613 else if(!ISDIGIT(c)) 614 return CURLE_FTP_BAD_FILE_LIST; 615 616 break; 617 } 618 return CURLE_OK; 619 } 620 621 static CURLcode parse_unix_time(struct ftp_parselist_data *parser, 622 struct fileinfo *infop, 623 const char c) 624 { 625 size_t len = curlx_dyn_len(&infop->buf); 626 char *mem = curlx_dyn_ptr(&infop->buf); 627 struct curl_fileinfo *finfo = &infop->info; 628 629 switch(parser->state.UNIX.sub.time) { 630 case PL_UNIX_TIME_PREPART1: 631 if(c != ' ') { 632 if(ISALNUM(c) && len) { 633 parser->item_offset = len -1; 634 parser->item_length = 1; 635 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1; 636 } 637 else 638 return CURLE_FTP_BAD_FILE_LIST; 639 } 640 break; 641 case PL_UNIX_TIME_PART1: 642 parser->item_length++; 643 if(c == ' ') 644 parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2; 645 646 else if(!ISALNUM(c) && c != '.') 647 return CURLE_FTP_BAD_FILE_LIST; 648 649 break; 650 case PL_UNIX_TIME_PREPART2: 651 parser->item_length++; 652 if(c != ' ') { 653 if(ISALNUM(c)) 654 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2; 655 else 656 return CURLE_FTP_BAD_FILE_LIST; 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 else if(!ISALNUM(c) && c != '.') 664 return CURLE_FTP_BAD_FILE_LIST; 665 break; 666 case PL_UNIX_TIME_PREPART3: 667 parser->item_length++; 668 if(c != ' ') { 669 if(ISALNUM(c)) 670 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3; 671 else 672 return CURLE_FTP_BAD_FILE_LIST; 673 } 674 break; 675 case PL_UNIX_TIME_PART3: 676 parser->item_length++; 677 if(c == ' ') { 678 mem[parser->item_offset + parser->item_length -1] = 0; 679 parser->offsets.time = parser->item_offset; 680 if(finfo->filetype == CURLFILETYPE_SYMLINK) { 681 parser->state.UNIX.main = PL_UNIX_SYMLINK; 682 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE; 683 } 684 else { 685 parser->state.UNIX.main = PL_UNIX_FILENAME; 686 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE; 687 } 688 } 689 else if(!ISALNUM(c) && c != '.' && c != ':') 690 return CURLE_FTP_BAD_FILE_LIST; 691 break; 692 } 693 return CURLE_OK; 694 } 695 696 static CURLcode parse_unix_filename(struct Curl_easy *data, 697 struct ftp_parselist_data *parser, 698 struct fileinfo *infop, 699 const char c) 700 { 701 size_t len = curlx_dyn_len(&infop->buf); 702 char *mem = curlx_dyn_ptr(&infop->buf); 703 CURLcode result = CURLE_OK; 704 705 switch(parser->state.UNIX.sub.filename) { 706 case PL_UNIX_FILENAME_PRESPACE: 707 if(c != ' ' && len) { 708 parser->item_offset = len - 1; 709 parser->item_length = 1; 710 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME; 711 } 712 break; 713 case PL_UNIX_FILENAME_NAME: 714 parser->item_length++; 715 if(c == '\r') 716 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL; 717 718 else if(c == '\n') { 719 mem[parser->item_offset + parser->item_length - 1] = 0; 720 parser->offsets.filename = parser->item_offset; 721 parser->state.UNIX.main = PL_UNIX_FILETYPE; 722 result = ftp_pl_insert_finfo(data, infop); 723 } 724 break; 725 case PL_UNIX_FILENAME_WINDOWSEOL: 726 if(c == '\n') { 727 mem[parser->item_offset + parser->item_length - 1] = 0; 728 parser->offsets.filename = parser->item_offset; 729 parser->state.UNIX.main = PL_UNIX_FILETYPE; 730 result = ftp_pl_insert_finfo(data, infop); 731 } 732 else 733 result = CURLE_FTP_BAD_FILE_LIST; 734 break; 735 } 736 return result; 737 } 738 739 static CURLcode parse_unix_symlink(struct Curl_easy *data, 740 struct ftp_parselist_data *parser, 741 struct fileinfo *infop, 742 const char c) 743 { 744 size_t len = curlx_dyn_len(&infop->buf); 745 char *mem = curlx_dyn_ptr(&infop->buf); 746 CURLcode result = CURLE_OK; 747 748 switch(parser->state.UNIX.sub.symlink) { 749 case PL_UNIX_SYMLINK_PRESPACE: 750 if(c != ' ' && len) { 751 parser->item_offset = len - 1; 752 parser->item_length = 1; 753 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; 754 } 755 break; 756 case PL_UNIX_SYMLINK_NAME: 757 parser->item_length++; 758 if(c == ' ') 759 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1; 760 761 else if(c == '\r' || c == '\n') 762 return CURLE_FTP_BAD_FILE_LIST; 763 764 break; 765 case PL_UNIX_SYMLINK_PRETARGET1: 766 parser->item_length++; 767 if(c == '-') 768 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2; 769 770 else if(c == '\r' || c == '\n') 771 return CURLE_FTP_BAD_FILE_LIST; 772 else 773 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; 774 break; 775 case PL_UNIX_SYMLINK_PRETARGET2: 776 parser->item_length++; 777 if(c == '>') 778 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3; 779 else if(c == '\r' || c == '\n') 780 return CURLE_FTP_BAD_FILE_LIST; 781 else 782 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; 783 784 break; 785 case PL_UNIX_SYMLINK_PRETARGET3: 786 parser->item_length++; 787 if(c == ' ') { 788 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4; 789 /* now place where is symlink following */ 790 mem[parser->item_offset + parser->item_length - 4] = 0; 791 parser->offsets.filename = parser->item_offset; 792 parser->item_length = 0; 793 parser->item_offset = 0; 794 } 795 else if(c == '\r' || c == '\n') 796 return CURLE_FTP_BAD_FILE_LIST; 797 else 798 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; 799 break; 800 case PL_UNIX_SYMLINK_PRETARGET4: 801 if(c != '\r' && c != '\n' && len) { 802 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET; 803 parser->item_offset = len - 1; 804 parser->item_length = 1; 805 } 806 else 807 return CURLE_FTP_BAD_FILE_LIST; 808 809 break; 810 case PL_UNIX_SYMLINK_TARGET: 811 parser->item_length++; 812 if(c == '\r') 813 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL; 814 815 else if(c == '\n') { 816 mem[parser->item_offset + parser->item_length - 1] = 0; 817 parser->offsets.symlink_target = parser->item_offset; 818 result = ftp_pl_insert_finfo(data, infop); 819 if(result) 820 break; 821 822 parser->state.UNIX.main = PL_UNIX_FILETYPE; 823 } 824 break; 825 case PL_UNIX_SYMLINK_WINDOWSEOL: 826 if(c == '\n') { 827 mem[parser->item_offset + parser->item_length - 1] = 0; 828 parser->offsets.symlink_target = parser->item_offset; 829 result = ftp_pl_insert_finfo(data, infop); 830 if(result) 831 break; 832 833 parser->state.UNIX.main = PL_UNIX_FILETYPE; 834 } 835 else 836 result = CURLE_FTP_BAD_FILE_LIST; 837 838 break; 839 } 840 return result; 841 } 842 843 static CURLcode parse_unix(struct Curl_easy *data, 844 struct ftp_parselist_data *parser, 845 struct fileinfo *infop, 846 const char c) 847 { 848 struct curl_fileinfo *finfo = &infop->info; 849 CURLcode result = CURLE_OK; 850 851 switch(parser->state.UNIX.main) { 852 case PL_UNIX_TOTALSIZE: 853 result = parse_unix_totalsize(parser, infop, c); 854 if(result) 855 break; 856 if(parser->state.UNIX.main != PL_UNIX_FILETYPE) 857 break; 858 FALLTHROUGH(); 859 case PL_UNIX_FILETYPE: 860 result = unix_filetype(c, &finfo->filetype); 861 if(!result) { 862 parser->state.UNIX.main = PL_UNIX_PERMISSION; 863 parser->item_length = 0; 864 parser->item_offset = 1; 865 } 866 break; 867 case PL_UNIX_PERMISSION: 868 result = parse_unix_permission(parser, infop, c); 869 break; 870 case PL_UNIX_HLINKS: 871 result = parse_unix_hlinks(parser, infop, c); 872 break; 873 case PL_UNIX_USER: 874 result = parse_unix_user(parser, infop, c); 875 break; 876 case PL_UNIX_GROUP: 877 result = parse_unix_group(parser, infop, c); 878 break; 879 case PL_UNIX_SIZE: 880 result = parse_unix_size(parser, infop, c); 881 break; 882 case PL_UNIX_TIME: 883 result = parse_unix_time(parser, infop, c); 884 break; 885 case PL_UNIX_FILENAME: 886 result = parse_unix_filename(data, parser, infop, c); 887 break; 888 case PL_UNIX_SYMLINK: 889 result = parse_unix_symlink(data, parser, infop, c); 890 break; 891 } 892 return result; 893 } 894 895 static CURLcode parse_winnt(struct Curl_easy *data, 896 struct ftp_parselist_data *parser, 897 struct fileinfo *infop, 898 const char c) 899 { 900 struct curl_fileinfo *finfo = &infop->info; 901 size_t len = curlx_dyn_len(&infop->buf); 902 char *mem = curlx_dyn_ptr(&infop->buf); 903 CURLcode result = CURLE_OK; 904 905 switch(parser->state.NT.main) { 906 case PL_WINNT_DATE: 907 parser->item_length++; 908 if(parser->item_length < 9) { 909 if(!strchr("0123456789-", c)) { /* only simple control */ 910 return CURLE_FTP_BAD_FILE_LIST; 911 } 912 } 913 else if(parser->item_length == 9) { 914 if(c == ' ') { 915 parser->state.NT.main = PL_WINNT_TIME; 916 parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE; 917 } 918 else 919 return CURLE_FTP_BAD_FILE_LIST; 920 } 921 else 922 return CURLE_FTP_BAD_FILE_LIST; 923 break; 924 case PL_WINNT_TIME: 925 parser->item_length++; 926 switch(parser->state.NT.sub.time) { 927 case PL_WINNT_TIME_PRESPACE: 928 if(!ISBLANK(c)) 929 parser->state.NT.sub.time = PL_WINNT_TIME_TIME; 930 break; 931 case PL_WINNT_TIME_TIME: 932 if(c == ' ') { 933 parser->offsets.time = parser->item_offset; 934 mem[parser->item_offset + parser->item_length -1] = 0; 935 parser->state.NT.main = PL_WINNT_DIRORSIZE; 936 parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE; 937 parser->item_length = 0; 938 } 939 else if(!strchr("APM0123456789:", c)) 940 return CURLE_FTP_BAD_FILE_LIST; 941 break; 942 } 943 break; 944 case PL_WINNT_DIRORSIZE: 945 switch(parser->state.NT.sub.dirorsize) { 946 case PL_WINNT_DIRORSIZE_PRESPACE: 947 if(c != ' ' && len) { 948 parser->item_offset = len - 1; 949 parser->item_length = 1; 950 parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT; 951 } 952 break; 953 case PL_WINNT_DIRORSIZE_CONTENT: 954 parser->item_length ++; 955 if(c == ' ') { 956 mem[parser->item_offset + parser->item_length - 1] = 0; 957 if(strcmp("<DIR>", mem + parser->item_offset) == 0) { 958 finfo->filetype = CURLFILETYPE_DIRECTORY; 959 finfo->size = 0; 960 } 961 else { 962 const char *p = mem + parser->item_offset; 963 if(curlx_str_numblanks(&p, &finfo->size)) { 964 return CURLE_FTP_BAD_FILE_LIST; 965 } 966 /* correct file type */ 967 parser->file_data->info.filetype = CURLFILETYPE_FILE; 968 } 969 970 parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE; 971 parser->item_length = 0; 972 parser->state.NT.main = PL_WINNT_FILENAME; 973 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; 974 } 975 break; 976 } 977 break; 978 case PL_WINNT_FILENAME: 979 switch(parser->state.NT.sub.filename) { 980 case PL_WINNT_FILENAME_PRESPACE: 981 if(c != ' ' && len) { 982 parser->item_offset = len -1; 983 parser->item_length = 1; 984 parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT; 985 } 986 break; 987 case PL_WINNT_FILENAME_CONTENT: 988 parser->item_length++; 989 if(!len) 990 return CURLE_FTP_BAD_FILE_LIST; 991 if(c == '\r') { 992 parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL; 993 mem[len - 1] = 0; 994 } 995 else if(c == '\n') { 996 parser->offsets.filename = parser->item_offset; 997 mem[len - 1] = 0; 998 result = ftp_pl_insert_finfo(data, infop); 999 if(result) 1000 return result; 1001 1002 parser->state.NT.main = PL_WINNT_DATE; 1003 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; 1004 } 1005 break; 1006 case PL_WINNT_FILENAME_WINEOL: 1007 if(c == '\n') { 1008 parser->offsets.filename = parser->item_offset; 1009 result = ftp_pl_insert_finfo(data, infop); 1010 if(result) 1011 return result; 1012 1013 parser->state.NT.main = PL_WINNT_DATE; 1014 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; 1015 } 1016 else 1017 return CURLE_FTP_BAD_FILE_LIST; 1018 1019 break; 1020 } 1021 break; 1022 } 1023 1024 return CURLE_OK; 1025 } 1026 1027 size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, 1028 void *connptr) 1029 { 1030 size_t bufflen = size*nmemb; 1031 struct Curl_easy *data = (struct Curl_easy *)connptr; 1032 struct ftp_wc *ftpwc = data->wildcard->ftpwc; 1033 struct ftp_parselist_data *parser = ftpwc->parser; 1034 size_t i = 0; 1035 CURLcode result; 1036 size_t retsize = bufflen; 1037 1038 if(parser->error) { /* error in previous call */ 1039 /* scenario: 1040 * 1. call => OK.. 1041 * 2. call => OUT_OF_MEMORY (or other error) 1042 * 3. (last) call => is skipped RIGHT HERE and the error is handled later 1043 * in wc_statemach() 1044 */ 1045 goto fail; 1046 } 1047 1048 if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) { 1049 /* considering info about FILE response format */ 1050 parser->os_type = ISDIGIT(buffer[0]) ? OS_TYPE_WIN_NT : OS_TYPE_UNIX; 1051 } 1052 1053 while(i < bufflen) { /* FSM */ 1054 char c = buffer[i]; 1055 struct fileinfo *infop; 1056 if(!parser->file_data) { /* tmp file data is not allocated yet */ 1057 parser->file_data = Curl_fileinfo_alloc(); 1058 if(!parser->file_data) { 1059 parser->error = CURLE_OUT_OF_MEMORY; 1060 goto fail; 1061 } 1062 parser->item_offset = 0; 1063 parser->item_length = 0; 1064 curlx_dyn_init(&parser->file_data->buf, MAX_FTPLIST_BUFFER); 1065 } 1066 1067 infop = parser->file_data; 1068 1069 if(curlx_dyn_addn(&infop->buf, &c, 1)) { 1070 parser->error = CURLE_OUT_OF_MEMORY; 1071 goto fail; 1072 } 1073 1074 switch(parser->os_type) { 1075 case OS_TYPE_UNIX: 1076 result = parse_unix(data, parser, infop, c); 1077 break; 1078 case OS_TYPE_WIN_NT: 1079 result = parse_winnt(data, parser, infop, c); 1080 break; 1081 default: 1082 retsize = bufflen + 1; 1083 goto fail; 1084 } 1085 if(result) { 1086 parser->error = result; 1087 goto fail; 1088 } 1089 1090 i++; 1091 } 1092 return retsize; 1093 1094 fail: 1095 1096 /* Clean up any allocated memory. */ 1097 if(parser->file_data) { 1098 Curl_fileinfo_cleanup(parser->file_data); 1099 parser->file_data = NULL; 1100 } 1101 1102 return retsize; 1103 } 1104 1105 #endif /* CURL_DISABLE_FTP */