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