ftplistparser.c 31 KB

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