ftplistparser.c 30 KB

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