Net_FTP
[ class tree: Net_FTP ] [ index: Net_FTP ] [ all elements ]

Source for file FTP.php

Documentation is available at FTP.php

  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | Net_FTP Version 1.3                                                  |
  4. // +----------------------------------------------------------------------+
  5. // | Copyright (c) 2001-2004 Tobias Schlitt                               |
  6. // +----------------------------------------------------------------------+
  7. // | This source file is subject to version 3.0 of the PHP license,       |
  8. // | that is available at through the world-wide-web at                   |
  9. // | http://www.php.net/license/3_0.txt.                                  |
  10. // | If you did not receive a copy of the PHP license and are unable to   |
  11. // | obtain it through the world-wide-web, please send a note to          |
  12. // | license@php.net so we can mail you a copy immediately.               |
  13. // +----------------------------------------------------------------------+
  14. // | Authors:       Tobias Schlitt <toby@php.net>                         |
  15. // +----------------------------------------------------------------------+
  16. //
  17. // $Id: FTP.php,v 1.19 2004/05/25 18:38:44 toby Exp $
  18.  
  19. require_once 'PEAR.php';
  20.  
  21. define("NET_FTP_FILES_ONLY"0true);
  22. define("NET_FTP_DIRS_ONLY",  1true);
  23. define("NET_FTP_DIRS_FILES"2true);
  24. define("NET_FTP_RAWLIST",    3true);
  25.  
  26. /**
  27.  * Class for comfortable FTP-communication
  28.  *
  29.  * This class provides comfortable communication with FTP-servers. You may do everything
  30.  * enabled by the PHP-FTP-extension and further functionalities, like recursive-deletion,
  31.  * -up- and -download. Another feature is to create directories recursively.
  32.  *
  33.  * @since     PHP 4.2.3
  34.  * @author    Tobias Schlitt <toby@php.net>
  35.  * @see       http://www.schlitt.info
  36.  * @license   http://www.php.net/license/3_0.txt  PHP License 3.0
  37.  */
  38.  
  39. class Net_FTP extends PEAR 
  40. {
  41.     /**
  42.      * The host to connect to
  43.      *
  44.      * @access  private
  45.      * @var     string 
  46.      */
  47.     
  48.     var $_hostname;
  49.  
  50.     /**
  51.      * The port for ftp-connection (standard is 21)
  52.      *
  53.      * @access  private
  54.      * @var     int 
  55.      */
  56.     
  57.     var $_port = 21;
  58.  
  59.     /**
  60.      * The username for login
  61.      *
  62.      * @access  private
  63.      * @var     string 
  64.      */
  65.     
  66.     var $_username;
  67.  
  68.     /**
  69.      * The password for login
  70.      *
  71.      * @access  private
  72.      * @var     string 
  73.      */
  74.     
  75.     var $_password;
  76.  
  77.     /**
  78.      * Determine whether to use passive-mode (true) or active-mode (false)
  79.      *
  80.      * @access  private
  81.      * @var     bool 
  82.      */
  83.     
  84.     var $_passv;
  85.  
  86.     /**
  87.      * The standard mode for ftp-transfer
  88.      *
  89.      * @access  private
  90.      * @var     int 
  91.      */
  92.     
  93.     var $_mode = FTP_BINARY;
  94.  
  95.     /**
  96.      * This holds the handle for the ftp-connection
  97.      *
  98.      * @access  private
  99.      * @var     resource 
  100.      */
  101.     
  102.     var $_handle;
  103.  
  104.     /**
  105.      * Saves file-extensions for ascii- and binary-mode
  106.      *
  107.      * The array contains 2 sub-arrays ("ascii" and "binary"), which both contain
  108.      * file-extensions without the "." (".php" = "php").
  109.      *
  110.      * @access  private
  111.      * @var     array 
  112.      */
  113.     
  114.     var $_file_extensions;
  115.  
  116.     /**
  117.      * ls match
  118.      * Matches the ls entries against a regex and maps the resulting array to speaking names
  119.      *
  120.      * @access  private
  121.      * @var     array 
  122.      */
  123.     
  124.     var $_ls_match = array(
  125.         'unix'    => array(
  126.             'pattern' => '/(?:(d)|.)([rwxt-]+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\S+\s+\S+\s+\S+)\s+(.+)/',
  127.             'map'     => array('name'=>8,'size'=>6,'rights'=>2,'user'=>4,'group'=>5,
  128.                               'files_inside'=>3,'date'=>7,'is_dir'=>1)
  129.         ),
  130.         'windows' => array(
  131.             'pattern' => '/(.+)\s+(.+)\s+((<DIR>)|[0-9]+)\s+(.+)/',
  132.             'map'     => array('name'=>5,'date'=>1,'is_dir'=>3)
  133.         )
  134.     );
  135.     
  136.     /**
  137.      * Holds all Net_FTP_Observer objects
  138.      * that wish to be notified of new messages.
  139.      *
  140.      * @var     array 
  141.      * @access  private
  142.      * @since   1.3
  143.      */
  144.     
  145.     var $_listeners = array();
  146.  
  147.     /**
  148.      * This generates a new FTP-Object. The FTP-connection will not be established, yet.
  149.      * You can leave $host and $port blank, if you want. The $host will not be set
  150.      * and the $port will be left at 21. You have to set the $host manualy before
  151.      * trying to connect.
  152.      *
  153.      * @access  public
  154.      * @param   string $host    (optional) The hostname
  155.      * @param   int    $port    (optional) The port
  156.      * @return  void 
  157.      */
  158.     
  159.     function Net_FTP($host = null$port = null)
  160.     {
  161.         $this->PEAR();
  162.         if (isset($host)) {
  163.             $this->setHostname($host);
  164.         }
  165.         if (isset($port)) {
  166.             $this->setPort($port);
  167.         }
  168.  
  169.         $this->_file_extensions[FTP_ASCII= array();
  170.         $this->_file_extensions[FTP_BINARY= array();
  171.     }
  172.  
  173.     /**
  174.      * This function generates the FTP-connection. You can optionally define a
  175.      * hostname and/or a port. If you do so, this data is stored inside the object.
  176.      *
  177.      * @access  public
  178.      * @param   string $host    (optional) The Hostname
  179.      * @param   int    $port    (optional) The Port
  180.      * @return  mixed           True on success, otherwise PEAR::Error
  181.      */
  182.     
  183.     function connect($host = null$port = null)
  184.     {
  185.         if (isset($host)) {
  186.             $this->setHostname($host);
  187.         }
  188.         if (isset($port)) {
  189.             $this->setPort($port);
  190.         }
  191.         $handle @ftp_connect($this->getHostname()$this->getPort());
  192.         if (!$handle{
  193.             return $this->raiseError("Connection to host failed"0);
  194.         else {
  195.             $this->_handle =$handle;
  196.             return true;
  197.         }
  198.     }
  199.  
  200.     /**
  201.      * This function close the FTP-connection
  202.      *
  203.      * @access  public
  204.      * @return  void 
  205.      */
  206.     
  207.     function disconnect()
  208.     {
  209.         @ftp_close($this->_handle);
  210.     }
  211.  
  212.     /**
  213.      * This logges you into the ftp-server. You are free to specify username and password
  214.      * in this method. If you specify it, the values will be taken into the corresponding
  215.      * attributes, if do not specify, the attributes are taken.
  216.      *
  217.      * @access  public
  218.      * @param   string $username  (optional) The username to use
  219.      * @param   string $password  (optional) The password to use
  220.      * @return  mixed             True on success, otherwise PEAR::Error
  221.      */
  222.     
  223.     function login($username = null$password = null)
  224.     {
  225.         if (!isset($username)) {
  226.             $username $this->getUsername();
  227.         else {
  228.             $this->setUsername($username);
  229.         }
  230.  
  231.         if (!isset($password)) {
  232.             $password $this->getPassword();
  233.         else {
  234.             $this->setPassword($password);
  235.         }
  236.  
  237.         $res @ftp_login($this->_handle$username$password);
  238.  
  239.         if (!$res{
  240.             return $this->raiseError("Unable to login"0);
  241.         else {
  242.             return true;
  243.         }
  244.     }
  245.  
  246.     /**
  247.      * This changes the currently used directory. You can use either an absolute
  248.      * directory-path (e.g. "/home/blah") or a relative one (e.g. "../test").
  249.      *
  250.      * @access  public
  251.      * @param   string $dir  The directory to go to.
  252.      * @return  mixed        True on success, otherwise PEAR::Error
  253.      */
  254.     
  255.     function cd($dir)
  256.     {
  257.         $erg @ftp_chdir($this->_handle$dir);
  258.         if (!$erg{
  259.             return $this->raiseError("Directory change failed"2);
  260.         else {
  261.             return true;
  262.         }
  263.     }
  264.  
  265.     /**
  266.      * Show's you the actual path on the server
  267.      * This function questions the ftp-handle for the actual selected path and returns it.
  268.      *
  269.      * @access  public
  270.      * @return  mixed        The actual path or PEAR::Error
  271.      */
  272.     
  273.     function pwd()
  274.     {
  275.         $res @ftp_pwd($this->_handle);
  276.         if (!$res{
  277.             return $this->raiseError("Could not determine the actual path."0);
  278.         else {
  279.             return $res;
  280.         }
  281.     }
  282.  
  283.     /**
  284.      * This works similar to the mkdir-command on your local machine. You can either give
  285.      * it an absolute or relative path. The relative path will be completed with the actual
  286.      * selected server-path. (see: pwd())
  287.      *
  288.      * @access  public
  289.      * @param   string $dir       Absolute or relative dir-path
  290.      * @param   bool   $recursive (optional) Create all needed directories
  291.      * @return  mixed             True on success, otherwise PEAR::Error
  292.      */
  293.     
  294.     function mkdir($dir$recursive = false)
  295.     {
  296.         $dir $this->_construct_path($dir);
  297.         if ($recursive === false){
  298.             $res @ftp_mkdir($this->_handle$dir);
  299.             if (!$res{
  300.                 return $this->raiseError("Creation of '$dir' failed"0);
  301.             else {
  302.                 return true;
  303.             }
  304.         else {
  305.             $pos = 0;
  306.             if(strpos($dir'/'=== false{
  307.                 return $this->mkdir($dir,false);
  308.             }
  309.             $elements = array();
  310.             while (false !== ($pos strpos($dir'/'$pos + 1))){
  311.                 $elements[substr($dir0$pos);
  312.             }
  313.             foreach ($elements as $element){
  314.                 $res $this->mkdir($elementfalse);
  315.                 if($res !== true{
  316.                     return $res;
  317.                 }
  318.             }
  319.             return true;
  320.         }
  321.     }
  322.  
  323.     /**
  324.      * This method tries executing a command on the ftp, using SITE EXEC.
  325.      *
  326.      * @access  public
  327.      * @param   string $command The command to execute
  328.      * @return  mixed           The result of the command (if successfull), otherwise PEAR::Error
  329.      */
  330.     
  331.     function execute($command)
  332.     {
  333.         $res @ftp_exec($this->_handle$command);
  334.         if (!$res{
  335.             return $this->raiseError("Execution of command '$command' failed."0);
  336.         else {
  337.             return $res;
  338.         }
  339.     }
  340.  
  341.     /**
  342.      * Execute a SITE command on the server
  343.      * This method tries to execute a SITE command on the ftp server.
  344.      *
  345.      * @access  public
  346.      * @param   string $command   The command with parameters to execute
  347.      * @return  mixed             True if successful, otherwise PEAR::Error
  348.      */
  349.     
  350.     function site($command)
  351.     {
  352.         $res @ftp_site($this->_handle$command);
  353.         if (!$res{
  354.             return $this->raiseError("Execution of SITE command '$command' failed."0);
  355.         else {
  356.             return $res;
  357.         }
  358.     }
  359.  
  360.     /**
  361.      * This method will try to chmod the file specified on the server
  362.      * Currently, you must give a number as the the permission argument (777 or
  363.      * similar). The file can be either a relative or absolute path.
  364.      * NOTE: Some servers do not support this feature. In that case, you will
  365.      * get a PEAR error object returned. If successful, the method returns true
  366.      *
  367.      * @access  public
  368.      * @param   mixed   $target        The file or array of files to set permissions for
  369.      * @param   integer $permissions   The mode to set the file permissions to
  370.      * @return  mixed                  True if successful, otherwise PEAR::Error
  371.      */
  372.     
  373.     function chmod($target$permissions)
  374.     {
  375.         // If $target is an array: Loop through it.
  376.         if (is_array($target)) {
  377.  
  378.             for ($i = 0; $i count($target)$i++{
  379.                 $res $this->chmod($target[$i]$permissions);
  380.                 if (PEAR::isError($res)) {
  381.                     return $res;
  382.                 // end if isError
  383.             // end for i < count($target)
  384.  
  385.         else {
  386.  
  387.             $res $this->site("CHMOD " $permissions " " $target);
  388.             if (!$res{
  389.                 return PEAR::raiseError("CHMOD " $permissions " " $target " failed"0PEAR_ERROR_RETURN);
  390.             else {
  391.                 return $res;
  392.             }
  393.  
  394.         // end if is_array
  395.  
  396.     // end method chmod
  397.  
  398.     /**
  399.      * This method will try to chmod a folder and all of its contents
  400.      * on the server. The target argument must be a folder or an array of folders
  401.      * and the permissions argument have to be an integer (i.e. 777).
  402.      * The file can be either a relative or absolute path.
  403.      * NOTE: Some servers do not support this feature. In that case, you
  404.      * will get a PEAR error object returned. If successful, the method
  405.      * returns true
  406.      *
  407.      * @access  public
  408.      * @param   mixed   $target        The folder or array of folders to
  409.      *                                  set permissions for
  410.      * @param   integer $permissions   The mode to set the folder
  411.      *                                  and file permissions to
  412.      * @return  mixed                  True if successful, otherwise PEAR::Error
  413.      */
  414.     
  415.     function chmodRecursive($target$permissions)
  416.     {
  417.         static $dir_permissions;
  418.  
  419.         if(!isset($dir_permissions))// Making directory specific permissions
  420.             $dir_permissions $this->makeDirPermissions($permissions);
  421.         }
  422.  
  423.         // If $target is an array: Loop through it
  424.         if (is_array($target)) {
  425.  
  426.             for ($i = 0; $i count($target)$i++{
  427.                 $res $this->chmodRecursive($target[$i]$permissions);
  428.                 if (PEAR::isError($res)) {
  429.                     return $res;
  430.                 // end if isError
  431.             // end for i < count($target)
  432.  
  433.         else {
  434.  
  435.             $remote_path $this->_construct_path($target);
  436.  
  437.             // Chmod the directory itself
  438.             $result $this->chmod($remote_path$dir_permissions);
  439.  
  440.             if (PEAR::isError($result)) {
  441.                 return $result;
  442.             }
  443.  
  444.             // If $remote_path last character is not a slash, add one
  445.             if (substr($remote_pathstrlen($remote_path)-1!= "/"{
  446.  
  447.                 $remote_path .= "/";
  448.             }
  449.  
  450.             $dir_list = array();
  451.             $mode NET_FTP_DIRS_ONLY;
  452.             $dir_list $this->ls($remote_path$mode);
  453.             foreach ($dir_list as $dir_entry{
  454.  
  455.                 $remote_path_new $remote_path.$dir_entry["name"]."/";
  456.  
  457.                 // Chmod the directory we're about to enter
  458.                 $result $this->chmod($remote_path_new$dir_permissions);
  459.  
  460.                 if (PEAR::isError($result)) {
  461.                     return $result;
  462.                 }
  463.  
  464.                 $result $this->chmodRecursive($remote_path_new$permissions);
  465.  
  466.                 if (PEAR::isError($result)) {
  467.                     return $result;
  468.                 }
  469.  
  470.             // end foreach dir_list as dir_entry
  471.  
  472.             $file_list = array();
  473.             $mode NET_FTP_FILES_ONLY;
  474.             $file_list $this->ls($remote_path$mode);
  475.  
  476.             foreach ($file_list as $file_entry{
  477.  
  478.                 $remote_file $remote_path.$file_entry["name"];
  479.  
  480.                 $result $this->chmod($remote_file$permissions);
  481.  
  482.                 if (PEAR::isError($result)) {
  483.                     return $result;
  484.                 }
  485.  
  486.             // end foreach $file_list
  487.  
  488.         // end if is_array
  489.  
  490.         return true; // No errors
  491.  
  492.     // end method chmodRecursive
  493.  
  494.     /**
  495.      * Rename or move a file or a directory from the ftp-server
  496.      *
  497.      * @access public
  498.      * @param string $remote_from The remote file or directory original to rename or move
  499.      * @param string $remote_to The remote file or directory final to rename or move
  500.      * @return bool $res True on success, otherwise PEAR::Error
  501.      */
  502.  
  503.     function rename ($remote_from$remote_to
  504.     {
  505.         $res @ftp_rename($this->_handle$remote_from$remote_to);
  506.         if(!$res{
  507.             return $this->raiseError("Could not rename ".$remote_from." to ".$remote_to." !"0);
  508.         }
  509.         return true;
  510.     }
  511.  
  512.     /**
  513.      * This will return logical permissions mask for directory.
  514.      * if directory have to be writeable it have also be executable
  515.      *
  516.      * @access  private
  517.      * @param   string $permissions    File permissions in digits for file (i.e. 666)
  518.      * @return  string                 File permissions in digits for directory (i.e. 777)
  519.      */
  520.  
  521.     function makeDirPermissions($permissions){
  522.         $permissions = (string)$permissions;
  523.  
  524.         for($i = 0; $i strlen($permissions)$i++)// going through (user, group, world)
  525.             if((int)$permissions{$i4 and !((int)$permissions{$i1))// Read permission is set
  526.                                                                             // but execute not yet
  527.                 (int)$permissions{$i= (int)$permissions{$i+ 1; // Adding execute flag
  528.             }
  529.         }
  530.  
  531.         return (string)$permissions;
  532.     }
  533.  
  534.     /**
  535.      * This will return the last modification-time of a file. You can either give this
  536.      * function a relative or an absolute path to the file to check.
  537.      * NOTE: Some servers will not support this feature and the function works
  538.      * only on files, not directories! When successful,
  539.      * it will return the last modification-time as a unix-timestamp or, when $format is
  540.      * specified, a preformated timestring.
  541.      *
  542.      * @access  public
  543.      * @param   string $file    The file to check
  544.      * @param   string $format  (optional) The format to give the date back
  545.      *                           if not set, it will return a Unix timestamp
  546.      * @return  mixed           Unix timestamp, a preformated date-string or PEAR::Error
  547.      */
  548.     
  549.     function mdtm($file$format = null)
  550.     {
  551.         $file $this->_construct_path($file);
  552.         if ($this->_check_dir($file)) {
  553.             return $this->raiseError("Filename '$file' seems to be a directory."0);
  554.         }
  555.         $res @ftp_mdtm($this->_handle$file);
  556.         if ($res == -1{
  557.             return $this->raiseError("Could not get last-modification-date of '$file'."0);
  558.         }
  559.         if (isset($format)) {
  560.             $res date($format$res);
  561.             if (!$res{
  562.                 return $this->raiseError("Date-format failed on timestamp '$res'."0);
  563.             }
  564.         }
  565.         return $res;
  566.     }
  567.  
  568.     /**
  569.      * This will return the size of a given file in bytes. You can either give this function
  570.      * a relative or an absolute file-path. NOTE: Some servers do not support this feature!
  571.      *
  572.      * @access  public
  573.      * @param   string $file   The file to check
  574.      * @return  mixed          Size in bytes or PEAR::Error
  575.      */
  576.     
  577.     function size($file)
  578.     {
  579.         $file $this->_construct_path($file);
  580.         $res @ftp_size($this->_handle$file);
  581.         if ($res == -1{
  582.             return $this->raiseError("Could not determine filesize of '$file'."0);
  583.         else {
  584.             return $res;
  585.         }
  586.     }
  587.  
  588.     /**
  589.      * This method returns a directory-list of the current directory or given one.
  590.      * To display the current selected directory, simply set the first parameter to null
  591.      * or leave it blank, if you do not want to use any other parameters.
  592.      * <BR><BR>
  593.      * There are 4 different modes of listing directories. Either to list only
  594.      * the files (using NET_FTP_FILES_ONLY), to list only directories (using
  595.      * NET_FTP_DIRS_ONLY) or to show both (using NET_FTP_DIRS_FILES, which is default).
  596.      * <BR><BR>
  597.      * The 4th one is the NET_FTP_RAWLIST, which returns just the array created by the
  598.      * ftp_rawlist()-function build into PHP.
  599.      * <BR><BR>
  600.      * The other function-modes will return an array containing the requested data.
  601.      * The files and dirs are listed in human-sorted order, but if you select
  602.      * NET_FTP_DIRS_FILES the directories will be added above the files,
  603.      * but although both sorted.
  604.      * <BR><BR>
  605.      * All elements in the arrays are associative arrays themselves. The have the following
  606.      * structure:
  607.      * <BR><BR>
  608.      * Dirs:<BR>
  609.      *           ["name"]        =>  string The name of the directory<BR>
  610.      *           ["rights"]      =>  string The rights of the directory (in style "rwxr-xr-x")<BR>
  611.      *           ["user"]        =>  string The owner of the directory<BR>
  612.      *           ["group"]       =>  string The group-owner of the directory<BR>
  613.      *           ["files_inside"]=>  string The number of files/dirs inside the directory
  614.      *                                      excluding "." and ".."<BR>
  615.      *           ["date"]        =>  int The creation-date as Unix timestamp<BR>
  616.      *           ["is_dir"]      =>  bool true, cause this is a dir<BR>
  617.      * <BR><BR>
  618.      * Files:<BR>
  619.      *           ["name"]        =>  string The name of the file<BR>
  620.      *           ["size"]        =>  int Size in bytes<BR>
  621.      *           ["rights"]      =>  string The rights of the file (in style "rwxr-xr-x")<BR>
  622.      *           ["user"]        =>  string The owner of the file<BR>
  623.      *           ["group"]       =>  string The group-owner of the file<BR>
  624.      *           ["date"]        =>  int The creation-date as Unix timestamp<BR>
  625.      *           ["is_dir"]      =>  bool false, cause this is a file<BR>
  626.      *
  627.      * @access  public
  628.      * @param   string $dir   (optional) The directory to list or null, when listing the current directory.
  629.      * @param   int    $mode  (optional) The mode which types to list (files, directories or both).
  630.      * @return  mixed         The directory list as described above or PEAR::Error on failure.
  631.      */
  632.      
  633.     function ls($dir = null$mode = NET_FTP_DIRS_FILES)
  634.     {
  635.         if (!isset($dir)) {
  636.             $dir @ftp_pwd($this->_handle);
  637.             if (!$dir{
  638.                 return $this->raiseError("Could not retrieve current directory"4);
  639.             }
  640.         }
  641.         if (($mode != NET_FTP_FILES_ONLY&& ($mode != NET_FTP_DIRS_ONLY&& ($mode != NET_FTP_RAWLIST)) {
  642.             $mode NET_FTP_DIRS_FILES;
  643.         }
  644.  
  645.         switch ($mode{
  646.             case NET_FTP_DIRS_FILES:    $res $this->_ls_both $dir );
  647.                                         break;
  648.             case NET_FTP_DIRS_ONLY:     $res $this->_ls_dirs $dir );
  649.                                         break;
  650.             case NET_FTP_FILES_ONLY:    $res $this->_ls_files $dir );
  651.                                         break;
  652.             case NET_FTP_RAWLIST:       $res @ftp_rawlist($this->_handle$dir);
  653.                                         break;
  654.         }
  655.  
  656.         return $res;
  657.     }
  658.  
  659.     /**
  660.      * This method will delete the given file or directory ($path) from the server
  661.      * (maybe recursive).
  662.      *
  663.      * Whether the given string is a file or directory is only determined by the last
  664.      * sign inside the string ("/" or not).
  665.      *
  666.      * If you specify a directory, you can optionally specify $recursive as true,
  667.      * to let the directory be deleted recursive (with all sub-directories and files
  668.      * inherited).
  669.      *
  670.      * You can either give a absolute or relative path for the file / dir. If you choose to
  671.      * use the relative path, it will be automatically completed with the actual
  672.      * selected directory.
  673.      *
  674.      * @access  public
  675.      * @param   string $path      The absolute or relative path to the file / directory.
  676.      * @param   bool   $recursive (optional)
  677.      * @return  mixed             True on success, otherwise PEAR::Error
  678.      */
  679.     
  680.     function rm($path$recursive = false)
  681.     {
  682.         $path $this->_construct_path($path);
  683.  
  684.         if ($this->_check_dir($path)) {
  685.             if ($recursive{
  686.                 return $this->_rm_dir_recursive($path);
  687.             else {
  688.                 return $this->_rm_dir($path);
  689.             }
  690.         else {
  691.             return $this->_rm_file($path);
  692.         }
  693.     }
  694.  
  695.     /**
  696.      * This function will download a file from the ftp-server. You can either spcify a absolute
  697.      * path to the file (beginning with "/") or a relative one, which will be completed
  698.      * with the actual directory you selected on the server. You can specify
  699.      * the path to which the file will be downloaded on the local
  700.      * maschine, if the file should be overwritten if it exists (optionally, default is
  701.      * no overwriting) and in which mode (FTP_ASCII or FTP_BINARY) the file should be
  702.      * downloaded (if you do not specify this, the method tries to determine it automatically
  703.      * from the mode-directory or uses the default-mode, set by you). If you give a relative
  704.      * path to the local-file, the script-path is used as basepath.
  705.      *
  706.      * @access  public
  707.      * @param   string $remote_file The absolute or relative path to the file to download
  708.      * @param   string $local_file  The local file to put the downloaded in
  709.      * @param   bool   $overwrite   (optional) Whether to overwrite existing file
  710.      * @param   int    $mode        (optional) Either FTP_ASCII or FTP_BINARY
  711.      * @return  mixed               True on success, otherwise PEAR::Error
  712.      */
  713.     
  714.     function get($remote_file$local_file$overwrite = false$mode = null)
  715.     {
  716.         if (!isset($mode)) {
  717.             $mode $this->checkFileExtension($remote_file);
  718.         }
  719.  
  720.         $remote_file $this->_construct_path($remote_file);
  721.  
  722.         if (@file_exists($local_file&& !$overwrite{
  723.             return $this->raiseError("Local file '$local_file' exists and may not be overwriten."0);
  724.         }
  725.         if (@file_exists($local_file&& !@is_writeable($local_file&& $overwrite{
  726.             return $this->raiseError("Local file '$local_file' is not writeable. Can not overwrite."0);
  727.         }
  728.  
  729.         if (@function_exists('ftp_nb_get')){
  730.             $res @ftp_nb_get($this->_handle$local_file$remote_file$mode);
  731.             while ($res == FTP_MOREDATA{
  732.                 $this->_announce('nb_get');
  733.                 $res @ftp_nb_continue ($this->_handle);
  734.             }
  735.         else {
  736.             $res @ftp_get($this->_handle$local_file$remote_file$mode);
  737.         }
  738.         if (!$res{
  739.             return $this->raiseError("File '$remote_file' could not be downloaded to '$local_file'."0);
  740.         else {
  741.             return true;
  742.         }
  743.     }
  744.  
  745.     /**
  746.      * This function will upload a file to the ftp-server. You can either specify a absolute
  747.      * path to the remote-file (beginning with "/") or a relative one, which will be completed
  748.      * with the actual directory you selected on the server. You can specify
  749.      * the path from which the file will be uploaded on the local
  750.      * maschine, if the file should be overwritten if it exists (optionally, default is
  751.      * no overwriting) and in which mode (FTP_ASCII or FTP_BINARY) the file should be
  752.      * downloaded (if you do not specify this, the method tries to determine it automatically
  753.      * from the mode-directory or uses the default-mode, set by you). If you give a relative
  754.      * path to the local-file, the script-path is used as basepath.
  755.      *
  756.      * @access  public
  757.      * @param   string $local_file  The local file to upload
  758.      * @param   string $remote_file The absolute or relative path to the file to upload to
  759.      * @param   bool   $overwrite   (optional) Whether to overwrite existing file
  760.      * @param   int    $mode        (optional) Either FTP_ASCII or FTP_BINARY
  761.      * @return  mixed               True on success, otherwise PEAR::Error
  762.      */
  763.     
  764.     function put($local_file$remote_file$overwrite = false$mode = null)
  765.     {
  766.         if (!isset($mode)) {
  767.             $mode $this->checkFileExtension($local_file);
  768.         }
  769.         $remote_file $this->_construct_path($remote_file);
  770.  
  771.         if (!@file_exists($local_file)) {
  772.             return $this->raiseError("Local file '$local_file' does not exist."0);
  773.         }
  774.         if ((@ftp_size($this->_handle$remote_file!= -1&& !$overwrite{
  775.             return $this->raiseError("Remote file '$remote_file' exists and may not be overwriten."0);
  776.         }
  777.  
  778.         if (function_exists('ftp_nb_put')){
  779.             $res @ftp_nb_put($this->_handle$remote_file$local_file$mode);
  780.             while ($res == FTP_MOREDATA{
  781.                 $this->_announce('nb_put');
  782.                 $res @ftp_nb_continue($this->_handle);
  783.             }
  784.  
  785.         else {
  786.             $res @ftp_put($this->_handle$remote_file$local_file$mode);
  787.         }
  788.         if (!$res{
  789.             return $this->raiseError("File '$local_file' could not be uploaded to '$remote_file'."0);
  790.         else {
  791.             return true;
  792.         }
  793.     }
  794.  
  795.     /**
  796.      * This functionality allows you to transfer a whole directory-structure from the
  797.      * remote-ftp to your local host. You have to give a remote-directory (ending with
  798.      * '/') and the local directory (ending with '/') where to put the files you download.
  799.      * The remote path is automatically completed with the current-remote-dir, if you give
  800.      * a relative path to this function. You can give a relative path for the $local_path,
  801.      * too. Then the script-basedir will be used for comletion of the path.
  802.      * The parameter $overwrite will determine, whether to overwrite existing files or not.
  803.      * Standard for this is false. Fourth you can explicitly set a mode for all transfer-
  804.      * actions done. If you do not set this, the method tries to determine the transfer-
  805.      * mode by checking your mode-directory for the file-extension. If the extension is not
  806.      * inside the mode-directory, it will get your default-mode.
  807.      *
  808.      * @access  public
  809.      * @param   string $remote_path The path to download
  810.      * @param   string $local_path  The path to download to
  811.      * @param   bool   $overwrite   (optional) Whether to overwrite existing files (true) or not (false, standard).
  812.      * @param   int    $mode        (optional) The transfermode (either FTP_ASCII or FTP_BINARY).
  813.      * @return  mixed               True on succes, otherwise PEAR::Error
  814.      */
  815.     
  816.     function getRecursive($remote_path$local_path$overwrite = false$mode = null)
  817.     {
  818.         $remote_path $this->_construct_path($remote_path);
  819.         if (!$this->_check_dir($remote_path)) {
  820.             return $this->raiseError("Given remote-path '$remote_path' seems not to be a directory."0);
  821.         }
  822.         if (!$this->_check_dir($local_path)) {
  823.             return $this->raiseError("Given local-path '$local_path' seems not to be a directory."0);
  824.         }
  825.  
  826.         if (!@is_dir($local_path)) {
  827.             $res @mkdir($local_path);
  828.             if (!$res{
  829.                 return $this->raiseError("Could not create dir '$local_path'"0);
  830.             }
  831.         }
  832.         $dir_list = array();
  833.         $dir_list $this->ls($remote_pathNET_FTP_DIRS_ONLY);
  834.         foreach ($dir_list as $dir_entry{
  835.             $remote_path_new $remote_path.$dir_entry["name"]."/";
  836.             $local_path_new $local_path.$dir_entry["name"]."/";
  837.             $result $this->getRecursive($remote_path_new$local_path_new$overwrite$mode);
  838.             if ($this->isError($result)) {
  839.                 return $result;
  840.             }
  841.         }
  842.         $file_list = array();
  843.         $file_list $this->ls($remote_pathNET_FTP_FILES_ONLY);
  844.         foreach ($file_list as $file_entry{
  845.             $remote_file $remote_path.$file_entry["name"];
  846.             $local_file $local_path.$file_entry["name"];
  847.             $result $this->get($remote_file$local_file$overwrite$mode);
  848.             if ($this->isError($result)) {
  849.                 return $result;
  850.             }
  851.         }
  852.         return true;
  853.     }
  854.  
  855.     /**
  856.      * This functionality allows you to transfer a whole directory-structure from your
  857.      * local host to the remote-ftp. You have to give a remote-directory (ending with
  858.      * '/') and the local directory (ending with '/') where to put the files you download.
  859.      * The remote path is automatically completed with the current-remote-dir, if you give
  860.      * a relative path to this function. You can give a relative path for the $local_path,
  861.      * too. Then the script-basedir will be used for comletion of the path.
  862.      * The parameter $overwrite will determine, whether to overwrite existing files or not.
  863.      * Standard for this is false. Fourth you can explicitly set a mode for all transfer-
  864.      * actions done. If you do not set this, the method tries to determine the transfer-
  865.      * mode by checking your mode-directory for the file-extension. If the extension is not
  866.      * inside the mode-directory, it will get your default-mode.
  867.      *
  868.      * @access  public
  869.      * @param   string $remote_path The path to download
  870.      * @param   string $local_path  The path to download to
  871.      * @param   bool   $overwrite   (optional) Whether to overwrite existing files (true) or not (false, standard).
  872.      * @param   int    $mode        (optional) The transfermode (either FTP_ASCII or FTP_BINARY).
  873.      * @return  mixed               True on succes, otherwise PEAR::Error
  874.      */
  875.     
  876.     function putRecursive($local_path$remote_path$overwrite = false$mode = null)
  877.     {
  878.         $remote_path $this->_construct_path($remote_path);
  879.         if (!$this->_check_dir($local_path|| !is_dir($local_path)) {
  880.             return $this->raiseError("Given local-path '$local_path' seems not to be a directory."0);
  881.         }
  882.         if (!$this->_check_dir($remote_path)) {
  883.             return $this->raiseError("Given remote-path '$remote_path' seems not to be a directory."0);
  884.         }
  885.         $old_path $this->pwd();
  886.         if ($this->isError($this->cd($remote_path))) {
  887.             $res $this->mkdir($remote_path);
  888.             if ($this->isError($res)) {
  889.                 return $res;
  890.             }
  891.         }
  892.         $this->cd($old_path);
  893.         $dir_list $this->_ls_local($local_path);
  894.         foreach ($dir_list["dirs"as $dir_entry{
  895.             $remote_path_new $remote_path.$dir_entry."/";
  896.             $local_path_new $local_path.$dir_entry."/";
  897.             $result $this->putRecursive($local_path_new$remote_path_new$overwrite$mode);
  898.             if ($this->isError($result)) {
  899.                 return $result;
  900.             }
  901.         }
  902.  
  903.         foreach ($dir_list["files"as $file_entry{
  904.             $remote_file $remote_path.$file_entry;
  905.             $local_file $local_path.$file_entry;
  906.             $result $this->put($local_file$remote_file$overwrite$mode);
  907.             /*if ($this->isError($result)) {
  908.                 return $result;
  909.             }*/
  910.         }
  911.         return true;
  912.     }
  913.  
  914.     /**
  915.      * This checks, whether a file should be transfered in ascii- or binary-mode
  916.      * by it's file-extension. If the file-extension is not set or
  917.      * the extension is not inside one of the extension-dirs, the actual set
  918.      * transfer-mode is returned.
  919.      *
  920.      * @access  public
  921.      * @param   string $filename  The filename to be checked
  922.      * @return  int               Either FTP_ASCII or FTP_BINARY
  923.      */
  924.     
  925.     function checkFileExtension($filename)
  926.     {
  927.         $pattern "/\.(.*)$/";
  928.         $has_extension preg_match($pattern$filename$eregs);
  929.         if (!$has_extension{
  930.             return $this->_mode;
  931.         else {
  932.             $ext $eregs[1];
  933.         }
  934.  
  935.         if (!empty($this->_file_extensions[$ext])) {
  936.             return $this->_file_extensions[$ext];
  937.         }
  938.  
  939.         return $this->_mode;
  940.     }
  941.  
  942.     /**
  943.      * Set the Hostname
  944.      *
  945.      * @access  public
  946.      * @param   string $host The Hostname to set
  947.      * @return  bool True on success, otherwise PEAR::Error
  948.      */
  949.     
  950.     function setHostname($host)
  951.     {
  952.         if (!is_string($host)) {
  953.             return PEAR::raiseError("Hostname must be a string."0);
  954.         }
  955.         $this->_hostname $host;
  956.         return true;
  957.     }
  958.  
  959.     /**
  960.      * Set the Port
  961.      *
  962.      * @access  public
  963.      * @param   int $port    The Port to set
  964.      * @return  bool True on success, otherwise PEAR::Error
  965.      */
  966.     
  967.     function setPort($port)
  968.     {
  969.         if (!is_int($port|| ($port < 0)) {
  970.             PEAR::raiseError("Invalid port. Has to be integer >= 0"0);
  971.         }
  972.         $this->_port $port;
  973.         return true;
  974.     }
  975.  
  976.     /**
  977.      * Set the Username
  978.      *
  979.      * @access  public
  980.      * @param   string $user The Username to set
  981.      */
  982.     
  983.     function setUsername($user)
  984.     {
  985.         $this->_username $user;
  986.     }
  987.  
  988.     /**
  989.      * Set the Password
  990.      *
  991.      * @access  private
  992.      * @param   string $password  The Password to set
  993.      */
  994.     
  995.     function setPassword($password)
  996.     {
  997.         $this->_password $password;
  998.     }
  999.  
  1000.     /**
  1001.      * Set the transfer-mode. You can use the predefined constants
  1002.      * FTP_ASCII or FTP_BINARY. The mode will be stored for any further transfers.
  1003.      *
  1004.      * @access  public
  1005.      * @param   int    $mode  The mode to set
  1006.      * @return  mixed         True on success, otherwise PEAR::Error
  1007.      */
  1008.     
  1009.     function setMode($mode)
  1010.     {
  1011.         if (($mode == FTP_ASCII|| ($mode == FTP_BINARY)) {
  1012.             $this->_mode $mode;
  1013.             return true;
  1014.         else {
  1015.             return $this->raiseError('FTP-Mode has either to be FTP_ASCII or FTP_BINARY'1);
  1016.         }
  1017.     }
  1018.  
  1019.     /**
  1020.      * Set the transfer-method to passive mode
  1021.      *
  1022.      * @access  public
  1023.      * @return  void 
  1024.      */
  1025.     
  1026.     function setPassive()
  1027.     {
  1028.         $this->_passv = true;
  1029.         @ftp_pasv($this->_handletrue);
  1030.     }
  1031.  
  1032.     /**
  1033.      * Set the transfer-method to active mode
  1034.      *
  1035.      * @access  public
  1036.      * @return  void 
  1037.      */
  1038.     
  1039.     function setActive()
  1040.     {
  1041.         $this->_passv = false;
  1042.         @ftp_pasv($this->_handlefalse);
  1043.     }
  1044.  
  1045.     /**
  1046.      * Set the timeout for FTP operations
  1047.      * Use this method to set a timeout for FTP operation. Timeout has to be an integer.
  1048.      *
  1049.      * @acess public
  1050.      * @param int $timeout the timeout to use
  1051.      * @return bool True on success, otherwise PEAR::Error
  1052.      */
  1053.      
  1054.     function setTimeout $timeout = 0 
  1055.     {
  1056.         if (!is_int($timeout|| ($timeout < 0)) {
  1057.             return PEAR::raiseError("Timeout $timeout is invalid, has to be an integer >= 0");
  1058.         }
  1059.         $res @ftp_set_option($this->_handleFTP_TIMEOUT_SEC$timeout);
  1060.         if (!$res{
  1061.             return PEAR::raiseError("Set timeout failed.");
  1062.         }
  1063.         return true;
  1064.     }        
  1065.     
  1066.     /**
  1067.      * Adds an extension to a mode-directory
  1068.      * The mode-directory saves file-extensions coresponding to filetypes
  1069.      * (ascii e.g.: 'php', 'txt', 'htm',...; binary e.g.: 'jpg', 'gif', 'exe',...).
  1070.      * The extensions have to be saved without the '.'. And
  1071.      * can be predefined in an external file (see: getExtensionsFile()).
  1072.      *
  1073.      * The array is build like this: 'php' => FTP_ASCII, 'png' => FTP_BINARY
  1074.      *
  1075.      * To change the mode of an extension, just add it again with the new mode!
  1076.      *
  1077.      * @access  public
  1078.      * @param   int    $mode  Either FTP_ASCII or FTP_BINARY
  1079.      * @param   string $ext   Extension
  1080.      * @return  void 
  1081.      */
  1082.     
  1083.     function addExtension($mode$ext)
  1084.     {
  1085.         $this->_file_extensions[$ext$mode;
  1086.     }
  1087.  
  1088.     /**
  1089.      * This function removes an extension from the mode-directories
  1090.      * (described above).
  1091.      *
  1092.      * @access  public
  1093.      * @param   string $ext  The extension to remove
  1094.      * @return  void 
  1095.      */
  1096.     
  1097.     function removeExtension($ext)
  1098.     {
  1099.         unset($this->_file_extensions[$ext]);
  1100.     }
  1101.  
  1102.     /**
  1103.      * This get's both (ascii- and binary-mode-directories) from the given file.
  1104.      * Beware, if you read a file into the mode-directory, all former set values
  1105.      * will be unset!
  1106.      *
  1107.      * @access  public
  1108.      * @param   string $filename  The file to get from
  1109.      * @return  mixed             True on success, otherwise PEAR::Error
  1110.      */
  1111.     
  1112.     function getExtensionsFile($filename)
  1113.     {
  1114.         if (!file_exists($filename)) {
  1115.             return $this->raiseError("Extensions-file '$filename' does not exist"0);
  1116.         }
  1117.  
  1118.         if (!is_readable($filename)) {
  1119.             return $this->raiseError("Extensions-file '$filename' is not readable"0);
  1120.         }
  1121.  
  1122.         $this->_file_extension @parse_ini_file($filename);
  1123.         return true;
  1124.     }
  1125.  
  1126.     /**
  1127.      * Returns the Hostname
  1128.      *
  1129.      * @access  public
  1130.      * @return  string  The Hostname
  1131.      */
  1132.     
  1133.     function getHostname()
  1134.     {
  1135.         return $this->_hostname;
  1136.     }
  1137.  
  1138.     /**
  1139.      * Returns the Port
  1140.      *
  1141.      * @access  public
  1142.      * @return  int     The Port
  1143.      */
  1144.     
  1145.     function getPort()
  1146.     {
  1147.         return $this->_port;
  1148.     }
  1149.  
  1150.     /**
  1151.      * Returns the Username
  1152.      *
  1153.      * @access  public
  1154.      * @return  string  The Username
  1155.      */
  1156.     
  1157.     function getUsername()
  1158.     {
  1159.         return $this->_username;
  1160.     }
  1161.  
  1162.     /**
  1163.      * Returns the Password
  1164.      *
  1165.      * @access  public
  1166.      * @return  string  The Password
  1167.      */
  1168.     
  1169.     function getPassword()
  1170.     {
  1171.         return $this->_password;
  1172.     }
  1173.  
  1174.     /**
  1175.      * Returns the Transfermode
  1176.      *
  1177.      * @access  public
  1178.      * @return  int     The transfermode, either FTP_ASCII or FTP_BINARY.
  1179.      */
  1180.     
  1181.     function getMode()
  1182.     {
  1183.         return $this->_mode;
  1184.     }
  1185.  
  1186.     /**
  1187.      * Returns, whether the connection is set to passive mode or not
  1188.      *
  1189.      * @access  public
  1190.      * @return  bool    True if passive-, false if active-mode
  1191.      */
  1192.     
  1193.     function isPassive()
  1194.     {
  1195.         return $this->_passive;
  1196.     }
  1197.  
  1198.     /**
  1199.      * Returns the mode set for a file-extension
  1200.      *
  1201.      * @access  public
  1202.      * @param   string   $ext    The extension you wanna ask for
  1203.      * @return  int              Either FTP_ASCII, FTP_BINARY or NULL (if not set a mode for it)
  1204.      */
  1205.     
  1206.     function getExtensionMode($ext)
  1207.     {
  1208.         return @$this->_file_extensions[$ext];
  1209.     }
  1210.  
  1211.     /**
  1212.      * Get the currently set timeout.
  1213.      * Returns the actual timeout set.
  1214.      *
  1215.      * @access public
  1216.      * @return int The actual timeout
  1217.      */
  1218.     
  1219.     function getTimeout )
  1220.     {
  1221.         return ftp_get_option($this->_handleFTP_TIMEOUT_SEC);
  1222.     }    
  1223.  
  1224.     /**
  1225.      * Adds a Net_FTP_Observer instance to the list of observers
  1226.      * that are listening for messages emitted by this Net_FTP instance.
  1227.      *
  1228.      * @param   object   $observer     The Net_FTP_Observer instance to attach
  1229.      *                                  as a listener.
  1230.      * @return  boolean                True if the observer is successfully attached.
  1231.      * @access  public
  1232.      * @since   1.3
  1233.      */
  1234.     
  1235.     function attach(&$observer)
  1236.     {
  1237.         if (!is_a($observer'Net_FTP_Observer')) {
  1238.             return false;
  1239.         }
  1240.  
  1241.         $this->_listeners[$observer->getId()&$observer;
  1242.         return true;
  1243.     }
  1244.  
  1245.     /**
  1246.      * Removes a Net_FTP_Observer instance from the list of observers.
  1247.      *
  1248.      * @param   object   $observer     The Net_FTP_Observer instance to detach
  1249.      *                                  from the list of listeners.
  1250.      * @return  boolean                True if the observer is successfully detached.
  1251.      * @access  public
  1252.      * @since   1.3
  1253.      */
  1254.     
  1255.     function detach($observer)
  1256.     {
  1257.         if (!is_a($observer'Net_FTP_Observer'||
  1258.             !isset($this->_listeners[$observer->getId()])) {
  1259.             return false;
  1260.         }
  1261.  
  1262.         unset($this->_listeners[$observer->getId()]);
  1263.         return true;
  1264.     }
  1265.  
  1266.     /**
  1267.      * Informs each registered observer instance that a new message has been
  1268.      * sent.
  1269.      *                                                                      
  1270.      * @param   mixed     $event       A hash describing the net event.
  1271.      * @access  private
  1272.      * @since   1.3
  1273.      */                                                                     
  1274.     
  1275.     function _announce($event)
  1276.     {
  1277.         foreach ($this->_listeners as $id => $listener{
  1278.             $this->_listeners[$id]->notify($event);
  1279.         }
  1280.     }
  1281.     
  1282.         /**
  1283.      * Rebuild the path, if given relative
  1284.      *
  1285.      * @access  private
  1286.      * @param   string $path   The path to check and construct
  1287.      * @return  string         The build path
  1288.      */
  1289.     
  1290.     function _construct_path($path)
  1291.     {
  1292.         if (substr($path01!= "/"{
  1293.             $actual_dir @ftp_pwd($this->_handle);
  1294.             if (substr($actual_dir(strlen($actual_dir- 2)1!= "/"{
  1295.                 $actual_dir .= "/";
  1296.             }
  1297.             $path $actual_dir.$path;
  1298.         }
  1299.         return $path;
  1300.     }
  1301.  
  1302.     /**
  1303.      * Checks, whether a given string is a directory-path (ends with "/") or not.
  1304.      *
  1305.      * @access  private
  1306.      * @param   string $path  Path to check
  1307.      * @return  bool          True if $path is a directory, otherwise false
  1308.      */
  1309.     
  1310.     function _check_dir($path)
  1311.     {
  1312.         if (substr($path(strlen($path- 1)1== "/"{
  1313.             return true;
  1314.         else {
  1315.             return false;
  1316.         }
  1317.     }
  1318.  
  1319.     /**
  1320.      * This will remove a file
  1321.      *
  1322.      * @access  private
  1323.      * @param   string $file   The file to delete
  1324.      * @return  mixed          True on success, otherwise PEAR::Error
  1325.      */
  1326.     
  1327.     function _rm_file($file)
  1328.     {
  1329.         if (substr($file01== "/"{
  1330.             $res @ftp_delete($this->_handle$file);
  1331.         else {
  1332.             $actual_dir @ftp_pwd($this->_handle);
  1333.             if (substr($actual_dir(strlen($actual_dir- 2)1!= "/"{
  1334.                 $actual_dir .= "/";
  1335.             }
  1336.             $file $actual_dir.$file;
  1337.             $res @ftp_delete($this->_handle$file);
  1338.         }
  1339.  
  1340.         if (!$res{
  1341.             return $this->raiseError("Could not delete file '$file'."0);
  1342.         else {
  1343.             return true;
  1344.         }
  1345.     }
  1346.  
  1347.     /**
  1348.      * This will remove a dir
  1349.      *
  1350.      * @access  private
  1351.      * @param   string $dir  The dir to delete
  1352.      * @return  mixed        True on success, otherwise PEAR::Error
  1353.      */
  1354.     
  1355.     function _rm_dir($dir)
  1356.     {
  1357.         if (substr($dir(strlen($dir- 1)1!= "/"{
  1358.             return $this->raiseError("Directory name '$dir' is invalid, has to end with '/'"0);
  1359.         }
  1360.         $res @ftp_rmdir($this->_handle$dir);
  1361.         if (!$res{
  1362.             return $this->raiseError("Could not delete directory '$dir'."0);
  1363.         else {
  1364.             return true;
  1365.         }
  1366.     }
  1367.  
  1368.     /**
  1369.      * This will remove a dir and all subdirs and -files
  1370.      *
  1371.      * @access  private
  1372.      * @param   string $file  The dir to delete recursively
  1373.      * @return  mixed         True on success, otherwise PEAR::Error
  1374.      */
  1375.     
  1376.     function _rm_dir_recursive($dir)
  1377.     {
  1378.         if (substr($dir(strlen($dir- 1)1!= "/"{
  1379.             return $this->raiseError("Directory name '$dir' is invalid, has to end with '/'"0);
  1380.         }
  1381.         $file_list $this->_ls_files($dir);
  1382.         foreach ($file_list as $file{
  1383.             $file $dir.$file["name"];
  1384.             $res $this->rm($file);
  1385.             if ($this->isError($res)) {
  1386.                 return $res;
  1387.             }
  1388.         }
  1389.         $dir_list $this->_ls_dirs($dir);
  1390.         foreach ($dir_list as $new_dir{
  1391.             $new_dir $dir.$new_dir["name"]."/";
  1392.             $res $this->_rm_dir_recursive($new_dir);
  1393.             if ($this->isError($res)) {
  1394.                 return $res;
  1395.             }
  1396.         }
  1397.         $res $this->_rm_dir($dir);
  1398.         if (!$res{
  1399.             return $res;
  1400.         else {
  1401.             return true;
  1402.         }
  1403.     }
  1404.  
  1405.     /**
  1406.      * Lists up files and directories
  1407.      *
  1408.      * @access  private
  1409.      * @param   string $dir  The directory to list up
  1410.      * @return  array        An array of dirs and files
  1411.      */
  1412.     
  1413.     function _ls_both($dir)
  1414.     {
  1415.         $list_splitted $this->_list_and_parse($dir);
  1416.         if (!is_array($list_splitted["files"])) {
  1417.             $list_splitted["files"= array();
  1418.         }
  1419.         if (!is_array($list_splitted["dirs"])) {
  1420.             $list_splitted["dirs"= array();
  1421.         }
  1422.         $res = array();
  1423.         @array_splice($res00$list_splitted["files"]);
  1424.         @array_splice($res00$list_splitted["dirs"]);
  1425.         return $res;
  1426.     }
  1427.  
  1428.     /**
  1429.      * Lists up directories
  1430.      *
  1431.      * @access  private
  1432.      * @param   string $dir  The directory to list up
  1433.      * @return  array        An array of dirs
  1434.      */
  1435.     
  1436.     function _ls_dirs($dir)
  1437.     {
  1438.         $list["dirs"= array();
  1439.         $list $this->_list_and_parse($dir);
  1440.         return $list["dirs"];
  1441.     }
  1442.  
  1443.     /**
  1444.      * Lists up files
  1445.      *
  1446.      * @access  private
  1447.      * @param   string $dir  The directory to list up
  1448.      * @return  array        An array of files
  1449.      */
  1450.     
  1451.     function _ls_files($dir)
  1452.     {
  1453.         $list $this->_list_and_parse($dir);
  1454.         if (!is_array($list["files"])) {
  1455.             $list["files"= array();
  1456.         }
  1457.         return $list["files"];
  1458.     }
  1459.  
  1460.     /**
  1461.      * This lists up the directory-content and parses the items into well-formated arrays
  1462.      * The results of this array are sorted (dirs on top, sorted by name;
  1463.      * files below, sorted by name).
  1464.      *
  1465.      * @access  private
  1466.      * @param   string $dir  The directory to parse
  1467.      * @return  array        Lists of dirs and files
  1468.      */
  1469.     
  1470.     function _list_and_parse($dir)
  1471.     {
  1472.         static $matcher = null;
  1473.         $dirs_list = array();
  1474.         $files_list = array();
  1475.         $dir_list @ftp_rawlist($this->_handle$dir);
  1476.         foreach ($dir_list as $entry{
  1477.             if (!isset($matcher)) {
  1478.                 $matcher $this->_determine_os_match($entry);
  1479.                 if (PEAR::isError($matcher)) {
  1480.                     return $matcher;
  1481.                 }
  1482.             }
  1483.             if (!preg_match($matcher['pattern']$entry$m)) {
  1484.                 continue;
  1485.             }
  1486.             $entry = array();
  1487.             foreach ($matcher['map'as $key=>$val{
  1488.                 $entry[$key$m[$val];
  1489.             }
  1490.             $entry['stamp'$this->_parse_Date($entry['date']);
  1491.  
  1492.             if ($entry['is_dir']{
  1493.                 $dirs_list[$entry;
  1494.             else {
  1495.                 $files_list[$entry;
  1496.             }
  1497.         }
  1498.         @usort($dirs_listarray("Net_FTP""_nat_sort"));
  1499.         @usort($files_listarray("Net_FTP""_nat_sort"));
  1500.         $res["dirs"$dirs_list;
  1501.         $res["files"$files_list;
  1502.         return $res;
  1503.     }
  1504.     
  1505.     /**
  1506.      * Determine server OS
  1507.      * This determines the server OS and returns a valid regex to parse
  1508.      * ls() output.
  1509.      *
  1510.      * @access private
  1511.      * @param string $entry The ls entry to parse
  1512.      * @return mixed An array of 'pattern' and 'map' on success, otherwise PEAR::Error
  1513.      */
  1514.     
  1515.     function _determine_os_match($entry{
  1516.         foreach ($this->_ls_match as $os => $match{
  1517.             if (preg_match($match['pattern']$entry)) {
  1518.                 return $match;
  1519.             }
  1520.         }
  1521.         $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!';
  1522.         return PEAR::raiseError($error);
  1523.     }
  1524.     /**
  1525.      * Lists a local directory
  1526.      *
  1527.      * @access  private
  1528.      * @param   string $dir_path  The dir to list
  1529.      * @return  array             The list of dirs and files
  1530.      */
  1531.     
  1532.     function _ls_local($dir_path)
  1533.     {
  1534.         $dir dir($dir_path);
  1535.         $dir_list = array();
  1536.         $file_list = array();
  1537.         while (false !== ($entry $dir->read())) {
  1538.             if (($entry != '.'&& ($entry != '..')) {
  1539.                 if (is_dir($dir_path.$entry)) {
  1540.                     $dir_list[$entry;
  1541.                 else {
  1542.                     $file_list[$entry;
  1543.                 }
  1544.             }
  1545.         }
  1546.         $dir->close();
  1547.         $res['dirs'$dir_list;
  1548.         $res['files'$file_list;
  1549.         return $res;
  1550.     }
  1551.  
  1552.     /**
  1553.      * Function for use with usort().
  1554.      * Compares the list-array-elements by name.
  1555.      *
  1556.      * @access  private
  1557.      */
  1558.     
  1559.     function _nat_sort($item_1$item_2)
  1560.     {
  1561.         return strnatcmp($item_1['name']$item_2['name']);
  1562.     }
  1563.  
  1564.     /**
  1565.      * Parse dates to timestamps
  1566.      *
  1567.      * @access  private
  1568.      * @param   string $date  Date
  1569.      * @return  int           Timestamp
  1570.      */
  1571.     
  1572.     function _parse_Date($date)
  1573.     {
  1574.         // Sep 10 22:06 => Sep 10, <year> 22:06
  1575.         if (preg_match('/([A-Za-z]+)[ ]+([0-9]+)[ ]+([0-9]+):([0-9]+)/'$date$res)) {
  1576.             $year date('Y');
  1577.             $month $res[1];
  1578.             $day $res[2];
  1579.             $hour $res[3];
  1580.             $minute $res[4];
  1581.             $date = "$month $day$year $hour:$minute";
  1582.         }
  1583.         // 09-10-04 => 09/10/04
  1584.         elseif (preg_match('/^\d\d-\d\d-\d\d/',$date)) {
  1585.             $date str_replace('-','/',$date);
  1586.         }
  1587.         $res strtotime($date);
  1588.         if (!$res{
  1589.             return $this->raiseError('Dateconversion failed.'0);
  1590.         }
  1591.         return $res;
  1592.     }
  1593. }
  1594. ?>

Documentation generated on Mon, 11 Mar 2019 10:16:52 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.