ftplistparser.c 31 KB

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