Coverage Report

Created: 2025-07-11 06:33

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