* @copyright 1997-2005 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id$ * @link http://pear.php.net/package/Net_FTP * @since File available since Release 0.0.1 */ error_reporting(E_ALL|E_STRICT); /** * Default FTP extension constants */ define('FTP_ASCII', 0); define('FTP_TEXT', 0); define('FTP_BINARY', 1); define('FTP_IMAGE', 1); define('FTP_TIMEOUT_SEC', 0); /** * What needs to be done overall? * #1 Install the rest of these functions * #2 Document better * #3 Alot of other things I don't remember */ # # # # # # # # !!! NOTE !!! # # # # # # # # # Most of the comment's are "not working", # # meaning they are not all up-to-date # # # # # # # # # !!! NOTE !!! # # # # # # # # /** * &resource ftp_connect ( string host [, int port [, int timeout ] ] ); * * Opens an FTP connection and return resource or false on failure. * * TODO: * The FTP extension has ftp_get_option() function which returns the * timeout variable. This function needs to be created and contain it as * static variable. * * TODO: * The FTP extension has ftp_set_option() function which sets the * timeout variable. This function needs to be created and called here. * * FTP Success respons code: 220 * * @param string $host ( Host to connect to ) * @param int $port ( Optional, port to connect to ) * @param int $timeout( Optional, seconds until function timeouts ) * @return &resource */ function &ftp_connect($host, $port = 21, $timeout = 90) { $false = false; // We are going to return refrence (E_STRICT) if (!is_string($host) || !is_integer($port) || !is_integer($timeout)) { return $false; } $control = @fsockopen($host, $port, $iError, $sError, $timeout); $GLOBALS['_NET_FTP']['timeout'] = $timeout; if (!is_resource($control)) { return $false; } stream_set_blocking($control, TRUE); stream_set_timeout($control, $timeout); do { $content[] = fgets($control, 8129); $array = socket_get_status($control); } while ($array['unread_bytes'] > 0); if (substr($content[count($content)-1], 0, 3) == 220) { return $control; } return $false; } /** * boolean ftp_login ( resource stream, string username, string password ); * * Logs in to an given FTP connection stream. * Returns TRUE on success or FALSE on failure. * * NOTE: * Username and password are *not* optional. Function will *not* * assume "anonymous" if username and/or password is empty * * FTP Success respons code: 230 * * @param resource $stream ( FTP resource to login to ) * @param string $username ( FTP Username to be used ) * @param string $password ( FTP Password to be used ) * @return boolean */ function ftp_login(&$control, $username, $password) { if (!is_resource($control) || is_null($username)) { return false; } fputs($control, 'USER '.$username."\r\n"); $contents = array(); do { $contents[] = fgets($control, 8192); $array = socket_get_status($control); } while ($array['unread_bytes'] > 0); if (substr($contents[count($contents)-1], 0, 3) != 331) { return false; } fputs($control, 'PASS '.$password."\r\n"); $contents = array(); do { $contents[] = fgets($control, 8192); $array = socket_get_status($control); } while ($array['unread_bytes']); if (substr($contents[count($contents)-1], 0, 3) == 230) { return true; } trigger_error('ftp_login() [function.ftp-login]: ' .$contents[count($contents)-1], E_USER_WARNING); return false; } /** * boolean ftp_quit ( resource stream ); * * Closes FTP connection. * Returns TRUE or FALSE on error. * * NOTE: The PHP function ftp_quit is *alias* to ftp_close, here it is * the *other-way-around* ( ftp_close() is alias to ftp_quit() ). * * NOTE: * resource is set to NULL since unset() can't unset the variable. * * @param integer $stream ( FTP resource ) * @return boolean */ function ftp_quit(&$control) { if (!is_resource($control)) { return false; } fputs($control, 'QUIT'."\r\n"); fclose($control); $control = NULL; return TRUE; } /** * Alias to ftp_quit() */ function ftp_close(&$control) { return ftp_quit($control); } /** * string ftp_pwd ( resource stream ); * * Gets the current directory name. * Returns the current directory. * * Needs data connection: NO * Success respons code: 257 * * @param integer $stream ( FTP resource ) * @return string */ function ftp_pwd(&$control) { if (!is_resource($control)) { return $control; } fputs($control, 'PWD'."\r\n"); $content = array(); do { $content[] = fgets($control, 8192); $array = socket_get_status($control); } while ($array['unread_bytes'] > 0); if (substr($cont = $content[count($content)-1], 0, 3) == 257) { $pos = strpos($cont, '"')+1; $pos2 = strrpos($cont, '"') - $pos; $path = substr($cont, $pos, $pos2); return $path; } return false; } /** * boolean ftp_chdir ( resource stream, string directory ); * * Changes the current directory to the specified directory. * Returns TRUE on success or FALSE on failure. * * FTP success respons code: 250 * Needs data connection: NO * * @param integer $stream ( FTP stream ) * @param string $pwd ( Directory name ) * @return boolean */ function ftp_chdir(&$control, $pwd) { if (!is_resource($control) || !is_string($pwd)) { return false; } fputs($control, 'CWD '.$pwd."\r\n"); $content = array(); do { $content[] = fgets($control, 8192); $array = socket_get_status($control); } while ($array['unread_bytes'] > 0); if (substr($content[count($content)-1], 0, 3) == 250) { return true; } trigger_error ('ftp_chdir() [function.ftp-chdir]: ' .$content[count($content)-1], E_USER_WARNING); return false; } /** * boolean ftp_pasv ( resource stream, boolean passive ); * * Toggles passive mode ON/OFF. * Returns TRUE on success or FALSE on failure. * * Comment: * Althou my lack of C knowlage I checked how the PHP FTP extension * do things here. Seems like they create the data connection and store * it in object for other functions to use. * This is now done here. * * FTP success respons code: 227 * * @param stream $control ( FTP stream ) * @return boolean */ $_NET_FTP = array(); $_NET_FTP['USE_PASSIVE'] = false; $_NET_FTP['DATA'] = null; function ftp_pasv(&$control, $pasv) { if (!is_resource($control) || !is_bool($pasv)) { return false; } // If data connection exists, destroy it if (isset($GLOBALS['_NET_FTP']['DATA'])) { fclose($GLOBALS['_NET_FTP']['DATA']); $GLOBALS['_NET_FTP']['DATA'] = null; do { fgets($control, 16); $array = socket_get_status($control); } while ($array['unread_bytes'] > 0); } // Are we suppost to create active or passive connection? if (!$pasv) { $GLOBALS['_NET_FTP']['USE_PASSIVE'] = false; # Pick random "low bit" $low = rand(39, 250); # Pick random "high bit" $high = rand(39, 250); # Lowest possible port would be; 10023 # Highest possible port would be; 64246 $port = ($low<<8)+$high; $ip = str_replace('.', ',', $_SERVER['SERVER_ADDR']); $s = $ip.','.$low.','.$high; $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if (is_resource($socket)) { if (socket_bind($socket, '0.0.0.0', $port)) { if (socket_listen($socket)) { $GLOBALS['_NET_FTP']['DATA'] = &$socket; fputs($control, 'PORT '.$s."\r\n"); $line = fgets($control, 512); if (substr($line, 0, 3) == 200) { return true; } } } } return false; } # Since we are here, we are suppost to create passive data connection. $i = fputs($control, 'PASV' ."\r\n"); $content = array(); do { $content[] = fgets($control, 128); $array = socket_get_status($control); } while ($array['unread_bytes']); if (substr($cont=$content[count($content)-1], 0, 3) != 227) { return false; } $pos = strpos($cont, '(')+1; $pos2 = strrpos($cont, ')')-$pos; $string = substr($cont, $pos, $pos2); $array = split(',', $string); # IP we are connecting to $ip = $array[0]. '.' .$array[1]. '.' .$array[2]. '.' .$array[3]; # Port ( 256*lowbit + highbit $port = ($array[4] << 8)+$array[5]; # Our data connection $data = fsockopen($ip, $port, $iError, $sError, $GLOBALS['_NET_FTP']['timeout']); if (is_resource($data)) { $GLOBALS['_NET_FTP']['USE_PASSIVE'] = true; $GLOBALS['_NET_FTP']['DATA'] = &$data; stream_set_blocking($data, true); stream_set_timeout($data, $GLOBALS['_NET_FTP']['timeout']); return true; } return false; } /** * array ftp_rawlist ( resource stream, string directory [,bool recursive] ); * * Returns a detailed list of files in the given directory. * * TODO: * Enable the recursive feature. * * Needs data connection: YES * * @param integer $stream ( FTP resource ) * @param string $pwd ( Path to retrive ) * @param boolean $recursive ( Optional, retrive recursive listing ) * @return array */ function ftp_rawlist(&$control, $pwd, $recursive = false) { if (!is_resource($control) || !is_string($pwd)) { return false; } if (!isset($GLOBALS['_NET_FTP']['DATA']) || !is_resource($GLOBALS['_NET_FTP']['DATA'])) { ftp_pasv($control, $GLOBALS['_NET_FTP']['USE_PASSIVE']); } fputs($control, 'LIST '.$pwd."\r\n"); $msg = fgets($control, 512); if (substr($msg, 0, 3) == 425) { return false; } $data = &$GLOBALS['_NET_FTP']['DATA']; if (!$GLOBALS['_NET_FTP']['USE_PASSIVE']) { $data = &socket_accept($data); } $content = array(); switch ($GLOBALS['_NET_FTP']['USE_PASSIVE']) { case true: while (true) { $string = rtrim(fgets($data, 1024)); if ($string=='') { break; } $content[] = $string; } fclose($data); break; case false: $string = socket_read($data, 1024, PHP_BINARY_READ); $content = explode("\n", $string); unset($content[count($content)-1]); socket_close($GLOBALS['_NET_FTP']['DATA']); socket_close($data); break; } $data = $GLOBALS['_NET_FTP']['DATA'] = NULL; $f = fgets($control, 1024); return $content; } /** * string ftp_systype ( resource stream ); * * Gets system type identifier of remote FTP server * Returns the remote system type * * @param resource $stream ( FTP resource ) * @return string */ function ftp_systype(&$control) { if (!is_resource($control)) { return false; } fputs($control, 'SYST'."\r\n"); $line = fgets($control, 256); if (substr($line, 0, 3) != 215) { return false; } $os = substr($line, 4, strpos($line, ' ', 4)-4); return $os; } /** * boolean ftp_alloc ( resource stream, integer bytes [, string &message ] ); * * Allocates space for a file to be uploaded * Return TRUE on success or FALSE on failure * * NOTE; Many FTP servers do not support this command and/or don't need it. * * FTP success respons key: Belive it's 200 * Needs data connection: NO * * @param resource $stream ( FTP stream ) * @param integer $int ( Space to allocate ) * @param string $msg ( Optional, textual representation of * the servers response will be * returned by refrence ) * @return boolean */ function ftp_alloc(&$control, $int, &$msg = NULL) { if (!is_resource($control) || !is_integer($int)) { return false; } fputs($control, 'ALLO '.$int.' R '.$int."\r\n"); $msg = rtrim(fgets($control, 256)); $code = substr($msg, 0, 3); if ($code == 200 || $code == 202) { return true; } return false; } /** * bool ftp_put ( resource stream, string remote_file, string local_file, * int mode [, int startpos ] ); * * Uploads a file to the FTP server * Returns TRUE on success or FALSE on failure. * * NOTE: * The transfer mode specified must be either FTP_ASCII or FTP_BINARY. * * @param resource $stream ( FTP stream ) * @param string $remote ( Remote file to write ) * @param string $local ( Local file to upload ) * @param integer $mode ( Upload mode, FTP_ASCI || FTP_BINARY ) * @param integer $pos ( Optional, start upload at position ) * @return boolean */ function ftp_put(&$control, $remote, $local, $mode, $pos = 0) { if (!is_resource($control) || !is_readable($local) || !is_integer($mode) || !is_integer($pos)) { return false; } $types = array ( 0 => 'A', 1 => 'I' ); $windows = array ( 0 => 't', 1 => 'b' ); /** * TYPE values: * A ( ASCII ) * I ( BINARY ) * E ( EBCDIC ) * L ( BYTE ) */ if (!isset($GLOBALS['_NET_FTP']['DATA']) || !is_resource($GLOBALS['_NET_FTP']['DATA'])) { ftp_pasv($control, $GLOBALS['_NET_FTP']['USE_PASSIVE']); } // Establish data connection variable $data = &$GLOBALS['_NET_FTP']['DATA']; // Decide TYPE to use fputs($control, 'TYPE '.$types[$mode]."\r\n"); $line = fgets($control, 256); // "Type set to TYPE" if (substr($line, 0, 3) != 200) { return false; } fputs($control, 'STOR '.$remote."\r\n"); sleep(1); $line = fgets($control, 256); // "Opening TYPE mode data connect." if (substr($line, 0, 3) != 150) { return false; } // Creating resource to $local file $fp = fopen($local, 'r'. $windows[$mode]); if (!is_resource($fp)) { $fp = NULL; return false; } // Loop throu that file and echo it to the data socket $i = 0; switch ($GLOBALS['_NET_FTP']['USE_PASSIVE']) { case false: $data = &socket_accept($data); while (!feof($fp)) { $i += socket_write($data, fread($fp, 10240), 10240); } socket_close($data); break; case true: while (!feof($fp)) { $i += fputs($data, fread($fp, 10240), 10240); } fclose($data); break; } $data = NULL; do { $line = fgets($control, 256); } while (substr($line, 0, 4) != "226 "); return true; } function ftp_get(&$control, $local, $remote, $mode, $resume = 0) { if (!is_resource($control) || !is_writable(dirname($local)) || !is_integer($mode) || !is_integer($resume)) { return false; } $types = array ( 0 => 'A', 1 => 'I' ); $windows = array ( 0 => 't', 1 => 'b' ); if (!isset($GLOBALS['_NET_FTP']['DATA']) || !is_resource($GLOBALS['_NET_FTP'][ 'DATA'])) { ftp_pasv($control, $GLOBALS['_NET_FTP']['USE_PASSIVE']); } $data = &$GLOBALS['NET_FTP']['DATA']; fputs($control, 'TYPE '.$types[$mode]."\r\n"); $line = fgets($control, 256); if (substr($line, 0, 3) != 200) { return false; } $fp = fopen($local, 'w'.$windows[$mode]); if (!is_resource($fp)) { $fp = NULL; return false; } } /** * Changes to the parent directory * Returns TRUE on success or FALSE on failure * * @access public * @param integer $stream ( Stream ID ) * @return boolean */ function ftp_cdup(&$control) { fputs($control, 'CDUP'."\r\n"); $line = fgets($control, 256); if (substr($line, 0, 3) != 250) { return false; } return true; } /** * Set permissions on a file via FTP * Returns the new file permission on success or FALSE on error * * NOTE: This command is *not* supported by the standart * TODO: Figure out a way to chmod files via FTP * NOTE: This command not ready! * * @access public * @param integer $stream ( Stream ID ) * @param integer $mode ( Octal value ) * @param string $file ( File to change permissions on ) * @return integer */ function ftp_chmod(&$control, $mode, $file) { if (!is_resource($control) || !is_integer($mode) || !is_string($file)) { return false; } // chmod not in the standart, proftpd doesn't recognize it // use SITE CHMOD? fputs($control, 'SITE CHMOD '.$mode. ' ' .$file."\r\n" ); $line = fgets($control, 256); if (substr($line, 0, 3) == 200) { return $mode; } trigger_error ( 'ftp_chmod() [function.ftp-chmod]: ' . rtrim ( $line ), E_USER_WARNING ); return false; } /** * Deletes a file on the FTP server * Returns TRUE on success or FALSE on failure * * @access integer $stream ( Stream ID ) * @param string $path ( File to delete ) * @return boolean */ function ftp_delete(&$control, $path) { if (!is_resource($control) || !is_string($path)) { return false; } fputs($control, 'DELE '.$path."\r\n"); $line = fgets($control, 256); if (substr($line, 0, 3) == 250) { return true; } return false; } /** * Requests execution of a program on the FTP server * NOTE; SITE EXEC is *not* supported by the standart * Returns TRUE on success or FALSE on error * * TODO: Look a littlebit better into this * * @access public * @param integer $stream ( Stream ID ) * @param string $cmd ( Command to send ) * @return boolean */ function ftp_exec(&$control, $cmd) { if (!is_resource($control) || !is_string($cmd)) { return false; } // Command not defined in the standart // proftpd doesn't recognize SITE EXEC (only help,chgrp,chmod and ratio) fputs($control, 'SITE EXEC '.$cmd."\r\n"); $line = fgets($control, 256); // php.net/ftp_exec uses respons code 200 to verify if command was sent // successfully or not, so we'll just do the same if (substr($line, 0, 3) == 200) { return true; } return false; } ?>