1
0

ftplistparser.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104
  1. /***************************************************************************
  2. * _ _ ____ _
  3. * Project ___| | | | _ \| |
  4. * / __| | | | |_) | |
  5. * | (__| |_| | _ <| |___
  6. * \___|\___/|_| \_\_____|
  7. *
  8. * Copyright (C) Daniel Stenberg, <[email protected]>, 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 "ftp.h"
  45. #include "ftplistparser.h"
  46. #include "curl_fnmatch.h"
  47. #include "multiif.h"
  48. #include "curlx/strparse.h"
  49. /* The last 2 #include files should be in this order */
  50. #include "curl_memory.h"
  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 = curlx_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. static CURLcode unix_filetype(const char c, curlfiletype *t)
  321. {
  322. switch(c) {
  323. case '-':
  324. *t = CURLFILETYPE_FILE;
  325. break;
  326. case 'd':
  327. *t = CURLFILETYPE_DIRECTORY;
  328. break;
  329. case 'l':
  330. *t = CURLFILETYPE_SYMLINK;
  331. break;
  332. case 'p':
  333. *t = CURLFILETYPE_NAMEDPIPE;
  334. break;
  335. case 's':
  336. *t = CURLFILETYPE_SOCKET;
  337. break;
  338. case 'c':
  339. *t = CURLFILETYPE_DEVICE_CHAR;
  340. break;
  341. case 'b':
  342. *t = CURLFILETYPE_DEVICE_BLOCK;
  343. break;
  344. case 'D':
  345. *t = CURLFILETYPE_DOOR;
  346. break;
  347. default:
  348. return CURLE_FTP_BAD_FILE_LIST;
  349. }
  350. return CURLE_OK;
  351. }
  352. static CURLcode parse_unix_totalsize(struct ftp_parselist_data *parser,
  353. struct fileinfo *infop,
  354. const char c)
  355. {
  356. size_t len = curlx_dyn_len(&infop->buf);
  357. char *mem = curlx_dyn_ptr(&infop->buf);
  358. switch(parser->state.UNIX.sub.total_dirsize) {
  359. case PL_UNIX_TOTALSIZE_INIT:
  360. if(c == 't') {
  361. parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
  362. parser->item_length++;
  363. }
  364. else {
  365. parser->state.UNIX.main = PL_UNIX_FILETYPE;
  366. /* continue to fall through */
  367. }
  368. break;
  369. case PL_UNIX_TOTALSIZE_READING:
  370. parser->item_length++;
  371. if(c == '\r') {
  372. parser->item_length--;
  373. if(len)
  374. curlx_dyn_setlen(&infop->buf, --len);
  375. }
  376. else if(c == '\n') {
  377. mem[parser->item_length - 1] = 0;
  378. if(!strncmp("total ", mem, 6)) {
  379. const char *endptr = mem + 6;
  380. /* here we can deal with directory size, pass the leading
  381. whitespace and then the digits */
  382. curlx_str_passblanks(&endptr);
  383. while(ISDIGIT(*endptr))
  384. endptr++;
  385. if(*endptr) {
  386. return CURLE_FTP_BAD_FILE_LIST;
  387. }
  388. parser->state.UNIX.main = PL_UNIX_FILETYPE;
  389. curlx_dyn_reset(&infop->buf);
  390. }
  391. else
  392. return CURLE_FTP_BAD_FILE_LIST;
  393. }
  394. break;
  395. }
  396. return CURLE_OK;
  397. }
  398. static CURLcode parse_unix_permission(struct ftp_parselist_data *parser,
  399. struct fileinfo *infop,
  400. const char c)
  401. {
  402. char *mem = curlx_dyn_ptr(&infop->buf);
  403. parser->item_length++;
  404. if((parser->item_length <= 9) && !strchr("rwx-tTsS", c))
  405. return CURLE_FTP_BAD_FILE_LIST;
  406. else if(parser->item_length == 10) {
  407. unsigned int perm;
  408. if(c != ' ')
  409. return CURLE_FTP_BAD_FILE_LIST;
  410. mem[10] = 0; /* terminate permissions */
  411. perm = ftp_pl_get_permission(mem + parser->item_offset);
  412. if(perm & FTP_LP_MALFORMATED_PERM)
  413. return CURLE_FTP_BAD_FILE_LIST;
  414. parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM;
  415. parser->file_data->info.perm = perm;
  416. parser->offsets.perm = parser->item_offset;
  417. parser->item_length = 0;
  418. parser->state.UNIX.main = PL_UNIX_HLINKS;
  419. parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
  420. }
  421. return CURLE_OK;
  422. }
  423. static CURLcode parse_unix_hlinks(struct ftp_parselist_data *parser,
  424. struct fileinfo *infop,
  425. const char c)
  426. {
  427. size_t len = curlx_dyn_len(&infop->buf);
  428. char *mem = curlx_dyn_ptr(&infop->buf);
  429. switch(parser->state.UNIX.sub.hlinks) {
  430. case PL_UNIX_HLINKS_PRESPACE:
  431. if(c != ' ') {
  432. if(ISDIGIT(c) && len) {
  433. parser->item_offset = len - 1;
  434. parser->item_length = 1;
  435. parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
  436. }
  437. else
  438. return CURLE_FTP_BAD_FILE_LIST;
  439. }
  440. break;
  441. case PL_UNIX_HLINKS_NUMBER:
  442. parser->item_length ++;
  443. if(c == ' ') {
  444. const char *p = &mem[parser->item_offset];
  445. curl_off_t hlinks;
  446. mem[parser->item_offset + parser->item_length - 1] = 0;
  447. if(!curlx_str_number(&p, &hlinks, LONG_MAX)) {
  448. parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
  449. parser->file_data->info.hardlinks = (long)hlinks;
  450. }
  451. parser->item_length = 0;
  452. parser->item_offset = 0;
  453. parser->state.UNIX.main = PL_UNIX_USER;
  454. parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
  455. }
  456. else if(!ISDIGIT(c))
  457. return CURLE_FTP_BAD_FILE_LIST;
  458. break;
  459. }
  460. return CURLE_OK;
  461. }
  462. static CURLcode parse_unix_user(struct ftp_parselist_data *parser,
  463. struct fileinfo *infop,
  464. const char c)
  465. {
  466. size_t len = curlx_dyn_len(&infop->buf);
  467. char *mem = curlx_dyn_ptr(&infop->buf);
  468. switch(parser->state.UNIX.sub.user) {
  469. case PL_UNIX_USER_PRESPACE:
  470. if(c != ' ' && len) {
  471. parser->item_offset = len - 1;
  472. parser->item_length = 1;
  473. parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
  474. }
  475. break;
  476. case PL_UNIX_USER_PARSING:
  477. parser->item_length++;
  478. if(c == ' ') {
  479. mem[parser->item_offset + parser->item_length - 1] = 0;
  480. parser->offsets.user = parser->item_offset;
  481. parser->state.UNIX.main = PL_UNIX_GROUP;
  482. parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
  483. parser->item_offset = 0;
  484. parser->item_length = 0;
  485. }
  486. break;
  487. }
  488. return CURLE_OK;
  489. }
  490. static CURLcode parse_unix_group(struct ftp_parselist_data *parser,
  491. struct fileinfo *infop,
  492. const char c)
  493. {
  494. size_t len = curlx_dyn_len(&infop->buf);
  495. char *mem = curlx_dyn_ptr(&infop->buf);
  496. switch(parser->state.UNIX.sub.group) {
  497. case PL_UNIX_GROUP_PRESPACE:
  498. if(c != ' ' && len) {
  499. parser->item_offset = len - 1;
  500. parser->item_length = 1;
  501. parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
  502. }
  503. break;
  504. case PL_UNIX_GROUP_NAME:
  505. parser->item_length++;
  506. if(c == ' ') {
  507. mem[parser->item_offset + parser->item_length - 1] = 0;
  508. parser->offsets.group = parser->item_offset;
  509. parser->state.UNIX.main = PL_UNIX_SIZE;
  510. parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
  511. parser->item_offset = 0;
  512. parser->item_length = 0;
  513. }
  514. break;
  515. }
  516. return CURLE_OK;
  517. }
  518. static CURLcode parse_unix_size(struct ftp_parselist_data *parser,
  519. struct fileinfo *infop,
  520. const char c)
  521. {
  522. size_t len = curlx_dyn_len(&infop->buf);
  523. char *mem = curlx_dyn_ptr(&infop->buf);
  524. switch(parser->state.UNIX.sub.size) {
  525. case PL_UNIX_SIZE_PRESPACE:
  526. if(c != ' ') {
  527. if(ISDIGIT(c) && len) {
  528. parser->item_offset = len - 1;
  529. parser->item_length = 1;
  530. parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
  531. }
  532. else
  533. return CURLE_FTP_BAD_FILE_LIST;
  534. }
  535. break;
  536. case PL_UNIX_SIZE_NUMBER:
  537. parser->item_length++;
  538. if(c == ' ') {
  539. const char *p = mem + parser->item_offset;
  540. curl_off_t fsize;
  541. mem[parser->item_offset + parser->item_length - 1] = 0;
  542. if(!curlx_str_numblanks(&p, &fsize)) {
  543. if(p[0] == '\0' && fsize != CURL_OFF_T_MAX) {
  544. parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
  545. parser->file_data->info.size = fsize;
  546. }
  547. parser->item_length = 0;
  548. parser->item_offset = 0;
  549. parser->state.UNIX.main = PL_UNIX_TIME;
  550. parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
  551. }
  552. }
  553. else if(!ISDIGIT(c))
  554. return CURLE_FTP_BAD_FILE_LIST;
  555. break;
  556. }
  557. return CURLE_OK;
  558. }
  559. static CURLcode parse_unix_time(struct ftp_parselist_data *parser,
  560. struct fileinfo *infop,
  561. const char c)
  562. {
  563. size_t len = curlx_dyn_len(&infop->buf);
  564. char *mem = curlx_dyn_ptr(&infop->buf);
  565. struct curl_fileinfo *finfo = &infop->info;
  566. switch(parser->state.UNIX.sub.time) {
  567. case PL_UNIX_TIME_PREPART1:
  568. if(c != ' ') {
  569. if(ISALNUM(c) && len) {
  570. parser->item_offset = len -1;
  571. parser->item_length = 1;
  572. parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
  573. }
  574. else
  575. return CURLE_FTP_BAD_FILE_LIST;
  576. }
  577. break;
  578. case PL_UNIX_TIME_PART1:
  579. parser->item_length++;
  580. if(c == ' ')
  581. parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
  582. else if(!ISALNUM(c) && c != '.')
  583. return CURLE_FTP_BAD_FILE_LIST;
  584. break;
  585. case PL_UNIX_TIME_PREPART2:
  586. parser->item_length++;
  587. if(c != ' ') {
  588. if(ISALNUM(c))
  589. parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
  590. else
  591. return CURLE_FTP_BAD_FILE_LIST;
  592. }
  593. break;
  594. case PL_UNIX_TIME_PART2:
  595. parser->item_length++;
  596. if(c == ' ')
  597. parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
  598. else if(!ISALNUM(c) && c != '.')
  599. return CURLE_FTP_BAD_FILE_LIST;
  600. break;
  601. case PL_UNIX_TIME_PREPART3:
  602. parser->item_length++;
  603. if(c != ' ') {
  604. if(ISALNUM(c))
  605. parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
  606. else
  607. return CURLE_FTP_BAD_FILE_LIST;
  608. }
  609. break;
  610. case PL_UNIX_TIME_PART3:
  611. parser->item_length++;
  612. if(c == ' ') {
  613. mem[parser->item_offset + parser->item_length -1] = 0;
  614. parser->offsets.time = parser->item_offset;
  615. if(finfo->filetype == CURLFILETYPE_SYMLINK) {
  616. parser->state.UNIX.main = PL_UNIX_SYMLINK;
  617. parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
  618. }
  619. else {
  620. parser->state.UNIX.main = PL_UNIX_FILENAME;
  621. parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
  622. }
  623. }
  624. else if(!ISALNUM(c) && c != '.' && c != ':')
  625. return CURLE_FTP_BAD_FILE_LIST;
  626. break;
  627. }
  628. return CURLE_OK;
  629. }
  630. static CURLcode parse_unix_filename(struct Curl_easy *data,
  631. struct ftp_parselist_data *parser,
  632. struct fileinfo *infop,
  633. const char c)
  634. {
  635. size_t len = curlx_dyn_len(&infop->buf);
  636. char *mem = curlx_dyn_ptr(&infop->buf);
  637. CURLcode result = CURLE_OK;
  638. switch(parser->state.UNIX.sub.filename) {
  639. case PL_UNIX_FILENAME_PRESPACE:
  640. if(c != ' ' && len) {
  641. parser->item_offset = len - 1;
  642. parser->item_length = 1;
  643. parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
  644. }
  645. break;
  646. case PL_UNIX_FILENAME_NAME:
  647. parser->item_length++;
  648. if(c == '\r')
  649. parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
  650. else if(c == '\n') {
  651. mem[parser->item_offset + parser->item_length - 1] = 0;
  652. parser->offsets.filename = parser->item_offset;
  653. parser->state.UNIX.main = PL_UNIX_FILETYPE;
  654. result = ftp_pl_insert_finfo(data, infop);
  655. }
  656. break;
  657. case PL_UNIX_FILENAME_WINDOWSEOL:
  658. if(c == '\n') {
  659. mem[parser->item_offset + parser->item_length - 1] = 0;
  660. parser->offsets.filename = parser->item_offset;
  661. parser->state.UNIX.main = PL_UNIX_FILETYPE;
  662. result = ftp_pl_insert_finfo(data, infop);
  663. }
  664. else
  665. result = CURLE_FTP_BAD_FILE_LIST;
  666. break;
  667. }
  668. return result;
  669. }
  670. static CURLcode parse_unix_symlink(struct Curl_easy *data,
  671. struct ftp_parselist_data *parser,
  672. struct fileinfo *infop,
  673. const char c)
  674. {
  675. size_t len = curlx_dyn_len(&infop->buf);
  676. char *mem = curlx_dyn_ptr(&infop->buf);
  677. CURLcode result = CURLE_OK;
  678. switch(parser->state.UNIX.sub.symlink) {
  679. case PL_UNIX_SYMLINK_PRESPACE:
  680. if(c != ' ' && len) {
  681. parser->item_offset = len - 1;
  682. parser->item_length = 1;
  683. parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
  684. }
  685. break;
  686. case PL_UNIX_SYMLINK_NAME:
  687. parser->item_length++;
  688. if(c == ' ')
  689. parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
  690. else if(c == '\r' || c == '\n')
  691. return CURLE_FTP_BAD_FILE_LIST;
  692. break;
  693. case PL_UNIX_SYMLINK_PRETARGET1:
  694. parser->item_length++;
  695. if(c == '-')
  696. parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
  697. else if(c == '\r' || c == '\n')
  698. return CURLE_FTP_BAD_FILE_LIST;
  699. else
  700. parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
  701. break;
  702. case PL_UNIX_SYMLINK_PRETARGET2:
  703. parser->item_length++;
  704. if(c == '>')
  705. parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
  706. else if(c == '\r' || c == '\n')
  707. return CURLE_FTP_BAD_FILE_LIST;
  708. else
  709. parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
  710. break;
  711. case PL_UNIX_SYMLINK_PRETARGET3:
  712. parser->item_length++;
  713. if(c == ' ') {
  714. parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
  715. /* now place where is symlink following */
  716. mem[parser->item_offset + parser->item_length - 4] = 0;
  717. parser->offsets.filename = parser->item_offset;
  718. parser->item_length = 0;
  719. parser->item_offset = 0;
  720. }
  721. else if(c == '\r' || c == '\n')
  722. return CURLE_FTP_BAD_FILE_LIST;
  723. else
  724. parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
  725. break;
  726. case PL_UNIX_SYMLINK_PRETARGET4:
  727. if(c != '\r' && c != '\n' && len) {
  728. parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
  729. parser->item_offset = len - 1;
  730. parser->item_length = 1;
  731. }
  732. else
  733. return CURLE_FTP_BAD_FILE_LIST;
  734. break;
  735. case PL_UNIX_SYMLINK_TARGET:
  736. parser->item_length++;
  737. if(c == '\r')
  738. parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
  739. else if(c == '\n') {
  740. mem[parser->item_offset + parser->item_length - 1] = 0;
  741. parser->offsets.symlink_target = parser->item_offset;
  742. result = ftp_pl_insert_finfo(data, infop);
  743. if(result)
  744. break;
  745. parser->state.UNIX.main = PL_UNIX_FILETYPE;
  746. }
  747. break;
  748. case PL_UNIX_SYMLINK_WINDOWSEOL:
  749. if(c == '\n') {
  750. mem[parser->item_offset + parser->item_length - 1] = 0;
  751. parser->offsets.symlink_target = parser->item_offset;
  752. result = ftp_pl_insert_finfo(data, infop);
  753. if(result)
  754. break;
  755. parser->state.UNIX.main = PL_UNIX_FILETYPE;
  756. }
  757. else
  758. result = CURLE_FTP_BAD_FILE_LIST;
  759. break;
  760. }
  761. return result;
  762. }
  763. static CURLcode parse_unix(struct Curl_easy *data,
  764. struct ftp_parselist_data *parser,
  765. struct fileinfo *infop,
  766. const char c)
  767. {
  768. struct curl_fileinfo *finfo = &infop->info;
  769. CURLcode result = CURLE_OK;
  770. switch(parser->state.UNIX.main) {
  771. case PL_UNIX_TOTALSIZE:
  772. result = parse_unix_totalsize(parser, infop, c);
  773. if(result)
  774. break;
  775. if(parser->state.UNIX.main != PL_UNIX_FILETYPE)
  776. break;
  777. FALLTHROUGH();
  778. case PL_UNIX_FILETYPE:
  779. result = unix_filetype(c, &finfo->filetype);
  780. if(!result) {
  781. parser->state.UNIX.main = PL_UNIX_PERMISSION;
  782. parser->item_length = 0;
  783. parser->item_offset = 1;
  784. }
  785. break;
  786. case PL_UNIX_PERMISSION:
  787. result = parse_unix_permission(parser, infop, c);
  788. break;
  789. case PL_UNIX_HLINKS:
  790. result = parse_unix_hlinks(parser, infop, c);
  791. break;
  792. case PL_UNIX_USER:
  793. result = parse_unix_user(parser, infop, c);
  794. break;
  795. case PL_UNIX_GROUP:
  796. result = parse_unix_group(parser, infop, c);
  797. break;
  798. case PL_UNIX_SIZE:
  799. result = parse_unix_size(parser, infop, c);
  800. break;
  801. case PL_UNIX_TIME:
  802. result = parse_unix_time(parser, infop, c);
  803. break;
  804. case PL_UNIX_FILENAME:
  805. result = parse_unix_filename(data, parser, infop, c);
  806. break;
  807. case PL_UNIX_SYMLINK:
  808. result = parse_unix_symlink(data, parser, infop, c);
  809. break;
  810. }
  811. return result;
  812. }
  813. static CURLcode parse_winnt(struct Curl_easy *data,
  814. struct ftp_parselist_data *parser,
  815. struct fileinfo *infop,
  816. const char c)
  817. {
  818. struct curl_fileinfo *finfo = &infop->info;
  819. size_t len = curlx_dyn_len(&infop->buf);
  820. char *mem = curlx_dyn_ptr(&infop->buf);
  821. CURLcode result = CURLE_OK;
  822. switch(parser->state.NT.main) {
  823. case PL_WINNT_DATE:
  824. parser->item_length++;
  825. if(parser->item_length < 9) {
  826. if(!strchr("0123456789-", c)) { /* only simple control */
  827. return CURLE_FTP_BAD_FILE_LIST;
  828. }
  829. }
  830. else if(parser->item_length == 9) {
  831. if(c == ' ') {
  832. parser->state.NT.main = PL_WINNT_TIME;
  833. parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE;
  834. }
  835. else
  836. return CURLE_FTP_BAD_FILE_LIST;
  837. }
  838. else
  839. return CURLE_FTP_BAD_FILE_LIST;
  840. break;
  841. case PL_WINNT_TIME:
  842. parser->item_length++;
  843. switch(parser->state.NT.sub.time) {
  844. case PL_WINNT_TIME_PRESPACE:
  845. if(!ISBLANK(c))
  846. parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
  847. break;
  848. case PL_WINNT_TIME_TIME:
  849. if(c == ' ') {
  850. parser->offsets.time = parser->item_offset;
  851. mem[parser->item_offset + parser->item_length -1] = 0;
  852. parser->state.NT.main = PL_WINNT_DIRORSIZE;
  853. parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE;
  854. parser->item_length = 0;
  855. }
  856. else if(!strchr("APM0123456789:", c))
  857. return CURLE_FTP_BAD_FILE_LIST;
  858. break;
  859. }
  860. break;
  861. case PL_WINNT_DIRORSIZE:
  862. switch(parser->state.NT.sub.dirorsize) {
  863. case PL_WINNT_DIRORSIZE_PRESPACE:
  864. if(c != ' ' && len) {
  865. parser->item_offset = len - 1;
  866. parser->item_length = 1;
  867. parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
  868. }
  869. break;
  870. case PL_WINNT_DIRORSIZE_CONTENT:
  871. parser->item_length ++;
  872. if(c == ' ') {
  873. mem[parser->item_offset + parser->item_length - 1] = 0;
  874. if(strcmp("<DIR>", mem + parser->item_offset) == 0) {
  875. finfo->filetype = CURLFILETYPE_DIRECTORY;
  876. finfo->size = 0;
  877. }
  878. else {
  879. const char *p = mem + parser->item_offset;
  880. if(curlx_str_numblanks(&p, &finfo->size)) {
  881. return CURLE_FTP_BAD_FILE_LIST;
  882. }
  883. /* correct file type */
  884. parser->file_data->info.filetype = CURLFILETYPE_FILE;
  885. }
  886. parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
  887. parser->item_length = 0;
  888. parser->state.NT.main = PL_WINNT_FILENAME;
  889. parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
  890. }
  891. break;
  892. }
  893. break;
  894. case PL_WINNT_FILENAME:
  895. switch(parser->state.NT.sub.filename) {
  896. case PL_WINNT_FILENAME_PRESPACE:
  897. if(c != ' ' && len) {
  898. parser->item_offset = len -1;
  899. parser->item_length = 1;
  900. parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
  901. }
  902. break;
  903. case PL_WINNT_FILENAME_CONTENT:
  904. parser->item_length++;
  905. if(!len)
  906. return CURLE_FTP_BAD_FILE_LIST;
  907. if(c == '\r') {
  908. parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
  909. mem[len - 1] = 0;
  910. }
  911. else if(c == '\n') {
  912. parser->offsets.filename = parser->item_offset;
  913. mem[len - 1] = 0;
  914. result = ftp_pl_insert_finfo(data, infop);
  915. if(result)
  916. return result;
  917. parser->state.NT.main = PL_WINNT_DATE;
  918. parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
  919. }
  920. break;
  921. case PL_WINNT_FILENAME_WINEOL:
  922. if(c == '\n') {
  923. parser->offsets.filename = parser->item_offset;
  924. result = ftp_pl_insert_finfo(data, infop);
  925. if(result)
  926. return result;
  927. parser->state.NT.main = PL_WINNT_DATE;
  928. parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
  929. }
  930. else
  931. return CURLE_FTP_BAD_FILE_LIST;
  932. break;
  933. }
  934. break;
  935. }
  936. return CURLE_OK;
  937. }
  938. size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
  939. void *connptr)
  940. {
  941. size_t bufflen = size*nmemb;
  942. struct Curl_easy *data = (struct Curl_easy *)connptr;
  943. struct ftp_wc *ftpwc = data->wildcard->ftpwc;
  944. struct ftp_parselist_data *parser = ftpwc->parser;
  945. size_t i = 0;
  946. CURLcode result;
  947. size_t retsize = bufflen;
  948. if(parser->error) { /* error in previous call */
  949. /* scenario:
  950. * 1. call => OK..
  951. * 2. call => OUT_OF_MEMORY (or other error)
  952. * 3. (last) call => is skipped RIGHT HERE and the error is handled later
  953. * in wc_statemach()
  954. */
  955. goto fail;
  956. }
  957. if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) {
  958. /* considering info about FILE response format */
  959. parser->os_type = ISDIGIT(buffer[0]) ? OS_TYPE_WIN_NT : OS_TYPE_UNIX;
  960. }
  961. while(i < bufflen) { /* FSM */
  962. char c = buffer[i];
  963. struct fileinfo *infop;
  964. if(!parser->file_data) { /* tmp file data is not allocated yet */
  965. parser->file_data = Curl_fileinfo_alloc();
  966. if(!parser->file_data) {
  967. parser->error = CURLE_OUT_OF_MEMORY;
  968. goto fail;
  969. }
  970. parser->item_offset = 0;
  971. parser->item_length = 0;
  972. curlx_dyn_init(&parser->file_data->buf, MAX_FTPLIST_BUFFER);
  973. }
  974. infop = parser->file_data;
  975. if(curlx_dyn_addn(&infop->buf, &c, 1)) {
  976. parser->error = CURLE_OUT_OF_MEMORY;
  977. goto fail;
  978. }
  979. switch(parser->os_type) {
  980. case OS_TYPE_UNIX:
  981. result = parse_unix(data, parser, infop, c);
  982. break;
  983. case OS_TYPE_WIN_NT:
  984. result = parse_winnt(data, parser, infop, c);
  985. break;
  986. default:
  987. retsize = bufflen + 1;
  988. goto fail;
  989. }
  990. if(result) {
  991. parser->error = result;
  992. goto fail;
  993. }
  994. i++;
  995. }
  996. return retsize;
  997. fail:
  998. /* Clean up any allocated memory. */
  999. if(parser->file_data) {
  1000. Curl_fileinfo_cleanup(parser->file_data);
  1001. parser->file_data = NULL;
  1002. }
  1003. return retsize;
  1004. }
  1005. #endif /* CURL_DISABLE_FTP */