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