home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / Net / FTP.php < prev    next >
Encoding:
PHP Script  |  2006-04-07  |  69.9 KB  |  2,149 lines

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3.  
  4. /**
  5.  * Net_FTP main file.
  6.  *
  7.  * This file must be included to use the Net_FTP package.
  8.  *
  9.  * PHP versions 4 and 5
  10.  *
  11.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  12.  * that is available through the world-wide-web at the following URI:
  13.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  14.  * the PHP License and are unable to obtain it through the web, please
  15.  * send a note to license@php.net so we can mail you a copy immediately.
  16.  *
  17.  * @category   Networking
  18.  * @package    FTP
  19.  * @author     Tobias Schlitt <toby@php.net>
  20.  * @copyright  1997-2005 The PHP Group
  21.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  22.  * @version    CVS: $Id: FTP.php,v 1.47 2006/02/09 23:12:33 toby Exp $
  23.  * @link       http://pear.php.net/package/Net_FTP
  24.  * @since      File available since Release 0.0.1
  25.  */
  26.  
  27. require_once 'PEAR.php';
  28.  
  29. /**
  30.  * Option to let the ls() method return only files.
  31.  *
  32.  * @since 1.3
  33.  * @name NET_FTP_FILES_ONLY
  34.  * @see Net_FTP::ls()
  35.  */
  36. define('NET_FTP_FILES_ONLY', 0, true);
  37.  
  38. /**
  39.  * Option to let the ls() method return only directories.
  40.  *
  41.  * @since 1.3
  42.  * @name NET_FTP_DIRS_ONLY
  43.  * @see Net_FTP::ls()
  44.  */
  45. define('NET_FTP_DIRS_ONLY', 1, true);
  46.  
  47. /**
  48.  * Option to let the ls() method return directories and files (default).
  49.  *
  50.  * @since 1.3
  51.  * @name NET_FTP_DIRS_FILES
  52.  * @see Net_FTP::ls()
  53.  */
  54. define('NET_FTP_DIRS_FILES', 2, true);
  55.  
  56. /**
  57.  * Option to let the ls() method return the raw directory listing from ftp_rawlist().
  58.  *
  59.  * @since 1.3
  60.  * @name NET_FTP_RAWLIST
  61.  * @see Net_FTP::ls()
  62.  */
  63. define('NET_FTP_RAWLIST', 3, true);
  64.  
  65.  
  66. /**
  67.  * Error code to indicate a failed connection
  68.  * This error code indicates, that the connection you tryed to set up
  69.  * could not be established. Check your connection settings (host & port)!
  70.  *
  71.  * @since 1.3
  72.  * @name NET_FTP_ERR_CONNECT_FAILED
  73.  * @see Net_FTP::connect()
  74.  */
  75. define('NET_FTP_ERR_CONNECT_FAILED', -1);
  76.  
  77. /**
  78.  * Error code to indicate a failed login
  79.  * This error code indicates, that the login to the FTP server failed. Check
  80.  * your user data (username & password).
  81.  *
  82.  * @since 1.3
  83.  * @name NET_FTP_ERR_LOGIN_FAILED
  84.  * @see Net_FTP::login()
  85.  */
  86. define('NET_FTP_ERR_LOGIN_FAILED', -2);
  87.  
  88. /**
  89.  * Error code to indicate a failed directory change
  90.  * The cd() method failed. Ensure that the directory you wanted to access exists.
  91.  *
  92.  * @since 1.3
  93.  * @name NET_FTP_ERR_DIRCHANGE_FAILED
  94.  * @see Net_FTP::cd()
  95.  */
  96. define('NET_FTP_ERR_DIRCHANGE_FAILED', 2); // Compatibillity reasons!
  97.  
  98. /**
  99.  * Error code to indicate that Net_FTP could not determine the current path
  100.  * The cwd() method failed and could not determine the path you currently reside
  101.  * in on the FTP server.
  102.  *
  103.  * @since 1.3
  104.  * @name NET_FTP_ERR_DETERMINEPATH_FAILED
  105.  * @see Net_FTP::pwd()
  106.  */
  107. define('NET_FTP_ERR_DETERMINEPATH_FAILED', 4); // Compatibillity reasons!
  108.  
  109. /**
  110.  * Error code to indicate that the creation of a directory failed
  111.  * The directory you tryed to create could not be created. Check the
  112.  * access rights on the parent directory!
  113.  *
  114.  * @since 1.3
  115.  * @name NET_FTP_ERR_CREATEDIR_FAILED
  116.  * @see Net_FTP::mkdir()
  117.  */
  118. define('NET_FTP_ERR_CREATEDIR_FAILED', -4);
  119.  
  120. /**
  121.  * Error code to indicate that the EXEC execution failed.
  122.  * The execution of a command using EXEC failed. Ensure, that your
  123.  * FTP server supports the EXEC command.
  124.  *
  125.  * @since 1.3
  126.  * @name NET_FTP_ERR_EXEC_FAILED
  127.  * @see Net_FTP::execute()
  128.  */
  129. define('NET_FTP_ERR_EXEC_FAILED', -5);
  130.  
  131. /**
  132.  * Error code to indicate that the SITE command failed.
  133.  * The execution of a command using SITE failed. Ensure, that your
  134.  * FTP server supports the SITE command.
  135.  *
  136.  * @since 1.3
  137.  * @name NET_FTP_ERR_SITE_FAILED
  138.  * @see Net_FTP::site()
  139.  */
  140. define('NET_FTP_ERR_SITE_FAILED', -6);
  141.  
  142. /**
  143.  * Error code to indicate that the CHMOD command failed.
  144.  * The execution of CHMOD failed. Ensure, that your
  145.  * FTP server supports the CHMOD command and that you have the appropriate
  146.  * access rights to use CHMOD.
  147.  *
  148.  * @since 1.3
  149.  * @name NET_FTP_ERR_CHMOD_FAILED
  150.  * @see Net_FTP::chmod()
  151.  */
  152. define('NET_FTP_ERR_CHMOD_FAILED', -7);
  153.  
  154. /**
  155.  * Error code to indicate that a file rename failed
  156.  * The renaming of a file on the server failed. Ensure that you have the
  157.  * appropriate access rights to rename the file.
  158.  *
  159.  * @since 1.3
  160.  * @name NET_FTP_ERR_RENAME_FAILED
  161.  * @see Net_FTP::rename()
  162.  */
  163. define('NET_FTP_ERR_RENAME_FAILED', -8);
  164.  
  165. /**
  166.  * Error code to indicate that the MDTM command failed
  167.  * The MDTM command is not supported for directories. Ensure that you gave
  168.  * a file path to the mdtm() method, not a directory path.
  169.  *
  170.  * @since 1.3
  171.  * @name NET_FTP_ERR_MDTMDIR_UNSUPPORTED
  172.  * @see Net_FTP::mdtm()
  173.  */
  174. define('NET_FTP_ERR_MDTMDIR_UNSUPPORTED', -9);
  175.  
  176. /**
  177.  * Error code to indicate that the MDTM command failed
  178.  * The MDTM command failed. Ensure that your server supports the MDTM command.
  179.  *
  180.  * @since 1.3
  181.  * @name NET_FTP_ERR_MDTM_FAILED
  182.  * @see Net_FTP::mdtm()
  183.  */
  184. define('NET_FTP_ERR_MDTM_FAILED', -10);
  185.  
  186. /**
  187.  * Error code to indicate that a date returned by the server was misformated
  188.  * A date string returned by your server seems to be missformated and could not be
  189.  * parsed. Check that the server is configured correctly. If you're sure, please
  190.  * send an email to the auhtor with a dumped output of $ftp->ls('./', NET_FTP_RAWLIST);
  191.  * to get the date format supported.
  192.  *
  193.  * @since 1.3
  194.  * @name NET_FTP_ERR_DATEFORMAT_FAILED
  195.  * @see Net_FTP::mdtm(), Net_FTP::ls()
  196.  */
  197. define('NET_FTP_ERR_DATEFORMAT_FAILED', -11);
  198.  
  199. /**
  200.  * Error code to indicate that the SIZE command failed
  201.  * The determination of the filesize of a file failed. Ensure that your server supports the
  202.  * SIZE command.
  203.  *
  204.  * @since 1.3
  205.  * @name NET_FTP_ERR_SIZE_FAILED
  206.  * @see Net_FTP::size()
  207.  */
  208. define('NET_FTP_ERR_SIZE_FAILED', -12);
  209.  
  210. /**
  211.  * Error code to indicate that a local file could not be overwritten
  212.  * You specified not to overwrite files. Therefore the local file has not been
  213.  * overwriten. If you want to get the file overwriten, please set the option to
  214.  * do so.
  215.  *
  216.  * @since 1.3
  217.  * @name NET_FTP_ERR_OVERWRITELOCALFILE_FORBIDDEN
  218.  * @see Net_FTP::get(), Net_FTP::getRecursive()
  219.  */
  220. define('NET_FTP_ERR_OVERWRITELOCALFILE_FORBIDDEN', -13);
  221.  
  222. /**
  223.  * Error code to indicate that a local file could not be overwritten
  224.  * Also you specified to overwrite the local file you want to download to,
  225.  * it has not been possible to do so. Check that you have the appropriate access
  226.  * rights on the local file to overwrite it.
  227.  *
  228.  * @since 1.3
  229.  * @name NET_FTP_ERR_OVERWRITELOCALFILE_FAILED
  230.  * @see Net_FTP::get(), Net_FTP::getRecursive()
  231.  */
  232. define('NET_FTP_ERR_OVERWRITELOCALFILE_FAILED', -14);
  233.  
  234. /**
  235.  * Error code to indicate that the file you wanted to upload does not exist
  236.  * The file you tried to upload does not exist. Ensure that it exists.
  237.  *
  238.  * @since 1.3
  239.  * @name NET_FTP_ERR_LOCALFILENOTEXIST
  240.  * @see Net_FTP::put(), Net_FTP::putRecursive()
  241.  */
  242. define('NET_FTP_ERR_LOCALFILENOTEXIST', -15);
  243.  
  244. /**
  245.  * Error code to indicate that a remote file could not be overwritten
  246.  * You specified not to overwrite files. Therefore the remote file has not been
  247.  * overwriten. If you want to get the file overwriten, please set the option to
  248.  * do so.
  249.  *
  250.  * @since 1.3
  251.  * @name NET_FTP_ERR_OVERWRITEREMOTEFILE_FORBIDDEN
  252.  * @see Net_FTP::put(), Net_FTP::putRecursive()
  253.  */
  254. define('NET_FTP_ERR_OVERWRITEREMOTEFILE_FORBIDDEN', -16);
  255.  
  256. /**
  257.  * Error code to indicate that the upload of a file failed
  258.  * The upload you tried failed. Ensure that you have appropriate access rights
  259.  * to upload the desired file.
  260.  *
  261.  * @since 1.3
  262.  * @name NET_FTP_ERR_UPLOADFILE_FAILED
  263.  * @see Net_FTP::put(), Net_FTP::putRecursive()
  264.  */
  265. define('NET_FTP_ERR_UPLOADFILE_FAILED', -17);
  266.  
  267. /**
  268.  * Error code to indicate that you specified an incorrect directory path
  269.  * The remote path you specified seems not to be a directory. Ensure that
  270.  * the path you specify is a directory and that the path string ends with
  271.  * a /.
  272.  *
  273.  * @since 1.3
  274.  * @name NET_FTP_ERR_REMOTEPATHNODIR
  275.  * @see Net_FTP::putRecursive(), Net_FTP::getRecursive()
  276.  */
  277. define('NET_FTP_ERR_REMOTEPATHNODIR', -18);
  278.  
  279. /**
  280.  * Error code to indicate that you specified an incorrect directory path
  281.  * The local path you specified seems not to be a directory. Ensure that
  282.  * the path you specify is a directory and that the path string ends with
  283.  * a /.
  284.  *
  285.  * @since 1.3
  286.  * @name NET_FTP_ERR_LOCALPATHNODIR
  287.  * @see Net_FTP::putRecursive(), Net_FTP::getRecursive()
  288.  */
  289. define('NET_FTP_ERR_LOCALPATHNODIR', -19);
  290.  
  291. /**
  292.  * Error code to indicate that a local directory failed to be created
  293.  * You tried to create a local directory through getRecursive() method,
  294.  * which has failed. Ensure that you have the appropriate access rights
  295.  * to create it.
  296.  *
  297.  * @since 1.3
  298.  * @name NET_FTP_ERR_CREATELOCALDIR_FAILED
  299.  * @see Net_FTP::getRecursive()
  300.  */
  301. define('NET_FTP_ERR_CREATELOCALDIR_FAILED', -20);
  302.  
  303. /**
  304.  * Error code to indicate that the provided hostname was incorrect
  305.  * The hostname you provided was invalid. Ensure to provide either a
  306.  * full qualified domain name or an IP address.
  307.  *
  308.  * @since 1.3
  309.  * @name NET_FTP_ERR_HOSTNAMENOSTRING
  310.  * @see Net_FTP::setHostname()
  311.  */
  312. define('NET_FTP_ERR_HOSTNAMENOSTRING', -21);
  313.  
  314. /**
  315.  * Error code to indicate that the provided port was incorrect
  316.  * The port number you provided was invalid. Ensure to provide either a
  317.  * a numeric port number greater zero.
  318.  *
  319.  * @since 1.3
  320.  * @name NET_FTP_ERR_PORTLESSZERO
  321.  * @see Net_FTP::setPort()
  322.  */
  323. define('NET_FTP_ERR_PORTLESSZERO', -22);
  324.  
  325. /**
  326.  * Error code to indicate that you provided an invalid mode constant
  327.  * The mode constant you provided was invalid. You may only provide
  328.  * FTP_ASCII or FTP_BINARY.
  329.  *
  330.  * @since 1.3
  331.  * @name NET_FTP_ERR_NOMODECONST
  332.  * @see Net_FTP::setMode()
  333.  */
  334. define('NET_FTP_ERR_NOMODECONST', -23);
  335.  
  336. /**
  337.  * Error code to indicate that you provided an invalid timeout
  338.  * The timeout you provided was invalid. You have to provide a timeout greater
  339.  * or equal to zero.
  340.  *
  341.  * @since 1.3
  342.  * @name NET_FTP_ERR_TIMEOUTLESSZERO
  343.  * @see Net_FTP::Net_FTP(), Net_FTP::setTimeout()
  344.  */
  345. define('NET_FTP_ERR_TIMEOUTLESSZERO', -24);
  346.  
  347. /**
  348.  * Error code to indicate that you provided an invalid timeout
  349.  * An error occured while setting the timeout. Ensure that you provide a
  350.  * valid integer for the timeount and that your PHP installation works
  351.  * correctly.
  352.  *
  353.  * @since 1.3
  354.  * @name NET_FTP_ERR_SETTIMEOUT_FAILED
  355.  * @see Net_FTP::Net_FTP(), Net_FTP::setTimeout()
  356.  */
  357. define('NET_FTP_ERR_SETTIMEOUT_FAILED', -25);
  358.  
  359. /**
  360.  * Error code to indicate that the provided extension file doesn't exist
  361.  * The provided extension file does not exist. Ensure to provided an
  362.  * existant extension file.
  363.  *
  364.  * @since 1.3
  365.  * @name NET_FTP_ERR_EXTFILENOTEXIST
  366.  * @see Net_FTP::getExtensionFile()
  367.  */
  368. define('NET_FTP_ERR_EXTFILENOTEXIST', -26);
  369.  
  370. /**
  371.  * Error code to indicate that the provided extension file is not readable
  372.  * The provided extension file is not readable. Ensure to have sufficient
  373.  * access rights for it.
  374.  *
  375.  * @since 1.3
  376.  * @name NET_FTP_ERR_EXTFILEREAD_FAILED
  377.  * @see Net_FTP::getExtensionFile()
  378.  */
  379. define('NET_FTP_ERR_EXTFILEREAD_FAILED', -27);
  380.  
  381. /**
  382.  * Error code to indicate that the deletion of a file failed
  383.  * The specified file could not be deleted. Ensure to have sufficient
  384.  * access rights to delete the file.
  385.  *
  386.  * @since 1.3
  387.  * @name NET_FTP_ERR_EXTFILEREAD_FAILED
  388.  * @see Net_FTP::rm()
  389.  */
  390. define('NET_FTP_ERR_DELETEFILE_FAILED', -28);
  391.  
  392. /**
  393.  * Error code to indicate that the deletion of a directory faild
  394.  * The specified file could not be deleted. Ensure to have sufficient
  395.  * access rights to delete the file.
  396.  *
  397.  * @since 1.3
  398.  * @name NET_FTP_ERR_EXTFILEREAD_FAILED
  399.  * @see Net_FTP::rm()
  400.  */
  401. define('NET_FTP_ERR_DELETEDIR_FAILED', -29);
  402.  
  403. /**
  404.  * Error code to indicate that the directory listing failed
  405.  * PHP could not list the directory contents on the server. Ensure
  406.  * that your server is configured appropriate.
  407.  *
  408.  * @since 1.3
  409.  * @name NET_FTP_ERR_RAWDIRLIST_FAILED
  410.  * @see Net_FTP::ls()
  411.  */
  412. define('NET_FTP_ERR_RAWDIRLIST_FAILED', -30);
  413.  
  414. /**
  415.  * Error code to indicate that the directory listing failed
  416.  * The directory listing format your server uses seems not to
  417.  * be supported by Net_FTP. Please send the output of the
  418.  * call ls('./', NET_FTP_RAWLIST); to the author of this
  419.  * class to get it supported.
  420.  *
  421.  * @since 1.3
  422.  * @name NET_FTP_ERR_DIRLIST_UNSUPPORTED
  423.  * @see Net_FTP::ls()
  424.  */
  425. define('NET_FTP_ERR_DIRLIST_UNSUPPORTED', -31);
  426.  
  427. /**
  428.  * Error code to indicate failed disconnecting
  429.  * This error code indicates, that disconnection was not possible.
  430.  *
  431.  * @since 1.3
  432.  * @name NET_FTP_ERR_DISCONNECT_FAILED
  433.  * @see Net_FTP::disconnect()
  434.  */
  435. define('NET_FTP_ERR_DISCONNECT_FAILED', -32);
  436.  
  437. /**
  438.  * Error code to indicate that the username you provided was invalid.
  439.  * Check that you provided a non-empty string as the username.
  440.  *
  441.  * @since 1.3
  442.  * @name NET_FTP_ERR_USERNAMENOSTRING
  443.  * @see Net_FTP::setUsername()
  444.  */
  445. define('NET_FTP_ERR_USERNAMENOSTRING', -33);
  446.  
  447. /**
  448.  * Error code to indicate that the username you provided was invalid.
  449.  * Check that you provided a non-empty string as the username.
  450.  *
  451.  * @since 1.3
  452.  * @name NET_FTP_ERR_PASSWORDNOSTRING
  453.  * @see Net_FTP::setPassword()
  454.  */
  455. define('NET_FTP_ERR_PASSWORDNOSTRING', -33);
  456.  
  457. /**
  458.  * Class for comfortable FTP-communication
  459.  *
  460.  * This class provides comfortable communication with FTP-servers. You may do everything
  461.  * enabled by the PHP-FTP-extension and further functionalities, like recursive-deletion,
  462.  * -up- and -download. Another feature is to create directories recursively.
  463.  *
  464.  * @license   http://www.php.net/license/3_0.txt  PHP License 3.0
  465.  * @category  Networking
  466.  * @package   FTP
  467.  * @author    Tobias Schlitt <toby@php.net>
  468.  * @copyright 1997-2005 The PHP Group
  469.  * @version   Release: @package_version@
  470.  * @link      http://pear.php.net/package/Net_FTP
  471.  * @since     0.0.1
  472.  * @access    public
  473.  */
  474. class Net_FTP extends PEAR 
  475. {
  476.     /**
  477.      * The host to connect to
  478.      *
  479.      * @access  private
  480.      * @var     string
  481.      */
  482.     
  483.     var $_hostname;
  484.  
  485.     /**
  486.      * The port for ftp-connection (standard is 21)
  487.      *
  488.      * @access  private
  489.      * @var     int
  490.      */
  491.     
  492.     var $_port = 21;
  493.  
  494.     /**
  495.      * The username for login
  496.      *
  497.      * @access  private
  498.      * @var     string
  499.      */
  500.     
  501.     var $_username;
  502.  
  503.     /**
  504.      * The password for login
  505.      *
  506.      * @access  private
  507.      * @var     string
  508.      */
  509.     
  510.     var $_password;
  511.  
  512.     /**
  513.      * Determine whether to use passive-mode (true) or active-mode (false)
  514.      *
  515.      * @access  private
  516.      * @var     bool
  517.      */
  518.     
  519.     var $_passv;
  520.  
  521.     /**
  522.      * The standard mode for ftp-transfer
  523.      *
  524.      * @access  private
  525.      * @var     int
  526.      */
  527.     
  528.     var $_mode = FTP_BINARY;
  529.  
  530.     /**
  531.      * This holds the handle for the ftp-connection
  532.      *
  533.      * @access  private
  534.      * @var     resource
  535.      */
  536.     
  537.     var $_handle;
  538.  
  539.     /**
  540.      * Contains the timeout for FTP operations
  541.      *
  542.      * @access  private
  543.      * @var     int
  544.      * @since   1.3
  545.      */
  546.     
  547.     var $_timeout = 90;
  548.         
  549.     /**
  550.      * Saves file-extensions for ascii- and binary-mode
  551.      *
  552.      * The array contains 2 sub-arrays ("ascii" and "binary"), which both contain
  553.      * file-extensions without the "." (".php" = "php").
  554.      *
  555.      * @access  private
  556.      * @var     array
  557.      */
  558.     
  559.     var $_file_extensions;
  560.  
  561.     /**
  562.      * ls match
  563.      * Matches the ls entries against a regex and maps the resulting array to speaking names
  564.      *
  565.      * @access  private
  566.      * @var     array
  567.      * @since   1.3
  568.      */
  569.     
  570.     var $_ls_match = array(
  571.         'unix'    => array(
  572.             'pattern' => '/(?:(d)|.)([rwxt-]+)\s+(\w+)\s+([\w\d-]+)\s+([\w\d-]+)\s+(\w+)\s+(\S+\s+\S+\s+\S+)\s+(.+)/',
  573.             'map'     => array(
  574.                 'is_dir'        => 1,
  575.                 'rights'        => 2,
  576.                 'files_inside'  => 3,
  577.                 'user'          => 4,
  578.                 'group'         => 5,
  579.                 'size'          => 6,
  580.                 'date'          => 7,
  581.                 'name'          => 8,
  582.             )
  583.         ),
  584.         'windows' => array(
  585.             'pattern' => '/(.+)\s+(.+)\s+((<DIR>)|[0-9]+)\s+(.+)/',
  586.             'map'     => array(
  587.                 'name'   => 5,
  588.                 'date'   => 1,
  589.                 'size'   => 3,
  590.                 'is_dir' => 4,
  591.             )
  592.         )
  593.     );
  594.     
  595.     /**
  596.      * matcher
  597.      * Stores the matcher for the current connection
  598.      *
  599.      * @access  private
  600.      * @var     array
  601.      * @since   1.3
  602.      */
  603.     var $_matcher = null;
  604.     
  605.     /**
  606.      * Holds all Net_FTP_Observer objects 
  607.      * that wish to be notified of new messages.
  608.      *
  609.      * @var     array
  610.      * @access  private
  611.      * @since   1.3
  612.      */
  613.     
  614.     var $_listeners = array();
  615.  
  616.     /**
  617.      * This generates a new FTP-Object. The FTP-connection will not be established, yet.
  618.      * You can leave $host and $port blank, if you want. The $host will not be set
  619.      * and the $port will be left at 21. You have to set the $host manualy before
  620.      * trying to connect or with the connect() method.
  621.      *
  622.      * @access  public
  623.      * @param   string $host    (optional) The hostname 
  624.      * @param   int    $port    (optional) The port
  625.      * @param   int    $timeout (optional) Sets the standard timeout
  626.      * @return  void
  627.      * @see     Net_FTP::setHostname(), Net_FTP::setPort(), Net_FTP::connect()
  628.      */
  629.     
  630.     function Net_FTP($host = null, $port = null, $timeout = 90)
  631.     {
  632.         $this->PEAR();
  633.         if (isset($host)) {
  634.             $this->setHostname($host);
  635.         }
  636.         if (isset($port)) {
  637.             $this->setPort($port);
  638.         }
  639.         $this->_timeout = $timeout;
  640.         $this->_file_extensions[FTP_ASCII] = array();
  641.         $this->_file_extensions[FTP_BINARY] = array();
  642.     }
  643.  
  644.     /**
  645.      * This function generates the FTP-connection. You can optionally define a
  646.      * hostname and/or a port. If you do so, this data is stored inside the object.
  647.      *
  648.      * @access  public
  649.      * @param   string $host    (optional) The Hostname
  650.      * @param   int    $port    (optional) The Port 
  651.      * @return  mixed           True on success, otherwise PEAR::Error
  652.      * @see     NET_FTP_ERR_CONNECT_FAILED
  653.      */
  654.     
  655.     function connect($host = null, $port = null)
  656.     {
  657.         $this->_matcher = null;
  658.         if (isset($host)) {
  659.             $this->setHostname($host);
  660.         }
  661.         if (isset($port)) {
  662.             $this->setPort($port);
  663.         }
  664.         $handle = @ftp_connect($this->getHostname(), $this->getPort(), $this->_timeout);
  665.         if (!$handle) {
  666.             return $this->raiseError("Connection to host failed", NET_FTP_ERR_CONNECT_FAILED);
  667.         } else {
  668.             $this->_handle =& $handle;
  669.             return true;
  670.         }
  671.     }
  672.  
  673.     /**
  674.      * This function close the FTP-connection
  675.      *
  676.      * @access  public
  677.      * @return  bool|PEAR_Error Returns true on success, PEAR_Error on failure
  678.      */
  679.     
  680.     function disconnect()
  681.     {
  682.         $res = @ftp_close($this->_handle);
  683.         if (!$res) {
  684.             return PEAR::raiseError('Disconnect failed.', NET_FTP_ERR_DISCONNECT_FAILED);
  685.         }
  686.         return true;
  687.     }
  688.  
  689.     /**
  690.      * This logges you into the ftp-server. You are free to specify username and password
  691.      * in this method. If you specify it, the values will be taken into the corresponding
  692.      * attributes, if do not specify, the attributes are taken.
  693.      *
  694.      * @access  public
  695.      * @param   string $username  (optional) The username to use 
  696.      * @param   string $password  (optional) The password to use
  697.      * @return  mixed             True on success, otherwise PEAR::Error
  698.      * @see     NET_FTP_ERR_LOGIN_FAILED
  699.      */
  700.     
  701.     function login($username = null, $password = null)
  702.     {
  703.         if (!isset($username)) {
  704.             $username = $this->getUsername();
  705.         } else {
  706.             $this->setUsername($username);
  707.         }
  708.  
  709.         if (!isset($password)) {
  710.             $password = $this->getPassword();
  711.         } else {
  712.             $this->setPassword($password);
  713.         }
  714.  
  715.         $res = @ftp_login($this->_handle, $username, $password);
  716.  
  717.         if (!$res) {
  718.             return $this->raiseError("Unable to login", NET_FTP_ERR_LOGIN_FAILED);
  719.         } else {
  720.             return true;
  721.         }
  722.     }
  723.  
  724.     /**
  725.      * This changes the currently used directory. You can use either an absolute
  726.      * directory-path (e.g. "/home/blah") or a relative one (e.g. "../test").
  727.      *
  728.      * @access  public
  729.      * @param   string $dir  The directory to go to.
  730.      * @return  mixed        True on success, otherwise PEAR::Error
  731.      * @see     NET_FTP_ERR_DIRCHANGE_FAILED
  732.      */
  733.     
  734.     function cd($dir)
  735.     {
  736.         $erg = @ftp_chdir($this->_handle, $dir);
  737.         if (!$erg) {
  738.             return $this->raiseError("Directory change failed", NET_FTP_ERR_DIRCHANGE_FAILED);
  739.         } else {
  740.             return true;
  741.         }
  742.     }
  743.  
  744.     /**
  745.      * Show's you the actual path on the server
  746.      * This function questions the ftp-handle for the actual selected path and returns it.
  747.      *
  748.      * @access  public
  749.      * @return  mixed        The actual path or PEAR::Error
  750.      * @see     NET_FTP_ERR_DETERMINEPATH_FAILED
  751.      */
  752.     
  753.     function pwd()
  754.     {
  755.         $res = @ftp_pwd($this->_handle);
  756.         if (!$res) {
  757.             return $this->raiseError("Could not determine the actual path.", NET_FTP_ERR_DETERMINEPATH_FAILED);
  758.         } else {
  759.             return $res;
  760.         }
  761.     }
  762.  
  763.     /**
  764.      * This works similar to the mkdir-command on your local machine. You can either give
  765.      * it an absolute or relative path. The relative path will be completed with the actual
  766.      * selected server-path. (see: pwd())
  767.      *
  768.      * @access  public
  769.      * @param   string $dir       Absolute or relative dir-path
  770.      * @param   bool   $recursive (optional) Create all needed directories
  771.      * @return  mixed             True on success, otherwise PEAR::Error
  772.      * @see     NET_FTP_ERR_CREATEDIR_FAILED
  773.      */
  774.     
  775.     function mkdir($dir, $recursive = false)
  776.     {
  777.         $dir = $this->_construct_path($dir);
  778.         $savedir = $this->pwd();
  779.         $this->pushErrorHandling(PEAR_ERROR_RETURN);
  780.         $e = $this->cd($dir);
  781.         $this->popErrorHandling();
  782.         if ($e === true) {
  783.             $this->cd($savedir);
  784.             return true;
  785.         }
  786.         $this->cd($savedir);
  787.         if ($recursive === false){
  788.             $res = @ftp_mkdir($this->_handle, $dir);
  789.             if (!$res) {
  790.                 return $this->raiseError("Creation of '$dir' failed", NET_FTP_ERR_CREATEDIR_FAILED);
  791.             } else {
  792.                 return true;
  793.             }
  794.         } else {
  795.             if(strpos($dir, '/') === false) {
  796.                 return $this->mkdir($dir,false);
  797.             }
  798.             $pos = 0;
  799.             $res = $this->mkdir(dirname($dir), true);
  800.             $res = $this->mkdir($dir, false);
  801.             if ($res !== true) {
  802.                 return $res;
  803.             }
  804.             return true;
  805.         }
  806.     }
  807.  
  808.     /**
  809.      * This method tries executing a command on the ftp, using SITE EXEC.
  810.      *
  811.      * @access  public
  812.      * @param   string $command The command to execute
  813.      * @return  mixed           The result of the command (if successfull), otherwise PEAR::Error
  814.      * @see     NET_FTP_ERR_EXEC_FAILED
  815.      */
  816.     
  817.     function execute($command)
  818.     {
  819.         $res = @ftp_exec($this->_handle, $command);
  820.         if (!$res) {
  821.             return $this->raiseError("Execution of command '$command' failed.", NET_FTP_ERR_EXEC_FAILED);
  822.         } else {
  823.             return $res;
  824.         }
  825.     }
  826.  
  827.     /**
  828.      * Execute a SITE command on the server
  829.      * This method tries to execute a SITE command on the ftp server.
  830.      *
  831.      * @access  public
  832.      * @param   string $command   The command with parameters to execute
  833.      * @return  mixed             True if successful, otherwise PEAR::Error
  834.      * @see     NET_FTP_ERR_SITE_FAILED
  835.      */
  836.     
  837.     function site($command)
  838.     {
  839.         $res = @ftp_site($this->_handle, $command);
  840.         if (!$res) {
  841.             return $this->raiseError("Execution of SITE command '$command' failed.", NET_FTP_ERR_SITE_FAILED);
  842.         } else {
  843.             return $res;
  844.         }
  845.     }
  846.  
  847.     /**
  848.      * This method will try to chmod the file specified on the server
  849.      * Currently, you must give a number as the the permission argument (777 or
  850.      * similar). The file can be either a relative or absolute path.
  851.      * NOTE: Some servers do not support this feature. In that case, you will
  852.      * get a PEAR error object returned. If successful, the method returns true
  853.      *
  854.      * @access  public
  855.      * @param   mixed   $target        The file or array of files to set permissions for
  856.      * @param   integer $permissions   The mode to set the file permissions to
  857.      * @return  mixed                  True if successful, otherwise PEAR::Error
  858.      * @see     NET_FTP_ERR_CHMOD_FAILED
  859.      */
  860.     
  861.     function chmod($target, $permissions)
  862.     {
  863.         // If $target is an array: Loop through it.
  864.         if (is_array($target)) {
  865.  
  866.             for ($i = 0; $i < count($target); $i++) {
  867.                 $res = $this->chmod($target[$i], $permissions);
  868.                 if (PEAR::isError($res)) {
  869.                     return $res;
  870.                 } // end if isError
  871.             } // end for i < count($target)
  872.  
  873.         } else {
  874.  
  875.             $res = $this->site("CHMOD " . $permissions . " " . $target);
  876.             if (!$res) {
  877.                 return PEAR::raiseError("CHMOD " . $permissions . " " . $target . " failed", NET_FTP_ERR_CHMOD_FAILED);
  878.             } else {
  879.                 return $res;
  880.             }
  881.  
  882.         } // end if is_array
  883.  
  884.     } // end method chmod
  885.  
  886.     /**
  887.      * This method will try to chmod a folder and all of its contents
  888.      * on the server. The target argument must be a folder or an array of folders
  889.      * and the permissions argument have to be an integer (i.e. 777).
  890.      * The file can be either a relative or absolute path.
  891.      * NOTE: Some servers do not support this feature. In that case, you
  892.      * will get a PEAR error object returned. If successful, the method
  893.      * returns true
  894.      *
  895.      * @access  public
  896.      * @param   mixed   $target        The folder or array of folders to
  897.      *                                 set permissions for
  898.      * @param   integer $permissions   The mode to set the folder
  899.      *                                 and file permissions to
  900.      * @return  mixed                  True if successful, otherwise PEAR::Error
  901.      * @see     NET_FTP_ERR_CHMOD_FAILED, NET_FTP_ERR_DETERMINEPATH_FAILED, NET_FTP_ERR_RAWDIRLIST_FAILED, NET_FTP_ERR_DIRLIST_UNSUPPORTED
  902.      */
  903.     
  904.     function chmodRecursive($target, $permissions)
  905.     {
  906.         static $dir_permissions;
  907.  
  908.         if(!isset($dir_permissions)){ // Making directory specific permissions
  909.             $dir_permissions = $this->_makeDirPermissions($permissions);
  910.         }
  911.  
  912.         // If $target is an array: Loop through it
  913.         if (is_array($target)) {
  914.  
  915.             for ($i = 0; $i < count($target); $i++) {
  916.                 $res = $this->chmodRecursive($target[$i], $permissions);
  917.                 if (PEAR::isError($res)) {
  918.                     return $res;
  919.                 } // end if isError
  920.             } // end for i < count($target)
  921.  
  922.         } else {
  923.  
  924.             $remote_path = $this->_construct_path($target);
  925.  
  926.             // Chmod the directory itself
  927.             $result = $this->chmod($remote_path, $dir_permissions);
  928.  
  929.             if (PEAR::isError($result)) {
  930.                 return $result;
  931.             }
  932.  
  933.             // If $remote_path last character is not a slash, add one
  934.             if (substr($remote_path, strlen($remote_path)-1) != "/") {
  935.  
  936.                 $remote_path .= "/";
  937.             }
  938.  
  939.             $dir_list = array();
  940.             $mode = NET_FTP_DIRS_ONLY;
  941.             $dir_list = $this->ls($remote_path, $mode);
  942.             foreach ($dir_list as $dir_entry) {
  943.                 if ($dir_entry == '.' || $dir_entry == '..') {;
  944.                     continue;
  945.                 }
  946.                 
  947.                 $remote_path_new = $remote_path.$dir_entry["name"]."/";
  948.  
  949.                 // Chmod the directory we're about to enter
  950.                 $result = $this->chmod($remote_path_new, $dir_permissions);
  951.  
  952.                 if (PEAR::isError($result)) {
  953.                     return $result;
  954.                 }
  955.  
  956.                 $result = $this->chmodRecursive($remote_path_new, $permissions);
  957.  
  958.                 if (PEAR::isError($result)) {
  959.                     return $result;
  960.                 }
  961.  
  962.             } // end foreach dir_list as dir_entry
  963.  
  964.             $file_list = array();
  965.             $mode = NET_FTP_FILES_ONLY;
  966.             $file_list = $this->ls($remote_path, $mode);
  967.  
  968.             foreach ($file_list as $file_entry) {
  969.  
  970.                 $remote_file = $remote_path.$file_entry["name"];
  971.  
  972.                 $result = $this->chmod($remote_file, $permissions);
  973.  
  974.                 if (PEAR::isError($result)) {
  975.                     return $result;
  976.                 }
  977.  
  978.             } // end foreach $file_list
  979.  
  980.         } // end if is_array
  981.  
  982.         return true; // No errors
  983.  
  984.     } // end method chmodRecursive
  985.  
  986.     /**
  987.      * Rename or move a file or a directory from the ftp-server
  988.      *
  989.      * @access  public
  990.      * @param   string $remote_from The remote file or directory original to rename or move
  991.      * @param   string $remote_to The remote file or directory final to rename or move
  992.      * @return  bool $res True on success, otherwise PEAR::Error
  993.      * @see     NET_FTP_ERR_RENAME_FAILED
  994.      */
  995.  
  996.     function rename ($remote_from, $remote_to) 
  997.     {
  998.         $res = @ftp_rename($this->_handle, $remote_from, $remote_to);
  999.         if(!$res) {
  1000.             return $this->raiseError("Could not rename ".$remote_from." to ".$remote_to." !", NET_FTP_ERR_RENAME_FAILED);
  1001.         }
  1002.         return true;
  1003.     }
  1004.  
  1005.     /**
  1006.      * This will return logical permissions mask for directory.
  1007.      * if directory have to be writeable it have also be executable
  1008.      *
  1009.      * @access  private
  1010.      * @param   string $permissions    File permissions in digits for file (i.e. 666)
  1011.      * @return  string                 File permissions in digits for directory (i.e. 777)
  1012.      */
  1013.  
  1014.     function _makeDirPermissions($permissions){
  1015.         $permissions = (string)$permissions;
  1016.  
  1017.         for($i = 0; $i < strlen($permissions); $i++){ // going through (user, group, world)
  1018.             if((int)$permissions{$i} & 4 and !((int)$permissions{$i} & 1)){ // Read permission is set
  1019.                                                                             // but execute not yet
  1020.                 (int)$permissions{$i} = (int)$permissions{$i} + 1; // Adding execute flag
  1021.             }
  1022.         }
  1023.  
  1024.         return (string)$permissions;
  1025.     }
  1026.  
  1027.     /**
  1028.      * This will return the last modification-time of a file. You can either give this
  1029.      * function a relative or an absolute path to the file to check.
  1030.      * NOTE: Some servers will not support this feature and the function works
  1031.      * only on files, not directories! When successful,
  1032.      * it will return the last modification-time as a unix-timestamp or, when $format is
  1033.      * specified, a preformated timestring.
  1034.      *
  1035.      * @access  public
  1036.      * @param   string $file    The file to check
  1037.      * @param   string $format  (optional) The format to give the date back 
  1038.      *                          if not set, it will return a Unix timestamp
  1039.      * @return  mixed           Unix timestamp, a preformated date-string or PEAR::Error
  1040.      * @see     NET_FTP_ERR_MDTMDIR_UNSUPPORTED, NET_FTP_ERR_MDTM_FAILED, NET_FTP_ERR_DATEFORMAT_FAILED
  1041.      */
  1042.     
  1043.     function mdtm($file, $format = null)
  1044.     {
  1045.         $file = $this->_construct_path($file);
  1046.         if ($this->_check_dir($file)) {
  1047.             return $this->raiseError("Filename '$file' seems to be a directory.", NET_FTP_ERR_MDTMDIR_UNSUPPORTED);
  1048.         }
  1049.         $res = @ftp_mdtm($this->_handle, $file);
  1050.         if ($res == -1) {
  1051.             return $this->raiseError("Could not get last-modification-date of '$file'.", NET_FTP_ERR_MDTM_FAILED);
  1052.         }
  1053.         if (isset($format)) {
  1054.             $res = date($format, $res);
  1055.             if (!$res) {
  1056.                 return $this->raiseError("Date-format failed on timestamp '$res'.", NET_FTP_ERR_DATEFORMAT_FAILED);
  1057.             }
  1058.         }
  1059.         return $res;
  1060.     }
  1061.  
  1062.     /**
  1063.      * This will return the size of a given file in bytes. You can either give this function
  1064.      * a relative or an absolute file-path. NOTE: Some servers do not support this feature!
  1065.      *
  1066.      * @access  public
  1067.      * @param   string $file   The file to check
  1068.      * @return  mixed          Size in bytes or PEAR::Error
  1069.      * @see     NET_FTP_ERR_SIZE_FAILED
  1070.      */
  1071.     
  1072.     function size($file)
  1073.     {
  1074.         $file = $this->_construct_path($file);
  1075.         $res = @ftp_size($this->_handle, $file);
  1076.         if ($res == -1) {
  1077.             return $this->raiseError("Could not determine filesize of '$file'.", NET_FTP_ERR_SIZE_FAILED);
  1078.         } else {
  1079.             return $res;
  1080.         }
  1081.     }
  1082.  
  1083.     /**
  1084.      * This method returns a directory-list of the current directory or given one.
  1085.      * To display the current selected directory, simply set the first parameter to null
  1086.      * or leave it blank, if you do not want to use any other parameters.
  1087.      * <BR><BR>
  1088.      * There are 4 different modes of listing directories. Either to list only
  1089.      * the files (using NET_FTP_FILES_ONLY), to list only directories (using
  1090.      * NET_FTP_DIRS_ONLY) or to show both (using NET_FTP_DIRS_FILES, which is default).
  1091.      * <BR><BR>
  1092.      * The 4th one is the NET_FTP_RAWLIST, which returns just the array created by the
  1093.      * ftp_rawlist()-function build into PHP.
  1094.      * <BR><BR>
  1095.      * The other function-modes will return an array containing the requested data.
  1096.      * The files and dirs are listed in human-sorted order, but if you select
  1097.      * NET_FTP_DIRS_FILES the directories will be added above the files,
  1098.      * but although both sorted.
  1099.      * <BR><BR>
  1100.      * All elements in the arrays are associative arrays themselves. The have the following
  1101.      * structure:
  1102.      * <BR><BR>
  1103.      * Dirs:<BR>
  1104.      *           ["name"]        =>  string The name of the directory<BR>
  1105.      *           ["rights"]      =>  string The rights of the directory (in style "rwxr-xr-x")<BR>
  1106.      *           ["user"]        =>  string The owner of the directory<BR>
  1107.      *           ["group"]       =>  string The group-owner of the directory<BR>
  1108.      *           ["files_inside"]=>  string The number of files/dirs inside the directory
  1109.      *                                      excluding "." and ".."<BR>
  1110.      *           ["date"]        =>  int The creation-date as Unix timestamp<BR>
  1111.      *           ["is_dir"]      =>  bool true, cause this is a dir<BR>
  1112.      * <BR><BR>
  1113.      * Files:<BR>
  1114.      *           ["name"]        =>  string The name of the file<BR>
  1115.      *           ["size"]        =>  int Size in bytes<BR>
  1116.      *           ["rights"]      =>  string The rights of the file (in style "rwxr-xr-x")<BR>
  1117.      *           ["user"]        =>  string The owner of the file<BR>
  1118.      *           ["group"]       =>  string The group-owner of the file<BR>
  1119.      *           ["date"]        =>  int The creation-date as Unix timestamp<BR>
  1120.      *           ["is_dir"]      =>  bool false, cause this is a file<BR>
  1121.      *
  1122.      * @access  public
  1123.      * @param   string $dir   (optional) The directory to list or null, when listing the current directory.
  1124.      * @param   int    $mode  (optional) The mode which types to list (files, directories or both).
  1125.      * @return  mixed         The directory list as described above or PEAR::Error on failure.
  1126.      * @see     NET_FTP_DIRS_FILES, NET_FTP_DIRS_ONLY, NET_FTP_FILES_ONLY, NET_FTP_RAWLIST, NET_FTP_ERR_DETERMINEPATH_FAILED, NET_FTP_ERR_RAWDIRLIST_FAILED, NET_FTP_ERR_DIRLIST_UNSUPPORTED
  1127.      */
  1128.      
  1129.     function ls($dir = null, $mode = NET_FTP_DIRS_FILES)
  1130.     {
  1131.         if (!isset($dir)) {
  1132.             $dir = @ftp_pwd($this->_handle);
  1133.             if (!$dir) {
  1134.                 return $this->raiseError("Could not retrieve current directory", NET_FTP_ERR_DETERMINEPATH_FAILED);
  1135.             }
  1136.         }
  1137.         if (($mode != NET_FTP_FILES_ONLY) && ($mode != NET_FTP_DIRS_ONLY) && ($mode != NET_FTP_RAWLIST)) {
  1138.             $mode = NET_FTP_DIRS_FILES;
  1139.         }
  1140.  
  1141.         switch ($mode) {
  1142.             case NET_FTP_DIRS_FILES:    $res = $this->_ls_both ( $dir );
  1143.                                         break;
  1144.             case NET_FTP_DIRS_ONLY:     $res = $this->_ls_dirs ( $dir );
  1145.                                         break;
  1146.             case NET_FTP_FILES_ONLY:    $res = $this->_ls_files ( $dir );
  1147.                                         break;
  1148.             case NET_FTP_RAWLIST:       $res = @ftp_rawlist($this->_handle, $dir);
  1149.                                         break;
  1150.         }
  1151.  
  1152.         return $res;
  1153.     }
  1154.  
  1155.     /**
  1156.      * This method will delete the given file or directory ($path) from the server
  1157.      * (maybe recursive).
  1158.      *
  1159.      * Whether the given string is a file or directory is only determined by the last
  1160.      * sign inside the string ("/" or not).
  1161.      *
  1162.      * If you specify a directory, you can optionally specify $recursive as true,
  1163.      * to let the directory be deleted recursive (with all sub-directories and files
  1164.      * inherited).
  1165.      *
  1166.      * You can either give a absolute or relative path for the file / dir. If you choose to
  1167.      * use the relative path, it will be automatically completed with the actual
  1168.      * selected directory.
  1169.      *
  1170.      * @access  public
  1171.      * @param   string $path      The absolute or relative path to the file / directory.
  1172.      * @param   bool   $recursive (optional)
  1173.      * @return  mixed             True on success, otherwise PEAR::Error
  1174.      * @see     NET_FTP_ERR_DELETEFILE_FAILED, NET_FTP_ERR_DELETEDIR_FAILED, NET_FTP_ERR_REMOTEPATHNODIR
  1175.      */
  1176.     
  1177.     function rm($path, $recursive = false)
  1178.     {
  1179.         $path = $this->_construct_path($path);
  1180.  
  1181.         if ($this->_check_dir($path)) {
  1182.             if ($recursive) {
  1183.                 return $this->_rm_dir_recursive($path);
  1184.             } else {
  1185.                 return $this->_rm_dir($path);
  1186.             }
  1187.         } else {
  1188.             return $this->_rm_file($path);
  1189.         }
  1190.     }
  1191.  
  1192.     /**
  1193.      * This function will download a file from the ftp-server. You can either spcify a absolute
  1194.      * path to the file (beginning with "/") or a relative one, which will be completed
  1195.      * with the actual directory you selected on the server. You can specify
  1196.      * the path to which the file will be downloaded on the local
  1197.      * maschine, if the file should be overwritten if it exists (optionally, default is
  1198.      * no overwriting) and in which mode (FTP_ASCII or FTP_BINARY) the file should be
  1199.      * downloaded (if you do not specify this, the method tries to determine it automatically
  1200.      * from the mode-directory or uses the default-mode, set by you). If you give a relative
  1201.      * path to the local-file, the script-path is used as basepath.
  1202.      *
  1203.      * @access  public
  1204.      * @param   string $remote_file The absolute or relative path to the file to download
  1205.      * @param   string $local_file  The local file to put the downloaded in
  1206.      * @param   bool   $overwrite   (optional) Whether to overwrite existing file
  1207.      * @param   int    $mode        (optional) Either FTP_ASCII or FTP_BINARY
  1208.      * @return  mixed               True on success, otherwise PEAR::Error
  1209.      * @see     NET_FTP_ERR_OVERWRITELOCALFILE_FORBIDDEN, NET_FTP_ERR_OVERWRITELOCALFILE_FAILED, NET_FTP_ERR_OVERWRITELOCALFILE_FAILED
  1210.      */
  1211.     
  1212.     function get($remote_file, $local_file, $overwrite = false, $mode = null)
  1213.     {
  1214.         if (!isset($mode)) {
  1215.             $mode = $this->checkFileExtension($remote_file);
  1216.         }
  1217.  
  1218.         $remote_file = $this->_construct_path($remote_file);
  1219.  
  1220.         if (@file_exists($local_file) && !$overwrite) {
  1221.             return $this->raiseError("Local file '$local_file' exists and may not be overwriten.", NET_FTP_ERR_OVERWRITELOCALFILE_FORBIDDEN);
  1222.         }
  1223.         if (@file_exists($local_file) && !@is_writeable($local_file) && $overwrite) {
  1224.             return $this->raiseError("Local file '$local_file' is not writeable. Can not overwrite.", NET_FTP_ERR_OVERWRITELOCALFILE_FAILED);
  1225.         }
  1226.  
  1227.         if (@function_exists('ftp_nb_get')){
  1228.             $res = @ftp_nb_get($this->_handle, $local_file, $remote_file, $mode);
  1229.             while ($res == FTP_MOREDATA) {
  1230.                 $this->_announce('nb_get');
  1231.                 $res = @ftp_nb_continue ($this->_handle);
  1232.             }
  1233.         } else {
  1234.             $res = @ftp_get($this->_handle, $local_file, $remote_file, $mode);
  1235.         }
  1236.         if (!$res) {
  1237.             return $this->raiseError("File '$remote_file' could not be downloaded to '$local_file'.", NET_FTP_ERR_OVERWRITELOCALFILE_FAILED);
  1238.         } else {
  1239.             return true;
  1240.         }
  1241.     }
  1242.  
  1243.     /**
  1244.      * This function will upload a file to the ftp-server. You can either specify a absolute
  1245.      * path to the remote-file (beginning with "/") or a relative one, which will be completed
  1246.      * with the actual directory you selected on the server. You can specify
  1247.      * the path from which the file will be uploaded on the local
  1248.      * maschine, if the file should be overwritten if it exists (optionally, default is
  1249.      * no overwriting) and in which mode (FTP_ASCII or FTP_BINARY) the file should be
  1250.      * downloaded (if you do not specify this, the method tries to determine it automatically
  1251.      * from the mode-directory or uses the default-mode, set by you). If you give a relative
  1252.      * path to the local-file, the script-path is used as basepath.
  1253.      *
  1254.      * @access  public
  1255.      * @param   string $local_file  The local file to upload
  1256.      * @param   string $remote_file The absolute or relative path to the file to upload to
  1257.      * @param   bool   $overwrite   (optional) Whether to overwrite existing file
  1258.      * @param   int    $mode        (optional) Either FTP_ASCII or FTP_BINARY
  1259.      * @return  mixed               True on success, otherwise PEAR::Error
  1260.      * @see     NET_FTP_ERR_LOCALFILENOTEXIST, NET_FTP_ERR_OVERWRITEREMOTEFILE_FORBIDDEN, NET_FTP_ERR_UPLOADFILE_FAILED
  1261.      */
  1262.     
  1263.     function put($local_file, $remote_file, $overwrite = false, $mode = null)
  1264.     {
  1265.         if (!isset($mode)) {
  1266.             $mode = $this->checkFileExtension($local_file);
  1267.         }
  1268.         $remote_file = $this->_construct_path($remote_file);
  1269.  
  1270.         if (!@file_exists($local_file)) {
  1271.             return $this->raiseError("Local file '$local_file' does not exist.", NET_FTP_ERR_LOCALFILENOTEXIST);
  1272.         }
  1273.         if ((@ftp_size($this->_handle, $remote_file) != -1) && !$overwrite) {
  1274.             return $this->raiseError("Remote file '$remote_file' exists and may not be overwriten.", NET_FTP_ERR_OVERWRITEREMOTEFILE_FORBIDDEN);
  1275.         }
  1276.  
  1277.         if (function_exists('ftp_nb_put')){
  1278.             $res = @ftp_nb_put($this->_handle, $remote_file, $local_file, $mode);
  1279.             while ($res == FTP_MOREDATA) {
  1280.                 $this->_announce('nb_put');
  1281.                 $res = @ftp_nb_continue($this->_handle);
  1282.             }
  1283.  
  1284.         } else {
  1285.             $res = @ftp_put($this->_handle, $remote_file, $local_file, $mode);
  1286.         }
  1287.         if (!$res) {
  1288.             return $this->raiseError("File '$local_file' could not be uploaded to '$remote_file'.", NET_FTP_ERR_UPLOADFILE_FAILED);
  1289.         } else {
  1290.             return true;
  1291.         }
  1292.     }
  1293.  
  1294.     /**
  1295.      * This functionality allows you to transfer a whole directory-structure from the
  1296.      * remote-ftp to your local host. You have to give a remote-directory (ending with
  1297.      * '/') and the local directory (ending with '/') where to put the files you download.
  1298.      * The remote path is automatically completed with the current-remote-dir, if you give
  1299.      * a relative path to this function. You can give a relative path for the $local_path,
  1300.      * too. Then the script-basedir will be used for comletion of the path.
  1301.      * The parameter $overwrite will determine, whether to overwrite existing files or not.
  1302.      * Standard for this is false. Fourth you can explicitly set a mode for all transfer-
  1303.      * actions done. If you do not set this, the method tries to determine the transfer-
  1304.      * mode by checking your mode-directory for the file-extension. If the extension is not
  1305.      * inside the mode-directory, it will get your default-mode.
  1306.      *
  1307.      * @access  public
  1308.      * @param   string $remote_path The path to download
  1309.      * @param   string $local_path  The path to download to
  1310.      * @param   bool   $overwrite   (optional) Whether to overwrite existing files (true) or not (false, standard).
  1311.      * @param   int    $mode        (optional) The transfermode (either FTP_ASCII or FTP_BINARY).
  1312.      * @return  mixed               True on succes, otherwise PEAR::Error
  1313.      * @see     NET_FTP_ERR_OVERWRITELOCALFILE_FORBIDDEN, NET_FTP_ERR_OVERWRITELOCALFILE_FAILED, NET_FTP_ERR_OVERWRITELOCALFILE_FAILED, NET_FTP_ERR_REMOTEPATHNODIR, NET_FTP_ERR_LOCALPATHNODIR,NET_FTP_ERR_CREATELOCALDIR_FAILED
  1314.      */
  1315.     
  1316.     function getRecursive($remote_path, $local_path, $overwrite = false, $mode = null)
  1317.     {
  1318.         $remote_path = $this->_construct_path($remote_path);
  1319.         if (!$this->_check_dir($remote_path)) {
  1320.             return $this->raiseError("Given remote-path '$remote_path' seems not to be a directory.", NET_FTP_ERR_REMOTEPATHNODIR);
  1321.         }
  1322.         if (!$this->_check_dir($local_path)) {
  1323.             return $this->raiseError("Given local-path '$local_path' seems not to be a directory.", NET_FTP_ERR_LOCALPATHNODIR);
  1324.         }
  1325.  
  1326.         if (!@is_dir($local_path)) {
  1327.             $res = @mkdir($local_path);
  1328.             if (!$res) {
  1329.                 return $this->raiseError("Could not create dir '$local_path'", NET_FTP_ERR_CREATELOCALDIR_FAILED);
  1330.             }
  1331.         }
  1332.         $dir_list = array();
  1333.         $dir_list = $this->ls($remote_path, NET_FTP_DIRS_ONLY);
  1334.         foreach ($dir_list as $dir_entry) {
  1335.             if ($dir_entry['name'] != '.' && $dir_entry['name'] != '..') {
  1336.                 $remote_path_new = $remote_path.$dir_entry["name"]."/";
  1337.                 $local_path_new = $local_path.$dir_entry["name"]."/";
  1338.                 $result = $this->getRecursive($remote_path_new, $local_path_new, $overwrite, $mode);
  1339.                 if ($this->isError($result)) {
  1340.                     return $result;
  1341.                 }
  1342.             }
  1343.         }
  1344.         $file_list = array();
  1345.         $file_list = $this->ls($remote_path, NET_FTP_FILES_ONLY);
  1346.         foreach ($file_list as $file_entry) {
  1347.             $remote_file = $remote_path.$file_entry["name"];
  1348.             $local_file = $local_path.$file_entry["name"];
  1349.             $result = $this->get($remote_file, $local_file, $overwrite, $mode);
  1350.             if ($this->isError($result)) {
  1351.                 return $result;
  1352.             }
  1353.         }
  1354.         return true;
  1355.     }
  1356.  
  1357.     /**
  1358.      * This functionality allows you to transfer a whole directory-structure from your
  1359.      * local host to the remote-ftp. You have to give a remote-directory (ending with
  1360.      * '/') and the local directory (ending with '/') where to put the files you download.
  1361.      * The remote path is automatically completed with the current-remote-dir, if you give
  1362.      * a relative path to this function. You can give a relative path for the $local_path,
  1363.      * too. Then the script-basedir will be used for comletion of the path.
  1364.      * The parameter $overwrite will determine, whether to overwrite existing files or not.
  1365.      * Standard for this is false. Fourth you can explicitly set a mode for all transfer-
  1366.      * actions done. If you do not set this, the method tries to determine the transfer-
  1367.      * mode by checking your mode-directory for the file-extension. If the extension is not
  1368.      * inside the mode-directory, it will get your default-mode.
  1369.      *
  1370.      * @access  public
  1371.      * @param   string $remote_path The path to download 
  1372.      * @param   string $local_path  The path to download to
  1373.      * @param   bool   $overwrite   (optional) Whether to overwrite existing files (true) or not (false, standard).
  1374.      * @param   int    $mode        (optional) The transfermode (either FTP_ASCII or FTP_BINARY).
  1375.      * @return  mixed               True on succes, otherwise PEAR::Error
  1376.      * @see     NET_FTP_ERR_LOCALFILENOTEXIST, NET_FTP_ERR_OVERWRITEREMOTEFILE_FORBIDDEN, NET_FTP_ERR_UPLOADFILE_FAILED, NET_FTP_ERR_LOCALPATHNODIR, NET_FTP_ERR_REMOTEPATHNODIR
  1377.      */
  1378.     
  1379.     function putRecursive($local_path, $remote_path, $overwrite = false, $mode = null)
  1380.     {
  1381.         $remote_path = $this->_construct_path($remote_path);
  1382.         if (!$this->_check_dir($local_path) || !is_dir($local_path)) {
  1383.             return $this->raiseError("Given local-path '$local_path' seems not to be a directory.", NET_FTP_ERR_LOCALPATHNODIR);
  1384.         }
  1385.         if (!$this->_check_dir($remote_path)) {
  1386.             return $this->raiseError("Given remote-path '$remote_path' seems not to be a directory.", NET_FTP_ERR_REMOTEPATHNODIR);
  1387.         }
  1388.         $old_path = $this->pwd();
  1389.         if ($this->isError($this->cd($remote_path))) {
  1390.             $res = $this->mkdir($remote_path);
  1391.             if ($this->isError($res)) {
  1392.                 return $res;
  1393.             }
  1394.         }
  1395.         $this->cd($old_path);
  1396.         $dir_list = $this->_ls_local($local_path);
  1397.         foreach ($dir_list["dirs"] as $dir_entry) {
  1398.             $remote_path_new = $remote_path.$dir_entry."/";
  1399.             $local_path_new = $local_path.$dir_entry."/";
  1400.             $result = $this->putRecursive($local_path_new, $remote_path_new, $overwrite, $mode);
  1401.             if ($this->isError($result)) {
  1402.                 return $result;
  1403.             }
  1404.         }
  1405.  
  1406.         foreach ($dir_list["files"] as $file_entry) {
  1407.             $remote_file = $remote_path.$file_entry;
  1408.             $local_file = $local_path.$file_entry;
  1409.             $result = $this->put($local_file, $remote_file, $overwrite, $mode);
  1410.             if ($this->isError($result)) {
  1411.                 return $result;
  1412.             }
  1413.         }
  1414.         return true;
  1415.     }
  1416.  
  1417.     /**
  1418.      * This checks, whether a file should be transfered in ascii- or binary-mode
  1419.      * by it's file-extension. If the file-extension is not set or
  1420.      * the extension is not inside one of the extension-dirs, the actual set
  1421.      * transfer-mode is returned.
  1422.      *
  1423.      * @access  public
  1424.      * @param   string $filename  The filename to be checked
  1425.      * @return  int               Either FTP_ASCII or FTP_BINARY
  1426.      */
  1427.     
  1428.     function checkFileExtension($filename)
  1429.     {
  1430.         $pattern = "/\.(.*)$/";
  1431.         $has_extension = preg_match($pattern, $filename, $eregs);
  1432.         if (!$has_extension) {
  1433.             return $this->_mode;
  1434.         } else {
  1435.             $ext = $eregs[1];
  1436.         }
  1437.  
  1438.         if (!empty($this->_file_extensions[$ext])) {
  1439.             return $this->_file_extensions[$ext];
  1440.         }
  1441.  
  1442.         return $this->_mode;
  1443.     }
  1444.  
  1445.     /**
  1446.      * Set the Hostname
  1447.      *
  1448.      * @access  public
  1449.      * @param   string $host The Hostname to set
  1450.      * @return  bool True on success, otherwise PEAR::Error
  1451.      * @see     NET_FTP_ERR_HOSTNAMENOSTRING
  1452.      */
  1453.     
  1454.     function setHostname($host)
  1455.     {
  1456.         if (!is_string($host)) {
  1457.             return PEAR::raiseError("Hostname must be a string.", NET_FTP_ERR_HOSTNAMENOSTRING);
  1458.         }
  1459.         $this->_hostname = $host;
  1460.         return true;
  1461.     }
  1462.  
  1463.     /**
  1464.      * Set the Port
  1465.      *
  1466.      * @access  public
  1467.      * @param   int $port    The Port to set
  1468.      * @return  bool True on success, otherwise PEAR::Error
  1469.      * @see     NET_FTP_ERR_PORTLESSZERO
  1470.      */
  1471.     
  1472.     function setPort($port)
  1473.     {
  1474.         if (!is_int($port) || ($port < 0)) {
  1475.             PEAR::raiseError("Invalid port. Has to be integer >= 0", NET_FTP_ERR_PORTLESSZERO);
  1476.         }
  1477.         $this->_port = $port;
  1478.         return true;
  1479.     }
  1480.  
  1481.     /**
  1482.      * Set the Username
  1483.      *
  1484.      * @access  public
  1485.      * @param   string $user The Username to set
  1486.      * @return  mixed True on success, otherwise PEAR::Error
  1487.      * @see     NET_FTP_ERR_USERNAMENOSTRING
  1488.      */
  1489.     
  1490.     function setUsername($user)
  1491.     {
  1492.         if (empty($user) || !is_string($user)) {
  1493.             return PEAR::raiseError('Username $user invalid.', NET_FTP_ERR_USERNAMENOSTRING);
  1494.         }
  1495.         $this->_username = $user;
  1496.     }
  1497.  
  1498.     /**
  1499.      * Set the Password
  1500.      *
  1501.      * @access  private
  1502.      * @param   string $password  The Password to set
  1503.      * @return  void
  1504.      * @see     NET_FTP_ERR_PASSWORDNOSTRING
  1505.      */
  1506.     
  1507.     function setPassword($password)
  1508.     {
  1509.         if (empty($password) || !is_string($password)) {
  1510.             return PEAR::raiseError('Password xxx invalid.', NET_FTP_ERR_PASSWORDNOSTRING);
  1511.         }
  1512.         $this->_password = $password;
  1513.     }
  1514.  
  1515.     /**
  1516.      * Set the transfer-mode. You can use the predefined constants
  1517.      * FTP_ASCII or FTP_BINARY. The mode will be stored for any further transfers.
  1518.      *
  1519.      * @access  public
  1520.      * @param   int    $mode  The mode to set
  1521.      * @return  mixed         True on success, otherwise PEAR::Error
  1522.      * @see     NET_FTP_ERR_NOMODECONST
  1523.      */
  1524.     
  1525.     function setMode($mode)
  1526.     {
  1527.         if (($mode == FTP_ASCII) || ($mode == FTP_BINARY)) {
  1528.             $this->_mode = $mode;
  1529.             return true;
  1530.         } else {
  1531.             return $this->raiseError('FTP-Mode has either to be FTP_ASCII or FTP_BINARY', NET_FTP_ERR_NOMODECONST);
  1532.         }
  1533.     }
  1534.  
  1535.     /**
  1536.      * Set the transfer-method to passive mode
  1537.      *
  1538.      * @access  public
  1539.      * @return  void
  1540.      */
  1541.     
  1542.     function setPassive()
  1543.     {
  1544.         $this->_passv = true;
  1545.         @ftp_pasv($this->_handle, true);
  1546.     }
  1547.  
  1548.     /**
  1549.      * Set the transfer-method to active mode
  1550.      *
  1551.      * @access  public
  1552.      * @return  void
  1553.      */
  1554.     
  1555.     function setActive()
  1556.     {
  1557.         $this->_passv = false;
  1558.         @ftp_pasv($this->_handle, false);
  1559.     }
  1560.  
  1561.     /**
  1562.      * Set the timeout for FTP operations
  1563.      * Use this method to set a timeout for FTP operation. Timeout has to be an integer.
  1564.      *
  1565.      * @acess   public
  1566.      * @param   int $timeout the timeout to use
  1567.      * @return  bool True on success, otherwise PEAR::Error
  1568.      * @see     NET_FTP_ERR_TIMEOUTLESSZERO, NET_FTP_ERR_SETTIMEOUT_FAILED
  1569.      */
  1570.      
  1571.     function setTimeout ( $timeout = 0 ) 
  1572.     {
  1573.         if (!is_int($timeout) || ($timeout < 0)) {
  1574.             return PEAR::raiseError("Timeout $timeout is invalid, has to be an integer >= 0", NET_FTP_ERR_TIMEOUTLESSZERO);
  1575.         }
  1576.         $this->_timeout = $timeout;
  1577.         if (isset($this->_handle) && is_resource($this->_handle)) {
  1578.             $res = @ftp_set_option($this->_handle, FTP_TIMEOUT_SEC, $timeout);
  1579.         } else {
  1580.             $res = true;
  1581.         }
  1582.         if (!$res) {
  1583.             return PEAR::raiseError("Set timeout failed.", NET_FTP_ERR_SETTIMEOUT_FAILED);
  1584.         }
  1585.         return true;
  1586.     }        
  1587.     
  1588.     /**
  1589.      * Adds an extension to a mode-directory
  1590.      * The mode-directory saves file-extensions coresponding to filetypes
  1591.      * (ascii e.g.: 'php', 'txt', 'htm',...; binary e.g.: 'jpg', 'gif', 'exe',...).
  1592.      * The extensions have to be saved without the '.'. And
  1593.      * can be predefined in an external file (see: getExtensionsFile()).
  1594.      *
  1595.      * The array is build like this: 'php' => FTP_ASCII, 'png' => FTP_BINARY
  1596.      *
  1597.      * To change the mode of an extension, just add it again with the new mode!
  1598.      *
  1599.      * @access  public
  1600.      * @param   int    $mode  Either FTP_ASCII or FTP_BINARY
  1601.      * @param   string $ext   Extension
  1602.      * @return  void
  1603.      */
  1604.     
  1605.     function addExtension($mode, $ext)
  1606.     {
  1607.         $this->_file_extensions[$ext] = $mode;
  1608.     }
  1609.  
  1610.     /**
  1611.      * This function removes an extension from the mode-directories 
  1612.      * (described above).
  1613.      *
  1614.      * @access  public
  1615.      * @param   string $ext  The extension to remove
  1616.      * @return  void
  1617.      */
  1618.     
  1619.     function removeExtension($ext)
  1620.     {
  1621.         unset($this->_file_extensions[$ext]);
  1622.     }
  1623.  
  1624.     /**
  1625.      * This get's both (ascii- and binary-mode-directories) from the given file.
  1626.      * Beware, if you read a file into the mode-directory, all former set values 
  1627.      * will be unset!
  1628.      *
  1629.      * @access  public
  1630.      * @param   string $filename  The file to get from
  1631.      * @return  mixed             True on success, otherwise PEAR::Error
  1632.      * @see     NET_FTP_ERR_EXTFILENOTEXIST, NET_FTP_ERR_EXTFILEREAD_FAILED
  1633.      */
  1634.     
  1635.     function getExtensionsFile($filename)
  1636.     {
  1637.         if (!file_exists($filename)) {
  1638.             return $this->raiseError("Extensions-file '$filename' does not exist", NET_FTP_ERR_EXTFILENOTEXIST);
  1639.         }
  1640.  
  1641.         if (!is_readable($filename)) {
  1642.             return $this->raiseError("Extensions-file '$filename' is not readable", NET_FTP_ERR_EXTFILEREAD_FAILED);
  1643.         }
  1644.  
  1645.         $this->_file_extension = @parse_ini_file($filename);
  1646.         return true;
  1647.     }
  1648.  
  1649.     /**
  1650.      * Returns the Hostname
  1651.      *
  1652.      * @access  public
  1653.      * @return  string  The Hostname
  1654.      */
  1655.     
  1656.     function getHostname()
  1657.     {
  1658.         return $this->_hostname;
  1659.     }
  1660.  
  1661.     /**
  1662.      * Returns the Port
  1663.      *
  1664.      * @access  public
  1665.      * @return  int     The Port
  1666.      */
  1667.     
  1668.     function getPort()
  1669.     {
  1670.         return $this->_port;
  1671.     }
  1672.  
  1673.     /**
  1674.      * Returns the Username
  1675.      *
  1676.      * @access  public
  1677.      * @return  string  The Username
  1678.      */
  1679.     
  1680.     function getUsername()
  1681.     {
  1682.         return $this->_username;
  1683.     }
  1684.  
  1685.     /**
  1686.      * Returns the Password
  1687.      *
  1688.      * @access  public
  1689.      * @return  string  The Password
  1690.      */
  1691.     
  1692.     function getPassword()
  1693.     {
  1694.         return $this->_password;
  1695.     }
  1696.  
  1697.     /**
  1698.      * Returns the Transfermode
  1699.      *
  1700.      * @access  public
  1701.      * @return  int     The transfermode, either FTP_ASCII or FTP_BINARY.
  1702.      */
  1703.     
  1704.     function getMode()
  1705.     {
  1706.         return $this->_mode;
  1707.     }
  1708.  
  1709.     /**
  1710.      * Returns, whether the connection is set to passive mode or not
  1711.      *
  1712.      * @access  public
  1713.      * @return  bool    True if passive-, false if active-mode
  1714.      */
  1715.     
  1716.     function isPassive()
  1717.     {
  1718.         return $this->_passv;
  1719.     }
  1720.  
  1721.     /**
  1722.      * Returns the mode set for a file-extension
  1723.      *
  1724.      * @access  public
  1725.      * @param   string   $ext    The extension you wanna ask for
  1726.      * @return  int              Either FTP_ASCII, FTP_BINARY or NULL (if not set a mode for it)
  1727.      */
  1728.     
  1729.     function getExtensionMode($ext)
  1730.     {
  1731.         return @$this->_file_extensions[$ext];
  1732.     }
  1733.  
  1734.     /**
  1735.      * Get the currently set timeout.
  1736.      * Returns the actual timeout set.
  1737.      *
  1738.      * @access public
  1739.      * @return int The actual timeout
  1740.      */
  1741.     
  1742.     function getTimeout ( )
  1743.     {
  1744.         return ftp_get_option($this->_handle, FTP_TIMEOUT_SEC);
  1745.     }    
  1746.  
  1747.     /**
  1748.      * Adds a Net_FTP_Observer instance to the list of observers 
  1749.      * that are listening for messages emitted by this Net_FTP instance.
  1750.      *
  1751.      * @param   object   $observer     The Net_FTP_Observer instance to attach 
  1752.      *                                 as a listener.
  1753.      * @return  boolean                True if the observer is successfully attached.
  1754.      * @access  public
  1755.      * @since   1.3
  1756.      */
  1757.     
  1758.     function attach(&$observer)
  1759.     {
  1760.         if (!is_a($observer, 'Net_FTP_Observer')) {
  1761.             return false;
  1762.         }
  1763.  
  1764.         $this->_listeners[$observer->getId()] = &$observer;
  1765.         return true;
  1766.     }
  1767.  
  1768.     /**
  1769.      * Removes a Net_FTP_Observer instance from the list of observers.
  1770.      *
  1771.      * @param   object   $observer     The Net_FTP_Observer instance to detach 
  1772.      *                                 from the list of listeners.
  1773.      * @return  boolean                True if the observer is successfully detached.
  1774.      * @access  public
  1775.      * @since   1.3
  1776.      */
  1777.     
  1778.     function detach($observer)
  1779.     {
  1780.         if (!is_a($observer, 'Net_FTP_Observer') ||
  1781.             !isset($this->_listeners[$observer->getId()])) {
  1782.             return false;
  1783.         }
  1784.  
  1785.         unset($this->_listeners[$observer->getId()]);
  1786.         return true;
  1787.     }
  1788.  
  1789.     /**
  1790.      * Informs each registered observer instance that a new message has been
  1791.      * sent.                                                                
  1792.      *                                                                      
  1793.      * @param   mixed     $event       A hash describing the net event.   
  1794.      * @access  private                                                     
  1795.      * @since   1.3                                                         
  1796.      */                                                                     
  1797.     
  1798.     function _announce($event)
  1799.     {
  1800.         foreach ($this->_listeners as $id => $listener) {
  1801.             $this->_listeners[$id]->notify($event);
  1802.         }
  1803.     }
  1804.     
  1805.         /**
  1806.      * Rebuild the path, if given relative
  1807.      *
  1808.      * @access  private
  1809.      * @param   string $path   The path to check and construct
  1810.      * @return  string         The build path
  1811.      */
  1812.     
  1813.     function _construct_path($path)
  1814.     {
  1815.         if ((substr($path, 0, 1) != "/") && (substr($path, 0, 2) != "./")) {
  1816.             $actual_dir = @ftp_pwd($this->_handle);
  1817.             if (substr($actual_dir, (strlen($actual_dir) - 2), 1) != "/") {
  1818.                 $actual_dir .= "/";
  1819.             }
  1820.             $path = $actual_dir.$path;
  1821.         }
  1822.         return $path;
  1823.     }
  1824.  
  1825.     /**
  1826.      * Checks, whether a given string is a directory-path (ends with "/") or not.
  1827.      *
  1828.      * @access  private
  1829.      * @param   string $path  Path to check
  1830.      * @return  bool          True if $path is a directory, otherwise false
  1831.      */
  1832.     
  1833.     function _check_dir($path)
  1834.     {
  1835.         if (!empty($path) && substr($path, (strlen($path) - 1), 1) == "/") {
  1836.             return true;
  1837.         } else {
  1838.             return false;
  1839.         }
  1840.     }
  1841.  
  1842.     /**
  1843.      * This will remove a file
  1844.      *
  1845.      * @access  private
  1846.      * @param   string $file   The file to delete
  1847.      * @return  mixed          True on success, otherwise PEAR::Error
  1848.      * @see     NET_FTP_ERR_DELETEFILE_FAILED
  1849.      */
  1850.     
  1851.     function _rm_file($file)
  1852.     {
  1853.         if (substr($file, 0, 1) != "/") {
  1854.             $actual_dir = @ftp_pwd($this->_handle);
  1855.             if (substr($actual_dir, (strlen($actual_dir) - 2), 1) != "/") {
  1856.                 $actual_dir .= "/";
  1857.             }
  1858.             $file = $actual_dir.$file;
  1859.         }
  1860.         $res = @ftp_delete($this->_handle, $file);
  1861.         
  1862.         if (!$res) {
  1863.             return $this->raiseError("Could not delete file '$file'.", NET_FTP_ERR_DELETEFILE_FAILED);
  1864.         } else {
  1865.             return true;
  1866.         }
  1867.     }
  1868.  
  1869.     /**
  1870.      * This will remove a dir
  1871.      *
  1872.      * @access  private
  1873.      * @param   string $dir  The dir to delete
  1874.      * @return  mixed        True on success, otherwise PEAR::Error
  1875.      * @see     NET_FTP_ERR_REMOTEPATHNODIR, NET_FTP_ERR_DELETEDIR_FAILED
  1876.      */
  1877.     
  1878.     function _rm_dir($dir)
  1879.     {
  1880.         if (substr($dir, (strlen($dir) - 1), 1) != "/") {
  1881.             return $this->raiseError("Directory name '$dir' is invalid, has to end with '/'", NET_FTP_ERR_REMOTEPATHNODIR);
  1882.         }
  1883.         $res = @ftp_rmdir($this->_handle, $dir);
  1884.         if (!$res) {
  1885.             return $this->raiseError("Could not delete directory '$dir'.", NET_FTP_ERR_DELETEDIR_FAILED);
  1886.         } else {
  1887.             return true;
  1888.         }
  1889.     }
  1890.  
  1891.     /**
  1892.      * This will remove a dir and all subdirs and -files
  1893.      *
  1894.      * @access  private
  1895.      * @param   string $file  The dir to delete recursively
  1896.      * @return  mixed         True on success, otherwise PEAR::Error
  1897.      * @see     NET_FTP_ERR_REMOTEPATHNODIR, NET_FTP_ERR_DELETEDIR_FAILED
  1898.      */
  1899.     
  1900.     function _rm_dir_recursive($dir)
  1901.     {
  1902.         if (substr($dir, (strlen($dir) - 1), 1) != "/") {
  1903.             return $this->raiseError("Directory name '$dir' is invalid, has to end with '/'", NET_FTP_ERR_REMOTEPATHNODIR);
  1904.         }
  1905.         $file_list = $this->_ls_files($dir);
  1906.         foreach ($file_list as $file) {
  1907.             $file = $dir.$file["name"];
  1908.             $res = $this->rm($file);
  1909.             if ($this->isError($res)) {
  1910.                 return $res;
  1911.             }
  1912.         }
  1913.         $dir_list = $this->_ls_dirs($dir);
  1914.         foreach ($dir_list as $new_dir) {
  1915.             if ($new_dir == '.' || $new_dir == '..') {
  1916.                 continue;
  1917.             }
  1918.             $new_dir = $dir.$new_dir["name"]."/";
  1919.             $res = $this->_rm_dir_recursive($new_dir);
  1920.             if ($this->isError($res)) {
  1921.                 return $res;
  1922.             }
  1923.         }
  1924.         $res = $this->_rm_dir($dir);
  1925.         if (PEAR::isError($res)) {
  1926.             return $res;
  1927.         } else {
  1928.             return true;
  1929.         }
  1930.     }
  1931.  
  1932.     /**
  1933.      * Lists up files and directories
  1934.      *
  1935.      * @access  private
  1936.      * @param   string $dir  The directory to list up
  1937.      * @return  array        An array of dirs and files
  1938.      */
  1939.     
  1940.     function _ls_both($dir)
  1941.     {
  1942.         $list_splitted = $this->_list_and_parse($dir);
  1943.         if (PEAR::isError($list_splitted)) {
  1944.             return $list_splitted;
  1945.         }
  1946.         if (!is_array($list_splitted["files"])) {
  1947.             $list_splitted["files"] = array();
  1948.         }
  1949.         if (!is_array($list_splitted["dirs"])) {
  1950.             $list_splitted["dirs"] = array();
  1951.         }
  1952.         $res = array();
  1953.         @array_splice($res, 0, 0, $list_splitted["files"]);
  1954.         @array_splice($res, 0, 0, $list_splitted["dirs"]);
  1955.         return $res;
  1956.     }
  1957.  
  1958.     /**
  1959.      * Lists up directories
  1960.      *
  1961.      * @access  private
  1962.      * @param   string $dir  The directory to list up
  1963.      * @return  array        An array of dirs
  1964.      */
  1965.     
  1966.     function _ls_dirs($dir)
  1967.     {
  1968.         $list = $this->_list_and_parse($dir);
  1969.         if (PEAR::isError($list)) {
  1970.             return $list;
  1971.         }
  1972.         return $list["dirs"];
  1973.     }
  1974.  
  1975.     /**
  1976.      * Lists up files
  1977.      *
  1978.      * @access  private
  1979.      * @param   string $dir  The directory to list up
  1980.      * @return  array        An array of files
  1981.      */
  1982.     
  1983.     function _ls_files($dir)
  1984.     {
  1985.         $list = $this->_list_and_parse($dir);
  1986.         if (PEAR::isError($list)) {
  1987.             return $list;
  1988.         }
  1989.         return $list["files"];
  1990.     }
  1991.  
  1992.     /**
  1993.      * This lists up the directory-content and parses the items into well-formated arrays
  1994.      * The results of this array are sorted (dirs on top, sorted by name;
  1995.      * files below, sorted by name).
  1996.      *
  1997.      * @access  private
  1998.      * @param   string $dir  The directory to parse
  1999.      * @return  array        Lists of dirs and files
  2000.      * @see     NET_FTP_ERR_RAWDIRLIST_FAILED
  2001.      */
  2002.     
  2003.     function _list_and_parse($dir)
  2004.     {
  2005.         $dirs_list = array();
  2006.         $files_list = array();
  2007.         $dir_list = @ftp_rawlist($this->_handle, $dir);
  2008.         if (!is_array($dir_list)) {
  2009.             return PEAR::raiseError('Could not get raw directory listing.', NET_FTP_ERR_RAWDIRLIST_FAILED);
  2010.         }
  2011.         // Handle empty directories
  2012.         if (count($dir_list) == 0) {
  2013.             return array('dirs' => $dirs_list, 'files' => $files_list);
  2014.         }
  2015.  
  2016.         // Exception for some FTP servers seem to return this wiered result instead of an empty list
  2017.         if (count($dirs_list) == 1 && $dirs_list[0] == 'total 0') {
  2018.             return array('dirs' => array(), 'files' => $files_list);
  2019.         }
  2020.         
  2021.         if (!isset($this->_matcher) || PEAR::isError($this->_matcher)) {
  2022.             $this->_matcher = $this->_determine_os_match($dir_list);
  2023.             if (PEAR::isError($this->_matcher)) {
  2024.                 return $this->_matcher;
  2025.             }
  2026.         }
  2027.         foreach ($dir_list as $entry) {
  2028.             if (!preg_match($this->_matcher['pattern'], $entry, $m)) {
  2029.                 continue;
  2030.             }
  2031.             $entry = array();
  2032.             foreach ($this->_matcher['map'] as $key=>$val) {
  2033.                 $entry[$key] = $m[$val];
  2034.             }
  2035.             $entry['stamp'] = $this->_parse_Date($entry['date']);
  2036.  
  2037.             if ($entry['is_dir']) {
  2038.                 $dirs_list[] = $entry;
  2039.             } else {
  2040.                 $files_list[] = $entry;
  2041.             }
  2042.         }
  2043.         @usort($dirs_list, array("Net_FTP", "_nat_sort"));
  2044.         @usort($files_list, array("Net_FTP", "_nat_sort"));
  2045.         $res["dirs"] = (is_array($dirs_list)) ? $dirs_list : array();
  2046.         $res["files"] = (is_array($files_list)) ? $files_list : array();
  2047.         return $res;
  2048.     }
  2049.     
  2050.     /**
  2051.      * Determine server OS
  2052.      * This determines the server OS and returns a valid regex to parse
  2053.      * ls() output.
  2054.      *
  2055.      * @access  private
  2056.      * @param   array $dir_list The raw dir list to parse
  2057.      * @return  mixed An array of 'pattern' and 'map' on success, otherwise PEAR::Error
  2058.      * @see     NET_FTP_ERR_DIRLIST_UNSUPPORTED
  2059.      */
  2060.     
  2061.     function _determine_os_match(&$dir_list) {
  2062.     foreach ($dir_list as $entry) {
  2063.         foreach ($this->_ls_match as $os => $match) {
  2064.             if (preg_match($match['pattern'], $entry)) {
  2065.                     return $match;
  2066.                 }
  2067.             }
  2068.     }
  2069.         $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!';
  2070.         return PEAR::raiseError($error, NET_FTP_ERR_DIRLIST_UNSUPPORTED);
  2071.     }
  2072.     /**
  2073.      * Lists a local directory
  2074.      *
  2075.      * @access  private
  2076.      * @param   string $dir_path  The dir to list
  2077.      * @return  array             The list of dirs and files
  2078.      */
  2079.     
  2080.     function _ls_local($dir_path)
  2081.     {
  2082.         $dir = dir($dir_path);
  2083.         $dir_list = array();
  2084.         $file_list = array();
  2085.         while (false !== ($entry = $dir->read())) {
  2086.             if (($entry != '.') && ($entry != '..')) {
  2087.                 if (is_dir($dir_path.$entry)) {
  2088.                     $dir_list[] = $entry;
  2089.                 } else {
  2090.                     $file_list[] = $entry;
  2091.                 }
  2092.             }
  2093.         }
  2094.         $dir->close();
  2095.         $res['dirs'] = $dir_list;
  2096.         $res['files'] = $file_list;
  2097.         return $res;
  2098.     }
  2099.  
  2100.     /**
  2101.      * Function for use with usort().
  2102.      * Compares the list-array-elements by name.
  2103.      *
  2104.      * @access  private
  2105.      */
  2106.     
  2107.     function _nat_sort($item_1, $item_2)
  2108.     {
  2109.         return strnatcmp($item_1['name'], $item_2['name']);
  2110.     }
  2111.  
  2112.     /**
  2113.      * Parse dates to timestamps
  2114.      *
  2115.      * @access  private
  2116.      * @param   string $date  Date
  2117.      * @return  int           Timestamp
  2118.      * @see     NET_FTP_ERR_DATEFORMAT_FAILED
  2119.      */
  2120.     
  2121.     function _parse_Date($date)
  2122.     {
  2123.         // Sep 10 22:06 => Sep 10, <year> 22:06
  2124.         if (preg_match('/([A-Za-z]+)[ ]+([0-9]+)[ ]+([0-9]+):([0-9]+)/', $date, $res)) {
  2125.             $year = date('Y');
  2126.             $month = $res[1];
  2127.             $day = $res[2];
  2128.             $hour = $res[3];
  2129.             $minute = $res[4];
  2130.             $date = "$month $day, $year $hour:$minute";
  2131.             $tmpDate = strtotime($date);
  2132.             if ($tmpDate > time()) {
  2133.                 $year--;
  2134.                 $date = "$month $day, $year $hour:$minute";
  2135.             }
  2136.         }
  2137.         // 09-10-04 => 09/10/04
  2138.         elseif (preg_match('/^\d\d-\d\d-\d\d/',$date)) {
  2139.             $date = str_replace('-','/',$date);
  2140.         }
  2141.         $res = strtotime($date);
  2142.         if (!$res) {
  2143.             return $this->raiseError('Dateconversion failed.', NET_FTP_ERR_DATEFORMAT_FAILED);
  2144.         }
  2145.         return $res;
  2146.     }
  2147. }
  2148. ?>
  2149.