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