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