ftplistparser.c 31 KB

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