Coverage Report

Created: 2022-12-01 06:42

/src/curl/lib/ftplistparser.c
Line
Count
Source (jump to first uncovered line)
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
25
/**
26
 * Now implemented:
27
 *
28
 * 1) Unix version 1
29
 * drwxr-xr-x 1 user01 ftp  512 Jan 29 23:32 prog
30
 * 2) Unix version 2
31
 * drwxr-xr-x 1 user01 ftp  512 Jan 29 1997  prog
32
 * 3) Unix version 3
33
 * drwxr-xr-x 1      1   1  512 Jan 29 23:32 prog
34
 * 4) Unix symlink
35
 * lrwxr-xr-x 1 user01 ftp  512 Jan 29 23:32 prog -> prog2000
36
 * 5) DOS style
37
 * 01-29-97 11:32PM <DIR> prog
38
 */
39
40
#include "curl_setup.h"
41
42
#ifndef CURL_DISABLE_FTP
43
44
#include <curl/curl.h>
45
46
#include "urldata.h"
47
#include "fileinfo.h"
48
#include "llist.h"
49
#include "strtoofft.h"
50
#include "ftp.h"
51
#include "ftplistparser.h"
52
#include "curl_fnmatch.h"
53
#include "curl_memory.h"
54
#include "multiif.h"
55
/* The last #include file should be: */
56
#include "memdebug.h"
57
58
/* allocs buffer which will contain one line of LIST command response */
59
0
#define FTP_BUFFER_ALLOCSIZE 160
60
61
typedef enum {
62
  PL_UNIX_TOTALSIZE = 0,
63
  PL_UNIX_FILETYPE,
64
  PL_UNIX_PERMISSION,
65
  PL_UNIX_HLINKS,
66
  PL_UNIX_USER,
67
  PL_UNIX_GROUP,
68
  PL_UNIX_SIZE,
69
  PL_UNIX_TIME,
70
  PL_UNIX_FILENAME,
71
  PL_UNIX_SYMLINK
72
} pl_unix_mainstate;
73
74
typedef union {
75
  enum {
76
    PL_UNIX_TOTALSIZE_INIT = 0,
77
    PL_UNIX_TOTALSIZE_READING
78
  } total_dirsize;
79
80
  enum {
81
    PL_UNIX_HLINKS_PRESPACE = 0,
82
    PL_UNIX_HLINKS_NUMBER
83
  } hlinks;
84
85
  enum {
86
    PL_UNIX_USER_PRESPACE = 0,
87
    PL_UNIX_USER_PARSING
88
  } user;
89
90
  enum {
91
    PL_UNIX_GROUP_PRESPACE = 0,
92
    PL_UNIX_GROUP_NAME
93
  } group;
94
95
  enum {
96
    PL_UNIX_SIZE_PRESPACE = 0,
97
    PL_UNIX_SIZE_NUMBER
98
  } size;
99
100
  enum {
101
    PL_UNIX_TIME_PREPART1 = 0,
102
    PL_UNIX_TIME_PART1,
103
    PL_UNIX_TIME_PREPART2,
104
    PL_UNIX_TIME_PART2,
105
    PL_UNIX_TIME_PREPART3,
106
    PL_UNIX_TIME_PART3
107
  } time;
108
109
  enum {
110
    PL_UNIX_FILENAME_PRESPACE = 0,
111
    PL_UNIX_FILENAME_NAME,
112
    PL_UNIX_FILENAME_WINDOWSEOL
113
  } filename;
114
115
  enum {
116
    PL_UNIX_SYMLINK_PRESPACE = 0,
117
    PL_UNIX_SYMLINK_NAME,
118
    PL_UNIX_SYMLINK_PRETARGET1,
119
    PL_UNIX_SYMLINK_PRETARGET2,
120
    PL_UNIX_SYMLINK_PRETARGET3,
121
    PL_UNIX_SYMLINK_PRETARGET4,
122
    PL_UNIX_SYMLINK_TARGET,
123
    PL_UNIX_SYMLINK_WINDOWSEOL
124
  } symlink;
125
} pl_unix_substate;
126
127
typedef enum {
128
  PL_WINNT_DATE = 0,
129
  PL_WINNT_TIME,
130
  PL_WINNT_DIRORSIZE,
131
  PL_WINNT_FILENAME
132
} pl_winNT_mainstate;
133
134
typedef union {
135
  enum {
136
    PL_WINNT_TIME_PRESPACE = 0,
137
    PL_WINNT_TIME_TIME
138
  } time;
139
  enum {
140
    PL_WINNT_DIRORSIZE_PRESPACE = 0,
141
    PL_WINNT_DIRORSIZE_CONTENT
142
  } dirorsize;
143
  enum {
144
    PL_WINNT_FILENAME_PRESPACE = 0,
145
    PL_WINNT_FILENAME_CONTENT,
146
    PL_WINNT_FILENAME_WINEOL
147
  } filename;
148
} pl_winNT_substate;
149
150
/* This struct is used in wildcard downloading - for parsing LIST response */
151
struct ftp_parselist_data {
152
  enum {
153
    OS_TYPE_UNKNOWN = 0,
154
    OS_TYPE_UNIX,
155
    OS_TYPE_WIN_NT
156
  } os_type;
157
158
  union {
159
    struct {
160
      pl_unix_mainstate main;
161
      pl_unix_substate sub;
162
    } UNIX;
163
164
    struct {
165
      pl_winNT_mainstate main;
166
      pl_winNT_substate sub;
167
    } NT;
168
  } state;
169
170
  CURLcode error;
171
  struct fileinfo *file_data;
172
  unsigned int item_length;
173
  size_t item_offset;
174
  struct {
175
    size_t filename;
176
    size_t user;
177
    size_t group;
178
    size_t time;
179
    size_t perm;
180
    size_t symlink_target;
181
  } offsets;
182
};
183
184
struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void)
185
0
{
186
0
  return calloc(1, sizeof(struct ftp_parselist_data));
187
0
}
188
189
190
void Curl_ftp_parselist_data_free(struct ftp_parselist_data **parserp)
191
0
{
192
0
  struct ftp_parselist_data *parser = *parserp;
193
0
  if(parser)
194
0
    Curl_fileinfo_cleanup(parser->file_data);
195
0
  free(parser);
196
0
  *parserp = NULL;
197
0
}
198
199
200
CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data)
201
0
{
202
0
  return pl_data->error;
203
0
}
204
205
206
0
#define FTP_LP_MALFORMATED_PERM 0x01000000
207
208
static unsigned int ftp_pl_get_permission(const char *str)
209
0
{
210
0
  unsigned int permissions = 0;
211
  /* USER */
212
0
  if(str[0] == 'r')
213
0
    permissions |= 1 << 8;
214
0
  else if(str[0] != '-')
215
0
    permissions |= FTP_LP_MALFORMATED_PERM;
216
0
  if(str[1] == 'w')
217
0
    permissions |= 1 << 7;
218
0
  else if(str[1] != '-')
219
0
    permissions |= FTP_LP_MALFORMATED_PERM;
220
221
0
  if(str[2] == 'x')
222
0
    permissions |= 1 << 6;
223
0
  else if(str[2] == 's') {
224
0
    permissions |= 1 << 6;
225
0
    permissions |= 1 << 11;
226
0
  }
227
0
  else if(str[2] == 'S')
228
0
    permissions |= 1 << 11;
229
0
  else if(str[2] != '-')
230
0
    permissions |= FTP_LP_MALFORMATED_PERM;
231
  /* GROUP */
232
0
  if(str[3] == 'r')
233
0
    permissions |= 1 << 5;
234
0
  else if(str[3] != '-')
235
0
    permissions |= FTP_LP_MALFORMATED_PERM;
236
0
  if(str[4] == 'w')
237
0
    permissions |= 1 << 4;
238
0
  else if(str[4] != '-')
239
0
    permissions |= FTP_LP_MALFORMATED_PERM;
240
0
  if(str[5] == 'x')
241
0
    permissions |= 1 << 3;
242
0
  else if(str[5] == 's') {
243
0
    permissions |= 1 << 3;
244
0
    permissions |= 1 << 10;
245
0
  }
246
0
  else if(str[5] == 'S')
247
0
    permissions |= 1 << 10;
248
0
  else if(str[5] != '-')
249
0
    permissions |= FTP_LP_MALFORMATED_PERM;
250
  /* others */
251
0
  if(str[6] == 'r')
252
0
    permissions |= 1 << 2;
253
0
  else if(str[6] != '-')
254
0
    permissions |= FTP_LP_MALFORMATED_PERM;
255
0
  if(str[7] == 'w')
256
0
    permissions |= 1 << 1;
257
0
  else if(str[7] != '-')
258
0
      permissions |= FTP_LP_MALFORMATED_PERM;
259
0
  if(str[8] == 'x')
260
0
    permissions |= 1;
261
0
  else if(str[8] == 't') {
262
0
    permissions |= 1;
263
0
    permissions |= 1 << 9;
264
0
  }
265
0
  else if(str[8] == 'T')
266
0
    permissions |= 1 << 9;
267
0
  else if(str[8] != '-')
268
0
    permissions |= FTP_LP_MALFORMATED_PERM;
269
270
0
  return permissions;
271
0
}
272
273
static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data,
274
                                    struct fileinfo *infop)
275
0
{
276
0
  curl_fnmatch_callback compare;
277
0
  struct WildcardData *wc = &data->wildcard;
278
0
  struct ftp_wc *ftpwc = wc->protdata;
279
0
  struct Curl_llist *llist = &wc->filelist;
280
0
  struct ftp_parselist_data *parser = ftpwc->parser;
281
0
  bool add = TRUE;
282
0
  struct curl_fileinfo *finfo = &infop->info;
283
284
  /* move finfo pointers to b_data */
285
0
  char *str = finfo->b_data;
286
0
  finfo->filename       = str + parser->offsets.filename;
287
0
  finfo->strings.group  = parser->offsets.group ?
288
0
                          str + parser->offsets.group : NULL;
289
0
  finfo->strings.perm   = parser->offsets.perm ?
290
0
                          str + parser->offsets.perm : NULL;
291
0
  finfo->strings.target = parser->offsets.symlink_target ?
292
0
                          str + parser->offsets.symlink_target : NULL;
293
0
  finfo->strings.time   = str + parser->offsets.time;
294
0
  finfo->strings.user   = parser->offsets.user ?
295
0
                          str + parser->offsets.user : NULL;
296
297
  /* get correct fnmatch callback */
298
0
  compare = data->set.fnmatch;
299
0
  if(!compare)
300
0
    compare = Curl_fnmatch;
301
302
  /* filter pattern-corresponding filenames */
303
0
  Curl_set_in_callback(data, true);
304
0
  if(compare(data->set.fnmatch_data, wc->pattern,
305
0
             finfo->filename) == 0) {
306
    /* discard symlink which is containing multiple " -> " */
307
0
    if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target &&
308
0
       (strstr(finfo->strings.target, " -> "))) {
309
0
      add = FALSE;
310
0
    }
311
0
  }
312
0
  else {
313
0
    add = FALSE;
314
0
  }
315
0
  Curl_set_in_callback(data, false);
316
317
0
  if(add) {
318
0
    Curl_llist_insert_next(llist, llist->tail, finfo, &infop->list);
319
0
  }
320
0
  else {
321
0
    Curl_fileinfo_cleanup(infop);
322
0
  }
323
324
0
  ftpwc->parser->file_data = NULL;
325
0
  return CURLE_OK;
326
0
}
327
328
size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
329
                          void *connptr)
330
0
{
331
0
  size_t bufflen = size*nmemb;
332
0
  struct Curl_easy *data = (struct Curl_easy *)connptr;
333
0
  struct ftp_wc *ftpwc = data->wildcard.protdata;
334
0
  struct ftp_parselist_data *parser = ftpwc->parser;
335
0
  struct fileinfo *infop;
336
0
  struct curl_fileinfo *finfo;
337
0
  unsigned long i = 0;
338
0
  CURLcode result;
339
0
  size_t retsize = bufflen;
340
341
0
  if(parser->error) { /* error in previous call */
342
    /* scenario:
343
     * 1. call => OK..
344
     * 2. call => OUT_OF_MEMORY (or other error)
345
     * 3. (last) call => is skipped RIGHT HERE and the error is hadled later
346
     *    in wc_statemach()
347
     */
348
0
    goto fail;
349
0
  }
350
351
0
  if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) {
352
    /* considering info about FILE response format */
353
0
    parser->os_type = (buffer[0] >= '0' && buffer[0] <= '9') ?
354
0
                       OS_TYPE_WIN_NT : OS_TYPE_UNIX;
355
0
  }
356
357
0
  while(i < bufflen) { /* FSM */
358
359
0
    char c = buffer[i];
360
0
    if(!parser->file_data) { /* tmp file data is not allocated yet */
361
0
      parser->file_data = Curl_fileinfo_alloc();
362
0
      if(!parser->file_data) {
363
0
        parser->error = CURLE_OUT_OF_MEMORY;
364
0
        goto fail;
365
0
      }
366
0
      parser->file_data->info.b_data = malloc(FTP_BUFFER_ALLOCSIZE);
367
0
      if(!parser->file_data->info.b_data) {
368
0
        parser->error = CURLE_OUT_OF_MEMORY;
369
0
        goto fail;
370
0
      }
371
0
      parser->file_data->info.b_size = FTP_BUFFER_ALLOCSIZE;
372
0
      parser->item_offset = 0;
373
0
      parser->item_length = 0;
374
0
    }
375
376
0
    infop = parser->file_data;
377
0
    finfo = &infop->info;
378
0
    finfo->b_data[finfo->b_used++] = c;
379
380
0
    if(finfo->b_used >= finfo->b_size - 1) {
381
      /* if it is important, extend buffer space for file data */
382
0
      char *tmp = realloc(finfo->b_data,
383
0
                          finfo->b_size + FTP_BUFFER_ALLOCSIZE);
384
0
      if(tmp) {
385
0
        finfo->b_size += FTP_BUFFER_ALLOCSIZE;
386
0
        finfo->b_data = tmp;
387
0
      }
388
0
      else {
389
0
        Curl_fileinfo_cleanup(parser->file_data);
390
0
        parser->file_data = NULL;
391
0
        parser->error = CURLE_OUT_OF_MEMORY;
392
0
        goto fail;
393
0
      }
394
0
    }
395
396
0
    switch(parser->os_type) {
397
0
    case OS_TYPE_UNIX:
398
0
      switch(parser->state.UNIX.main) {
399
0
      case PL_UNIX_TOTALSIZE:
400
0
        switch(parser->state.UNIX.sub.total_dirsize) {
401
0
        case PL_UNIX_TOTALSIZE_INIT:
402
0
          if(c == 't') {
403
0
            parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
404
0
            parser->item_length++;
405
0
          }
406
0
          else {
407
0
            parser->state.UNIX.main = PL_UNIX_FILETYPE;
408
            /* start FSM again not considering size of directory */
409
0
            finfo->b_used = 0;
410
0
            continue;
411
0
          }
412
0
          break;
413
0
        case PL_UNIX_TOTALSIZE_READING:
414
0
          parser->item_length++;
415
0
          if(c == '\r') {
416
0
            parser->item_length--;
417
0
            finfo->b_used--;
418
0
          }
419
0
          else if(c == '\n') {
420
0
            finfo->b_data[parser->item_length - 1] = 0;
421
0
            if(strncmp("total ", finfo->b_data, 6) == 0) {
422
0
              char *endptr = finfo->b_data + 6;
423
              /* here we can deal with directory size, pass the leading
424
                 whitespace and then the digits */
425
0
              while(ISBLANK(*endptr))
426
0
                endptr++;
427
0
              while(ISDIGIT(*endptr))
428
0
                endptr++;
429
0
              if(*endptr) {
430
0
                parser->error = CURLE_FTP_BAD_FILE_LIST;
431
0
                goto fail;
432
0
              }
433
0
              parser->state.UNIX.main = PL_UNIX_FILETYPE;
434
0
              finfo->b_used = 0;
435
0
            }
436
0
            else {
437
0
              parser->error = CURLE_FTP_BAD_FILE_LIST;
438
0
              goto fail;
439
0
            }
440
0
          }
441
0
          break;
442
0
        }
443
0
        break;
444
0
      case PL_UNIX_FILETYPE:
445
0
        switch(c) {
446
0
        case '-':
447
0
          finfo->filetype = CURLFILETYPE_FILE;
448
0
          break;
449
0
        case 'd':
450
0
          finfo->filetype = CURLFILETYPE_DIRECTORY;
451
0
          break;
452
0
        case 'l':
453
0
          finfo->filetype = CURLFILETYPE_SYMLINK;
454
0
          break;
455
0
        case 'p':
456
0
          finfo->filetype = CURLFILETYPE_NAMEDPIPE;
457
0
          break;
458
0
        case 's':
459
0
          finfo->filetype = CURLFILETYPE_SOCKET;
460
0
          break;
461
0
        case 'c':
462
0
          finfo->filetype = CURLFILETYPE_DEVICE_CHAR;
463
0
          break;
464
0
        case 'b':
465
0
          finfo->filetype = CURLFILETYPE_DEVICE_BLOCK;
466
0
          break;
467
0
        case 'D':
468
0
          finfo->filetype = CURLFILETYPE_DOOR;
469
0
          break;
470
0
        default:
471
0
          parser->error = CURLE_FTP_BAD_FILE_LIST;
472
0
          goto fail;
473
0
        }
474
0
        parser->state.UNIX.main = PL_UNIX_PERMISSION;
475
0
        parser->item_length = 0;
476
0
        parser->item_offset = 1;
477
0
        break;
478
0
      case PL_UNIX_PERMISSION:
479
0
        parser->item_length++;
480
0
        if(parser->item_length <= 9) {
481
0
          if(!strchr("rwx-tTsS", c)) {
482
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
483
0
            goto fail;
484
0
          }
485
0
        }
486
0
        else if(parser->item_length == 10) {
487
0
          unsigned int perm;
488
0
          if(c != ' ') {
489
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
490
0
            goto fail;
491
0
          }
492
0
          finfo->b_data[10] = 0; /* terminate permissions */
493
0
          perm = ftp_pl_get_permission(finfo->b_data + parser->item_offset);
494
0
          if(perm & FTP_LP_MALFORMATED_PERM) {
495
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
496
0
            goto fail;
497
0
          }
498
0
          parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM;
499
0
          parser->file_data->info.perm = perm;
500
0
          parser->offsets.perm = parser->item_offset;
501
502
0
          parser->item_length = 0;
503
0
          parser->state.UNIX.main = PL_UNIX_HLINKS;
504
0
          parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
505
0
        }
506
0
        break;
507
0
      case PL_UNIX_HLINKS:
508
0
        switch(parser->state.UNIX.sub.hlinks) {
509
0
        case PL_UNIX_HLINKS_PRESPACE:
510
0
          if(c != ' ') {
511
0
            if(c >= '0' && c <= '9') {
512
0
              parser->item_offset = finfo->b_used - 1;
513
0
              parser->item_length = 1;
514
0
              parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
515
0
            }
516
0
            else {
517
0
              parser->error = CURLE_FTP_BAD_FILE_LIST;
518
0
              goto fail;
519
0
            }
520
0
          }
521
0
          break;
522
0
        case PL_UNIX_HLINKS_NUMBER:
523
0
          parser->item_length ++;
524
0
          if(c == ' ') {
525
0
            char *p;
526
0
            long int hlinks;
527
0
            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
528
0
            hlinks = strtol(finfo->b_data + parser->item_offset, &p, 10);
529
0
            if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) {
530
0
              parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
531
0
              parser->file_data->info.hardlinks = hlinks;
532
0
            }
533
0
            parser->item_length = 0;
534
0
            parser->item_offset = 0;
535
0
            parser->state.UNIX.main = PL_UNIX_USER;
536
0
            parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
537
0
          }
538
0
          else if(c < '0' || c > '9') {
539
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
540
0
            goto fail;
541
0
          }
542
0
          break;
543
0
        }
544
0
        break;
545
0
      case PL_UNIX_USER:
546
0
        switch(parser->state.UNIX.sub.user) {
547
0
        case PL_UNIX_USER_PRESPACE:
548
0
          if(c != ' ') {
549
0
            parser->item_offset = finfo->b_used - 1;
550
0
            parser->item_length = 1;
551
0
            parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
552
0
          }
553
0
          break;
554
0
        case PL_UNIX_USER_PARSING:
555
0
          parser->item_length++;
556
0
          if(c == ' ') {
557
0
            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
558
0
            parser->offsets.user = parser->item_offset;
559
0
            parser->state.UNIX.main = PL_UNIX_GROUP;
560
0
            parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
561
0
            parser->item_offset = 0;
562
0
            parser->item_length = 0;
563
0
          }
564
0
          break;
565
0
        }
566
0
        break;
567
0
      case PL_UNIX_GROUP:
568
0
        switch(parser->state.UNIX.sub.group) {
569
0
        case PL_UNIX_GROUP_PRESPACE:
570
0
          if(c != ' ') {
571
0
            parser->item_offset = finfo->b_used - 1;
572
0
            parser->item_length = 1;
573
0
            parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
574
0
          }
575
0
          break;
576
0
        case PL_UNIX_GROUP_NAME:
577
0
          parser->item_length++;
578
0
          if(c == ' ') {
579
0
            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
580
0
            parser->offsets.group = parser->item_offset;
581
0
            parser->state.UNIX.main = PL_UNIX_SIZE;
582
0
            parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
583
0
            parser->item_offset = 0;
584
0
            parser->item_length = 0;
585
0
          }
586
0
          break;
587
0
        }
588
0
        break;
589
0
      case PL_UNIX_SIZE:
590
0
        switch(parser->state.UNIX.sub.size) {
591
0
        case PL_UNIX_SIZE_PRESPACE:
592
0
          if(c != ' ') {
593
0
            if(c >= '0' && c <= '9') {
594
0
              parser->item_offset = finfo->b_used - 1;
595
0
              parser->item_length = 1;
596
0
              parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
597
0
            }
598
0
            else {
599
0
              parser->error = CURLE_FTP_BAD_FILE_LIST;
600
0
              goto fail;
601
0
            }
602
0
          }
603
0
          break;
604
0
        case PL_UNIX_SIZE_NUMBER:
605
0
          parser->item_length++;
606
0
          if(c == ' ') {
607
0
            char *p;
608
0
            curl_off_t fsize;
609
0
            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
610
0
            if(!curlx_strtoofft(finfo->b_data + parser->item_offset,
611
0
                                &p, 10, &fsize)) {
612
0
              if(p[0] == '\0' && fsize != CURL_OFF_T_MAX &&
613
0
                 fsize != CURL_OFF_T_MIN) {
614
0
                parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
615
0
                parser->file_data->info.size = fsize;
616
0
              }
617
0
              parser->item_length = 0;
618
0
              parser->item_offset = 0;
619
0
              parser->state.UNIX.main = PL_UNIX_TIME;
620
0
              parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
621
0
            }
622
0
          }
623
0
          else if(!ISDIGIT(c)) {
624
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
625
0
            goto fail;
626
0
          }
627
0
          break;
628
0
        }
629
0
        break;
630
0
      case PL_UNIX_TIME:
631
0
        switch(parser->state.UNIX.sub.time) {
632
0
        case PL_UNIX_TIME_PREPART1:
633
0
          if(c != ' ') {
634
0
            if(ISALNUM(c)) {
635
0
              parser->item_offset = finfo->b_used -1;
636
0
              parser->item_length = 1;
637
0
              parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
638
0
            }
639
0
            else {
640
0
              parser->error = CURLE_FTP_BAD_FILE_LIST;
641
0
              goto fail;
642
0
            }
643
0
          }
644
0
          break;
645
0
        case PL_UNIX_TIME_PART1:
646
0
          parser->item_length++;
647
0
          if(c == ' ') {
648
0
            parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
649
0
          }
650
0
          else if(!ISALNUM(c) && c != '.') {
651
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
652
0
            goto fail;
653
0
          }
654
0
          break;
655
0
        case PL_UNIX_TIME_PREPART2:
656
0
          parser->item_length++;
657
0
          if(c != ' ') {
658
0
            if(ISALNUM(c)) {
659
0
              parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
660
0
            }
661
0
            else {
662
0
              parser->error = CURLE_FTP_BAD_FILE_LIST;
663
0
              goto fail;
664
0
            }
665
0
          }
666
0
          break;
667
0
        case PL_UNIX_TIME_PART2:
668
0
          parser->item_length++;
669
0
          if(c == ' ') {
670
0
            parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
671
0
          }
672
0
          else if(!ISALNUM(c) && c != '.') {
673
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
674
0
            goto fail;
675
0
          }
676
0
          break;
677
0
        case PL_UNIX_TIME_PREPART3:
678
0
          parser->item_length++;
679
0
          if(c != ' ') {
680
0
            if(ISALNUM(c)) {
681
0
              parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
682
0
            }
683
0
            else {
684
0
              parser->error = CURLE_FTP_BAD_FILE_LIST;
685
0
              goto fail;
686
0
            }
687
0
          }
688
0
          break;
689
0
        case PL_UNIX_TIME_PART3:
690
0
          parser->item_length++;
691
0
          if(c == ' ') {
692
0
            finfo->b_data[parser->item_offset + parser->item_length -1] = 0;
693
0
            parser->offsets.time = parser->item_offset;
694
            /*
695
              if(ftp_pl_gettime(parser, finfo->b_data + parser->item_offset)) {
696
                parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME;
697
              }
698
            */
699
0
            if(finfo->filetype == CURLFILETYPE_SYMLINK) {
700
0
              parser->state.UNIX.main = PL_UNIX_SYMLINK;
701
0
              parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
702
0
            }
703
0
            else {
704
0
              parser->state.UNIX.main = PL_UNIX_FILENAME;
705
0
              parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
706
0
            }
707
0
          }
708
0
          else if(!ISALNUM(c) && c != '.' && c != ':') {
709
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
710
0
            goto fail;
711
0
          }
712
0
          break;
713
0
        }
714
0
        break;
715
0
      case PL_UNIX_FILENAME:
716
0
        switch(parser->state.UNIX.sub.filename) {
717
0
        case PL_UNIX_FILENAME_PRESPACE:
718
0
          if(c != ' ') {
719
0
            parser->item_offset = finfo->b_used - 1;
720
0
            parser->item_length = 1;
721
0
            parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
722
0
          }
723
0
          break;
724
0
        case PL_UNIX_FILENAME_NAME:
725
0
          parser->item_length++;
726
0
          if(c == '\r') {
727
0
            parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
728
0
          }
729
0
          else if(c == '\n') {
730
0
            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
731
0
            parser->offsets.filename = parser->item_offset;
732
0
            parser->state.UNIX.main = PL_UNIX_FILETYPE;
733
0
            result = ftp_pl_insert_finfo(data, infop);
734
0
            if(result) {
735
0
              parser->error = result;
736
0
              goto fail;
737
0
            }
738
0
          }
739
0
          break;
740
0
        case PL_UNIX_FILENAME_WINDOWSEOL:
741
0
          if(c == '\n') {
742
0
            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
743
0
            parser->offsets.filename = parser->item_offset;
744
0
            parser->state.UNIX.main = PL_UNIX_FILETYPE;
745
0
            result = ftp_pl_insert_finfo(data, infop);
746
0
            if(result) {
747
0
              parser->error = result;
748
0
              goto fail;
749
0
            }
750
0
          }
751
0
          else {
752
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
753
0
            goto fail;
754
0
          }
755
0
          break;
756
0
        }
757
0
        break;
758
0
      case PL_UNIX_SYMLINK:
759
0
        switch(parser->state.UNIX.sub.symlink) {
760
0
        case PL_UNIX_SYMLINK_PRESPACE:
761
0
          if(c != ' ') {
762
0
            parser->item_offset = finfo->b_used - 1;
763
0
            parser->item_length = 1;
764
0
            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
765
0
          }
766
0
          break;
767
0
        case PL_UNIX_SYMLINK_NAME:
768
0
          parser->item_length++;
769
0
          if(c == ' ') {
770
0
            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
771
0
          }
772
0
          else if(c == '\r' || c == '\n') {
773
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
774
0
            goto fail;
775
0
          }
776
0
          break;
777
0
        case PL_UNIX_SYMLINK_PRETARGET1:
778
0
          parser->item_length++;
779
0
          if(c == '-') {
780
0
            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
781
0
          }
782
0
          else if(c == '\r' || c == '\n') {
783
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
784
0
            goto fail;
785
0
          }
786
0
          else {
787
0
            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
788
0
          }
789
0
          break;
790
0
        case PL_UNIX_SYMLINK_PRETARGET2:
791
0
          parser->item_length++;
792
0
          if(c == '>') {
793
0
            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
794
0
          }
795
0
          else if(c == '\r' || c == '\n') {
796
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
797
0
            goto fail;
798
0
          }
799
0
          else {
800
0
            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
801
0
          }
802
0
          break;
803
0
        case PL_UNIX_SYMLINK_PRETARGET3:
804
0
          parser->item_length++;
805
0
          if(c == ' ') {
806
0
            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
807
            /* now place where is symlink following */
808
0
            finfo->b_data[parser->item_offset + parser->item_length - 4] = 0;
809
0
            parser->offsets.filename = parser->item_offset;
810
0
            parser->item_length = 0;
811
0
            parser->item_offset = 0;
812
0
          }
813
0
          else if(c == '\r' || c == '\n') {
814
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
815
0
            goto fail;
816
0
          }
817
0
          else {
818
0
            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
819
0
          }
820
0
          break;
821
0
        case PL_UNIX_SYMLINK_PRETARGET4:
822
0
          if(c != '\r' && c != '\n') {
823
0
            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
824
0
            parser->item_offset = finfo->b_used - 1;
825
0
            parser->item_length = 1;
826
0
          }
827
0
          else {
828
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
829
0
            goto fail;
830
0
          }
831
0
          break;
832
0
        case PL_UNIX_SYMLINK_TARGET:
833
0
          parser->item_length++;
834
0
          if(c == '\r') {
835
0
            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
836
0
          }
837
0
          else if(c == '\n') {
838
0
            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
839
0
            parser->offsets.symlink_target = parser->item_offset;
840
0
            result = ftp_pl_insert_finfo(data, infop);
841
0
            if(result) {
842
0
              parser->error = result;
843
0
              goto fail;
844
0
            }
845
0
            parser->state.UNIX.main = PL_UNIX_FILETYPE;
846
0
          }
847
0
          break;
848
0
        case PL_UNIX_SYMLINK_WINDOWSEOL:
849
0
          if(c == '\n') {
850
0
            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
851
0
            parser->offsets.symlink_target = parser->item_offset;
852
0
            result = ftp_pl_insert_finfo(data, infop);
853
0
            if(result) {
854
0
              parser->error = result;
855
0
              goto fail;
856
0
            }
857
0
            parser->state.UNIX.main = PL_UNIX_FILETYPE;
858
0
          }
859
0
          else {
860
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
861
0
            goto fail;
862
0
          }
863
0
          break;
864
0
        }
865
0
        break;
866
0
      }
867
0
      break;
868
0
    case OS_TYPE_WIN_NT:
869
0
      switch(parser->state.NT.main) {
870
0
      case PL_WINNT_DATE:
871
0
        parser->item_length++;
872
0
        if(parser->item_length < 9) {
873
0
          if(!strchr("0123456789-", c)) { /* only simple control */
874
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
875
0
            goto fail;
876
0
          }
877
0
        }
878
0
        else if(parser->item_length == 9) {
879
0
          if(c == ' ') {
880
0
            parser->state.NT.main = PL_WINNT_TIME;
881
0
            parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE;
882
0
          }
883
0
          else {
884
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
885
0
            goto fail;
886
0
          }
887
0
        }
888
0
        else {
889
0
          parser->error = CURLE_FTP_BAD_FILE_LIST;
890
0
          goto fail;
891
0
        }
892
0
        break;
893
0
      case PL_WINNT_TIME:
894
0
        parser->item_length++;
895
0
        switch(parser->state.NT.sub.time) {
896
0
        case PL_WINNT_TIME_PRESPACE:
897
0
          if(!ISBLANK(c)) {
898
0
            parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
899
0
          }
900
0
          break;
901
0
        case PL_WINNT_TIME_TIME:
902
0
          if(c == ' ') {
903
0
            parser->offsets.time = parser->item_offset;
904
0
            finfo->b_data[parser->item_offset + parser->item_length -1] = 0;
905
0
            parser->state.NT.main = PL_WINNT_DIRORSIZE;
906
0
            parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE;
907
0
            parser->item_length = 0;
908
0
          }
909
0
          else if(!strchr("APM0123456789:", c)) {
910
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
911
0
            goto fail;
912
0
          }
913
0
          break;
914
0
        }
915
0
        break;
916
0
      case PL_WINNT_DIRORSIZE:
917
0
        switch(parser->state.NT.sub.dirorsize) {
918
0
        case PL_WINNT_DIRORSIZE_PRESPACE:
919
0
          if(c != ' ') {
920
0
            parser->item_offset = finfo->b_used - 1;
921
0
            parser->item_length = 1;
922
0
            parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
923
0
          }
924
0
          break;
925
0
        case PL_WINNT_DIRORSIZE_CONTENT:
926
0
          parser->item_length ++;
927
0
          if(c == ' ') {
928
0
            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
929
0
            if(strcmp("<DIR>", finfo->b_data + parser->item_offset) == 0) {
930
0
              finfo->filetype = CURLFILETYPE_DIRECTORY;
931
0
              finfo->size = 0;
932
0
            }
933
0
            else {
934
0
              char *endptr;
935
0
              if(curlx_strtoofft(finfo->b_data +
936
0
                                 parser->item_offset,
937
0
                                 &endptr, 10, &finfo->size)) {
938
0
                parser->error = CURLE_FTP_BAD_FILE_LIST;
939
0
                goto fail;
940
0
              }
941
              /* correct file type */
942
0
              parser->file_data->info.filetype = CURLFILETYPE_FILE;
943
0
            }
944
945
0
            parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
946
0
            parser->item_length = 0;
947
0
            parser->state.NT.main = PL_WINNT_FILENAME;
948
0
            parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
949
0
          }
950
0
          break;
951
0
        }
952
0
        break;
953
0
      case PL_WINNT_FILENAME:
954
0
        switch(parser->state.NT.sub.filename) {
955
0
        case PL_WINNT_FILENAME_PRESPACE:
956
0
          if(c != ' ') {
957
0
            parser->item_offset = finfo->b_used -1;
958
0
            parser->item_length = 1;
959
0
            parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
960
0
          }
961
0
          break;
962
0
        case PL_WINNT_FILENAME_CONTENT:
963
0
          parser->item_length++;
964
0
          if(c == '\r') {
965
0
            parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
966
0
            finfo->b_data[finfo->b_used - 1] = 0;
967
0
          }
968
0
          else if(c == '\n') {
969
0
            parser->offsets.filename = parser->item_offset;
970
0
            finfo->b_data[finfo->b_used - 1] = 0;
971
0
            result = ftp_pl_insert_finfo(data, infop);
972
0
            if(result) {
973
0
              parser->error = result;
974
0
              goto fail;
975
0
            }
976
0
            parser->state.NT.main = PL_WINNT_DATE;
977
0
            parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
978
0
          }
979
0
          break;
980
0
        case PL_WINNT_FILENAME_WINEOL:
981
0
          if(c == '\n') {
982
0
            parser->offsets.filename = parser->item_offset;
983
0
            result = ftp_pl_insert_finfo(data, infop);
984
0
            if(result) {
985
0
              parser->error = result;
986
0
              goto fail;
987
0
            }
988
0
            parser->state.NT.main = PL_WINNT_DATE;
989
0
            parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
990
0
          }
991
0
          else {
992
0
            parser->error = CURLE_FTP_BAD_FILE_LIST;
993
0
            goto fail;
994
0
          }
995
0
          break;
996
0
        }
997
0
        break;
998
0
      }
999
0
      break;
1000
0
    default:
1001
0
      retsize = bufflen + 1;
1002
0
      goto fail;
1003
0
    }
1004
1005
0
    i++;
1006
0
  }
1007
0
  return retsize;
1008
1009
0
fail:
1010
1011
  /* Clean up any allocated memory. */
1012
0
  if(parser->file_data) {
1013
0
    Curl_fileinfo_cleanup(parser->file_data);
1014
0
    parser->file_data = NULL;
1015
0
  }
1016
1017
0
  return retsize;
1018
0
}
1019
1020
#endif /* CURL_DISABLE_FTP */