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