ftplistparser.c 32 KB

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