1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2016, 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 /* 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 58 typedef 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 71 typedef 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 124 typedef enum { 125 PL_WINNT_DATE = 0, 126 PL_WINNT_TIME, 127 PL_WINNT_DIRORSIZE, 128 PL_WINNT_FILENAME 129 } pl_winNT_mainstate; 130 131 typedef 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 */ 148 struct 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 curl_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 181 struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void) 182 { 183 return calloc(1, sizeof(struct ftp_parselist_data)); 184 } 185 186 187 void Curl_ftp_parselist_data_free(struct ftp_parselist_data **pl_data) 188 { 189 free(*pl_data); 190 *pl_data = NULL; 191 } 192 193 194 CURLcode 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 202 static 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 267 static void PL_ERROR(struct connectdata *conn, CURLcode err) 268 { 269 struct ftp_wc_tmpdata *tmpdata = conn->data->wildcard.tmp; 270 struct ftp_parselist_data *parser = tmpdata->parser; 271 if(parser->file_data) 272 Curl_fileinfo_dtor(NULL, parser->file_data); 273 parser->file_data = NULL; 274 parser->error = err; 275 } 276 277 static CURLcode ftp_pl_insert_finfo(struct connectdata *conn, 278 struct curl_fileinfo *finfo) 279 { 280 curl_fnmatch_callback compare; 281 struct WildcardData *wc = &conn->data->wildcard; 282 struct ftp_wc_tmpdata *tmpdata = wc->tmp; 283 struct curl_llist *llist = wc->filelist; 284 struct ftp_parselist_data *parser = tmpdata->parser; 285 bool add = TRUE; 286 287 /* move finfo pointers to b_data */ 288 char *str = finfo->b_data; 289 finfo->filename = str + parser->offsets.filename; 290 finfo->strings.group = parser->offsets.group ? 291 str + parser->offsets.group : NULL; 292 finfo->strings.perm = parser->offsets.perm ? 293 str + parser->offsets.perm : NULL; 294 finfo->strings.target = parser->offsets.symlink_target ? 295 str + parser->offsets.symlink_target : NULL; 296 finfo->strings.time = str + parser->offsets.time; 297 finfo->strings.user = parser->offsets.user ? 298 str + parser->offsets.user : NULL; 299 300 /* get correct fnmatch callback */ 301 compare = conn->data->set.fnmatch; 302 if(!compare) 303 compare = Curl_fnmatch; 304 305 /* filter pattern-corresponding filenames */ 306 if(compare(conn->data->set.fnmatch_data, wc->pattern, 307 finfo->filename) == 0) { 308 /* discard symlink which is containing multiple " -> " */ 309 if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target && 310 (strstr(finfo->strings.target, " -> "))) { 311 add = FALSE; 312 } 313 } 314 else { 315 add = FALSE; 316 } 317 318 if(add) { 319 if(!Curl_llist_insert_next(llist, llist->tail, finfo)) { 320 Curl_fileinfo_dtor(NULL, finfo); 321 tmpdata->parser->file_data = NULL; 322 return CURLE_OUT_OF_MEMORY; 323 } 324 } 325 else { 326 Curl_fileinfo_dtor(NULL, finfo); 327 } 328 329 tmpdata->parser->file_data = NULL; 330 return CURLE_OK; 331 } 332 333 size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, 334 void *connptr) 335 { 336 size_t bufflen = size*nmemb; 337 struct connectdata *conn = (struct connectdata *)connptr; 338 struct ftp_wc_tmpdata *tmpdata = conn->data->wildcard.tmp; 339 struct ftp_parselist_data *parser = tmpdata->parser; 340 struct curl_fileinfo *finfo; 341 unsigned long i = 0; 342 CURLcode result; 343 344 if(parser->error) { /* error in previous call */ 345 /* scenario: 346 * 1. call => OK.. 347 * 2. call => OUT_OF_MEMORY (or other error) 348 * 3. (last) call => is skipped RIGHT HERE and the error is hadled later 349 * in wc_statemach() 350 */ 351 return bufflen; 352 } 353 354 if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) { 355 /* considering info about FILE response format */ 356 parser->os_type = (buffer[0] >= '0' && buffer[0] <= '9') ? 357 OS_TYPE_WIN_NT : OS_TYPE_UNIX; 358 } 359 360 while(i < bufflen) { /* FSM */ 361 362 char c = buffer[i]; 363 if(!parser->file_data) { /* tmp file data is not allocated yet */ 364 parser->file_data = Curl_fileinfo_alloc(); 365 if(!parser->file_data) { 366 parser->error = CURLE_OUT_OF_MEMORY; 367 return bufflen; 368 } 369 parser->file_data->b_data = malloc(FTP_BUFFER_ALLOCSIZE); 370 if(!parser->file_data->b_data) { 371 PL_ERROR(conn, CURLE_OUT_OF_MEMORY); 372 return bufflen; 373 } 374 parser->file_data->b_size = FTP_BUFFER_ALLOCSIZE; 375 parser->item_offset = 0; 376 parser->item_length = 0; 377 } 378 379 finfo = parser->file_data; 380 finfo->b_data[finfo->b_used++] = c; 381 382 if(finfo->b_used >= finfo->b_size - 1) { 383 /* if it is important, extend buffer space for file data */ 384 char *tmp = realloc(finfo->b_data, 385 finfo->b_size + FTP_BUFFER_ALLOCSIZE); 386 if(tmp) { 387 finfo->b_size += FTP_BUFFER_ALLOCSIZE; 388 finfo->b_data = tmp; 389 } 390 else { 391 Curl_fileinfo_dtor(NULL, parser->file_data); 392 parser->file_data = NULL; 393 parser->error = CURLE_OUT_OF_MEMORY; 394 PL_ERROR(conn, CURLE_OUT_OF_MEMORY); 395 return bufflen; 396 } 397 } 398 399 switch (parser->os_type) { 400 case OS_TYPE_UNIX: 401 switch (parser->state.UNIX.main) { 402 case PL_UNIX_TOTALSIZE: 403 switch(parser->state.UNIX.sub.total_dirsize) { 404 case PL_UNIX_TOTALSIZE_INIT: 405 if(c == 't') { 406 parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING; 407 parser->item_length++; 408 } 409 else { 410 parser->state.UNIX.main = PL_UNIX_FILETYPE; 411 /* start FSM again not considering size of directory */ 412 finfo->b_used = 0; 413 i--; 414 } 415 break; 416 case PL_UNIX_TOTALSIZE_READING: 417 parser->item_length++; 418 if(c == '\r') { 419 parser->item_length--; 420 finfo->b_used--; 421 } 422 else if(c == '\n') { 423 finfo->b_data[parser->item_length - 1] = 0; 424 if(strncmp("total ", finfo->b_data, 6) == 0) { 425 char *endptr = finfo->b_data+6; 426 /* here we can deal with directory size, pass the leading white 427 spaces and then the digits */ 428 while(ISSPACE(*endptr)) 429 endptr++; 430 while(ISDIGIT(*endptr)) 431 endptr++; 432 if(*endptr != 0) { 433 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 434 return bufflen; 435 } 436 else { 437 parser->state.UNIX.main = PL_UNIX_FILETYPE; 438 finfo->b_used = 0; 439 } 440 } 441 else { 442 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 443 return bufflen; 444 } 445 } 446 break; 447 } 448 break; 449 case PL_UNIX_FILETYPE: 450 switch (c) { 451 case '-': 452 finfo->filetype = CURLFILETYPE_FILE; 453 break; 454 case 'd': 455 finfo->filetype = CURLFILETYPE_DIRECTORY; 456 break; 457 case 'l': 458 finfo->filetype = CURLFILETYPE_SYMLINK; 459 break; 460 case 'p': 461 finfo->filetype = CURLFILETYPE_NAMEDPIPE; 462 break; 463 case 's': 464 finfo->filetype = CURLFILETYPE_SOCKET; 465 break; 466 case 'c': 467 finfo->filetype = CURLFILETYPE_DEVICE_CHAR; 468 break; 469 case 'b': 470 finfo->filetype = CURLFILETYPE_DEVICE_BLOCK; 471 break; 472 case 'D': 473 finfo->filetype = CURLFILETYPE_DOOR; 474 break; 475 default: 476 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 477 return bufflen; 478 } 479 parser->state.UNIX.main = PL_UNIX_PERMISSION; 480 parser->item_length = 0; 481 parser->item_offset = 1; 482 break; 483 case PL_UNIX_PERMISSION: 484 parser->item_length++; 485 if(parser->item_length <= 9) { 486 if(!strchr("rwx-tTsS", c)) { 487 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 488 return bufflen; 489 } 490 } 491 else if(parser->item_length == 10) { 492 unsigned int perm; 493 if(c != ' ') { 494 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 495 return bufflen; 496 } 497 finfo->b_data[10] = 0; /* terminate permissions */ 498 perm = ftp_pl_get_permission(finfo->b_data + parser->item_offset); 499 if(perm & FTP_LP_MALFORMATED_PERM) { 500 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 501 return bufflen; 502 } 503 parser->file_data->flags |= CURLFINFOFLAG_KNOWN_PERM; 504 parser->file_data->perm = perm; 505 parser->offsets.perm = parser->item_offset; 506 507 parser->item_length = 0; 508 parser->state.UNIX.main = PL_UNIX_HLINKS; 509 parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE; 510 } 511 break; 512 case PL_UNIX_HLINKS: 513 switch(parser->state.UNIX.sub.hlinks) { 514 case PL_UNIX_HLINKS_PRESPACE: 515 if(c != ' ') { 516 if(c >= '0' && c <= '9') { 517 parser->item_offset = finfo->b_used - 1; 518 parser->item_length = 1; 519 parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER; 520 } 521 else { 522 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 523 return bufflen; 524 } 525 } 526 break; 527 case PL_UNIX_HLINKS_NUMBER: 528 parser->item_length ++; 529 if(c == ' ') { 530 char *p; 531 long int hlinks; 532 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; 533 hlinks = strtol(finfo->b_data + parser->item_offset, &p, 10); 534 if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) { 535 parser->file_data->flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT; 536 parser->file_data->hardlinks = hlinks; 537 } 538 parser->item_length = 0; 539 parser->item_offset = 0; 540 parser->state.UNIX.main = PL_UNIX_USER; 541 parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE; 542 } 543 else if(c < '0' || c > '9') { 544 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 545 return bufflen; 546 } 547 break; 548 } 549 break; 550 case PL_UNIX_USER: 551 switch(parser->state.UNIX.sub.user) { 552 case PL_UNIX_USER_PRESPACE: 553 if(c != ' ') { 554 parser->item_offset = finfo->b_used - 1; 555 parser->item_length = 1; 556 parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING; 557 } 558 break; 559 case PL_UNIX_USER_PARSING: 560 parser->item_length++; 561 if(c == ' ') { 562 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; 563 parser->offsets.user = parser->item_offset; 564 parser->state.UNIX.main = PL_UNIX_GROUP; 565 parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE; 566 parser->item_offset = 0; 567 parser->item_length = 0; 568 } 569 break; 570 } 571 break; 572 case PL_UNIX_GROUP: 573 switch(parser->state.UNIX.sub.group) { 574 case PL_UNIX_GROUP_PRESPACE: 575 if(c != ' ') { 576 parser->item_offset = finfo->b_used - 1; 577 parser->item_length = 1; 578 parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME; 579 } 580 break; 581 case PL_UNIX_GROUP_NAME: 582 parser->item_length++; 583 if(c == ' ') { 584 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; 585 parser->offsets.group = parser->item_offset; 586 parser->state.UNIX.main = PL_UNIX_SIZE; 587 parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE; 588 parser->item_offset = 0; 589 parser->item_length = 0; 590 } 591 break; 592 } 593 break; 594 case PL_UNIX_SIZE: 595 switch(parser->state.UNIX.sub.size) { 596 case PL_UNIX_SIZE_PRESPACE: 597 if(c != ' ') { 598 if(c >= '0' && c <= '9') { 599 parser->item_offset = finfo->b_used - 1; 600 parser->item_length = 1; 601 parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER; 602 } 603 else { 604 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 605 return bufflen; 606 } 607 } 608 break; 609 case PL_UNIX_SIZE_NUMBER: 610 parser->item_length++; 611 if(c == ' ') { 612 char *p; 613 curl_off_t fsize; 614 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; 615 fsize = curlx_strtoofft(finfo->b_data+parser->item_offset, &p, 10); 616 if(p[0] == '\0' && fsize != CURL_OFF_T_MAX && 617 fsize != CURL_OFF_T_MIN) { 618 parser->file_data->flags |= CURLFINFOFLAG_KNOWN_SIZE; 619 parser->file_data->size = fsize; 620 } 621 parser->item_length = 0; 622 parser->item_offset = 0; 623 parser->state.UNIX.main = PL_UNIX_TIME; 624 parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1; 625 } 626 else if(!ISDIGIT(c)) { 627 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 628 return bufflen; 629 } 630 break; 631 } 632 break; 633 case PL_UNIX_TIME: 634 switch(parser->state.UNIX.sub.time) { 635 case PL_UNIX_TIME_PREPART1: 636 if(c != ' ') { 637 if(ISALNUM(c)) { 638 parser->item_offset = finfo->b_used -1; 639 parser->item_length = 1; 640 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1; 641 } 642 else { 643 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 644 return bufflen; 645 } 646 } 647 break; 648 case PL_UNIX_TIME_PART1: 649 parser->item_length++; 650 if(c == ' ') { 651 parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2; 652 } 653 else if(!ISALNUM(c) && c != '.') { 654 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 655 return bufflen; 656 } 657 break; 658 case PL_UNIX_TIME_PREPART2: 659 parser->item_length++; 660 if(c != ' ') { 661 if(ISALNUM(c)) { 662 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2; 663 } 664 else { 665 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 666 return bufflen; 667 } 668 } 669 break; 670 case PL_UNIX_TIME_PART2: 671 parser->item_length++; 672 if(c == ' ') { 673 parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3; 674 } 675 else if(!ISALNUM(c) && c != '.') { 676 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 677 return bufflen; 678 } 679 break; 680 case PL_UNIX_TIME_PREPART3: 681 parser->item_length++; 682 if(c != ' ') { 683 if(ISALNUM(c)) { 684 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3; 685 } 686 else { 687 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 688 return bufflen; 689 } 690 } 691 break; 692 case PL_UNIX_TIME_PART3: 693 parser->item_length++; 694 if(c == ' ') { 695 finfo->b_data[parser->item_offset + parser->item_length -1] = 0; 696 parser->offsets.time = parser->item_offset; 697 /* 698 if(ftp_pl_gettime(parser, finfo->b_data + parser->item_offset)) { 699 parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME; 700 } 701 */ 702 if(finfo->filetype == CURLFILETYPE_SYMLINK) { 703 parser->state.UNIX.main = PL_UNIX_SYMLINK; 704 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE; 705 } 706 else { 707 parser->state.UNIX.main = PL_UNIX_FILENAME; 708 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE; 709 } 710 } 711 else if(!ISALNUM(c) && c != '.' && c != ':') { 712 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 713 return bufflen; 714 } 715 break; 716 } 717 break; 718 case PL_UNIX_FILENAME: 719 switch(parser->state.UNIX.sub.filename) { 720 case PL_UNIX_FILENAME_PRESPACE: 721 if(c != ' ') { 722 parser->item_offset = finfo->b_used - 1; 723 parser->item_length = 1; 724 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME; 725 } 726 break; 727 case PL_UNIX_FILENAME_NAME: 728 parser->item_length++; 729 if(c == '\r') { 730 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL; 731 } 732 else if(c == '\n') { 733 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; 734 parser->offsets.filename = parser->item_offset; 735 parser->state.UNIX.main = PL_UNIX_FILETYPE; 736 result = ftp_pl_insert_finfo(conn, finfo); 737 if(result) { 738 PL_ERROR(conn, result); 739 return bufflen; 740 } 741 } 742 break; 743 case PL_UNIX_FILENAME_WINDOWSEOL: 744 if(c == '\n') { 745 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; 746 parser->offsets.filename = parser->item_offset; 747 parser->state.UNIX.main = PL_UNIX_FILETYPE; 748 result = ftp_pl_insert_finfo(conn, finfo); 749 if(result) { 750 PL_ERROR(conn, result); 751 return bufflen; 752 } 753 } 754 else { 755 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 756 return bufflen; 757 } 758 break; 759 } 760 break; 761 case PL_UNIX_SYMLINK: 762 switch(parser->state.UNIX.sub.symlink) { 763 case PL_UNIX_SYMLINK_PRESPACE: 764 if(c != ' ') { 765 parser->item_offset = finfo->b_used - 1; 766 parser->item_length = 1; 767 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; 768 } 769 break; 770 case PL_UNIX_SYMLINK_NAME: 771 parser->item_length++; 772 if(c == ' ') { 773 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1; 774 } 775 else if(c == '\r' || c == '\n') { 776 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 777 return bufflen; 778 } 779 break; 780 case PL_UNIX_SYMLINK_PRETARGET1: 781 parser->item_length++; 782 if(c == '-') { 783 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2; 784 } 785 else if(c == '\r' || c == '\n') { 786 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 787 return bufflen; 788 } 789 else { 790 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; 791 } 792 break; 793 case PL_UNIX_SYMLINK_PRETARGET2: 794 parser->item_length++; 795 if(c == '>') { 796 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3; 797 } 798 else if(c == '\r' || c == '\n') { 799 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 800 return bufflen; 801 } 802 else { 803 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; 804 } 805 break; 806 case PL_UNIX_SYMLINK_PRETARGET3: 807 parser->item_length++; 808 if(c == ' ') { 809 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4; 810 /* now place where is symlink following */ 811 finfo->b_data[parser->item_offset + parser->item_length - 4] = 0; 812 parser->offsets.filename = parser->item_offset; 813 parser->item_length = 0; 814 parser->item_offset = 0; 815 } 816 else if(c == '\r' || c == '\n') { 817 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 818 return bufflen; 819 } 820 else { 821 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; 822 } 823 break; 824 case PL_UNIX_SYMLINK_PRETARGET4: 825 if(c != '\r' && c != '\n') { 826 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET; 827 parser->item_offset = finfo->b_used - 1; 828 parser->item_length = 1; 829 } 830 else { 831 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 832 return bufflen; 833 } 834 break; 835 case PL_UNIX_SYMLINK_TARGET: 836 parser->item_length++; 837 if(c == '\r') { 838 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL; 839 } 840 else if(c == '\n') { 841 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; 842 parser->offsets.symlink_target = parser->item_offset; 843 result = ftp_pl_insert_finfo(conn, finfo); 844 if(result) { 845 PL_ERROR(conn, result); 846 return bufflen; 847 } 848 parser->state.UNIX.main = PL_UNIX_FILETYPE; 849 } 850 break; 851 case PL_UNIX_SYMLINK_WINDOWSEOL: 852 if(c == '\n') { 853 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; 854 parser->offsets.symlink_target = parser->item_offset; 855 result = ftp_pl_insert_finfo(conn, finfo); 856 if(result) { 857 PL_ERROR(conn, result); 858 return bufflen; 859 } 860 parser->state.UNIX.main = PL_UNIX_FILETYPE; 861 } 862 else { 863 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 864 return bufflen; 865 } 866 break; 867 } 868 break; 869 } 870 break; 871 case OS_TYPE_WIN_NT: 872 switch(parser->state.NT.main) { 873 case PL_WINNT_DATE: 874 parser->item_length++; 875 if(parser->item_length < 9) { 876 if(!strchr("0123456789-", c)) { /* only simple control */ 877 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 878 return bufflen; 879 } 880 } 881 else if(parser->item_length == 9) { 882 if(c == ' ') { 883 parser->state.NT.main = PL_WINNT_TIME; 884 parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE; 885 } 886 else { 887 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 888 return bufflen; 889 } 890 } 891 else { 892 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 893 return bufflen; 894 } 895 break; 896 case PL_WINNT_TIME: 897 parser->item_length++; 898 switch(parser->state.NT.sub.time) { 899 case PL_WINNT_TIME_PRESPACE: 900 if(!ISSPACE(c)) { 901 parser->state.NT.sub.time = PL_WINNT_TIME_TIME; 902 } 903 break; 904 case PL_WINNT_TIME_TIME: 905 if(c == ' ') { 906 parser->offsets.time = parser->item_offset; 907 finfo->b_data[parser->item_offset + parser->item_length -1] = 0; 908 parser->state.NT.main = PL_WINNT_DIRORSIZE; 909 parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE; 910 parser->item_length = 0; 911 } 912 else if(!strchr("APM0123456789:", c)) { 913 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 914 return bufflen; 915 } 916 break; 917 } 918 break; 919 case PL_WINNT_DIRORSIZE: 920 switch(parser->state.NT.sub.dirorsize) { 921 case PL_WINNT_DIRORSIZE_PRESPACE: 922 if(c == ' ') { 923 924 } 925 else { 926 parser->item_offset = finfo->b_used - 1; 927 parser->item_length = 1; 928 parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT; 929 } 930 break; 931 case PL_WINNT_DIRORSIZE_CONTENT: 932 parser->item_length ++; 933 if(c == ' ') { 934 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; 935 if(strcmp("<DIR>", finfo->b_data + parser->item_offset) == 0) { 936 finfo->filetype = CURLFILETYPE_DIRECTORY; 937 finfo->size = 0; 938 } 939 else { 940 char *endptr; 941 finfo->size = curlx_strtoofft(finfo->b_data + 942 parser->item_offset, 943 &endptr, 10); 944 if(!*endptr) { 945 if(finfo->size == CURL_OFF_T_MAX || 946 finfo->size == CURL_OFF_T_MIN) { 947 if(errno == ERANGE) { 948 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 949 return bufflen; 950 } 951 } 952 } 953 else { 954 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 955 return bufflen; 956 } 957 /* correct file type */ 958 parser->file_data->filetype = CURLFILETYPE_FILE; 959 } 960 961 parser->file_data->flags |= CURLFINFOFLAG_KNOWN_SIZE; 962 parser->item_length = 0; 963 parser->state.NT.main = PL_WINNT_FILENAME; 964 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; 965 } 966 break; 967 } 968 break; 969 case PL_WINNT_FILENAME: 970 switch (parser->state.NT.sub.filename) { 971 case PL_WINNT_FILENAME_PRESPACE: 972 if(c != ' ') { 973 parser->item_offset = finfo->b_used -1; 974 parser->item_length = 1; 975 parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT; 976 } 977 break; 978 case PL_WINNT_FILENAME_CONTENT: 979 parser->item_length++; 980 if(c == '\r') { 981 parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL; 982 finfo->b_data[finfo->b_used - 1] = 0; 983 } 984 else if(c == '\n') { 985 parser->offsets.filename = parser->item_offset; 986 finfo->b_data[finfo->b_used - 1] = 0; 987 parser->offsets.filename = parser->item_offset; 988 result = ftp_pl_insert_finfo(conn, finfo); 989 if(result) { 990 PL_ERROR(conn, result); 991 return bufflen; 992 } 993 parser->state.NT.main = PL_WINNT_DATE; 994 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; 995 } 996 break; 997 case PL_WINNT_FILENAME_WINEOL: 998 if(c == '\n') { 999 parser->offsets.filename = parser->item_offset; 1000 result = ftp_pl_insert_finfo(conn, finfo); 1001 if(result) { 1002 PL_ERROR(conn, result); 1003 return bufflen; 1004 } 1005 parser->state.NT.main = PL_WINNT_DATE; 1006 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; 1007 } 1008 else { 1009 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 1010 return bufflen; 1011 } 1012 break; 1013 } 1014 break; 1015 } 1016 break; 1017 default: 1018 return bufflen + 1; 1019 } 1020 1021 i++; 1022 } 1023 1024 return bufflen; 1025 } 1026 1027 #endif /* CURL_DISABLE_FTP */ 1028