1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2012, 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 */ 453 while(ISSPACE(*endptr)) 454 endptr++; 455 if(*endptr != 0) { 456 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 457 return bufflen; 458 } 459 else { 460 parser->state.UNIX.main = PL_UNIX_FILETYPE; 461 finfo->b_used = 0; 462 } 463 } 464 else { 465 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 466 return bufflen; 467 } 468 } 469 break; 470 } 471 break; 472 case PL_UNIX_FILETYPE: 473 switch (c) { 474 case '-': 475 finfo->filetype = CURLFILETYPE_FILE; 476 break; 477 case 'd': 478 finfo->filetype = CURLFILETYPE_DIRECTORY; 479 break; 480 case 'l': 481 finfo->filetype = CURLFILETYPE_SYMLINK; 482 break; 483 case 'p': 484 finfo->filetype = CURLFILETYPE_NAMEDPIPE; 485 break; 486 case 's': 487 finfo->filetype = CURLFILETYPE_SOCKET; 488 break; 489 case 'c': 490 finfo->filetype = CURLFILETYPE_DEVICE_CHAR; 491 break; 492 case 'b': 493 finfo->filetype = CURLFILETYPE_DEVICE_BLOCK; 494 break; 495 case 'D': 496 finfo->filetype = CURLFILETYPE_DOOR; 497 break; 498 default: 499 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 500 return bufflen; 501 } 502 parser->state.UNIX.main = PL_UNIX_PERMISSION; 503 parser->item_length = 0; 504 parser->item_offset = 1; 505 break; 506 case PL_UNIX_PERMISSION: 507 parser->item_length++; 508 if(parser->item_length <= 9) { 509 if(!strchr("rwx-tTsS", c)) { 510 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 511 return bufflen; 512 } 513 } 514 else if(parser->item_length == 10) { 515 unsigned int perm; 516 if(c != ' ') { 517 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 518 return bufflen; 519 } 520 finfo->b_data[10] = 0; /* terminate permissions */ 521 perm = ftp_pl_get_permission(finfo->b_data + parser->item_offset); 522 if(perm & FTP_LP_MALFORMATED_PERM) { 523 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 524 return bufflen; 525 } 526 parser->file_data->flags |= CURLFINFOFLAG_KNOWN_PERM; 527 parser->file_data->perm = perm; 528 parser->offsets.perm = parser->item_offset; 529 530 parser->item_length = 0; 531 parser->state.UNIX.main = PL_UNIX_HLINKS; 532 parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE; 533 } 534 break; 535 case PL_UNIX_HLINKS: 536 switch(parser->state.UNIX.sub.hlinks) { 537 case PL_UNIX_HLINKS_PRESPACE: 538 if(c != ' ') { 539 if(c >= '0' && c <= '9') { 540 parser->item_offset = finfo->b_used - 1; 541 parser->item_length = 1; 542 parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER; 543 } 544 else { 545 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 546 return bufflen; 547 } 548 } 549 break; 550 case PL_UNIX_HLINKS_NUMBER: 551 parser->item_length ++; 552 if(c == ' ') { 553 char *p; 554 long int hlinks; 555 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; 556 hlinks = strtol(finfo->b_data + parser->item_offset, &p, 10); 557 if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) { 558 parser->file_data->flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT; 559 parser->file_data->hardlinks = hlinks; 560 } 561 parser->item_length = 0; 562 parser->item_offset = 0; 563 parser->state.UNIX.main = PL_UNIX_USER; 564 parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE; 565 } 566 else if(c < '0' || c > '9') { 567 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 568 return bufflen; 569 } 570 break; 571 } 572 break; 573 case PL_UNIX_USER: 574 switch(parser->state.UNIX.sub.user) { 575 case PL_UNIX_USER_PRESPACE: 576 if(c != ' ') { 577 parser->item_offset = finfo->b_used - 1; 578 parser->item_length = 1; 579 parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING; 580 } 581 break; 582 case PL_UNIX_USER_PARSING: 583 parser->item_length++; 584 if(c == ' ') { 585 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; 586 parser->offsets.user = parser->item_offset; 587 parser->state.UNIX.main = PL_UNIX_GROUP; 588 parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE; 589 parser->item_offset = 0; 590 parser->item_length = 0; 591 } 592 break; 593 } 594 break; 595 case PL_UNIX_GROUP: 596 switch(parser->state.UNIX.sub.group) { 597 case PL_UNIX_GROUP_PRESPACE: 598 if(c != ' ') { 599 parser->item_offset = finfo->b_used - 1; 600 parser->item_length = 1; 601 parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME; 602 } 603 break; 604 case PL_UNIX_GROUP_NAME: 605 parser->item_length++; 606 if(c == ' ') { 607 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; 608 parser->offsets.group = parser->item_offset; 609 parser->state.UNIX.main = PL_UNIX_SIZE; 610 parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE; 611 parser->item_offset = 0; 612 parser->item_length = 0; 613 } 614 break; 615 } 616 break; 617 case PL_UNIX_SIZE: 618 switch(parser->state.UNIX.sub.size) { 619 case PL_UNIX_SIZE_PRESPACE: 620 if(c != ' ') { 621 if(c >= '0' && c <= '9') { 622 parser->item_offset = finfo->b_used - 1; 623 parser->item_length = 1; 624 parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER; 625 } 626 else { 627 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 628 return bufflen; 629 } 630 } 631 break; 632 case PL_UNIX_SIZE_NUMBER: 633 parser->item_length++; 634 if(c == ' ') { 635 char *p; 636 curl_off_t fsize; 637 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; 638 fsize = curlx_strtoofft(finfo->b_data+parser->item_offset, &p, 10); 639 if(p[0] == '\0' && fsize != CURL_OFF_T_MAX && 640 fsize != CURL_OFF_T_MIN) { 641 parser->file_data->flags |= CURLFINFOFLAG_KNOWN_SIZE; 642 parser->file_data->size = fsize; 643 } 644 parser->item_length = 0; 645 parser->item_offset = 0; 646 parser->state.UNIX.main = PL_UNIX_TIME; 647 parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1; 648 } 649 else if(!ISDIGIT(c)) { 650 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 651 return bufflen; 652 } 653 break; 654 } 655 break; 656 case PL_UNIX_TIME: 657 switch(parser->state.UNIX.sub.time) { 658 case PL_UNIX_TIME_PREPART1: 659 if(c != ' ') { 660 if(ISALNUM(c)) { 661 parser->item_offset = finfo->b_used -1; 662 parser->item_length = 1; 663 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1; 664 } 665 else { 666 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 667 return bufflen; 668 } 669 } 670 break; 671 case PL_UNIX_TIME_PART1: 672 parser->item_length++; 673 if(c == ' ') { 674 parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2; 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_PREPART2: 682 parser->item_length++; 683 if(c != ' ') { 684 if(ISALNUM(c)) { 685 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2; 686 } 687 else { 688 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 689 return bufflen; 690 } 691 } 692 break; 693 case PL_UNIX_TIME_PART2: 694 parser->item_length++; 695 if(c == ' ') { 696 parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3; 697 } 698 else if(!ISALNUM(c) && c != '.') { 699 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 700 return bufflen; 701 } 702 break; 703 case PL_UNIX_TIME_PREPART3: 704 parser->item_length++; 705 if(c != ' ') { 706 if(ISALNUM(c)) { 707 parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3; 708 } 709 else { 710 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 711 return bufflen; 712 } 713 } 714 break; 715 case PL_UNIX_TIME_PART3: 716 parser->item_length++; 717 if(c == ' ') { 718 finfo->b_data[parser->item_offset + parser->item_length -1] = 0; 719 parser->offsets.time = parser->item_offset; 720 if(ftp_pl_gettime(parser, finfo->b_data + parser->item_offset)) { 721 parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME; 722 } 723 if(finfo->filetype == CURLFILETYPE_SYMLINK) { 724 parser->state.UNIX.main = PL_UNIX_SYMLINK; 725 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE; 726 } 727 else { 728 parser->state.UNIX.main = PL_UNIX_FILENAME; 729 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE; 730 } 731 } 732 else if(!ISALNUM(c) && c != '.' && c != ':') { 733 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 734 return bufflen; 735 } 736 break; 737 } 738 break; 739 case PL_UNIX_FILENAME: 740 switch(parser->state.UNIX.sub.filename) { 741 case PL_UNIX_FILENAME_PRESPACE: 742 if(c != ' ') { 743 parser->item_offset = finfo->b_used - 1; 744 parser->item_length = 1; 745 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME; 746 } 747 break; 748 case PL_UNIX_FILENAME_NAME: 749 parser->item_length++; 750 if(c == '\r') { 751 parser->item_length--; 752 parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL; 753 } 754 else if(c == '\n') { 755 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; 756 parser->offsets.filename = parser->item_offset; 757 parser->state.UNIX.main = PL_UNIX_FILETYPE; 758 rc = ftp_pl_insert_finfo(conn, finfo); 759 if(rc) { 760 PL_ERROR(conn, rc); 761 return bufflen; 762 } 763 } 764 break; 765 case PL_UNIX_FILENAME_WINDOWSEOL: 766 if(c == '\n') { 767 finfo->b_data[parser->item_offset + parser->item_length] = 0; 768 parser->offsets.filename = parser->item_offset; 769 parser->state.UNIX.main = PL_UNIX_FILETYPE; 770 rc = ftp_pl_insert_finfo(conn, finfo); 771 if(rc) { 772 PL_ERROR(conn, rc); 773 return bufflen; 774 } 775 } 776 else { 777 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 778 return bufflen; 779 } 780 break; 781 } 782 break; 783 case PL_UNIX_SYMLINK: 784 switch(parser->state.UNIX.sub.symlink) { 785 case PL_UNIX_SYMLINK_PRESPACE: 786 if(c != ' ') { 787 parser->item_offset = finfo->b_used - 1; 788 parser->item_length = 1; 789 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; 790 } 791 break; 792 case PL_UNIX_SYMLINK_NAME: 793 parser->item_length++; 794 if(c == ' ') { 795 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1; 796 } 797 else if(c == '\r' || c == '\n') { 798 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 799 return bufflen; 800 } 801 break; 802 case PL_UNIX_SYMLINK_PRETARGET1: 803 parser->item_length++; 804 if(c == '-') { 805 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2; 806 } 807 else if(c == '\r' || c == '\n') { 808 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 809 return bufflen; 810 } 811 else { 812 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; 813 } 814 break; 815 case PL_UNIX_SYMLINK_PRETARGET2: 816 parser->item_length++; 817 if(c == '>') { 818 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3; 819 } 820 else if(c == '\r' || c == '\n') { 821 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 822 return bufflen; 823 } 824 else { 825 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; 826 } 827 break; 828 case PL_UNIX_SYMLINK_PRETARGET3: 829 parser->item_length++; 830 if(c == ' ') { 831 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4; 832 /* now place where is symlink following */ 833 finfo->b_data[parser->item_offset + parser->item_length - 4] = 0; 834 parser->offsets.filename = parser->item_offset; 835 parser->item_length = 0; 836 parser->item_offset = 0; 837 } 838 else if(c == '\r' || c == '\n') { 839 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 840 return bufflen; 841 } 842 else { 843 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; 844 } 845 break; 846 case PL_UNIX_SYMLINK_PRETARGET4: 847 if(c != '\r' && c != '\n') { 848 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET; 849 parser->item_offset = finfo->b_used - 1; 850 parser->item_length = 1; 851 } 852 else { 853 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 854 return bufflen; 855 } 856 break; 857 case PL_UNIX_SYMLINK_TARGET: 858 parser->item_length ++; 859 if(c == '\r') { 860 parser->item_length --; 861 parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL; 862 } 863 else if(c == '\n') { 864 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; 865 parser->offsets.symlink_target = parser->item_offset; 866 rc = ftp_pl_insert_finfo(conn, finfo); 867 if(rc) { 868 PL_ERROR(conn, rc); 869 return bufflen; 870 } 871 parser->state.UNIX.main = PL_UNIX_FILETYPE; 872 } 873 break; 874 case PL_UNIX_SYMLINK_WINDOWSEOL: 875 if(c == '\n') { 876 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; 877 parser->offsets.symlink_target = parser->item_offset; 878 rc = ftp_pl_insert_finfo(conn, finfo); 879 if(rc) { 880 PL_ERROR(conn, rc); 881 return bufflen; 882 } 883 parser->state.UNIX.main = PL_UNIX_FILETYPE; 884 } 885 else { 886 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 887 return bufflen; 888 } 889 break; 890 } 891 break; 892 } 893 break; 894 case OS_TYPE_WIN_NT: 895 switch(parser->state.NT.main) { 896 case PL_WINNT_DATE: 897 parser->item_length++; 898 if(parser->item_length < 9) { 899 if(!strchr("0123456789-", c)) { /* only simple control */ 900 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 901 return bufflen; 902 } 903 } 904 else if(parser->item_length == 9) { 905 if(c == ' ') { 906 parser->state.NT.main = PL_WINNT_TIME; 907 parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE; 908 } 909 else { 910 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 911 return bufflen; 912 } 913 } 914 else { 915 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 916 return bufflen; 917 } 918 break; 919 case PL_WINNT_TIME: 920 parser->item_length++; 921 switch(parser->state.NT.sub.time) { 922 case PL_WINNT_TIME_PRESPACE: 923 if(!ISSPACE(c)) { 924 parser->state.NT.sub.time = PL_WINNT_TIME_TIME; 925 } 926 break; 927 case PL_WINNT_TIME_TIME: 928 if(c == ' ') { 929 parser->offsets.time = parser->item_offset; 930 finfo->b_data[parser->item_offset + parser->item_length -1] = 0; 931 parser->state.NT.main = PL_WINNT_DIRORSIZE; 932 parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE; 933 parser->item_length = 0; 934 } 935 else if(!strchr("APM0123456789:", c)) { 936 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 937 return bufflen; 938 } 939 break; 940 } 941 break; 942 case PL_WINNT_DIRORSIZE: 943 switch(parser->state.NT.sub.dirorsize) { 944 case PL_WINNT_DIRORSIZE_PRESPACE: 945 if(c == ' ') { 946 947 } 948 else { 949 parser->item_offset = finfo->b_used - 1; 950 parser->item_length = 1; 951 parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT; 952 } 953 break; 954 case PL_WINNT_DIRORSIZE_CONTENT: 955 parser->item_length ++; 956 if(c == ' ') { 957 finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; 958 if(strcmp("<DIR>", finfo->b_data + parser->item_offset) == 0) { 959 finfo->filetype = CURLFILETYPE_DIRECTORY; 960 finfo->size = 0; 961 } 962 else { 963 char *endptr; 964 finfo->size = curlx_strtoofft(finfo->b_data + 965 parser->item_offset, 966 &endptr, 10); 967 if(!*endptr) { 968 if(finfo->size == CURL_OFF_T_MAX || 969 finfo->size == CURL_OFF_T_MIN) { 970 if(errno == ERANGE) { 971 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 972 return bufflen; 973 } 974 } 975 } 976 else { 977 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 978 return bufflen; 979 } 980 /* correct file type */ 981 parser->file_data->filetype = CURLFILETYPE_FILE; 982 } 983 984 parser->file_data->flags |= CURLFINFOFLAG_KNOWN_SIZE; 985 parser->item_length = 0; 986 parser->state.NT.main = PL_WINNT_FILENAME; 987 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; 988 } 989 break; 990 } 991 break; 992 case PL_WINNT_FILENAME: 993 switch (parser->state.NT.sub.filename) { 994 case PL_WINNT_FILENAME_PRESPACE: 995 if(c != ' ') { 996 parser->item_offset = finfo->b_used -1; 997 parser->item_length = 1; 998 parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT; 999 } 1000 break; 1001 case PL_WINNT_FILENAME_CONTENT: 1002 parser->item_length++; 1003 if(c == '\r') { 1004 parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL; 1005 finfo->b_data[finfo->b_used - 1] = 0; 1006 } 1007 else if(c == '\n') { 1008 parser->offsets.filename = parser->item_offset; 1009 finfo->b_data[finfo->b_used - 1] = 0; 1010 parser->offsets.filename = parser->item_offset; 1011 rc = ftp_pl_insert_finfo(conn, finfo); 1012 if(rc) { 1013 PL_ERROR(conn, rc); 1014 return bufflen; 1015 } 1016 parser->state.NT.main = PL_WINNT_DATE; 1017 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; 1018 } 1019 break; 1020 case PL_WINNT_FILENAME_WINEOL: 1021 if(c == '\n') { 1022 parser->offsets.filename = parser->item_offset; 1023 rc = ftp_pl_insert_finfo(conn, finfo); 1024 if(rc) { 1025 PL_ERROR(conn, rc); 1026 return bufflen; 1027 } 1028 parser->state.NT.main = PL_WINNT_DATE; 1029 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; 1030 } 1031 else { 1032 PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); 1033 return bufflen; 1034 } 1035 break; 1036 } 1037 break; 1038 } 1039 break; 1040 default: 1041 return bufflen+1; 1042 } 1043 1044 i++; 1045 } 1046 1047 return bufflen; 1048} 1049 1050#endif /* CURL_DISABLE_FTP */ 1051