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