Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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
?>