Coverage Report

Created: 2024-02-25 06:14

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