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