ftplistparser.c 30 KB

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