ftplistparser.c 29 KB

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