1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_ucb.hxx" 30 31 /************************************************************************** 32 TODO 33 ************************************************************************** 34 35 *************************************************************************/ 36 #include "ftpdirp.hxx" 37 #include <osl/time.h> 38 39 40 using namespace rtl; 41 using namespace ftp; 42 43 44 typedef sal_uInt32 ULONG; 45 46 47 inline sal_Bool ascii_isLetter( sal_Unicode ch ) 48 { 49 return (( (ch >= 0x0041) && (ch <= 0x005A)) || 50 (( ch >= 0x0061) && (ch <= 0x007A))); 51 } 52 53 inline sal_Bool ascii_isWhitespace( sal_Unicode ch ) 54 { 55 return ((ch <= 0x20) && ch); 56 } 57 58 59 60 /*======================================================================== 61 * 62 * FTPDirectoryParser implementation. 63 * 64 *======================================================================*/ 65 /* 66 * parseDOS. 67 * Accepts one of two styles: 68 * 69 * 1 *WSP 1*2DIGIT ("." / "-") 1*2DIGIT ("." / "-") 1*4DIGIT 1*WSP 70 * 1*2DIGIT ":" 1*2DIGIT [*WSP ("A" / "P") "M"] 1*WSP 71 * ((DIGIT *(DIGIT / "." / ",")) / "<DIR>") 1*WSP 1*OCTET 72 * 73 * interpreted as: mm.dd.yy hh:mm (size / <DIR>) name 74 * 75 * 2 *WSP 1*DIGIT 1*WSP *(1*CHAR *WSP) *1("DIR" 1*WSP) 1*2DIGIT "-" 1*2DIGIT 76 * "-" 1*4DIGIT 1*WSP 1*2DIGIT ":" 1*2DIGIT 1*WSP 1*OCTET 77 * 78 * interpreted as: size attribs DIR mm-dd-yy hh:mm name 79 */ 80 81 sal_Bool FTPDirectoryParser::parseDOS ( 82 FTPDirentry &rEntry, 83 const sal_Char *pBuffer) 84 { 85 sal_Bool bDirectory = false; 86 sal_uInt32 nSize = 0; 87 sal_uInt16 nYear = 0; 88 sal_uInt16 nMonth = 0; 89 sal_uInt16 nDay = 0; 90 sal_uInt16 nHour = 0; 91 sal_uInt16 nMinute = 0; 92 93 enum StateType 94 { 95 STATE_INIT_LWS, 96 STATE_MONTH_OR_SIZE, 97 STATE_1_DAY, STATE_1_YEAR, STATE_1_YEAR_LWS, STATE_1_HOUR, 98 STATE_1_MINUTE, STATE_1_MINUTE_LWS, STATE_1_AP, 99 STATE_1_APM, STATE_1_LESS, STATE_1_D, STATE_1_DI, 100 STATE_1_DIR, STATE_1_SIZE, 101 STATE_2_SIZE, STATE_2_SIZE_LWS, STATE_2_ATTRIB, 102 STATE_2_D, STATE_2_DI, STATE_2_DIR_LWS, 103 STATE_2_MONTH, STATE_2_DAY, STATE_2_YEAR, STATE_2_YEAR_LWS, 104 STATE_2_HOUR, STATE_2_MINUTE, 105 STATE_LWS_NAME, 106 STATE_ERROR 107 }; 108 109 int nDigits = 0; 110 enum StateType eState = STATE_INIT_LWS; 111 for (const sal_Char *p = pBuffer; 112 eState != STATE_ERROR && *p; 113 ++p) 114 { 115 switch (eState) 116 { 117 case STATE_INIT_LWS: 118 if (*p >= '0' && *p <= '9') 119 { 120 nMonth = *p - '0'; 121 nDigits = 1; 122 eState = STATE_MONTH_OR_SIZE; 123 } 124 else if (!ascii_isWhitespace(*p)) 125 eState = STATE_ERROR; 126 break; 127 128 case STATE_MONTH_OR_SIZE: 129 if (*p >= '0' && *p <= '9') 130 { 131 nMonth = 10 * nMonth + (*p - '0'); 132 if (nDigits < 2) 133 ++nDigits; 134 else 135 { 136 nSize = nMonth; 137 nMonth = 0; 138 eState = STATE_2_SIZE; 139 } 140 } 141 else if (ascii_isWhitespace(*p)) 142 { 143 nSize = nMonth; 144 nMonth = 0; 145 eState = STATE_2_SIZE_LWS; 146 } 147 else if ((*p == '.' || *p == '-') && nMonth && nMonth <= 12) 148 { 149 nDigits = 0; 150 eState = STATE_1_DAY; 151 } 152 else 153 eState = STATE_ERROR; 154 break; 155 156 case STATE_1_DAY: 157 if (*p >= '0' && *p <= '9') 158 if (nDigits < 2) 159 { 160 nDay = 10 * nDay + (*p - '0'); 161 ++nDigits; 162 } 163 else 164 eState = STATE_ERROR; 165 else if ((*p == '.' || *p == '-') && nDay && nDay <= 31) 166 { 167 nDigits = 0; 168 eState = STATE_1_YEAR; 169 } 170 else 171 eState = STATE_ERROR; 172 break; 173 174 case STATE_1_YEAR: 175 if (*p >= '0' && *p <= '9') 176 { 177 if (nDigits < 4) 178 { 179 nYear = 10 * nYear + (*p - '0'); 180 ++nDigits; 181 } 182 else 183 eState = STATE_ERROR; 184 } 185 else 186 { 187 if (ascii_isWhitespace(*p)) 188 eState = STATE_1_YEAR_LWS; 189 else 190 eState = STATE_ERROR; 191 } 192 break; 193 194 case STATE_1_YEAR_LWS: 195 if (*p >= '0' && *p <= '9') 196 { 197 nHour = *p - '0'; 198 nDigits = 1; 199 eState = STATE_1_HOUR; 200 } 201 else if (!ascii_isWhitespace(*p)) 202 eState = STATE_ERROR; 203 break; 204 205 case STATE_1_HOUR: 206 if (*p >= '0' && *p <= '9') 207 if (nDigits < 2) 208 { 209 nHour = 10 * nHour + (*p - '0'); 210 ++nDigits; 211 } 212 else 213 eState = STATE_ERROR; 214 else if (*p == ':' && nHour < 24) 215 { 216 nDigits = 0; 217 eState = STATE_1_MINUTE; 218 } 219 else 220 eState = STATE_ERROR; 221 break; 222 223 case STATE_1_MINUTE: 224 if (*p >= '0' && *p <= '9') 225 if (nDigits < 2) 226 { 227 nMinute = 10 * nMinute + (*p - '0'); 228 ++nDigits; 229 } 230 else 231 eState = STATE_ERROR; 232 else if ((*p == 'a' || *p == 'A') && nMinute < 60) 233 if (nHour >= 1 && nHour <= 11) 234 eState = STATE_1_AP; 235 else if (nHour == 12) 236 { 237 nHour = 0; 238 eState = STATE_1_AP; 239 } 240 else 241 eState = STATE_ERROR; 242 else if ((*p == 'p' || *p == 'P') && nMinute < 60) 243 if (nHour >= 1 && nHour <= 11) 244 { 245 nHour += 12; 246 eState = STATE_1_AP; 247 } 248 else if (nHour == 12) 249 eState = STATE_1_AP; 250 else 251 eState = STATE_ERROR; 252 else if (ascii_isWhitespace(*p) && (nMinute < 60)) 253 eState = STATE_1_MINUTE_LWS; 254 else 255 eState = STATE_ERROR; 256 break; 257 258 case STATE_1_MINUTE_LWS: 259 if (*p == 'a' || *p == 'A') 260 if (nHour >= 1 && nHour <= 11) 261 eState = STATE_1_AP; 262 else if (nHour == 12) 263 { 264 nHour = 0; 265 eState = STATE_1_AP; 266 } 267 else 268 eState = STATE_ERROR; 269 else if (*p == 'p' || *p == 'P') 270 if (nHour >= 1 && nHour <= 11) 271 { 272 nHour += 12; 273 eState = STATE_1_AP; 274 } 275 else if (nHour == 12) 276 eState = STATE_1_AP; 277 else 278 eState = STATE_ERROR; 279 else if (*p == '<') 280 eState = STATE_1_LESS; 281 else if (*p >= '0' && *p <= '9') 282 { 283 nSize = *p - '0'; 284 eState = STATE_1_SIZE; 285 } 286 else if (!ascii_isWhitespace(*p)) 287 eState = STATE_ERROR; 288 break; 289 290 case STATE_1_AP: 291 eState = *p == 'm' || *p == 'M' ? STATE_1_APM : STATE_ERROR; 292 break; 293 294 case STATE_1_APM: 295 if (*p == '<') 296 eState = STATE_1_LESS; 297 else if (*p >= '0' && *p <= '9') 298 { 299 nSize = *p - '0'; 300 eState = STATE_1_SIZE; 301 } 302 else if (!ascii_isWhitespace(*p)) 303 eState = STATE_ERROR; 304 break; 305 306 case STATE_1_LESS: 307 eState = *p == 'd' || *p == 'D' ? STATE_1_D : STATE_ERROR; 308 break; 309 310 case STATE_1_D: 311 eState = *p == 'i' || *p == 'I' ? STATE_1_DI : STATE_ERROR; 312 break; 313 314 case STATE_1_DI: 315 eState = *p == 'r' || *p == 'R' ? STATE_1_DIR : STATE_ERROR; 316 break; 317 318 case STATE_1_DIR: 319 if (*p == '>') 320 { 321 bDirectory = true; 322 eState = STATE_LWS_NAME; 323 } 324 else 325 eState = STATE_ERROR; 326 break; 327 328 case STATE_1_SIZE: 329 if (*p >= '0' && *p <= '9') 330 nSize = 10 * nSize + (*p - '0'); 331 else if (ascii_isWhitespace(*p)) 332 eState = STATE_LWS_NAME; 333 else 334 eState = STATE_ERROR; 335 break; 336 337 case STATE_2_SIZE: 338 if (*p >= '0' && *p <= '9') 339 nSize = 10 * nSize + (*p - '0'); 340 else if (ascii_isWhitespace(*p)) 341 eState = STATE_2_SIZE_LWS; 342 else 343 eState = STATE_ERROR; 344 break; 345 346 case STATE_2_SIZE_LWS: 347 if (*p == 'd' || *p == 'D') 348 eState = STATE_2_D; 349 else if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z')) 350 eState = STATE_2_ATTRIB; 351 else if (*p >= '0' && *p <= '9') 352 { 353 nMonth = *p - '0'; 354 nDigits = 1; 355 eState = STATE_2_MONTH; 356 } 357 else if (!ascii_isWhitespace(*p)) 358 eState = STATE_ERROR; 359 break; 360 361 case STATE_2_ATTRIB: 362 if (ascii_isWhitespace(*p)) 363 eState = STATE_2_SIZE_LWS; 364 else if ((*p < 'a' || *p > 'z') && (*p < 'A' || *p > 'Z')) 365 eState = STATE_ERROR; 366 break; 367 368 case STATE_2_D: 369 if (*p == 'i' || *p == 'I') 370 eState = STATE_2_DI; 371 else if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z')) 372 eState = STATE_2_ATTRIB; 373 else if (ascii_isWhitespace(*p)) 374 eState = STATE_2_SIZE_LWS; 375 else 376 eState = STATE_ERROR; 377 break; 378 379 case STATE_2_DI: 380 if (*p == 'r' || *p == 'R') 381 { 382 bDirectory = true; 383 eState = STATE_2_DIR_LWS; 384 } 385 else 386 { 387 if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z')) 388 eState = STATE_2_ATTRIB; 389 else if (ascii_isWhitespace(*p)) 390 eState = STATE_2_SIZE_LWS; 391 else 392 eState = STATE_ERROR; 393 } 394 break; 395 396 case STATE_2_DIR_LWS: 397 if (*p >= '0' && *p <= '9') 398 { 399 nMonth = *p - '0'; 400 nDigits = 1; 401 eState = STATE_2_MONTH; 402 } 403 else if (!ascii_isWhitespace(*p)) 404 eState = STATE_ERROR; 405 break; 406 407 case STATE_2_MONTH: 408 if (*p >= '0' && *p <= '9') 409 if (nDigits < 2) 410 { 411 nMonth = 10 * nMonth + (*p - '0'); 412 ++nDigits; 413 } 414 else 415 eState = STATE_ERROR; 416 else if (*p == '-' && nMonth && nMonth <= 12) 417 { 418 nDigits = 0; 419 eState = STATE_2_DAY; 420 } 421 else 422 eState = STATE_ERROR; 423 break; 424 425 case STATE_2_DAY: 426 if (*p >= '0' && *p <= '9') 427 if (nDigits < 2) 428 { 429 nDay = 10 * nDay + (*p - '0'); 430 ++nDigits; 431 } 432 else 433 eState = STATE_ERROR; 434 else if (*p == '-' && nDay && nDay <= 31) 435 { 436 nDigits = 0; 437 eState = STATE_2_YEAR; 438 } 439 else 440 eState = STATE_ERROR; 441 break; 442 443 case STATE_2_YEAR: 444 if (*p >= '0' && *p <= '9') 445 { 446 if (nDigits < 4) 447 { 448 nYear = 10 * nYear + (*p - '0'); 449 ++nDigits; 450 } 451 else 452 eState = STATE_ERROR; 453 } 454 else 455 { 456 if (ascii_isWhitespace(*p)) 457 eState = STATE_2_YEAR_LWS; 458 else 459 eState = STATE_ERROR; 460 } 461 break; 462 463 case STATE_2_YEAR_LWS: 464 if (*p >= '0' && *p <= '9') 465 { 466 nHour = *p - '0'; 467 nDigits = 1; 468 eState = STATE_2_HOUR; 469 } 470 else if (!ascii_isWhitespace(*p)) 471 eState = STATE_ERROR; 472 break; 473 474 case STATE_2_HOUR: 475 if (*p >= '0' && *p <= '9') 476 if (nDigits < 2) 477 { 478 nHour = 10 * nHour + (*p - '0'); 479 ++nDigits; 480 } 481 else 482 eState = STATE_ERROR; 483 else if (*p == ':' && nHour < 24) 484 { 485 nDigits = 0; 486 eState = STATE_2_MINUTE; 487 } 488 else 489 eState = STATE_ERROR; 490 break; 491 492 case STATE_2_MINUTE: 493 if (*p >= '0' && *p <= '9') 494 { 495 if (nDigits < 2) 496 { 497 nMinute = 10 * nMinute + (*p - '0'); 498 ++nDigits; 499 } 500 else 501 eState = STATE_ERROR; 502 } 503 else 504 { 505 if (ascii_isWhitespace(*p) && (nMinute < 60)) 506 eState = STATE_LWS_NAME; 507 else 508 eState = STATE_ERROR; 509 } 510 break; 511 512 case STATE_LWS_NAME: 513 if (!ascii_isWhitespace(*p)) 514 { 515 setPath (rEntry.m_aName, p); 516 if (bDirectory) 517 rEntry.m_nMode |= INETCOREFTP_FILEMODE_ISDIR; 518 rEntry.m_nSize = nSize; 519 520 setYear (rEntry.m_aDate, nYear); 521 522 rEntry.m_aDate.SetMonth(nMonth); 523 rEntry.m_aDate.SetDay(nDay); 524 rEntry.m_aDate.SetHour(nHour); 525 rEntry.m_aDate.SetMin(nMinute); 526 527 return sal_True; 528 } 529 break; 530 case STATE_ERROR: 531 break; 532 } 533 } 534 535 return sal_False; 536 } 537 538 /* 539 * parseVMS. 540 * Directory entries may span one or two lines: 541 * 542 * entry: *lws name *1(*lws <NEWLINE>) 1*lws size 1*lws datetime rest 543 * 544 * name: filename "." filetype ";" version 545 * filename: 1*39fchar 546 * filetype: 1*39fchar 547 * version: non0digit *digit 548 * 549 * size: "0" / non0digit *digit 550 * 551 * datetime: date 1*lwsp time 552 * date: day "-" month "-" year 553 * day: (*1"0" non0digit) / ("1"-"2" digit) / ("3" "0"-"1") 554 * month: "JAN" / "FEB" / "MAR" / "APR" / "MAY" / "JUN" / "JUL" / "AUG" 555 * / "SEP" / "OCT" / "NOV" / "DEC" ; all case insensitive 556 * year: 2digit / 4digit 557 * time: hour ":" minute 558 * hour: ((*1"0" / "1") digit) / ("2" "0"-"3") 559 * minute: "0"-"5" digit 560 * 561 * rest: *1(lws *<ANY>) 562 * 563 * lws: <TAB> / <SPACE> 564 * non0digit: "1"-"9" 565 * digit: "0" / non0digit 566 * fchar: "A"-"Z" / "a"-"z" / digit / "-" / "_" / "$" 567 * 568 * For directories, the returned name is the <filename> part; for non- 569 * directory files, the returned name is the <filename "." filetype> part. 570 * An entry is a directory iff its filetype is "DIR" (ignoring case). 571 * 572 * The READ, WRITE, and ISLINK mode bits are not supported. 573 * 574 * The returned size is the <size> part, multiplied by 512, and with the high 575 * order bits truncated to fit into a ULONG. 576 * 577 */ 578 sal_Bool FTPDirectoryParser::parseVMS ( 579 FTPDirentry &rEntry, 580 const sal_Char *pBuffer) 581 { 582 static OUString aFirstLineName; 583 static sal_Bool bFirstLineDir = sal_False; 584 585 for (sal_Bool bFirstLine = sal_True;; bFirstLine = sal_False) 586 { 587 const sal_Char *p = pBuffer; 588 if (bFirstLine) 589 { 590 // Skip <*lws> part: 591 while (*p == '\t' || *p == ' ') 592 ++p; 593 594 // Parse <filename "."> part: 595 const sal_Char *pFileName = p; 596 while ((*p >= 'A' && *p <= 'Z') || 597 (*p >= 'a' && *p <= 'z') || 598 (*p >= '0' && *p <= '9') || 599 *p == '-' || *p == '_' || *p == '$') 600 ++p; 601 602 if (*p != '.' || p == pFileName || p - pFileName > 39) 603 { 604 if (aFirstLineName.getLength()) 605 continue; 606 else 607 return sal_False; 608 } 609 610 // Parse <filetype ";"> part: 611 const sal_Char *pFileType = ++p; 612 while ((*p >= 'A' && *p <= 'Z') || 613 (*p >= 'a' && *p <= 'z') || 614 (*p >= '0' && *p <= '9') || 615 *p == '-' || *p == '_' || *p == '$') 616 ++p; 617 618 if (*p != ';' || p == pFileName || p - pFileName > 39) 619 { 620 if (aFirstLineName.getLength()) 621 continue; 622 else 623 return sal_False; 624 } 625 ++p; 626 627 // Set entry's name and mode (ISDIR flag): 628 if ((p - pFileType == 4) && 629 (pFileType[0] == 'D' || pFileType[0] == 'd') && 630 (pFileType[1] == 'I' || pFileType[1] == 'i') && 631 (pFileType[2] == 'R' || pFileType[2] == 'r') ) 632 { 633 setPath (rEntry.m_aName, pFileName, (pFileType - pFileName)); 634 rEntry.m_nMode = INETCOREFTP_FILEMODE_ISDIR; 635 } 636 else 637 { 638 setPath (rEntry.m_aName, pFileName, (p - pFileName)); 639 rEntry.m_nMode = 0; 640 } 641 642 // Skip <version> part: 643 if (*p < '1' || *p > '9') 644 { 645 if (aFirstLineName.getLength()) 646 continue; 647 else 648 return sal_False; 649 } 650 ++p; 651 while (*p >= '0' && *p <= '9') 652 ++p; 653 654 // Parse <1*lws> or <*lws <NEWLINE>> part: 655 sal_Bool bLWS = false; 656 while (*p == '\t' || *p == ' ') 657 { 658 bLWS = true; 659 ++p; 660 } 661 if (*p) 662 { 663 if (!bLWS) 664 { 665 if (aFirstLineName.getLength()) 666 continue; 667 else 668 return sal_False; 669 } 670 } 671 else 672 { 673 /* 674 * First line of entry spanning two lines, 675 * wait for second line. 676 */ 677 aFirstLineName = rEntry.m_aName; 678 bFirstLineDir = 679 ((rEntry.m_nMode & INETCOREFTP_FILEMODE_ISDIR) != 0); 680 return sal_False; 681 } 682 } 683 else 684 { 685 /* 686 * Second line of entry spanning two lines, 687 * restore entry's name and mode (ISDIR flag). 688 */ 689 rEntry.m_aName = aFirstLineName; 690 rEntry.m_nMode = (bFirstLineDir ? INETCOREFTP_FILEMODE_ISDIR : 0); 691 692 // Skip <1*lws> part: 693 if (*p != '\t' && *p != ' ') 694 return sal_False; 695 ++p; 696 while (*p == '\t' || *p == ' ') 697 ++p; 698 } 699 700 // Parse <size> part and set entry's size: 701 if (*p < '0' || *p > '9') 702 return sal_False; 703 ULONG nSize = *p - '0'; 704 if (*p++ != '0') 705 while (*p >= '0' && *p <= '9') 706 nSize = 10 * rEntry.m_nSize + (*p++ - '0'); 707 rEntry.m_nSize = 512 * nSize; 708 709 // Skip <1*lws> part: 710 if (*p != '\t' && *p != ' ') 711 return sal_False; 712 ++p; 713 while (*p == '\t' || *p == ' ') 714 ++p; 715 716 // Parse <day "-"> part and set entry date's day: 717 sal_uInt16 nDay; 718 if (*p == '0') 719 { 720 ++p; 721 if (*p < '1' || *p > '9') 722 return sal_False; 723 nDay = *p++ - '0'; 724 } 725 else if (*p == '1' || *p == '2') 726 { 727 nDay = *p++ - '0'; 728 if (*p >= '0' && *p <= '9') 729 nDay = 10 * nDay + (*p++ - '0'); 730 } 731 else if (*p == '3') 732 { 733 ++p; 734 nDay = (*p == '0' || *p == '1') ? 30 + (*p++ - '0') : 3; 735 } 736 else if (*p >= '4' && *p <= '9') 737 nDay = *p++ - '0'; 738 else 739 return sal_False; 740 741 rEntry.m_aDate.SetDay(nDay); 742 if (*p++ != '-') 743 return sal_False; 744 745 // Parse <month "-"> part and set entry date's month: 746 sal_Char const * pMonth = p; 747 sal_Int32 const monthLen = 3; 748 for (int i = 0; i < monthLen; ++i) 749 { 750 if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z'))) 751 return sal_False; 752 ++p; 753 } 754 if (rtl_str_compareIgnoreAsciiCase_WithLength( 755 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("JAN")) == 0) 756 rEntry.m_aDate.SetMonth(1); 757 else if (rtl_str_compareIgnoreAsciiCase_WithLength( 758 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("FEB")) == 0) 759 rEntry.m_aDate.SetMonth(2); 760 else if (rtl_str_compareIgnoreAsciiCase_WithLength( 761 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("MAR")) == 0) 762 rEntry.m_aDate.SetMonth(3); 763 else if (rtl_str_compareIgnoreAsciiCase_WithLength( 764 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("APR")) == 0) 765 rEntry.m_aDate.SetMonth(4); 766 else if (rtl_str_compareIgnoreAsciiCase_WithLength( 767 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("MAY")) == 0) 768 rEntry.m_aDate.SetMonth(5); 769 else if (rtl_str_compareIgnoreAsciiCase_WithLength( 770 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("JUN")) == 0) 771 rEntry.m_aDate.SetMonth(6); 772 else if (rtl_str_compareIgnoreAsciiCase_WithLength( 773 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("JUL")) == 0) 774 rEntry.m_aDate.SetMonth(7); 775 else if (rtl_str_compareIgnoreAsciiCase_WithLength( 776 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("AUG")) == 0) 777 rEntry.m_aDate.SetMonth(8); 778 else if (rtl_str_compareIgnoreAsciiCase_WithLength( 779 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("SEP")) == 0) 780 rEntry.m_aDate.SetMonth(9); 781 else if (rtl_str_compareIgnoreAsciiCase_WithLength( 782 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("OCT")) == 0) 783 rEntry.m_aDate.SetMonth(10); 784 else if (rtl_str_compareIgnoreAsciiCase_WithLength( 785 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("NOV")) == 0) 786 rEntry.m_aDate.SetMonth(11); 787 else if (rtl_str_compareIgnoreAsciiCase_WithLength( 788 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("DEC")) == 0) 789 rEntry.m_aDate.SetMonth(12); 790 else 791 return sal_False; 792 if (*p++ != '-') 793 return sal_False; 794 795 // Parse <year> part and set entry date's year: 796 sal_uInt16 nYear = 0; 797 {for (int i = 0; i < 2; ++i) 798 { 799 if (*p < '0' || *p > '9') 800 return sal_False; 801 nYear = 10 * nYear + (*p++ - '0'); 802 }} 803 if (*p >= '0' && *p <= '9') 804 { 805 nYear = 10 * nYear + (*p++ - '0'); 806 if (*p < '0' || *p > '9') 807 return sal_False; 808 nYear = 10 * nYear + (*p++ - '0'); 809 } 810 setYear (rEntry.m_aDate, nYear); 811 812 // Skip <1*lws> part: 813 if (*p != '\t' && *p != ' ') 814 return sal_False; 815 ++p; 816 while (*p == '\t' || *p == ' ') 817 ++p; 818 819 // Parse <hour ":"> part and set entry time's hour: 820 sal_uInt16 nHour; 821 if (*p == '0' || *p == '1') 822 { 823 nHour = *p++ - '0'; 824 if (*p >= '0' && *p <= '9') 825 nHour = 10 * nHour + (*p++ - '0'); 826 } 827 else if (*p == '2') 828 { 829 ++p; 830 nHour = (*p >= '0' && *p <= '3') ? 20 + (*p++ - '0') : 2; 831 } 832 else if (*p >= '3' && *p <= '9') 833 nHour = *p++ - '0'; 834 else 835 return sal_False; 836 837 rEntry.m_aDate.SetHour(nHour); 838 if (*p++ != ':') 839 return sal_False; 840 841 /* 842 * Parse <minute> part and set entry time's minutes, 843 * seconds (0), and 1/100 seconds (0). 844 */ 845 if (*p < '0' || *p > '5') 846 return sal_False; 847 848 sal_uInt16 nMinute = *p++ - '0'; 849 if (*p < '0' || *p > '9') 850 return sal_False; 851 852 nMinute = 10 * nMinute + (*p++ - '0'); 853 rEntry.m_aDate.SetMin(nMinute); 854 rEntry.m_aDate.SetSec(0); 855 rEntry.m_aDate.Set100Sec(0); 856 857 // Skip <rest> part: 858 if (*p && (*p != '\t' && *p != ' ')) 859 return sal_False; 860 861 return sal_True; 862 } 863 } 864 865 /* 866 * parseUNIX 867 */ 868 sal_Bool FTPDirectoryParser::parseUNIX ( 869 FTPDirentry &rEntry, 870 const sal_Char *pBuffer) 871 { 872 const sal_Char *p1, *p2; 873 p1 = p2 = pBuffer; 874 875 if (!((*p1 == '-') || (*p1 == 'd') || (*p1 == 'l'))) 876 return sal_False; 877 878 // 1st column: FileMode. 879 if (*p1 == 'd') 880 rEntry.m_nMode |= INETCOREFTP_FILEMODE_ISDIR; 881 882 if (*p1 == 'l') 883 rEntry.m_nMode |= INETCOREFTP_FILEMODE_ISLINK; 884 885 // Skip to end of column and set rights by the way 886 while (*p1 && !ascii_isWhitespace(*p1)) { 887 if(*p1 == 'r') 888 rEntry.m_nMode |= INETCOREFTP_FILEMODE_READ; 889 else if(*p1 == 'w') 890 rEntry.m_nMode |= INETCOREFTP_FILEMODE_WRITE; 891 p1++; 892 } 893 894 /* 895 * Scan for the sequence of size and date fields: 896 * *LWS 1*DIGIT 1*LWS 3CHAR 1*LWS 1*2DIGIT 1*LWS 897 * (4DIGIT / (1*2DIGIT ":" 2DIGIT)) 1*LWS 898 */ 899 enum Mode 900 { 901 FOUND_NONE, FOUND_SIZE, FOUND_MONTH, FOUND_DAY, FOUND_YEAR_TIME 902 }; 903 904 const sal_Char *pDayStart = 0; 905 const sal_Char *pDayEnd = 0; 906 Mode eMode; 907 for (eMode = FOUND_NONE; *p1 && eMode != FOUND_YEAR_TIME; p1 = p2 + 1) 908 { 909 while (*p1 && ascii_isWhitespace(*p1)) 910 ++p1; 911 p2 = p1; 912 while (*p2 && !ascii_isWhitespace(*p2)) 913 ++p2; 914 915 switch (eMode) 916 { 917 case FOUND_NONE: 918 if (parseUNIX_isSizeField (p1, p2, rEntry.m_nSize)) 919 eMode = FOUND_SIZE; 920 break; 921 922 case FOUND_SIZE: 923 if (parseUNIX_isMonthField (p1, p2, rEntry.m_aDate)) 924 eMode = FOUND_MONTH; 925 else if (!parseUNIX_isSizeField (p1, p2, rEntry.m_nSize)) 926 eMode = FOUND_NONE; 927 break; 928 929 case FOUND_MONTH: 930 if (parseUNIX_isDayField (p1, p2, rEntry.m_aDate)) 931 { 932 pDayStart = p1; 933 pDayEnd = p2; 934 eMode = FOUND_DAY; 935 } 936 else if (parseUNIX_isSizeField (p1, p2, rEntry.m_nSize)) 937 eMode = FOUND_SIZE; 938 else 939 eMode = FOUND_NONE; 940 break; 941 942 case FOUND_DAY: 943 if (parseUNIX_isYearTimeField (p1, p2, rEntry.m_aDate)) 944 eMode = FOUND_YEAR_TIME; 945 else if ( 946 parseUNIX_isSizeField ( 947 pDayStart, pDayEnd, rEntry.m_nSize) && 948 parseUNIX_isMonthField ( 949 p1, p2, rEntry.m_aDate)) 950 eMode = FOUND_MONTH; 951 else if (parseUNIX_isSizeField (p1, p2, rEntry.m_nSize)) 952 eMode = FOUND_SIZE; 953 else 954 eMode = FOUND_NONE; 955 break; 956 case FOUND_YEAR_TIME: 957 break; 958 } 959 } 960 961 if (eMode == FOUND_YEAR_TIME) 962 { 963 // 9th column: FileName (rest of line). 964 while (*p1 && ascii_isWhitespace(*p1)) p1++; 965 setPath (rEntry.m_aName, p1); 966 967 // Done. 968 return sal_True; 969 } 970 return sal_False; 971 } 972 973 /* 974 * parseUNIX_isSizeField. 975 */ 976 sal_Bool FTPDirectoryParser::parseUNIX_isSizeField ( 977 const sal_Char *pStart, 978 const sal_Char *pEnd, 979 sal_uInt32 &rSize) 980 { 981 if (!*pStart || !*pEnd || pStart == pEnd) 982 return sal_False; 983 984 rSize = 0; 985 if (*pStart >= '0' && *pStart <= '9') 986 { 987 for (; pStart < pEnd; ++pStart) 988 if ((*pStart >= '0') && (*pStart <= '9')) 989 rSize = 10 * rSize + (*pStart - '0'); 990 else 991 return sal_False; 992 return sal_True; 993 } 994 else 995 { 996 /* 997 * For a combination of long group name and large file size, 998 * some FTPDs omit LWS between those two columns. 999 */ 1000 int nNonDigits = 0; 1001 int nDigits = 0; 1002 1003 for (; pStart < pEnd; ++pStart) 1004 if ((*pStart >= '1') && (*pStart <= '9')) 1005 { 1006 ++nDigits; 1007 rSize = 10 * rSize + (*pStart - '0'); 1008 } 1009 else if ((*pStart == '0') && nDigits) 1010 { 1011 ++nDigits; 1012 rSize *= 10; 1013 } 1014 else if ((*pStart > ' ') && (sal::static_int_cast<sal_uInt8>(*pStart) <= '\x7F')) 1015 { 1016 nNonDigits += nDigits + 1; 1017 nDigits = 0; 1018 rSize = 0; 1019 } 1020 else 1021 return sal_False; 1022 return ((nNonDigits >= 9) && (nDigits >= 7)); 1023 } 1024 } 1025 1026 /* 1027 * parseUNIX_isMonthField. 1028 */ 1029 sal_Bool FTPDirectoryParser::parseUNIX_isMonthField ( 1030 const sal_Char *pStart, 1031 const sal_Char *pEnd, 1032 DateTime &rDateTime) 1033 { 1034 if (!*pStart || !*pEnd || pStart + 3 != pEnd) 1035 return sal_False; 1036 1037 if ((pStart[0] == 'j' || pStart[0] == 'J') && 1038 (pStart[1] == 'a' || pStart[1] == 'A') && 1039 (pStart[2] == 'n' || pStart[2] == 'N') ) 1040 { 1041 rDateTime.SetMonth(1); 1042 return sal_True; 1043 } 1044 if ((pStart[0] == 'f' || pStart[0] == 'F') && 1045 (pStart[1] == 'e' || pStart[1] == 'E') && 1046 (pStart[2] == 'b' || pStart[2] == 'B') ) 1047 { 1048 rDateTime.SetMonth(2); 1049 return sal_True; 1050 } 1051 if ((pStart[0] == 'm' || pStart[0] == 'M') && 1052 (pStart[1] == 'a' || pStart[1] == 'A') && 1053 (pStart[2] == 'r' || pStart[2] == 'R') ) 1054 { 1055 rDateTime.SetMonth(3); 1056 return sal_True; 1057 } 1058 if ((pStart[0] == 'a' || pStart[0] == 'A') && 1059 (pStart[1] == 'p' || pStart[1] == 'P') && 1060 (pStart[2] == 'r' || pStart[2] == 'R') ) 1061 { 1062 rDateTime.SetMonth(4); 1063 return sal_True; 1064 } 1065 if ((pStart[0] == 'm' || pStart[0] == 'M') && 1066 (pStart[1] == 'a' || pStart[1] == 'A') && 1067 (pStart[2] == 'y' || pStart[2] == 'Y') ) 1068 { 1069 rDateTime.SetMonth(5); 1070 return sal_True; 1071 } 1072 if ((pStart[0] == 'j' || pStart[0] == 'J') && 1073 (pStart[1] == 'u' || pStart[1] == 'U') && 1074 (pStart[2] == 'n' || pStart[2] == 'N') ) 1075 { 1076 rDateTime.SetMonth(6); 1077 return sal_True; 1078 } 1079 if ((pStart[0] == 'j' || pStart[0] == 'J') && 1080 (pStart[1] == 'u' || pStart[1] == 'U') && 1081 (pStart[2] == 'l' || pStart[2] == 'L') ) 1082 { 1083 rDateTime.SetMonth(7); 1084 return sal_True; 1085 } 1086 if ((pStart[0] == 'a' || pStart[0] == 'A') && 1087 (pStart[1] == 'u' || pStart[1] == 'U') && 1088 (pStart[2] == 'g' || pStart[2] == 'G') ) 1089 { 1090 rDateTime.SetMonth(8); 1091 return sal_True; 1092 } 1093 if ((pStart[0] == 's' || pStart[0] == 'S') && 1094 (pStart[1] == 'e' || pStart[1] == 'E') && 1095 (pStart[2] == 'p' || pStart[2] == 'P') ) 1096 { 1097 rDateTime.SetMonth(9); 1098 return sal_True; 1099 } 1100 if ((pStart[0] == 'o' || pStart[0] == 'O') && 1101 (pStart[1] == 'c' || pStart[1] == 'C') && 1102 (pStart[2] == 't' || pStart[2] == 'T') ) 1103 { 1104 rDateTime.SetMonth(10); 1105 return sal_True; 1106 } 1107 if ((pStart[0] == 'n' || pStart[0] == 'N') && 1108 (pStart[1] == 'o' || pStart[1] == 'O') && 1109 (pStart[2] == 'v' || pStart[2] == 'V') ) 1110 { 1111 rDateTime.SetMonth(11); 1112 return sal_True; 1113 } 1114 if ((pStart[0] == 'd' || pStart[0] == 'D') && 1115 (pStart[1] == 'e' || pStart[1] == 'E') && 1116 (pStart[2] == 'c' || pStart[2] == 'C') ) 1117 { 1118 rDateTime.SetMonth(12); 1119 return sal_True; 1120 } 1121 return sal_False; 1122 } 1123 1124 /* 1125 * parseUNIX_isDayField. 1126 */ 1127 sal_Bool FTPDirectoryParser::parseUNIX_isDayField ( 1128 const sal_Char *pStart, 1129 const sal_Char *pEnd, 1130 DateTime &rDateTime) 1131 { 1132 if (!*pStart || !*pEnd || pStart == pEnd) 1133 return sal_False; 1134 if (*pStart < '0' || *pStart > '9') 1135 return sal_False; 1136 1137 sal_uInt16 nDay = *pStart - '0'; 1138 if (pStart + 1 < pEnd) 1139 { 1140 if (pStart + 2 != pEnd || pStart[1] < '0' || pStart[1] > '9') 1141 return sal_False; 1142 nDay = 10 * nDay + (pStart[1] - '0'); 1143 } 1144 if (!nDay || nDay > 31) 1145 return sal_False; 1146 1147 rDateTime.SetDay(nDay); 1148 return sal_True; 1149 } 1150 1151 /* 1152 * parseUNIX_isYearTimeField. 1153 */ 1154 sal_Bool FTPDirectoryParser::parseUNIX_isYearTimeField ( 1155 const sal_Char *pStart, 1156 const sal_Char *pEnd, 1157 DateTime &rDateTime) 1158 { 1159 if (!*pStart || !*pEnd || pStart == pEnd || 1160 *pStart < '0' || *pStart > '9') 1161 return sal_False; 1162 1163 sal_uInt16 nNumber = *pStart - '0'; 1164 ++pStart; 1165 1166 if (pStart == pEnd) 1167 return sal_False; 1168 if (*pStart == ':') 1169 return parseUNIX_isTime (pStart, pEnd, nNumber, rDateTime); 1170 if (*pStart < '0' || *pStart > '9') 1171 return sal_False; 1172 1173 nNumber = 10 * nNumber + (*pStart - '0'); 1174 ++pStart; 1175 1176 if (pStart == pEnd) 1177 return sal_False; 1178 if (*pStart == ':') 1179 return parseUNIX_isTime (pStart, pEnd, nNumber, rDateTime); 1180 if (*pStart < '0' || *pStart > '9') 1181 return sal_False; 1182 1183 nNumber = 10 * nNumber + (*pStart - '0'); 1184 ++pStart; 1185 1186 if (pStart == pEnd || *pStart < '0' || *pStart > '9') 1187 return sal_False; 1188 1189 nNumber = 10 * nNumber + (*pStart - '0'); 1190 if (pStart + 1 != pEnd || nNumber < 1970) 1191 return sal_False; 1192 1193 rDateTime.SetYear(nNumber); 1194 rDateTime.SetTime(0); 1195 return sal_True; 1196 } 1197 1198 /* 1199 * parseUNIX_isTime. 1200 */ 1201 sal_Bool FTPDirectoryParser::parseUNIX_isTime ( 1202 const sal_Char *pStart, 1203 const sal_Char *pEnd, 1204 sal_uInt16 nHour, 1205 DateTime &rDateTime) 1206 { 1207 if ((nHour > 23 ) || (pStart + 3 != pEnd) || 1208 (pStart[1] < '0') || (pStart[1] > '5') || 1209 (pStart[2] < '0') || (pStart[2] > '9') ) 1210 return sal_False; 1211 1212 sal_uInt16 nMin = 10 * (pStart[1] - '0') + (pStart[2] - '0'); 1213 1214 rDateTime.SetHour (nHour); 1215 rDateTime.SetMin (nMin); 1216 rDateTime.SetSec (0); 1217 rDateTime.Set100Sec (0); 1218 1219 // Date aCurDate; 1220 // if (rDateTime.GetMonth() > aCurDate.GetMonth()) 1221 // rDateTime.SetYear(aCurDate.GetYear() - 1); 1222 // else 1223 // rDateTime.SetYear(aCurDate.GetYear()); 1224 // return sal_True; 1225 1226 TimeValue aTimeVal; 1227 osl_getSystemTime(&aTimeVal); 1228 oslDateTime aCurrDateTime; 1229 osl_getDateTimeFromTimeValue(&aTimeVal,&aCurrDateTime); 1230 1231 if (rDateTime.GetMonth() > aCurrDateTime.Month) 1232 rDateTime.SetYear(aCurrDateTime.Year - 1); 1233 else 1234 rDateTime.SetYear(aCurrDateTime.Year); 1235 return sal_True; 1236 } 1237 1238 /* 1239 * setYear. 1240 * 1241 * Two-digit years are taken as within 50 years back and 49 years forward 1242 * (both ends inclusive) from the current year. The returned date is not 1243 * checked for validity of the given day in the given month and year. 1244 * 1245 */ 1246 sal_Bool FTPDirectoryParser::setYear ( 1247 DateTime &rDateTime, sal_uInt16 nYear) 1248 { 1249 if (nYear < 100) 1250 { 1251 TimeValue aTimeVal; 1252 osl_getSystemTime(&aTimeVal); 1253 oslDateTime aCurrDateTime; 1254 osl_getDateTimeFromTimeValue(&aTimeVal,&aCurrDateTime); 1255 sal_uInt16 nCurrentYear = aCurrDateTime.Year; 1256 // sal_uInt16 nCurrentYear = Date().GetYear(); 1257 sal_uInt16 nCurrentCentury = nCurrentYear / 100; 1258 nCurrentYear %= 100; 1259 if (nCurrentYear < 50) 1260 if (nYear <= nCurrentYear) 1261 nYear += nCurrentCentury * 100; 1262 else if (nYear < nCurrentYear + 50) 1263 nYear += nCurrentCentury * 100; 1264 else 1265 nYear += (nCurrentCentury - 1) * 100; 1266 else 1267 if (nYear >= nCurrentYear) 1268 nYear += nCurrentCentury * 100; 1269 else if (nYear >= nCurrentYear - 50) 1270 nYear += nCurrentCentury * 100; 1271 else 1272 nYear += (nCurrentCentury + 1) * 100; 1273 } 1274 1275 rDateTime.SetYear(nYear); 1276 return sal_True; 1277 } 1278 1279 /* 1280 * setPath. 1281 */ 1282 sal_Bool FTPDirectoryParser::setPath ( 1283 OUString &rPath, const sal_Char *value, sal_Int32 length) 1284 { 1285 if (value) 1286 { 1287 if (length < 0) 1288 length = rtl_str_getLength (value); 1289 rPath = OUString (value, length, RTL_TEXTENCODING_UTF8); 1290 } 1291 return (!!value); 1292 } 1293