home *** CD-ROM | disk | FTP | other *** search
/ Enter 2004 June / ENTER.ISO / files / xampp-win32-1.4.5-installer.exe / xampp / FTP.php < prev    next >
Encoding:
PHP Script  |  2004-03-24  |  45.3 KB  |  1,274 lines

  1. <?php
  2.  
  3.     require_once 'PEAR.php';
  4.  
  5.     define("NET_FTP_FILES_ONLY", 0, true);
  6.     define("NET_FTP_DIRS_ONLY", 1, true);
  7.     define("NET_FTP_DIRS_FILES", 2, true);
  8.     define("NET_FTP_RAWLIST", 3, true);
  9.  
  10.     // drwx---r-x  2 system   System            0 Nov 15 11:17 Foo
  11.     define("NET_FTP_DIR_EREG", "([-dl])([rwx-]{9})[ ]*([0-9]*)[ ]*([a-zA-Z0-9_-]*)[ ]*([a-zA-Z0-9_-]*)[ ]*([0-9]*)[ ]*([A-Za-z]+ [0-9: ]*) (.+)", true);
  12.     // lrwxrwxrwx 1 oir100 nogroup 17 Sep 23 07:39 README.mod_sql -> ../README.mod_sql
  13.     
  14.     
  15.  
  16.      /**
  17.      * Class for comfortable FTP-communication
  18.      *
  19.      * This class provides comfortable communication with FTP-servers. You may do everything 
  20.     * enabled by the PHP-FTP-extension and further functionalities, like recursive-deletion,
  21.     * -up- and -download. Another feature is to create directories recursively.
  22.      *
  23.      * Change-Log:
  24.     *
  25.     * 2002-02-21:
  26.     *
  27.      * Now that the class runs into beta stadium, the API may not change any more. (Maybe there will be some
  28.     * optional parameters added later on, but the existing once will not change any further.) Notice, that
  29.     * the API has not changed until alpha stadiu. So, you have not to change anything on your
  30.     * existing code using this class.
  31.      *
  32.     * - Added support for ftp_nb_{put||get}-functions. If these functions do not exist the methods
  33.     * will fall back on ftp_{put||get}. (Thanks to Alexander Skwar!)
  34.     * - Added support for recursive directory creation. (Thanks to Alexander Skwar!)
  35.     * - Fixed some bugs
  36.     * - Fixed documentation
  37.     *
  38.      *
  39.      * @since PHP 4.2.3
  40.      * @author Tobias Schlitt <tobias@schlitt.info>
  41.      * @see http://www.schlitt.info
  42.      */
  43.  
  44.  
  45.  
  46.  
  47.  
  48.     class Net_FTP extends PEAR {
  49.  
  50.         /**
  51.         * The host to connect to
  52.         *
  53.         * @access private
  54.         * @var string
  55.         */
  56.         
  57.         var $_hostname;
  58.         
  59.         /**
  60.         * The port for ftp-connection (standard is 21)
  61.         *
  62.         * @access private
  63.         * @var string
  64.         */
  65.         
  66.         var $_port = 21;
  67.         
  68.         /**
  69.         * The username for login
  70.         *
  71.         * @access private
  72.         * @var string
  73.         */
  74.         
  75.         var $_username;
  76.         
  77.         /**
  78.         * The password for login
  79.         *
  80.         * @access private
  81.         * @var string
  82.         */
  83.         
  84.         var $_password;
  85.         
  86.         /**
  87.         * Determine wether to use passive-mode (true) or active-mode (false)
  88.         *
  89.         * @access private
  90.         * @var bool
  91.         */
  92.         
  93.         var $_passv;
  94.         
  95.         /**
  96.         * The standard mode for ftp-transfer 
  97.         *
  98.         * @access private
  99.         * @var string
  100.         */
  101.         
  102.         var $_mode;
  103.         
  104.         /**
  105.         * This holds the handle for the ftp-connection
  106.         *
  107.         * @access private
  108.         * @var int
  109.         */
  110.  
  111.         var $_handle;
  112.  
  113.         /**
  114.         * Saves file-extensions for ascii- and binary-mode
  115.         *
  116.         * The array contains 2 sub-arrays ("ascii" and "binary"), which both contain
  117.         * file-extensions without the "." (".php" = "php").
  118.         *
  119.         * @access private
  120.         * @var array
  121.         */
  122.  
  123.         var $_file_extensions;
  124.  
  125.         /**
  126.         * This generates a new FTP-Object
  127.         * This generates a new FTP-Object. The FTP-connection will not be established, yet.
  128.         * You can leave $host and $port blank, if you want. The $host will not be set
  129.         * and the $port will be left at 21. You have to set the $host manualy before
  130.         * trying to connect.
  131.         *
  132.         * @access public
  133.         * @param string $host The hostname (optional)
  134.         * @param int $port The port (optional)
  135.         * @return void
  136.         */
  137.         
  138.         function Net_FTP ( $host = null, $port = null ) {
  139.             
  140.             $this->PEAR();
  141.             if (isset($host)) {
  142.                 $this->setHostname($host);
  143.             }   
  144.             if (isset($port)) {
  145.                 $this->setPort($port);
  146.             }
  147.             
  148.             $this->_file_extensions[FTP_ASCII] = array();
  149.             $this->_file_extensions[FTP_BINARY] = array();
  150.             
  151.         }
  152.         
  153.         /**
  154.         * Build the FTP-connection
  155.         * This function generates the FTP-connection. You can optionally define a
  156.         * hostname and/or a port. If you do so, this data is stored inside the object.
  157.         *
  158.         * @access public
  159.         * @param string $host The Hostname (optional)
  160.         * @param int $port The Port (optional)
  161.         * @return bool $res True on success, otherwise PEAR::Error
  162.         */
  163.         
  164.         function connect ( $host = null, $port = null ) {
  165.             if (isset($host)) {
  166.                 $this->setHostname($host);
  167.             }
  168.             if (isset($port)) {
  169.                 $this->setPort($port);
  170.             }
  171.             $handle = @ftp_connect($this->getHostname(), $this->getPort());
  172.             if (!$handle) {
  173.                 return $this->raiseError("Connection to host failed", 0);
  174.             } else {
  175.                 $this->_handle =& $handle;
  176.                 return true;
  177.             }
  178.         }
  179.         
  180.         /**
  181.         * Close the FTP-connection
  182.         * This method closes the FTP-connection
  183.         *
  184.         * @access public
  185.         * @return void
  186.         */
  187.  
  188.         function disconnect ( ) {
  189.             ftp_close($this->_handle);
  190.         }
  191.  
  192.         /**
  193.         * This logges you into the ftp-server
  194.         * This logges you into the ftp-server. You are free to specify username and password
  195.         * in this method. If you specify it, the values will be taken into the coresponding
  196.         * attributes, if do not specify, the attributes are taken.
  197.         *
  198.         * @access public
  199.         * @param string $username The username to use (optional)
  200.         * @param string $password The password to use (optional)
  201.         * @return bool $res True on success, otherwise PEAR::Error
  202.         */
  203.  
  204.         function login ( $username = null, $password = null ) {
  205.  
  206.             if (!isset($username)) {
  207.                 $username = $this->getUsername();
  208.             } else {
  209.                 $this->setUsername($username);
  210.             }
  211.  
  212.             if (!isset($password)) {
  213.                 $password = $this->getPassword();
  214.             } else {
  215.                 $this->setPassword($password);
  216.             }
  217.  
  218.             $res = ftp_login($this->_handle, $username, $password);
  219.  
  220.             if (!$res) {
  221.                 return $this->raiseError("Unable to login", 0);
  222.             } else {
  223.                 return true;
  224.             }
  225.         }
  226.  
  227.  
  228.         /**
  229.         * Change the current directory to the following
  230.         * This changes the currently used directory. You can use either an absolute
  231.         * directory-path (e.g. "/home/blah") or a relative one (e.g. "../test").
  232.         *
  233.         * @access public
  234.         * @param string $dir The directory to go to.
  235.         * @return bool $res True on success, otherwise PEAR::Error
  236.         */
  237.  
  238.         function cd ( $dir ) {
  239.             $erg = @ftp_chdir($this->_handle, $dir);
  240.             if (!$erg) {
  241.                 return $this->raiseError("Directory change failed", 2);
  242.             } else {
  243.                 return true;
  244.             }
  245.         }
  246.  
  247.         /**
  248.         * Show's you the actual path on the server
  249.         * This function questions the ftp-handle for the actual selected path and returns it.
  250.         *
  251.         * @access public
  252.         * @return string $path The actual path or PEAR::Error
  253.         */
  254.  
  255.         function pwd () {
  256.             $res = @ftp_pwd($this->_handle);
  257.             if (!$res) {
  258.                 return $this->raiseErrro("Could not determine the actual path.", 0);
  259.             } else {
  260.                 return $res;
  261.             }
  262.         }
  263.  
  264.         /**
  265.         * Make a new dir on the ftp-server
  266.         * This works similar to the mkdir-command on your local maschine. You can either give
  267.         * it an absolute or relative path. The relative path will be completed with the actual
  268.         * selected server-path. (see: pwd())
  269.         *
  270.         * @access public
  271.         * @param string $dir Absolute or relative dir-path
  272.         * @param bool $recursive Create all needed directories
  273.         * @return bool $res True on success, otherwise PEAR::Error
  274.         */
  275.  
  276.         function mkdir($dir, $recursive = false) {
  277.             $dir = $this->_construct_path($dir);
  278.             if ($recursive === false){
  279.                 $res = @ftp_mkdir($this->_handle, $dir);
  280.                 if (!$res) {
  281.                    return $this->raiseError("Creation of '$dir' failed", 0);
  282.                 } else {
  283.                     return true;
  284.                 }
  285.             } else {
  286.                 $pos = 0;
  287.                 $elements = array();
  288.                 while (false !== ($pos = strpos($dir, '/', $pos + 1))){
  289.                     $elements[] = substr($dir, 0, $pos);
  290.                 }
  291.                 foreach ($elements as $element){
  292.                     $this->mkdir($element, false);
  293.                 }
  294.                 return true;
  295.             }
  296.         }
  297.  
  298.         /**
  299.         * Execute a command on the server (per SITE EXEC)
  300.         * This method tries executing a command on the ftp, using SITE EXEC.
  301.         *
  302.         * @access public
  303.         * @param string $command The command to execute
  304.         * @return mixed $res The result of the command (if successfull), otherwise PEAR::Error
  305.         */
  306.  
  307.         function execute ( $command ) {
  308.             $res = @ftp_exec($this->_handle, $command);
  309.             if (!$res) {
  310.                 return $this->raiseError("Execution of command '$command' faild.", 0);
  311.             } else {
  312.                 return $res;
  313.             }
  314.         }
  315.  
  316.         /**
  317.         * Get the last modification-time of a file.
  318.         * This will return the last modification-time of a file. You can either give this
  319.         * function a relative or an absolute path to the file to ckeck.
  320.         * NOTE: Some servers will not support this feature and the function works
  321.         * only on files, not directories! When successfull,
  322.         * it will return the last modification-time as a unix-timestamp or, when $format is
  323.         * specified, a preformated timestring.
  324.         *
  325.         * @access public
  326.         * @param string $file The file to check
  327.         * @param string $format The format to give the date back (optional, if not set, it will return a Unix timestamp)
  328.         * @return mixed Unix timestamp, a preformated date-string or PEAR::Error
  329.         */
  330.  
  331.         function mdtm ( $file, $format = null ) {
  332.  
  333.             $file = $this->_construct_path($file);
  334.             if ($this->_check_dir($file)) {
  335.                 return $this->raiseError("Filename '$file' seems to be a directory.", 0);
  336.             }
  337.             $res = ftp_mdtm($this->_handle, $file);
  338.             if ($res == -1) {
  339.                 return $this->raiseError("Could not get last-modification-date of '$file'.", 0);
  340.             }
  341.             if (isset($format)) {
  342.                 $res = date($format, $res);
  343.                 if (!$res) {
  344.                     return $this->raiseError("Date-format failed on timestamp '$res'.", 0);
  345.                 }
  346.             }
  347.             return $res;
  348.         }
  349.  
  350.         /**
  351.         * Get the size of a file
  352.         * This will return the size of a given file in bytes. You can either give this function
  353.         * a relative or an absolute file-path. NOTE: Some servers do not support this feature!
  354.         *
  355.         * @access public
  356.         * @param string $file The file to check
  357.         * @return int $res Size in bytes or PEAR::Error
  358.         */
  359.  
  360.         function size ( $file ) {
  361.  
  362.             $file = $this->_construct_path($file);
  363.             $res = ftp_size($this->_handle, $file);
  364.             if ($res == -1) {
  365.                 return $this->raiseError("Could not determine filesize of '$file'.", 0);
  366.             } else {
  367.                 return $res;
  368.             }
  369.         }
  370.  
  371.         /**
  372.         * List the current (or another) directory
  373.         * This method returns a directory-list of the current directory or given one.
  374.         * To display the current selected directory, simply set the first parameter to null
  375.         * or leave it blank, if you do not want to use any other parameters.
  376.         * <BR><BR>
  377.         * There are 4 different modes of listing directories. Either to list only
  378.         * the files (using NET_FTP_FILES_ONLY), to list only directories (using
  379.         * NET_FTP_DIRS_ONLY) or to show both (using NET_FTP_DIRS_FILES, which is default).
  380.         * <BR><BR>
  381.         * The 4th one is the NET_FTP_RAWLIST, which returns just the array created by the
  382.         * ftp_rawlist()-function build into PHP.
  383.         * <BR><BR>
  384.         * The other function-modes will return an array containing the requested data.
  385.         * The files and dirs are listed in human-sorted order, but if you select
  386.         * NET_FTP_DIRS_FILES the directories will be added above the files,
  387.         * but although both sorted.
  388.         * <BR><BR>
  389.         * All elements in the arrays are assotiative arrays themselves. The have the following
  390.         * structure:
  391.         * <BR><BR>
  392.         * Dirs:<BR>
  393.         *           ["name"]        =>  string The name of the directory<BR>
  394.         *           ["rights"]      =>  string The rights of the directory (in style "rwxr-xr-x")<BR>
  395.         *           ["user"]        =>  string The owner of the directory<BR>
  396.         *           ["group"]       =>  string The group-owner of the directory<BR>
  397.         *           ["files_inside"]=>  string The number of files/dirs inside the directory
  398.         *                                      excluding "." and ".."<BR>
  399.         *           ["date"]        =>  int The creation-date as Unix timestamp<BR>
  400.         *           ["is_dir"]      =>  bool true, cause this is a dir<BR>
  401.         * <BR><BR>
  402.         * Files:<BR>
  403.         *           ["name"]        =>  string The name of the file<BR>
  404.         *           ["size"]        =>  int Size in bytes<BR>
  405.         *           ["rights"]      =>  string The rights of the file (in style "rwxr-xr-x")<BR>
  406.         *           ["user"]        =>  string The owner of the file<BR>
  407.         *           ["group"]       =>  string The group-owner of the file<BR>
  408.         *           ["date"]        =>  int The creation-date as Unix timestamp<BR>
  409.         *           ["is_dir"]      =>  bool false, cause this is a file<BR>
  410.         *
  411.         * @access public
  412.         * @param string $dir The directory to list or null, when listing the current directory.
  413.         * @param int $mode The mode which types to list (files, directories or both).
  414.         * @return array $listing The directory list as described above or PEAR::Error on failure.
  415.         */
  416.  
  417.         function ls ( $dir = null, $mode = NET_FTP_DIRS_FILES ) {
  418.             if (!isset($dir)) {
  419.                 $dir = ftp_pwd($this->_handle);
  420.                 if (!$dir) {
  421.                     return $this->raiseError("Could not retrieve current directory", 4);
  422.                 }
  423.             }
  424.             if (($mode != NET_FTP_FILES_ONLY) && ($mode != NET_FTP_DIRS_ONLY) && ($mode != NET_FTP_RAWLIST)) {
  425.                 $mode = NET_FTP_DIRS_FILES;
  426.             }
  427.  
  428.             switch ($mode) {
  429.                 case NET_FTP_DIRS_FILES:    $res = $this->_ls_both ( $dir );
  430.                                             break;
  431.                 case NET_FTP_DIRS_ONLY:     $res = $this->_ls_dirs ( $dir );
  432.                                             break;
  433.                 case NET_FTP_FILES_ONLY:    $res = $this->_ls_files ( $dir );
  434.                                             break;
  435.                 case NET_FTP_RAWLIST:       $res = ftp_rawlist($this->_handle, $dir);
  436.                                             break;
  437.             }
  438.  
  439.             return $res;
  440.         }
  441.  
  442.         /**
  443.         * This will delete a file or directory (maybe recursive).
  444.         * This method will delete the given file or directory ($path) from the server.
  445.         *
  446.         * Wether the given string is a file or directory is only determined by the last
  447.         * sign inside the string ("/" or not).
  448.         *
  449.         * If you specify a directory, you can optionally specify $recursive as true,
  450.         * to let the directory be deleted recursive (with all sub-directories and files
  451.         * inherited).
  452.         *
  453.         * You can either give a absolute or relative path for the file / dir. If you choose to
  454.         * use the relative path, it will be automatically completed with the actual
  455.         * selected directory.
  456.         *
  457.         * @access public
  458.         * @param string $path The absolute or relative path to the file / directory.
  459.         * @return bool $res True on success, otherwise PEAR::Error
  460.         */
  461.  
  462.         function rm ( $path, $recursive = false ) {
  463.  
  464.             $path = $this->_construct_path($path);
  465.  
  466.             if ($this->_check_dir($path)) {
  467.                 if ($recursive) {
  468.                     return $this->_rm_dir_recursive($path);
  469.                 } else {
  470.                     return $this->_rm_dir($path);
  471.                 }
  472.             } else {
  473.                 return $this->_rm_file($path);
  474.             }
  475.         }
  476.  
  477.         /**
  478.         * Download a file from the ftp-server
  479.         * This function will download a file from the ftp-server. You can either spcify a absolute
  480.         * path to the file (beginning with "/") or a relative one, which will be completed
  481.         * with the actual directory you selected on the server. You can specify
  482.         * the path to which the file will be downloaded on the local
  483.         * maschine, if the file should be overwritten if it exists (optionally, default is
  484.         * no overwriting) and in which mode (FTP_ASCII or FTP_BINARY) the file should be
  485.         * downloaded (if you do not specify this, the method tries to determine it automatically
  486.         * from the mode-directory or uses the default-mode, set by you). If you give a relative
  487.         * path to the local-file, the script-path is used as basepath.
  488.         *
  489.         * @access public
  490.         * @param string $remote_file The absolute or relative path to the file to download
  491.         * @param string $local_file The local file to put the downloaded in
  492.         * @param bool $overwrite Wether to overwrite existing file
  493.         * @param int $mode Either FTP_ASCII or FTP_BINARY
  494.         * @return bool $res True on success, otherwise PEAR::Error
  495.         */
  496.  
  497.         function get ( $remote_file, $local_file, $overwrite = false, $mode = null ) {
  498.  
  499.             if (!isset($mode)) {
  500.                 $mode = $this->checkFileExtension($remote_file);
  501.             }
  502.  
  503.             $remote_file = $this->_construct_path($remote_file);
  504.  
  505.             if (@file_exists($local_file) && !$overwrite) {
  506.                 return $this->raiseError("Local file '$local_file' exists and may not be overwriten.", 0);
  507.             }
  508.             if (@file_exists($local_file) && !@is_writeable($local_file) && $overwrite) {
  509.                 return $this->raiseError("Local file '$file' is not writeable. Can not overwrite.", 0);
  510.             }
  511.  
  512.             if (@function_exists('ftp_nb_get')){
  513.                 $res = @ftp_nb_get($this->_handle, $local_file, $remote_file, $mode);
  514.                 while ($res == FTP_MOREDATA) {
  515.                     $res = ftp_nb_continue ($this->_handle);
  516.                 }
  517.             } else {
  518.                 $res = @ftp_get($this->_handle, $local_file, $remote_file, $mode);
  519.             }
  520.             if (!$res) {
  521.                 return $this->raiseError("File '$remote_file' could not be downloaded to '$local_file'.", 0);
  522.             } else {
  523.                 return true;
  524.             }
  525.         }
  526.  
  527.         /**
  528.         * Upload a file from the ftp-server
  529.         * This function will upload a file to the ftp-server. You can either spcify a absolute
  530.         * path to the remote-file (beginning with "/") or a relative one, which will be completed
  531.         * with the actual directory you selected on the server. You can specify
  532.         * the path from which the file will be uploaded on the local
  533.         * maschine, if the file should be overwritten if it exists (optionally, default is
  534.         * no overwriting) and in which mode (FTP_ASCII or FTP_BINARY) the file should be
  535.         * downloaded (if you do not specify this, the method tries to determine it automatically
  536.         * from the mode-directory or uses the default-mode, set by you). If you give a relative
  537.         * path to the local-file, the script-path is used as basepath.
  538.         *
  539.         * @access public
  540.         * @param string $local_file The local file to upload
  541.         * @param string $remote_file The absolute or relative path to the file to upload to
  542.         * @param bool $overwrite Wether to overwrite existing file
  543.         * @param int $mode Either FTP_ASCII or FTP_BINARY
  544.         * @return bool $res True on success, otherwise PEAR::Error
  545.         */
  546.  
  547.         function put ( $local_file, $remote_file, $overwrite = false, $mode = null ) {
  548.  
  549.             if (!isset($mode)) {
  550.                 $mode = $this->checkFileExtension($local_file);
  551.             }
  552.             $remote_file = $this->_construct_path($remote_file);
  553.  
  554.             if (!@file_exists($local_file)) {
  555.                 return $this->raiseError("Local file '$local_file' does not exist.", 0);
  556.             }
  557.             if ((@ftp_size($this->_handle, $remote_file) != -1) && !$overwrite) {
  558.                 return $this->raiseError("Remote file '$remote_file' exists and may not be overwriten.", 0);
  559.             }
  560.  
  561.             if (function_exists('ftp_nb_put')){
  562.                 $res = @ftp_nb_put($this->_handle, $remote_file, $local_file, $mode);
  563.                 while ($res == FTP_MOREDATA) {
  564.                     $res = @ftp_nb_continue($this->_handle);
  565.                 }
  566.  
  567.             } else {
  568.                 $res = ftp_put($this->_handle, $remote_file, $local_file, $mode);
  569.             }
  570.             if (!$res) {
  571.                 return $this->raiseError("File '$local_file' could not be uploaded to '$remote_file'.", 0);
  572.             } else {
  573.                 return true;
  574.             }
  575.         }
  576.  
  577.  
  578.         /**
  579.         * Transfer a whole directory-structure to the local host
  580.         * This functiona allows you to transfer a whole directory-structure from the
  581.         * remote-ftp to your local host. You have to give a remote-directory (ending with
  582.         * '/') and the local directory (ending with '/') where to put the files you download.
  583.         * The remote path is automatically completed with the current-remote-dir, if you give
  584.         * a relative path to this function. You can give a relative path for the $local_path,
  585.         * too. Then the script-basedir will be used for comletion of the path.
  586.         * The parameter $overwrite will determine, wether to overwrite existing files or not.
  587.         * Standard for this is false. Fourth you can explicitly set a mode for all transfer-
  588.         * actions done. If you do not set this, the method tries to determine the transfer-
  589.         * mode by checking your mode-directory for the file-extension. If the extension is not
  590.         * inside the mode-directory, it will get your default-mode.
  591.         *
  592.         * @access public
  593.         * @param string $remote_path The path to download
  594.         * @param string $local_path The path to download to
  595.         * @param bool $overwrite Wether to overwrite existing files (true) or not (false, standard). (optional)
  596.         * @param int $mode The transfermode (either FTP_ASCII or FTP_BINARY). (optional)
  597.         * @return bool $res True on succes, otherwise PEAR::Error
  598.         */
  599.  
  600.         function getRecursive ( $remote_path, $local_path, $overwrite = false, $mode = null ) {
  601.  
  602.             $remote_path = $this->_construct_path($remote_path);
  603.             if (!$this->_check_dir($remote_path)) {
  604.                 return $this->raiseError("Given remote-path '$remote_path' seems not to be a directory.", 0);
  605.             }
  606.             if (!$this->_check_dir($local_path)) {
  607.                 return $this->raiseError("Given local-path '$local_path' seems not to be a directory.", 0);
  608.             }
  609.  
  610.             if (!@is_dir($local_path)) {
  611.                 $res = @mkdir($local_path);
  612.                 if (!$res) {
  613.                     return $this->raiseError("Could not create dir '$local_path'", 0);
  614.                 }
  615.             }
  616.             $dir_list = array();
  617.             $dir_list = $this->ls($remote_path, $mode = NET_FTP_DIRS_ONLY);
  618.             foreach ($dir_list as $dir_entry) {
  619.                 $remote_path_new = $remote_path.$dir_entry["name"]."/";
  620.                 $local_path_new = $local_path.$dir_entry["name"]."/";
  621.                 $result = $this->getRecursive($remote_path_new, $local_path_new, $overwrite, $mode);
  622.                 if ($this->isError($result)) {
  623.                     return $result;
  624.                 }
  625.             }
  626.             $file_list = array();
  627.             $file_list = $this->ls($remote_path, $mode = NET_FTP_FILES_ONLY);
  628.             foreach ($file_list as $file_entry) {
  629.                 $remote_file = $remote_path.$file_entry["name"];
  630.                 $local_file = $local_path.$file_entry["name"];
  631.                 $result = $this->get($remote_file, $local_file, $overwrite, $mode);
  632.                 if ($this->isError($result)) {
  633.                     return $result;
  634.                 }
  635.             }
  636.             return true;
  637.         }
  638.  
  639.         /**
  640.         * Transfer a whole directory-structure to the local host
  641.         * This functiona allows you to transfer a whole directory-structure from the
  642.         * remote-ftp to your local host. You have to give a remote-directory (ending with
  643.         * '/') and the local directory (ending with '/') where to put the files you download.
  644.         * The remote path is automatically completed with the current-remote-dir, if you give
  645.         * a relative path to this function. You can give a relative path for the $local_path,
  646.         * too. Then the script-basedir will be used for comletion of the path.
  647.         * The parameter $overwrite will determine, wether to overwrite existing files or not.
  648.         * Standard for this is false. Fourth you can explicitly set a mode for all transfer-
  649.         * actions done. If you do not set this, the method tries to determine the transfer-
  650.         * mode by checking your mode-directory for the file-extension. If the extension is not
  651.         * inside the mode-directory, it will get your default-mode.
  652.         *
  653.         * @access public
  654.         * @param string $remote_path The path to download
  655.         * @param string $local_path The path to download to
  656.         * @param bool $overwrite Wether to overwrite existing files (true) or not (false, standard). (optional)
  657.         * @param int $mode The transfermode (either FTP_ASCII or FTP_BINARY). (optional)
  658.         * @return bool $res True on succes, otherwise PEAR::Error
  659.         */
  660.  
  661.         function putRecursive ( $local_path, $remote_path, $overwrite = false, $mode = null ) {
  662.             $remote_path = $this->_construct_path($remote_path);
  663.             if (!$this->_check_dir($local_path) || !is_dir($local_path)) {
  664.                 return $this->raiseError("Given local-path '$local_path' seems not to be a directory.", 0);
  665.             }
  666.             if (!$this->_check_dir($remote_path)) {
  667.                 return $this->raiseError("Given remote-path '$remote_path' seems not to be a directory.", 0);
  668.             }
  669.             $old_path = $this->pwd();
  670.             if ($this->isError($this->cd($remote_path))) {
  671.                 $res = $this->mkdir($remote_path);
  672.                 if ($this->isError($res)) {
  673.                     return $res;
  674.                 }
  675.             }
  676.             $this->cd($old_path);
  677.             $dir_list = $this->_ls_local($local_path);
  678.             foreach ($dir_list["dirs"] as $dir_entry) {
  679.                 $remote_path_new = $remote_path.$dir_entry."/";
  680.                 $local_path_new = $local_path.$dir_entry."/";
  681.                 $result = $this->putRecursive($local_path_new, $remote_path_new, $overwrite, $mode);
  682.                 if ($this->isError($result)) {
  683.                     return $result;
  684.                 }
  685.             }
  686.  
  687.             foreach ($dir_list["files"] as $file_entry) {
  688.                 $remote_file = $remote_path.$file_entry;
  689.                 $local_file = $local_path.$file_entry;
  690.                 $result = $this->put($local_file, $remote_file, $overwrite, $mode);
  691.                 /*if ($this->isError($result)) {
  692.                     return $result;
  693.                 }*/
  694.             }
  695.             return true;
  696.         }
  697.  
  698.         /**
  699.         * Check a filname to be transfered in ascii- or binary-mode
  700.         * This checks, wether a file should be transfered in ascii- or binary-mode
  701.         * by it's file-extension. If the file-extension is not set or
  702.         * the extension is not inside one of the extension-dirs, the actual set
  703.         * transfer-mode is returned.
  704.         *
  705.         * @access public
  706.         * @param string $filename The filename to be checked
  707.         * @return int Either FTP_ASCII or FTP_BINARY
  708.         */
  709.  
  710.         function checkFileExtension ( $filename ) {
  711.  
  712.             $pattern = "/\.(.*)$/";
  713.             $has_extension = preg_match($pattern, $filename, $eregs);
  714.             if (!$has_extension) {
  715.                 return $this->_mode;
  716.             } else {
  717.                 $ext = $eregs[1];
  718.             }
  719.  
  720.             if (!empty($this->_file_extensions[$ext])) {
  721.                 return $this->_file_extensions[$ext];
  722.             }
  723.  
  724.             return $this->_mode;
  725.         }
  726.  
  727.         /**
  728.         * Reconstruates the path, if given relative
  729.         * Reconstruates the path, if given relative
  730.         *
  731.         * @access private
  732.         * @param string $path The path to check and construct
  733.         * @return string $path The build path
  734.         */
  735.  
  736.         function _construct_path ( $path ) {
  737.  
  738.             if (substr($path, 0, 1) != "/") {
  739.                 $actual_dir = ftp_pwd($this->_handle);
  740.                 if (substr($actual_dir, (strlen($actual_dir) - 2), 1) != "/") {
  741.                     $actual_dir .= "/";
  742.                 }
  743.                 $path = $actual_dir.$path;
  744.             }
  745.             return $path;
  746.         }
  747.         
  748.         /**
  749.         * Checks, wether a given string is a directory-path (ends with "/") or not.
  750.         * Checks, wether a given string is a directory-path (ends with "/") or not.
  751.         *
  752.         * @access private
  753.         * @param string $path Path to check
  754.         * @return bool $res True if $path is a directory, otherwise false
  755.         */
  756.  
  757.         function _check_dir ( $path ) {
  758.  
  759.             if (substr($path, (strlen($path) - 1), 1) == "/") {
  760.                 return true;
  761.             } else {
  762.                 return false;
  763.             }
  764.         }
  765.  
  766.         /**
  767.         * This will remove a file
  768.         * This will remove a file
  769.         *
  770.         * @access private
  771.         * @param string $file The file to delete
  772.         * @return bool $res true on success, otherwise PEAR::Error
  773.         */
  774.  
  775.         function _rm_file ( $file ) {
  776.  
  777.             if (substr($file, 0, 1) == "/") {
  778.                 $res = @ftp_delete($this->_handle, $file);
  779.             } else {
  780.                 $actual_dir = ftp_pwd($this->_handle);
  781.                 if (substr($actual_dir, (strlen($actual_dir) - 2), 1) != "/") {
  782.                     $actual_dir .= "/";
  783.                 }
  784.                 $file = $actual_dir.$file;
  785.                 $res = @ftp_delete($this->_handle, $file);
  786.             }
  787.  
  788.             if (!$res) {
  789.                 return $this->raiseError("Could not delete file '$file'.", 0);
  790.             } else {
  791.                 return true;
  792.             }
  793.         }
  794.  
  795.         /**
  796.         * This will remove a dir
  797.         * This will remove a dir
  798.         *
  799.         * @access private
  800.         * @param string $dir The dir to delete
  801.         * @return bool $res true on success, otherwise PEAR::Error
  802.         */
  803.  
  804.         function _rm_dir ( $dir ) {
  805.             if (substr($dir, (strlen($dir) - 1), 1) != "/") {
  806.                 return $this->raiseError("Directory name '$dir' is invalid, has to end with '/'", 0);
  807.             }
  808.             $res = @ftp_rmdir($this->_handle, $dir);
  809.             if (!$res) {
  810.                 return $this->raiseError("Could not delete directory '$dir'.", 0);
  811.             } else {
  812.                 return $true;
  813.             }
  814.         }
  815.  
  816.         /**
  817.         * This will remove a dir and all subdirs and -files
  818.         * This will remove a dir and all subdirs and -files
  819.         *
  820.         * @access private
  821.         * @param string $file The dir to delete recursively
  822.         * @return bool $res true on success, otherwise PEAR::Error
  823.         */
  824.  
  825.         function _rm_dir_recursive ( $dir ) {
  826.             if (substr($dir, (strlen($dir) - 1), 1) != "/") {
  827.                 return $this->raiseError("Directory name '$dir' is invalid, has to end with '/'", 0);
  828.             }
  829.             $file_list = $this->_ls_files($dir);
  830.             foreach ($file_list as $file) {
  831.                 $file = $dir.$file["name"];
  832.                 $res = $this->rm($file);
  833.                 if ($this->isError($res)) {
  834.                     return $res;
  835.                 }
  836.  
  837.             }
  838.             $dir_list = $this->_ls_dirs($dir);
  839.             foreach ($dir_list as $new_dir) {
  840.                 $new_dir = $dir.$new_dir["name"]."/";
  841.                 $res = $this->_rm_dir_recursive($new_dir);
  842.                 if ($this->isError($res)) {
  843.                     return $res;
  844.                 }
  845.             }
  846.             $res = $this->_rm_dir($dir);
  847.             if (!$res) {
  848.                 return $res;
  849.             } else {
  850.                 return true;
  851.             }
  852.         }
  853.  
  854.         /**
  855.         * Lists up files and directories
  856.         * Lists up files and directories
  857.         *
  858.         * @access private
  859.         * @param string $dir The directory to list up
  860.         * @return array $res An array of dirs and files
  861.         */
  862.  
  863.         function _ls_both ( $dir ) {
  864.             $list_splitted = $this->_list_and_parse($dir);
  865.             if (!is_array($list_splitted["files"])) {
  866.                 $list_splitted["files"] = array();
  867.             }
  868.             if (!is_array($list_splitted["dirs"])) {
  869.                 $list_splitted["dirs"] = array();
  870.             }
  871.             $res = array();
  872.             @array_splice($res, 0, 0, $list_splitted["files"]);
  873.             @array_splice($res, 0, 0, $list_splitted["dirs"]);
  874.             return $res;
  875.         }
  876.  
  877.         /**
  878.         * Lists up directories
  879.         * Lists up directories
  880.         *
  881.         * @access private
  882.         * @param string $dir The directory to list up
  883.         * @return array $res An array of dirs
  884.         */
  885.  
  886.         function _ls_dirs ( $dir ) {
  887.             if (!is_array($list["files"])) {
  888.                 $list["dirs"] = array();
  889.             }
  890.             $list = $this->_list_and_parse($dir);
  891.             return $list["dirs"];
  892.         }
  893.  
  894.         /**
  895.         * Lists up files
  896.         * Lists up files
  897.         *
  898.         * @access private
  899.         * @param string $dir The directory to list up
  900.         * @return array $res An array of files
  901.         */
  902.  
  903.         function _ls_files ( $dir ) {
  904.             $list = $this->_list_and_parse($dir);
  905.             if (!is_array($list["files"])) {
  906.                 $list["files"] = array();
  907.             }
  908.             return $list["files"];
  909.         }
  910.  
  911.         /**
  912.         * This lists up the directory-content and parses the items into well-formated arrays
  913.         * This lists up the directory-content and parses the items into well-formated arrays
  914.         * The results of this array are sorted (dirs on top, sorted by name;
  915.         * files below, sorted by name).
  916.         *
  917.         * @access private
  918.         * @param string $dir The directory to parse
  919.         * @return array $res Lists of dirs and files
  920.         */
  921.  
  922.         function _list_and_parse ( $dir ) {
  923.             $dirs_list = array();
  924.             $files_list = array();
  925.             $dir_list = ftp_rawlist($this->_handle, $dir);
  926.             foreach ($dir_list as $entry) {
  927.                 // ([1] = directory?, [2] = rights, [3] = files below, [4] = user,
  928.                 //  [5] = group, [6] = size, [7] = date, [8]  = name)
  929.                 $res_1 = @ereg(NET_FTP_DIR_EREG ,$entry,$eregs);
  930.                 if (!$res_1) {
  931.                     var_dump($entry);
  932.                     return $this->raiseError("Raw directory-list in wrong format.", 0);
  933.                 }
  934.                 $is_dir = (@trim($eregs[1]) == "d");
  935.                 // snip link-locations (have to clean that up later)
  936.                 if (@trim($eregs[1]) == "l") {
  937.                     preg_match("/(.*) -> (.*)/", $eregs[8], $matches);
  938.                     $eregs[8] = $matches[1];
  939.                 }
  940.                 
  941.                 $date = $this->_parse_date($eregs[7]);
  942.                 // $date = $eregs[7];
  943.                 if (!$date) {
  944.                     return $this->raiseError("Can not parse date from raw directory-list on '$dir'.", 0);
  945.                 }
  946.                 if ($is_dir) {
  947.                     $dirs_list[] = array("name"         =>  $eregs[8],
  948.                                         "rights"        =>  $eregs[2],
  949.                                         "user"          =>  $eregs[4],
  950.                                         "group"         =>  $eregs[5],
  951.                                         "files_inside"  =>  $eregs[3],
  952.                                         "date"          =>  $date,
  953.                                         "is_dir"        =>  $is_dir);
  954.                 } else {
  955.                     $files_list[] = array("name"        =>  $eregs[8],
  956.                                          "size"         =>  (int)$eregs[6],
  957.                                          "rights"       =>  $eregs[2],
  958.                                          "user"         =>  $eregs[4],
  959.                                          "group"        =>  $eregs[5],
  960.                                          "date"         =>  $date,
  961.                                          "is_dir"       =>  $is_dir);
  962.                 }
  963.             }
  964.             @usort($dirs_list, array("Net_FTP", "_nat_sort"));
  965.             @usort($files_list, array("Net_FTP", "_nat_sort"));
  966.             $res["dirs"] = $dirs_list;
  967.             $res["files"] = $files_list;
  968.             return $res;
  969.         }
  970.  
  971.         /**
  972.         * Lists a local directory
  973.         * Lists a local directory
  974.         *
  975.         * @access private
  976.         * @param string $dir_path The dir to list
  977.         * @return array $res The list of dirs and files
  978.         */
  979.  
  980.         function _ls_local ($dir_path) {
  981.  
  982.             $dir = dir($dir_path);
  983.             $dir_list = array();
  984.             $file_list = array();
  985.             while (false !== ($entry = $dir->read())) {
  986.                 if (($entry != ".") && ($entry != "..")) {
  987.                     if (is_dir($dir_path.$entry)) {
  988.                         $dir_list[] = $entry;
  989.                     } else {
  990.                         $file_list[] = $entry;
  991.                     }
  992.                 }
  993.             }
  994.             $dir->close();
  995.             $res["dirs"] = $dir_list;
  996.             $res["files"] = $file_list;
  997.             return $res;
  998.         }
  999.  
  1000.  
  1001.         /**
  1002.         * Function for use with usort().
  1003.         * Function for use with usort().
  1004.         * Compares the list-array-elements by name.
  1005.         *
  1006.         * @access private
  1007.         */
  1008.  
  1009.         function _nat_sort ( $item_1, $item_2 ) {
  1010.             return strnatcmp($item_1["name"], $item_2["name"]);
  1011.         }
  1012.  
  1013.         /**
  1014.         * Parse dates to timestamps
  1015.         * Parse dates to timestamps
  1016.         *
  1017.         * @access private
  1018.         * @param string $date Date
  1019.         * @return int $res Timestamp
  1020.         */
  1021.  
  1022.         function _parse_Date ( $date ) {
  1023.             // Sep 10 22:06 => Sep 10, <year> 22:06
  1024.             if (preg_match("/([A-Za-z]+)[ ]+([0-9]+)[ ]+([0-9]+):([0-9]+)/", $date, $res)) {
  1025.                 $year = date("Y");
  1026.                 $month = $res[1];
  1027.                 $day = $res[2];
  1028.                 $hour = $res[3];
  1029.                 $minute = $res[4];
  1030.                 $date = "$month $day, $year $hour:$minute";
  1031.             }
  1032.             $res = strtotime($date);
  1033.             if (!$res) {
  1034.                 return $this->raiseError("Dateconversion faild.", 0);
  1035.             }
  1036.             return $res;
  1037.         }
  1038.  
  1039.         /**
  1040.         * Set the Hostname
  1041.         * Set the Hostname
  1042.         *
  1043.         * @access public
  1044.         * @param string $host The Hostname to set
  1045.         */
  1046.  
  1047.         function setHostname ( $host ) {
  1048.             $this->_hostname = $host;
  1049.         }
  1050.  
  1051.         /**
  1052.         * Set the Port
  1053.         * Set the Port
  1054.         *
  1055.         * @access public
  1056.         * @param int $port The Port to set
  1057.         */
  1058.  
  1059.         function setPort ( $port ) {
  1060.             $this->_port = $port;
  1061.         }
  1062.  
  1063.         /**
  1064.         * Set the Username
  1065.         * Set the Username
  1066.         *
  1067.         * @access public
  1068.         * @param string $user The Username to set
  1069.         */
  1070.  
  1071.         function setUsername ( $user ) {
  1072.             $this->_username = $user;
  1073.         }
  1074.  
  1075.         /**
  1076.         * Set the Password
  1077.         * Set the Password
  1078.         *
  1079.         * @access private
  1080.         * @param string $password The Password to set
  1081.         */
  1082.  
  1083.         function setPassword ( $password ) {
  1084.             $this->_password = $password;
  1085.         }
  1086.  
  1087.         /**
  1088.         * Set the transfer-mode
  1089.         * Set the transfer-mode. You can use the predefined constants
  1090.         * FTP_ASCII or FTP_BINARY. The mode will be stored for any further transfers.
  1091.         *
  1092.         * @access public
  1093.         * @param int $mode The mode to set
  1094.         * @return bool $res True on success, otherwise PEAR::Error
  1095.         */
  1096.  
  1097.         function setMode ( $mode ) {
  1098.             if (($mode == FTP_ASCII) || ($mode == FTP_BINARY)) {
  1099.                 $this->_mode = $mode;
  1100.                 return true;
  1101.             } else {
  1102.                 return $this->raiseError("FTP-Mode has either to be FTP_ASCII or FTP_BINARY", 1);
  1103.             }
  1104.         }
  1105.  
  1106.         /**
  1107.         * Set the transfer-method to passive mode
  1108.         * Set the transfer-method to passive mode
  1109.         *
  1110.         * @access public
  1111.         * @return void
  1112.         */
  1113.  
  1114.         function setPassive () {
  1115.             $this->_passv = true;
  1116.             ftp_pasv($this->_handle, true);
  1117.         }
  1118.  
  1119.         /**
  1120.         * Set the transfer-method to active mode
  1121.         * Set the transfer-method to active mode
  1122.         *
  1123.         * @access public
  1124.         * @return void
  1125.         */
  1126.  
  1127.         function setActive () {
  1128.             $this->_passv = false;
  1129.             ftp_pasv($this->_handle, false);
  1130.         }
  1131.  
  1132.         /**
  1133.         * Adds an extension to a mode-directory
  1134.         * The mode-directory saves file-extensions coresponding to filetypes
  1135.         * (ascii e.g.: "php", "txt", "htm",...; binary e.g.: "jpg", "gif", "exe",...).
  1136.         * The extensions have to be saved without the ".". And
  1137.         * can be predefined in an external file (see: getExtensionsFile()).
  1138.         *
  1139.         * The array is build like this: "php" => FTP_ASCII, "png" => FTP_BINARY
  1140.         *
  1141.         * To change the mode of an extension, just add it again with the new mode!
  1142.         *
  1143.         * @access public
  1144.         * @param int $mode Either FTP_ASCII or FTP_BINARY
  1145.         * @param string extension
  1146.         * @return void
  1147.         */
  1148.  
  1149.         function addExtension ( $mode, $ext ) {
  1150.             $this->_file_extensions[$ext] = $mode;
  1151.         }
  1152.  
  1153.         /**
  1154.         * Remove an extension from a mode-directory
  1155.         * The removes an extension from the mode-directories (described above).
  1156.         *
  1157.         * @access public
  1158.         * @param string $ext The extension to remove
  1159.         * @return void
  1160.         */
  1161.  
  1162.         function removeExtension ( $ext ) {
  1163.             unset($this->_file_extensions[$ext]);
  1164.         }
  1165.  
  1166.         /**
  1167.         * Get the mode-directories from a file
  1168.         * This get's both (ascii- and binary-mode-directories) from the given file. Beware,
  1169.         * if you read a file into the mode-directory, all former set values will be
  1170.         * unset!
  1171.         *
  1172.         * @access public
  1173.         * @param string $filename The file to get from
  1174.         * @return bool True on success, otherwise PEAR::Error
  1175.         */
  1176.  
  1177.         function getExtensionsFile ( $filename ) {
  1178.  
  1179.             if (!file_exists($filename)) {
  1180.                 return $this->raiseError("Extensions-file '$filename' does not exist", 0);
  1181.             }
  1182.  
  1183.             if (!is_readable($filename)) {
  1184.                 return $this->raiseError("Extensions-file '$filename' is not readable", 0);
  1185.             }
  1186.  
  1187.             $this->_file_extension = @parse_ini_file($filename);
  1188.             return true;
  1189.         }
  1190.  
  1191.         /**
  1192.         * Returns the Hostname
  1193.         *
  1194.         * @access public
  1195.         * @return string $host The Hostname
  1196.         */
  1197.  
  1198.         function getHostname ( ) {
  1199.             return $this->_hostname;
  1200.         }
  1201.  
  1202.         /**
  1203.         * Returns the Port
  1204.         *
  1205.         * @access public
  1206.         * @return int $port The Port
  1207.         */
  1208.  
  1209.         function getPort ( ) {
  1210.             return $this->_port;
  1211.         }
  1212.  
  1213.         /**
  1214.         * Returns the Username
  1215.         *
  1216.         * @access public
  1217.         * @return string $username The Username
  1218.         */
  1219.  
  1220.         function getUsername ( ) {
  1221.             return $this->_username;
  1222.         }
  1223.  
  1224.         /**
  1225.         * Returns the Password
  1226.         *
  1227.         * @access public
  1228.         * @return string $password The Password
  1229.         */
  1230.  
  1231.         function getPassword ( ) {
  1232.             return $this->_password;
  1233.         }
  1234.  
  1235.         /**
  1236.         * Returns the Transfermode
  1237.         * Returns the Transfermode
  1238.         *
  1239.         * @access public
  1240.         * @return int $mode The transfermode, either FTP_ASCII or FTP_BINARY.
  1241.         */
  1242.  
  1243.         function getMode ( ) {
  1244.             return $this->_mode;
  1245.         }
  1246.  
  1247.         /**
  1248.         * Returns, wether the connection is set to passive mode or not
  1249.         * Returns, wether the connection is set to passive mode or not
  1250.         *
  1251.         * @access public
  1252.         * @return bool True if passive-, false if active-mode
  1253.         */
  1254.  
  1255.         function isPassive ( ) {
  1256.             return $this->_passive;
  1257.         }
  1258.  
  1259.         /**
  1260.         * Returns the mode set for a file-extension
  1261.         * Returns the mode set for a file-extension
  1262.         *
  1263.         * @access public
  1264.         * @param string $ext The extension you wanna ask for
  1265.         * @return int $mode Either FTP_ASCII, FTP_BINARY or NULL (if not set a mode for it)
  1266.         */
  1267.  
  1268.         function getExtensionMode ( $ext ) {
  1269.             return @$this->_file_extensions[$ext];
  1270.         }
  1271.  
  1272.     }
  1273. ?>
  1274.