ftp.c 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2016 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Authors: Andrew Skalski <askalski@chek.com> |
  16. | Stefan Esser <sesser@php.net> (resume functions) |
  17. +----------------------------------------------------------------------+
  18. */
  19. /* $Id$ */
  20. #ifdef HAVE_CONFIG_H
  21. #include "config.h"
  22. #endif
  23. #include "php.h"
  24. #if HAVE_FTP
  25. #include <stdio.h>
  26. #include <ctype.h>
  27. #include <stdlib.h>
  28. #ifdef HAVE_UNISTD_H
  29. #include <unistd.h>
  30. #endif
  31. #include <fcntl.h>
  32. #include <string.h>
  33. #include <time.h>
  34. #ifdef PHP_WIN32
  35. #include <winsock2.h>
  36. #elif defined(NETWARE)
  37. #ifdef USE_WINSOCK /* Modified to use Winsock (NOVSOCK2.H), at least for now */
  38. #include <novsock2.h>
  39. #else
  40. #include <sys/socket.h>
  41. #include <netinet/in.h>
  42. #include <netdb.h>
  43. #endif
  44. #else
  45. #ifdef HAVE_SYS_TYPES_H
  46. #include <sys/types.h>
  47. #endif
  48. #include <sys/socket.h>
  49. #include <netinet/in.h>
  50. #include <arpa/inet.h>
  51. #include <netdb.h>
  52. #endif
  53. #include <errno.h>
  54. #if HAVE_SYS_TIME_H
  55. #include <sys/time.h>
  56. #endif
  57. #ifdef HAVE_SYS_SELECT_H
  58. #include <sys/select.h>
  59. #endif
  60. #if HAVE_OPENSSL_EXT
  61. #include <openssl/ssl.h>
  62. #endif
  63. #include "ftp.h"
  64. #include "ext/standard/fsock.h"
  65. /* Additional headers for NetWare */
  66. #if defined(NETWARE) && !defined(USE_WINSOCK)
  67. #include <sys/select.h>
  68. #endif
  69. /* sends an ftp command, returns true on success, false on error.
  70. * it sends the string "cmd args\r\n" if args is non-null, or
  71. * "cmd\r\n" if args is null
  72. */
  73. static int ftp_putcmd( ftpbuf_t *ftp,
  74. const char *cmd,
  75. const char *args);
  76. /* wrapper around send/recv to handle timeouts */
  77. static int my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);
  78. static int my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);
  79. static int my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen);
  80. /* reads a line the socket , returns true on success, false on error */
  81. static int ftp_readline(ftpbuf_t *ftp);
  82. /* reads an ftp response, returns true on success, false on error */
  83. static int ftp_getresp(ftpbuf_t *ftp);
  84. /* sets the ftp transfer type */
  85. static int ftp_type(ftpbuf_t *ftp, ftptype_t type);
  86. /* opens up a data stream */
  87. static databuf_t* ftp_getdata(ftpbuf_t *ftp TSRMLS_DC);
  88. /* accepts the data connection, returns updated data buffer */
  89. static databuf_t* data_accept(databuf_t *data, ftpbuf_t *ftp TSRMLS_DC);
  90. /* closes the data connection, returns NULL */
  91. static databuf_t* data_close(ftpbuf_t *ftp, databuf_t *data);
  92. /* generic file lister */
  93. static char** ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path TSRMLS_DC);
  94. /* IP and port conversion box */
  95. union ipbox {
  96. struct in_addr ia[2];
  97. unsigned short s[4];
  98. unsigned char c[8];
  99. };
  100. /* {{{ ftp_open
  101. */
  102. ftpbuf_t*
  103. ftp_open(const char *host, short port, long timeout_sec TSRMLS_DC)
  104. {
  105. ftpbuf_t *ftp;
  106. socklen_t size;
  107. struct timeval tv;
  108. /* alloc the ftp structure */
  109. ftp = ecalloc(1, sizeof(*ftp));
  110. tv.tv_sec = timeout_sec;
  111. tv.tv_usec = 0;
  112. ftp->fd = php_network_connect_socket_to_host(host,
  113. (unsigned short) (port ? port : 21), SOCK_STREAM,
  114. 0, &tv, NULL, NULL, NULL, 0 TSRMLS_CC);
  115. if (ftp->fd == -1) {
  116. goto bail;
  117. }
  118. /* Default Settings */
  119. ftp->timeout_sec = timeout_sec;
  120. ftp->nb = 0;
  121. size = sizeof(ftp->localaddr);
  122. memset(&ftp->localaddr, 0, size);
  123. if (getsockname(ftp->fd, (struct sockaddr*) &ftp->localaddr, &size) != 0) {
  124. php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname failed: %s (%d)", strerror(errno), errno);
  125. goto bail;
  126. }
  127. if (!ftp_getresp(ftp) || ftp->resp != 220) {
  128. goto bail;
  129. }
  130. return ftp;
  131. bail:
  132. if (ftp->fd != -1) {
  133. closesocket(ftp->fd);
  134. }
  135. efree(ftp);
  136. return NULL;
  137. }
  138. /* }}} */
  139. /* {{{ ftp_close
  140. */
  141. ftpbuf_t*
  142. ftp_close(ftpbuf_t *ftp)
  143. {
  144. if (ftp == NULL) {
  145. return NULL;
  146. }
  147. if (ftp->data) {
  148. data_close(ftp, ftp->data);
  149. }
  150. if (ftp->stream && ftp->closestream) {
  151. TSRMLS_FETCH();
  152. php_stream_close(ftp->stream);
  153. }
  154. if (ftp->fd != -1) {
  155. #if HAVE_OPENSSL_EXT
  156. if (ftp->ssl_active) {
  157. SSL_shutdown(ftp->ssl_handle);
  158. SSL_free(ftp->ssl_handle);
  159. }
  160. #endif
  161. closesocket(ftp->fd);
  162. }
  163. ftp_gc(ftp);
  164. efree(ftp);
  165. return NULL;
  166. }
  167. /* }}} */
  168. /* {{{ ftp_gc
  169. */
  170. void
  171. ftp_gc(ftpbuf_t *ftp)
  172. {
  173. if (ftp == NULL) {
  174. return;
  175. }
  176. if (ftp->pwd) {
  177. efree(ftp->pwd);
  178. ftp->pwd = NULL;
  179. }
  180. if (ftp->syst) {
  181. efree(ftp->syst);
  182. ftp->syst = NULL;
  183. }
  184. }
  185. /* }}} */
  186. /* {{{ ftp_quit
  187. */
  188. int
  189. ftp_quit(ftpbuf_t *ftp)
  190. {
  191. if (ftp == NULL) {
  192. return 0;
  193. }
  194. if (!ftp_putcmd(ftp, "QUIT", NULL)) {
  195. return 0;
  196. }
  197. if (!ftp_getresp(ftp) || ftp->resp != 221) {
  198. return 0;
  199. }
  200. if (ftp->pwd) {
  201. efree(ftp->pwd);
  202. ftp->pwd = NULL;
  203. }
  204. return 1;
  205. }
  206. /* }}} */
  207. /* {{{ ftp_login
  208. */
  209. int
  210. ftp_login(ftpbuf_t *ftp, const char *user, const char *pass TSRMLS_DC)
  211. {
  212. #if HAVE_OPENSSL_EXT
  213. SSL_CTX *ctx = NULL;
  214. long ssl_ctx_options = SSL_OP_ALL;
  215. #endif
  216. if (ftp == NULL) {
  217. return 0;
  218. }
  219. #if HAVE_OPENSSL_EXT
  220. if (ftp->use_ssl && !ftp->ssl_active) {
  221. if (!ftp_putcmd(ftp, "AUTH", "TLS")) {
  222. return 0;
  223. }
  224. if (!ftp_getresp(ftp)) {
  225. return 0;
  226. }
  227. if (ftp->resp != 234) {
  228. if (!ftp_putcmd(ftp, "AUTH", "SSL")) {
  229. return 0;
  230. }
  231. if (!ftp_getresp(ftp)) {
  232. return 0;
  233. }
  234. if (ftp->resp != 334) {
  235. return 0;
  236. } else {
  237. ftp->old_ssl = 1;
  238. ftp->use_ssl_for_data = 1;
  239. }
  240. }
  241. ctx = SSL_CTX_new(SSLv23_client_method());
  242. if (ctx == NULL) {
  243. php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL context");
  244. return 0;
  245. }
  246. #if OPENSSL_VERSION_NUMBER >= 0x0090605fL
  247. ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
  248. #endif
  249. SSL_CTX_set_options(ctx, ssl_ctx_options);
  250. /* allow SSL to re-use sessions */
  251. SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
  252. ftp->ssl_handle = SSL_new(ctx);
  253. if (ftp->ssl_handle == NULL) {
  254. php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL handle");
  255. SSL_CTX_free(ctx);
  256. return 0;
  257. }
  258. SSL_set_fd(ftp->ssl_handle, ftp->fd);
  259. if (SSL_connect(ftp->ssl_handle) <= 0) {
  260. php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS handshake failed");
  261. SSL_shutdown(ftp->ssl_handle);
  262. SSL_free(ftp->ssl_handle);
  263. return 0;
  264. }
  265. ftp->ssl_active = 1;
  266. if (!ftp->old_ssl) {
  267. /* set protection buffersize to zero */
  268. if (!ftp_putcmd(ftp, "PBSZ", "0")) {
  269. return 0;
  270. }
  271. if (!ftp_getresp(ftp)) {
  272. return 0;
  273. }
  274. /* enable data conn encryption */
  275. if (!ftp_putcmd(ftp, "PROT", "P")) {
  276. return 0;
  277. }
  278. if (!ftp_getresp(ftp)) {
  279. return 0;
  280. }
  281. ftp->use_ssl_for_data = (ftp->resp >= 200 && ftp->resp <=299);
  282. }
  283. }
  284. #endif
  285. if (!ftp_putcmd(ftp, "USER", user)) {
  286. return 0;
  287. }
  288. if (!ftp_getresp(ftp)) {
  289. return 0;
  290. }
  291. if (ftp->resp == 230) {
  292. return 1;
  293. }
  294. if (ftp->resp != 331) {
  295. return 0;
  296. }
  297. if (!ftp_putcmd(ftp, "PASS", pass)) {
  298. return 0;
  299. }
  300. if (!ftp_getresp(ftp)) {
  301. return 0;
  302. }
  303. return (ftp->resp == 230);
  304. }
  305. /* }}} */
  306. /* {{{ ftp_reinit
  307. */
  308. int
  309. ftp_reinit(ftpbuf_t *ftp)
  310. {
  311. if (ftp == NULL) {
  312. return 0;
  313. }
  314. ftp_gc(ftp);
  315. ftp->nb = 0;
  316. if (!ftp_putcmd(ftp, "REIN", NULL)) {
  317. return 0;
  318. }
  319. if (!ftp_getresp(ftp) || ftp->resp != 220) {
  320. return 0;
  321. }
  322. return 1;
  323. }
  324. /* }}} */
  325. /* {{{ ftp_syst
  326. */
  327. const char*
  328. ftp_syst(ftpbuf_t *ftp)
  329. {
  330. char *syst, *end;
  331. if (ftp == NULL) {
  332. return NULL;
  333. }
  334. /* default to cached value */
  335. if (ftp->syst) {
  336. return ftp->syst;
  337. }
  338. if (!ftp_putcmd(ftp, "SYST", NULL)) {
  339. return NULL;
  340. }
  341. if (!ftp_getresp(ftp) || ftp->resp != 215) {
  342. return NULL;
  343. }
  344. syst = ftp->inbuf;
  345. while (*syst == ' ') {
  346. syst++;
  347. }
  348. if ((end = strchr(syst, ' '))) {
  349. *end = 0;
  350. }
  351. ftp->syst = estrdup(syst);
  352. if (end) {
  353. *end = ' ';
  354. }
  355. return ftp->syst;
  356. }
  357. /* }}} */
  358. /* {{{ ftp_pwd
  359. */
  360. const char*
  361. ftp_pwd(ftpbuf_t *ftp)
  362. {
  363. char *pwd, *end;
  364. if (ftp == NULL) {
  365. return NULL;
  366. }
  367. /* default to cached value */
  368. if (ftp->pwd) {
  369. return ftp->pwd;
  370. }
  371. if (!ftp_putcmd(ftp, "PWD", NULL)) {
  372. return NULL;
  373. }
  374. if (!ftp_getresp(ftp) || ftp->resp != 257) {
  375. return NULL;
  376. }
  377. /* copy out the pwd from response */
  378. if ((pwd = strchr(ftp->inbuf, '"')) == NULL) {
  379. return NULL;
  380. }
  381. if ((end = strrchr(++pwd, '"')) == NULL) {
  382. return NULL;
  383. }
  384. ftp->pwd = estrndup(pwd, end - pwd);
  385. return ftp->pwd;
  386. }
  387. /* }}} */
  388. /* {{{ ftp_exec
  389. */
  390. int
  391. ftp_exec(ftpbuf_t *ftp, const char *cmd)
  392. {
  393. if (ftp == NULL) {
  394. return 0;
  395. }
  396. if (!ftp_putcmd(ftp, "SITE EXEC", cmd)) {
  397. return 0;
  398. }
  399. if (!ftp_getresp(ftp) || ftp->resp != 200) {
  400. return 0;
  401. }
  402. return 1;
  403. }
  404. /* }}} */
  405. /* {{{ ftp_raw
  406. */
  407. void
  408. ftp_raw(ftpbuf_t *ftp, const char *cmd, zval *return_value)
  409. {
  410. if (ftp == NULL || cmd == NULL) {
  411. RETURN_NULL();
  412. }
  413. if (!ftp_putcmd(ftp, cmd, NULL)) {
  414. RETURN_NULL();
  415. }
  416. array_init(return_value);
  417. while (ftp_readline(ftp)) {
  418. add_next_index_string(return_value, ftp->inbuf, 1);
  419. if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
  420. return;
  421. }
  422. }
  423. }
  424. /* }}} */
  425. /* {{{ ftp_chdir
  426. */
  427. int
  428. ftp_chdir(ftpbuf_t *ftp, const char *dir)
  429. {
  430. if (ftp == NULL) {
  431. return 0;
  432. }
  433. if (ftp->pwd) {
  434. efree(ftp->pwd);
  435. ftp->pwd = NULL;
  436. }
  437. if (!ftp_putcmd(ftp, "CWD", dir)) {
  438. return 0;
  439. }
  440. if (!ftp_getresp(ftp) || ftp->resp != 250) {
  441. return 0;
  442. }
  443. return 1;
  444. }
  445. /* }}} */
  446. /* {{{ ftp_cdup
  447. */
  448. int
  449. ftp_cdup(ftpbuf_t *ftp)
  450. {
  451. if (ftp == NULL) {
  452. return 0;
  453. }
  454. if (ftp->pwd) {
  455. efree(ftp->pwd);
  456. ftp->pwd = NULL;
  457. }
  458. if (!ftp_putcmd(ftp, "CDUP", NULL)) {
  459. return 0;
  460. }
  461. if (!ftp_getresp(ftp) || ftp->resp != 250) {
  462. return 0;
  463. }
  464. return 1;
  465. }
  466. /* }}} */
  467. /* {{{ ftp_mkdir
  468. */
  469. char*
  470. ftp_mkdir(ftpbuf_t *ftp, const char *dir)
  471. {
  472. char *mkd, *end;
  473. if (ftp == NULL) {
  474. return NULL;
  475. }
  476. if (!ftp_putcmd(ftp, "MKD", dir)) {
  477. return NULL;
  478. }
  479. if (!ftp_getresp(ftp) || ftp->resp != 257) {
  480. return NULL;
  481. }
  482. /* copy out the dir from response */
  483. if ((mkd = strchr(ftp->inbuf, '"')) == NULL) {
  484. mkd = estrdup(dir);
  485. return mkd;
  486. }
  487. if ((end = strrchr(++mkd, '"')) == NULL) {
  488. return NULL;
  489. }
  490. *end = 0;
  491. mkd = estrdup(mkd);
  492. *end = '"';
  493. return mkd;
  494. }
  495. /* }}} */
  496. /* {{{ ftp_rmdir
  497. */
  498. int
  499. ftp_rmdir(ftpbuf_t *ftp, const char *dir)
  500. {
  501. if (ftp == NULL) {
  502. return 0;
  503. }
  504. if (!ftp_putcmd(ftp, "RMD", dir)) {
  505. return 0;
  506. }
  507. if (!ftp_getresp(ftp) || ftp->resp != 250) {
  508. return 0;
  509. }
  510. return 1;
  511. }
  512. /* }}} */
  513. /* {{{ ftp_chmod
  514. */
  515. int
  516. ftp_chmod(ftpbuf_t *ftp, const int mode, const char *filename, const int filename_len)
  517. {
  518. char *buffer;
  519. if (ftp == NULL || filename_len <= 0) {
  520. return 0;
  521. }
  522. spprintf(&buffer, 0, "CHMOD %o %s", mode, filename);
  523. if (!ftp_putcmd(ftp, "SITE", buffer)) {
  524. efree(buffer);
  525. return 0;
  526. }
  527. efree(buffer);
  528. if (!ftp_getresp(ftp) || ftp->resp != 200) {
  529. return 0;
  530. }
  531. return 1;
  532. }
  533. /* }}} */
  534. /* {{{ ftp_alloc
  535. */
  536. int
  537. ftp_alloc(ftpbuf_t *ftp, const long size, char **response)
  538. {
  539. char buffer[64];
  540. if (ftp == NULL || size <= 0) {
  541. return 0;
  542. }
  543. snprintf(buffer, sizeof(buffer) - 1, "%ld", size);
  544. if (!ftp_putcmd(ftp, "ALLO", buffer)) {
  545. return 0;
  546. }
  547. if (!ftp_getresp(ftp)) {
  548. return 0;
  549. }
  550. if (response) {
  551. *response = estrdup(ftp->inbuf);
  552. }
  553. if (ftp->resp < 200 || ftp->resp >= 300) {
  554. return 0;
  555. }
  556. return 1;
  557. }
  558. /* }}} */
  559. /* {{{ ftp_nlist
  560. */
  561. char**
  562. ftp_nlist(ftpbuf_t *ftp, const char *path TSRMLS_DC)
  563. {
  564. return ftp_genlist(ftp, "NLST", path TSRMLS_CC);
  565. }
  566. /* }}} */
  567. /* {{{ ftp_list
  568. */
  569. char**
  570. ftp_list(ftpbuf_t *ftp, const char *path, int recursive TSRMLS_DC)
  571. {
  572. return ftp_genlist(ftp, ((recursive) ? "LIST -R" : "LIST"), path TSRMLS_CC);
  573. }
  574. /* }}} */
  575. /* {{{ ftp_type
  576. */
  577. int
  578. ftp_type(ftpbuf_t *ftp, ftptype_t type)
  579. {
  580. char typechar[2] = "?";
  581. if (ftp == NULL) {
  582. return 0;
  583. }
  584. if (type == ftp->type) {
  585. return 1;
  586. }
  587. if (type == FTPTYPE_ASCII) {
  588. typechar[0] = 'A';
  589. } else if (type == FTPTYPE_IMAGE) {
  590. typechar[0] = 'I';
  591. } else {
  592. return 0;
  593. }
  594. if (!ftp_putcmd(ftp, "TYPE", typechar)) {
  595. return 0;
  596. }
  597. if (!ftp_getresp(ftp) || ftp->resp != 200) {
  598. return 0;
  599. }
  600. ftp->type = type;
  601. return 1;
  602. }
  603. /* }}} */
  604. /* {{{ ftp_pasv
  605. */
  606. int
  607. ftp_pasv(ftpbuf_t *ftp, int pasv)
  608. {
  609. char *ptr;
  610. union ipbox ipbox;
  611. unsigned long b[6];
  612. socklen_t n;
  613. struct sockaddr *sa;
  614. struct sockaddr_in *sin;
  615. if (ftp == NULL) {
  616. return 0;
  617. }
  618. if (pasv && ftp->pasv == 2) {
  619. return 1;
  620. }
  621. ftp->pasv = 0;
  622. if (!pasv) {
  623. return 1;
  624. }
  625. n = sizeof(ftp->pasvaddr);
  626. memset(&ftp->pasvaddr, 0, n);
  627. sa = (struct sockaddr *) &ftp->pasvaddr;
  628. if (getpeername(ftp->fd, sa, &n) < 0) {
  629. return 0;
  630. }
  631. #if HAVE_IPV6
  632. if (sa->sa_family == AF_INET6) {
  633. struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
  634. char *endptr, delimiter;
  635. /* try EPSV first */
  636. if (!ftp_putcmd(ftp, "EPSV", NULL)) {
  637. return 0;
  638. }
  639. if (!ftp_getresp(ftp)) {
  640. return 0;
  641. }
  642. if (ftp->resp == 229) {
  643. /* parse out the port */
  644. for (ptr = ftp->inbuf; *ptr && *ptr != '('; ptr++);
  645. if (!*ptr) {
  646. return 0;
  647. }
  648. delimiter = *++ptr;
  649. for (n = 0; *ptr && n < 3; ptr++) {
  650. if (*ptr == delimiter) {
  651. n++;
  652. }
  653. }
  654. sin6->sin6_port = htons((unsigned short) strtoul(ptr, &endptr, 10));
  655. if (ptr == endptr || *endptr != delimiter) {
  656. return 0;
  657. }
  658. ftp->pasv = 2;
  659. return 1;
  660. }
  661. }
  662. /* fall back to PASV */
  663. #endif
  664. if (!ftp_putcmd(ftp, "PASV", NULL)) {
  665. return 0;
  666. }
  667. if (!ftp_getresp(ftp) || ftp->resp != 227) {
  668. return 0;
  669. }
  670. /* parse out the IP and port */
  671. for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
  672. n = sscanf(ptr, "%lu,%lu,%lu,%lu,%lu,%lu", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]);
  673. if (n != 6) {
  674. return 0;
  675. }
  676. for (n = 0; n < 6; n++) {
  677. ipbox.c[n] = (unsigned char) b[n];
  678. }
  679. sin = (struct sockaddr_in *) sa;
  680. if (ftp->usepasvaddress) {
  681. sin->sin_addr = ipbox.ia[0];
  682. }
  683. sin->sin_port = ipbox.s[2];
  684. ftp->pasv = 2;
  685. return 1;
  686. }
  687. /* }}} */
  688. /* {{{ ftp_get
  689. */
  690. int
  691. ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, long resumepos TSRMLS_DC)
  692. {
  693. databuf_t *data = NULL;
  694. size_t rcvd;
  695. char arg[11];
  696. if (ftp == NULL) {
  697. return 0;
  698. }
  699. if (!ftp_type(ftp, type)) {
  700. goto bail;
  701. }
  702. if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
  703. goto bail;
  704. }
  705. ftp->data = data;
  706. if (resumepos > 0) {
  707. snprintf(arg, sizeof(arg), "%ld", resumepos);
  708. if (!ftp_putcmd(ftp, "REST", arg)) {
  709. goto bail;
  710. }
  711. if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
  712. goto bail;
  713. }
  714. }
  715. if (!ftp_putcmd(ftp, "RETR", path)) {
  716. goto bail;
  717. }
  718. if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
  719. goto bail;
  720. }
  721. if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
  722. goto bail;
  723. }
  724. while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
  725. if (rcvd == -1) {
  726. goto bail;
  727. }
  728. if (type == FTPTYPE_ASCII) {
  729. #ifndef PHP_WIN32
  730. char *s;
  731. #endif
  732. char *ptr = data->buf;
  733. char *e = ptr + rcvd;
  734. /* logic depends on the OS EOL
  735. * Win32 -> \r\n
  736. * Everything Else \n
  737. */
  738. #ifdef PHP_WIN32
  739. php_stream_write(outstream, ptr, (e - ptr));
  740. ptr = e;
  741. #else
  742. while (e > ptr && (s = memchr(ptr, '\r', (e - ptr)))) {
  743. php_stream_write(outstream, ptr, (s - ptr));
  744. if (*(s + 1) == '\n') {
  745. s++;
  746. php_stream_putc(outstream, '\n');
  747. }
  748. ptr = s + 1;
  749. }
  750. #endif
  751. if (ptr < e) {
  752. php_stream_write(outstream, ptr, (e - ptr));
  753. }
  754. } else if (rcvd != php_stream_write(outstream, data->buf, rcvd)) {
  755. goto bail;
  756. }
  757. }
  758. ftp->data = data = data_close(ftp, data);
  759. if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
  760. goto bail;
  761. }
  762. return 1;
  763. bail:
  764. ftp->data = data_close(ftp, data);
  765. return 0;
  766. }
  767. /* }}} */
  768. /* {{{ ftp_put
  769. */
  770. int
  771. ftp_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, long startpos TSRMLS_DC)
  772. {
  773. databuf_t *data = NULL;
  774. long size;
  775. char *ptr;
  776. int ch;
  777. char arg[11];
  778. if (ftp == NULL) {
  779. return 0;
  780. }
  781. if (!ftp_type(ftp, type)) {
  782. goto bail;
  783. }
  784. if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
  785. goto bail;
  786. }
  787. ftp->data = data;
  788. if (startpos > 0) {
  789. snprintf(arg, sizeof(arg), "%ld", startpos);
  790. if (!ftp_putcmd(ftp, "REST", arg)) {
  791. goto bail;
  792. }
  793. if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
  794. goto bail;
  795. }
  796. }
  797. if (!ftp_putcmd(ftp, "STOR", path)) {
  798. goto bail;
  799. }
  800. if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
  801. goto bail;
  802. }
  803. if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
  804. goto bail;
  805. }
  806. size = 0;
  807. ptr = data->buf;
  808. while (!php_stream_eof(instream) && (ch = php_stream_getc(instream))!=EOF) {
  809. /* flush if necessary */
  810. if (FTP_BUFSIZE - size < 2) {
  811. if (my_send(ftp, data->fd, data->buf, size) != size) {
  812. goto bail;
  813. }
  814. ptr = data->buf;
  815. size = 0;
  816. }
  817. if (ch == '\n' && type == FTPTYPE_ASCII) {
  818. *ptr++ = '\r';
  819. size++;
  820. }
  821. *ptr++ = ch;
  822. size++;
  823. }
  824. if (size && my_send(ftp, data->fd, data->buf, size) != size) {
  825. goto bail;
  826. }
  827. ftp->data = data = data_close(ftp, data);
  828. if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250 && ftp->resp != 200)) {
  829. goto bail;
  830. }
  831. return 1;
  832. bail:
  833. ftp->data = data_close(ftp, data);
  834. return 0;
  835. }
  836. /* }}} */
  837. /* {{{ ftp_size
  838. */
  839. long
  840. ftp_size(ftpbuf_t *ftp, const char *path)
  841. {
  842. if (ftp == NULL) {
  843. return -1;
  844. }
  845. if (!ftp_type(ftp, FTPTYPE_IMAGE)) {
  846. return -1;
  847. }
  848. if (!ftp_putcmd(ftp, "SIZE", path)) {
  849. return -1;
  850. }
  851. if (!ftp_getresp(ftp) || ftp->resp != 213) {
  852. return -1;
  853. }
  854. return atol(ftp->inbuf);
  855. }
  856. /* }}} */
  857. /* {{{ ftp_mdtm
  858. */
  859. time_t
  860. ftp_mdtm(ftpbuf_t *ftp, const char *path)
  861. {
  862. time_t stamp;
  863. struct tm *gmt, tmbuf;
  864. struct tm tm;
  865. char *ptr;
  866. int n;
  867. if (ftp == NULL) {
  868. return -1;
  869. }
  870. if (!ftp_putcmd(ftp, "MDTM", path)) {
  871. return -1;
  872. }
  873. if (!ftp_getresp(ftp) || ftp->resp != 213) {
  874. return -1;
  875. }
  876. /* parse out the timestamp */
  877. for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
  878. n = sscanf(ptr, "%4u%2u%2u%2u%2u%2u", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
  879. if (n != 6) {
  880. return -1;
  881. }
  882. tm.tm_year -= 1900;
  883. tm.tm_mon--;
  884. tm.tm_isdst = -1;
  885. /* figure out the GMT offset */
  886. stamp = time(NULL);
  887. gmt = php_gmtime_r(&stamp, &tmbuf);
  888. if (!gmt) {
  889. return -1;
  890. }
  891. gmt->tm_isdst = -1;
  892. /* apply the GMT offset */
  893. tm.tm_sec += stamp - mktime(gmt);
  894. tm.tm_isdst = gmt->tm_isdst;
  895. stamp = mktime(&tm);
  896. return stamp;
  897. }
  898. /* }}} */
  899. /* {{{ ftp_delete
  900. */
  901. int
  902. ftp_delete(ftpbuf_t *ftp, const char *path)
  903. {
  904. if (ftp == NULL) {
  905. return 0;
  906. }
  907. if (!ftp_putcmd(ftp, "DELE", path)) {
  908. return 0;
  909. }
  910. if (!ftp_getresp(ftp) || ftp->resp != 250) {
  911. return 0;
  912. }
  913. return 1;
  914. }
  915. /* }}} */
  916. /* {{{ ftp_rename
  917. */
  918. int
  919. ftp_rename(ftpbuf_t *ftp, const char *src, const char *dest)
  920. {
  921. if (ftp == NULL) {
  922. return 0;
  923. }
  924. if (!ftp_putcmd(ftp, "RNFR", src)) {
  925. return 0;
  926. }
  927. if (!ftp_getresp(ftp) || ftp->resp != 350) {
  928. return 0;
  929. }
  930. if (!ftp_putcmd(ftp, "RNTO", dest)) {
  931. return 0;
  932. }
  933. if (!ftp_getresp(ftp) || ftp->resp != 250) {
  934. return 0;
  935. }
  936. return 1;
  937. }
  938. /* }}} */
  939. /* {{{ ftp_site
  940. */
  941. int
  942. ftp_site(ftpbuf_t *ftp, const char *cmd)
  943. {
  944. if (ftp == NULL) {
  945. return 0;
  946. }
  947. if (!ftp_putcmd(ftp, "SITE", cmd)) {
  948. return 0;
  949. }
  950. if (!ftp_getresp(ftp) || ftp->resp < 200 || ftp->resp >= 300) {
  951. return 0;
  952. }
  953. return 1;
  954. }
  955. /* }}} */
  956. /* static functions */
  957. /* {{{ ftp_putcmd
  958. */
  959. int
  960. ftp_putcmd(ftpbuf_t *ftp, const char *cmd, const char *args)
  961. {
  962. int size;
  963. char *data;
  964. if (strpbrk(cmd, "\r\n")) {
  965. return 0;
  966. }
  967. /* build the output buffer */
  968. if (args && args[0]) {
  969. /* "cmd args\r\n\0" */
  970. if (strlen(cmd) + strlen(args) + 4 > FTP_BUFSIZE) {
  971. return 0;
  972. }
  973. if (strpbrk(args, "\r\n")) {
  974. return 0;
  975. }
  976. size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s %s\r\n", cmd, args);
  977. } else {
  978. /* "cmd\r\n\0" */
  979. if (strlen(cmd) + 3 > FTP_BUFSIZE) {
  980. return 0;
  981. }
  982. size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s\r\n", cmd);
  983. }
  984. data = ftp->outbuf;
  985. /* Clear the extra-lines buffer */
  986. ftp->extra = NULL;
  987. if (my_send(ftp, ftp->fd, data, size) != size) {
  988. return 0;
  989. }
  990. return 1;
  991. }
  992. /* }}} */
  993. /* {{{ ftp_readline
  994. */
  995. int
  996. ftp_readline(ftpbuf_t *ftp)
  997. {
  998. long size, rcvd;
  999. char *data, *eol;
  1000. /* shift the extra to the front */
  1001. size = FTP_BUFSIZE;
  1002. rcvd = 0;
  1003. if (ftp->extra) {
  1004. memmove(ftp->inbuf, ftp->extra, ftp->extralen);
  1005. rcvd = ftp->extralen;
  1006. }
  1007. data = ftp->inbuf;
  1008. do {
  1009. size -= rcvd;
  1010. for (eol = data; rcvd; rcvd--, eol++) {
  1011. if (*eol == '\r') {
  1012. *eol = 0;
  1013. ftp->extra = eol + 1;
  1014. if (rcvd > 1 && *(eol + 1) == '\n') {
  1015. ftp->extra++;
  1016. rcvd--;
  1017. }
  1018. if ((ftp->extralen = --rcvd) == 0) {
  1019. ftp->extra = NULL;
  1020. }
  1021. return 1;
  1022. } else if (*eol == '\n') {
  1023. *eol = 0;
  1024. ftp->extra = eol + 1;
  1025. if ((ftp->extralen = --rcvd) == 0) {
  1026. ftp->extra = NULL;
  1027. }
  1028. return 1;
  1029. }
  1030. }
  1031. data = eol;
  1032. if ((rcvd = my_recv(ftp, ftp->fd, data, size)) < 1) {
  1033. return 0;
  1034. }
  1035. } while (size);
  1036. return 0;
  1037. }
  1038. /* }}} */
  1039. /* {{{ ftp_getresp
  1040. */
  1041. int
  1042. ftp_getresp(ftpbuf_t *ftp)
  1043. {
  1044. if (ftp == NULL) {
  1045. return 0;
  1046. }
  1047. ftp->resp = 0;
  1048. while (1) {
  1049. if (!ftp_readline(ftp)) {
  1050. return 0;
  1051. }
  1052. /* Break out when the end-tag is found */
  1053. if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
  1054. break;
  1055. }
  1056. }
  1057. /* translate the tag */
  1058. if (!isdigit(ftp->inbuf[0]) || !isdigit(ftp->inbuf[1]) || !isdigit(ftp->inbuf[2])) {
  1059. return 0;
  1060. }
  1061. ftp->resp = 100 * (ftp->inbuf[0] - '0') + 10 * (ftp->inbuf[1] - '0') + (ftp->inbuf[2] - '0');
  1062. memmove(ftp->inbuf, ftp->inbuf + 4, FTP_BUFSIZE - 4);
  1063. if (ftp->extra) {
  1064. ftp->extra -= 4;
  1065. }
  1066. return 1;
  1067. }
  1068. /* }}} */
  1069. /* {{{ my_send
  1070. */
  1071. int
  1072. my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
  1073. {
  1074. long size, sent;
  1075. int n;
  1076. size = len;
  1077. while (size) {
  1078. n = php_pollfd_for_ms(s, POLLOUT, ftp->timeout_sec * 1000);
  1079. if (n < 1) {
  1080. #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
  1081. if (n == 0) {
  1082. errno = ETIMEDOUT;
  1083. }
  1084. #endif
  1085. return -1;
  1086. }
  1087. #if HAVE_OPENSSL_EXT
  1088. if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
  1089. sent = SSL_write(ftp->ssl_handle, buf, size);
  1090. } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
  1091. sent = SSL_write(ftp->data->ssl_handle, buf, size);
  1092. } else {
  1093. #endif
  1094. sent = send(s, buf, size, 0);
  1095. #if HAVE_OPENSSL_EXT
  1096. }
  1097. #endif
  1098. if (sent == -1) {
  1099. return -1;
  1100. }
  1101. buf = (char*) buf + sent;
  1102. size -= sent;
  1103. }
  1104. return len;
  1105. }
  1106. /* }}} */
  1107. /* {{{ my_recv
  1108. */
  1109. int
  1110. my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
  1111. {
  1112. int n, nr_bytes;
  1113. n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
  1114. if (n < 1) {
  1115. #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
  1116. if (n == 0) {
  1117. errno = ETIMEDOUT;
  1118. }
  1119. #endif
  1120. return -1;
  1121. }
  1122. #if HAVE_OPENSSL_EXT
  1123. if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
  1124. nr_bytes = SSL_read(ftp->ssl_handle, buf, len);
  1125. } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
  1126. nr_bytes = SSL_read(ftp->data->ssl_handle, buf, len);
  1127. } else {
  1128. #endif
  1129. nr_bytes = recv(s, buf, len, 0);
  1130. #if HAVE_OPENSSL_EXT
  1131. }
  1132. #endif
  1133. return (nr_bytes);
  1134. }
  1135. /* }}} */
  1136. /* {{{ data_available
  1137. */
  1138. int
  1139. data_available(ftpbuf_t *ftp, php_socket_t s)
  1140. {
  1141. int n;
  1142. n = php_pollfd_for_ms(s, PHP_POLLREADABLE, 1000);
  1143. if (n < 1) {
  1144. #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
  1145. if (n == 0) {
  1146. errno = ETIMEDOUT;
  1147. }
  1148. #endif
  1149. return 0;
  1150. }
  1151. return 1;
  1152. }
  1153. /* }}} */
  1154. /* {{{ data_writeable
  1155. */
  1156. int
  1157. data_writeable(ftpbuf_t *ftp, php_socket_t s)
  1158. {
  1159. int n;
  1160. n = php_pollfd_for_ms(s, POLLOUT, 1000);
  1161. if (n < 1) {
  1162. #ifndef PHP_WIN32
  1163. if (n == 0) {
  1164. errno = ETIMEDOUT;
  1165. }
  1166. #endif
  1167. return 0;
  1168. }
  1169. return 1;
  1170. }
  1171. /* }}} */
  1172. /* {{{ my_accept
  1173. */
  1174. int
  1175. my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen)
  1176. {
  1177. int n;
  1178. n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
  1179. if (n < 1) {
  1180. #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
  1181. if (n == 0) {
  1182. errno = ETIMEDOUT;
  1183. }
  1184. #endif
  1185. return -1;
  1186. }
  1187. return accept(s, addr, addrlen);
  1188. }
  1189. /* }}} */
  1190. /* {{{ ftp_getdata
  1191. */
  1192. databuf_t*
  1193. ftp_getdata(ftpbuf_t *ftp TSRMLS_DC)
  1194. {
  1195. int fd = -1;
  1196. databuf_t *data;
  1197. php_sockaddr_storage addr;
  1198. struct sockaddr *sa;
  1199. socklen_t size;
  1200. union ipbox ipbox;
  1201. char arg[sizeof("255, 255, 255, 255, 255, 255")];
  1202. struct timeval tv;
  1203. /* ask for a passive connection if we need one */
  1204. if (ftp->pasv && !ftp_pasv(ftp, 1)) {
  1205. return NULL;
  1206. }
  1207. /* alloc the data structure */
  1208. data = ecalloc(1, sizeof(*data));
  1209. data->listener = -1;
  1210. data->fd = -1;
  1211. data->type = ftp->type;
  1212. sa = (struct sockaddr *) &ftp->localaddr;
  1213. /* bind/listen */
  1214. if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == SOCK_ERR) {
  1215. php_error_docref(NULL TSRMLS_CC, E_WARNING, "socket() failed: %s (%d)", strerror(errno), errno);
  1216. goto bail;
  1217. }
  1218. /* passive connection handler */
  1219. if (ftp->pasv) {
  1220. /* clear the ready status */
  1221. ftp->pasv = 1;
  1222. /* connect */
  1223. /* Win 95/98 seems not to like size > sizeof(sockaddr_in) */
  1224. size = php_sockaddr_size(&ftp->pasvaddr);
  1225. tv.tv_sec = ftp->timeout_sec;
  1226. tv.tv_usec = 0;
  1227. if (php_connect_nonb(fd, (struct sockaddr*) &ftp->pasvaddr, size, &tv) == -1) {
  1228. php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_connect_nonb() failed: %s (%d)", strerror(errno), errno);
  1229. goto bail;
  1230. }
  1231. data->fd = fd;
  1232. ftp->data = data;
  1233. return data;
  1234. }
  1235. /* active (normal) connection */
  1236. /* bind to a local address */
  1237. php_any_addr(sa->sa_family, &addr, 0);
  1238. size = php_sockaddr_size(&addr);
  1239. if (bind(fd, (struct sockaddr*) &addr, size) != 0) {
  1240. php_error_docref(NULL TSRMLS_CC, E_WARNING, "bind() failed: %s (%d)", strerror(errno), errno);
  1241. goto bail;
  1242. }
  1243. if (getsockname(fd, (struct sockaddr*) &addr, &size) != 0) {
  1244. php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname() failed: %s (%d)", strerror(errno), errno);
  1245. goto bail;
  1246. }
  1247. if (listen(fd, 5) != 0) {
  1248. php_error_docref(NULL TSRMLS_CC, E_WARNING, "listen() failed: %s (%d)", strerror(errno), errno);
  1249. goto bail;
  1250. }
  1251. data->listener = fd;
  1252. #if HAVE_IPV6 && HAVE_INET_NTOP
  1253. if (sa->sa_family == AF_INET6) {
  1254. /* need to use EPRT */
  1255. char eprtarg[INET6_ADDRSTRLEN + sizeof("|x||xxxxx|")];
  1256. char out[INET6_ADDRSTRLEN];
  1257. inet_ntop(AF_INET6, &((struct sockaddr_in6*) sa)->sin6_addr, out, sizeof(out));
  1258. snprintf(eprtarg, sizeof(eprtarg), "|2|%s|%hu|", out, ntohs(((struct sockaddr_in6 *) &addr)->sin6_port));
  1259. if (!ftp_putcmd(ftp, "EPRT", eprtarg)) {
  1260. goto bail;
  1261. }
  1262. if (!ftp_getresp(ftp) || ftp->resp != 200) {
  1263. goto bail;
  1264. }
  1265. ftp->data = data;
  1266. return data;
  1267. }
  1268. #endif
  1269. /* send the PORT */
  1270. ipbox.ia[0] = ((struct sockaddr_in*) sa)->sin_addr;
  1271. ipbox.s[2] = ((struct sockaddr_in*) &addr)->sin_port;
  1272. snprintf(arg, sizeof(arg), "%u,%u,%u,%u,%u,%u", ipbox.c[0], ipbox.c[1], ipbox.c[2], ipbox.c[3], ipbox.c[4], ipbox.c[5]);
  1273. if (!ftp_putcmd(ftp, "PORT", arg)) {
  1274. goto bail;
  1275. }
  1276. if (!ftp_getresp(ftp) || ftp->resp != 200) {
  1277. goto bail;
  1278. }
  1279. ftp->data = data;
  1280. return data;
  1281. bail:
  1282. if (fd != -1) {
  1283. closesocket(fd);
  1284. }
  1285. efree(data);
  1286. return NULL;
  1287. }
  1288. /* }}} */
  1289. /* {{{ data_accept
  1290. */
  1291. databuf_t*
  1292. data_accept(databuf_t *data, ftpbuf_t *ftp TSRMLS_DC)
  1293. {
  1294. php_sockaddr_storage addr;
  1295. socklen_t size;
  1296. #if HAVE_OPENSSL_EXT
  1297. SSL_CTX *ctx;
  1298. SSL_SESSION *session;
  1299. int result;
  1300. #endif
  1301. if (data->fd != -1) {
  1302. goto data_accepted;
  1303. }
  1304. size = sizeof(addr);
  1305. data->fd = my_accept(ftp, data->listener, (struct sockaddr*) &addr, &size);
  1306. closesocket(data->listener);
  1307. data->listener = -1;
  1308. if (data->fd == -1) {
  1309. efree(data);
  1310. return NULL;
  1311. }
  1312. data_accepted:
  1313. #if HAVE_OPENSSL_EXT
  1314. /* now enable ssl if we need to */
  1315. if (ftp->use_ssl && ftp->use_ssl_for_data) {
  1316. ctx = SSL_get_SSL_CTX(ftp->ssl_handle);
  1317. if (ctx == NULL) {
  1318. php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to retreive the existing SSL context");
  1319. return 0;
  1320. }
  1321. data->ssl_handle = SSL_new(ctx);
  1322. if (data->ssl_handle == NULL) {
  1323. php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL handle");
  1324. return 0;
  1325. }
  1326. SSL_set_fd(data->ssl_handle, data->fd);
  1327. if (ftp->old_ssl) {
  1328. SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle);
  1329. }
  1330. /* get the session from the control connection so we can re-use it */
  1331. session = SSL_get_session(ftp->ssl_handle);
  1332. if (session == NULL) {
  1333. php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to retreive the existing SSL session");
  1334. SSL_free(data->ssl_handle);
  1335. return 0;
  1336. }
  1337. /* and set it on the data connection */
  1338. result = SSL_set_session(data->ssl_handle, session);
  1339. if (result == 0) {
  1340. php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to set the existing SSL session");
  1341. SSL_free(data->ssl_handle);
  1342. return 0;
  1343. }
  1344. if (SSL_connect(data->ssl_handle) <= 0) {
  1345. php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: SSL/TLS handshake failed");
  1346. SSL_shutdown(data->ssl_handle);
  1347. SSL_free(data->ssl_handle);
  1348. return 0;
  1349. }
  1350. data->ssl_active = 1;
  1351. }
  1352. #endif
  1353. return data;
  1354. }
  1355. /* }}} */
  1356. /* {{{ data_close
  1357. */
  1358. databuf_t*
  1359. data_close(ftpbuf_t *ftp, databuf_t *data)
  1360. {
  1361. #if HAVE_OPENSSL_EXT
  1362. SSL_CTX *ctx;
  1363. #endif
  1364. if (data == NULL) {
  1365. return NULL;
  1366. }
  1367. if (data->listener != -1) {
  1368. #if HAVE_OPENSSL_EXT
  1369. if (data->ssl_active) {
  1370. /* don't free the data context, it's the same as the control */
  1371. SSL_shutdown(data->ssl_handle);
  1372. SSL_free(data->ssl_handle);
  1373. data->ssl_active = 0;
  1374. }
  1375. #endif
  1376. closesocket(data->listener);
  1377. }
  1378. if (data->fd != -1) {
  1379. #if HAVE_OPENSSL_EXT
  1380. if (data->ssl_active) {
  1381. /* don't free the data context, it's the same as the control */
  1382. SSL_shutdown(data->ssl_handle);
  1383. SSL_free(data->ssl_handle);
  1384. data->ssl_active = 0;
  1385. }
  1386. #endif
  1387. closesocket(data->fd);
  1388. }
  1389. if (ftp) {
  1390. ftp->data = NULL;
  1391. }
  1392. efree(data);
  1393. return NULL;
  1394. }
  1395. /* }}} */
  1396. /* {{{ ftp_genlist
  1397. */
  1398. char**
  1399. ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path TSRMLS_DC)
  1400. {
  1401. php_stream *tmpstream = NULL;
  1402. databuf_t *data = NULL;
  1403. char *ptr;
  1404. int ch, lastch;
  1405. size_t size, rcvd;
  1406. size_t lines;
  1407. char **ret = NULL;
  1408. char **entry;
  1409. char *text;
  1410. if ((tmpstream = php_stream_fopen_tmpfile()) == NULL) {
  1411. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create temporary file. Check permissions in temporary files directory.");
  1412. return NULL;
  1413. }
  1414. if (!ftp_type(ftp, FTPTYPE_ASCII)) {
  1415. goto bail;
  1416. }
  1417. if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
  1418. goto bail;
  1419. }
  1420. ftp->data = data;
  1421. if (!ftp_putcmd(ftp, cmd, path)) {
  1422. goto bail;
  1423. }
  1424. if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125 && ftp->resp != 226)) {
  1425. goto bail;
  1426. }
  1427. /* some servers don't open a ftp-data connection if the directory is empty */
  1428. if (ftp->resp == 226) {
  1429. ftp->data = data_close(ftp, data);
  1430. php_stream_close(tmpstream);
  1431. return ecalloc(1, sizeof(char*));
  1432. }
  1433. /* pull data buffer into tmpfile */
  1434. if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
  1435. goto bail;
  1436. }
  1437. size = 0;
  1438. lines = 0;
  1439. lastch = 0;
  1440. while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
  1441. if (rcvd == -1 || rcvd > ((size_t)(-1))-size) {
  1442. goto bail;
  1443. }
  1444. php_stream_write(tmpstream, data->buf, rcvd);
  1445. size += rcvd;
  1446. for (ptr = data->buf; rcvd; rcvd--, ptr++) {
  1447. if (*ptr == '\n' && lastch == '\r') {
  1448. lines++;
  1449. }
  1450. lastch = *ptr;
  1451. }
  1452. }
  1453. ftp->data = data_close(ftp, data);
  1454. php_stream_rewind(tmpstream);
  1455. ret = safe_emalloc((lines + 1), sizeof(char*), size);
  1456. entry = ret;
  1457. text = (char*) (ret + lines + 1);
  1458. *entry = text;
  1459. lastch = 0;
  1460. while ((ch = php_stream_getc(tmpstream)) != EOF) {
  1461. if (ch == '\n' && lastch == '\r') {
  1462. *(text - 1) = 0;
  1463. *++entry = text;
  1464. } else {
  1465. *text++ = ch;
  1466. }
  1467. lastch = ch;
  1468. }
  1469. *entry = NULL;
  1470. php_stream_close(tmpstream);
  1471. if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
  1472. efree(ret);
  1473. return NULL;
  1474. }
  1475. return ret;
  1476. bail:
  1477. ftp->data = data_close(ftp, data);
  1478. php_stream_close(tmpstream);
  1479. if (ret)
  1480. efree(ret);
  1481. return NULL;
  1482. }
  1483. /* }}} */
  1484. /* {{{ ftp_nb_get
  1485. */
  1486. int
  1487. ftp_nb_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, long resumepos TSRMLS_DC)
  1488. {
  1489. databuf_t *data = NULL;
  1490. char arg[11];
  1491. if (ftp == NULL) {
  1492. return PHP_FTP_FAILED;
  1493. }
  1494. if (!ftp_type(ftp, type)) {
  1495. goto bail;
  1496. }
  1497. if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
  1498. goto bail;
  1499. }
  1500. if (resumepos>0) {
  1501. snprintf(arg, sizeof(arg), "%ld", resumepos);
  1502. if (!ftp_putcmd(ftp, "REST", arg)) {
  1503. goto bail;
  1504. }
  1505. if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
  1506. goto bail;
  1507. }
  1508. }
  1509. if (!ftp_putcmd(ftp, "RETR", path)) {
  1510. goto bail;
  1511. }
  1512. if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
  1513. goto bail;
  1514. }
  1515. if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
  1516. goto bail;
  1517. }
  1518. ftp->data = data;
  1519. ftp->stream = outstream;
  1520. ftp->lastch = 0;
  1521. ftp->nb = 1;
  1522. return (ftp_nb_continue_read(ftp TSRMLS_CC));
  1523. bail:
  1524. ftp->data = data_close(ftp, data);
  1525. return PHP_FTP_FAILED;
  1526. }
  1527. /* }}} */
  1528. /* {{{ ftp_nb_continue_read
  1529. */
  1530. int
  1531. ftp_nb_continue_read(ftpbuf_t *ftp TSRMLS_DC)
  1532. {
  1533. databuf_t *data = NULL;
  1534. char *ptr;
  1535. int lastch;
  1536. size_t rcvd;
  1537. ftptype_t type;
  1538. data = ftp->data;
  1539. /* check if there is already more data */
  1540. if (!data_available(ftp, data->fd)) {
  1541. return PHP_FTP_MOREDATA;
  1542. }
  1543. type = ftp->type;
  1544. lastch = ftp->lastch;
  1545. if ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
  1546. if (rcvd == -1) {
  1547. goto bail;
  1548. }
  1549. if (type == FTPTYPE_ASCII) {
  1550. for (ptr = data->buf; rcvd; rcvd--, ptr++) {
  1551. if (lastch == '\r' && *ptr != '\n') {
  1552. php_stream_putc(ftp->stream, '\r');
  1553. }
  1554. if (*ptr != '\r') {
  1555. php_stream_putc(ftp->stream, *ptr);
  1556. }
  1557. lastch = *ptr;
  1558. }
  1559. } else if (rcvd != php_stream_write(ftp->stream, data->buf, rcvd)) {
  1560. goto bail;
  1561. }
  1562. ftp->lastch = lastch;
  1563. return PHP_FTP_MOREDATA;
  1564. }
  1565. if (type == FTPTYPE_ASCII && lastch == '\r') {
  1566. php_stream_putc(ftp->stream, '\r');
  1567. }
  1568. ftp->data = data = data_close(ftp, data);
  1569. if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
  1570. goto bail;
  1571. }
  1572. ftp->nb = 0;
  1573. return PHP_FTP_FINISHED;
  1574. bail:
  1575. ftp->nb = 0;
  1576. ftp->data = data_close(ftp, data);
  1577. return PHP_FTP_FAILED;
  1578. }
  1579. /* }}} */
  1580. /* {{{ ftp_nb_put
  1581. */
  1582. int
  1583. ftp_nb_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, long startpos TSRMLS_DC)
  1584. {
  1585. databuf_t *data = NULL;
  1586. char arg[11];
  1587. if (ftp == NULL) {
  1588. return 0;
  1589. }
  1590. if (!ftp_type(ftp, type)) {
  1591. goto bail;
  1592. }
  1593. if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
  1594. goto bail;
  1595. }
  1596. if (startpos > 0) {
  1597. snprintf(arg, sizeof(arg), "%ld", startpos);
  1598. if (!ftp_putcmd(ftp, "REST", arg)) {
  1599. goto bail;
  1600. }
  1601. if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
  1602. goto bail;
  1603. }
  1604. }
  1605. if (!ftp_putcmd(ftp, "STOR", path)) {
  1606. goto bail;
  1607. }
  1608. if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
  1609. goto bail;
  1610. }
  1611. if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
  1612. goto bail;
  1613. }
  1614. ftp->data = data;
  1615. ftp->stream = instream;
  1616. ftp->lastch = 0;
  1617. ftp->nb = 1;
  1618. return (ftp_nb_continue_write(ftp TSRMLS_CC));
  1619. bail:
  1620. ftp->data = data_close(ftp, data);
  1621. return PHP_FTP_FAILED;
  1622. }
  1623. /* }}} */
  1624. /* {{{ ftp_nb_continue_write
  1625. */
  1626. int
  1627. ftp_nb_continue_write(ftpbuf_t *ftp TSRMLS_DC)
  1628. {
  1629. long size;
  1630. char *ptr;
  1631. int ch;
  1632. /* check if we can write more data */
  1633. if (!data_writeable(ftp, ftp->data->fd)) {
  1634. return PHP_FTP_MOREDATA;
  1635. }
  1636. size = 0;
  1637. ptr = ftp->data->buf;
  1638. while (!php_stream_eof(ftp->stream) && (ch = php_stream_getc(ftp->stream)) != EOF) {
  1639. if (ch == '\n' && ftp->type == FTPTYPE_ASCII) {
  1640. *ptr++ = '\r';
  1641. size++;
  1642. }
  1643. *ptr++ = ch;
  1644. size++;
  1645. /* flush if necessary */
  1646. if (FTP_BUFSIZE - size < 2) {
  1647. if (my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
  1648. goto bail;
  1649. }
  1650. return PHP_FTP_MOREDATA;
  1651. }
  1652. }
  1653. if (size && my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
  1654. goto bail;
  1655. }
  1656. ftp->data = data_close(ftp, ftp->data);
  1657. if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
  1658. goto bail;
  1659. }
  1660. ftp->nb = 0;
  1661. return PHP_FTP_FINISHED;
  1662. bail:
  1663. ftp->data = data_close(ftp, ftp->data);
  1664. ftp->nb = 0;
  1665. return PHP_FTP_FAILED;
  1666. }
  1667. /* }}} */
  1668. #endif /* HAVE_FTP */
  1669. /*
  1670. * Local variables:
  1671. * tab-width: 4
  1672. * c-basic-offset: 4
  1673. * End:
  1674. * vim600: sw=4 ts=4 fdm=marker
  1675. * vim<600: sw=4 ts=4
  1676. */