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