Version 4.0.2.1, tag libreoffice-4.0.2.1
[LibreOffice.git] / ucb / source / ucp / ftp / ftpdirp.cxx
blobc847a32d7acf83815eca2fdda90a813abb5c3834
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 using ::rtl::OUString;
33 typedef sal_uInt32 ULONG;
36 inline sal_Bool ascii_isLetter( sal_Unicode ch )
38 return (( (ch >= 0x0041) && (ch <= 0x005A)) ||
39 (( ch >= 0x0061) && (ch <= 0x007A)));
42 inline sal_Bool ascii_isWhitespace( sal_Unicode ch )
44 return ((ch <= 0x20) && ch);
49 /*========================================================================
51 * FTPDirectoryParser implementation.
53 *======================================================================*/
55 * parseDOS.
56 * Accepts one of two styles:
58 * 1 *WSP 1*2DIGIT ("." / "-") 1*2DIGIT ("." / "-") 1*4DIGIT 1*WSP
59 * 1*2DIGIT ":" 1*2DIGIT [*WSP ("A" / "P") "M"] 1*WSP
60 * ((DIGIT *(DIGIT / "." / ",")) / "<DIR>") 1*WSP 1*OCTET
62 * interpreted as: mm.dd.yy hh:mm (size / <DIR>) name
64 * 2 *WSP 1*DIGIT 1*WSP *(1*CHAR *WSP) *1("DIR" 1*WSP) 1*2DIGIT "-" 1*2DIGIT
65 * "-" 1*4DIGIT 1*WSP 1*2DIGIT ":" 1*2DIGIT 1*WSP 1*OCTET
67 * interpreted as: size attribs DIR mm-dd-yy hh:mm name
70 sal_Bool FTPDirectoryParser::parseDOS (
71 FTPDirentry &rEntry,
72 const sal_Char *pBuffer)
74 sal_Bool bDirectory = false;
75 sal_uInt32 nSize = 0;
76 sal_uInt16 nYear = 0;
77 sal_uInt16 nMonth = 0;
78 sal_uInt16 nDay = 0;
79 sal_uInt16 nHour = 0;
80 sal_uInt16 nMinute = 0;
82 enum StateType
84 STATE_INIT_LWS,
85 STATE_MONTH_OR_SIZE,
86 STATE_1_DAY, STATE_1_YEAR, STATE_1_YEAR_LWS, STATE_1_HOUR,
87 STATE_1_MINUTE, STATE_1_MINUTE_LWS, STATE_1_AP,
88 STATE_1_APM, STATE_1_LESS, STATE_1_D, STATE_1_DI,
89 STATE_1_DIR, STATE_1_SIZE,
90 STATE_2_SIZE, STATE_2_SIZE_LWS, STATE_2_ATTRIB,
91 STATE_2_D, STATE_2_DI, STATE_2_DIR_LWS,
92 STATE_2_MONTH, STATE_2_DAY, STATE_2_YEAR, STATE_2_YEAR_LWS,
93 STATE_2_HOUR, STATE_2_MINUTE,
94 STATE_LWS_NAME,
95 STATE_ERROR
98 int nDigits = 0;
99 enum StateType eState = STATE_INIT_LWS;
100 for (const sal_Char *p = pBuffer;
101 eState != STATE_ERROR && *p;
102 ++p)
104 switch (eState)
106 case STATE_INIT_LWS:
107 if (*p >= '0' && *p <= '9')
109 nMonth = *p - '0';
110 nDigits = 1;
111 eState = STATE_MONTH_OR_SIZE;
113 else if (!ascii_isWhitespace(*p))
114 eState = STATE_ERROR;
115 break;
117 case STATE_MONTH_OR_SIZE:
118 if (*p >= '0' && *p <= '9')
120 nMonth = 10 * nMonth + (*p - '0');
121 if (nDigits < 2)
122 ++nDigits;
123 else
125 nSize = nMonth;
126 nMonth = 0;
127 eState = STATE_2_SIZE;
130 else if (ascii_isWhitespace(*p))
132 nSize = nMonth;
133 nMonth = 0;
134 eState = STATE_2_SIZE_LWS;
136 else if ((*p == '.' || *p == '-') && nMonth && nMonth <= 12)
138 nDigits = 0;
139 eState = STATE_1_DAY;
141 else
142 eState = STATE_ERROR;
143 break;
145 case STATE_1_DAY:
146 if (*p >= '0' && *p <= '9')
147 if (nDigits < 2)
149 nDay = 10 * nDay + (*p - '0');
150 ++nDigits;
152 else
153 eState = STATE_ERROR;
154 else if ((*p == '.' || *p == '-') && nDay && nDay <= 31)
156 nDigits = 0;
157 eState = STATE_1_YEAR;
159 else
160 eState = STATE_ERROR;
161 break;
163 case STATE_1_YEAR:
164 if (*p >= '0' && *p <= '9')
166 if (nDigits < 4)
168 nYear = 10 * nYear + (*p - '0');
169 ++nDigits;
171 else
172 eState = STATE_ERROR;
174 else
176 if (ascii_isWhitespace(*p))
177 eState = STATE_1_YEAR_LWS;
178 else
179 eState = STATE_ERROR;
181 break;
183 case STATE_1_YEAR_LWS:
184 if (*p >= '0' && *p <= '9')
186 nHour = *p - '0';
187 nDigits = 1;
188 eState = STATE_1_HOUR;
190 else if (!ascii_isWhitespace(*p))
191 eState = STATE_ERROR;
192 break;
194 case STATE_1_HOUR:
195 if (*p >= '0' && *p <= '9')
196 if (nDigits < 2)
198 nHour = 10 * nHour + (*p - '0');
199 ++nDigits;
201 else
202 eState = STATE_ERROR;
203 else if (*p == ':' && nHour < 24)
205 nDigits = 0;
206 eState = STATE_1_MINUTE;
208 else
209 eState = STATE_ERROR;
210 break;
212 case STATE_1_MINUTE:
213 if (*p >= '0' && *p <= '9')
214 if (nDigits < 2)
216 nMinute = 10 * nMinute + (*p - '0');
217 ++nDigits;
219 else
220 eState = STATE_ERROR;
221 else if ((*p == 'a' || *p == 'A') && nMinute < 60)
222 if (nHour >= 1 && nHour <= 11)
223 eState = STATE_1_AP;
224 else if (nHour == 12)
226 nHour = 0;
227 eState = STATE_1_AP;
229 else
230 eState = STATE_ERROR;
231 else if ((*p == 'p' || *p == 'P') && nMinute < 60)
232 if (nHour >= 1 && nHour <= 11)
234 nHour += 12;
235 eState = STATE_1_AP;
237 else if (nHour == 12)
238 eState = STATE_1_AP;
239 else
240 eState = STATE_ERROR;
241 else if (ascii_isWhitespace(*p) && (nMinute < 60))
242 eState = STATE_1_MINUTE_LWS;
243 else
244 eState = STATE_ERROR;
245 break;
247 case STATE_1_MINUTE_LWS:
248 if (*p == 'a' || *p == 'A')
249 if (nHour >= 1 && nHour <= 11)
250 eState = STATE_1_AP;
251 else if (nHour == 12)
253 nHour = 0;
254 eState = STATE_1_AP;
256 else
257 eState = STATE_ERROR;
258 else if (*p == 'p' || *p == 'P')
259 if (nHour >= 1 && nHour <= 11)
261 nHour += 12;
262 eState = STATE_1_AP;
264 else if (nHour == 12)
265 eState = STATE_1_AP;
266 else
267 eState = STATE_ERROR;
268 else if (*p == '<')
269 eState = STATE_1_LESS;
270 else if (*p >= '0' && *p <= '9')
272 nSize = *p - '0';
273 eState = STATE_1_SIZE;
275 else if (!ascii_isWhitespace(*p))
276 eState = STATE_ERROR;
277 break;
279 case STATE_1_AP:
280 eState = *p == 'm' || *p == 'M' ? STATE_1_APM : STATE_ERROR;
281 break;
283 case STATE_1_APM:
284 if (*p == '<')
285 eState = STATE_1_LESS;
286 else if (*p >= '0' && *p <= '9')
288 nSize = *p - '0';
289 eState = STATE_1_SIZE;
291 else if (!ascii_isWhitespace(*p))
292 eState = STATE_ERROR;
293 break;
295 case STATE_1_LESS:
296 eState = *p == 'd' || *p == 'D' ? STATE_1_D : STATE_ERROR;
297 break;
299 case STATE_1_D:
300 eState = *p == 'i' || *p == 'I' ? STATE_1_DI : STATE_ERROR;
301 break;
303 case STATE_1_DI:
304 eState = *p == 'r' || *p == 'R' ? STATE_1_DIR : STATE_ERROR;
305 break;
307 case STATE_1_DIR:
308 if (*p == '>')
310 bDirectory = true;
311 eState = STATE_LWS_NAME;
313 else
314 eState = STATE_ERROR;
315 break;
317 case STATE_1_SIZE:
318 if (*p >= '0' && *p <= '9')
319 nSize = 10 * nSize + (*p - '0');
320 else if (ascii_isWhitespace(*p))
321 eState = STATE_LWS_NAME;
322 else
323 eState = STATE_ERROR;
324 break;
326 case STATE_2_SIZE:
327 if (*p >= '0' && *p <= '9')
328 nSize = 10 * nSize + (*p - '0');
329 else if (ascii_isWhitespace(*p))
330 eState = STATE_2_SIZE_LWS;
331 else
332 eState = STATE_ERROR;
333 break;
335 case STATE_2_SIZE_LWS:
336 if (*p == 'd' || *p == 'D')
337 eState = STATE_2_D;
338 else if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))
339 eState = STATE_2_ATTRIB;
340 else if (*p >= '0' && *p <= '9')
342 nMonth = *p - '0';
343 nDigits = 1;
344 eState = STATE_2_MONTH;
346 else if (!ascii_isWhitespace(*p))
347 eState = STATE_ERROR;
348 break;
350 case STATE_2_ATTRIB:
351 if (ascii_isWhitespace(*p))
352 eState = STATE_2_SIZE_LWS;
353 else if ((*p < 'a' || *p > 'z') && (*p < 'A' || *p > 'Z'))
354 eState = STATE_ERROR;
355 break;
357 case STATE_2_D:
358 if (*p == 'i' || *p == 'I')
359 eState = STATE_2_DI;
360 else if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))
361 eState = STATE_2_ATTRIB;
362 else if (ascii_isWhitespace(*p))
363 eState = STATE_2_SIZE_LWS;
364 else
365 eState = STATE_ERROR;
366 break;
368 case STATE_2_DI:
369 if (*p == 'r' || *p == 'R')
371 bDirectory = true;
372 eState = STATE_2_DIR_LWS;
374 else
376 if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))
377 eState = STATE_2_ATTRIB;
378 else if (ascii_isWhitespace(*p))
379 eState = STATE_2_SIZE_LWS;
380 else
381 eState = STATE_ERROR;
383 break;
385 case STATE_2_DIR_LWS:
386 if (*p >= '0' && *p <= '9')
388 nMonth = *p - '0';
389 nDigits = 1;
390 eState = STATE_2_MONTH;
392 else if (!ascii_isWhitespace(*p))
393 eState = STATE_ERROR;
394 break;
396 case STATE_2_MONTH:
397 if (*p >= '0' && *p <= '9')
398 if (nDigits < 2)
400 nMonth = 10 * nMonth + (*p - '0');
401 ++nDigits;
403 else
404 eState = STATE_ERROR;
405 else if (*p == '-' && nMonth && nMonth <= 12)
407 nDigits = 0;
408 eState = STATE_2_DAY;
410 else
411 eState = STATE_ERROR;
412 break;
414 case STATE_2_DAY:
415 if (*p >= '0' && *p <= '9')
416 if (nDigits < 2)
418 nDay = 10 * nDay + (*p - '0');
419 ++nDigits;
421 else
422 eState = STATE_ERROR;
423 else if (*p == '-' && nDay && nDay <= 31)
425 nDigits = 0;
426 eState = STATE_2_YEAR;
428 else
429 eState = STATE_ERROR;
430 break;
432 case STATE_2_YEAR:
433 if (*p >= '0' && *p <= '9')
435 if (nDigits < 4)
437 nYear = 10 * nYear + (*p - '0');
438 ++nDigits;
440 else
441 eState = STATE_ERROR;
443 else
445 if (ascii_isWhitespace(*p))
446 eState = STATE_2_YEAR_LWS;
447 else
448 eState = STATE_ERROR;
450 break;
452 case STATE_2_YEAR_LWS:
453 if (*p >= '0' && *p <= '9')
455 nHour = *p - '0';
456 nDigits = 1;
457 eState = STATE_2_HOUR;
459 else if (!ascii_isWhitespace(*p))
460 eState = STATE_ERROR;
461 break;
463 case STATE_2_HOUR:
464 if (*p >= '0' && *p <= '9')
465 if (nDigits < 2)
467 nHour = 10 * nHour + (*p - '0');
468 ++nDigits;
470 else
471 eState = STATE_ERROR;
472 else if (*p == ':' && nHour < 24)
474 nDigits = 0;
475 eState = STATE_2_MINUTE;
477 else
478 eState = STATE_ERROR;
479 break;
481 case STATE_2_MINUTE:
482 if (*p >= '0' && *p <= '9')
484 if (nDigits < 2)
486 nMinute = 10 * nMinute + (*p - '0');
487 ++nDigits;
489 else
490 eState = STATE_ERROR;
492 else
494 if (ascii_isWhitespace(*p) && (nMinute < 60))
495 eState = STATE_LWS_NAME;
496 else
497 eState = STATE_ERROR;
499 break;
501 case STATE_LWS_NAME:
502 if (!ascii_isWhitespace(*p))
504 setPath (rEntry.m_aName, p);
505 if (bDirectory)
506 rEntry.m_nMode |= INETCOREFTP_FILEMODE_ISDIR;
507 rEntry.m_nSize = nSize;
509 setYear (rEntry.m_aDate, nYear);
511 rEntry.m_aDate.SetMonth(nMonth);
512 rEntry.m_aDate.SetDay(nDay);
513 rEntry.m_aDate.SetHour(nHour);
514 rEntry.m_aDate.SetMin(nMinute);
516 return sal_True;
518 break;
519 case STATE_ERROR:
520 break;
524 return sal_False;
528 * parseVMS.
529 * Directory entries may span one or two lines:
531 * entry: *lws name *1(*lws <NEWLINE>) 1*lws size 1*lws datetime rest
533 * name: filename "." filetype ";" version
534 * filename: 1*39fchar
535 * filetype: 1*39fchar
536 * version: non0digit *digit
538 * size: "0" / non0digit *digit
540 * datetime: date 1*lwsp time
541 * date: day "-" month "-" year
542 * day: (*1"0" non0digit) / ("1"-"2" digit) / ("3" "0"-"1")
543 * month: "JAN" / "FEB" / "MAR" / "APR" / "MAY" / "JUN" / "JUL" / "AUG"
544 * / "SEP" / "OCT" / "NOV" / "DEC" ; all case insensitive
545 * year: 2digit / 4digit
546 * time: hour ":" minute
547 * hour: ((*1"0" / "1") digit) / ("2" "0"-"3")
548 * minute: "0"-"5" digit
550 * rest: *1(lws *<ANY>)
552 * lws: <TAB> / <SPACE>
553 * non0digit: "1"-"9"
554 * digit: "0" / non0digit
555 * fchar: "A"-"Z" / "a"-"z" / digit / "-" / "_" / "$"
557 * For directories, the returned name is the <filename> part; for non-
558 * directory files, the returned name is the <filename "." filetype> part.
559 * An entry is a directory iff its filetype is "DIR" (ignoring case).
561 * The READ, WRITE, and ISLINK mode bits are not supported.
563 * The returned size is the <size> part, multiplied by 512, and with the high
564 * order bits truncated to fit into a ULONG.
567 sal_Bool FTPDirectoryParser::parseVMS (
568 FTPDirentry &rEntry,
569 const sal_Char *pBuffer)
571 static OUString aFirstLineName;
572 static sal_Bool bFirstLineDir = sal_False;
574 for (sal_Bool bFirstLine = sal_True;; bFirstLine = sal_False)
576 const sal_Char *p = pBuffer;
577 if (bFirstLine)
579 // Skip <*lws> part:
580 while (*p == '\t' || *p == ' ')
581 ++p;
583 // Parse <filename "."> part:
584 const sal_Char *pFileName = p;
585 while ((*p >= 'A' && *p <= 'Z') ||
586 (*p >= 'a' && *p <= 'z') ||
587 (*p >= '0' && *p <= '9') ||
588 *p == '-' || *p == '_' || *p == '$')
589 ++p;
591 if (*p != '.' || p == pFileName || p - pFileName > 39)
593 if (!aFirstLineName.isEmpty())
594 continue;
595 else
596 return sal_False;
599 // Parse <filetype ";"> part:
600 const sal_Char *pFileType = ++p;
601 while ((*p >= 'A' && *p <= 'Z') ||
602 (*p >= 'a' && *p <= 'z') ||
603 (*p >= '0' && *p <= '9') ||
604 *p == '-' || *p == '_' || *p == '$')
605 ++p;
607 if (*p != ';' || p == pFileName || p - pFileName > 39)
609 if (!aFirstLineName.isEmpty())
610 continue;
611 else
612 return sal_False;
614 ++p;
616 // Set entry's name and mode (ISDIR flag):
617 if ((p - pFileType == 4) &&
618 (pFileType[0] == 'D' || pFileType[0] == 'd') &&
619 (pFileType[1] == 'I' || pFileType[1] == 'i') &&
620 (pFileType[2] == 'R' || pFileType[2] == 'r') )
622 setPath (rEntry.m_aName, pFileName, (pFileType - pFileName));
623 rEntry.m_nMode = INETCOREFTP_FILEMODE_ISDIR;
625 else
627 setPath (rEntry.m_aName, pFileName, (p - pFileName));
628 rEntry.m_nMode = 0;
631 // Skip <version> part:
632 if (*p < '1' || *p > '9')
634 if (!aFirstLineName.isEmpty())
635 continue;
636 else
637 return sal_False;
639 ++p;
640 while (*p >= '0' && *p <= '9')
641 ++p;
643 // Parse <1*lws> or <*lws <NEWLINE>> part:
644 sal_Bool bLWS = false;
645 while (*p == '\t' || *p == ' ')
647 bLWS = true;
648 ++p;
650 if (*p)
652 if (!bLWS)
654 if (!aFirstLineName.isEmpty())
655 continue;
656 else
657 return sal_False;
660 else
663 * First line of entry spanning two lines,
664 * wait for second line.
666 aFirstLineName = rEntry.m_aName;
667 bFirstLineDir =
668 ((rEntry.m_nMode & INETCOREFTP_FILEMODE_ISDIR) != 0);
669 return sal_False;
672 else
675 * Second line of entry spanning two lines,
676 * restore entry's name and mode (ISDIR flag).
678 rEntry.m_aName = aFirstLineName;
679 rEntry.m_nMode = (bFirstLineDir ? INETCOREFTP_FILEMODE_ISDIR : 0);
681 // Skip <1*lws> part:
682 if (*p != '\t' && *p != ' ')
683 return sal_False;
684 ++p;
685 while (*p == '\t' || *p == ' ')
686 ++p;
689 // Parse <size> part and set entry's size:
690 if (*p < '0' || *p > '9')
691 return sal_False;
692 ULONG nSize = *p - '0';
693 if (*p++ != '0')
694 while (*p >= '0' && *p <= '9')
695 nSize = 10 * rEntry.m_nSize + (*p++ - '0');
696 rEntry.m_nSize = 512 * nSize;
698 // Skip <1*lws> part:
699 if (*p != '\t' && *p != ' ')
700 return sal_False;
701 ++p;
702 while (*p == '\t' || *p == ' ')
703 ++p;
705 // Parse <day "-"> part and set entry date's day:
706 sal_uInt16 nDay;
707 if (*p == '0')
709 ++p;
710 if (*p < '1' || *p > '9')
711 return sal_False;
712 nDay = *p++ - '0';
714 else if (*p == '1' || *p == '2')
716 nDay = *p++ - '0';
717 if (*p >= '0' && *p <= '9')
718 nDay = 10 * nDay + (*p++ - '0');
720 else if (*p == '3')
722 ++p;
723 nDay = (*p == '0' || *p == '1') ? 30 + (*p++ - '0') : 3;
725 else if (*p >= '4' && *p <= '9')
726 nDay = *p++ - '0';
727 else
728 return sal_False;
730 rEntry.m_aDate.SetDay(nDay);
731 if (*p++ != '-')
732 return sal_False;
734 // Parse <month "-"> part and set entry date's month:
735 sal_Char const * pMonth = p;
736 sal_Int32 const monthLen = 3;
737 for (int i = 0; i < monthLen; ++i)
739 if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z')))
740 return sal_False;
741 ++p;
743 if (rtl_str_compareIgnoreAsciiCase_WithLength(
744 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("JAN")) == 0)
745 rEntry.m_aDate.SetMonth(1);
746 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
747 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("FEB")) == 0)
748 rEntry.m_aDate.SetMonth(2);
749 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
750 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("MAR")) == 0)
751 rEntry.m_aDate.SetMonth(3);
752 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
753 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("APR")) == 0)
754 rEntry.m_aDate.SetMonth(4);
755 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
756 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("MAY")) == 0)
757 rEntry.m_aDate.SetMonth(5);
758 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
759 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("JUN")) == 0)
760 rEntry.m_aDate.SetMonth(6);
761 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
762 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("JUL")) == 0)
763 rEntry.m_aDate.SetMonth(7);
764 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
765 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("AUG")) == 0)
766 rEntry.m_aDate.SetMonth(8);
767 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
768 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("SEP")) == 0)
769 rEntry.m_aDate.SetMonth(9);
770 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
771 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("OCT")) == 0)
772 rEntry.m_aDate.SetMonth(10);
773 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
774 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("NOV")) == 0)
775 rEntry.m_aDate.SetMonth(11);
776 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
777 pMonth, monthLen, RTL_CONSTASCII_STRINGPARAM("DEC")) == 0)
778 rEntry.m_aDate.SetMonth(12);
779 else
780 return sal_False;
781 if (*p++ != '-')
782 return sal_False;
784 // Parse <year> part and set entry date's year:
785 sal_uInt16 nYear = 0;
786 {for (int i = 0; i < 2; ++i)
788 if (*p < '0' || *p > '9')
789 return sal_False;
790 nYear = 10 * nYear + (*p++ - '0');
792 if (*p >= '0' && *p <= '9')
794 nYear = 10 * nYear + (*p++ - '0');
795 if (*p < '0' || *p > '9')
796 return sal_False;
797 nYear = 10 * nYear + (*p++ - '0');
799 setYear (rEntry.m_aDate, nYear);
801 // Skip <1*lws> part:
802 if (*p != '\t' && *p != ' ')
803 return sal_False;
804 ++p;
805 while (*p == '\t' || *p == ' ')
806 ++p;
808 // Parse <hour ":"> part and set entry time's hour:
809 sal_uInt16 nHour;
810 if (*p == '0' || *p == '1')
812 nHour = *p++ - '0';
813 if (*p >= '0' && *p <= '9')
814 nHour = 10 * nHour + (*p++ - '0');
816 else if (*p == '2')
818 ++p;
819 nHour = (*p >= '0' && *p <= '3') ? 20 + (*p++ - '0') : 2;
821 else if (*p >= '3' && *p <= '9')
822 nHour = *p++ - '0';
823 else
824 return sal_False;
826 rEntry.m_aDate.SetHour(nHour);
827 if (*p++ != ':')
828 return sal_False;
831 * Parse <minute> part and set entry time's minutes,
832 * seconds (0), and 1/100 seconds (0).
834 if (*p < '0' || *p > '5')
835 return sal_False;
837 sal_uInt16 nMinute = *p++ - '0';
838 if (*p < '0' || *p > '9')
839 return sal_False;
841 nMinute = 10 * nMinute + (*p++ - '0');
842 rEntry.m_aDate.SetMin(nMinute);
843 rEntry.m_aDate.SetSec(0);
844 rEntry.m_aDate.Set100Sec(0);
846 // Skip <rest> part:
847 if (*p && (*p != '\t' && *p != ' '))
848 return sal_False;
850 return sal_True;
855 * parseUNIX
857 sal_Bool FTPDirectoryParser::parseUNIX (
858 FTPDirentry &rEntry,
859 const sal_Char *pBuffer)
861 const sal_Char *p1, *p2;
862 p1 = p2 = pBuffer;
864 if (!((*p1 == '-') || (*p1 == 'd') || (*p1 == 'l')))
865 return sal_False;
867 // 1st column: FileMode.
868 if (*p1 == 'd')
869 rEntry.m_nMode |= INETCOREFTP_FILEMODE_ISDIR;
871 if (*p1 == 'l')
872 rEntry.m_nMode |= INETCOREFTP_FILEMODE_ISLINK;
874 // Skip to end of column and set rights by the way
875 while (*p1 && !ascii_isWhitespace(*p1)) {
876 if(*p1 == 'r')
877 rEntry.m_nMode |= INETCOREFTP_FILEMODE_READ;
878 else if(*p1 == 'w')
879 rEntry.m_nMode |= INETCOREFTP_FILEMODE_WRITE;
880 p1++;
884 * Scan for the sequence of size and date fields:
885 * *LWS 1*DIGIT 1*LWS 3CHAR 1*LWS 1*2DIGIT 1*LWS
886 * (4DIGIT / (1*2DIGIT ":" 2DIGIT)) 1*LWS
888 enum Mode
890 FOUND_NONE, FOUND_SIZE, FOUND_MONTH, FOUND_DAY, FOUND_YEAR_TIME
893 const sal_Char *pDayStart = 0;
894 const sal_Char *pDayEnd = 0;
895 Mode eMode;
896 for (eMode = FOUND_NONE; *p1 && eMode != FOUND_YEAR_TIME; p1 = p2 + 1)
898 while (*p1 && ascii_isWhitespace(*p1))
899 ++p1;
900 p2 = p1;
901 while (*p2 && !ascii_isWhitespace(*p2))
902 ++p2;
904 switch (eMode)
906 case FOUND_NONE:
907 if (parseUNIX_isSizeField (p1, p2, rEntry.m_nSize))
908 eMode = FOUND_SIZE;
909 break;
911 case FOUND_SIZE:
912 if (parseUNIX_isMonthField (p1, p2, rEntry.m_aDate))
913 eMode = FOUND_MONTH;
914 else if (!parseUNIX_isSizeField (p1, p2, rEntry.m_nSize))
915 eMode = FOUND_NONE;
916 break;
918 case FOUND_MONTH:
919 if (parseUNIX_isDayField (p1, p2, rEntry.m_aDate))
921 pDayStart = p1;
922 pDayEnd = p2;
923 eMode = FOUND_DAY;
925 else if (parseUNIX_isSizeField (p1, p2, rEntry.m_nSize))
926 eMode = FOUND_SIZE;
927 else
928 eMode = FOUND_NONE;
929 break;
931 case FOUND_DAY:
932 if (parseUNIX_isYearTimeField (p1, p2, rEntry.m_aDate))
933 eMode = FOUND_YEAR_TIME;
934 else if (
935 parseUNIX_isSizeField (
936 pDayStart, pDayEnd, rEntry.m_nSize) &&
937 parseUNIX_isMonthField (
938 p1, p2, rEntry.m_aDate))
939 eMode = FOUND_MONTH;
940 else if (parseUNIX_isSizeField (p1, p2, rEntry.m_nSize))
941 eMode = FOUND_SIZE;
942 else
943 eMode = FOUND_NONE;
944 break;
945 case FOUND_YEAR_TIME:
946 break;
950 if (eMode == FOUND_YEAR_TIME)
952 // 9th column: FileName (rest of line).
953 while (*p1 && ascii_isWhitespace(*p1)) p1++;
954 setPath (rEntry.m_aName, p1);
956 // Done.
957 return sal_True;
959 return sal_False;
963 * parseUNIX_isSizeField.
965 sal_Bool FTPDirectoryParser::parseUNIX_isSizeField (
966 const sal_Char *pStart,
967 const sal_Char *pEnd,
968 sal_uInt32 &rSize)
970 if (!*pStart || !*pEnd || pStart == pEnd)
971 return sal_False;
973 rSize = 0;
974 if (*pStart >= '0' && *pStart <= '9')
976 for (; pStart < pEnd; ++pStart)
977 if ((*pStart >= '0') && (*pStart <= '9'))
978 rSize = 10 * rSize + (*pStart - '0');
979 else
980 return sal_False;
981 return sal_True;
983 else
986 * For a combination of long group name and large file size,
987 * some FTPDs omit LWS between those two columns.
989 int nNonDigits = 0;
990 int nDigits = 0;
992 for (; pStart < pEnd; ++pStart)
993 if ((*pStart >= '1') && (*pStart <= '9'))
995 ++nDigits;
996 rSize = 10 * rSize + (*pStart - '0');
998 else if ((*pStart == '0') && nDigits)
1000 ++nDigits;
1001 rSize *= 10;
1003 else if ((*pStart > ' ') && (sal::static_int_cast<sal_uInt8>(*pStart) <= '\x7F'))
1005 nNonDigits += nDigits + 1;
1006 nDigits = 0;
1007 rSize = 0;
1009 else
1010 return sal_False;
1011 return ((nNonDigits >= 9) && (nDigits >= 7));
1016 * parseUNIX_isMonthField.
1018 sal_Bool FTPDirectoryParser::parseUNIX_isMonthField (
1019 const sal_Char *pStart,
1020 const sal_Char *pEnd,
1021 DateTime &rDateTime)
1023 if (!*pStart || !*pEnd || pStart + 3 != pEnd)
1024 return sal_False;
1026 if ((pStart[0] == 'j' || pStart[0] == 'J') &&
1027 (pStart[1] == 'a' || pStart[1] == 'A') &&
1028 (pStart[2] == 'n' || pStart[2] == 'N') )
1030 rDateTime.SetMonth(1);
1031 return sal_True;
1033 if ((pStart[0] == 'f' || pStart[0] == 'F') &&
1034 (pStart[1] == 'e' || pStart[1] == 'E') &&
1035 (pStart[2] == 'b' || pStart[2] == 'B') )
1037 rDateTime.SetMonth(2);
1038 return sal_True;
1040 if ((pStart[0] == 'm' || pStart[0] == 'M') &&
1041 (pStart[1] == 'a' || pStart[1] == 'A') &&
1042 (pStart[2] == 'r' || pStart[2] == 'R') )
1044 rDateTime.SetMonth(3);
1045 return sal_True;
1047 if ((pStart[0] == 'a' || pStart[0] == 'A') &&
1048 (pStart[1] == 'p' || pStart[1] == 'P') &&
1049 (pStart[2] == 'r' || pStart[2] == 'R') )
1051 rDateTime.SetMonth(4);
1052 return sal_True;
1054 if ((pStart[0] == 'm' || pStart[0] == 'M') &&
1055 (pStart[1] == 'a' || pStart[1] == 'A') &&
1056 (pStart[2] == 'y' || pStart[2] == 'Y') )
1058 rDateTime.SetMonth(5);
1059 return sal_True;
1061 if ((pStart[0] == 'j' || pStart[0] == 'J') &&
1062 (pStart[1] == 'u' || pStart[1] == 'U') &&
1063 (pStart[2] == 'n' || pStart[2] == 'N') )
1065 rDateTime.SetMonth(6);
1066 return sal_True;
1068 if ((pStart[0] == 'j' || pStart[0] == 'J') &&
1069 (pStart[1] == 'u' || pStart[1] == 'U') &&
1070 (pStart[2] == 'l' || pStart[2] == 'L') )
1072 rDateTime.SetMonth(7);
1073 return sal_True;
1075 if ((pStart[0] == 'a' || pStart[0] == 'A') &&
1076 (pStart[1] == 'u' || pStart[1] == 'U') &&
1077 (pStart[2] == 'g' || pStart[2] == 'G') )
1079 rDateTime.SetMonth(8);
1080 return sal_True;
1082 if ((pStart[0] == 's' || pStart[0] == 'S') &&
1083 (pStart[1] == 'e' || pStart[1] == 'E') &&
1084 (pStart[2] == 'p' || pStart[2] == 'P') )
1086 rDateTime.SetMonth(9);
1087 return sal_True;
1089 if ((pStart[0] == 'o' || pStart[0] == 'O') &&
1090 (pStart[1] == 'c' || pStart[1] == 'C') &&
1091 (pStart[2] == 't' || pStart[2] == 'T') )
1093 rDateTime.SetMonth(10);
1094 return sal_True;
1096 if ((pStart[0] == 'n' || pStart[0] == 'N') &&
1097 (pStart[1] == 'o' || pStart[1] == 'O') &&
1098 (pStart[2] == 'v' || pStart[2] == 'V') )
1100 rDateTime.SetMonth(11);
1101 return sal_True;
1103 if ((pStart[0] == 'd' || pStart[0] == 'D') &&
1104 (pStart[1] == 'e' || pStart[1] == 'E') &&
1105 (pStart[2] == 'c' || pStart[2] == 'C') )
1107 rDateTime.SetMonth(12);
1108 return sal_True;
1110 return sal_False;
1114 * parseUNIX_isDayField.
1116 sal_Bool FTPDirectoryParser::parseUNIX_isDayField (
1117 const sal_Char *pStart,
1118 const sal_Char *pEnd,
1119 DateTime &rDateTime)
1121 if (!*pStart || !*pEnd || pStart == pEnd)
1122 return sal_False;
1123 if (*pStart < '0' || *pStart > '9')
1124 return sal_False;
1126 sal_uInt16 nDay = *pStart - '0';
1127 if (pStart + 1 < pEnd)
1129 if (pStart + 2 != pEnd || pStart[1] < '0' || pStart[1] > '9')
1130 return sal_False;
1131 nDay = 10 * nDay + (pStart[1] - '0');
1133 if (!nDay || nDay > 31)
1134 return sal_False;
1136 rDateTime.SetDay(nDay);
1137 return sal_True;
1141 * parseUNIX_isYearTimeField.
1143 sal_Bool FTPDirectoryParser::parseUNIX_isYearTimeField (
1144 const sal_Char *pStart,
1145 const sal_Char *pEnd,
1146 DateTime &rDateTime)
1148 if (!*pStart || !*pEnd || pStart == pEnd ||
1149 *pStart < '0' || *pStart > '9')
1150 return sal_False;
1152 sal_uInt16 nNumber = *pStart - '0';
1153 ++pStart;
1155 if (pStart == pEnd)
1156 return sal_False;
1157 if (*pStart == ':')
1158 return parseUNIX_isTime (pStart, pEnd, nNumber, rDateTime);
1159 if (*pStart < '0' || *pStart > '9')
1160 return sal_False;
1162 nNumber = 10 * nNumber + (*pStart - '0');
1163 ++pStart;
1165 if (pStart == pEnd)
1166 return sal_False;
1167 if (*pStart == ':')
1168 return parseUNIX_isTime (pStart, pEnd, nNumber, rDateTime);
1169 if (*pStart < '0' || *pStart > '9')
1170 return sal_False;
1172 nNumber = 10 * nNumber + (*pStart - '0');
1173 ++pStart;
1175 if (pStart == pEnd || *pStart < '0' || *pStart > '9')
1176 return sal_False;
1178 nNumber = 10 * nNumber + (*pStart - '0');
1179 if (pStart + 1 != pEnd || nNumber < 1970)
1180 return sal_False;
1182 rDateTime.SetYear(nNumber);
1183 rDateTime.SetTime(0);
1184 return sal_True;
1188 * parseUNIX_isTime.
1190 sal_Bool FTPDirectoryParser::parseUNIX_isTime (
1191 const sal_Char *pStart,
1192 const sal_Char *pEnd,
1193 sal_uInt16 nHour,
1194 DateTime &rDateTime)
1196 if ((nHour > 23 ) || (pStart + 3 != pEnd) ||
1197 (pStart[1] < '0') || (pStart[1] > '5') ||
1198 (pStart[2] < '0') || (pStart[2] > '9') )
1199 return sal_False;
1201 sal_uInt16 nMin = 10 * (pStart[1] - '0') + (pStart[2] - '0');
1203 rDateTime.SetHour (nHour);
1204 rDateTime.SetMin (nMin);
1205 rDateTime.SetSec (0);
1206 rDateTime.Set100Sec (0);
1208 // Date aCurDate;
1209 // if (rDateTime.GetMonth() > aCurDate.GetMonth())
1210 // rDateTime.SetYear(aCurDate.GetYear() - 1);
1211 // else
1212 // rDateTime.SetYear(aCurDate.GetYear());
1213 // return sal_True;
1215 TimeValue aTimeVal;
1216 osl_getSystemTime(&aTimeVal);
1217 oslDateTime aCurrDateTime;
1218 osl_getDateTimeFromTimeValue(&aTimeVal,&aCurrDateTime);
1220 if (rDateTime.GetMonth() > aCurrDateTime.Month)
1221 rDateTime.SetYear(aCurrDateTime.Year - 1);
1222 else
1223 rDateTime.SetYear(aCurrDateTime.Year);
1224 return sal_True;
1228 * setYear.
1230 * Two-digit years are taken as within 50 years back and 49 years forward
1231 * (both ends inclusive) from the current year. The returned date is not
1232 * checked for validity of the given day in the given month and year.
1235 sal_Bool FTPDirectoryParser::setYear (
1236 DateTime &rDateTime, sal_uInt16 nYear)
1238 if (nYear < 100)
1240 TimeValue aTimeVal;
1241 osl_getSystemTime(&aTimeVal);
1242 oslDateTime aCurrDateTime;
1243 osl_getDateTimeFromTimeValue(&aTimeVal,&aCurrDateTime);
1244 sal_uInt16 nCurrentYear = aCurrDateTime.Year;
1245 // sal_uInt16 nCurrentYear = Date().GetYear();
1246 sal_uInt16 nCurrentCentury = nCurrentYear / 100;
1247 nCurrentYear %= 100;
1248 if (nCurrentYear < 50)
1249 if (nYear <= nCurrentYear)
1250 nYear += nCurrentCentury * 100;
1251 else if (nYear < nCurrentYear + 50)
1252 nYear += nCurrentCentury * 100;
1253 else
1254 nYear += (nCurrentCentury - 1) * 100;
1255 else
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;
1264 rDateTime.SetYear(nYear);
1265 return sal_True;
1269 * setPath.
1271 sal_Bool FTPDirectoryParser::setPath (
1272 OUString &rPath, const sal_Char *value, sal_Int32 length)
1274 if (value)
1276 if (length < 0)
1277 length = rtl_str_getLength (value);
1278 rPath = OUString (value, length, RTL_TEXTENCODING_UTF8);
1280 return (!!value);
1283 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */