ftplistparser.c 31 KB

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