update credits
[LibreOffice.git] / ucb / source / ucp / ftp / ftpdirp.cxx
blob87f38b88a56931abfcd350ffeeec988faf05330a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 /**************************************************************************
22 TODO
23 **************************************************************************
25 *************************************************************************/
26 #include "ftpdirp.hxx"
27 #include <osl/time.h>
30 using namespace ftp;
32 typedef sal_uInt32 ULONG;
35 inline sal_Bool ascii_isLetter( sal_Unicode ch )
37 return (( (ch >= 0x0041) && (ch <= 0x005A)) ||
38 (( ch >= 0x0061) && (ch <= 0x007A)));
41 inline sal_Bool ascii_isWhitespace( sal_Unicode ch )
43 return ((ch <= 0x20) && ch);
48 /*========================================================================
50 * FTPDirectoryParser implementation.
52 *======================================================================*/
54 * parseDOS.
55 * Accepts one of two styles:
57 * 1 *WSP 1*2DIGIT ("." / "-") 1*2DIGIT ("." / "-") 1*4DIGIT 1*WSP
58 * 1*2DIGIT ":" 1*2DIGIT [*WSP ("A" / "P") "M"] 1*WSP
59 * ((DIGIT *(DIGIT / "." / ",")) / "<DIR>") 1*WSP 1*OCTET
61 * interpreted as: mm.dd.yy hh:mm (size / <DIR>) name
63 * 2 *WSP 1*DIGIT 1*WSP *(1*CHAR *WSP) *1("DIR" 1*WSP) 1*2DIGIT "-" 1*2DIGIT
64 * "-" 1*4DIGIT 1*WSP 1*2DIGIT ":" 1*2DIGIT 1*WSP 1*OCTET
66 * interpreted as: size attribs DIR mm-dd-yy hh:mm name
69 sal_Bool FTPDirectoryParser::parseDOS (
70 FTPDirentry &rEntry,
71 const sal_Char *pBuffer)
73 sal_Bool bDirectory = false;
74 sal_uInt32 nSize = 0;
75 sal_uInt16 nYear = 0;
76 sal_uInt16 nMonth = 0;
77 sal_uInt16 nDay = 0;
78 sal_uInt16 nHour = 0;
79 sal_uInt16 nMinute = 0;
81 enum StateType
83 STATE_INIT_LWS,
84 STATE_MONTH_OR_SIZE,
85 STATE_1_DAY, STATE_1_YEAR, STATE_1_YEAR_LWS, STATE_1_HOUR,
86 STATE_1_MINUTE, STATE_1_MINUTE_LWS, STATE_1_AP,
87 STATE_1_APM, STATE_1_LESS, STATE_1_D, STATE_1_DI,
88 STATE_1_DIR, STATE_1_SIZE,
89 STATE_2_SIZE, STATE_2_SIZE_LWS, STATE_2_ATTRIB,
90 STATE_2_D, STATE_2_DI, STATE_2_DIR_LWS,
91 STATE_2_MONTH, STATE_2_DAY, STATE_2_YEAR, STATE_2_YEAR_LWS,
92 STATE_2_HOUR, STATE_2_MINUTE,
93 STATE_LWS_NAME,
94 STATE_ERROR
97 int nDigits = 0;
98 enum StateType eState = STATE_INIT_LWS;
99 for (const sal_Char *p = pBuffer;
100 eState != STATE_ERROR && *p;
101 ++p)
103 switch (eState)
105 case STATE_INIT_LWS:
106 if (*p >= '0' && *p <= '9')
108 nMonth = *p - '0';
109 nDigits = 1;
110 eState = STATE_MONTH_OR_SIZE;
112 else if (!ascii_isWhitespace(*p))
113 eState = STATE_ERROR;
114 break;
116 case STATE_MONTH_OR_SIZE:
117 if (*p >= '0' && *p <= '9')
119 nMonth = 10 * nMonth + (*p - '0');
120 if (nDigits < 2)
121 ++nDigits;
122 else
124 nSize = nMonth;
125 nMonth = 0;
126 eState = STATE_2_SIZE;
129 else if (ascii_isWhitespace(*p))
131 nSize = nMonth;
132 nMonth = 0;
133 eState = STATE_2_SIZE_LWS;
135 else if ((*p == '.' || *p == '-') && nMonth && nMonth <= 12)
137 nDigits = 0;
138 eState = STATE_1_DAY;
140 else
141 eState = STATE_ERROR;
142 break;
144 case STATE_1_DAY:
145 if (*p >= '0' && *p <= '9')
146 if (nDigits < 2)
148 nDay = 10 * nDay + (*p - '0');
149 ++nDigits;
151 else
152 eState = STATE_ERROR;
153 else if ((*p == '.' || *p == '-') && nDay && nDay <= 31)
155 nDigits = 0;
156 eState = STATE_1_YEAR;
158 else
159 eState = STATE_ERROR;
160 break;
162 case STATE_1_YEAR:
163 if (*p >= '0' && *p <= '9')
165 if (nDigits < 4)
167 nYear = 10 * nYear + (*p - '0');
168 ++nDigits;
170 else
171 eState = STATE_ERROR;
173 else
175 if (ascii_isWhitespace(*p))
176 eState = STATE_1_YEAR_LWS;
177 else
178 eState = STATE_ERROR;
180 break;
182 case STATE_1_YEAR_LWS:
183 if (*p >= '0' && *p <= '9')
185 nHour = *p - '0';
186 nDigits = 1;
187 eState = STATE_1_HOUR;
189 else if (!ascii_isWhitespace(*p))
190 eState = STATE_ERROR;
191 break;
193 case STATE_1_HOUR:
194 if (*p >= '0' && *p <= '9')
195 if (nDigits < 2)
197 nHour = 10 * nHour + (*p - '0');
198 ++nDigits;
200 else
201 eState = STATE_ERROR;
202 else if (*p == ':' && nHour < 24)
204 nDigits = 0;
205 eState = STATE_1_MINUTE;
207 else
208 eState = STATE_ERROR;
209 break;
211 case STATE_1_MINUTE:
212 if (*p >= '0' && *p <= '9')
213 if (nDigits < 2)
215 nMinute = 10 * nMinute + (*p - '0');
216 ++nDigits;
218 else
219 eState = STATE_ERROR;
220 else if ((*p == 'a' || *p == 'A') && nMinute < 60)
221 if (nHour >= 1 && nHour <= 11)
222 eState = STATE_1_AP;
223 else if (nHour == 12)
225 nHour = 0;
226 eState = STATE_1_AP;
228 else
229 eState = STATE_ERROR;
230 else if ((*p == 'p' || *p == 'P') && nMinute < 60)
231 if (nHour >= 1 && nHour <= 11)
233 nHour += 12;
234 eState = STATE_1_AP;
236 else if (nHour == 12)
237 eState = STATE_1_AP;
238 else
239 eState = STATE_ERROR;
240 else if (ascii_isWhitespace(*p) && (nMinute < 60))
241 eState = STATE_1_MINUTE_LWS;
242 else
243 eState = STATE_ERROR;
244 break;
246 case STATE_1_MINUTE_LWS:
247 if (*p == 'a' || *p == 'A')
248 if (nHour >= 1 && nHour <= 11)
249 eState = STATE_1_AP;
250 else if (nHour == 12)
252 nHour = 0;
253 eState = STATE_1_AP;
255 else
256 eState = STATE_ERROR;
257 else if (*p == 'p' || *p == 'P')
258 if (nHour >= 1 && nHour <= 11)
260 nHour += 12;
261 eState = STATE_1_AP;
263 else if (nHour == 12)
264 eState = STATE_1_AP;
265 else
266 eState = STATE_ERROR;
267 else if (*p == '<')
268 eState = STATE_1_LESS;
269 else if (*p >= '0' && *p <= '9')
271 nSize = *p - '0';
272 eState = STATE_1_SIZE;
274 else if (!ascii_isWhitespace(*p))
275 eState = STATE_ERROR;
276 break;
278 case STATE_1_AP:
279 eState = *p == 'm' || *p == 'M' ? STATE_1_APM : STATE_ERROR;
280 break;
282 case STATE_1_APM:
283 if (*p == '<')
284 eState = STATE_1_LESS;
285 else if (*p >= '0' && *p <= '9')
287 nSize = *p - '0';
288 eState = STATE_1_SIZE;
290 else if (!ascii_isWhitespace(*p))
291 eState = STATE_ERROR;
292 break;
294 case STATE_1_LESS:
295 eState = *p == 'd' || *p == 'D' ? STATE_1_D : STATE_ERROR;
296 break;
298 case STATE_1_D:
299 eState = *p == 'i' || *p == 'I' ? STATE_1_DI : STATE_ERROR;
300 break;
302 case STATE_1_DI:
303 eState = *p == 'r' || *p == 'R' ? STATE_1_DIR : STATE_ERROR;
304 break;
306 case STATE_1_DIR:
307 if (*p == '>')
309 bDirectory = true;
310 eState = STATE_LWS_NAME;
312 else
313 eState = STATE_ERROR;
314 break;
316 case STATE_1_SIZE:
317 if (*p >= '0' && *p <= '9')
318 nSize = 10 * nSize + (*p - '0');
319 else if (ascii_isWhitespace(*p))
320 eState = STATE_LWS_NAME;
321 else
322 eState = STATE_ERROR;
323 break;
325 case STATE_2_SIZE:
326 if (*p >= '0' && *p <= '9')
327 nSize = 10 * nSize + (*p - '0');
328 else if (ascii_isWhitespace(*p))
329 eState = STATE_2_SIZE_LWS;
330 else
331 eState = STATE_ERROR;
332 break;
334 case STATE_2_SIZE_LWS:
335 if (*p == 'd' || *p == 'D')
336 eState = STATE_2_D;
337 else if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))
338 eState = STATE_2_ATTRIB;
339 else if (*p >= '0' && *p <= '9')
341 nMonth = *p - '0';
342 nDigits = 1;
343 eState = STATE_2_MONTH;
345 else if (!ascii_isWhitespace(*p))
346 eState = STATE_ERROR;
347 break;
349 case STATE_2_ATTRIB:
350 if (ascii_isWhitespace(*p))
351 eState = STATE_2_SIZE_LWS;
352 else if ((*p < 'a' || *p > 'z') && (*p < 'A' || *p > 'Z'))
353 eState = STATE_ERROR;
354 break;
356 case STATE_2_D:
357 if (*p == 'i' || *p == 'I')
358 eState = STATE_2_DI;
359 else if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))
360 eState = STATE_2_ATTRIB;
361 else if (ascii_isWhitespace(*p))
362 eState = STATE_2_SIZE_LWS;
363 else
364 eState = STATE_ERROR;
365 break;
367 case STATE_2_DI:
368 if (*p == 'r' || *p == 'R')
370 bDirectory = true;
371 eState = STATE_2_DIR_LWS;
373 else
375 if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))
376 eState = STATE_2_ATTRIB;
377 else if (ascii_isWhitespace(*p))
378 eState = STATE_2_SIZE_LWS;
379 else
380 eState = STATE_ERROR;
382 break;
384 case STATE_2_DIR_LWS:
385 if (*p >= '0' && *p <= '9')
387 nMonth = *p - '0';
388 nDigits = 1;
389 eState = STATE_2_MONTH;
391 else if (!ascii_isWhitespace(*p))
392 eState = STATE_ERROR;
393 break;
395 case STATE_2_MONTH:
396 if (*p >= '0' && *p <= '9')
397 if (nDigits < 2)
399 nMonth = 10 * nMonth + (*p - '0');
400 ++nDigits;
402 else
403 eState = STATE_ERROR;
404 else if (*p == '-' && nMonth && nMonth <= 12)
406 nDigits = 0;
407 eState = STATE_2_DAY;
409 else
410 eState = STATE_ERROR;
411 break;
413 case STATE_2_DAY:
414 if (*p >= '0' && *p <= '9')
415 if (nDigits < 2)
417 nDay = 10 * nDay + (*p - '0');
418 ++nDigits;
420 else
421 eState = STATE_ERROR;
422 else if (*p == '-' && nDay && nDay <= 31)
424 nDigits = 0;
425 eState = STATE_2_YEAR;
427 else
428 eState = STATE_ERROR;
429 break;
431 case STATE_2_YEAR:
432 if (*p >= '0' && *p <= '9')
434 if (nDigits < 4)
436 nYear = 10 * nYear + (*p - '0');
437 ++nDigits;
439 else
440 eState = STATE_ERROR;
442 else
444 if (ascii_isWhitespace(*p))
445 eState = STATE_2_YEAR_LWS;
446 else
447 eState = STATE_ERROR;
449 break;
451 case STATE_2_YEAR_LWS:
452 if (*p >= '0' && *p <= '9')
454 nHour = *p - '0';
455 nDigits = 1;
456 eState = STATE_2_HOUR;
458 else if (!ascii_isWhitespace(*p))
459 eState = STATE_ERROR;
460 break;
462 case STATE_2_HOUR:
463 if (*p >= '0' && *p <= '9')
464 if (nDigits < 2)
466 nHour = 10 * nHour + (*p - '0');
467 ++nDigits;
469 else
470 eState = STATE_ERROR;
471 else if (*p == ':' && nHour < 24)
473 nDigits = 0;
474 eState = STATE_2_MINUTE;
476 else
477 eState = STATE_ERROR;
478 break;
480 case STATE_2_MINUTE:
481 if (*p >= '0' && *p <= '9')
483 if (nDigits < 2)
485 nMinute = 10 * nMinute + (*p - '0');
486 ++nDigits;
488 else
489 eState = STATE_ERROR;
491 else
493 if (ascii_isWhitespace(*p) && (nMinute < 60))
494 eState = STATE_LWS_NAME;
495 else
496 eState = STATE_ERROR;
498 break;
500 case STATE_LWS_NAME:
501 if (!ascii_isWhitespace(*p))
503 setPath (rEntry.m_aName, p);
504 if (bDirectory)
505 rEntry.m_nMode |= INETCOREFTP_FILEMODE_ISDIR;
506 rEntry.m_nSize = nSize;
508 setYear (rEntry.m_aDate, nYear);
510 rEntry.m_aDate.SetMonth(nMonth);
511 rEntry.m_aDate.SetDay(nDay);
512 rEntry.m_aDate.SetHour(nHour);
513 rEntry.m_aDate.SetMin(nMinute);
515 return sal_True;
517 break;
518 case STATE_ERROR:
519 break;
523 return sal_False;
527 * parseVMS.
528 * Directory entries may span one or two lines:
530 * entry: *lws name *1(*lws <NEWLINE>) 1*lws size 1*lws datetime rest
532 * name: filename "." filetype ";" version
533 * filename: 1*39fchar
534 * filetype: 1*39fchar
535 * version: non0digit *digit
537 * size: "0" / non0digit *digit
539 * datetime: date 1*lwsp time
540 * date: day "-" month "-" year
541 * day: (*1"0" non0digit) / ("1"-"2" digit) / ("3" "0"-"1")
542 * month: "JAN" / "FEB" / "MAR" / "APR" / "MAY" / "JUN" / "JUL" / "AUG"
543 * / "SEP" / "OCT" / "NOV" / "DEC" ; all case insensitive
544 * year: 2digit / 4digit
545 * time: hour ":" minute
546 * hour: ((*1"0" / "1") digit) / ("2" "0"-"3")
547 * minute: "0"-"5" digit
549 * rest: *1(lws *<ANY>)
551 * lws: <TAB> / <SPACE>
552 * non0digit: "1"-"9"
553 * digit: "0" / non0digit
554 * fchar: "A"-"Z" / "a"-"z" / digit / "-" / "_" / "$"
556 * For directories, the returned name is the <filename> part; for non-
557 * directory files, the returned name is the <filename "." filetype> part.
558 * An entry is a directory iff its filetype is "DIR" (ignoring case).
560 * The READ, WRITE, and ISLINK mode bits are not supported.
562 * The returned size is the <size> part, multiplied by 512, and with the high
563 * order bits truncated to fit into a ULONG.
566 sal_Bool FTPDirectoryParser::parseVMS (
567 FTPDirentry &rEntry,
568 const sal_Char *pBuffer)
570 static OUString aFirstLineName;
571 static sal_Bool bFirstLineDir = sal_False;
573 for (sal_Bool bFirstLine = sal_True;; bFirstLine = sal_False)
575 const sal_Char *p = pBuffer;
576 if (bFirstLine)
578 // Skip <*lws> part:
579 while (*p == '\t' || *p == ' ')
580 ++p;
582 // Parse <filename "."> part:
583 const sal_Char *pFileName = p;
584 while ((*p >= 'A' && *p <= 'Z') ||
585 (*p >= 'a' && *p <= 'z') ||
586 (*p >= '0' && *p <= '9') ||
587 *p == '-' || *p == '_' || *p == '$')
588 ++p;
590 if (*p != '.' || p == pFileName || p - pFileName > 39)
592 if (!aFirstLineName.isEmpty())
593 continue;
594 else
595 return sal_False;
598 // Parse <filetype ";"> part:
599 const sal_Char *pFileType = ++p;
600 while ((*p >= 'A' && *p <= 'Z') ||
601 (*p >= 'a' && *p <= 'z') ||
602 (*p >= '0' && *p <= '9') ||
603 *p == '-' || *p == '_' || *p == '$')
604 ++p;
606 if (*p != ';' || p == pFileName || p - pFileName > 39)
608 if (!aFirstLineName.isEmpty())
609 continue;
610 else
611 return sal_False;
613 ++p;
615 // Set entry's name and mode (ISDIR flag):
616 if ((p - pFileType == 4) &&
617 (pFileType[0] == 'D' || pFileType[0] == 'd') &&
618 (pFileType[1] == 'I' || pFileType[1] == 'i') &&
619 (pFileType[2] == 'R' || pFileType[2] == 'r') )
621 setPath (rEntry.m_aName, pFileName, (pFileType - pFileName));
622 rEntry.m_nMode = INETCOREFTP_FILEMODE_ISDIR;
624 else
626 setPath (rEntry.m_aName, pFileName, (p - pFileName));
627 rEntry.m_nMode = 0;
630 // Skip <version> part:
631 if (*p < '1' || *p > '9')
633 if (!aFirstLineName.isEmpty())
634 continue;
635 else
636 return sal_False;
638 ++p;
639 while (*p >= '0' && *p <= '9')
640 ++p;
642 // Parse <1*lws> or <*lws <NEWLINE>> part:
643 sal_Bool bLWS = false;
644 while (*p == '\t' || *p == ' ')
646 bLWS = true;
647 ++p;
649 if (*p)
651 if (!bLWS)
653 if (!aFirstLineName.isEmpty())
654 continue;
655 else
656 return sal_False;
659 else
662 * First line of entry spanning two lines,
663 * wait for second line.
665 aFirstLineName = rEntry.m_aName;
666 bFirstLineDir =
667 ((rEntry.m_nMode & INETCOREFTP_FILEMODE_ISDIR) != 0);
668 return sal_False;
671 else
674 * Second line of entry spanning two lines,
675 * restore entry's name and mode (ISDIR flag).
677 rEntry.m_aName = aFirstLineName;
678 rEntry.m_nMode = (bFirstLineDir ? INETCOREFTP_FILEMODE_ISDIR : 0);
680 // Skip <1*lws> part:
681 if (*p != '\t' && *p != ' ')
682 return sal_False;
683 ++p;
684 while (*p == '\t' || *p == ' ')
685 ++p;
688 // Parse <size> part and set entry's size:
689 if (*p < '0' || *p > '9')
690 return sal_False;
691 ULONG nSize = *p - '0';
692 if (*p++ != '0')
693 while (*p >= '0' && *p <= '9')
694 nSize = 10 * rEntry.m_nSize + (*p++ - '0');
695 rEntry.m_nSize = 512 * nSize;
697 // Skip <1*lws> part:
698 if (*p != '\t' && *p != ' ')
699 return sal_False;
700 ++p;
701 while (*p == '\t' || *p == ' ')
702 ++p;
704 // Parse <day "-"> part and set entry date's day:
705 sal_uInt16 nDay;
706 if (*p == '0')
708 ++p;
709 if (*p < '1' || *p > '9')
710 return sal_False;
711 nDay = *p++ - '0';
713 else if (*p == '1' || *p == '2')
715 nDay = *p++ - '0';
716 if (*p >= '0' && *p <= '9')
717 nDay = 10 * nDay + (*p++ - '0');
719 else if (*p == '3')
721 ++p;
722 nDay = (*p == '0' || *p == '1') ? 30 + (*p++ - '0') : 3;
724 else if (*p >= '4' && *p <= '9')
725 nDay = *p++ - '0';
726 else
727 return sal_False;
729 rEntry.m_aDate.SetDay(nDay);
730 if (*p++ != '-')
731 return sal_False;
733 // Parse <month "-"> part and set entry date's month:
734 sal_Char const * pMonth = p;
735 sal_Int32 const monthLen = 3;
736 for (int i = 0; i < monthLen; ++i)
738 if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z')))
739 return sal_False;
740 ++p;
742 if (rtl_str_compareIgnoreAsciiCase_WithLength(
743 pMonth, monthLen, "JAN", monthLen) == 0)
744 rEntry.m_aDate.SetMonth(1);
745 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
746 pMonth, monthLen, "FEB", monthLen) == 0)
747 rEntry.m_aDate.SetMonth(2);
748 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
749 pMonth, monthLen, "MAR", monthLen) == 0)
750 rEntry.m_aDate.SetMonth(3);
751 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
752 pMonth, monthLen, "APR", monthLen) == 0)
753 rEntry.m_aDate.SetMonth(4);
754 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
755 pMonth, monthLen, "MAY", monthLen) == 0)
756 rEntry.m_aDate.SetMonth(5);
757 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
758 pMonth, monthLen, "JUN", monthLen) == 0)
759 rEntry.m_aDate.SetMonth(6);
760 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
761 pMonth, monthLen, "JUL", monthLen) == 0)
762 rEntry.m_aDate.SetMonth(7);
763 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
764 pMonth, monthLen, "AUG", monthLen) == 0)
765 rEntry.m_aDate.SetMonth(8);
766 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
767 pMonth, monthLen, "SEP", monthLen) == 0)
768 rEntry.m_aDate.SetMonth(9);
769 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
770 pMonth, monthLen, "OCT", monthLen) == 0)
771 rEntry.m_aDate.SetMonth(10);
772 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
773 pMonth, monthLen, "NOV", monthLen) == 0)
774 rEntry.m_aDate.SetMonth(11);
775 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
776 pMonth, monthLen, "DEC", monthLen) == 0)
777 rEntry.m_aDate.SetMonth(12);
778 else
779 return sal_False;
780 if (*p++ != '-')
781 return sal_False;
783 // Parse <year> part and set entry date's year:
784 sal_uInt16 nYear = 0;
785 for (int i = 0; i < 2; ++i)
787 if (*p < '0' || *p > '9')
788 return sal_False;
789 nYear = 10 * nYear + (*p++ - '0');
791 if (*p >= '0' && *p <= '9')
793 nYear = 10 * nYear + (*p++ - '0');
794 if (*p < '0' || *p > '9')
795 return sal_False;
796 nYear = 10 * nYear + (*p++ - '0');
798 setYear (rEntry.m_aDate, nYear);
800 // Skip <1*lws> part:
801 if (*p != '\t' && *p != ' ')
802 return sal_False;
803 ++p;
804 while (*p == '\t' || *p == ' ')
805 ++p;
807 // Parse <hour ":"> part and set entry time's hour:
808 sal_uInt16 nHour;
809 if (*p == '0' || *p == '1')
811 nHour = *p++ - '0';
812 if (*p >= '0' && *p <= '9')
813 nHour = 10 * nHour + (*p++ - '0');
815 else if (*p == '2')
817 ++p;
818 nHour = (*p >= '0' && *p <= '3') ? 20 + (*p++ - '0') : 2;
820 else if (*p >= '3' && *p <= '9')
821 nHour = *p++ - '0';
822 else
823 return sal_False;
825 rEntry.m_aDate.SetHour(nHour);
826 if (*p++ != ':')
827 return sal_False;
830 * Parse <minute> part and set entry time's minutes,
831 * seconds (0), and nanoseconds (0).
833 if (*p < '0' || *p > '5')
834 return sal_False;
836 sal_uInt16 nMinute = *p++ - '0';
837 if (*p < '0' || *p > '9')
838 return sal_False;
840 nMinute = 10 * nMinute + (*p++ - '0');
841 rEntry.m_aDate.SetMin(nMinute);
842 rEntry.m_aDate.SetSec(0);
843 rEntry.m_aDate.SetNanoSec(0);
845 // Skip <rest> part:
846 if (*p && (*p != '\t' && *p != ' '))
847 return sal_False;
849 return sal_True;
854 * parseUNIX
856 sal_Bool FTPDirectoryParser::parseUNIX (
857 FTPDirentry &rEntry,
858 const sal_Char *pBuffer)
860 const sal_Char *p1, *p2;
861 p1 = p2 = pBuffer;
863 if (!((*p1 == '-') || (*p1 == 'd') || (*p1 == 'l')))
864 return sal_False;
866 // 1st column: FileMode.
867 if (*p1 == 'd')
868 rEntry.m_nMode |= INETCOREFTP_FILEMODE_ISDIR;
870 if (*p1 == 'l')
871 rEntry.m_nMode |= INETCOREFTP_FILEMODE_ISLINK;
873 // Skip to end of column and set rights by the way
874 while (*p1 && !ascii_isWhitespace(*p1)) {
875 if(*p1 == 'r')
876 rEntry.m_nMode |= INETCOREFTP_FILEMODE_READ;
877 else if(*p1 == 'w')
878 rEntry.m_nMode |= INETCOREFTP_FILEMODE_WRITE;
879 p1++;
883 * Scan for the sequence of size and date fields:
884 * *LWS 1*DIGIT 1*LWS 3CHAR 1*LWS 1*2DIGIT 1*LWS
885 * (4DIGIT / (1*2DIGIT ":" 2DIGIT)) 1*LWS
887 enum Mode
889 FOUND_NONE, FOUND_SIZE, FOUND_MONTH, FOUND_DAY, FOUND_YEAR_TIME
892 const sal_Char *pDayStart = 0;
893 const sal_Char *pDayEnd = 0;
894 Mode eMode;
895 for (eMode = FOUND_NONE; *p1 && eMode != FOUND_YEAR_TIME; p1 = p2 + 1)
897 while (*p1 && ascii_isWhitespace(*p1))
898 ++p1;
899 p2 = p1;
900 while (*p2 && !ascii_isWhitespace(*p2))
901 ++p2;
903 switch (eMode)
905 case FOUND_NONE:
906 if (parseUNIX_isSizeField (p1, p2, rEntry.m_nSize))
907 eMode = FOUND_SIZE;
908 break;
910 case FOUND_SIZE:
911 if (parseUNIX_isMonthField (p1, p2, rEntry.m_aDate))
912 eMode = FOUND_MONTH;
913 else if (!parseUNIX_isSizeField (p1, p2, rEntry.m_nSize))
914 eMode = FOUND_NONE;
915 break;
917 case FOUND_MONTH:
918 if (parseUNIX_isDayField (p1, p2, rEntry.m_aDate))
920 pDayStart = p1;
921 pDayEnd = p2;
922 eMode = FOUND_DAY;
924 else if (parseUNIX_isSizeField (p1, p2, rEntry.m_nSize))
925 eMode = FOUND_SIZE;
926 else
927 eMode = FOUND_NONE;
928 break;
930 case FOUND_DAY:
931 if (parseUNIX_isYearTimeField (p1, p2, rEntry.m_aDate))
932 eMode = FOUND_YEAR_TIME;
933 else if (
934 parseUNIX_isSizeField (
935 pDayStart, pDayEnd, rEntry.m_nSize) &&
936 parseUNIX_isMonthField (
937 p1, p2, rEntry.m_aDate))
938 eMode = FOUND_MONTH;
939 else if (parseUNIX_isSizeField (p1, p2, rEntry.m_nSize))
940 eMode = FOUND_SIZE;
941 else
942 eMode = FOUND_NONE;
943 break;
944 case FOUND_YEAR_TIME:
945 break;
949 if (eMode == FOUND_YEAR_TIME)
951 // 9th column: FileName (rest of line).
952 while (*p1 && ascii_isWhitespace(*p1)) p1++;
953 setPath (rEntry.m_aName, p1);
955 // Done.
956 return sal_True;
958 return sal_False;
962 * parseUNIX_isSizeField.
964 sal_Bool FTPDirectoryParser::parseUNIX_isSizeField (
965 const sal_Char *pStart,
966 const sal_Char *pEnd,
967 sal_uInt32 &rSize)
969 if (!*pStart || !*pEnd || pStart == pEnd)
970 return sal_False;
972 rSize = 0;
973 if (*pStart >= '0' && *pStart <= '9')
975 for (; pStart < pEnd; ++pStart)
976 if ((*pStart >= '0') && (*pStart <= '9'))
977 rSize = 10 * rSize + (*pStart - '0');
978 else
979 return sal_False;
980 return sal_True;
982 else
985 * For a combination of long group name and large file size,
986 * some FTPDs omit LWS between those two columns.
988 int nNonDigits = 0;
989 int nDigits = 0;
991 for (; pStart < pEnd; ++pStart)
992 if ((*pStart >= '1') && (*pStart <= '9'))
994 ++nDigits;
995 rSize = 10 * rSize + (*pStart - '0');
997 else if ((*pStart == '0') && nDigits)
999 ++nDigits;
1000 rSize *= 10;
1002 else if ((*pStart > ' ') && (sal::static_int_cast<sal_uInt8>(*pStart) <= '\x7F'))
1004 nNonDigits += nDigits + 1;
1005 nDigits = 0;
1006 rSize = 0;
1008 else
1009 return sal_False;
1010 return ((nNonDigits >= 9) && (nDigits >= 7));
1015 * parseUNIX_isMonthField.
1017 sal_Bool FTPDirectoryParser::parseUNIX_isMonthField (
1018 const sal_Char *pStart,
1019 const sal_Char *pEnd,
1020 DateTime &rDateTime)
1022 if (!*pStart || !*pEnd || pStart + 3 != pEnd)
1023 return sal_False;
1025 if ((pStart[0] == 'j' || pStart[0] == 'J') &&
1026 (pStart[1] == 'a' || pStart[1] == 'A') &&
1027 (pStart[2] == 'n' || pStart[2] == 'N') )
1029 rDateTime.SetMonth(1);
1030 return sal_True;
1032 if ((pStart[0] == 'f' || pStart[0] == 'F') &&
1033 (pStart[1] == 'e' || pStart[1] == 'E') &&
1034 (pStart[2] == 'b' || pStart[2] == 'B') )
1036 rDateTime.SetMonth(2);
1037 return sal_True;
1039 if ((pStart[0] == 'm' || pStart[0] == 'M') &&
1040 (pStart[1] == 'a' || pStart[1] == 'A') &&
1041 (pStart[2] == 'r' || pStart[2] == 'R') )
1043 rDateTime.SetMonth(3);
1044 return sal_True;
1046 if ((pStart[0] == 'a' || pStart[0] == 'A') &&
1047 (pStart[1] == 'p' || pStart[1] == 'P') &&
1048 (pStart[2] == 'r' || pStart[2] == 'R') )
1050 rDateTime.SetMonth(4);
1051 return sal_True;
1053 if ((pStart[0] == 'm' || pStart[0] == 'M') &&
1054 (pStart[1] == 'a' || pStart[1] == 'A') &&
1055 (pStart[2] == 'y' || pStart[2] == 'Y') )
1057 rDateTime.SetMonth(5);
1058 return sal_True;
1060 if ((pStart[0] == 'j' || pStart[0] == 'J') &&
1061 (pStart[1] == 'u' || pStart[1] == 'U') &&
1062 (pStart[2] == 'n' || pStart[2] == 'N') )
1064 rDateTime.SetMonth(6);
1065 return sal_True;
1067 if ((pStart[0] == 'j' || pStart[0] == 'J') &&
1068 (pStart[1] == 'u' || pStart[1] == 'U') &&
1069 (pStart[2] == 'l' || pStart[2] == 'L') )
1071 rDateTime.SetMonth(7);
1072 return sal_True;
1074 if ((pStart[0] == 'a' || pStart[0] == 'A') &&
1075 (pStart[1] == 'u' || pStart[1] == 'U') &&
1076 (pStart[2] == 'g' || pStart[2] == 'G') )
1078 rDateTime.SetMonth(8);
1079 return sal_True;
1081 if ((pStart[0] == 's' || pStart[0] == 'S') &&
1082 (pStart[1] == 'e' || pStart[1] == 'E') &&
1083 (pStart[2] == 'p' || pStart[2] == 'P') )
1085 rDateTime.SetMonth(9);
1086 return sal_True;
1088 if ((pStart[0] == 'o' || pStart[0] == 'O') &&
1089 (pStart[1] == 'c' || pStart[1] == 'C') &&
1090 (pStart[2] == 't' || pStart[2] == 'T') )
1092 rDateTime.SetMonth(10);
1093 return sal_True;
1095 if ((pStart[0] == 'n' || pStart[0] == 'N') &&
1096 (pStart[1] == 'o' || pStart[1] == 'O') &&
1097 (pStart[2] == 'v' || pStart[2] == 'V') )
1099 rDateTime.SetMonth(11);
1100 return sal_True;
1102 if ((pStart[0] == 'd' || pStart[0] == 'D') &&
1103 (pStart[1] == 'e' || pStart[1] == 'E') &&
1104 (pStart[2] == 'c' || pStart[2] == 'C') )
1106 rDateTime.SetMonth(12);
1107 return sal_True;
1109 return sal_False;
1113 * parseUNIX_isDayField.
1115 sal_Bool FTPDirectoryParser::parseUNIX_isDayField (
1116 const sal_Char *pStart,
1117 const sal_Char *pEnd,
1118 DateTime &rDateTime)
1120 if (!*pStart || !*pEnd || pStart == pEnd)
1121 return sal_False;
1122 if (*pStart < '0' || *pStart > '9')
1123 return sal_False;
1125 sal_uInt16 nDay = *pStart - '0';
1126 if (pStart + 1 < pEnd)
1128 if (pStart + 2 != pEnd || pStart[1] < '0' || pStart[1] > '9')
1129 return sal_False;
1130 nDay = 10 * nDay + (pStart[1] - '0');
1132 if (!nDay || nDay > 31)
1133 return sal_False;
1135 rDateTime.SetDay(nDay);
1136 return sal_True;
1140 * parseUNIX_isYearTimeField.
1142 sal_Bool FTPDirectoryParser::parseUNIX_isYearTimeField (
1143 const sal_Char *pStart,
1144 const sal_Char *pEnd,
1145 DateTime &rDateTime)
1147 if (!*pStart || !*pEnd || pStart == pEnd ||
1148 *pStart < '0' || *pStart > '9')
1149 return sal_False;
1151 sal_uInt16 nNumber = *pStart - '0';
1152 ++pStart;
1154 if (pStart == pEnd)
1155 return sal_False;
1156 if (*pStart == ':')
1157 return parseUNIX_isTime (pStart, pEnd, nNumber, rDateTime);
1158 if (*pStart < '0' || *pStart > '9')
1159 return sal_False;
1161 nNumber = 10 * nNumber + (*pStart - '0');
1162 ++pStart;
1164 if (pStart == pEnd)
1165 return sal_False;
1166 if (*pStart == ':')
1167 return parseUNIX_isTime (pStart, pEnd, nNumber, rDateTime);
1168 if (*pStart < '0' || *pStart > '9')
1169 return sal_False;
1171 nNumber = 10 * nNumber + (*pStart - '0');
1172 ++pStart;
1174 if (pStart == pEnd || *pStart < '0' || *pStart > '9')
1175 return sal_False;
1177 nNumber = 10 * nNumber + (*pStart - '0');
1178 if (pStart + 1 != pEnd || nNumber < 1970)
1179 return sal_False;
1181 rDateTime.SetYear(nNumber);
1182 rDateTime.SetTime(0);
1183 return sal_True;
1187 * parseUNIX_isTime.
1189 sal_Bool FTPDirectoryParser::parseUNIX_isTime (
1190 const sal_Char *pStart,
1191 const sal_Char *pEnd,
1192 sal_uInt16 nHour,
1193 DateTime &rDateTime)
1195 if ((nHour > 23 ) || (pStart + 3 != pEnd) ||
1196 (pStart[1] < '0') || (pStart[1] > '5') ||
1197 (pStart[2] < '0') || (pStart[2] > '9') )
1198 return sal_False;
1200 sal_uInt16 nMin = 10 * (pStart[1] - '0') + (pStart[2] - '0');
1202 rDateTime.SetHour (nHour);
1203 rDateTime.SetMin (nMin);
1204 rDateTime.SetSec (0);
1205 rDateTime.SetNanoSec (0);
1207 // Date aCurDate;
1208 // if (rDateTime.GetMonth() > aCurDate.GetMonth())
1209 // rDateTime.SetYear(aCurDate.GetYear() - 1);
1210 // else
1211 // rDateTime.SetYear(aCurDate.GetYear());
1212 // return sal_True;
1214 TimeValue aTimeVal;
1215 osl_getSystemTime(&aTimeVal);
1216 oslDateTime aCurrDateTime;
1217 osl_getDateTimeFromTimeValue(&aTimeVal,&aCurrDateTime);
1219 if (rDateTime.GetMonth() > aCurrDateTime.Month)
1220 rDateTime.SetYear(aCurrDateTime.Year - 1);
1221 else
1222 rDateTime.SetYear(aCurrDateTime.Year);
1223 return sal_True;
1227 * setYear.
1229 * Two-digit years are taken as within 50 years back and 49 years forward
1230 * (both ends inclusive) from the current year. The returned date is not
1231 * checked for validity of the given day in the given month and year.
1234 sal_Bool FTPDirectoryParser::setYear (
1235 DateTime &rDateTime, sal_uInt16 nYear)
1237 if (nYear < 100)
1239 TimeValue aTimeVal;
1240 osl_getSystemTime(&aTimeVal);
1241 oslDateTime aCurrDateTime;
1242 osl_getDateTimeFromTimeValue(&aTimeVal,&aCurrDateTime);
1243 sal_uInt16 nCurrentYear = aCurrDateTime.Year;
1244 // sal_uInt16 nCurrentYear = Date().GetYear();
1245 sal_uInt16 nCurrentCentury = nCurrentYear / 100;
1246 nCurrentYear %= 100;
1247 if (nCurrentYear < 50)
1248 if (nYear <= nCurrentYear)
1249 nYear += nCurrentCentury * 100;
1250 else if (nYear < nCurrentYear + 50)
1251 nYear += nCurrentCentury * 100;
1252 else
1253 nYear += (nCurrentCentury - 1) * 100;
1254 else
1255 if (nYear >= nCurrentYear)
1256 nYear += nCurrentCentury * 100;
1257 else if (nYear >= nCurrentYear - 50)
1258 nYear += nCurrentCentury * 100;
1259 else
1260 nYear += (nCurrentCentury + 1) * 100;
1263 rDateTime.SetYear(nYear);
1264 return sal_True;
1268 * setPath.
1270 sal_Bool FTPDirectoryParser::setPath (
1271 OUString &rPath, const sal_Char *value, sal_Int32 length)
1273 if (value)
1275 if (length < 0)
1276 length = rtl_str_getLength (value);
1277 rPath = OUString (value, length, RTL_TEXTENCODING_UTF8);
1279 return (!!value);
1282 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */