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_ftp.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
ascii_isLetter(sal_Unicode ch)43 inline sal_Bool ascii_isLetter( sal_Unicode ch )
44 {
45 return (( (ch >= 0x0041) && (ch <= 0x005A)) ||
46 (( ch >= 0x0061) && (ch <= 0x007A)));
47 }
48
ascii_isWhitespace(sal_Unicode ch)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
parseDOS(FTPDirentry & rEntry,const sal_Char * pBuffer)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 */
parseVMS(FTPDirentry & rEntry,const sal_Char * pBuffer)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 */
parseUNIX(FTPDirentry & rEntry,const sal_Char * pBuffer)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 */
parseUNIX_isSizeField(const sal_Char * pStart,const sal_Char * pEnd,sal_uInt32 & rSize)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 */
parseUNIX_isMonthField(const sal_Char * pStart,const sal_Char * pEnd,DateTime & rDateTime)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 */
parseUNIX_isDayField(const sal_Char * pStart,const sal_Char * pEnd,DateTime & rDateTime)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 */
parseUNIX_isYearTimeField(const sal_Char * pStart,const sal_Char * pEnd,DateTime & rDateTime)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 */
parseUNIX_isTime(const sal_Char * pStart,const sal_Char * pEnd,sal_uInt16 nHour,DateTime & rDateTime)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 */
setYear(DateTime & rDateTime,sal_uInt16 nYear)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 */
setPath(OUString & rPath,const sal_Char * value,sal_Int32 length)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