Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / ucb / source / ucp / ftp / ftpdirp.cxx
blob69bea64ab0e8686bc0511bfd3ee1f8b8a9553cbe
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 static bool ascii_isWhitespace( sal_Unicode ch )
34 return ((ch <= 0x20) && ch);
38 /*========================================================================
40 * FTPDirectoryParser implementation.
42 *======================================================================*/
44 * parseDOS.
45 * Accepts one of two styles:
47 * 1 *WSP 1*2DIGIT ("." / "-") 1*2DIGIT ("." / "-") 1*4DIGIT 1*WSP
48 * 1*2DIGIT ":" 1*2DIGIT [*WSP ("A" / "P") "M"] 1*WSP
49 * ((DIGIT *(DIGIT / "." / ",")) / "<DIR>") 1*WSP 1*OCTET
51 * interpreted as: mm.dd.yy hh:mm (size / <DIR>) name
53 * 2 *WSP 1*DIGIT 1*WSP *(1*CHAR *WSP) *1("DIR" 1*WSP) 1*2DIGIT "-" 1*2DIGIT
54 * "-" 1*4DIGIT 1*WSP 1*2DIGIT ":" 1*2DIGIT 1*WSP 1*OCTET
56 * interpreted as: size attribs DIR mm-dd-yy hh:mm name
59 bool FTPDirectoryParser::parseDOS (
60 FTPDirentry &rEntry,
61 const char *pBuffer)
63 bool bDirectory = false;
64 sal_uInt32 nSize = 0;
65 sal_uInt16 nYear = 0;
66 sal_uInt16 nMonth = 0;
67 sal_uInt16 nDay = 0;
68 sal_uInt16 nHour = 0;
69 sal_uInt16 nMinute = 0;
71 enum StateType
73 STATE_INIT_LWS,
74 STATE_MONTH_OR_SIZE,
75 STATE_1_DAY, STATE_1_YEAR, STATE_1_YEAR_LWS, STATE_1_HOUR,
76 STATE_1_MINUTE, STATE_1_MINUTE_LWS, STATE_1_AP,
77 STATE_1_APM, STATE_1_LESS, STATE_1_D, STATE_1_DI,
78 STATE_1_DIR, STATE_1_SIZE,
79 STATE_2_SIZE, STATE_2_SIZE_LWS, STATE_2_ATTRIB,
80 STATE_2_D, STATE_2_DI, STATE_2_DIR_LWS,
81 STATE_2_MONTH, STATE_2_DAY, STATE_2_YEAR, STATE_2_YEAR_LWS,
82 STATE_2_HOUR, STATE_2_MINUTE,
83 STATE_LWS_NAME,
84 STATE_ERROR
87 int nDigits = 0;
88 enum StateType eState = STATE_INIT_LWS;
89 for (const char *p = pBuffer;
90 eState != STATE_ERROR && *p;
91 ++p)
93 switch (eState)
95 case STATE_INIT_LWS:
96 if (*p >= '0' && *p <= '9')
98 nMonth = *p - '0';
99 nDigits = 1;
100 eState = STATE_MONTH_OR_SIZE;
102 else if (!ascii_isWhitespace(*p))
103 eState = STATE_ERROR;
104 break;
106 case STATE_MONTH_OR_SIZE:
107 if (*p >= '0' && *p <= '9')
109 nMonth = 10 * nMonth + (*p - '0');
110 if (nDigits < 2)
111 ++nDigits;
112 else
114 nSize = nMonth;
115 nMonth = 0;
116 eState = STATE_2_SIZE;
119 else if (ascii_isWhitespace(*p))
121 nSize = nMonth;
122 nMonth = 0;
123 eState = STATE_2_SIZE_LWS;
125 else if ((*p == '.' || *p == '-') && nMonth && nMonth <= 12)
127 nDigits = 0;
128 eState = STATE_1_DAY;
130 else
131 eState = STATE_ERROR;
132 break;
134 case STATE_1_DAY:
135 if (*p >= '0' && *p <= '9')
136 if (nDigits < 2)
138 nDay = 10 * nDay + (*p - '0');
139 ++nDigits;
141 else
142 eState = STATE_ERROR;
143 else if ((*p == '.' || *p == '-') && nDay && nDay <= 31)
145 nDigits = 0;
146 eState = STATE_1_YEAR;
148 else
149 eState = STATE_ERROR;
150 break;
152 case STATE_1_YEAR:
153 if (*p >= '0' && *p <= '9')
155 if (nDigits < 4)
157 nYear = 10 * nYear + (*p - '0');
158 ++nDigits;
160 else
161 eState = STATE_ERROR;
163 else
165 if (ascii_isWhitespace(*p))
166 eState = STATE_1_YEAR_LWS;
167 else
168 eState = STATE_ERROR;
170 break;
172 case STATE_1_YEAR_LWS:
173 if (*p >= '0' && *p <= '9')
175 nHour = *p - '0';
176 nDigits = 1;
177 eState = STATE_1_HOUR;
179 else if (!ascii_isWhitespace(*p))
180 eState = STATE_ERROR;
181 break;
183 case STATE_1_HOUR:
184 if (*p >= '0' && *p <= '9')
185 if (nDigits < 2)
187 nHour = 10 * nHour + (*p - '0');
188 ++nDigits;
190 else
191 eState = STATE_ERROR;
192 else if (*p == ':' && nHour < 24)
194 nDigits = 0;
195 eState = STATE_1_MINUTE;
197 else
198 eState = STATE_ERROR;
199 break;
201 case STATE_1_MINUTE:
202 if (*p >= '0' && *p <= '9')
203 if (nDigits < 2)
205 nMinute = 10 * nMinute + (*p - '0');
206 ++nDigits;
208 else
209 eState = STATE_ERROR;
210 else if ((*p == 'a' || *p == 'A') && nMinute < 60)
211 if (nHour >= 1 && nHour <= 11)
212 eState = STATE_1_AP;
213 else if (nHour == 12)
215 nHour = 0;
216 eState = STATE_1_AP;
218 else
219 eState = STATE_ERROR;
220 else if ((*p == 'p' || *p == 'P') && nMinute < 60)
221 if (nHour >= 1 && nHour <= 11)
223 nHour += 12;
224 eState = STATE_1_AP;
226 else if (nHour == 12)
227 eState = STATE_1_AP;
228 else
229 eState = STATE_ERROR;
230 else if (ascii_isWhitespace(*p) && (nMinute < 60))
231 eState = STATE_1_MINUTE_LWS;
232 else
233 eState = STATE_ERROR;
234 break;
236 case STATE_1_MINUTE_LWS:
237 if (*p == 'a' || *p == 'A')
238 if (nHour >= 1 && nHour <= 11)
239 eState = STATE_1_AP;
240 else if (nHour == 12)
242 nHour = 0;
243 eState = STATE_1_AP;
245 else
246 eState = STATE_ERROR;
247 else if (*p == 'p' || *p == 'P')
248 if (nHour >= 1 && nHour <= 11)
250 nHour += 12;
251 eState = STATE_1_AP;
253 else if (nHour == 12)
254 eState = STATE_1_AP;
255 else
256 eState = STATE_ERROR;
257 else if (*p == '<')
258 eState = STATE_1_LESS;
259 else if (*p >= '0' && *p <= '9')
261 nSize = *p - '0';
262 eState = STATE_1_SIZE;
264 else if (!ascii_isWhitespace(*p))
265 eState = STATE_ERROR;
266 break;
268 case STATE_1_AP:
269 eState = *p == 'm' || *p == 'M' ? STATE_1_APM : STATE_ERROR;
270 break;
272 case STATE_1_APM:
273 if (*p == '<')
274 eState = STATE_1_LESS;
275 else if (*p >= '0' && *p <= '9')
277 nSize = *p - '0';
278 eState = STATE_1_SIZE;
280 else if (!ascii_isWhitespace(*p))
281 eState = STATE_ERROR;
282 break;
284 case STATE_1_LESS:
285 eState = *p == 'd' || *p == 'D' ? STATE_1_D : STATE_ERROR;
286 break;
288 case STATE_1_D:
289 eState = *p == 'i' || *p == 'I' ? STATE_1_DI : STATE_ERROR;
290 break;
292 case STATE_1_DI:
293 eState = *p == 'r' || *p == 'R' ? STATE_1_DIR : STATE_ERROR;
294 break;
296 case STATE_1_DIR:
297 if (*p == '>')
299 bDirectory = true;
300 eState = STATE_LWS_NAME;
302 else
303 eState = STATE_ERROR;
304 break;
306 case STATE_1_SIZE:
307 if (*p >= '0' && *p <= '9')
308 nSize = 10 * nSize + (*p - '0');
309 else if (ascii_isWhitespace(*p))
310 eState = STATE_LWS_NAME;
311 else
312 eState = STATE_ERROR;
313 break;
315 case STATE_2_SIZE:
316 if (*p >= '0' && *p <= '9')
317 nSize = 10 * nSize + (*p - '0');
318 else if (ascii_isWhitespace(*p))
319 eState = STATE_2_SIZE_LWS;
320 else
321 eState = STATE_ERROR;
322 break;
324 case STATE_2_SIZE_LWS:
325 if (*p == 'd' || *p == 'D')
326 eState = STATE_2_D;
327 else if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))
328 eState = STATE_2_ATTRIB;
329 else if (*p >= '0' && *p <= '9')
331 nMonth = *p - '0';
332 nDigits = 1;
333 eState = STATE_2_MONTH;
335 else if (!ascii_isWhitespace(*p))
336 eState = STATE_ERROR;
337 break;
339 case STATE_2_ATTRIB:
340 if (ascii_isWhitespace(*p))
341 eState = STATE_2_SIZE_LWS;
342 else if ((*p < 'a' || *p > 'z') && (*p < 'A' || *p > 'Z'))
343 eState = STATE_ERROR;
344 break;
346 case STATE_2_D:
347 if (*p == 'i' || *p == 'I')
348 eState = STATE_2_DI;
349 else if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))
350 eState = STATE_2_ATTRIB;
351 else if (ascii_isWhitespace(*p))
352 eState = STATE_2_SIZE_LWS;
353 else
354 eState = STATE_ERROR;
355 break;
357 case STATE_2_DI:
358 if (*p == 'r' || *p == 'R')
360 bDirectory = true;
361 eState = STATE_2_DIR_LWS;
363 else
365 if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))
366 eState = STATE_2_ATTRIB;
367 else if (ascii_isWhitespace(*p))
368 eState = STATE_2_SIZE_LWS;
369 else
370 eState = STATE_ERROR;
372 break;
374 case STATE_2_DIR_LWS:
375 if (*p >= '0' && *p <= '9')
377 nMonth = *p - '0';
378 nDigits = 1;
379 eState = STATE_2_MONTH;
381 else if (!ascii_isWhitespace(*p))
382 eState = STATE_ERROR;
383 break;
385 case STATE_2_MONTH:
386 if (*p >= '0' && *p <= '9')
387 if (nDigits < 2)
389 nMonth = 10 * nMonth + (*p - '0');
390 ++nDigits;
392 else
393 eState = STATE_ERROR;
394 else if (*p == '-' && nMonth && nMonth <= 12)
396 nDigits = 0;
397 eState = STATE_2_DAY;
399 else
400 eState = STATE_ERROR;
401 break;
403 case STATE_2_DAY:
404 if (*p >= '0' && *p <= '9')
405 if (nDigits < 2)
407 nDay = 10 * nDay + (*p - '0');
408 ++nDigits;
410 else
411 eState = STATE_ERROR;
412 else if (*p == '-' && nDay && nDay <= 31)
414 nDigits = 0;
415 eState = STATE_2_YEAR;
417 else
418 eState = STATE_ERROR;
419 break;
421 case STATE_2_YEAR:
422 if (*p >= '0' && *p <= '9')
424 if (nDigits < 4)
426 nYear = 10 * nYear + (*p - '0');
427 ++nDigits;
429 else
430 eState = STATE_ERROR;
432 else
434 if (ascii_isWhitespace(*p))
435 eState = STATE_2_YEAR_LWS;
436 else
437 eState = STATE_ERROR;
439 break;
441 case STATE_2_YEAR_LWS:
442 if (*p >= '0' && *p <= '9')
444 nHour = *p - '0';
445 nDigits = 1;
446 eState = STATE_2_HOUR;
448 else if (!ascii_isWhitespace(*p))
449 eState = STATE_ERROR;
450 break;
452 case STATE_2_HOUR:
453 if (*p >= '0' && *p <= '9')
454 if (nDigits < 2)
456 nHour = 10 * nHour + (*p - '0');
457 ++nDigits;
459 else
460 eState = STATE_ERROR;
461 else if (*p == ':' && nHour < 24)
463 nDigits = 0;
464 eState = STATE_2_MINUTE;
466 else
467 eState = STATE_ERROR;
468 break;
470 case STATE_2_MINUTE:
471 if (*p >= '0' && *p <= '9')
473 if (nDigits < 2)
475 nMinute = 10 * nMinute + (*p - '0');
476 ++nDigits;
478 else
479 eState = STATE_ERROR;
481 else
483 if (ascii_isWhitespace(*p) && (nMinute < 60))
484 eState = STATE_LWS_NAME;
485 else
486 eState = STATE_ERROR;
488 break;
490 case STATE_LWS_NAME:
491 if (!ascii_isWhitespace(*p))
493 setPath (rEntry.m_aName, p);
494 if (bDirectory)
495 rEntry.m_nMode |= INETCOREFTP_FILEMODE_ISDIR;
496 rEntry.m_nSize = nSize;
498 setYear (rEntry.m_aDate, nYear);
500 rEntry.m_aDate.SetMonth(nMonth);
501 rEntry.m_aDate.SetDay(nDay);
502 rEntry.m_aDate.SetHour(nHour);
503 rEntry.m_aDate.SetMin(nMinute);
505 return true;
507 break;
508 case STATE_ERROR:
509 break;
513 return false;
517 * parseVMS.
518 * Directory entries may span one or two lines:
520 * entry: *lws name *1(*lws <NEWLINE>) 1*lws size 1*lws datetime rest
522 * name: filename "." filetype ";" version
523 * filename: 1*39fchar
524 * filetype: 1*39fchar
525 * version: non0digit *digit
527 * size: "0" / non0digit *digit
529 * datetime: date 1*lwsp time
530 * date: day "-" month "-" year
531 * day: (*1"0" non0digit) / ("1"-"2" digit) / ("3" "0"-"1")
532 * month: "JAN" / "FEB" / "MAR" / "APR" / "MAY" / "JUN" / "JUL" / "AUG"
533 * / "SEP" / "OCT" / "NOV" / "DEC" ; all case insensitive
534 * year: 2digit / 4digit
535 * time: hour ":" minute
536 * hour: ((*1"0" / "1") digit) / ("2" "0"-"3")
537 * minute: "0"-"5" digit
539 * rest: *1(lws *<ANY>)
541 * lws: <TAB> / <SPACE>
542 * non0digit: "1"-"9"
543 * digit: "0" / non0digit
544 * fchar: "A"-"Z" / "a"-"z" / digit / "-" / "_" / "$"
546 * For directories, the returned name is the <filename> part; for non-
547 * directory files, the returned name is the <filename "." filetype> part.
548 * An entry is a directory iff its filetype is "DIR" (ignoring case).
550 * The READ, WRITE, and ISLINK mode bits are not supported.
552 * The returned size is the <size> part, multiplied by 512, and with the high
553 * order bits truncated to fit into a sal_uInt32.
556 bool FTPDirectoryParser::parseVMS (
557 FTPDirentry &rEntry,
558 const char *pBuffer)
560 static OUString aFirstLineName;
561 static bool bFirstLineDir = false;
563 for (bool bFirstLine = true;; bFirstLine = false)
565 const char *p = pBuffer;
566 if (bFirstLine)
568 // Skip <*lws> part:
569 while (*p == '\t' || *p == ' ')
570 ++p;
572 // Parse <filename "."> part:
573 const char *pFileName = p;
574 while ((*p >= 'A' && *p <= 'Z') ||
575 (*p >= 'a' && *p <= 'z') ||
576 (*p >= '0' && *p <= '9') ||
577 *p == '-' || *p == '_' || *p == '$')
578 ++p;
580 if (*p != '.' || p == pFileName || p - pFileName > 39)
582 if (!aFirstLineName.isEmpty())
583 continue;
584 else
585 return false;
588 // Parse <filetype ";"> part:
589 const char *pFileType = ++p;
590 while ((*p >= 'A' && *p <= 'Z') ||
591 (*p >= 'a' && *p <= 'z') ||
592 (*p >= '0' && *p <= '9') ||
593 *p == '-' || *p == '_' || *p == '$')
594 ++p;
596 if (*p != ';' || p == pFileName || p - pFileName > 39)
598 if (!aFirstLineName.isEmpty())
599 continue;
600 else
601 return false;
603 ++p;
605 // Set entry's name and mode (ISDIR flag):
606 if ((p - pFileType == 4) &&
607 (pFileType[0] == 'D' || pFileType[0] == 'd') &&
608 (pFileType[1] == 'I' || pFileType[1] == 'i') &&
609 (pFileType[2] == 'R' || pFileType[2] == 'r') )
611 setPath (rEntry.m_aName, pFileName, (pFileType - pFileName));
612 rEntry.m_nMode = INETCOREFTP_FILEMODE_ISDIR;
614 else
616 setPath (rEntry.m_aName, pFileName, (p - pFileName));
617 rEntry.m_nMode = 0;
620 // Skip <version> part:
621 if (*p < '1' || *p > '9')
623 if (!aFirstLineName.isEmpty())
624 continue;
625 else
626 return false;
628 ++p;
629 while (*p >= '0' && *p <= '9')
630 ++p;
632 // Parse <1*lws> or <*lws <NEWLINE>> part:
633 bool bLWS = false;
634 while (*p == '\t' || *p == ' ')
636 bLWS = true;
637 ++p;
639 if (*p)
641 if (!bLWS)
643 if (!aFirstLineName.isEmpty())
644 continue;
645 else
646 return false;
649 else
652 * First line of entry spanning two lines,
653 * wait for second line.
655 aFirstLineName = rEntry.m_aName;
656 bFirstLineDir =
657 ((rEntry.m_nMode & INETCOREFTP_FILEMODE_ISDIR) != 0);
658 return false;
661 else
664 * Second line of entry spanning two lines,
665 * restore entry's name and mode (ISDIR flag).
667 rEntry.m_aName = aFirstLineName;
668 rEntry.m_nMode = (bFirstLineDir ? INETCOREFTP_FILEMODE_ISDIR : 0);
670 // Skip <1*lws> part:
671 if (*p != '\t' && *p != ' ')
672 return false;
673 ++p;
674 while (*p == '\t' || *p == ' ')
675 ++p;
678 // Parse <size> part and set entry's size:
679 if (*p < '0' || *p > '9')
680 return false;
681 sal_uInt32 nSize = *p - '0';
682 if (*p++ != '0')
683 while (*p >= '0' && *p <= '9')
684 nSize = 10 * rEntry.m_nSize + (*p++ - '0');
685 rEntry.m_nSize = 512 * nSize;
687 // Skip <1*lws> part:
688 if (*p != '\t' && *p != ' ')
689 return false;
690 ++p;
691 while (*p == '\t' || *p == ' ')
692 ++p;
694 // Parse <day "-"> part and set entry date's day:
695 sal_uInt16 nDay;
696 if (*p == '0')
698 ++p;
699 if (*p < '1' || *p > '9')
700 return false;
701 nDay = *p++ - '0';
703 else if (*p == '1' || *p == '2')
705 nDay = *p++ - '0';
706 if (*p >= '0' && *p <= '9')
707 nDay = 10 * nDay + (*p++ - '0');
709 else if (*p == '3')
711 ++p;
712 nDay = (*p == '0' || *p == '1') ? 30 + (*p++ - '0') : 3;
714 else if (*p >= '4' && *p <= '9')
715 nDay = *p++ - '0';
716 else
717 return false;
719 rEntry.m_aDate.SetDay(nDay);
720 if (*p++ != '-')
721 return false;
723 // Parse <month "-"> part and set entry date's month:
724 char const * pMonth = p;
725 sal_Int32 const monthLen = 3;
726 for (int i = 0; i < monthLen; ++i)
728 if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z')))
729 return false;
730 ++p;
732 if (rtl_str_compareIgnoreAsciiCase_WithLength(
733 pMonth, monthLen, "JAN", monthLen) == 0)
734 rEntry.m_aDate.SetMonth(1);
735 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
736 pMonth, monthLen, "FEB", monthLen) == 0)
737 rEntry.m_aDate.SetMonth(2);
738 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
739 pMonth, monthLen, "MAR", monthLen) == 0)
740 rEntry.m_aDate.SetMonth(3);
741 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
742 pMonth, monthLen, "APR", monthLen) == 0)
743 rEntry.m_aDate.SetMonth(4);
744 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
745 pMonth, monthLen, "MAY", monthLen) == 0)
746 rEntry.m_aDate.SetMonth(5);
747 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
748 pMonth, monthLen, "JUN", monthLen) == 0)
749 rEntry.m_aDate.SetMonth(6);
750 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
751 pMonth, monthLen, "JUL", monthLen) == 0)
752 rEntry.m_aDate.SetMonth(7);
753 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
754 pMonth, monthLen, "AUG", monthLen) == 0)
755 rEntry.m_aDate.SetMonth(8);
756 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
757 pMonth, monthLen, "SEP", monthLen) == 0)
758 rEntry.m_aDate.SetMonth(9);
759 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
760 pMonth, monthLen, "OCT", monthLen) == 0)
761 rEntry.m_aDate.SetMonth(10);
762 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
763 pMonth, monthLen, "NOV", monthLen) == 0)
764 rEntry.m_aDate.SetMonth(11);
765 else if (rtl_str_compareIgnoreAsciiCase_WithLength(
766 pMonth, monthLen, "DEC", monthLen) == 0)
767 rEntry.m_aDate.SetMonth(12);
768 else
769 return false;
770 if (*p++ != '-')
771 return false;
773 // Parse <year> part and set entry date's year:
774 sal_uInt16 nYear = 0;
775 for (int i = 0; i < 2; ++i)
777 if (*p < '0' || *p > '9')
778 return false;
779 nYear = 10 * nYear + (*p++ - '0');
781 if (*p >= '0' && *p <= '9')
783 nYear = 10 * nYear + (*p++ - '0');
784 if (*p < '0' || *p > '9')
785 return false;
786 nYear = 10 * nYear + (*p++ - '0');
788 setYear (rEntry.m_aDate, nYear);
790 // Skip <1*lws> part:
791 if (*p != '\t' && *p != ' ')
792 return false;
793 ++p;
794 while (*p == '\t' || *p == ' ')
795 ++p;
797 // Parse <hour ":"> part and set entry time's hour:
798 sal_uInt16 nHour;
799 if (*p == '0' || *p == '1')
801 nHour = *p++ - '0';
802 if (*p >= '0' && *p <= '9')
803 nHour = 10 * nHour + (*p++ - '0');
805 else if (*p == '2')
807 ++p;
808 nHour = (*p >= '0' && *p <= '3') ? 20 + (*p++ - '0') : 2;
810 else if (*p >= '3' && *p <= '9')
811 nHour = *p++ - '0';
812 else
813 return false;
815 rEntry.m_aDate.SetHour(nHour);
816 if (*p++ != ':')
817 return false;
820 * Parse <minute> part and set entry time's minutes,
821 * seconds (0), and nanoseconds (0).
823 if (*p < '0' || *p > '5')
824 return false;
826 sal_uInt16 nMinute = *p++ - '0';
827 if (*p < '0' || *p > '9')
828 return false;
830 nMinute = 10 * nMinute + (*p++ - '0');
831 rEntry.m_aDate.SetMin(nMinute);
832 rEntry.m_aDate.SetSec(0);
833 rEntry.m_aDate.SetNanoSec(0);
835 // Skip <rest> part:
836 return !*p || *p == '\t' || *p == ' ';
841 * parseUNIX
843 bool FTPDirectoryParser::parseUNIX (
844 FTPDirentry &rEntry,
845 const char *pBuffer)
847 const char *p1, *p2;
848 p1 = pBuffer;
850 if (!((*p1 == '-') || (*p1 == 'd') || (*p1 == 'l')))
851 return false;
853 // 1st column: FileMode.
854 if (*p1 == 'd')
855 rEntry.m_nMode |= INETCOREFTP_FILEMODE_ISDIR;
857 if (*p1 == 'l')
858 rEntry.m_nMode |= INETCOREFTP_FILEMODE_ISLINK;
860 // Skip to end of column and set rights by the way
861 while (*p1 && !ascii_isWhitespace(*p1)) {
862 if(*p1 == 'r')
863 rEntry.m_nMode |= INETCOREFTP_FILEMODE_READ;
864 else if(*p1 == 'w')
865 rEntry.m_nMode |= INETCOREFTP_FILEMODE_WRITE;
866 p1++;
870 * Scan for the sequence of size and date fields:
871 * *LWS 1*DIGIT 1*LWS 3CHAR 1*LWS 1*2DIGIT 1*LWS
872 * (4DIGIT / (1*2DIGIT ":" 2DIGIT)) 1*LWS
874 enum Mode
876 FOUND_NONE, FOUND_SIZE, FOUND_MONTH, FOUND_DAY, FOUND_YEAR_TIME
879 const char *pDayStart = nullptr;
880 const char *pDayEnd = nullptr;
881 Mode eMode;
882 for (eMode = FOUND_NONE; *p1 && eMode != FOUND_YEAR_TIME; p1 = p2 + 1)
884 while (*p1 && ascii_isWhitespace(*p1))
885 ++p1;
886 p2 = p1;
887 while (*p2 && !ascii_isWhitespace(*p2))
888 ++p2;
890 switch (eMode)
892 case FOUND_NONE:
893 if (parseUNIX_isSizeField (p1, p2, rEntry.m_nSize))
894 eMode = FOUND_SIZE;
895 break;
897 case FOUND_SIZE:
898 if (parseUNIX_isMonthField (p1, p2, rEntry.m_aDate))
899 eMode = FOUND_MONTH;
900 else if (!parseUNIX_isSizeField (p1, p2, rEntry.m_nSize))
901 eMode = FOUND_NONE;
902 break;
904 case FOUND_MONTH:
905 if (parseUNIX_isDayField (p1, p2, rEntry.m_aDate))
907 pDayStart = p1;
908 pDayEnd = p2;
909 eMode = FOUND_DAY;
911 else if (parseUNIX_isSizeField (p1, p2, rEntry.m_nSize))
912 eMode = FOUND_SIZE;
913 else
914 eMode = FOUND_NONE;
915 break;
917 case FOUND_DAY:
918 if (parseUNIX_isYearTimeField (p1, p2, rEntry.m_aDate))
919 eMode = FOUND_YEAR_TIME;
920 else if (
921 parseUNIX_isSizeField (
922 pDayStart, pDayEnd, rEntry.m_nSize) &&
923 parseUNIX_isMonthField (
924 p1, p2, rEntry.m_aDate))
925 eMode = FOUND_MONTH;
926 else if (parseUNIX_isSizeField (p1, p2, rEntry.m_nSize))
927 eMode = FOUND_SIZE;
928 else
929 eMode = FOUND_NONE;
930 break;
931 // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
932 case FOUND_YEAR_TIME:
933 break;
937 if (eMode == FOUND_YEAR_TIME)
939 // 9th column: FileName (rest of line).
940 while (*p1 && ascii_isWhitespace(*p1)) p1++;
941 setPath (rEntry.m_aName, p1);
943 // Done.
944 return true;
946 return false;
950 * parseUNIX_isSizeField.
952 bool FTPDirectoryParser::parseUNIX_isSizeField (
953 const char *pStart,
954 const char *pEnd,
955 sal_uInt32 &rSize)
957 if (!*pStart || !*pEnd || pStart == pEnd)
958 return false;
960 rSize = 0;
961 if (*pStart >= '0' && *pStart <= '9')
963 for (; pStart < pEnd; ++pStart)
964 if ((*pStart >= '0') && (*pStart <= '9'))
965 rSize = 10 * rSize + (*pStart - '0');
966 else
967 return false;
968 return true;
970 else
973 * For a combination of long group name and large file size,
974 * some FTPDs omit LWS between those two columns.
976 int nNonDigits = 0;
977 int nDigits = 0;
979 for (; pStart < pEnd; ++pStart)
980 if ((*pStart >= '1') && (*pStart <= '9'))
982 ++nDigits;
983 rSize = 10 * rSize + (*pStart - '0');
985 else if ((*pStart == '0') && nDigits)
987 ++nDigits;
988 rSize *= 10;
990 else if ((*pStart > ' ') && (sal::static_int_cast<sal_uInt8>(*pStart) <= '\x7F'))
992 nNonDigits += nDigits + 1;
993 nDigits = 0;
994 rSize = 0;
996 else
997 return false;
998 return ((nNonDigits >= 9) && (nDigits >= 7));
1003 * parseUNIX_isMonthField.
1005 bool FTPDirectoryParser::parseUNIX_isMonthField (
1006 const char *pStart,
1007 const char *pEnd,
1008 DateTime &rDateTime)
1010 if (!*pStart || !*pEnd || pStart + 3 != pEnd)
1011 return false;
1013 if ((pStart[0] == 'j' || pStart[0] == 'J') &&
1014 (pStart[1] == 'a' || pStart[1] == 'A') &&
1015 (pStart[2] == 'n' || pStart[2] == 'N') )
1017 rDateTime.SetMonth(1);
1018 return true;
1020 if ((pStart[0] == 'f' || pStart[0] == 'F') &&
1021 (pStart[1] == 'e' || pStart[1] == 'E') &&
1022 (pStart[2] == 'b' || pStart[2] == 'B') )
1024 rDateTime.SetMonth(2);
1025 return true;
1027 if ((pStart[0] == 'm' || pStart[0] == 'M') &&
1028 (pStart[1] == 'a' || pStart[1] == 'A') &&
1029 (pStart[2] == 'r' || pStart[2] == 'R') )
1031 rDateTime.SetMonth(3);
1032 return true;
1034 if ((pStart[0] == 'a' || pStart[0] == 'A') &&
1035 (pStart[1] == 'p' || pStart[1] == 'P') &&
1036 (pStart[2] == 'r' || pStart[2] == 'R') )
1038 rDateTime.SetMonth(4);
1039 return true;
1041 if ((pStart[0] == 'm' || pStart[0] == 'M') &&
1042 (pStart[1] == 'a' || pStart[1] == 'A') &&
1043 (pStart[2] == 'y' || pStart[2] == 'Y') )
1045 rDateTime.SetMonth(5);
1046 return true;
1048 if ((pStart[0] == 'j' || pStart[0] == 'J') &&
1049 (pStart[1] == 'u' || pStart[1] == 'U') &&
1050 (pStart[2] == 'n' || pStart[2] == 'N') )
1052 rDateTime.SetMonth(6);
1053 return true;
1055 if ((pStart[0] == 'j' || pStart[0] == 'J') &&
1056 (pStart[1] == 'u' || pStart[1] == 'U') &&
1057 (pStart[2] == 'l' || pStart[2] == 'L') )
1059 rDateTime.SetMonth(7);
1060 return true;
1062 if ((pStart[0] == 'a' || pStart[0] == 'A') &&
1063 (pStart[1] == 'u' || pStart[1] == 'U') &&
1064 (pStart[2] == 'g' || pStart[2] == 'G') )
1066 rDateTime.SetMonth(8);
1067 return true;
1069 if ((pStart[0] == 's' || pStart[0] == 'S') &&
1070 (pStart[1] == 'e' || pStart[1] == 'E') &&
1071 (pStart[2] == 'p' || pStart[2] == 'P') )
1073 rDateTime.SetMonth(9);
1074 return true;
1076 if ((pStart[0] == 'o' || pStart[0] == 'O') &&
1077 (pStart[1] == 'c' || pStart[1] == 'C') &&
1078 (pStart[2] == 't' || pStart[2] == 'T') )
1080 rDateTime.SetMonth(10);
1081 return true;
1083 if ((pStart[0] == 'n' || pStart[0] == 'N') &&
1084 (pStart[1] == 'o' || pStart[1] == 'O') &&
1085 (pStart[2] == 'v' || pStart[2] == 'V') )
1087 rDateTime.SetMonth(11);
1088 return true;
1090 if ((pStart[0] == 'd' || pStart[0] == 'D') &&
1091 (pStart[1] == 'e' || pStart[1] == 'E') &&
1092 (pStart[2] == 'c' || pStart[2] == 'C') )
1094 rDateTime.SetMonth(12);
1095 return true;
1097 return false;
1101 * parseUNIX_isDayField.
1103 bool FTPDirectoryParser::parseUNIX_isDayField (
1104 const char *pStart,
1105 const char *pEnd,
1106 DateTime &rDateTime)
1108 if (!*pStart || !*pEnd || pStart == pEnd)
1109 return false;
1110 if (*pStart < '0' || *pStart > '9')
1111 return false;
1113 sal_uInt16 nDay = *pStart - '0';
1114 if (pStart + 1 < pEnd)
1116 if (pStart + 2 != pEnd || pStart[1] < '0' || pStart[1] > '9')
1117 return false;
1118 nDay = 10 * nDay + (pStart[1] - '0');
1120 if (!nDay || nDay > 31)
1121 return false;
1123 rDateTime.SetDay(nDay);
1124 return true;
1128 * parseUNIX_isYearTimeField.
1130 bool FTPDirectoryParser::parseUNIX_isYearTimeField (
1131 const char *pStart,
1132 const char *pEnd,
1133 DateTime &rDateTime)
1135 if (!*pStart || !*pEnd || pStart == pEnd ||
1136 *pStart < '0' || *pStart > '9')
1137 return false;
1139 sal_uInt16 nNumber = *pStart - '0';
1140 ++pStart;
1142 if (pStart == pEnd)
1143 return false;
1144 if (*pStart == ':')
1145 return parseUNIX_isTime (pStart, pEnd, nNumber, rDateTime);
1146 if (*pStart < '0' || *pStart > '9')
1147 return false;
1149 nNumber = 10 * nNumber + (*pStart - '0');
1150 ++pStart;
1152 if (pStart == pEnd)
1153 return false;
1154 if (*pStart == ':')
1155 return parseUNIX_isTime (pStart, pEnd, nNumber, rDateTime);
1156 if (*pStart < '0' || *pStart > '9')
1157 return false;
1159 nNumber = 10 * nNumber + (*pStart - '0');
1160 ++pStart;
1162 if (pStart == pEnd || *pStart < '0' || *pStart > '9')
1163 return false;
1165 nNumber = 10 * nNumber + (*pStart - '0');
1166 if (pStart + 1 != pEnd || nNumber < 1970)
1167 return false;
1169 rDateTime.SetYear(nNumber);
1170 rDateTime.SetTime();
1171 return true;
1175 * parseUNIX_isTime.
1177 bool FTPDirectoryParser::parseUNIX_isTime (
1178 const char *pStart,
1179 const char *pEnd,
1180 sal_uInt16 nHour,
1181 DateTime &rDateTime)
1183 if ((nHour > 23 ) || (pStart + 3 != pEnd) ||
1184 (pStart[1] < '0') || (pStart[1] > '5') ||
1185 (pStart[2] < '0') || (pStart[2] > '9') )
1186 return false;
1188 sal_uInt16 nMin = 10 * (pStart[1] - '0') + (pStart[2] - '0');
1190 rDateTime.SetHour (nHour);
1191 rDateTime.SetMin (nMin);
1192 rDateTime.SetSec (0);
1193 rDateTime.SetNanoSec (0);
1195 // Date aCurDate;
1196 // if (rDateTime.GetMonth() > aCurDate.GetMonth())
1197 // rDateTime.SetYear(aCurDate.GetYear() - 1);
1198 // else
1199 // rDateTime.SetYear(aCurDate.GetYear());
1200 // return sal_True;
1202 TimeValue aTimeVal;
1203 osl_getSystemTime(&aTimeVal);
1204 oslDateTime aCurrDateTime;
1205 osl_getDateTimeFromTimeValue(&aTimeVal,&aCurrDateTime);
1207 if (rDateTime.GetMonth() > aCurrDateTime.Month)
1208 rDateTime.SetYear(aCurrDateTime.Year - 1);
1209 else
1210 rDateTime.SetYear(aCurrDateTime.Year);
1211 return true;
1215 * setYear.
1217 * Two-digit years are taken as within 50 years back and 49 years forward
1218 * (both ends inclusive) from the current year. The returned date is not
1219 * checked for validity of the given day in the given month and year.
1222 void FTPDirectoryParser::setYear (
1223 DateTime &rDateTime, sal_uInt16 nYear)
1225 if (nYear < 100)
1227 TimeValue aTimeVal;
1228 osl_getSystemTime(&aTimeVal);
1229 oslDateTime aCurrDateTime;
1230 osl_getDateTimeFromTimeValue(&aTimeVal,&aCurrDateTime);
1231 sal_uInt16 nCurrentYear = aCurrDateTime.Year;
1232 // sal_uInt16 nCurrentYear = Date().GetYear();
1233 sal_uInt16 nCurrentCentury = nCurrentYear / 100;
1234 nCurrentYear %= 100;
1235 if (nCurrentYear < 50)
1236 if (nYear <= nCurrentYear)
1237 nYear += nCurrentCentury * 100;
1238 else if (nYear < nCurrentYear + 50)
1239 nYear += nCurrentCentury * 100;
1240 else
1241 nYear += (nCurrentCentury - 1) * 100;
1242 else
1243 if (nYear >= nCurrentYear)
1244 nYear += nCurrentCentury * 100;
1245 else if (nYear >= nCurrentYear - 50)
1246 nYear += nCurrentCentury * 100;
1247 else
1248 nYear += (nCurrentCentury + 1) * 100;
1251 rDateTime.SetYear(nYear);
1255 * setPath.
1257 bool FTPDirectoryParser::setPath (
1258 OUString &rPath, const char *value, sal_Int32 length)
1260 if (value)
1262 if (length < 0)
1263 length = rtl_str_getLength (value);
1264 rPath = OUString (value, length, RTL_TEXTENCODING_UTF8);
1266 return (!!value);
1269 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */