post by tommy @ 07 九月, 2008 10:17
在 php 官方的說明文件中, 提到要使用 ftp_ssl_connect() 必須要有 openssl 的支援才可以. 而且在 Windows 下頭, 必須要自行重新編譯一個內建 openssl 支援的版本才可以.
文件上頭, 有人提到了這個網頁, 上頭有說明如何自行在 Windows 下頭編譯出支援 ftp_ssl_connect() 的 php 版本. 不過上頭的說明, 也許是編排有誤的關係, 發現有一段少了幾句話, 會無法正常的編譯出 php 的執行檔. 所以我重新整理了一下.
- 安裝 Visual C++.
使用 Visual Studio Express 或 Visual Studio 2005 都可以. - 安裝所需要的函式庫.
到這兒下載 zip.zip, 這是編譯 php 所需要的 library. - 如果你編譯 php 時, 有出現找不到 xmlXPathCompiledEvalToBoolean 的錯誤時, 表示你下載的 zip.zip 裡頭的 libxml2 並未更新到新的版本. 請到這兒抓取新的 libxml2 回來.
- 到 php 官方網站下載 php 的原始碼.
我是使用 php 5.2.6 的版本. - 把 php 的原始碼解開來, 放在 c:\work\php5 下頭.
- 把 zip.zip 裡頭的 php_build 解開來, 放到 c:\work\php_build 下頭.
- 如果有 xml 的錯誤, 把 libxml2 解開來, 裡頭的 include, lib, util 目錄, 搬到 c:\work\php_build 下頭, 覆蓋原來的檔案.
- 打開一個 Command Prompt 視窗, 然後執行 vcvars32.bat
這個批次檔應該是在你的 VC++ 下頭的 bin 目錄中. - 使用 patch 修正你的 ftp.c, 修正如下:
檔案可以由這兒抓取.--- php-5.2.6.orig/ext/ftp/ftp.c Mon Dec 31 15:20:06 2007
+++ php-5.2.6/ext/ftp/ftp.c Sun Sep 7 10:10:39 2008
@@ -242,6 +242,7 @@
ftp_login(ftpbuf_t *ftp, const char *user, const char *pass TSRMLS_DC)
{
#if HAVE_OPENSSL_EXT
+ int errcode;
SSL_CTX *ctx = NULL;
#endif
if (ftp == NULL) {
@@ -290,11 +291,23 @@
SSL_set_fd(ftp->ssl_handle, ftp->fd);
- if (SSL_connect(ftp->ssl_handle) <= 0) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS handshake failed");
- SSL_shutdown(ftp->ssl_handle);
- return 0;
- }
+ do {
+ errcode = SSL_connect(ftp->ssl_handle);
+ switch (SSL_get_error(ftp->ssl_handle, errcode)) {
+ case SSL_ERROR_NONE:
+ errcode = 1;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ errcode = 0;
+ break;
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS handshake failed");
+ SSL_shutdown(ftp->ssl_handle);
+ return 0;
+ }
+ } while (errcode == 0 && !SSL_is_init_finished(ftp->ssl_handle));
ftp->ssl_active = 1;
@@ -1272,6 +1285,9 @@
my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
{
int n, nr_bytes;
+#if HAVE_OPENSSL_EXT
+ int no_error;
+#endif
n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
if (n < 1) {
@@ -1287,7 +1303,25 @@
if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
nr_bytes = SSL_read(ftp->ssl_handle, buf, len);
} else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
- nr_bytes = SSL_read(ftp->data->ssl_handle, buf, len);
+ no_error = 1; // there is no error
+ do {
+ nr_bytes = SSL_read(ftp->data->ssl_handle, buf, len);
+ switch (SSL_get_error(ftp->data->ssl_handle, nr_bytes)) {
+ case SSL_ERROR_NONE:
+ case SSL_ERROR_ZERO_RETURN:
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ case SSL_ERROR_WANT_READ:
+ break;
+ case SSL_ERROR_WANT_CONNECT:
+ case SSL_ERROR_WANT_ACCEPT:
+ case SSL_ERROR_SSL:
+ case SSL_ERROR_SYSCALL:
+ default:
+ no_error = 0; //major error, get out
+ break;
+ }
+ } while (nr_bytes < 0 && no_error);
} else {
#endif
nr_bytes = recv(s, buf, len, 0);
@@ -1491,6 +1525,7 @@
socklen_t size;
#if HAVE_OPENSSL_EXT
+ int errcode;
SSL_CTX *ctx;
#endif
@@ -1534,11 +1569,23 @@
SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle);
}
- if (SSL_connect(data->ssl_handle) <= 0) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: SSL/TLS handshake failed");
- SSL_shutdown(data->ssl_handle);
- return 0;
- }
+ do {
+ errcode = SSL_connect(data->ssl_handle);
+ switch (SSL_get_error(data->ssl_handle, errcode)) {
+ case SSL_ERROR_NONE:
+ errcode = 1;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ errcode = 0;
+ break;
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: SSL/TLS handshake failed");
+ SSL_shutdown(data->ssl_handle);
+ return 0;
+ }
+ } while (errcode == 0 && !SSL_is_init_finished(data->ssl_handle));
data->ssl_active = 1;
} - 執行下列幾個指令.
set PATH=%PATH%;C:\work\php_build\bin;
set BISON_SIMPLE=C:\work\php_build\bin\bison.simple
set INCLUDE=C:\work\php_build\include;%INCLUDE%
set LIB=C:\work\php_build\lib;%LIB%
set LIBPATH=C:\work\php_build\lib;%LIBPATH% - 開始編譯 php. 執行下列的指令:cd c:\work\php5
buildconf
cscript /nologo configure.js --with-openssl
nmake - 如果沒有錯誤 (會有一堆 warnning, 可以忽略), 最後在 c:\work\php5\Release_TS 下頭會有最後產生的檔案. 把 php.exe, php5ts.dll, php-cgi.exe 複製到你原本安裝 php 的目錄下, 覆蓋舊的檔案.
- 把 zip.zip 裡頭 dev\template\dlls 裡頭的 libeay32.dll, ssleay32.dll 複製到你原本安裝的 php 的目錄下.
幾過上頭的動作, 你的 php 就應該可以使用 ftp_ssl_connect() 了. 如果你不知道怎麼編譯, 且不怕我編出來的執行檔有問題的話, 可以下載這個檔案 (php 5.2.6) 回去, 解開來放到你安裝 php 的目錄下, 覆蓋舊的檔案就可以了. 不過.... 如果因為這個檔案所造成的任何問題, 請自行負責, 不要來找我. :-)