ftplistparser.c 32 KB

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