ftplistparser.c 30 KB

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