* @author Jorrit Schippers * @copyright 1997-2008 The PHP Group * @license BSD http://www.opensource.org/licenses/bsd-license.php * @version CVS: $Id: FTP.php 246 2016-02-10 21:21:12Z soeren $ * @link http://pear.php.net/package/Net_FTP * @since File available since Release 0.0.1 */ /** * Include PEAR.php to obtain the PEAR base class */ require_once 'PEAR.php'; /** * Option to let the ls() method return only files. * * @since 1.3 * @name NET_FTP_FILES_ONLY * @see Net_FTP::ls() */ define('NET_FTP_FILES_ONLY', 0); /** * Option to let the ls() method return only directories. * * @since 1.3 * @name NET_FTP_DIRS_ONLY * @see Net_FTP::ls() */ define('NET_FTP_DIRS_ONLY', 1); /** * Option to let the ls() method return directories and files (default). * * @since 1.3 * @name NET_FTP_DIRS_FILES * @see Net_FTP::ls() */ define('NET_FTP_DIRS_FILES', 2); /** * Option to let the ls() method return the raw directory listing from ftp_rawlist() * * @since 1.3 * @name NET_FTP_RAWLIST * @see Net_FTP::ls() */ define('NET_FTP_RAWLIST', 3); /** * Option to indicate that non-blocking features should not be used in * put(). This will also disable the listener functionality as a side effect. * * @since 1.4a1 * @name NET_FTP_BLOCKING * @see Net_FTP::put() */ define('NET_FTP_BLOCKING', 1); /** * Option to indicate that non-blocking features should be used if available in * put(). This will also enable the listener functionality. * * This is the default behaviour. * * @since 1.4a1 * @name NET_FTP_NONBLOCKING * @see Net_FTP::put() */ define('NET_FTP_NONBLOCKING', 2); /** * Error code to indicate a failed connection * This error code indicates, that the connection you tryed to set up * could not be established. Check your connection settings (host & port)! * * @since 1.3 * @name NET_FTP_ERR_CONNECT_FAILED * @see Net_FTP::connect() */ define('NET_FTP_ERR_CONNECT_FAILED', -1); /** * Error code to indicate a failed login * This error code indicates, that the login to the FTP server failed. Check * your user data (username & password). * * @since 1.3 * @name NET_FTP_ERR_LOGIN_FAILED * @see Net_FTP::login() */ define('NET_FTP_ERR_LOGIN_FAILED', -2); /** * Error code to indicate a failed directory change * The cd() method failed. Ensure that the directory you wanted to access exists. * * @since 1.3 * @name NET_FTP_ERR_DIRCHANGE_FAILED * @see Net_FTP::cd() */ define('NET_FTP_ERR_DIRCHANGE_FAILED', 2); // Compatibillity reasons! /** * Error code to indicate that Net_FTP could not determine the current path * The pwd() method failed and could not determine the path you currently reside * in on the FTP server. * * @since 1.3 * @name NET_FTP_ERR_DETERMINEPATH_FAILED * @see Net_FTP::pwd() */ define('NET_FTP_ERR_DETERMINEPATH_FAILED', 4); // Compatibillity reasons! /** * Error code to indicate that the creation of a directory failed * The directory you tryed to create could not be created. Check the * access rights on the parent directory! * * @since 1.3 * @name NET_FTP_ERR_CREATEDIR_FAILED * @see Net_FTP::mkdir() */ define('NET_FTP_ERR_CREATEDIR_FAILED', -4); /** * Error code to indicate that the EXEC execution failed. * The execution of a command using EXEC failed. Ensure, that your * FTP server supports the EXEC command. * * @since 1.3 * @name NET_FTP_ERR_EXEC_FAILED * @see Net_FTP::execute() */ define('NET_FTP_ERR_EXEC_FAILED', -5); /** * Error code to indicate that the SITE command failed. * The execution of a command using SITE failed. Ensure, that your * FTP server supports the SITE command. * * @since 1.3 * @name NET_FTP_ERR_SITE_FAILED * @see Net_FTP::site() */ define('NET_FTP_ERR_SITE_FAILED', -6); /** * Error code to indicate that the CHMOD command failed. * The execution of CHMOD failed. Ensure, that your * FTP server supports the CHMOD command and that you have the appropriate * access rights to use CHMOD. * * @since 1.3 * @name NET_FTP_ERR_CHMOD_FAILED * @see Net_FTP::chmod() */ define('NET_FTP_ERR_CHMOD_FAILED', -7); /** * Error code to indicate that a file rename failed * The renaming of a file on the server failed. Ensure that you have the * appropriate access rights to rename the file. * * @since 1.3 * @name NET_FTP_ERR_RENAME_FAILED * @see Net_FTP::rename() */ define('NET_FTP_ERR_RENAME_FAILED', -8); /** * Error code to indicate that the MDTM command failed * The MDTM command is not supported for directories. Ensure that you gave * a file path to the mdtm() method, not a directory path. * * @since 1.3 * @name NET_FTP_ERR_MDTMDIR_UNSUPPORTED * @see Net_FTP::mdtm() */ define('NET_FTP_ERR_MDTMDIR_UNSUPPORTED', -9); /** * Error code to indicate that the MDTM command failed * The MDTM command failed. Ensure that your server supports the MDTM command. * * @since 1.3 * @name NET_FTP_ERR_MDTM_FAILED * @see Net_FTP::mdtm() */ define('NET_FTP_ERR_MDTM_FAILED', -10); /** * Error code to indicate that a date returned by the server was misformated * A date string returned by your server seems to be missformated and could not be * parsed. Check that the server is configured correctly. If you're sure, please * send an email to the auhtor with a dumped output of * $ftp->ls('./', NET_FTP_RAWLIST); to get the date format supported. * * @since 1.3 * @name NET_FTP_ERR_DATEFORMAT_FAILED * @see Net_FTP::mdtm(), Net_FTP::ls() */ define('NET_FTP_ERR_DATEFORMAT_FAILED', -11); /** * Error code to indicate that the SIZE command failed * The determination of the filesize of a file failed. Ensure that your server * supports the SIZE command. * * @since 1.3 * @name NET_FTP_ERR_SIZE_FAILED * @see Net_FTP::size() */ define('NET_FTP_ERR_SIZE_FAILED', -12); /** * Error code to indicate that a local file could not be overwritten * You specified not to overwrite files. Therefore the local file has not been * overwriten. If you want to get the file overwriten, please set the option to * do so. * * @since 1.3 * @name NET_FTP_ERR_OVERWRITELOCALFILE_FORBIDDEN * @see Net_FTP::get(), Net_FTP::getRecursive() */ define('NET_FTP_ERR_OVERWRITELOCALFILE_FORBIDDEN', -13); /** * Error code to indicate that a local file could not be overwritten * Also you specified to overwrite the local file you want to download to, * it has not been possible to do so. Check that you have the appropriate access * rights on the local file to overwrite it. * * @since 1.3 * @name NET_FTP_ERR_OVERWRITELOCALFILE_FAILED * @see Net_FTP::get(), Net_FTP::getRecursive() */ define('NET_FTP_ERR_OVERWRITELOCALFILE_FAILED', -14); /** * Error code to indicate that the file you wanted to upload does not exist * The file you tried to upload does not exist. Ensure that it exists. * * @since 1.3 * @name NET_FTP_ERR_LOCALFILENOTEXIST * @see Net_FTP::put(), Net_FTP::putRecursive() */ define('NET_FTP_ERR_LOCALFILENOTEXIST', -15); /** * Error code to indicate that a remote file could not be overwritten * You specified not to overwrite files. Therefore the remote file has not been * overwriten. If you want to get the file overwriten, please set the option to * do so. * * @since 1.3 * @name NET_FTP_ERR_OVERWRITEREMOTEFILE_FORBIDDEN * @see Net_FTP::put(), Net_FTP::putRecursive() */ define('NET_FTP_ERR_OVERWRITEREMOTEFILE_FORBIDDEN', -16); /** * Error code to indicate that the upload of a file failed * The upload you tried failed. Ensure that you have appropriate access rights * to upload the desired file. * * @since 1.3 * @name NET_FTP_ERR_UPLOADFILE_FAILED * @see Net_FTP::put(), Net_FTP::putRecursive() */ define('NET_FTP_ERR_UPLOADFILE_FAILED', -17); /** * Error code to indicate that you specified an incorrect directory path * The remote path you specified seems not to be a directory. Ensure that * the path you specify is a directory and that the path string ends with * a /. * * @since 1.3 * @name NET_FTP_ERR_REMOTEPATHNODIR * @see Net_FTP::putRecursive(), Net_FTP::getRecursive() */ define('NET_FTP_ERR_REMOTEPATHNODIR', -18); /** * Error code to indicate that you specified an incorrect directory path * The local path you specified seems not to be a directory. Ensure that * the path you specify is a directory and that the path string ends with * a /. * * @since 1.3 * @name NET_FTP_ERR_LOCALPATHNODIR * @see Net_FTP::putRecursive(), Net_FTP::getRecursive() */ define('NET_FTP_ERR_LOCALPATHNODIR', -19); /** * Error code to indicate that a local directory failed to be created * You tried to create a local directory through getRecursive() method, * which has failed. Ensure that you have the appropriate access rights * to create it. * * @since 1.3 * @name NET_FTP_ERR_CREATELOCALDIR_FAILED * @see Net_FTP::getRecursive() */ define('NET_FTP_ERR_CREATELOCALDIR_FAILED', -20); /** * Error code to indicate that the provided hostname was incorrect * The hostname you provided was invalid. Ensure to provide either a * full qualified domain name or an IP address. * * @since 1.3 * @name NET_FTP_ERR_HOSTNAMENOSTRING * @see Net_FTP::setHostname() */ define('NET_FTP_ERR_HOSTNAMENOSTRING', -21); /** * Error code to indicate that the provided port was incorrect * The port number you provided was invalid. Ensure to provide either a * a numeric port number greater zero. * * @since 1.3 * @name NET_FTP_ERR_PORTLESSZERO * @see Net_FTP::setPort() */ define('NET_FTP_ERR_PORTLESSZERO', -22); /** * Error code to indicate that you provided an invalid mode constant * The mode constant you provided was invalid. You may only provide * FTP_ASCII or FTP_BINARY. * * @since 1.3 * @name NET_FTP_ERR_NOMODECONST * @see Net_FTP::setMode() */ define('NET_FTP_ERR_NOMODECONST', -23); /** * Error code to indicate that you provided an invalid timeout * The timeout you provided was invalid. You have to provide a timeout greater * or equal to zero. * * @since 1.3 * @name NET_FTP_ERR_TIMEOUTLESSZERO * @see Net_FTP::Net_FTP(), Net_FTP::setTimeout() */ define('NET_FTP_ERR_TIMEOUTLESSZERO', -24); /** * Error code to indicate that you provided an invalid timeout * An error occured while setting the timeout. Ensure that you provide a * valid integer for the timeount and that your PHP installation works * correctly. * * @since 1.3 * @name NET_FTP_ERR_SETTIMEOUT_FAILED * @see Net_FTP::Net_FTP(), Net_FTP::setTimeout() */ define('NET_FTP_ERR_SETTIMEOUT_FAILED', -25); /** * Error code to indicate that the provided extension file doesn't exist * The provided extension file does not exist. Ensure to provided an * existant extension file. * * @since 1.3 * @name NET_FTP_ERR_EXTFILENOTEXIST * @see Net_FTP::getExtensionsFile() */ define('NET_FTP_ERR_EXTFILENOTEXIST', -26); /** * Error code to indicate that the provided extension file is not readable * The provided extension file is not readable. Ensure to have sufficient * access rights for it. * * @since 1.3 * @name NET_FTP_ERR_EXTFILEREAD_FAILED * @see Net_FTP::getExtensionsFile() */ define('NET_FTP_ERR_EXTFILEREAD_FAILED', -27); /** * Error code to indicate that the deletion of a file failed * The specified file could not be deleted. Ensure to have sufficient * access rights to delete the file. * * @since 1.3 * @name NET_FTP_ERR_EXTFILEREAD_FAILED * @see Net_FTP::rm() */ define('NET_FTP_ERR_DELETEFILE_FAILED', -28); /** * Error code to indicate that the deletion of a directory faild * The specified file could not be deleted. Ensure to have sufficient * access rights to delete the file. * * @since 1.3 * @name NET_FTP_ERR_EXTFILEREAD_FAILED * @see Net_FTP::rm() */ define('NET_FTP_ERR_DELETEDIR_FAILED', -29); /** * Error code to indicate that the directory listing failed * PHP could not list the directory contents on the server. Ensure * that your server is configured appropriate. * * @since 1.3 * @name NET_FTP_ERR_RAWDIRLIST_FAILED * @see Net_FTP::ls() */ define('NET_FTP_ERR_RAWDIRLIST_FAILED', -30); /** * Error code to indicate that the directory listing failed * The directory listing format your server uses seems not to * be supported by Net_FTP. Please send the output of the * call ls('./', NET_FTP_RAWLIST); to the author of this * class to get it supported. * * @since 1.3 * @name NET_FTP_ERR_DIRLIST_UNSUPPORTED * @see Net_FTP::ls() */ define('NET_FTP_ERR_DIRLIST_UNSUPPORTED', -31); /** * Error code to indicate failed disconnecting * This error code indicates, that disconnection was not possible. * * @since 1.3 * @name NET_FTP_ERR_DISCONNECT_FAILED * @see Net_FTP::disconnect() */ define('NET_FTP_ERR_DISCONNECT_FAILED', -32); /** * Error code to indicate that the username you provided was invalid. * Check that you provided a non-empty string as the username. * * @since 1.3 * @name NET_FTP_ERR_USERNAMENOSTRING * @see Net_FTP::setUsername() */ define('NET_FTP_ERR_USERNAMENOSTRING', -33); /** * Error code to indicate that the username you provided was invalid. * Check that you provided a non-empty string as the username. * * @since 1.3 * @name NET_FTP_ERR_PASSWORDNOSTRING * @see Net_FTP::setPassword() */ define('NET_FTP_ERR_PASSWORDNOSTRING', -34); /** * Error code to indicate that the provided extension file is not loadable * The provided extension file is not loadable. Ensure to have a correct file * syntax. * * @since 1.3.3 * @name NET_FTP_ERR_EXTFILELOAD_FAILED * @see Net_FTP::getExtensionsFile() */ define('NET_FTP_ERR_EXTFILELOAD_FAILED', -35); /** * Error code to indicate that the directory listing pattern provided is not a * string. * * @since 1.4.0a1 * @name NET_FTP_ERR_ILLEGALPATTERN * @see Net_FTP::setDirMatcher() */ define('NET_FTP_ERR_ILLEGALPATTERN', -36); /** * Error code to indicate that the directory listing matcher map provided is not an * array. * * @since 1.4.0a1 * @name NET_FTP_ERR_ILLEGALMAP * @see Net_FTP::setDirMatcher() */ define('NET_FTP_ERR_ILLEGALMAP', -37); /** * Error code to indicate that the directory listing matcher map provided contains * wrong values (ie: it contains non-numerical values) * * @since 1.4.0a1 * @name NET_FTP_ERR_ILLEGALMAPVALUE * @see Net_FTP::setDirMatcher() */ define('NET_FTP_ERR_ILLEGALMAPVALUE', -38); /** * Error code indicating that bad options were supplied to the * put() method. * * @since 1.4a1 * @name NET_FTP_ERR_BADOPTIONS * @see Net_FTP::put() */ define('NET_FTP_ERR_BADOPTIONS', -39); /** * Error code indicating that SSL connection is not supported as either the * ftp module or OpenSSL support is not statically built into php. * * @since 1.4a2 * @name NET_FTP_ERR_NOSSL * @see Net_FTP::setSsl() */ define('NET_FTP_ERR_NOSSL', -40); /** * Class for comfortable FTP-communication * * This class provides comfortable communication with FTP-servers. You may do * everything enabled by the PHP-FTP-extension and further functionalities, like * recursive-deletion, -up- and -download. Another feature is to create directories * recursively. * * @category Networking * @package FTP * @author Tobias Schlitt * @author Jorrit Schippers * @copyright 1997-2008 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.4.0 * @link http://pear.php.net/package/Net_FTP * @since 0.0.1 * @access public */ class Net_FTP extends PEAR { /** * The host to connect to * * @access private * @var string */ var $_hostname; /** * The port for ftp-connection (standard is 21) * * @access private * @var int */ var $_port = 21; /** * The username for login * * @access private * @var string */ var $_username; /** * The password for login * * @access private * @var string */ var $_password; /** * Determine whether to connect through secure SSL connection or not * * Is null when it hasn't been explicitly set * * @access private * @var bool */ var $_ssl; /** * Determine whether to use passive-mode (true) or active-mode (false) * * Is null when it hasn't been explicitly set * * @access private * @var bool */ var $_passv = null; /** * The standard mode for ftp-transfer * * @access private * @var int */ var $_mode = FTP_BINARY; /** * This holds the handle for the ftp-connection * * If null, the connection hasn't been setup yet. If false, the connection * attempt has failed. Else, it contains an ftp resource. * * @access private * @var resource */ var $_handle = null; /** * Contains the timeout for FTP operations * * @access private * @var int * @since 1.3 */ var $_timeout = 90; /** * Saves file-extensions for ascii- and binary-mode * * The array is built like this: 'php' => FTP_ASCII, 'png' => FTP_BINARY * * @access private * @var array */ var $_file_extensions = array(); /** * ls match * Matches the ls entries against a regex and maps the resulting array to * speaking names * * The values are set in the constructor because of line length constaints. * * Typical lines for the Windows format: * 07-05-07 08:40AM 4701 SomeFile.ext * 04-29-07 10:28PM SomeDir * * @access private * @var array * @since 1.3 */ var $_ls_match = null; /** * matcher * Stores the matcher for the current connection * * @access private * @var array * @since 1.3 */ var $_matcher = null; /** * Holds all Net_FTP_Observer objects * that wish to be notified of new messages. * * @var array * @access private * @since 1.3 */ var $_listeners = array(); /** * Is true when a login has been performed * and was successful * * @access private * @var boolean * @since 1.4 */ var $_loggedin = false; /** * This generates a new FTP-Object. The FTP-connection will not be established, * yet. * You can leave $host and $port blank, if you want. The $host will not be set * and the $port will be left at 21. You have to set the $host manualy before * trying to connect or with the connect() method. * * @param string $host (optional) The hostname * @param int $port (optional) The port * @param int $timeout (optional) Sets the standard timeout * * @access public * @return void * @see Net_FTP::setHostname(), Net_FTP::setPort(), Net_FTP::connect() */ function __construct($host = null, $port = null, $timeout = 90) { $this->PEAR(); if (isset($host)) { $this->setHostname($host); } if (isset($port)) { $this->setPort($port); } $this->_timeout = $timeout; $this->_ls_match = array( 'unix' => array( 'pattern' => '/(?:(d)|.)([rwxts-]{9})\s+(\w+)\s+([\w\d-()?.]+)\s+'. '([\w\d-()?.]+)\s+(\w+)\s+(\S+\s+\S+\s+\S+)\s+(.+)/', 'map' => array( 'is_dir' => 1, 'rights' => 2, 'files_inside' => 3, 'user' => 4, 'group' => 5, 'size' => 6, 'date' => 7, 'name' => 8, ) ), 'windows' => array( 'pattern' => '/([0-9\-]+\s+[0-9:APM]+)\s+(()|\d+)\s+(.+)/', 'map' => array( 'date' => 1, 'size' => 2, 'is_dir' => 3, 'name' => 4, ) ) ); } /** * This function generates the FTP-connection. You can optionally define a * hostname and/or a port. If you do so, this data is stored inside the object. * * @param string $host (optional) The Hostname * @param int $port (optional) The Port * @param bool $ssl (optional) Whether to connect through secure SSL connection * * @access public * @return mixed True on success, otherwise PEAR::Error * @see NET_FTP_ERR_CONNECT_FAILED */ function connect($host = null, $port = null, $ssl = null) { $this->_matcher = null; if (isset($host)) { $this->setHostname($host); } if (isset($port)) { $this->setPort($port); } if (isset($ssl) && is_bool($ssl) && $ssl) { $this->setSsl(); } if ($this->getSsl()) { $handle = @ftp_ssl_connect($this->getHostname(), $this->getPort(), $this->_timeout); } else { $handle = @ftp_connect($this->getHostname(), $this->getPort(), $this->_timeout); } if (!$handle) { $this->_handle = false; return $this->raiseError("Connection to host failed", NET_FTP_ERR_CONNECT_FAILED); } else { $this->_handle =& $handle; return true; } } /** * This function close the FTP-connection * * @access public * @return bool|PEAR_Error Returns true on success, PEAR_Error on failure */ function disconnect() { $res = @ftp_close($this->_handle); if (!$res) { return PEAR::raiseError('Disconnect failed.', NET_FTP_ERR_DISCONNECT_FAILED); } $this->_handle = null; return true; } /** * This logs you into the ftp-server. You are free to specify username and * password in this method. If you specify it, the values will be taken into * the corresponding attributes, if do not specify, the attributes are taken. * * If connect() has not been called yet, a connection will be setup * * @param string $username (optional) The username to use * @param string $password (optional) The password to use * * @access public * @return mixed True on success, otherwise PEAR::Error * @see NET_FTP_ERR_LOGIN_FAILED */ function login($username = null, $password = null) { if ($this->_handle === null) { $res = $this->connect(); if (PEAR::isError($res)) { return $res; } } if (!isset($username)) { $username = $this->getUsername(); } else { $this->setUsername($username); } if (!isset($password)) { $password = $this->getPassword(); } else { $this->setPassword($password); } $res = @ftp_login($this->_handle, $username, $password); if (!$res) { return $this->raiseError("Unable to login", NET_FTP_ERR_LOGIN_FAILED); } else { $this->_loggedin = true; // distinguish between null and false, null means this setting wasn't // explicitly changed, so we only change it when setPassive or // setActive was called by the user if ($this->_passv === true) { $this->setPassive(); } elseif ($this->_passv === false) { $this->setActive(); } return true; } } /** * This changes the currently used directory. You can use either an absolute * directory-path (e.g. "/home/blah") or a relative one (e.g. "../test"). * * @param string $dir The directory to go to. * * @access public * @return mixed True on success, otherwise PEAR::Error * @see NET_FTP_ERR_DIRCHANGE_FAILED */ function cd($dir) { $erg = @ftp_chdir($this->_handle, $dir); if (!$erg) { return $this->raiseError("Directory change failed", NET_FTP_ERR_DIRCHANGE_FAILED); } else { return true; } } /** * Show's you the actual path on the server * This function questions the ftp-handle for the actual selected path and * returns it. * * @access public * @return mixed The actual path or PEAR::Error * @see NET_FTP_ERR_DETERMINEPATH_FAILED */ function pwd() { $res = @ftp_pwd($this->_handle); if (!$res) { return $this->raiseError("Could not determine the actual path.", NET_FTP_ERR_DETERMINEPATH_FAILED); } else { return $res; } } /** * This works similar to the mkdir-command on your local machine. You can either * give it an absolute or relative path. The relative path will be completed * with the actual selected server-path. (see: pwd()) * * @param string $dir Absolute or relative dir-path * @param bool $recursive (optional) Create all needed directories * * @access public * @return mixed True on success, otherwise PEAR::Error * @see NET_FTP_ERR_CREATEDIR_FAILED */ function mkdir($dir, $recursive = false) { $dir = $this->_constructPath($dir); $savedir = $this->pwd(); $this->pushErrorHandling(PEAR_ERROR_RETURN); $e = $this->cd($dir); $this->popErrorHandling(); if ($e === true) { $this->cd($savedir); return true; } $this->cd($savedir); if ($recursive === false) { $res = @ftp_mkdir($this->_handle, $dir); if (!$res) { return $this->raiseError("Creation of '$dir' failed", NET_FTP_ERR_CREATEDIR_FAILED); } else { return true; } } else { // do not look at the first character, as $dir is absolute, // it will always be a / if (strpos(substr($dir, 1), '/') === false) { return $this->mkdir($dir, false); } if (substr($dir, -1) == '/') { $dir = substr($dir, 0, -1); } $parent = substr($dir, 0, strrpos($dir, '/')); $res = $this->mkdir($parent, true); if ($res === true) { $res = $this->mkdir($dir, false); } if ($res !== true) { return $res; } return true; } } /** * This method tries executing a command on the ftp, using SITE EXEC. * * @param string $command The command to execute * * @access public * @return mixed The result of the command (if successfull), otherwise * PEAR::Error * @see NET_FTP_ERR_EXEC_FAILED */ function execute($command) { $res = @ftp_exec($this->_handle, $command); if (!$res) { return $this->raiseError("Execution of command '$command' failed.", NET_FTP_ERR_EXEC_FAILED); } else { return $res; } } /** * Execute a SITE command on the server * This method tries to execute a SITE command on the ftp server. * * @param string $command The command with parameters to execute * * @access public * @return mixed True if successful, otherwise PEAR::Error * @see NET_FTP_ERR_SITE_FAILED */ function site($command) { $res = @ftp_site($this->_handle, $command); if (!$res) { return $this->raiseError("Execution of SITE command '$command' failed.", NET_FTP_ERR_SITE_FAILED); } else { return $res; } } /** * This method will try to chmod the file specified on the server * Currently, you must give a number as the the permission argument (777 or * similar). The file can be either a relative or absolute path. * NOTE: Some servers do not support this feature. In that case, you will * get a PEAR error object returned. If successful, the method returns true * * @param mixed $target The file or array of files to set permissions for * @param integer $permissions The mode to set the file permissions to * * @access public * @return mixed True if successful, otherwise PEAR::Error * @see NET_FTP_ERR_CHMOD_FAILED */ function chmod($target, $permissions) { // If $target is an array: Loop through it. if (is_array($target)) { for ($i = 0; $i < count($target); $i++) { $res = $this->chmod($target[$i], $permissions); if (PEAR::isError($res)) { return $res; } // end if isError } // end for i < count($target) return true; } else { $res = $this->site("CHMOD " . $permissions . " " . $target); if (!$res) { return PEAR::raiseError("CHMOD " . $permissions . " " . $target . " failed", NET_FTP_ERR_CHMOD_FAILED); } else { return $res; } } // end if is_array } // end method chmod /** * This method will try to chmod a folder and all of its contents * on the server. The target argument must be a folder or an array of folders * and the permissions argument have to be an integer (i.e. 777). * The file can be either a relative or absolute path. * NOTE: Some servers do not support this feature. In that case, you * will get a PEAR error object returned. If successful, the method * returns true * * @param mixed $target The folder or array of folders to * set permissions for * @param integer $permissions The mode to set the folder * and file permissions to * * @access public * @return mixed True if successful, otherwise PEAR::Error * @see NET_FTP_ERR_CHMOD_FAILED, NET_FTP_ERR_DETERMINEPATH_FAILED, * NET_FTP_ERR_RAWDIRLIST_FAILED, NET_FTP_ERR_DIRLIST_UNSUPPORTED */ function chmodRecursive($target, $permissions) { static $dir_permissions; if (!isset($dir_permissions)) { // Making directory specific permissions $dir_permissions = $this->_makeDirPermissions($permissions); } // If $target is an array: Loop through it if (is_array($target)) { for ($i = 0; $i < count($target); $i++) { $res = $this->chmodRecursive($target[$i], $permissions); if (PEAR::isError($res)) { return $res; } // end if isError } // end for i < count($target) } else { $remote_path = $this->_constructPath($target); // Chmod the directory itself $result = $this->chmod($remote_path, $dir_permissions); if (PEAR::isError($result)) { return $result; } // If $remote_path last character is not a slash, add one if (substr($remote_path, strlen($remote_path)-1) != "/") { $remote_path .= "/"; } $dir_list = array(); $mode = NET_FTP_DIRS_ONLY; $dir_list = $this->ls($remote_path, $mode); foreach ($dir_list as $dir_entry) { if ($dir_entry['name'] == '.' || $dir_entry['name'] == '..') { continue; } $remote_path_new = $remote_path.$dir_entry["name"]."/"; // Chmod the directory we're about to enter $result = $this->chmod($remote_path_new, $dir_permissions); if (PEAR::isError($result)) { return $result; } $result = $this->chmodRecursive($remote_path_new, $permissions); if (PEAR::isError($result)) { return $result; } } // end foreach dir_list as dir_entry $file_list = array(); $mode = NET_FTP_FILES_ONLY; $file_list = $this->ls($remote_path, $mode); if (PEAR::isError($file_list)) { return $file_list; } foreach ($file_list as $file_entry) { $remote_file = $remote_path.$file_entry["name"]; $result = $this->chmod($remote_file, $permissions); if (PEAR::isError($result)) { return $result; } } // end foreach $file_list } // end if is_array return true; // No errors } // end method chmodRecursive /** * Rename or move a file or a directory from the ftp-server * * @param string $remote_from The remote file or directory original to rename or * move * @param string $remote_to The remote file or directory final to rename or * move * * @access public * @return bool $res True on success, otherwise PEAR::Error * @see NET_FTP_ERR_RENAME_FAILED */ function rename ($remote_from, $remote_to) { $res = @ftp_rename($this->_handle, $remote_from, $remote_to); if (!$res) { return $this->raiseError("Could not rename ".$remote_from." to ". $remote_to." !", NET_FTP_ERR_RENAME_FAILED); } return true; } /** * This will return logical permissions mask for directory. * if directory has to be readable it have also be executable * * @param string $permissions File permissions in digits for file (i.e. 666) * * @access private * @return string File permissions in digits for directory (i.e. 777) */ function _makeDirPermissions($permissions) { $permissions = (string)$permissions; // going through (user, group, world) for ($i = 0; $i < strlen($permissions); $i++) { // Read permission is set but execute not yet if ((int)$permissions[$i] & 4 and !((int)$permissions[$i] & 1)) { // Adding execute flag $permissions[$i] = (int)$permissions[$i] + 1; } } return (string)$permissions; } /** * This will return the last modification-time of a file. You can either give * this function a relative or an absolute path to the file to check. * NOTE: Some servers will not support this feature and the function works * only on files, not directories! When successful, * it will return the last modification-time as a unix-timestamp or, when * $format is specified, a preformated timestring. * * @param string $file The file to check * @param string $format (optional) The format to give the date back * if not set, it will return a Unix timestamp * * @access public * @return mixed Unix timestamp, a preformated date-string or PEAR::Error * @see NET_FTP_ERR_MDTMDIR_UNSUPPORTED, NET_FTP_ERR_MDTM_FAILED, * NET_FTP_ERR_DATEFORMAT_FAILED */ function mdtm($file, $format = null) { $file = $this->_constructPath($file); if ($this->_checkRemoteDir($file) !== false) { return $this->raiseError("Filename '$file' seems to be a directory.", NET_FTP_ERR_MDTMDIR_UNSUPPORTED); } $res = @ftp_mdtm($this->_handle, $file); if ($res == -1) { return $this->raiseError("Could not get last-modification-date of '". $file."'.", NET_FTP_ERR_MDTM_FAILED); } if (isset($format)) { $res = date($format, $res); if (!$res) { return $this->raiseError("Date-format failed on timestamp '".$res. "'.", NET_FTP_ERR_DATEFORMAT_FAILED); } } return $res; } /** * This will return the size of a given file in bytes. You can either give this * function a relative or an absolute file-path. NOTE: Some servers do not * support this feature! * * @param string $file The file to check * * @access public * @return mixed Size in bytes or PEAR::Error * @see NET_FTP_ERR_SIZE_FAILED */ function size($file) { $file = $this->_constructPath($file); $res = @ftp_size($this->_handle, $file); if ($res == -1) { return $this->raiseError("Could not determine filesize of '$file'.", NET_FTP_ERR_SIZE_FAILED); } else { return $res; } } /** * This method returns a directory-list of the current directory or given one. * To display the current selected directory, simply set the first parameter to * null * or leave it blank, if you do not want to use any other parameters. *

* There are 4 different modes of listing directories. Either to list only * the files (using NET_FTP_FILES_ONLY), to list only directories (using * NET_FTP_DIRS_ONLY) or to show both (using NET_FTP_DIRS_FILES, which is * default). *

* The 4th one is the NET_FTP_RAWLIST, which returns just the array created by * the ftp_rawlist()-function build into PHP. *

* The other function-modes will return an array containing the requested data. * The files and dirs are listed in human-sorted order, but if you select * NET_FTP_DIRS_FILES the directories will be added above the files, * but although both sorted. *

* All elements in the arrays are associative arrays themselves. They have the * following structure: *

* Dirs:
* ["name"] => string The name of the directory
* ["rights"] => string The rights of the directory (in style * "rwxr-xr-x")
* ["user"] => string The owner of the directory
* ["group"] => string The group-owner of the directory
* ["files_inside"]=> string The number of files/dirs inside the * directory excluding "." and ".."
* ["date"] => int The creation-date as Unix timestamp
* ["is_dir"] => bool true, cause this is a dir
*

* Files:
* ["name"] => string The name of the file
* ["size"] => int Size in bytes
* ["rights"] => string The rights of the file (in style * "rwxr-xr-x")
* ["user"] => string The owner of the file
* ["group"] => string The group-owner of the file
* ["date"] => int The creation-date as Unix timestamp
* ["is_dir"] => bool false, cause this is a file
* * @param string $dir (optional) The directory to list or null, when listing * the current directory. * @param int $mode (optional) The mode which types to list (files, * directories or both). * * @access public * @return mixed The directory list as described above or PEAR::Error on failure * @see NET_FTP_DIRS_FILES, NET_FTP_DIRS_ONLY, NET_FTP_FILES_ONLY, * NET_FTP_RAWLIST, NET_FTP_ERR_DETERMINEPATH_FAILED, * NET_FTP_ERR_RAWDIRLIST_FAILED, NET_FTP_ERR_DIRLIST_UNSUPPORTED */ function ls($dir = null, $mode = NET_FTP_DIRS_FILES) { if (!isset($dir)) { $dir = @ftp_pwd($this->_handle); if (!$dir) { return $this->raiseError("Could not retrieve current directory", NET_FTP_ERR_DETERMINEPATH_FAILED); } } if (($mode != NET_FTP_FILES_ONLY) && ($mode != NET_FTP_DIRS_ONLY) && ($mode != NET_FTP_RAWLIST)) { $mode = NET_FTP_DIRS_FILES; } switch ($mode) { case NET_FTP_DIRS_FILES: $res = $this->_lsBoth($dir); break; case NET_FTP_DIRS_ONLY: $res = $this->_lsDirs($dir); break; case NET_FTP_FILES_ONLY: $res = $this->_lsFiles($dir); break; case NET_FTP_RAWLIST: $res = @ftp_rawlist($this->_handle, $dir); break; } return $res; } /** * This method will delete the given file or directory ($path) from the server * (maybe recursive). * * Whether the given string is a file or directory is only determined by the * last sign inside the string ("/" or not). * * If you specify a directory, you can optionally specify $recursive as true, * to let the directory be deleted recursive (with all sub-directories and files * inherited). * * You can either give a absolute or relative path for the file / dir. If you * choose to use the relative path, it will be automatically completed with the * actual selected directory. * * @param string $path The absolute or relative path to the file/directory. * @param bool $recursive Recursively delete everything in $path * @param bool $filesonly When deleting recursively, only delete files so the * directory structure is preserved * * @access public * @return mixed True on success, otherwise PEAR::Error * @see NET_FTP_ERR_DELETEFILE_FAILED, NET_FTP_ERR_DELETEDIR_FAILED, * NET_FTP_ERR_REMOTEPATHNODIR */ function rm($path, $recursive = false, $filesonly = false) { $path = $this->_constructPath($path); if ($this->_checkRemoteDir($path) === true) { if ($recursive) { return $this->_rmDirRecursive($path, $filesonly); } else { return $this->_rmDir($path); } } else { return $this->_rmFile($path); } } /** * This function will download a file from the ftp-server. You can either * specify an absolute path to the file (beginning with "/") or a relative one, * which will be completed with the actual directory you selected on the server. * You can specify the path to which the file will be downloaded on the local * machine, if the file should be overwritten if it exists (optionally, default * is no overwriting) and in which mode (FTP_ASCII or FTP_BINARY) the file * should be downloaded (if you do not specify this, the method tries to * determine it automatically from the mode-directory or uses the default-mode, * set by you). * If you give a relative path to the local-file, the script-path is used as * basepath. * * @param string $remote_file The absolute or relative path to the file to * download * @param string $local_file The local file to put the downloaded in * @param bool $overwrite (optional) Whether to overwrite existing file * @param int $mode (optional) Either FTP_ASCII or FTP_BINARY * * @access public * @return mixed True on success, otherwise PEAR::Error * @see NET_FTP_ERR_OVERWRITELOCALFILE_FORBIDDEN, * NET_FTP_ERR_OVERWRITELOCALFILE_FAILED, * NET_FTP_ERR_OVERWRITELOCALFILE_FAILED */ function get($remote_file, $local_file, $overwrite = false, $mode = null) { if (!isset($mode)) { $mode = $this->checkFileExtension($remote_file); } $remote_file = $this->_constructPath($remote_file); if (@file_exists($local_file) && !$overwrite) { return $this->raiseError("Local file '".$local_file. "' exists and may not be overwriten.", NET_FTP_ERR_OVERWRITELOCALFILE_FORBIDDEN); } if (@file_exists($local_file) && !@is_writeable($local_file) && $overwrite) { return $this->raiseError("Local file '".$local_file. "' is not writeable. Can not overwrite.", NET_FTP_ERR_OVERWRITELOCALFILE_FAILED); } if (@function_exists('ftp_nb_get')) { $res = @ftp_nb_get($this->_handle, $local_file, $remote_file, $mode); while ($res == FTP_MOREDATA) { $this->_announce('nb_get'); $res = @ftp_nb_continue($this->_handle); } } else { $res = @ftp_get($this->_handle, $local_file, $remote_file, $mode); } if (!$res) { return $this->raiseError("File '".$remote_file. "' could not be downloaded to '$local_file'.", NET_FTP_ERR_OVERWRITELOCALFILE_FAILED); } else { return true; } } /** * This function will upload a file to the ftp-server. You can either specify a * absolute path to the remote-file (beginning with "/") or a relative one, * which will be completed with the actual directory you selected on the server. * You can specify the path from which the file will be uploaded on the local * maschine, if the file should be overwritten if it exists (optionally, default * is no overwriting) and in which mode (FTP_ASCII or FTP_BINARY) the file * should be downloaded (if you do not specify this, the method tries to * determine it automatically from the mode-directory or uses the default-mode, * set by you). * If you give a relative path to the local-file, the script-path is used as * basepath. * * @param string $local_file The local file to upload * @param string $remote_file The absolute or relative path to the file to * upload to * @param bool $overwrite (optional) Whether to overwrite existing file * @param int $mode (optional) Either FTP_ASCII or FTP_BINARY * @param int $options (optional) Flags describing the behaviour of this * function. Currently NET_FTP_BLOCKING and * NET_FTP_NONBLOCKING are supported, of which * NET_FTP_NONBLOCKING is the default. * * @access public * @return mixed True on success, otherwise PEAR::Error * @see NET_FTP_ERR_LOCALFILENOTEXIST, * NET_FTP_ERR_OVERWRITEREMOTEFILE_FORBIDDEN, * NET_FTP_ERR_UPLOADFILE_FAILED, NET_FTP_NONBLOCKING, NET_FTP_BLOCKING */ function put($local_file, $remote_file, $overwrite = false, $mode = null, $options = 0) { if ($options & (NET_FTP_BLOCKING | NET_FTP_NONBLOCKING) === (NET_FTP_BLOCKING | NET_FTP_NONBLOCKING)) { return $this->raiseError('Bad options given: NET_FTP_NONBLOCKING and '. 'NET_FTP_BLOCKING can\'t both be set', NET_FTP_ERR_BADOPTIONS); } $usenb = ! ($options & (NET_FTP_BLOCKING == NET_FTP_BLOCKING)); if (!isset($mode)) { $mode = $this->checkFileExtension($local_file); } $remote_file = $this->_constructPath($remote_file); if (!@file_exists($local_file)) { return $this->raiseError("Local file '$local_file' does not exist.", NET_FTP_ERR_LOCALFILENOTEXIST); } if ((@ftp_size($this->_handle, $remote_file) != -1) && !$overwrite) { return $this->raiseError("Remote file '".$remote_file. "' exists and may not be overwriten.", NET_FTP_ERR_OVERWRITEREMOTEFILE_FORBIDDEN); } if (function_exists('ftp_alloc')) { ftp_alloc($this->_handle, filesize($local_file)); } if ($usenb && function_exists('ftp_nb_put')) { $res = @ftp_nb_put($this->_handle, $remote_file, $local_file, $mode); while ($res == FTP_MOREDATA) { $this->_announce('nb_put'); $res = @ftp_nb_continue($this->_handle); } } else { $res = @ftp_put($this->_handle, $remote_file, $local_file, $mode); } if (!$res) { return $this->raiseError("File '$local_file' could not be uploaded to '" .$remote_file."'.", NET_FTP_ERR_UPLOADFILE_FAILED); } else { return true; } } /** * This functionality allows you to transfer a whole directory-structure from * the remote-ftp to your local host. You have to give a remote-directory * (ending with '/') and the local directory (ending with '/') where to put the * files you download. * The remote path is automatically completed with the current-remote-dir, if * you give a relative path to this function. You can give a relative path for * the $local_path, too. Then the script-basedir will be used for comletion of * the path. * The parameter $overwrite will determine, whether to overwrite existing files * or not. Standard for this is false. Fourth you can explicitly set a mode for * all transfer actions done. If you do not set this, the method tries to * determine the transfer mode by checking your mode-directory for the file * extension. If the extension is not inside the mode-directory, it will get * your default mode. * * Since 1.4 no error will be returned when a file exists while $overwrite is * set to false. * * @param string $remote_path The path to download * @param string $local_path The path to download to * @param bool $overwrite (optional) Whether to overwrite existing files * (true) or not (false, standard). * @param int $mode (optional) The transfermode (either FTP_ASCII or * FTP_BINARY). * @param array $excluded_paths (optional) List of remote files or directories to * exclude from the transfer. All files and * directories must be stated as absolute paths. * Note: You must include a trailing slash on directory names. * * @access public * @return mixed True on succes, otherwise PEAR::Error * @see NET_FTP_ERR_OVERWRITELOCALFILE_FORBIDDEN, * NET_FTP_ERR_OVERWRITELOCALFILE_FAILED, NET_FTP_ERR_OVERWRITELOCALFILE_FAILED, * NET_FTP_ERR_REMOTEPATHNODIR, NET_FTP_ERR_LOCALPATHNODIR, * NET_FTP_ERR_CREATELOCALDIR_FAILED */ function getRecursive($remote_path, $local_path, $overwrite = false, $mode = null, $excluded_paths = array()) { $remote_path = $this->_constructPath($remote_path); if ($this->_checkRemoteDir($remote_path) !== true) { return $this->raiseError("Given remote-path '".$remote_path. "' seems not to be a directory.", NET_FTP_ERR_REMOTEPATHNODIR); } if (!@file_exists($local_path)) { $res = @mkdir($local_path); if (!$res) { return $this->raiseError("Could not create dir '$local_path'", NET_FTP_ERR_CREATELOCALDIR_FAILED); } } elseif (!@is_dir($local_path)) { return $this->raiseError("Given local-path '".$local_path. "' seems not to be a directory.", NET_FTP_ERR_LOCALPATHNODIR); } $dir_list = array(); $dir_list = $this->ls($remote_path, NET_FTP_DIRS_ONLY); if (PEAR::isError($dir_list)) { return $dir_list; } foreach ($dir_list as $dir_entry) { if ($dir_entry['name'] != '.' && $dir_entry['name'] != '..') { $remote_path_new = $remote_path.$dir_entry["name"]."/"; $local_path_new = $local_path.$dir_entry["name"]."/"; // Check whether the directory should be excluded if (!in_array($remote_path_new, $excluded_paths)) { $result = $this->getRecursive($remote_path_new, $local_path_new, $overwrite, $mode); if ($this->isError($result)) { return $result; } } } } $file_list = array(); $file_list = $this->ls($remote_path, NET_FTP_FILES_ONLY); if (PEAR::isError($file_list)) { return $file_list; } foreach ($file_list as $file_entry) { $remote_file = $remote_path.$file_entry["name"]; $local_file = $local_path.$file_entry["name"]; // Check whether the file should be excluded if (!in_array($remote_file, $excluded_paths)) { $result = $this->get($remote_file, $local_file, $overwrite, $mode); if ($this->isError($result) && $result->getCode() != NET_FTP_ERR_OVERWRITELOCALFILE_FORBIDDEN) { return $result; } } } return true; } /** * This functionality allows you to transfer a whole directory-structure from * your local host to the remote-ftp. You have to give a remote-directory * (ending with '/') and the local directory (ending with '/') where to put the * files you download. The remote path is automatically completed with the * current-remote-dir, if you give a relative path to this function. You can * give a relative path for the $local_path, too. Then the script-basedir will * be used for comletion of the path. * The parameter $overwrite will determine, whether to overwrite existing files * or not. * Standard for this is false. Fourth you can explicitly set a mode for all * transfer actions done. If you do not set this, the method tries to determine * the transfer mode by checking your mode-directory for the file-extension. If * the extension is not inside the mode-directory, it will get your default * mode. * * @param string $local_path The path to download to * @param string $remote_path The path to download * @param bool $overwrite (optional) Whether to overwrite existing files * (true) or not (false, standard). * @param int $mode (optional) The transfermode (either FTP_ASCII or * FTP_BINARY). * @param array $excluded_paths (optional) List of local files or directories to * exclude from the transfer. All files and * directories must be stated as absolute paths. * Note: You must include a trailing slash on * directory names. * * @access public * @return mixed True on succes, otherwise PEAR::Error * @see NET_FTP_ERR_LOCALFILENOTEXIST, * NET_FTP_ERR_OVERWRITEREMOTEFILE_FORBIDDEN, * NET_FTP_ERR_UPLOADFILE_FAILED, NET_FTP_ERR_LOCALPATHNODIR, * NET_FTP_ERR_REMOTEPATHNODIR */ function putRecursive($local_path, $remote_path, $overwrite = false, $mode = null, $excluded_paths = array()) { $remote_path = $this->_constructPath($remote_path); if (!file_exists($local_path) || !is_dir($local_path)) { return $this->raiseError("Given local-path '".$local_path. "' seems not to be a directory.", NET_FTP_ERR_LOCALPATHNODIR); } // try to create directory if it doesn't exist $old_path = $this->pwd(); if ($this->isError($this->cd($remote_path))) { $res = $this->mkdir($remote_path); if ($this->isError($res)) { return $res; } } $this->cd($old_path); if ($this->_checkRemoteDir($remote_path) !== true) { return $this->raiseError("Given remote-path '".$remote_path. "' seems not to be a directory.", NET_FTP_ERR_REMOTEPATHNODIR); } $dir_list = $this->_lsLocal($local_path); foreach ($dir_list["dirs"] as $dir_entry) { // local directories do not have arrays as entry $remote_path_new = $remote_path.$dir_entry."/"; $local_path_new = $local_path.$dir_entry."/"; // Check whether the directory should be excluded if (!in_array($local_path_new, $excluded_paths)) { $result = $this->putRecursive($local_path_new, $remote_path_new, $overwrite, $mode, $excluded_paths); if ($this->isError($result)) { return $result; } } } foreach ($dir_list["files"] as $file_entry) { $remote_file = $remote_path.$file_entry; $local_file = $local_path.$file_entry; // Check whether the file should be excluded if (!in_array($local_file, $excluded_paths)) { $result = $this->put($local_file, $remote_file, $overwrite, $mode); if ($this->isError($result)) { return $result; } } } return true; } /** * This checks, whether a file should be transfered in ascii- or binary-mode * by it's file-extension. If the file-extension is not set or * the extension is not inside one of the extension-dirs, the actual set * transfer-mode is returned. * * @param string $filename The filename to be checked * * @access public * @return int Either FTP_ASCII or FTP_BINARY */ function checkFileExtension($filename) { if (($pos = strrpos($filename, '.')) === false) { return $this->_mode; } else { $ext = substr($filename, $pos + 1); } if (isset($this->_file_extensions[$ext])) { return $this->_file_extensions[$ext]; } return $this->_mode; } /** * Set the hostname * * @param string $host The hostname to set * * @access public * @return bool True on success, otherwise PEAR::Error * @see NET_FTP_ERR_HOSTNAMENOSTRING */ function setHostname($host) { if (!is_string($host)) { return PEAR::raiseError("Hostname must be a string.", NET_FTP_ERR_HOSTNAMENOSTRING); } $this->_hostname = $host; return true; } /** * Set the Port * * @param int $port The port to set * * @access public * @return bool True on success, otherwise PEAR::Error * @see NET_FTP_ERR_PORTLESSZERO */ function setPort($port) { if (!is_int($port) || ($port < 0)) { PEAR::raiseError("Invalid port. Has to be integer >= 0", NET_FTP_ERR_PORTLESSZERO); } $this->_port = $port; return true; } /** * Set to connect through secure SSL connection * * @access public * @return bool True on success, otherwise PEAR::Error */ function setSsl() { if (!function_exists('ftp_ssl_connect')) { return PEAR::raiseError('SSL connection not supported. Function ftp_ssl_connect does not exist.', NET_FTP_ERR_NOSSL); } $this->_ssl = true; return true; } /** * Set the Username * * @param string $user The username to set * * @access public * @return mixed True on success, otherwise PEAR::Error * @see NET_FTP_ERR_USERNAMENOSTRING */ function setUsername($user) { if (empty($user) || !is_string($user)) { return PEAR::raiseError('Username $user invalid.', NET_FTP_ERR_USERNAMENOSTRING); } $this->_username = $user; return true; } /** * Set the password * * @param string $password The password to set * * @access private * @return mixed True on success, otherwise PEAR::Error * @see NET_FTP_ERR_PASSWORDNOSTRING */ function setPassword($password) { if (empty($password) || !is_string($password)) { return PEAR::raiseError('Password xxx invalid.', NET_FTP_ERR_PASSWORDNOSTRING); } $this->_password = $password; return true; } /** * Set the transfer-mode. You can use the predefined constants * FTP_ASCII or FTP_BINARY. The mode will be stored for any further transfers. * * @param int $mode The mode to set * * @access public * @return mixed True on success, otherwise PEAR::Error * @see NET_FTP_ERR_NOMODECONST */ function setMode($mode) { if (($mode == FTP_ASCII) || ($mode == FTP_BINARY)) { $this->_mode = $mode; return true; } else { return $this->raiseError('FTP-Mode has either to be FTP_ASCII or'. 'FTP_BINARY', NET_FTP_ERR_NOMODECONST); } } /** * Set the transfer-method to passive mode * * @access public * @return void */ function setPassive() { $this->_passv = true; if ($this->_handle != null && $this->_loggedin) { @ftp_pasv($this->_handle, true); } } /** * Set the transfer-method to active mode * * @access public * @return void */ function setActive() { $this->_passv = false; if ($this->_handle != null && $this->_loggedin) { @ftp_pasv($this->_handle, false); } } /** * Set the timeout for FTP operations * * Use this method to set a timeout for FTP operation. Timeout has to be an * integer. * * @param int $timeout the timeout to use * * @access public * @return bool True on success, otherwise PEAR::Error * @see NET_FTP_ERR_TIMEOUTLESSZERO, NET_FTP_ERR_SETTIMEOUT_FAILED */ function setTimeout ( $timeout = 0 ) { if (!is_int($timeout) || ($timeout < 0)) { return PEAR::raiseError('Timeout '.$timeout. ' is invalid, has to be an integer >= 0', NET_FTP_ERR_TIMEOUTLESSZERO); } $this->_timeout = $timeout; if (isset($this->_handle) && is_resource($this->_handle)) { $res = @ftp_set_option($this->_handle, FTP_TIMEOUT_SEC, $timeout); } else { $res = true; } if (!$res) { return PEAR::raiseError("Set timeout failed.", NET_FTP_ERR_SETTIMEOUT_FAILED); } return true; } /** * Adds an extension to a mode-directory * * The mode-directory saves file-extensions coresponding to filetypes * (ascii e.g.: 'php', 'txt', 'htm',...; binary e.g.: 'jpg', 'gif', 'exe',...). * The extensions have to be saved without the '.'. And * can be predefined in an external file (see: getExtensionsFile()). * * The array is build like this: 'php' => FTP_ASCII, 'png' => FTP_BINARY * * To change the mode of an extension, just add it again with the new mode! * * @param int $mode Either FTP_ASCII or FTP_BINARY * @param string $ext Extension * * @access public * @return void */ function addExtension($mode, $ext) { $this->_file_extensions[$ext] = $mode; } /** * This function removes an extension from the mode-directories * (described above). * * @param string $ext The extension to remove * * @access public * @return void */ function removeExtension($ext) { if (isset($this->_file_extensions[$ext])) { unset($this->_file_extensions[$ext]); } } /** * This get's both (ascii- and binary-mode-directories) from the given file. * Beware, if you read a file into the mode-directory, all former set values * will be unset! * * Example file contents: * [ASCII] * asc = 0 * txt = 0 * [BINARY] * bin = 1 * jpg = 1 * * @param string $filename The file to get from * * @access public * @return mixed True on success, otherwise PEAR::Error * @see NET_FTP_ERR_EXTFILENOTEXIST, NET_FTP_ERR_EXTFILEREAD_FAILED */ function getExtensionsFile($filename) { if (!file_exists($filename)) { return $this->raiseError("Extensions-file '$filename' does not exist", NET_FTP_ERR_EXTFILENOTEXIST); } if (!is_readable($filename)) { return $this->raiseError("Extensions-file '$filename' is not readable", NET_FTP_ERR_EXTFILEREAD_FAILED); } $exts = @parse_ini_file($filename, true); if (!is_array($exts)) { return $this->raiseError("Extensions-file '$filename' could not be". "loaded", NET_FTP_ERR_EXTFILELOAD_FAILED); } $this->_file_extensions = array(); if (isset($exts['ASCII'])) { foreach (array_keys($exts['ASCII']) as $ext) { $this->_file_extensions[$ext] = FTP_ASCII; } } if (isset($exts['BINARY'])) { foreach (array_keys($exts['BINARY']) as $ext) { $this->_file_extensions[$ext] = FTP_BINARY; } } return true; } /** * Returns the hostname * * @access public * @return string The hostname */ function getHostname() { return $this->_hostname; } /** * Returns the port * * @access public * @return int The port */ function getPort() { return $this->_port; } /** * Returns whether to connect through secure SSL connection * * @access public * @return bool True if with SSL, false if without SSL */ function getSsl() { return $this->_ssl; } /** * Returns the username * * @access public * @return string The username */ function getUsername() { return $this->_username; } /** * Returns the password * * @access public * @return string The password */ function getPassword() { return $this->_password; } /** * Returns the transfermode * * @access public * @return int The transfermode, either FTP_ASCII or FTP_BINARY. */ function getMode() { return $this->_mode; } /** * Returns, whether the connection is set to passive mode or not * * @access public * @return bool True if passive-, false if active-mode */ function isPassive() { return $this->_passv; } /** * Returns the mode set for a file-extension * * @param string $ext The extension you wanna ask for * * @return int Either FTP_ASCII, FTP_BINARY or NULL (if not set a mode for it) * @access public */ function getExtensionMode($ext) { return @$this->_file_extensions[$ext]; } /** * Get the currently set timeout. * Returns the actual timeout set. * * @access public * @return int The actual timeout */ function getTimeout() { return ftp_get_option($this->_handle, FTP_TIMEOUT_SEC); } /** * Adds a Net_FTP_Observer instance to the list of observers * that are listening for messages emitted by this Net_FTP instance. * * @param object &$observer The Net_FTP_Observer instance to attach * as a listener. * * @return boolean True if the observer is successfully attached. * @access public * @since 1.3 */ function attach(&$observer) { if (!is_a($observer, 'Net_FTP_Observer')) { return false; } $this->_listeners[$observer->getId()] = &$observer; return true; } /** * Removes a Net_FTP_Observer instance from the list of observers. * * @param object $observer The Net_FTP_Observer instance to detach * from the list of listeners. * * @return boolean True if the observer is successfully detached. * @access public * @since 1.3 */ function detach($observer) { if (!is_a($observer, 'Net_FTP_Observer') || !isset($this->_listeners[$observer->getId()])) { return false; } unset($this->_listeners[$observer->getId()]); return true; } /** * Sets the directory listing matcher * * Use this method to set the directory listing matcher to a specific pattern. * Indicate the pattern as a perl regular expression and give an array * containing as keys the fields selected in the regular expression and as * values the offset of the subpattern in the pattern. * * Example: * $pattern = '/(?:(d)|.)([rwxt-]+)\s+(\w+)\s+([\w\d-]+)\s+([\w\d-]+)\s+(\w+) * \s+(\S+\s+\S+\s+\S+)\s+(.+)/', * $matchmap = array( * 'is_dir' => 1, * 'rights' => 2, * 'files_inside' => 3, * 'user' => 4, * 'group' => 5, * 'size' => 6, * 'date' => 7, * 'name' => 8, * ) * * Make sure at least the is_dir and name keys are set. The is_dir key should * point to a subpattern that is empty for non-directories and non-empty * for directories. * * @param string $pattern The new matcher pattern to use * @param array $matchmap An mapping from key to subpattern offset * * @since 1.4.0a1 * @access public * @return bool|PEAR_Error True if matcher set successfully, PEAR_Error * otherwise * @see NET_FTP_ERR_ILLEGALPATTERN, * NET_FTP_ERR_ILLEGALMAP * NET_FTP_ERR_ILLEGALMAPVALUE */ function setDirMatcher($pattern, $matchmap) { if (!is_string($pattern)) { return $this->raiseError('The supplied pattern is not a string', NET_FTP_ERR_ILLEGALPATTERN); } if (!is_array($matchmap)) { return $this->raiseError('The supplied pattern is not an array', NET_FTP_ERR_ILLEGALMAP); } else { foreach ($matchmap AS $val) { if (!is_numeric($val)) { return $this->raiseError('The supplied pattern contains'. 'invalid value '.$val, NET_FTP_ERR_ILLEGALMAPVALUE); } } } $this->_matcher = array('pattern' => $pattern, 'map' => $matchmap); return true; } /** * Informs each registered observer instance that a new message has been * sent. * * @param mixed $event A hash describing the net event. * * @access private * @since 1.3 * @return void */ function _announce($event) { foreach ($this->_listeners as $listener) { $listener->notify($event); } } /** * Rebuild the path, if given relative * * This method will make a relative path absolute by prepending the current * remote directory in front of it. * * @param string $path The path to check and construct * * @access private * @return string The build path */ function _constructPath($path) { if ((substr($path, 0, 1) != '/') && (substr($path, 0, 2) != './')) { $actual_dir = @ftp_pwd($this->_handle); if (substr($actual_dir, -1) != '/') { $actual_dir .= '/'; } $path = $actual_dir.$path; } return $path; } /** * Checks whether the given path is a remote directory by trying to * chdir() into it (and back out) * * @param string $path Path to check * * @access private * @return mixed True if $path is a directory, otherwise false, PEAR_Error * when an error occurs in determining path type */ function _checkRemoteDir($path) { $pwd = $this->pwd(); if ($this->isError($pwd)) { return $pwd; } $res = $this->cd($path); $this->cd($pwd); return $this->isError($res, NET_FTP_ERR_DIRCHANGE_FAILED) === false; } /** * This will remove a file * * @param string $file The file to delete * * @access private * @return mixed True on success, otherwise PEAR::Error * @see NET_FTP_ERR_DELETEFILE_FAILED */ function _rmFile($file) { if (substr($file, 0, 1) != "/") { $actual_dir = @ftp_pwd($this->_handle); if (substr($actual_dir, (strlen($actual_dir) - 2), 1) != "/") { $actual_dir .= "/"; } $file = $actual_dir.$file; } $res = @ftp_delete($this->_handle, $file); if (!$res) { return $this->raiseError("Could not delete file '$file'.", NET_FTP_ERR_DELETEFILE_FAILED); } else { return true; } } /** * This will remove a dir * * @param string $dir The dir to delete * * @access private * @return mixed True on success, otherwise PEAR::Error * @see NET_FTP_ERR_REMOTEPATHNODIR, NET_FTP_ERR_DELETEDIR_FAILED */ function _rmDir($dir) { if (substr($dir, (strlen($dir) - 1), 1) != "/") { return $this->raiseError("Directory name '".$dir. "' is invalid, has to end with '/'", NET_FTP_ERR_REMOTEPATHNODIR); } $res = @ftp_rmdir($this->_handle, $dir); if (!$res) { return $this->raiseError("Could not delete directory '$dir'.", NET_FTP_ERR_DELETEDIR_FAILED); } else { return true; } } /** * This will remove a dir and all subdirs and -files * * @param string $dir The dir to delete recursively * @param bool $filesonly Only delete files so the directory structure is * preserved * * @access private * @return mixed True on success, otherwise PEAR::Error * @see NET_FTP_ERR_REMOTEPATHNODIR, NET_FTP_ERR_DELETEDIR_FAILED */ function _rmDirRecursive($dir, $filesonly = false) { if (substr($dir, (strlen($dir) - 1), 1) != "/") { return $this->raiseError("Directory name '".$dir. "' is invalid, has to end with '/'", NET_FTP_ERR_REMOTEPATHNODIR); } $file_list = $this->_lsFiles($dir); if (PEAR::isError($file_list)) { return $file_list; } foreach ($file_list as $file) { $file = $dir.$file["name"]; $res = $this->rm($file); if ($this->isError($res)) { return $res; } } $dir_list = $this->_lsDirs($dir); if (PEAR::isError($dir_list)) { return $dir_list; } foreach ($dir_list as $new_dir) { if ($new_dir["name"] == '.' || $new_dir["name"] == '..') { continue; } $new_dir = $dir.$new_dir["name"]."/"; $res = $this->_rmDirRecursive($new_dir, $filesonly); if ($this->isError($res)) { return $res; } } if (!$filesonly) { $res = $this->_rmDir($dir); } if (PEAR::isError($res)) { return $res; } else { return true; } } /** * Lists up files and directories * * @param string $dir The directory to list up * * @access private * @return array An array of dirs and files */ function _lsBoth($dir) { $list_splitted = $this->_listAndParse($dir); if (PEAR::isError($list_splitted)) { return $list_splitted; } if (!is_array($list_splitted["files"])) { $list_splitted["files"] = array(); } if (!is_array($list_splitted["dirs"])) { $list_splitted["dirs"] = array(); } $res = array(); @array_splice($res, 0, 0, $list_splitted["files"]); @array_splice($res, 0, 0, $list_splitted["dirs"]); return $res; } /** * Lists up directories * * @param string $dir The directory to list up * * @access private * @return array An array of dirs */ function _lsDirs($dir) { $list = $this->_listAndParse($dir); if (PEAR::isError($list)) { return $list; } return $list["dirs"]; } /** * Lists up files * * @param string $dir The directory to list up * * @access private * @return array An array of files */ function _lsFiles($dir) { $list = $this->_listAndParse($dir); if (PEAR::isError($list)) { return $list; } return $list["files"]; } /** * This lists up the directory-content and parses the items into well-formated * arrays. * The results of this array are sorted (dirs on top, sorted by name; * files below, sorted by name). * * @param string $dir The directory to parse * * @access private * @return array Lists of dirs and files * @see NET_FTP_ERR_RAWDIRLIST_FAILED */ function _listAndParse($dir) { $dirs_list = array(); $files_list = array(); $dir_list = @ftp_rawlist($this->_handle, $dir); if (!is_array($dir_list)) { return PEAR::raiseError('Could not get raw directory listing.', NET_FTP_ERR_RAWDIRLIST_FAILED); } foreach ($dir_list AS $k=>$v) { if (strncmp($v, 'total: ', 7) == 0 && preg_match('/total: \d+/', $v)) { unset($dir_list[$k]); break; // usually there is just one line like this } } // Handle empty directories if (count($dir_list) == 0) { return array('dirs' => $dirs_list, 'files' => $files_list); } // Exception for some FTP servers seem to return this wiered result instead // of an empty list if (count($dirs_list) == 1 && $dirs_list[0] == 'total 0') { return array('dirs' => array(), 'files' => $files_list); } if (!isset($this->_matcher) || PEAR::isError($this->_matcher)) { $this->_matcher = $this->_determineOSMatch($dir_list); if (PEAR::isError($this->_matcher)) { return $this->_matcher; } } foreach ($dir_list as $entry) { $m = array(); if (!preg_match($this->_matcher['pattern'], $entry, $m)) { continue; } $entry = array(); foreach ($this->_matcher['map'] as $key=>$val) { $entry[$key] = $m[$val]; } $entry['stamp'] = $this->_parseDate($entry['date']); if ($entry['is_dir']) { $dirs_list[] = $entry; } else { $files_list[] = $entry; } } @usort($dirs_list, array("Net_FTP", "_natSort")); @usort($files_list, array("Net_FTP", "_natSort")); $res["dirs"] = (is_array($dirs_list)) ? $dirs_list : array(); $res["files"] = (is_array($files_list)) ? $files_list : array(); return $res; } /** * Determine server OS * This determines the server OS and returns a valid regex to parse * ls() output. * * @param array &$dir_list The raw dir list to parse * * @access private * @return mixed An array of 'pattern' and 'map' on success, otherwise * PEAR::Error * @see NET_FTP_ERR_DIRLIST_UNSUPPORTED */ function _determineOSMatch(&$dir_list) { foreach ($dir_list as $entry) { foreach ($this->_ls_match as $match) { if (preg_match($match['pattern'], $entry)) { return $match; } } } $error = 'The list style of your server seems not to be supported. Please'. 'email a "$ftp->ls(NET_FTP_RAWLIST);" output plus info on the'. 'server to the maintainer of this package to get it supported!'. 'Thanks for your help!'; return PEAR::raiseError($error, NET_FTP_ERR_DIRLIST_UNSUPPORTED); } /** * Lists a local directory * * @param string $dir_path The dir to list * * @access private * @return array The list of dirs and files */ function _lsLocal($dir_path) { $dir = dir($dir_path); $dir_list = array(); $file_list = array(); while (false !== ($entry = $dir->read())) { if (($entry != '.') && ($entry != '..')) { if (is_dir($dir_path.$entry)) { $dir_list[] = $entry; } else { $file_list[] = $entry; } } } $dir->close(); $res['dirs'] = $dir_list; $res['files'] = $file_list; return $res; } /** * Function for use with usort(). * Compares the list-array-elements by name. * * @param string $item_1 first item to be compared * @param string $item_2 second item to be compared * * @access private * @return int < 0 if $item_1 is less than $item_2, 0 if equal and > 0 otherwise */ function _natSort($item_1, $item_2) { return strnatcmp($item_1['name'], $item_2['name']); } /** * Parse dates to timestamps * * @param string $date Date * * @access private * @return int Timestamp * @see NET_FTP_ERR_DATEFORMAT_FAILED */ function _parseDate($date) { // Sep 10 22:06 => Sep 10, 22:06 $res = array(); if (preg_match('/([A-Za-z]+)[ ]+([0-9]+)[ ]+([0-9]+):([0-9]+)/', $date, $res)) { $year = date('Y'); $month = $res[1]; $day = $res[2]; $hour = $res[3]; $minute = $res[4]; $date = "$month $day, $year $hour:$minute"; $tmpDate = strtotime($date); if ($tmpDate > time()) { $year--; $date = "$month $day, $year $hour:$minute"; } } elseif (preg_match('/^\d\d-\d\d-\d\d/', $date)) { // 09-10-04 => 09/10/04 $date = str_replace('-', '/', $date); } $res = strtotime($date); if (!$res) { return $this->raiseError('Dateconversion failed.', NET_FTP_ERR_DATEFORMAT_FAILED); } return $res; } } ?>