📄 ftp.c
字号:
/* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2007 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andrew Skalski <askalski@chek.com> | | Stefan Esser <sesser@php.net> (resume functions) | +----------------------------------------------------------------------+ *//* $Id: ftp.c,v 1.68.2.22.2.6 2007/03/24 16:26:50 iliaa Exp $ */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include "php.h"#if HAVE_FTP#include <stdio.h>#include <ctype.h>#include <stdlib.h>#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#include <fcntl.h>#include <string.h>#include <time.h>#ifdef PHP_WIN32#include <winsock.h>#elif defined(NETWARE)#ifdef USE_WINSOCK /* Modified to use Winsock (NOVSOCK2.H), atleast for now */#include <novsock2.h>#else#ifdef NEW_LIBC#include <sys/socket.h>#include <netinet/in.h>#include <netdb.h>#else#include <sys/socket.h>#endif#endif#else#ifdef HAVE_SYS_TYPES_H#include <sys/types.h>#endif#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <netdb.h>#endif#include <errno.h>#if HAVE_SYS_TIME_H#include <sys/time.h>#endif#ifdef HAVE_SYS_SELECT_H#include <sys/select.h>#endif#include "ftp.h"#include "ext/standard/fsock.h"/* sends an ftp command, returns true on success, false on error. * it sends the string "cmd args\r\n" if args is non-null, or * "cmd\r\n" if args is null */static int ftp_putcmd( ftpbuf_t *ftp, const char *cmd, const char *args);/* wrapper around send/recv to handle timeouts */static int my_send(ftpbuf_t *ftp, int s, void *buf, size_t len);static int my_recv(ftpbuf_t *ftp, int s, void *buf, size_t len);static int my_accept(ftpbuf_t *ftp, int s, struct sockaddr *addr, socklen_t *addrlen);/* reads a line the socket , returns true on success, false on error */static int ftp_readline(ftpbuf_t *ftp);/* reads an ftp response, returns true on success, false on error */static int ftp_getresp(ftpbuf_t *ftp);/* sets the ftp transfer type */static int ftp_type(ftpbuf_t *ftp, ftptype_t type);/* opens up a data stream */static databuf_t* ftp_getdata(ftpbuf_t *ftp TSRMLS_DC);/* accepts the data connection, returns updated data buffer */static databuf_t* data_accept(databuf_t *data, ftpbuf_t *ftp);/* closes the data connection, returns NULL */static databuf_t* data_close(ftpbuf_t *ftp, databuf_t *data);/* generic file lister */static char** ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path TSRMLS_DC);/* IP and port conversion box */union ipbox { struct in_addr ia[2]; unsigned short s[4]; unsigned char c[8];};/* {{{ ftp_open */ftpbuf_t*ftp_open(const char *host, short port, long timeout_sec TSRMLS_DC){ ftpbuf_t *ftp; socklen_t size; struct timeval tv; /* alloc the ftp structure */ ftp = ecalloc(1, sizeof(*ftp)); tv.tv_sec = timeout_sec; tv.tv_usec = 0; ftp->fd = php_hostconnect(host, (unsigned short) (port ? port : 21), SOCK_STREAM, &tv TSRMLS_CC); if (ftp->fd == -1) { goto bail; } /* Default Settings */ ftp->timeout_sec = timeout_sec; ftp->nb = 0; size = sizeof(ftp->localaddr); memset(&ftp->localaddr, 0, size); if (getsockname(ftp->fd, (struct sockaddr*) &ftp->localaddr, &size) == -1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname failed: %s (%d)\n", strerror(errno), errno); goto bail; } if (!ftp_getresp(ftp) || ftp->resp != 220) { goto bail; } return ftp;bail: if (ftp->fd != -1) closesocket(ftp->fd); efree(ftp); return NULL;}/* }}} *//* {{{ ftp_close */ftpbuf_t*ftp_close(ftpbuf_t *ftp){ if (ftp == NULL) return NULL; if (ftp->data) data_close(ftp, ftp->data); if (ftp->fd != -1) {#ifdef HAVE_OPENSSL_EXT if (ftp->ssl_active) { SSL_shutdown(ftp->ssl_handle); }#endif closesocket(ftp->fd); } ftp_gc(ftp); efree(ftp); return NULL;}/* }}} *//* {{{ ftp_gc */voidftp_gc(ftpbuf_t *ftp){ if (ftp == NULL) return; if (ftp->pwd) { efree(ftp->pwd); ftp->pwd = NULL; } if (ftp->syst) { efree(ftp->syst); ftp->syst = NULL; }}/* }}} *//* {{{ ftp_quit */intftp_quit(ftpbuf_t *ftp){ if (ftp == NULL) return 0; if (!ftp_putcmd(ftp, "QUIT", NULL)) return 0; if (!ftp_getresp(ftp) || ftp->resp != 221) return 0; if (ftp->pwd) { efree(ftp->pwd); ftp->pwd = NULL; } return 1;}/* }}} *//* {{{ ftp_login */intftp_login(ftpbuf_t *ftp, const char *user, const char *pass TSRMLS_DC){#ifdef HAVE_OPENSSL_EXT SSL_CTX *ctx = NULL;#endif if (ftp == NULL) return 0;#ifdef HAVE_OPENSSL_EXT if (ftp->use_ssl && !ftp->ssl_active) { if (!ftp_putcmd(ftp, "AUTH", "TLS")) return 0; if (!ftp_getresp(ftp)) return 0; if (ftp->resp != 234) { if (!ftp_putcmd(ftp, "AUTH", "SSL")) return 0; if (!ftp_getresp(ftp)) return 0; if (ftp->resp != 334) { ftp->use_ssl = 0; } else { ftp->old_ssl = 1; ftp->use_ssl_for_data = 1; } } /* now enable ssl if we still need to */ if (ftp->use_ssl) { ctx = SSL_CTX_new(SSLv23_client_method()); if (ctx == NULL) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "ftp_login: failed to create the SSL context"); return 0; } SSL_CTX_set_options(ctx, SSL_OP_ALL); ftp->ssl_handle = SSL_new(ctx); if (ftp->ssl_handle == NULL) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "ftp_login: failed to create the SSL handle"); SSL_CTX_free(ctx); return 0; } SSL_set_fd(ftp->ssl_handle, ftp->fd); if (SSL_connect(ftp->ssl_handle) <= 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "ftp_login: SSL/TLS handshake failed"); SSL_shutdown(ftp->ssl_handle); return 0; } ftp->ssl_active = 1; if (!ftp->old_ssl) { /* set protection buffersize to zero */ if (!ftp_putcmd(ftp, "PBSZ", "0")) return 0; if (!ftp_getresp(ftp)) return 0; /* enable data conn encryption */ if (!ftp_putcmd(ftp, "PROT", "P")) return 0; if (!ftp_getresp(ftp)) return 0; ftp->use_ssl_for_data = (ftp->resp >= 200 && ftp->resp <=299); } } }#endif if (!ftp_putcmd(ftp, "USER", user)) return 0; if (!ftp_getresp(ftp)) return 0; if (ftp->resp == 230) return 1; if (ftp->resp != 331) return 0; if (!ftp_putcmd(ftp, "PASS", pass)) return 0; if (!ftp_getresp(ftp)) return 0; return (ftp->resp == 230);}/* }}} *//* {{{ ftp_reinit */intftp_reinit(ftpbuf_t *ftp){ if (ftp == NULL) return 0; ftp_gc(ftp); ftp->nb = 0; if (!ftp_putcmd(ftp, "REIN", NULL)) return 0; if (!ftp_getresp(ftp) || ftp->resp != 220) return 0; return 1;}/* }}} *//* {{{ ftp_syst */const char*ftp_syst(ftpbuf_t *ftp){ char *syst, *end; if (ftp == NULL) return NULL; /* default to cached value */ if (ftp->syst) return ftp->syst; if (!ftp_putcmd(ftp, "SYST", NULL)) return NULL; if (!ftp_getresp(ftp) || ftp->resp != 215) return NULL; syst = ftp->inbuf; while (*syst == ' ') { syst++; } if ((end = strchr(syst, ' '))) *end = 0; ftp->syst = estrdup(syst); if (end) *end = ' '; return ftp->syst;}/* }}} *//* {{{ ftp_pwd */const char*ftp_pwd(ftpbuf_t *ftp){ char *pwd, *end; if (ftp == NULL) return NULL; /* default to cached value */ if (ftp->pwd) return ftp->pwd; if (!ftp_putcmd(ftp, "PWD", NULL)) return NULL; if (!ftp_getresp(ftp) || ftp->resp != 257) return NULL; /* copy out the pwd from response */ if ((pwd = strchr(ftp->inbuf, '"')) == NULL) return NULL; if ((end = strrchr(++pwd, '"')) == NULL) return NULL; ftp->pwd = estrndup(pwd, end - pwd); return ftp->pwd;}/* }}} *//* {{{ ftp_exec */intftp_exec(ftpbuf_t *ftp, const char *cmd){ if (ftp == NULL) return 0; if (!ftp_putcmd(ftp, "SITE EXEC", cmd)) return 0; if (!ftp_getresp(ftp) || ftp->resp != 200) return 0; return 1;}/* }}} *//* {{{ ftp_chdir */intftp_chdir(ftpbuf_t *ftp, const char *dir){ if (ftp == NULL) return 0; if (ftp->pwd) { efree(ftp->pwd); ftp->pwd = NULL; } if (!ftp_putcmd(ftp, "CWD", dir)) return 0; if (!ftp_getresp(ftp) || ftp->resp != 250) return 0; return 1;}/* }}} *//* {{{ ftp_cdup */intftp_cdup(ftpbuf_t *ftp){ if (ftp == NULL) return 0; if (ftp->pwd) { efree(ftp->pwd); ftp->pwd = NULL; } if (!ftp_putcmd(ftp, "CDUP", NULL)) return 0; if (!ftp_getresp(ftp) || ftp->resp != 250) return 0; return 1;}/* }}} *//* {{{ ftp_mkdir */char*ftp_mkdir(ftpbuf_t *ftp, const char *dir){ char *mkd, *end; if (ftp == NULL) return NULL; if (!ftp_putcmd(ftp, "MKD", dir)) return NULL; if (!ftp_getresp(ftp) || ftp->resp != 257) return NULL; /* copy out the dir from response */ if ((mkd = strchr(ftp->inbuf, '"')) == NULL) { mkd = estrdup(dir); return mkd; } if ((end = strrchr(++mkd, '"')) == NULL) { return NULL; } *end = 0; mkd = estrdup(mkd); *end = '"'; return mkd;}/* }}} *//* {{{ ftp_rmdir */intftp_rmdir(ftpbuf_t *ftp, const char *dir){ if (ftp == NULL) return 0; if (!ftp_putcmd(ftp, "RMD", dir)) return 0; if (!ftp_getresp(ftp) || ftp->resp != 250) return 0; return 1;}/* }}} *//* {{{ ftp_nlist */char**ftp_nlist(ftpbuf_t *ftp, const char *path TSRMLS_DC){ return ftp_genlist(ftp, "NLST", path TSRMLS_CC);}/* }}} *//* {{{ ftp_list */char**ftp_list(ftpbuf_t *ftp, const char *path, int recursive TSRMLS_DC){ return ftp_genlist(ftp, ((recursive) ? "LIST -R" : "LIST"), path TSRMLS_CC);}/* }}} *//* {{{ ftp_type */intftp_type(ftpbuf_t *ftp, ftptype_t type){ char typechar[2] = "?"; if (ftp == NULL) return 0; if (type == ftp->type) return 1; if (type == FTPTYPE_ASCII) typechar[0] = 'A'; else if (type == FTPTYPE_IMAGE) typechar[0] = 'I'; else return 0; if (!ftp_putcmd(ftp, "TYPE", typechar)) return 0; if (!ftp_getresp(ftp) || ftp->resp != 200) return 0; ftp->type = type; return 1;}/* }}} *//* {{{ ftp_pasv */intftp_pasv(ftpbuf_t *ftp, int pasv){ char *ptr; union ipbox ipbox; unsigned long b[6]; socklen_t n; struct sockaddr *sa; struct sockaddr_in *sin; if (ftp == NULL) return 0; if (pasv && ftp->pasv == 2) return 1; ftp->pasv = 0; if (!pasv) return 1; n = sizeof(ftp->pasvaddr); memset(&ftp->pasvaddr, 0, n); sa = (struct sockaddr *) &ftp->pasvaddr;#ifdef HAVE_IPV6 if (getpeername(ftp->fd, sa, &n) < 0) return 0; if (sa->sa_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa; char *endptr, delimiter; /* try EPSV first */ if (!ftp_putcmd(ftp, "EPSV", NULL)) return 0; if (!ftp_getresp(ftp)) return 0; if (ftp->resp == 229) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -