Subversion Repositories oidplus

Rev

Rev 846 | Rev 1042 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 846 Rev 874
Line 23... Line 23...
23
 *    $sftp->put('filename.ext', 'hello, world!');
23
 *    $sftp->put('filename.ext', 'hello, world!');
24
 *    print_r($sftp->nlist());
24
 *    print_r($sftp->nlist());
25
 * ?>
25
 * ?>
26
 * </code>
26
 * </code>
27
 *
27
 *
-
 
28
 * @category  Net
-
 
29
 * @package   SFTP
28
 * @author    Jim Wigginton <terrafrost@php.net>
30
 * @author    Jim Wigginton <terrafrost@php.net>
29
 * @copyright 2009 Jim Wigginton
31
 * @copyright 2009 Jim Wigginton
30
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
32
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
31
 * @link      http://phpseclib.sourceforge.net
33
 * @link      http://phpseclib.sourceforge.net
32
 */
34
 */
33
 
35
 
34
namespace phpseclib3\Net;
36
namespace phpseclib3\Net;
35
 
37
 
36
use phpseclib3\Common\Functions\Strings;
38
use phpseclib3\Common\Functions\Strings;
37
use phpseclib3\Exception\FileNotFoundException;
39
use phpseclib3\Exception\FileNotFoundException;
38
use phpseclib3\Net\SFTP\Attribute;
-
 
39
use phpseclib3\Net\SFTP\FileType;
-
 
40
use phpseclib3\Net\SFTP\OpenFlag;
-
 
41
use phpseclib3\Net\SFTP\OpenFlag5;
-
 
42
use phpseclib3\Net\SFTP\PacketType as SFTPPacketType;
-
 
43
use phpseclib3\Net\SFTP\StatusCode;
-
 
44
use phpseclib3\Net\SSH2\MessageType as SSH2MessageType;
-
 
45
 
40
 
46
/**
41
/**
47
 * Pure-PHP implementations of SFTP.
42
 * Pure-PHP implementations of SFTP.
48
 *
43
 *
-
 
44
 * @package SFTP
49
 * @author  Jim Wigginton <terrafrost@php.net>
45
 * @author  Jim Wigginton <terrafrost@php.net>
-
 
46
 * @access  public
50
 */
47
 */
51
class SFTP extends SSH2
48
class SFTP extends SSH2
52
{
49
{
53
    /**
50
    /**
54
     * SFTP channel constant
51
     * SFTP channel constant
55
     *
52
     *
56
     * \phpseclib3\Net\SSH2::exec() uses 0 and \phpseclib3\Net\SSH2::read() / \phpseclib3\Net\SSH2::write() use 1.
53
     * \phpseclib3\Net\SSH2::exec() uses 0 and \phpseclib3\Net\SSH2::read() / \phpseclib3\Net\SSH2::write() use 1.
57
     *
54
     *
58
     * @see \phpseclib3\Net\SSH2::send_channel_packet()
55
     * @see \phpseclib3\Net\SSH2::send_channel_packet()
59
     * @see \phpseclib3\Net\SSH2::get_channel_packet()
56
     * @see \phpseclib3\Net\SSH2::get_channel_packet()
-
 
57
     * @access private
60
     */
58
     */
61
    const CHANNEL = 0x100;
59
    const CHANNEL = 0x100;
62
 
60
 
63
    /**
61
    /**
64
     * Reads data from a local file.
62
     * Reads data from a local file.
65
     *
63
     *
-
 
64
     * @access public
66
     * @see \phpseclib3\Net\SFTP::put()
65
     * @see \phpseclib3\Net\SFTP::put()
67
     */
66
     */
68
    const SOURCE_LOCAL_FILE = 1;
67
    const SOURCE_LOCAL_FILE = 1;
69
    /**
68
    /**
70
     * Reads data from a string.
69
     * Reads data from a string.
71
     *
70
     *
-
 
71
     * @access public
72
     * @see \phpseclib3\Net\SFTP::put()
72
     * @see \phpseclib3\Net\SFTP::put()
73
     */
73
     */
74
    // this value isn't really used anymore but i'm keeping it reserved for historical reasons
74
    // this value isn't really used anymore but i'm keeping it reserved for historical reasons
75
    const SOURCE_STRING = 2;
75
    const SOURCE_STRING = 2;
76
    /**
76
    /**
77
     * Reads data from callback:
77
     * Reads data from callback:
78
     * function callback($length) returns string to proceed, null for EOF
78
     * function callback($length) returns string to proceed, null for EOF
79
     *
79
     *
-
 
80
     * @access public
80
     * @see \phpseclib3\Net\SFTP::put()
81
     * @see \phpseclib3\Net\SFTP::put()
81
     */
82
     */
82
    const SOURCE_CALLBACK = 16;
83
    const SOURCE_CALLBACK = 16;
83
    /**
84
    /**
84
     * Resumes an upload
85
     * Resumes an upload
85
     *
86
     *
-
 
87
     * @access public
86
     * @see \phpseclib3\Net\SFTP::put()
88
     * @see \phpseclib3\Net\SFTP::put()
87
     */
89
     */
88
    const RESUME = 4;
90
    const RESUME = 4;
89
    /**
91
    /**
90
     * Append a local file to an already existing remote file
92
     * Append a local file to an already existing remote file
91
     *
93
     *
-
 
94
     * @access public
92
     * @see \phpseclib3\Net\SFTP::put()
95
     * @see \phpseclib3\Net\SFTP::put()
93
     */
96
     */
94
    const RESUME_START = 8;
97
    const RESUME_START = 8;
95
 
98
 
96
    /**
99
    /**
-
 
100
     * Packet Types
-
 
101
     *
-
 
102
     * @see self::__construct()
-
 
103
     * @var array
-
 
104
     * @access private
-
 
105
     */
-
 
106
    private $packet_types = [];
-
 
107
 
-
 
108
    /**
-
 
109
     * Status Codes
-
 
110
     *
-
 
111
     * @see self::__construct()
-
 
112
     * @var array
-
 
113
     * @access private
-
 
114
     */
-
 
115
    private $status_codes = [];
-
 
116
 
-
 
117
    /** @var array<int, string> */
-
 
118
    private $attributes;
-
 
119
 
-
 
120
    /** @var array<int, string> */
-
 
121
    private $open_flags;
-
 
122
 
-
 
123
    /** @var array<int, string> */
-
 
124
    private $open_flags5;
-
 
125
 
-
 
126
    /** @var array<int, string> */
-
 
127
    private $file_types;
-
 
128
 
-
 
129
    /**
97
     * The Request ID
130
     * The Request ID
98
     *
131
     *
99
     * The request ID exists in the off chance that a packet is sent out-of-order.  Of course, this library doesn't support
132
     * The request ID exists in the off chance that a packet is sent out-of-order.  Of course, this library doesn't support
100
     * concurrent actions, so it's somewhat academic, here.
133
     * concurrent actions, so it's somewhat academic, here.
101
     *
134
     *
102
     * @var boolean
135
     * @var boolean
103
     * @see self::_send_sftp_packet()
136
     * @see self::_send_sftp_packet()
-
 
137
     * @access private
104
     */
138
     */
105
    private $use_request_id = false;
139
    private $use_request_id = false;
106
 
140
 
107
    /**
141
    /**
108
     * The Packet Type
142
     * The Packet Type
Line 110... Line 144...
110
     * The request ID exists in the off chance that a packet is sent out-of-order.  Of course, this library doesn't support
144
     * The request ID exists in the off chance that a packet is sent out-of-order.  Of course, this library doesn't support
111
     * concurrent actions, so it's somewhat academic, here.
145
     * concurrent actions, so it's somewhat academic, here.
112
     *
146
     *
113
     * @var int
147
     * @var int
114
     * @see self::_get_sftp_packet()
148
     * @see self::_get_sftp_packet()
-
 
149
     * @access private
115
     */
150
     */
116
    private $packet_type = -1;
151
    private $packet_type = -1;
117
 
152
 
118
    /**
153
    /**
119
     * Packet Buffer
154
     * Packet Buffer
120
     *
155
     *
121
     * @var string
156
     * @var string
122
     * @see self::_get_sftp_packet()
157
     * @see self::_get_sftp_packet()
-
 
158
     * @access private
123
     */
159
     */
124
    private $packet_buffer = '';
160
    private $packet_buffer = '';
125
 
161
 
126
    /**
162
    /**
127
     * Extensions supported by the server
163
     * Extensions supported by the server
128
     *
164
     *
129
     * @var array
165
     * @var array
130
     * @see self::_initChannel()
166
     * @see self::_initChannel()
-
 
167
     * @access private
131
     */
168
     */
132
    private $extensions = [];
169
    private $extensions = [];
133
 
170
 
134
    /**
171
    /**
135
     * Server SFTP version
172
     * Server SFTP version
136
     *
173
     *
137
     * @var int
174
     * @var int
138
     * @see self::_initChannel()
175
     * @see self::_initChannel()
-
 
176
     * @access private
139
     */
177
     */
140
    private $version;
178
    private $version;
141
 
179
 
142
    /**
180
    /**
143
     * Default Server SFTP version
181
     * Default Server SFTP version
144
     *
182
     *
145
     * @var int
183
     * @var int
146
     * @see self::_initChannel()
184
     * @see self::_initChannel()
-
 
185
     * @access private
147
     */
186
     */
148
    private $defaultVersion;
187
    private $defaultVersion;
149
 
188
 
150
    /**
189
    /**
151
     * Preferred SFTP version
190
     * Preferred SFTP version
152
     *
191
     *
153
     * @var int
192
     * @var int
154
     * @see self::_initChannel()
193
     * @see self::_initChannel()
-
 
194
     * @access private
155
     */
195
     */
156
    private $preferredVersion = 3;
196
    private $preferredVersion = 3;
157
 
197
 
158
    /**
198
    /**
159
     * Current working directory
199
     * Current working directory
160
     *
200
     *
161
     * @var string|bool
201
     * @var string|bool
162
     * @see self::realpath()
202
     * @see self::realpath()
163
     * @see self::chdir()
203
     * @see self::chdir()
-
 
204
     * @access private
164
     */
205
     */
165
    private $pwd = false;
206
    private $pwd = false;
166
 
207
 
167
    /**
208
    /**
168
     * Packet Type Log
209
     * Packet Type Log
169
     *
210
     *
170
     * @see self::getLog()
211
     * @see self::getLog()
171
     * @var array
212
     * @var array
-
 
213
     * @access private
172
     */
214
     */
173
    private $packet_type_log = [];
215
    private $packet_type_log = [];
174
 
216
 
175
    /**
217
    /**
176
     * Packet Log
218
     * Packet Log
177
     *
219
     *
178
     * @see self::getLog()
220
     * @see self::getLog()
179
     * @var array
221
     * @var array
-
 
222
     * @access private
180
     */
223
     */
181
    private $packet_log = [];
224
    private $packet_log = [];
182
 
225
 
183
    /**
226
    /**
184
     * Error information
227
     * Error information
185
     *
228
     *
186
     * @see self::getSFTPErrors()
229
     * @see self::getSFTPErrors()
187
     * @see self::getLastSFTPError()
230
     * @see self::getLastSFTPError()
188
     * @var array
231
     * @var array
-
 
232
     * @access private
189
     */
233
     */
190
    private $sftp_errors = [];
234
    private $sftp_errors = [];
191
 
235
 
192
    /**
236
    /**
193
     * Stat Cache
237
     * Stat Cache
Line 197... Line 241...
197
     *
241
     *
198
     * @see self::_update_stat_cache()
242
     * @see self::_update_stat_cache()
199
     * @see self::_remove_from_stat_cache()
243
     * @see self::_remove_from_stat_cache()
200
     * @see self::_query_stat_cache()
244
     * @see self::_query_stat_cache()
201
     * @var array
245
     * @var array
-
 
246
     * @access private
202
     */
247
     */
203
    private $stat_cache = [];
248
    private $stat_cache = [];
204
 
249
 
205
    /**
250
    /**
206
     * Max SFTP Packet Size
251
     * Max SFTP Packet Size
207
     *
252
     *
208
     * @see self::__construct()
253
     * @see self::__construct()
209
     * @see self::get()
254
     * @see self::get()
210
     * @var int
255
     * @var int
-
 
256
     * @access private
211
     */
257
     */
212
    private $max_sftp_packet;
258
    private $max_sftp_packet;
213
 
259
 
214
    /**
260
    /**
215
     * Stat Cache Flag
261
     * Stat Cache Flag
216
     *
262
     *
217
     * @see self::disableStatCache()
263
     * @see self::disableStatCache()
218
     * @see self::enableStatCache()
264
     * @see self::enableStatCache()
219
     * @var bool
265
     * @var bool
-
 
266
     * @access private
220
     */
267
     */
221
    private $use_stat_cache = true;
268
    private $use_stat_cache = true;
222
 
269
 
223
    /**
270
    /**
224
     * Sort Options
271
     * Sort Options
225
     *
272
     *
226
     * @see self::_comparator()
273
     * @see self::_comparator()
227
     * @see self::setListOrder()
274
     * @see self::setListOrder()
228
     * @var array
275
     * @var array
-
 
276
     * @access private
229
     */
277
     */
230
    protected $sortOptions = [];
278
    protected $sortOptions = [];
231
 
279
 
232
    /**
280
    /**
233
     * Canonicalization Flag
281
     * Canonicalization Flag
Line 237... Line 285...
237
     *
285
     *
238
     * @see self::enablePathCanonicalization()
286
     * @see self::enablePathCanonicalization()
239
     * @see self::disablePathCanonicalization()
287
     * @see self::disablePathCanonicalization()
240
     * @see self::realpath()
288
     * @see self::realpath()
241
     * @var bool
289
     * @var bool
-
 
290
     * @access private
242
     */
291
     */
243
    private $canonicalize_paths = true;
292
    private $canonicalize_paths = true;
244
 
293
 
245
    /**
294
    /**
246
     * Request Buffers
295
     * Request Buffers
247
     *
296
     *
248
     * @see self::_get_sftp_packet()
297
     * @see self::_get_sftp_packet()
249
     * @var array
298
     * @var array
-
 
299
     * @access private
250
     */
300
     */
251
    private $requestBuffer = [];
301
    private $requestBuffer = [];
252
 
302
 
253
    /**
303
    /**
254
     * Preserve timestamps on file downloads / uploads
304
     * Preserve timestamps on file downloads / uploads
255
     *
305
     *
256
     * @see self::get()
306
     * @see self::get()
257
     * @see self::put()
307
     * @see self::put()
258
     * @var bool
308
     * @var bool
-
 
309
     * @access private
259
     */
310
     */
260
    private $preserveTime = false;
311
    private $preserveTime = false;
261
 
312
 
262
    /**
313
    /**
263
     * Arbitrary Length Packets Flag
314
     * Arbitrary Length Packets Flag
Line 268... Line 319...
268
     * 256 * 1024 bytes (SFTP_MAX_MSG_LENGTH from OpenSSH's sftp-common.h)
319
     * 256 * 1024 bytes (SFTP_MAX_MSG_LENGTH from OpenSSH's sftp-common.h)
269
     *
320
     *
270
     * @see self::enableArbitraryLengthPackets()
321
     * @see self::enableArbitraryLengthPackets()
271
     * @see self::_get_sftp_packet()
322
     * @see self::_get_sftp_packet()
272
     * @var bool
323
     * @var bool
-
 
324
     * @access private
273
     */
325
     */
274
    private $allow_arbitrary_length_packets = false;
326
    private $allow_arbitrary_length_packets = false;
275
 
327
 
276
    /**
328
    /**
277
     * Was the last packet due to the channels being closed or not?
329
     * Was the last packet due to the channels being closed or not?
278
     *
330
     *
279
     * @see self::get()
331
     * @see self::get()
280
     * @see self::get_sftp_packet()
332
     * @see self::get_sftp_packet()
281
     * @var bool
333
     * @var bool
-
 
334
     * @access private
282
     */
335
     */
283
    private $channel_close = false;
336
    private $channel_close = false;
284
 
337
 
285
    /**
338
    /**
286
     * Has the SFTP channel been partially negotiated?
339
     * Has the SFTP channel been partially negotiated?
287
     *
340
     *
288
     * @var bool
341
     * @var bool
-
 
342
     * @access private
289
     */
343
     */
290
    private $partial_init = false;
344
    private $partial_init = false;
291
 
345
 
292
    /** @var int */
-
 
293
    private $queueSize = 32;
-
 
294
    /** @var int */
-
 
295
    private $uploadQueueSize = 1024;
-
 
296
 
-
 
297
    /**
346
    /**
298
     * Default Constructor.
347
     * Default Constructor.
299
     *
348
     *
300
     * Connects to an SFTP server
349
     * Connects to an SFTP server
301
     *
350
     *
302
     * @param string $host
351
     * @param string $host
303
     * @param int $port
352
     * @param int $port
304
     * @param int $timeout
353
     * @param int $timeout
-
 
354
     * @access public
305
     */
355
     */
306
    public function __construct($host, $port = 22, $timeout = 10)
356
    public function __construct($host, $port = 22, $timeout = 10)
307
    {
357
    {
308
        parent::__construct($host, $port, $timeout);
358
        parent::__construct($host, $port, $timeout);
309
 
359
 
310
        $this->max_sftp_packet = 1 << 15;
360
        $this->max_sftp_packet = 1 << 15;
311
 
361
 
-
 
362
        $this->packet_types = [
-
 
363
            1  => 'NET_SFTP_INIT',
-
 
364
            2  => 'NET_SFTP_VERSION',
-
 
365
            3  => 'NET_SFTP_OPEN',
-
 
366
            4  => 'NET_SFTP_CLOSE',
-
 
367
            5  => 'NET_SFTP_READ',
-
 
368
            6  => 'NET_SFTP_WRITE',
-
 
369
            7  => 'NET_SFTP_LSTAT',
-
 
370
            9  => 'NET_SFTP_SETSTAT',
-
 
371
            10 => 'NET_SFTP_FSETSTAT',
-
 
372
            11 => 'NET_SFTP_OPENDIR',
-
 
373
            12 => 'NET_SFTP_READDIR',
-
 
374
            13 => 'NET_SFTP_REMOVE',
-
 
375
            14 => 'NET_SFTP_MKDIR',
-
 
376
            15 => 'NET_SFTP_RMDIR',
-
 
377
            16 => 'NET_SFTP_REALPATH',
-
 
378
            17 => 'NET_SFTP_STAT',
-
 
379
            18 => 'NET_SFTP_RENAME',
-
 
380
            19 => 'NET_SFTP_READLINK',
-
 
381
            20 => 'NET_SFTP_SYMLINK',
-
 
382
            21 => 'NET_SFTP_LINK',
-
 
383
 
-
 
384
            101 => 'NET_SFTP_STATUS',
-
 
385
            102 => 'NET_SFTP_HANDLE',
-
 
386
            103 => 'NET_SFTP_DATA',
-
 
387
            104 => 'NET_SFTP_NAME',
-
 
388
            105 => 'NET_SFTP_ATTRS',
-
 
389
 
-
 
390
            200 => 'NET_SFTP_EXTENDED'
-
 
391
        ];
-
 
392
        $this->status_codes = [
-
 
393
            0 => 'NET_SFTP_STATUS_OK',
-
 
394
            1 => 'NET_SFTP_STATUS_EOF',
-
 
395
            2 => 'NET_SFTP_STATUS_NO_SUCH_FILE',
-
 
396
            3 => 'NET_SFTP_STATUS_PERMISSION_DENIED',
-
 
397
            4 => 'NET_SFTP_STATUS_FAILURE',
-
 
398
            5 => 'NET_SFTP_STATUS_BAD_MESSAGE',
-
 
399
            6 => 'NET_SFTP_STATUS_NO_CONNECTION',
-
 
400
            7 => 'NET_SFTP_STATUS_CONNECTION_LOST',
-
 
401
            8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED',
-
 
402
            9 => 'NET_SFTP_STATUS_INVALID_HANDLE',
-
 
403
            10 => 'NET_SFTP_STATUS_NO_SUCH_PATH',
-
 
404
            11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS',
-
 
405
            12 => 'NET_SFTP_STATUS_WRITE_PROTECT',
-
 
406
            13 => 'NET_SFTP_STATUS_NO_MEDIA',
-
 
407
            14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM',
-
 
408
            15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED',
-
 
409
            16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL',
-
 
410
            17 => 'NET_SFTP_STATUS_LOCK_CONFLICT',
-
 
411
            18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY',
-
 
412
            19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY',
-
 
413
            20 => 'NET_SFTP_STATUS_INVALID_FILENAME',
-
 
414
            21 => 'NET_SFTP_STATUS_LINK_LOOP',
-
 
415
            22 => 'NET_SFTP_STATUS_CANNOT_DELETE',
-
 
416
            23 => 'NET_SFTP_STATUS_INVALID_PARAMETER',
-
 
417
            24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY',
-
 
418
            25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT',
-
 
419
            26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED',
-
 
420
            27 => 'NET_SFTP_STATUS_DELETE_PENDING',
-
 
421
            28 => 'NET_SFTP_STATUS_FILE_CORRUPT',
-
 
422
            29 => 'NET_SFTP_STATUS_OWNER_INVALID',
-
 
423
            30 => 'NET_SFTP_STATUS_GROUP_INVALID',
-
 
424
            31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK'
-
 
425
        ];
-
 
426
        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1
-
 
427
        // the order, in this case, matters quite a lot - see \phpseclib3\Net\SFTP::_parseAttributes() to understand why
-
 
428
        $this->attributes = [
-
 
429
            0x00000001 => 'NET_SFTP_ATTR_SIZE',
-
 
430
            0x00000002 => 'NET_SFTP_ATTR_UIDGID',          // defined in SFTPv3, removed in SFTPv4+
-
 
431
            0x00000080 => 'NET_SFTP_ATTR_OWNERGROUP',      // defined in SFTPv4+
-
 
432
            0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
-
 
433
            0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
-
 
434
            0x00000010 => 'NET_SFTP_ATTR_CREATETIME',      // SFTPv4+
-
 
435
            0x00000020 => 'NET_SFTP_ATTR_MODIFYTIME',
-
 
436
            0x00000040 => 'NET_SFTP_ATTR_ACL',
-
 
437
            0x00000100 => 'NET_SFTP_ATTR_SUBSECOND_TIMES',
-
 
438
            0x00000200 => 'NET_SFTP_ATTR_BITS',            // SFTPv5+
-
 
439
            0x00000400 => 'NET_SFTP_ATTR_ALLOCATION_SIZE', // SFTPv6+
-
 
440
            0x00000800 => 'NET_SFTP_ATTR_TEXT_HINT',
-
 
441
            0x00001000 => 'NET_SFTP_ATTR_MIME_TYPE',
-
 
442
            0x00002000 => 'NET_SFTP_ATTR_LINK_COUNT',
-
 
443
            0x00004000 => 'NET_SFTP_ATTR_UNTRANSLATED_NAME',
-
 
444
            0x00008000 => 'NET_SFTP_ATTR_CTIME',
-
 
445
            // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers
-
 
446
            // yields inconsistent behavior depending on how php is compiled.  so we left shift -1 (which, in
-
 
447
            // two's compliment, consists of all 1 bits) by 31.  on 64-bit systems this'll yield 0xFFFFFFFF80000000.
-
 
448
            // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored.
-
 
449
            (-1 << 31) & 0xFFFFFFFF => 'NET_SFTP_ATTR_EXTENDED'
-
 
450
        ];
-
 
451
        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3
-
 
452
        // the flag definitions change somewhat in SFTPv5+.  if SFTPv5+ support is added to this library, maybe name
-
 
453
        // the array for that $this->open5_flags and similarly alter the constant names.
-
 
454
        $this->open_flags = [
-
 
455
            0x00000001 => 'NET_SFTP_OPEN_READ',
-
 
456
            0x00000002 => 'NET_SFTP_OPEN_WRITE',
-
 
457
            0x00000004 => 'NET_SFTP_OPEN_APPEND',
-
 
458
            0x00000008 => 'NET_SFTP_OPEN_CREATE',
-
 
459
            0x00000010 => 'NET_SFTP_OPEN_TRUNCATE',
-
 
460
            0x00000020 => 'NET_SFTP_OPEN_EXCL',
-
 
461
            0x00000040 => 'NET_SFTP_OPEN_TEXT' // defined in SFTPv4
-
 
462
        ];
-
 
463
        // SFTPv5+ changed the flags up:
-
 
464
        // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-8.1.1.3
-
 
465
        $this->open_flags5 = [
-
 
466
            // when SSH_FXF_ACCESS_DISPOSITION is a 3 bit field that controls how the file is opened
-
 
467
            0x00000000 => 'NET_SFTP_OPEN_CREATE_NEW',
-
 
468
            0x00000001 => 'NET_SFTP_OPEN_CREATE_TRUNCATE',
-
 
469
            0x00000002 => 'NET_SFTP_OPEN_OPEN_EXISTING',
-
 
470
            0x00000003 => 'NET_SFTP_OPEN_OPEN_OR_CREATE',
-
 
471
            0x00000004 => 'NET_SFTP_OPEN_TRUNCATE_EXISTING',
-
 
472
            // the rest of the flags are not supported
-
 
473
            0x00000008 => 'NET_SFTP_OPEN_APPEND_DATA', // "the offset field of SS_FXP_WRITE requests is ignored"
-
 
474
            0x00000010 => 'NET_SFTP_OPEN_APPEND_DATA_ATOMIC',
-
 
475
            0x00000020 => 'NET_SFTP_OPEN_TEXT_MODE',
-
 
476
            0x00000040 => 'NET_SFTP_OPEN_BLOCK_READ',
-
 
477
            0x00000080 => 'NET_SFTP_OPEN_BLOCK_WRITE',
-
 
478
            0x00000100 => 'NET_SFTP_OPEN_BLOCK_DELETE',
-
 
479
            0x00000200 => 'NET_SFTP_OPEN_BLOCK_ADVISORY',
-
 
480
            0x00000400 => 'NET_SFTP_OPEN_NOFOLLOW',
-
 
481
            0x00000800 => 'NET_SFTP_OPEN_DELETE_ON_CLOSE',
-
 
482
            0x00001000 => 'NET_SFTP_OPEN_ACCESS_AUDIT_ALARM_INFO',
-
 
483
            0x00002000 => 'NET_SFTP_OPEN_ACCESS_BACKUP',
-
 
484
            0x00004000 => 'NET_SFTP_OPEN_BACKUP_STREAM',
-
 
485
            0x00008000 => 'NET_SFTP_OPEN_OVERRIDE_OWNER',
-
 
486
        ];
-
 
487
        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
-
 
488
        // see \phpseclib3\Net\SFTP::_parseLongname() for an explanation
-
 
489
        $this->file_types = [
-
 
490
            1 => 'NET_SFTP_TYPE_REGULAR',
-
 
491
            2 => 'NET_SFTP_TYPE_DIRECTORY',
-
 
492
            3 => 'NET_SFTP_TYPE_SYMLINK',
-
 
493
            4 => 'NET_SFTP_TYPE_SPECIAL',
-
 
494
            5 => 'NET_SFTP_TYPE_UNKNOWN',
-
 
495
            // the following types were first defined for use in SFTPv5+
-
 
496
            // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
-
 
497
            6 => 'NET_SFTP_TYPE_SOCKET',
-
 
498
            7 => 'NET_SFTP_TYPE_CHAR_DEVICE',
-
 
499
            8 => 'NET_SFTP_TYPE_BLOCK_DEVICE',
-
 
500
            9 => 'NET_SFTP_TYPE_FIFO'
-
 
501
        ];
-
 
502
        $this->define_array(
-
 
503
            $this->packet_types,
-
 
504
            $this->status_codes,
-
 
505
            $this->attributes,
-
 
506
            $this->open_flags,
-
 
507
            $this->open_flags5,
-
 
508
            $this->file_types
-
 
509
        );
-
 
510
 
312
        if (defined('NET_SFTP_QUEUE_SIZE')) {
511
        if (!defined('NET_SFTP_QUEUE_SIZE')) {
313
            $this->queueSize = NET_SFTP_QUEUE_SIZE;
512
            define('NET_SFTP_QUEUE_SIZE', 32);
314
        }
513
        }
315
        if (defined('NET_SFTP_UPLOAD_QUEUE_SIZE')) {
514
        if (!defined('NET_SFTP_UPLOAD_QUEUE_SIZE')) {
316
            $this->uploadQueueSize = NET_SFTP_UPLOAD_QUEUE_SIZE;
515
            define('NET_SFTP_UPLOAD_QUEUE_SIZE', 1024);
317
        }
516
        }
318
    }
517
    }
319
 
518
 
320
    /**
519
    /**
321
     * Check a few things before SFTP functions are called
520
     * Check a few things before SFTP functions are called
322
     *
521
     *
323
     * @return bool
522
     * @return bool
-
 
523
     * @access public
324
     */
524
     */
325
    private function precheck()
525
    private function precheck()
326
    {
526
    {
327
        if (!($this->bitmap & SSH2::MASK_LOGIN)) {
527
        if (!($this->bitmap & SSH2::MASK_LOGIN)) {
328
            return false;
528
            return false;
Line 338... Line 538...
338
    /**
538
    /**
339
     * Partially initialize an SFTP connection
539
     * Partially initialize an SFTP connection
340
     *
540
     *
341
     * @throws \UnexpectedValueException on receipt of unexpected packets
541
     * @throws \UnexpectedValueException on receipt of unexpected packets
342
     * @return bool
542
     * @return bool
-
 
543
     * @access public
343
     */
544
     */
344
    private function partial_init_sftp_connection()
545
    private function partial_init_sftp_connection()
345
    {
546
    {
346
        $this->window_size_server_to_client[self::CHANNEL] = $this->window_size;
547
        $this->window_size_server_to_client[self::CHANNEL] = $this->window_size;
347
 
548
 
348
        $packet = Strings::packSSH2(
549
        $packet = Strings::packSSH2(
349
            'CsN3',
550
            'CsN3',
350
            SSH2MessageType::CHANNEL_OPEN,
551
            NET_SSH2_MSG_CHANNEL_OPEN,
351
            'session',
552
            'session',
352
            self::CHANNEL,
553
            self::CHANNEL,
353
            $this->window_size,
554
            $this->window_size,
354
            0x4000
555
            0x4000
355
        );
556
        );
356
 
557
 
357
        $this->send_binary_packet($packet);
558
        $this->send_binary_packet($packet);
358
 
559
 
359
        $this->channel_status[self::CHANNEL] = SSH2MessageType::CHANNEL_OPEN;
560
        $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN;
360
 
561
 
361
        $response = $this->get_channel_packet(self::CHANNEL, true);
562
        $response = $this->get_channel_packet(self::CHANNEL, true);
362
        if ($response === true && $this->isTimeout()) {
563
        if ($response === true && $this->isTimeout()) {
363
            return false;
564
            return false;
364
        }
565
        }
365
 
566
 
366
        $packet = Strings::packSSH2(
567
        $packet = Strings::packSSH2(
367
            'CNsbs',
568
            'CNsbs',
368
            SSH2MessageType::CHANNEL_REQUEST,
569
            NET_SSH2_MSG_CHANNEL_REQUEST,
369
            $this->server_channels[self::CHANNEL],
570
            $this->server_channels[self::CHANNEL],
370
            'subsystem',
571
            'subsystem',
371
            true,
572
            true,
372
            'sftp'
573
            'sftp'
373
        );
574
        );
374
        $this->send_binary_packet($packet);
575
        $this->send_binary_packet($packet);
375
 
576
 
376
        $this->channel_status[self::CHANNEL] = SSH2MessageType::CHANNEL_REQUEST;
577
        $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST;
377
 
578
 
378
        $response = $this->get_channel_packet(self::CHANNEL, true);
579
        $response = $this->get_channel_packet(self::CHANNEL, true);
379
        if ($response === false) {
580
        if ($response === false) {
380
            // from PuTTY's psftp.exe
581
            // from PuTTY's psftp.exe
381
            $command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" .
582
            $command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" .
Line 383... Line 584...
383
                       "exec sftp-server";
584
                       "exec sftp-server";
384
            // we don't do $this->exec($command, false) because exec() operates on a different channel and plus the SSH_MSG_CHANNEL_OPEN that exec() does
585
            // we don't do $this->exec($command, false) because exec() operates on a different channel and plus the SSH_MSG_CHANNEL_OPEN that exec() does
385
            // is redundant
586
            // is redundant
386
            $packet = Strings::packSSH2(
587
            $packet = Strings::packSSH2(
387
                'CNsCs',
588
                'CNsCs',
388
                SSH2MessageType::CHANNEL_REQUEST,
589
                NET_SSH2_MSG_CHANNEL_REQUEST,
389
                $this->server_channels[self::CHANNEL],
590
                $this->server_channels[self::CHANNEL],
390
                'exec',
591
                'exec',
391
                1,
592
                1,
392
                $command
593
                $command
393
            );
594
            );
394
            $this->send_binary_packet($packet);
595
            $this->send_binary_packet($packet);
395
 
596
 
396
            $this->channel_status[self::CHANNEL] = SSH2MessageType::CHANNEL_REQUEST;
597
            $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST;
397
 
598
 
398
            $response = $this->get_channel_packet(self::CHANNEL, true);
599
            $response = $this->get_channel_packet(self::CHANNEL, true);
399
            if ($response === false) {
600
            if ($response === false) {
400
                return false;
601
                return false;
401
            }
602
            }
402
        } elseif ($response === true && $this->isTimeout()) {
603
        } elseif ($response === true && $this->isTimeout()) {
403
            return false;
604
            return false;
404
        }
605
        }
405
 
606
 
406
        $this->channel_status[self::CHANNEL] = SSH2MessageType::CHANNEL_DATA;
607
        $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA;
407
        $this->send_sftp_packet(SFTPPacketType::INIT, "\0\0\0\3");
608
        $this->send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3");
408
 
609
 
409
        $response = $this->get_sftp_packet();
610
        $response = $this->get_sftp_packet();
410
        if ($this->packet_type != SFTPPacketType::VERSION) {
611
        if ($this->packet_type != NET_SFTP_VERSION) {
411
            throw new \UnexpectedValueException('Expected PacketType::VERSION. '
612
            throw new \UnexpectedValueException('Expected NET_SFTP_VERSION. '
412
                                              . 'Got packet type: ' . $this->packet_type);
613
                                              . 'Got packet type: ' . $this->packet_type);
413
        }
614
        }
414
 
615
 
415
        $this->use_request_id = true;
616
        $this->use_request_id = true;
416
 
617
 
Line 427... Line 628...
427
 
628
 
428
    /**
629
    /**
429
     * (Re)initializes the SFTP channel
630
     * (Re)initializes the SFTP channel
430
     *
631
     *
431
     * @return bool
632
     * @return bool
-
 
633
     * @access private
432
     */
634
     */
433
    private function init_sftp_connection()
635
    private function init_sftp_connection()
434
    {
636
    {
435
        if (!$this->partial_init && !$this->partial_init_sftp_connection()) {
637
        if (!$this->partial_init && !$this->partial_init_sftp_connection()) {
436
            return false;
638
            return false;
Line 472... Line 674...
472
                    if ($ver === $this->version) {
674
                    if ($ver === $this->version) {
473
                        break;
675
                        break;
474
                    }
676
                    }
475
                    $this->version = (int) $ver;
677
                    $this->version = (int) $ver;
476
                    $packet = Strings::packSSH2('ss', 'version-select', "$ver");
678
                    $packet = Strings::packSSH2('ss', 'version-select', "$ver");
477
                    $this->send_sftp_packet(SFTPPacketType::EXTENDED, $packet);
679
                    $this->send_sftp_packet(NET_SFTP_EXTENDED, $packet);
478
                    $response = $this->get_sftp_packet();
680
                    $response = $this->get_sftp_packet();
479
                    if ($this->packet_type != SFTPPacketType::STATUS) {
681
                    if ($this->packet_type != NET_SFTP_STATUS) {
480
                        throw new \UnexpectedValueException('Expected PacketType::STATUS. '
682
                        throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
481
                            . 'Got packet type: ' . $this->packet_type);
683
                            . 'Got packet type: ' . $this->packet_type);
482
                    }
684
                    }
483
                    list($status) = Strings::unpackSSH2('N', $response);
685
                    list($status) = Strings::unpackSSH2('N', $response);
484
                    if ($status != StatusCode::OK) {
686
                    if ($status != NET_SFTP_STATUS_OK) {
485
                        $this->logError($response, $status);
687
                        $this->logError($response, $status);
486
                        throw new \UnexpectedValueException('Expected StatusCode::OK. '
688
                        throw new \UnexpectedValueException('Expected NET_SFTP_STATUS_OK. '
487
                            . ' Got ' . $status);
689
                            . ' Got ' . $status);
488
                    }
690
                    }
489
                    break;
691
                    break;
490
                }
692
                }
491
            }
693
            }
Line 517... Line 719...
517
    }
719
    }
518
 
720
 
519
    /**
721
    /**
520
     * Disable the stat cache
722
     * Disable the stat cache
521
     *
723
     *
-
 
724
     * @access public
522
     */
725
     */
523
    public function disableStatCache()
726
    public function disableStatCache()
524
    {
727
    {
525
        $this->use_stat_cache = false;
728
        $this->use_stat_cache = false;
526
    }
729
    }
527
 
730
 
528
    /**
731
    /**
529
     * Enable the stat cache
732
     * Enable the stat cache
530
     *
733
     *
-
 
734
     * @access public
531
     */
735
     */
532
    public function enableStatCache()
736
    public function enableStatCache()
533
    {
737
    {
534
        $this->use_stat_cache = true;
738
        $this->use_stat_cache = true;
535
    }
739
    }
536
 
740
 
537
    /**
741
    /**
538
     * Clear the stat cache
742
     * Clear the stat cache
539
     *
743
     *
-
 
744
     * @access public
540
     */
745
     */
541
    public function clearStatCache()
746
    public function clearStatCache()
542
    {
747
    {
543
        $this->stat_cache = [];
748
        $this->stat_cache = [];
544
    }
749
    }
545
 
750
 
546
    /**
751
    /**
547
     * Enable path canonicalization
752
     * Enable path canonicalization
548
     *
753
     *
-
 
754
     * @access public
549
     */
755
     */
550
    public function enablePathCanonicalization()
756
    public function enablePathCanonicalization()
551
    {
757
    {
552
        $this->canonicalize_paths = true;
758
        $this->canonicalize_paths = true;
553
    }
759
    }
554
 
760
 
555
    /**
761
    /**
556
     * Enable path canonicalization
762
     * Enable path canonicalization
557
     *
763
     *
-
 
764
     * @access public
558
     */
765
     */
559
    public function disablePathCanonicalization()
766
    public function disablePathCanonicalization()
560
    {
767
    {
561
        $this->canonicalize_paths = false;
768
        $this->canonicalize_paths = false;
562
    }
769
    }
563
 
770
 
564
    /**
771
    /**
565
     * Enable arbitrary length packets
772
     * Enable arbitrary length packets
566
     *
773
     *
-
 
774
     * @access public
567
     */
775
     */
568
    public function enableArbitraryLengthPackets()
776
    public function enableArbitraryLengthPackets()
569
    {
777
    {
570
        $this->allow_arbitrary_length_packets = true;
778
        $this->allow_arbitrary_length_packets = true;
571
    }
779
    }
572
 
780
 
573
    /**
781
    /**
574
     * Disable arbitrary length packets
782
     * Disable arbitrary length packets
575
     *
783
     *
-
 
784
     * @access public
576
     */
785
     */
577
    public function disableArbitraryLengthPackets()
786
    public function disableArbitraryLengthPackets()
578
    {
787
    {
579
        $this->allow_arbitrary_length_packets = false;
788
        $this->allow_arbitrary_length_packets = false;
580
    }
789
    }
581
 
790
 
582
    /**
791
    /**
583
     * Returns the current directory name
792
     * Returns the current directory name
584
     *
793
     *
585
     * @return string|bool
794
     * @return string|bool
-
 
795
     * @access public
586
     */
796
     */
587
    public function pwd()
797
    public function pwd()
588
    {
798
    {
589
        if (!$this->precheck()) {
799
        if (!$this->precheck()) {
590
            return false;
800
            return false;
Line 596... Line 806...
596
    /**
806
    /**
597
     * Logs errors
807
     * Logs errors
598
     *
808
     *
599
     * @param string $response
809
     * @param string $response
600
     * @param int $status
810
     * @param int $status
-
 
811
     * @access private
601
     */
812
     */
602
    private function logError($response, $status = -1)
813
    private function logError($response, $status = -1)
603
    {
814
    {
604
        if ($status == -1) {
815
        if ($status == -1) {
605
            list($status) = Strings::unpackSSH2('N', $response);
816
            list($status) = Strings::unpackSSH2('N', $response);
606
        }
817
        }
607
 
818
 
608
        $error = StatusCode::getConstantNameByValue($status);
819
        $error = $this->status_codes[$status];
609
 
820
 
610
        if ($this->version > 2) {
821
        if ($this->version > 2) {
611
            list($message) = Strings::unpackSSH2('s', $response);
822
            list($message) = Strings::unpackSSH2('s', $response);
612
            $this->sftp_errors[] = "$error: $message";
823
            $this->sftp_errors[] = "$error: $message";
613
        } else {
824
        } else {
Line 626... Line 837...
626
     * @see self::chdir()
837
     * @see self::chdir()
627
     * @see self::disablePathCanonicalization()
838
     * @see self::disablePathCanonicalization()
628
     * @param string $path
839
     * @param string $path
629
     * @throws \UnexpectedValueException on receipt of unexpected packets
840
     * @throws \UnexpectedValueException on receipt of unexpected packets
630
     * @return mixed
841
     * @return mixed
-
 
842
     * @access public
631
     */
843
     */
632
    public function realpath($path)
844
    public function realpath($path)
633
    {
845
    {
634
        if ($this->precheck() === false) {
846
        if ($this->precheck() === false) {
635
            return false;
847
            return false;
Line 639... Line 851...
639
            return $path;
851
            return $path;
640
        }
852
        }
641
 
853
 
642
        if ($this->pwd === true) {
854
        if ($this->pwd === true) {
643
            // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9
855
            // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9
644
            $this->send_sftp_packet(SFTPPacketType::REALPATH, Strings::packSSH2('s', $path));
856
            $this->send_sftp_packet(NET_SFTP_REALPATH, Strings::packSSH2('s', $path));
645
 
857
 
646
            $response = $this->get_sftp_packet();
858
            $response = $this->get_sftp_packet();
647
            switch ($this->packet_type) {
859
            switch ($this->packet_type) {
648
                case SFTPPacketType::NAME:
860
                case NET_SFTP_NAME:
649
                    // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following
861
                    // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following
650
                    // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks
862
                    // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks
651
                    // at is the first part and that part is defined the same in SFTP versions 3 through 6.
863
                    // at is the first part and that part is defined the same in SFTP versions 3 through 6.
652
                    list(, $filename) = Strings::unpackSSH2('Ns', $response);
864
                    list(, $filename) = Strings::unpackSSH2('Ns', $response);
653
                    return $filename;
865
                    return $filename;
654
                case SFTPPacketType::STATUS:
866
                case NET_SFTP_STATUS:
655
                    $this->logError($response);
867
                    $this->logError($response);
656
                    return false;
868
                    return false;
657
                default:
869
                default:
658
                    throw new \UnexpectedValueException('Expected PacketType::NAME or PacketType::STATUS. '
870
                    throw new \UnexpectedValueException('Expected NET_SFTP_NAME or NET_SFTP_STATUS. '
659
                                                      . 'Got packet type: ' . $this->packet_type);
871
                                                      . 'Got packet type: ' . $this->packet_type);
660
            }
872
            }
661
        }
873
        }
662
 
874
 
663
        if (!strlen($path) || $path[0] != '/') {
875
        if (!strlen($path) || $path[0] != '/') {
Line 688... Line 900...
688
     * Changes the current directory
900
     * Changes the current directory
689
     *
901
     *
690
     * @param string $dir
902
     * @param string $dir
691
     * @throws \UnexpectedValueException on receipt of unexpected packets
903
     * @throws \UnexpectedValueException on receipt of unexpected packets
692
     * @return bool
904
     * @return bool
-
 
905
     * @access public
693
     */
906
     */
694
    public function chdir($dir)
907
    public function chdir($dir)
695
    {
908
    {
696
        if (!$this->precheck()) {
909
        if (!$this->precheck()) {
697
            return false;
910
            return false;
Line 716... Line 929...
716
        // we could do a stat on the alleged $dir to see if it's a directory but that doesn't tell us
929
        // we could do a stat on the alleged $dir to see if it's a directory but that doesn't tell us
717
        // the currently logged in user has the appropriate permissions or not. maybe you could see if
930
        // the currently logged in user has the appropriate permissions or not. maybe you could see if
718
        // the file's uid / gid match the currently logged in user's uid / gid but how there's no easy
931
        // the file's uid / gid match the currently logged in user's uid / gid but how there's no easy
719
        // way to get those with SFTP
932
        // way to get those with SFTP
720
 
933
 
721
        $this->send_sftp_packet(SFTPPacketType::OPENDIR, Strings::packSSH2('s', $dir));
934
        $this->send_sftp_packet(NET_SFTP_OPENDIR, Strings::packSSH2('s', $dir));
722
 
935
 
723
        // see \phpseclib3\Net\SFTP::nlist() for a more thorough explanation of the following
936
        // see \phpseclib3\Net\SFTP::nlist() for a more thorough explanation of the following
724
        $response = $this->get_sftp_packet();
937
        $response = $this->get_sftp_packet();
725
        switch ($this->packet_type) {
938
        switch ($this->packet_type) {
726
            case SFTPPacketType::HANDLE:
939
            case NET_SFTP_HANDLE:
727
                $handle = substr($response, 4);
940
                $handle = substr($response, 4);
728
                break;
941
                break;
729
            case SFTPPacketType::STATUS:
942
            case NET_SFTP_STATUS:
730
                $this->logError($response);
943
                $this->logError($response);
731
                return false;
944
                return false;
732
            default:
945
            default:
733
                throw new \UnexpectedValueException('Expected PacketType::HANDLE or PacketType::STATUS' .
946
                throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS' .
734
                                                    'Got packet type: ' . $this->packet_type);
947
                                                    'Got packet type: ' . $this->packet_type);
735
        }
948
        }
736
 
949
 
737
        if (!$this->close_handle($handle)) {
950
        if (!$this->close_handle($handle)) {
738
            return false;
951
            return false;
Line 748... Line 961...
748
     * Returns a list of files in the given directory
961
     * Returns a list of files in the given directory
749
     *
962
     *
750
     * @param string $dir
963
     * @param string $dir
751
     * @param bool $recursive
964
     * @param bool $recursive
752
     * @return array|false
965
     * @return array|false
-
 
966
     * @access public
753
     */
967
     */
754
    public function nlist($dir = '.', $recursive = false)
968
    public function nlist($dir = '.', $recursive = false)
755
    {
969
    {
756
        return $this->nlist_helper($dir, $recursive, '');
970
        return $this->nlist_helper($dir, $recursive, '');
757
    }
971
    }
Line 761... Line 975...
761
     *
975
     *
762
     * @param string $dir
976
     * @param string $dir
763
     * @param bool $recursive
977
     * @param bool $recursive
764
     * @param string $relativeDir
978
     * @param string $relativeDir
765
     * @return array|false
979
     * @return array|false
-
 
980
     * @access private
766
     */
981
     */
767
    private function nlist_helper($dir, $recursive, $relativeDir)
982
    private function nlist_helper($dir, $recursive, $relativeDir)
768
    {
983
    {
769
        $files = $this->readlist($dir, false);
984
        $files = $this->readlist($dir, false);
770
 
985
 
Line 794... Line 1009...
794
     * Returns a detailed list of files in the given directory
1009
     * Returns a detailed list of files in the given directory
795
     *
1010
     *
796
     * @param string $dir
1011
     * @param string $dir
797
     * @param bool $recursive
1012
     * @param bool $recursive
798
     * @return array|false
1013
     * @return array|false
-
 
1014
     * @access public
799
     */
1015
     */
800
    public function rawlist($dir = '.', $recursive = false)
1016
    public function rawlist($dir = '.', $recursive = false)
801
    {
1017
    {
802
        $files = $this->readlist($dir, true);
1018
        $files = $this->readlist($dir, true);
803
        if (!$recursive || $files === false) {
1019
        if (!$recursive || $files === false) {
Line 815... Line 1031...
815
            if ($key != '.' && $key != '..') {
1031
            if ($key != '.' && $key != '..') {
816
                if ($this->use_stat_cache) {
1032
                if ($this->use_stat_cache) {
817
                    $is_directory = is_array($this->query_stat_cache($this->realpath($dir . '/' . $key)));
1033
                    $is_directory = is_array($this->query_stat_cache($this->realpath($dir . '/' . $key)));
818
                } else {
1034
                } else {
819
                    $stat = $this->lstat($dir . '/' . $key);
1035
                    $stat = $this->lstat($dir . '/' . $key);
820
                    $is_directory = $stat && $stat['type'] === FileType::DIRECTORY;
1036
                    $is_directory = $stat && $stat['type'] === NET_SFTP_TYPE_DIRECTORY;
821
                }
1037
                }
822
            }
1038
            }
823
 
1039
 
824
            if ($is_directory) {
1040
            if ($is_directory) {
825
                $depth++;
1041
                $depth++;
Line 838... Line 1054...
838
     *
1054
     *
839
     * @param string $dir
1055
     * @param string $dir
840
     * @param bool $raw
1056
     * @param bool $raw
841
     * @return array|false
1057
     * @return array|false
842
     * @throws \UnexpectedValueException on receipt of unexpected packets
1058
     * @throws \UnexpectedValueException on receipt of unexpected packets
-
 
1059
     * @access private
843
     */
1060
     */
844
    private function readlist($dir, $raw = true)
1061
    private function readlist($dir, $raw = true)
845
    {
1062
    {
846
        if (!$this->precheck()) {
1063
        if (!$this->precheck()) {
847
            return false;
1064
            return false;
Line 851... Line 1068...
851
        if ($dir === false) {
1068
        if ($dir === false) {
852
            return false;
1069
            return false;
853
        }
1070
        }
854
 
1071
 
855
        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2
1072
        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2
856
        $this->send_sftp_packet(SFTPPacketType::OPENDIR, Strings::packSSH2('s', $dir));
1073
        $this->send_sftp_packet(NET_SFTP_OPENDIR, Strings::packSSH2('s', $dir));
857
 
1074
 
858
        $response = $this->get_sftp_packet();
1075
        $response = $this->get_sftp_packet();
859
        switch ($this->packet_type) {
1076
        switch ($this->packet_type) {
860
            case SFTPPacketType::HANDLE:
1077
            case NET_SFTP_HANDLE:
861
                // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2
1078
                // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2
862
                // since 'handle' is the last field in the SSH_FXP_HANDLE packet, we'll just remove the first four bytes that
1079
                // since 'handle' is the last field in the SSH_FXP_HANDLE packet, we'll just remove the first four bytes that
863
                // represent the length of the string and leave it at that
1080
                // represent the length of the string and leave it at that
864
                $handle = substr($response, 4);
1081
                $handle = substr($response, 4);
865
                break;
1082
                break;
866
            case SFTPPacketType::STATUS:
1083
            case NET_SFTP_STATUS:
867
                // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
1084
                // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
868
                $this->logError($response);
1085
                $this->logError($response);
869
                return false;
1086
                return false;
870
            default:
1087
            default:
871
                throw new \UnexpectedValueException('Expected PacketType::HANDLE or PacketType::STATUS. '
1088
                throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
872
                                                  . 'Got packet type: ' . $this->packet_type);
1089
                                                  . 'Got packet type: ' . $this->packet_type);
873
        }
1090
        }
874
 
1091
 
875
        $this->update_stat_cache($dir, []);
1092
        $this->update_stat_cache($dir, []);
876
 
1093
 
877
        $contents = [];
1094
        $contents = [];
878
        while (true) {
1095
        while (true) {
879
            // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2
1096
            // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2
880
            // why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many
1097
            // why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many
881
            // SSH_MSG_CHANNEL_DATA messages is not known to me.
1098
            // SSH_MSG_CHANNEL_DATA messages is not known to me.
882
            $this->send_sftp_packet(SFTPPacketType::READDIR, Strings::packSSH2('s', $handle));
1099
            $this->send_sftp_packet(NET_SFTP_READDIR, Strings::packSSH2('s', $handle));
883
 
1100
 
884
            $response = $this->get_sftp_packet();
1101
            $response = $this->get_sftp_packet();
885
            switch ($this->packet_type) {
1102
            switch ($this->packet_type) {
886
                case SFTPPacketType::NAME:
1103
                case NET_SFTP_NAME:
887
                    list($count) = Strings::unpackSSH2('N', $response);
1104
                    list($count) = Strings::unpackSSH2('N', $response);
888
                    for ($i = 0; $i < $count; $i++) {
1105
                    for ($i = 0; $i < $count; $i++) {
889
                        list($shortname) = Strings::unpackSSH2('s', $response);
1106
                        list($shortname) = Strings::unpackSSH2('s', $response);
890
                        // SFTPv4 "removed the long filename from the names structure-- it can now be
1107
                        // SFTPv4 "removed the long filename from the names structure-- it can now be
891
                        //         built from information available in the attrs structure."
1108
                        //         built from information available in the attrs structure."
Line 899... Line 1116...
899
                                $attributes['type'] = $fileType;
1116
                                $attributes['type'] = $fileType;
900
                            }
1117
                            }
901
                        }
1118
                        }
902
                        $contents[$shortname] = $attributes + ['filename' => $shortname];
1119
                        $contents[$shortname] = $attributes + ['filename' => $shortname];
903
 
1120
 
904
                        if (isset($attributes['type']) && $attributes['type'] == FileType::DIRECTORY && ($shortname != '.' && $shortname != '..')) {
1121
                        if (isset($attributes['type']) && $attributes['type'] == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) {
905
                            $this->update_stat_cache($dir . '/' . $shortname, []);
1122
                            $this->update_stat_cache($dir . '/' . $shortname, []);
906
                        } else {
1123
                        } else {
907
                            if ($shortname == '..') {
1124
                            if ($shortname == '..') {
908
                                $temp = $this->realpath($dir . '/..') . '/.';
1125
                                $temp = $this->realpath($dir . '/..') . '/.';
909
                            } else {
1126
                            } else {
Line 913... Line 1130...
913
                        }
1130
                        }
914
                        // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the
1131
                        // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the
915
                        // final SSH_FXP_STATUS packet should tell us that, already.
1132
                        // final SSH_FXP_STATUS packet should tell us that, already.
916
                    }
1133
                    }
917
                    break;
1134
                    break;
918
                case SFTPPacketType::STATUS:
1135
                case NET_SFTP_STATUS:
919
                    list($status) = Strings::unpackSSH2('N', $response);
1136
                    list($status) = Strings::unpackSSH2('N', $response);
920
                    if ($status != StatusCode::EOF) {
1137
                    if ($status != NET_SFTP_STATUS_EOF) {
921
                        $this->logError($response, $status);
1138
                        $this->logError($response, $status);
922
                        return false;
1139
                        return false;
923
                    }
1140
                    }
924
                    break 2;
1141
                    break 2;
925
                default:
1142
                default:
926
                    throw new \UnexpectedValueException('Expected PacketType::NAME or PacketType::STATUS. '
1143
                    throw new \UnexpectedValueException('Expected NET_SFTP_NAME or NET_SFTP_STATUS. '
927
                                                      . 'Got packet type: ' . $this->packet_type);
1144
                                                      . 'Got packet type: ' . $this->packet_type);
928
            }
1145
            }
929
        }
1146
        }
930
 
1147
 
931
        if (!$this->close_handle($handle)) {
1148
        if (!$this->close_handle($handle)) {
Line 945... Line 1162...
945
     * Intended for use with uasort()
1162
     * Intended for use with uasort()
946
     *
1163
     *
947
     * @param array $a
1164
     * @param array $a
948
     * @param array $b
1165
     * @param array $b
949
     * @return int
1166
     * @return int
-
 
1167
     * @access private
950
     */
1168
     */
951
    private function comparator($a, $b)
1169
    private function comparator($a, $b)
952
    {
1170
    {
953
        switch (true) {
1171
        switch (true) {
954
            case $a['filename'] === '.' || $b['filename'] === '.':
1172
            case $a['filename'] === '.' || $b['filename'] === '.':
Line 959... Line 1177...
959
            case $a['filename'] === '..' || $b['filename'] === '..':
1177
            case $a['filename'] === '..' || $b['filename'] === '..':
960
                if ($a['filename'] === $b['filename']) {
1178
                if ($a['filename'] === $b['filename']) {
961
                    return 0;
1179
                    return 0;
962
                }
1180
                }
963
                return $a['filename'] === '..' ? -1 : 1;
1181
                return $a['filename'] === '..' ? -1 : 1;
964
            case isset($a['type']) && $a['type'] === FileType::DIRECTORY:
1182
            case isset($a['type']) && $a['type'] === NET_SFTP_TYPE_DIRECTORY:
965
                if (!isset($b['type'])) {
1183
                if (!isset($b['type'])) {
966
                    return 1;
1184
                    return 1;
967
                }
1185
                }
968
                if ($b['type'] !== $a['type']) {
1186
                if ($b['type'] !== $a['type']) {
969
                    return -1;
1187
                    return -1;
970
                }
1188
                }
971
                break;
1189
                break;
972
            case isset($b['type']) && $b['type'] === FileType::DIRECTORY:
1190
            case isset($b['type']) && $b['type'] === NET_SFTP_TYPE_DIRECTORY:
973
                return 1;
1191
                return 1;
974
        }
1192
        }
975
        foreach ($this->sortOptions as $sort => $order) {
1193
        foreach ($this->sortOptions as $sort => $order) {
976
            if (!isset($a[$sort]) || !isset($b[$sort])) {
1194
            if (!isset($a[$sort]) || !isset($b[$sort])) {
977
                if (isset($a[$sort])) {
1195
                if (isset($a[$sort])) {
Line 1019... Line 1237...
1019
     *    Separates directories from files but doesn't do any sorting beyond that
1237
     *    Separates directories from files but doesn't do any sorting beyond that
1020
     * $sftp->setListOrder();
1238
     * $sftp->setListOrder();
1021
     *    Don't do any sort of sorting
1239
     *    Don't do any sort of sorting
1022
     *
1240
     *
1023
     * @param string ...$args
1241
     * @param string ...$args
-
 
1242
     * @access public
1024
     */
1243
     */
1025
    public function setListOrder(...$args)
1244
    public function setListOrder(...$args)
1026
    {
1245
    {
1027
        $this->sortOptions = [];
1246
        $this->sortOptions = [];
1028
        if (empty($args)) {
1247
        if (empty($args)) {
Line 1040... Line 1259...
1040
    /**
1259
    /**
1041
     * Save files / directories to cache
1260
     * Save files / directories to cache
1042
     *
1261
     *
1043
     * @param string $path
1262
     * @param string $path
1044
     * @param mixed $value
1263
     * @param mixed $value
-
 
1264
     * @access private
1045
     */
1265
     */
1046
    private function update_stat_cache($path, $value)
1266
    private function update_stat_cache($path, $value)
1047
    {
1267
    {
1048
        if ($this->use_stat_cache === false) {
1268
        if ($this->use_stat_cache === false) {
1049
            return;
1269
            return;
Line 1083... Line 1303...
1083
    /**
1303
    /**
1084
     * Remove files / directories from cache
1304
     * Remove files / directories from cache
1085
     *
1305
     *
1086
     * @param string $path
1306
     * @param string $path
1087
     * @return bool
1307
     * @return bool
-
 
1308
     * @access private
1088
     */
1309
     */
1089
    private function remove_from_stat_cache($path)
1310
    private function remove_from_stat_cache($path)
1090
    {
1311
    {
1091
        $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));
1312
        $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));
1092
 
1313
 
Line 1112... Line 1333...
1112
     *
1333
     *
1113
     * Mainly used by file_exists
1334
     * Mainly used by file_exists
1114
     *
1335
     *
1115
     * @param string $path
1336
     * @param string $path
1116
     * @return mixed
1337
     * @return mixed
-
 
1338
     * @access private
1117
     */
1339
     */
1118
    private function query_stat_cache($path)
1340
    private function query_stat_cache($path)
1119
    {
1341
    {
1120
        $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));
1342
        $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));
1121
 
1343
 
Line 1137... Line 1359...
1137
     *
1359
     *
1138
     * Returns an array on success and false otherwise.
1360
     * Returns an array on success and false otherwise.
1139
     *
1361
     *
1140
     * @param string $filename
1362
     * @param string $filename
1141
     * @return array|false
1363
     * @return array|false
-
 
1364
     * @access public
1142
     */
1365
     */
1143
    public function stat($filename)
1366
    public function stat($filename)
1144
    {
1367
    {
1145
        if (!$this->precheck()) {
1368
        if (!$this->precheck()) {
1146
            return false;
1369
            return false;
Line 1159... Line 1382...
1159
            if (is_object($result) && isset($result->stat)) {
1382
            if (is_object($result) && isset($result->stat)) {
1160
                return $result->stat;
1383
                return $result->stat;
1161
            }
1384
            }
1162
        }
1385
        }
1163
 
1386
 
1164
        $stat = $this->stat_helper($filename, SFTPPacketType::STAT);
1387
        $stat = $this->stat_helper($filename, NET_SFTP_STAT);
1165
        if ($stat === false) {
1388
        if ($stat === false) {
1166
            $this->remove_from_stat_cache($filename);
1389
            $this->remove_from_stat_cache($filename);
1167
            return false;
1390
            return false;
1168
        }
1391
        }
1169
        if (isset($stat['type'])) {
1392
        if (isset($stat['type'])) {
1170
            if ($stat['type'] == FileType::DIRECTORY) {
1393
            if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) {
1171
                $filename .= '/.';
1394
                $filename .= '/.';
1172
            }
1395
            }
1173
            $this->update_stat_cache($filename, (object) ['stat' => $stat]);
1396
            $this->update_stat_cache($filename, (object) ['stat' => $stat]);
1174
            return $stat;
1397
            return $stat;
1175
        }
1398
        }
1176
 
1399
 
1177
        $pwd = $this->pwd;
1400
        $pwd = $this->pwd;
1178
        $stat['type'] = $this->chdir($filename) ?
1401
        $stat['type'] = $this->chdir($filename) ?
1179
            FileType::DIRECTORY :
1402
            NET_SFTP_TYPE_DIRECTORY :
1180
            FileType::REGULAR;
1403
            NET_SFTP_TYPE_REGULAR;
1181
        $this->pwd = $pwd;
1404
        $this->pwd = $pwd;
1182
 
1405
 
1183
        if ($stat['type'] == FileType::DIRECTORY) {
1406
        if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) {
1184
            $filename .= '/.';
1407
            $filename .= '/.';
1185
        }
1408
        }
1186
        $this->update_stat_cache($filename, (object) ['stat' => $stat]);
1409
        $this->update_stat_cache($filename, (object) ['stat' => $stat]);
1187
 
1410
 
1188
        return $stat;
1411
        return $stat;
Line 1193... Line 1416...
1193
     *
1416
     *
1194
     * Returns an array on success and false otherwise.
1417
     * Returns an array on success and false otherwise.
1195
     *
1418
     *
1196
     * @param string $filename
1419
     * @param string $filename
1197
     * @return array|false
1420
     * @return array|false
-
 
1421
     * @access public
1198
     */
1422
     */
1199
    public function lstat($filename)
1423
    public function lstat($filename)
1200
    {
1424
    {
1201
        if (!$this->precheck()) {
1425
        if (!$this->precheck()) {
1202
            return false;
1426
            return false;
Line 1215... Line 1439...
1215
            if (is_object($result) && isset($result->lstat)) {
1439
            if (is_object($result) && isset($result->lstat)) {
1216
                return $result->lstat;
1440
                return $result->lstat;
1217
            }
1441
            }
1218
        }
1442
        }
1219
 
1443
 
1220
        $lstat = $this->stat_helper($filename, SFTPPacketType::LSTAT);
1444
        $lstat = $this->stat_helper($filename, NET_SFTP_LSTAT);
1221
        if ($lstat === false) {
1445
        if ($lstat === false) {
1222
            $this->remove_from_stat_cache($filename);
1446
            $this->remove_from_stat_cache($filename);
1223
            return false;
1447
            return false;
1224
        }
1448
        }
1225
        if (isset($lstat['type'])) {
1449
        if (isset($lstat['type'])) {
1226
            if ($lstat['type'] == FileType::DIRECTORY) {
1450
            if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) {
1227
                $filename .= '/.';
1451
                $filename .= '/.';
1228
            }
1452
            }
1229
            $this->update_stat_cache($filename, (object) ['lstat' => $lstat]);
1453
            $this->update_stat_cache($filename, (object) ['lstat' => $lstat]);
1230
            return $lstat;
1454
            return $lstat;
1231
        }
1455
        }
1232
 
1456
 
1233
        $stat = $this->stat_helper($filename, SFTPPacketType::STAT);
1457
        $stat = $this->stat_helper($filename, NET_SFTP_STAT);
1234
 
1458
 
1235
        if ($lstat != $stat) {
1459
        if ($lstat != $stat) {
1236
            $lstat = array_merge($lstat, ['type' => FileType::SYMLINK]);
1460
            $lstat = array_merge($lstat, ['type' => NET_SFTP_TYPE_SYMLINK]);
1237
            $this->update_stat_cache($filename, (object) ['lstat' => $lstat]);
1461
            $this->update_stat_cache($filename, (object) ['lstat' => $lstat]);
1238
            return $stat;
1462
            return $stat;
1239
        }
1463
        }
1240
 
1464
 
1241
        $pwd = $this->pwd;
1465
        $pwd = $this->pwd;
1242
        $lstat['type'] = $this->chdir($filename) ?
1466
        $lstat['type'] = $this->chdir($filename) ?
1243
            FileType::DIRECTORY :
1467
            NET_SFTP_TYPE_DIRECTORY :
1244
            FileType::REGULAR;
1468
            NET_SFTP_TYPE_REGULAR;
1245
        $this->pwd = $pwd;
1469
        $this->pwd = $pwd;
1246
 
1470
 
1247
        if ($lstat['type'] == FileType::DIRECTORY) {
1471
        if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) {
1248
            $filename .= '/.';
1472
            $filename .= '/.';
1249
        }
1473
        }
1250
        $this->update_stat_cache($filename, (object) ['lstat' => $lstat]);
1474
        $this->update_stat_cache($filename, (object) ['lstat' => $lstat]);
1251
 
1475
 
1252
        return $lstat;
1476
        return $lstat;
Line 1254... Line 1478...
1254
 
1478
 
1255
    /**
1479
    /**
1256
     * Returns general information about a file or symbolic link
1480
     * Returns general information about a file or symbolic link
1257
     *
1481
     *
1258
     * Determines information without calling \phpseclib3\Net\SFTP::realpath().
1482
     * Determines information without calling \phpseclib3\Net\SFTP::realpath().
1259
     * The second parameter can be either PacketType::STAT or PacketType::LSTAT.
1483
     * The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT.
1260
     *
1484
     *
1261
     * @param string $filename
1485
     * @param string $filename
1262
     * @param int $type
1486
     * @param int $type
1263
     * @throws \UnexpectedValueException on receipt of unexpected packets
1487
     * @throws \UnexpectedValueException on receipt of unexpected packets
1264
     * @return array|false
1488
     * @return array|false
-
 
1489
     * @access private
1265
     */
1490
     */
1266
    private function stat_helper($filename, $type)
1491
    private function stat_helper($filename, $type)
1267
    {
1492
    {
1268
        // SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
1493
        // SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
1269
        $packet = Strings::packSSH2('s', $filename);
1494
        $packet = Strings::packSSH2('s', $filename);
1270
        $this->send_sftp_packet($type, $packet);
1495
        $this->send_sftp_packet($type, $packet);
1271
 
1496
 
1272
        $response = $this->get_sftp_packet();
1497
        $response = $this->get_sftp_packet();
1273
        switch ($this->packet_type) {
1498
        switch ($this->packet_type) {
1274
            case SFTPPacketType::ATTRS:
1499
            case NET_SFTP_ATTRS:
1275
                return $this->parseAttributes($response);
1500
                return $this->parseAttributes($response);
1276
            case SFTPPacketType::STATUS:
1501
            case NET_SFTP_STATUS:
1277
                $this->logError($response);
1502
                $this->logError($response);
1278
                return false;
1503
                return false;
1279
        }
1504
        }
1280
 
1505
 
1281
        throw new \UnexpectedValueException('Expected PacketType::ATTRS or PacketType::STATUS. '
1506
        throw new \UnexpectedValueException('Expected NET_SFTP_ATTRS or NET_SFTP_STATUS. '
1282
                                          . 'Got packet type: ' . $this->packet_type);
1507
                                          . 'Got packet type: ' . $this->packet_type);
1283
    }
1508
    }
1284
 
1509
 
1285
    /**
1510
    /**
1286
     * Truncates a file to a given length
1511
     * Truncates a file to a given length
1287
     *
1512
     *
1288
     * @param string $filename
1513
     * @param string $filename
1289
     * @param int $new_size
1514
     * @param int $new_size
1290
     * @return bool
1515
     * @return bool
-
 
1516
     * @access public
1291
     */
1517
     */
1292
    public function truncate($filename, $new_size)
1518
    public function truncate($filename, $new_size)
1293
    {
1519
    {
1294
        $attr = Strings::packSSH2('NQ', Attribute::SIZE, $new_size);
1520
        $attr = Strings::packSSH2('NQ', NET_SFTP_ATTR_SIZE, $new_size);
1295
 
1521
 
1296
        return $this->setstat($filename, $attr, false);
1522
        return $this->setstat($filename, $attr, false);
1297
    }
1523
    }
1298
 
1524
 
1299
    /**
1525
    /**
Line 1304... Line 1530...
1304
     * @param string $filename
1530
     * @param string $filename
1305
     * @param int $time
1531
     * @param int $time
1306
     * @param int $atime
1532
     * @param int $atime
1307
     * @throws \UnexpectedValueException on receipt of unexpected packets
1533
     * @throws \UnexpectedValueException on receipt of unexpected packets
1308
     * @return bool
1534
     * @return bool
-
 
1535
     * @access public
1309
     */
1536
     */
1310
    public function touch($filename, $time = null, $atime = null)
1537
    public function touch($filename, $time = null, $atime = null)
1311
    {
1538
    {
1312
        if (!$this->precheck()) {
1539
        if (!$this->precheck()) {
1313
            return false;
1540
            return false;
Line 1324... Line 1551...
1324
        if (!isset($atime)) {
1551
        if (!isset($atime)) {
1325
            $atime = $time;
1552
            $atime = $time;
1326
        }
1553
        }
1327
 
1554
 
1328
        $attr = $this->version < 4 ?
1555
        $attr = $this->version < 4 ?
1329
            pack('N3', Attribute::ACCESSTIME, $atime, $time) :
1556
            pack('N3', NET_SFTP_ATTR_ACCESSTIME, $atime, $time) :
1330
            Strings::packSSH2('NQ2', Attribute::ACCESSTIME | Attribute::MODIFYTIME, $atime, $time);
1557
            Strings::packSSH2('NQ2', NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME, $atime, $time);
1331
 
1558
 
1332
        $packet = Strings::packSSH2('s', $filename);
1559
        $packet = Strings::packSSH2('s', $filename);
1333
        $packet .= $this->version >= 5 ?
1560
        $packet .= $this->version >= 5 ?
1334
            pack('N2', 0, OpenFlag5::OPEN_EXISTING) :
1561
            pack('N2', 0, NET_SFTP_OPEN_OPEN_EXISTING) :
1335
            pack('N', OpenFlag::WRITE | OpenFlag::CREATE | OpenFlag::EXCL);
1562
            pack('N', NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL);
1336
        $packet .= $attr;
1563
        $packet .= $attr;
1337
 
1564
 
1338
        $this->send_sftp_packet(SFTPPacketType::OPEN, $packet);
1565
        $this->send_sftp_packet(NET_SFTP_OPEN, $packet);
1339
 
1566
 
1340
        $response = $this->get_sftp_packet();
1567
        $response = $this->get_sftp_packet();
1341
        switch ($this->packet_type) {
1568
        switch ($this->packet_type) {
1342
            case SFTPPacketType::HANDLE:
1569
            case NET_SFTP_HANDLE:
1343
                return $this->close_handle(substr($response, 4));
1570
                return $this->close_handle(substr($response, 4));
1344
            case SFTPPacketType::STATUS:
1571
            case NET_SFTP_STATUS:
1345
                $this->logError($response);
1572
                $this->logError($response);
1346
                break;
1573
                break;
1347
            default:
1574
            default:
1348
                throw new \UnexpectedValueException('Expected PacketType::HANDLE or PacketType::STATUS. '
1575
                throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
1349
                                                  . 'Got packet type: ' . $this->packet_type);
1576
                                                  . 'Got packet type: ' . $this->packet_type);
1350
        }
1577
        }
1351
 
1578
 
1352
        return $this->setstat($filename, $attr, false);
1579
        return $this->setstat($filename, $attr, false);
1353
    }
1580
    }
Line 1364... Line 1591...
1364
     *
1591
     *
1365
     * @param string $filename
1592
     * @param string $filename
1366
     * @param int|string $uid
1593
     * @param int|string $uid
1367
     * @param bool $recursive
1594
     * @param bool $recursive
1368
     * @return bool
1595
     * @return bool
-
 
1596
     * @access public
1369
     */
1597
     */
1370
    public function chown($filename, $uid, $recursive = false)
1598
    public function chown($filename, $uid, $recursive = false)
1371
    {
1599
    {
1372
        /*
1600
        /*
1373
         quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>,
1601
         quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>,
Line 1387... Line 1615...
1387
       */
1615
       */
1388
 
1616
 
1389
        $attr = $this->version < 4 ?
1617
        $attr = $this->version < 4 ?
1390
            // quoting <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>,
1618
            // quoting <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>,
1391
            // "if the owner or group is specified as -1, then that ID is not changed"
1619
            // "if the owner or group is specified as -1, then that ID is not changed"
1392
            pack('N3', Attribute::UIDGID, $uid, -1) :
1620
            pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1) :
1393
            // quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>,
1621
            // quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>,
1394
            // "If either the owner or group field is zero length, the field should be
1622
            // "If either the owner or group field is zero length, the field should be
1395
            //  considered absent, and no change should be made to that specific field
1623
            //  considered absent, and no change should be made to that specific field
1396
            //  during a modification operation"
1624
            //  during a modification operation"
1397
            Strings::packSSH2('Nss', Attribute::OWNERGROUP, $uid, '');
1625
            Strings::packSSH2('Nss', NET_SFTP_ATTR_OWNERGROUP, $uid, '');
1398
 
1626
 
1399
        return $this->setstat($filename, $attr, $recursive);
1627
        return $this->setstat($filename, $attr, $recursive);
1400
    }
1628
    }
1401
 
1629
 
1402
    /**
1630
    /**
Line 1411... Line 1639...
1411
     *
1639
     *
1412
     * @param string $filename
1640
     * @param string $filename
1413
     * @param int|string $gid
1641
     * @param int|string $gid
1414
     * @param bool $recursive
1642
     * @param bool $recursive
1415
     * @return bool
1643
     * @return bool
-
 
1644
     * @access public
1416
     */
1645
     */
1417
    public function chgrp($filename, $gid, $recursive = false)
1646
    public function chgrp($filename, $gid, $recursive = false)
1418
    {
1647
    {
1419
        $attr = $this->version < 4 ?
1648
        $attr = $this->version < 4 ?
1420
            pack('N3', Attribute::UIDGID, -1, $gid) :
1649
            pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid) :
1421
            Strings::packSSH2('Nss', Attribute::OWNERGROUP, '', $gid);
1650
            Strings::packSSH2('Nss', NET_SFTP_ATTR_OWNERGROUP, '', $gid);
1422
 
1651
 
1423
        return $this->setstat($filename, $attr, $recursive);
1652
        return $this->setstat($filename, $attr, $recursive);
1424
    }
1653
    }
1425
 
1654
 
1426
    /**
1655
    /**
Line 1432... Line 1661...
1432
     * @param int $mode
1661
     * @param int $mode
1433
     * @param string $filename
1662
     * @param string $filename
1434
     * @param bool $recursive
1663
     * @param bool $recursive
1435
     * @throws \UnexpectedValueException on receipt of unexpected packets
1664
     * @throws \UnexpectedValueException on receipt of unexpected packets
1436
     * @return mixed
1665
     * @return mixed
-
 
1666
     * @access public
1437
     */
1667
     */
1438
    public function chmod($mode, $filename, $recursive = false)
1668
    public function chmod($mode, $filename, $recursive = false)
1439
    {
1669
    {
1440
        if (is_string($mode) && is_int($filename)) {
1670
        if (is_string($mode) && is_int($filename)) {
1441
            $temp = $mode;
1671
            $temp = $mode;
1442
            $mode = $filename;
1672
            $mode = $filename;
1443
            $filename = $temp;
1673
            $filename = $temp;
1444
        }
1674
        }
1445
 
1675
 
1446
        $attr = pack('N2', Attribute::PERMISSIONS, $mode & 07777);
1676
        $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777);
1447
        if (!$this->setstat($filename, $attr, $recursive)) {
1677
        if (!$this->setstat($filename, $attr, $recursive)) {
1448
            return false;
1678
            return false;
1449
        }
1679
        }
1450
        if ($recursive) {
1680
        if ($recursive) {
1451
            return true;
1681
            return true;
Line 1454... Line 1684...
1454
        $filename = $this->realpath($filename);
1684
        $filename = $this->realpath($filename);
1455
        // rather than return what the permissions *should* be, we'll return what they actually are.  this will also
1685
        // rather than return what the permissions *should* be, we'll return what they actually are.  this will also
1456
        // tell us if the file actually exists.
1686
        // tell us if the file actually exists.
1457
        // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
1687
        // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
1458
        $packet = pack('Na*', strlen($filename), $filename);
1688
        $packet = pack('Na*', strlen($filename), $filename);
1459
        $this->send_sftp_packet(SFTPPacketType::STAT, $packet);
1689
        $this->send_sftp_packet(NET_SFTP_STAT, $packet);
1460
 
1690
 
1461
        $response = $this->get_sftp_packet();
1691
        $response = $this->get_sftp_packet();
1462
        switch ($this->packet_type) {
1692
        switch ($this->packet_type) {
1463
            case SFTPPacketType::ATTRS:
1693
            case NET_SFTP_ATTRS:
1464
                $attrs = $this->parseAttributes($response);
1694
                $attrs = $this->parseAttributes($response);
1465
                return $attrs['mode'];
1695
                return $attrs['mode'];
1466
            case SFTPPacketType::STATUS:
1696
            case NET_SFTP_STATUS:
1467
                $this->logError($response);
1697
                $this->logError($response);
1468
                return false;
1698
                return false;
1469
        }
1699
        }
1470
 
1700
 
1471
        throw new \UnexpectedValueException('Expected PacketType::ATTRS or PacketType::STATUS. '
1701
        throw new \UnexpectedValueException('Expected NET_SFTP_ATTRS or NET_SFTP_STATUS. '
1472
                                          . 'Got packet type: ' . $this->packet_type);
1702
                                          . 'Got packet type: ' . $this->packet_type);
1473
    }
1703
    }
1474
 
1704
 
1475
    /**
1705
    /**
1476
     * Sets information about a file
1706
     * Sets information about a file
Line 1478... Line 1708...
1478
     * @param string $filename
1708
     * @param string $filename
1479
     * @param string $attr
1709
     * @param string $attr
1480
     * @param bool $recursive
1710
     * @param bool $recursive
1481
     * @throws \UnexpectedValueException on receipt of unexpected packets
1711
     * @throws \UnexpectedValueException on receipt of unexpected packets
1482
     * @return bool
1712
     * @return bool
-
 
1713
     * @access private
1483
     */
1714
     */
1484
    private function setstat($filename, $attr, $recursive)
1715
    private function setstat($filename, $attr, $recursive)
1485
    {
1716
    {
1486
        if (!$this->precheck()) {
1717
        if (!$this->precheck()) {
1487
            return false;
1718
            return false;
Line 1501... Line 1732...
1501
            return $result;
1732
            return $result;
1502
        }
1733
        }
1503
 
1734
 
1504
        $packet = Strings::packSSH2('s', $filename);
1735
        $packet = Strings::packSSH2('s', $filename);
1505
        $packet .= $this->version >= 4 ?
1736
        $packet .= $this->version >= 4 ?
1506
            pack('a*Ca*', substr($attr, 0, 4), FileType::UNKNOWN, substr($attr, 4)) :
1737
            pack('a*Ca*', substr($attr, 0, 4), NET_SFTP_TYPE_UNKNOWN, substr($attr, 4)) :
1507
            $attr;
1738
            $attr;
1508
        $this->send_sftp_packet(SFTPPacketType::SETSTAT, $packet);
1739
        $this->send_sftp_packet(NET_SFTP_SETSTAT, $packet);
1509
 
1740
 
1510
        /*
1741
        /*
1511
         "Because some systems must use separate system calls to set various attributes, it is possible that a failure
1742
         "Because some systems must use separate system calls to set various attributes, it is possible that a failure
1512
          response will be returned, but yet some of the attributes may be have been successfully modified.  If possible,
1743
          response will be returned, but yet some of the attributes may be have been successfully modified.  If possible,
1513
          servers SHOULD avoid this situation; however, clients MUST be aware that this is possible."
1744
          servers SHOULD avoid this situation; however, clients MUST be aware that this is possible."
1514
 
1745
 
1515
          -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6
1746
          -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6
1516
        */
1747
        */
1517
        $response = $this->get_sftp_packet();
1748
        $response = $this->get_sftp_packet();
1518
        if ($this->packet_type != SFTPPacketType::STATUS) {
1749
        if ($this->packet_type != NET_SFTP_STATUS) {
1519
            throw new \UnexpectedValueException('Expected PacketType::STATUS. '
1750
            throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
1520
                                              . 'Got packet type: ' . $this->packet_type);
1751
                                              . 'Got packet type: ' . $this->packet_type);
1521
        }
1752
        }
1522
 
1753
 
1523
        list($status) = Strings::unpackSSH2('N', $response);
1754
        list($status) = Strings::unpackSSH2('N', $response);
1524
        if ($status != StatusCode::OK) {
1755
        if ($status != NET_SFTP_STATUS_OK) {
1525
            $this->logError($response, $status);
1756
            $this->logError($response, $status);
1526
            return false;
1757
            return false;
1527
        }
1758
        }
1528
 
1759
 
1529
        return true;
1760
        return true;
Line 1536... Line 1767...
1536
     *
1767
     *
1537
     * @param string $path
1768
     * @param string $path
1538
     * @param string $attr
1769
     * @param string $attr
1539
     * @param int $i
1770
     * @param int $i
1540
     * @return bool
1771
     * @return bool
-
 
1772
     * @access private
1541
     */
1773
     */
1542
    private function setstat_recursive($path, $attr, &$i)
1774
    private function setstat_recursive($path, $attr, &$i)
1543
    {
1775
    {
1544
        if (!$this->read_put_responses($i)) {
1776
        if (!$this->read_put_responses($i)) {
1545
            return false;
1777
            return false;
Line 1562... Line 1794...
1562
            if (!isset($props['type'])) {
1794
            if (!isset($props['type'])) {
1563
                return false;
1795
                return false;
1564
            }
1796
            }
1565
 
1797
 
1566
            $temp = $path . '/' . $filename;
1798
            $temp = $path . '/' . $filename;
1567
            if ($props['type'] == FileType::DIRECTORY) {
1799
            if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
1568
                if (!$this->setstat_recursive($temp, $attr, $i)) {
1800
                if (!$this->setstat_recursive($temp, $attr, $i)) {
1569
                    return false;
1801
                    return false;
1570
                }
1802
                }
1571
            } else {
1803
            } else {
1572
                $packet = Strings::packSSH2('s', $temp);
1804
                $packet = Strings::packSSH2('s', $temp);
1573
                $packet .= $this->version >= 4 ?
1805
                $packet .= $this->version >= 4 ?
1574
                    pack('Ca*', FileType::UNKNOWN, $attr) :
1806
                    pack('Ca*', NET_SFTP_TYPE_UNKNOWN, $attr) :
1575
                    $attr;
1807
                    $attr;
1576
                $this->send_sftp_packet(SFTPPacketType::SETSTAT, $packet);
1808
                $this->send_sftp_packet(NET_SFTP_SETSTAT, $packet);
1577
 
1809
 
1578
                $i++;
1810
                $i++;
1579
 
1811
 
1580
                if ($i >= $this->queueSize) {
1812
                if ($i >= NET_SFTP_QUEUE_SIZE) {
1581
                    if (!$this->read_put_responses($i)) {
1813
                    if (!$this->read_put_responses($i)) {
1582
                        return false;
1814
                        return false;
1583
                    }
1815
                    }
1584
                    $i = 0;
1816
                    $i = 0;
1585
                }
1817
                }
1586
            }
1818
            }
1587
        }
1819
        }
1588
 
1820
 
1589
        $packet = Strings::packSSH2('s', $path);
1821
        $packet = Strings::packSSH2('s', $path);
1590
        $packet .= $this->version >= 4 ?
1822
        $packet .= $this->version >= 4 ?
1591
            pack('Ca*', FileType::UNKNOWN, $attr) :
1823
            pack('Ca*', NET_SFTP_TYPE_UNKNOWN, $attr) :
1592
            $attr;
1824
            $attr;
1593
        $this->send_sftp_packet(SFTPPacketType::SETSTAT, $packet);
1825
        $this->send_sftp_packet(NET_SFTP_SETSTAT, $packet);
1594
 
1826
 
1595
        $i++;
1827
        $i++;
1596
 
1828
 
1597
        if ($i >= $this->queueSize) {
1829
        if ($i >= NET_SFTP_QUEUE_SIZE) {
1598
            if (!$this->read_put_responses($i)) {
1830
            if (!$this->read_put_responses($i)) {
1599
                return false;
1831
                return false;
1600
            }
1832
            }
1601
            $i = 0;
1833
            $i = 0;
1602
        }
1834
        }
Line 1608... Line 1840...
1608
     * Return the target of a symbolic link
1840
     * Return the target of a symbolic link
1609
     *
1841
     *
1610
     * @param string $link
1842
     * @param string $link
1611
     * @throws \UnexpectedValueException on receipt of unexpected packets
1843
     * @throws \UnexpectedValueException on receipt of unexpected packets
1612
     * @return mixed
1844
     * @return mixed
-
 
1845
     * @access public
1613
     */
1846
     */
1614
    public function readlink($link)
1847
    public function readlink($link)
1615
    {
1848
    {
1616
        if (!$this->precheck()) {
1849
        if (!$this->precheck()) {
1617
            return false;
1850
            return false;
1618
        }
1851
        }
1619
 
1852
 
1620
        $link = $this->realpath($link);
1853
        $link = $this->realpath($link);
1621
 
1854
 
1622
        $this->send_sftp_packet(SFTPPacketType::READLINK, Strings::packSSH2('s', $link));
1855
        $this->send_sftp_packet(NET_SFTP_READLINK, Strings::packSSH2('s', $link));
1623
 
1856
 
1624
        $response = $this->get_sftp_packet();
1857
        $response = $this->get_sftp_packet();
1625
        switch ($this->packet_type) {
1858
        switch ($this->packet_type) {
1626
            case SFTPPacketType::NAME:
1859
            case NET_SFTP_NAME:
1627
                break;
1860
                break;
1628
            case SFTPPacketType::STATUS:
1861
            case NET_SFTP_STATUS:
1629
                $this->logError($response);
1862
                $this->logError($response);
1630
                return false;
1863
                return false;
1631
            default:
1864
            default:
1632
                throw new \UnexpectedValueException('Expected PacketType::NAME or PacketType::STATUS. '
1865
                throw new \UnexpectedValueException('Expected NET_SFTP_NAME or NET_SFTP_STATUS. '
1633
                                                  . 'Got packet type: ' . $this->packet_type);
1866
                                                  . 'Got packet type: ' . $this->packet_type);
1634
        }
1867
        }
1635
 
1868
 
1636
        list($count) = Strings::unpackSSH2('N', $response);
1869
        list($count) = Strings::unpackSSH2('N', $response);
1637
        // the file isn't a symlink
1870
        // the file isn't a symlink
Line 1651... Line 1884...
1651
     *
1884
     *
1652
     * @param string $target
1885
     * @param string $target
1653
     * @param string $link
1886
     * @param string $link
1654
     * @throws \UnexpectedValueException on receipt of unexpected packets
1887
     * @throws \UnexpectedValueException on receipt of unexpected packets
1655
     * @return bool
1888
     * @return bool
-
 
1889
     * @access public
1656
     */
1890
     */
1657
    public function symlink($target, $link)
1891
    public function symlink($target, $link)
1658
    {
1892
    {
1659
        if (!$this->precheck()) {
1893
        if (!$this->precheck()) {
1660
            return false;
1894
            return false;
Line 1669... Line 1903...
1669
           create hard links.  Also change it's packet number because many
1903
           create hard links.  Also change it's packet number because many
1670
           implementation implemented SYMLINK with the arguments reversed.
1904
           implementation implemented SYMLINK with the arguments reversed.
1671
           Hopefully the new argument names make it clear which way is which.
1905
           Hopefully the new argument names make it clear which way is which.
1672
        */
1906
        */
1673
        if ($this->version == 6) {
1907
        if ($this->version == 6) {
1674
            $type = SFTPPacketType::LINK;
1908
            $type = NET_SFTP_LINK;
1675
            $packet = Strings::packSSH2('ssC', $link, $target, 1);
1909
            $packet = Strings::packSSH2('ssC', $link, $target, 1);
1676
        } else {
1910
        } else {
1677
            $type = SFTPPacketType::SYMLINK;
1911
            $type = NET_SFTP_SYMLINK;
1678
            /* quoting http://bxr.su/OpenBSD/usr.bin/ssh/PROTOCOL#347 :
1912
            /* quoting http://bxr.su/OpenBSD/usr.bin/ssh/PROTOCOL#347 :
1679
 
1913
 
1680
               3.1. sftp: Reversal of arguments to SSH_FXP_SYMLINK
1914
               3.1. sftp: Reversal of arguments to SSH_FXP_SYMLINK
1681
 
1915
 
1682
               When OpenSSH's sftp-server was implemented, the order of the arguments
1916
               When OpenSSH's sftp-server was implemented, the order of the arguments
Line 1694... Line 1928...
1694
                Strings::packSSH2('ss', $link, $target);
1928
                Strings::packSSH2('ss', $link, $target);
1695
        }
1929
        }
1696
        $this->send_sftp_packet($type, $packet);
1930
        $this->send_sftp_packet($type, $packet);
1697
 
1931
 
1698
        $response = $this->get_sftp_packet();
1932
        $response = $this->get_sftp_packet();
1699
        if ($this->packet_type != SFTPPacketType::STATUS) {
1933
        if ($this->packet_type != NET_SFTP_STATUS) {
1700
            throw new \UnexpectedValueException('Expected PacketType::STATUS. '
1934
            throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
1701
                                              . 'Got packet type: ' . $this->packet_type);
1935
                                              . 'Got packet type: ' . $this->packet_type);
1702
        }
1936
        }
1703
 
1937
 
1704
        list($status) = Strings::unpackSSH2('N', $response);
1938
        list($status) = Strings::unpackSSH2('N', $response);
1705
        if ($status != StatusCode::OK) {
1939
        if ($status != NET_SFTP_STATUS_OK) {
1706
            $this->logError($response, $status);
1940
            $this->logError($response, $status);
1707
            return false;
1941
            return false;
1708
        }
1942
        }
1709
 
1943
 
1710
        return true;
1944
        return true;
Line 1715... Line 1949...
1715
     *
1949
     *
1716
     * @param string $dir
1950
     * @param string $dir
1717
     * @param int $mode
1951
     * @param int $mode
1718
     * @param bool $recursive
1952
     * @param bool $recursive
1719
     * @return bool
1953
     * @return bool
-
 
1954
     * @access public
1720
     */
1955
     */
1721
    public function mkdir($dir, $mode = -1, $recursive = false)
1956
    public function mkdir($dir, $mode = -1, $recursive = false)
1722
    {
1957
    {
1723
        if (!$this->precheck()) {
1958
        if (!$this->precheck()) {
1724
            return false;
1959
            return false;
Line 1747... Line 1982...
1747
     * Helper function for directory creation
1982
     * Helper function for directory creation
1748
     *
1983
     *
1749
     * @param string $dir
1984
     * @param string $dir
1750
     * @param int $mode
1985
     * @param int $mode
1751
     * @return bool
1986
     * @return bool
-
 
1987
     * @access private
1752
     */
1988
     */
1753
    private function mkdir_helper($dir, $mode)
1989
    private function mkdir_helper($dir, $mode)
1754
    {
1990
    {
1755
        // send SSH_FXP_MKDIR without any attributes (that's what the \0\0\0\0 is doing)
1991
        // send SSH_FXP_MKDIR without any attributes (that's what the \0\0\0\0 is doing)
1756
        $this->send_sftp_packet(SFTPPacketType::MKDIR, Strings::packSSH2('s', $dir) . "\0\0\0\0");
1992
        $this->send_sftp_packet(NET_SFTP_MKDIR, Strings::packSSH2('s', $dir) . "\0\0\0\0");
1757
 
1993
 
1758
        $response = $this->get_sftp_packet();
1994
        $response = $this->get_sftp_packet();
1759
        if ($this->packet_type != SFTPPacketType::STATUS) {
1995
        if ($this->packet_type != NET_SFTP_STATUS) {
1760
            throw new \UnexpectedValueException('Expected PacketType::STATUS. '
1996
            throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
1761
                                              . 'Got packet type: ' . $this->packet_type);
1997
                                              . 'Got packet type: ' . $this->packet_type);
1762
        }
1998
        }
1763
 
1999
 
1764
        list($status) = Strings::unpackSSH2('N', $response);
2000
        list($status) = Strings::unpackSSH2('N', $response);
1765
        if ($status != StatusCode::OK) {
2001
        if ($status != NET_SFTP_STATUS_OK) {
1766
            $this->logError($response, $status);
2002
            $this->logError($response, $status);
1767
            return false;
2003
            return false;
1768
        }
2004
        }
1769
 
2005
 
1770
        if ($mode !== -1) {
2006
        if ($mode !== -1) {
Line 1778... Line 2014...
1778
     * Removes a directory.
2014
     * Removes a directory.
1779
     *
2015
     *
1780
     * @param string $dir
2016
     * @param string $dir
1781
     * @throws \UnexpectedValueException on receipt of unexpected packets
2017
     * @throws \UnexpectedValueException on receipt of unexpected packets
1782
     * @return bool
2018
     * @return bool
-
 
2019
     * @access public
1783
     */
2020
     */
1784
    public function rmdir($dir)
2021
    public function rmdir($dir)
1785
    {
2022
    {
1786
        if (!$this->precheck()) {
2023
        if (!$this->precheck()) {
1787
            return false;
2024
            return false;
Line 1790... Line 2027...
1790
        $dir = $this->realpath($dir);
2027
        $dir = $this->realpath($dir);
1791
        if ($dir === false) {
2028
        if ($dir === false) {
1792
            return false;
2029
            return false;
1793
        }
2030
        }
1794
 
2031
 
1795
        $this->send_sftp_packet(SFTPPacketType::RMDIR, Strings::packSSH2('s', $dir));
2032
        $this->send_sftp_packet(NET_SFTP_RMDIR, Strings::packSSH2('s', $dir));
1796
 
2033
 
1797
        $response = $this->get_sftp_packet();
2034
        $response = $this->get_sftp_packet();
1798
        if ($this->packet_type != SFTPPacketType::STATUS) {
2035
        if ($this->packet_type != NET_SFTP_STATUS) {
1799
            throw new \UnexpectedValueException('Expected PacketType::STATUS. '
2036
            throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
1800
                                              . 'Got packet type: ' . $this->packet_type);
2037
                                              . 'Got packet type: ' . $this->packet_type);
1801
        }
2038
        }
1802
 
2039
 
1803
        list($status) = Strings::unpackSSH2('N', $response);
2040
        list($status) = Strings::unpackSSH2('N', $response);
1804
        if ($status != StatusCode::OK) {
2041
        if ($status != NET_SFTP_STATUS_OK) {
1805
            // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED?
2042
            // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED?
1806
            $this->logError($response, $status);
2043
            $this->logError($response, $status);
1807
            return false;
2044
            return false;
1808
        }
2045
        }
1809
 
2046
 
Line 1862... Line 2099...
1862
     * @param callable|null $progressCallback
2099
     * @param callable|null $progressCallback
1863
     * @throws \UnexpectedValueException on receipt of unexpected packets
2100
     * @throws \UnexpectedValueException on receipt of unexpected packets
1864
     * @throws \BadFunctionCallException if you're uploading via a callback and the callback function is invalid
2101
     * @throws \BadFunctionCallException if you're uploading via a callback and the callback function is invalid
1865
     * @throws \phpseclib3\Exception\FileNotFoundException if you're uploading via a file and the file doesn't exist
2102
     * @throws \phpseclib3\Exception\FileNotFoundException if you're uploading via a file and the file doesn't exist
1866
     * @return bool
2103
     * @return bool
-
 
2104
     * @access public
1867
     */
2105
     */
1868
    public function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null)
2106
    public function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null)
1869
    {
2107
    {
1870
        if (!$this->precheck()) {
2108
        if (!$this->precheck()) {
1871
            return false;
2109
            return false;
Line 1877... Line 2115...
1877
        }
2115
        }
1878
 
2116
 
1879
        $this->remove_from_stat_cache($remote_file);
2117
        $this->remove_from_stat_cache($remote_file);
1880
 
2118
 
1881
        if ($this->version >= 5) {
2119
        if ($this->version >= 5) {
1882
            $flags = OpenFlag5::OPEN_OR_CREATE;
2120
            $flags = NET_SFTP_OPEN_OPEN_OR_CREATE;
1883
        } else {
2121
        } else {
1884
            $flags = OpenFlag::WRITE | OpenFlag::CREATE;
2122
            $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE;
1885
            // according to the SFTP specs, OpenFlag::APPEND should "force all writes to append data at the end of the file."
2123
            // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file."
1886
            // in practice, it doesn't seem to do that.
2124
            // in practice, it doesn't seem to do that.
1887
            //$flags|= ($mode & self::RESUME) ? OpenFlag::APPEND : OpenFlag::TRUNCATE;
2125
            //$flags|= ($mode & self::RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE;
1888
        }
2126
        }
1889
 
2127
 
1890
        if ($start >= 0) {
2128
        if ($start >= 0) {
1891
            $offset = $start;
2129
            $offset = $start;
1892
        } elseif ($mode & self::RESUME) {
2130
        } elseif ($mode & self::RESUME) {
1893
            // if OpenFlag::APPEND worked as it should _size() wouldn't need to be called
2131
            // if NET_SFTP_OPEN_APPEND worked as it should _size() wouldn't need to be called
1894
            $size = $this->stat($remote_file)['size'];
2132
            $size = $this->stat($remote_file)['size'];
1895
            $offset = $size !== false ? $size : 0;
2133
            $offset = $size !== false ? $size : 0;
1896
        } else {
2134
        } else {
1897
            $offset = 0;
2135
            $offset = 0;
1898
            if ($this->version >= 5) {
2136
            if ($this->version >= 5) {
1899
                $flags = OpenFlag5::CREATE_TRUNCATE;
2137
                $flags = NET_SFTP_OPEN_CREATE_TRUNCATE;
1900
            } else {
2138
            } else {
1901
                $flags |= OpenFlag::TRUNCATE;
2139
                $flags |= NET_SFTP_OPEN_TRUNCATE;
1902
            }
2140
            }
1903
        }
2141
        }
1904
 
2142
 
1905
        $this->remove_from_stat_cache($remote_file);
2143
        $this->remove_from_stat_cache($remote_file);
1906
 
2144
 
1907
        $packet = Strings::packSSH2('s', $remote_file);
2145
        $packet = Strings::packSSH2('s', $remote_file);
1908
        $packet .= $this->version >= 5 ?
2146
        $packet .= $this->version >= 5 ?
1909
            pack('N3', 0, $flags, 0) :
2147
            pack('N3', 0, $flags, 0) :
1910
            pack('N2', $flags, 0);
2148
            pack('N2', $flags, 0);
1911
        $this->send_sftp_packet(SFTPPacketType::OPEN, $packet);
2149
        $this->send_sftp_packet(NET_SFTP_OPEN, $packet);
1912
 
2150
 
1913
        $response = $this->get_sftp_packet();
2151
        $response = $this->get_sftp_packet();
1914
        switch ($this->packet_type) {
2152
        switch ($this->packet_type) {
1915
            case SFTPPacketType::HANDLE:
2153
            case NET_SFTP_HANDLE:
1916
                $handle = substr($response, 4);
2154
                $handle = substr($response, 4);
1917
                break;
2155
                break;
1918
            case SFTPPacketType::STATUS:
2156
            case NET_SFTP_STATUS:
1919
                $this->logError($response);
2157
                $this->logError($response);
1920
                return false;
2158
                return false;
1921
            default:
2159
            default:
1922
                throw new \UnexpectedValueException('Expected PacketType::HANDLE or PacketType::STATUS. '
2160
                throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
1923
                                                  . 'Got packet type: ' . $this->packet_type);
2161
                                                  . 'Got packet type: ' . $this->packet_type);
1924
        }
2162
        }
1925
 
2163
 
1926
        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3
2164
        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3
1927
        $dataCallback = false;
2165
        $dataCallback = false;
Line 1934... Line 2172...
1934
                // do nothing
2172
                // do nothing
1935
                break;
2173
                break;
1936
            case is_resource($data):
2174
            case is_resource($data):
1937
                $mode = $mode & ~self::SOURCE_LOCAL_FILE;
2175
                $mode = $mode & ~self::SOURCE_LOCAL_FILE;
1938
                $info = stream_get_meta_data($data);
2176
                $info = stream_get_meta_data($data);
1939
                if (isset($info['wrapper_type']) && $info['wrapper_type'] == 'PHP' && $info['stream_type'] == 'Input') {
2177
                if ($info['wrapper_type'] == 'PHP' && $info['stream_type'] == 'Input') {
1940
                    $fp = fopen('php://memory', 'w+');
2178
                    $fp = fopen('php://memory', 'w+');
1941
                    stream_copy_to_stream($data, $fp);
2179
                    stream_copy_to_stream($data, $fp);
1942
                    rewind($fp);
2180
                    rewind($fp);
1943
                } else {
2181
                } else {
1944
                    $fp = $data;
2182
                    $fp = $data;
Line 1970... Line 2208...
1970
 
2208
 
1971
        $sent = 0;
2209
        $sent = 0;
1972
        $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;
2210
        $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;
1973
 
2211
 
1974
        $sftp_packet_size = $this->max_sftp_packet;
2212
        $sftp_packet_size = $this->max_sftp_packet;
1975
        // make the SFTP packet be exactly the SFTP packet size by including the bytes in the PacketType::WRITE packets "header"
2213
        // make the SFTP packet be exactly the SFTP packet size by including the bytes in the NET_SFTP_WRITE packets "header"
1976
        $sftp_packet_size -= strlen($handle) + 25;
2214
        $sftp_packet_size -= strlen($handle) + 25;
1977
        $i = $j = 0;
2215
        $i = $j = 0;
1978
        while ($dataCallback || ($size === 0 || $sent < $size)) {
2216
        while ($dataCallback || ($size === 0 || $sent < $size)) {
1979
            if ($dataCallback) {
2217
            if ($dataCallback) {
1980
                $temp = $dataCallback($sftp_packet_size);
2218
                $temp = $dataCallback($sftp_packet_size);
Line 1989... Line 2227...
1989
            }
2227
            }
1990
 
2228
 
1991
            $subtemp = $offset + $sent;
2229
            $subtemp = $offset + $sent;
1992
            $packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp);
2230
            $packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp);
1993
            try {
2231
            try {
1994
                $this->send_sftp_packet(SFTPPacketType::WRITE, $packet, $j);
2232
                $this->send_sftp_packet(NET_SFTP_WRITE, $packet, $j);
1995
            } catch (\Exception $e) {
2233
            } catch (\Exception $e) {
1996
                if ($mode & self::SOURCE_LOCAL_FILE) {
2234
                if ($mode & self::SOURCE_LOCAL_FILE) {
1997
                    fclose($fp);
2235
                    fclose($fp);
1998
                }
2236
                }
1999
                throw $e;
2237
                throw $e;
Line 2003... Line 2241...
2003
                $progressCallback($sent);
2241
                $progressCallback($sent);
2004
            }
2242
            }
2005
 
2243
 
2006
            $i++;
2244
            $i++;
2007
            $j++;
2245
            $j++;
2008
            if ($i == $this->uploadQueueSize) {
2246
            if ($i == NET_SFTP_UPLOAD_QUEUE_SIZE) {
2009
                if (!$this->read_put_responses($i)) {
2247
                if (!$this->read_put_responses($i)) {
2010
                    $i = 0;
2248
                    $i = 0;
2011
                    break;
2249
                    break;
2012
                }
2250
                }
2013
                $i = 0;
2251
                $i = 0;
Line 2030... Line 2268...
2030
            }
2268
            }
2031
 
2269
 
2032
            if ($this->preserveTime) {
2270
            if ($this->preserveTime) {
2033
                $stat = stat($data);
2271
                $stat = stat($data);
2034
                $attr = $this->version < 4 ?
2272
                $attr = $this->version < 4 ?
2035
                    pack('N3', Attribute::ACCESSTIME, $stat['atime'], $stat['mtime']) :
2273
                    pack('N3', NET_SFTP_ATTR_ACCESSTIME, $stat['atime'], $stat['time']) :
2036
                    Strings::packSSH2('NQ2', Attribute::ACCESSTIME | Attribute::MODIFYTIME, $stat['atime'], $stat['mtime']);
2274
                    Strings::packSSH2('NQ2', NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME, $stat['atime'], $stat['time']);
2037
                if (!$this->setstat($remote_file, $attr, false)) {
2275
                if (!$this->setstat($remote_file, $attr, false)) {
2038
                    throw new \RuntimeException('Error setting file time');
2276
                    throw new \RuntimeException('Error setting file time');
2039
                }
2277
                }
2040
            }
2278
            }
2041
        }
2279
        }
Line 2050... Line 2288...
2050
     * SSH_FXP_WRITEs, in succession, and then reading $i responses.
2288
     * SSH_FXP_WRITEs, in succession, and then reading $i responses.
2051
     *
2289
     *
2052
     * @param int $i
2290
     * @param int $i
2053
     * @return bool
2291
     * @return bool
2054
     * @throws \UnexpectedValueException on receipt of unexpected packets
2292
     * @throws \UnexpectedValueException on receipt of unexpected packets
-
 
2293
     * @access private
2055
     */
2294
     */
2056
    private function read_put_responses($i)
2295
    private function read_put_responses($i)
2057
    {
2296
    {
2058
        while ($i--) {
2297
        while ($i--) {
2059
            $response = $this->get_sftp_packet();
2298
            $response = $this->get_sftp_packet();
2060
            if ($this->packet_type != SFTPPacketType::STATUS) {
2299
            if ($this->packet_type != NET_SFTP_STATUS) {
2061
                throw new \UnexpectedValueException('Expected PacketType::STATUS. '
2300
                throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
2062
                                                  . 'Got packet type: ' . $this->packet_type);
2301
                                                  . 'Got packet type: ' . $this->packet_type);
2063
            }
2302
            }
2064
 
2303
 
2065
            list($status) = Strings::unpackSSH2('N', $response);
2304
            list($status) = Strings::unpackSSH2('N', $response);
2066
            if ($status != StatusCode::OK) {
2305
            if ($status != NET_SFTP_STATUS_OK) {
2067
                $this->logError($response, $status);
2306
                $this->logError($response, $status);
2068
                break;
2307
                break;
2069
            }
2308
            }
2070
        }
2309
        }
2071
 
2310
 
Line 2076... Line 2315...
2076
     * Close handle
2315
     * Close handle
2077
     *
2316
     *
2078
     * @param string $handle
2317
     * @param string $handle
2079
     * @return bool
2318
     * @return bool
2080
     * @throws \UnexpectedValueException on receipt of unexpected packets
2319
     * @throws \UnexpectedValueException on receipt of unexpected packets
-
 
2320
     * @access private
2081
     */
2321
     */
2082
    private function close_handle($handle)
2322
    private function close_handle($handle)
2083
    {
2323
    {
2084
        $this->send_sftp_packet(SFTPPacketType::CLOSE, pack('Na*', strlen($handle), $handle));
2324
        $this->send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle));
2085
 
2325
 
2086
        // "The client MUST release all resources associated with the handle regardless of the status."
2326
        // "The client MUST release all resources associated with the handle regardless of the status."
2087
        //  -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3
2327
        //  -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3
2088
        $response = $this->get_sftp_packet();
2328
        $response = $this->get_sftp_packet();
2089
        if ($this->packet_type != SFTPPacketType::STATUS) {
2329
        if ($this->packet_type != NET_SFTP_STATUS) {
2090
            throw new \UnexpectedValueException('Expected PacketType::STATUS. '
2330
            throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
2091
                                              . 'Got packet type: ' . $this->packet_type);
2331
                                              . 'Got packet type: ' . $this->packet_type);
2092
        }
2332
        }
2093
 
2333
 
2094
        list($status) = Strings::unpackSSH2('N', $response);
2334
        list($status) = Strings::unpackSSH2('N', $response);
2095
        if ($status != StatusCode::OK) {
2335
        if ($status != NET_SFTP_STATUS_OK) {
2096
            $this->logError($response, $status);
2336
            $this->logError($response, $status);
2097
            return false;
2337
            return false;
2098
        }
2338
        }
2099
 
2339
 
2100
        return true;
2340
        return true;
Line 2113... Line 2353...
2113
     * @param string|bool|resource|callable $local_file
2353
     * @param string|bool|resource|callable $local_file
2114
     * @param int $offset
2354
     * @param int $offset
2115
     * @param int $length
2355
     * @param int $length
2116
     * @param callable|null $progressCallback
2356
     * @param callable|null $progressCallback
2117
     * @throws \UnexpectedValueException on receipt of unexpected packets
2357
     * @throws \UnexpectedValueException on receipt of unexpected packets
2118
     * @return string|bool
2358
     * @return string|false
-
 
2359
     * @access public
2119
     */
2360
     */
2120
    public function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null)
2361
    public function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null)
2121
    {
2362
    {
2122
        if (!$this->precheck()) {
2363
        if (!$this->precheck()) {
2123
            return false;
2364
            return false;
Line 2128... Line 2369...
2128
            return false;
2369
            return false;
2129
        }
2370
        }
2130
 
2371
 
2131
        $packet = Strings::packSSH2('s', $remote_file);
2372
        $packet = Strings::packSSH2('s', $remote_file);
2132
        $packet .= $this->version >= 5 ?
2373
        $packet .= $this->version >= 5 ?
2133
            pack('N3', 0, OpenFlag5::OPEN_EXISTING, 0) :
2374
            pack('N3', 0, NET_SFTP_OPEN_OPEN_EXISTING, 0) :
2134
            pack('N2', OpenFlag::READ, 0);
2375
            pack('N2', NET_SFTP_OPEN_READ, 0);
2135
        $this->send_sftp_packet(SFTPPacketType::OPEN, $packet);
2376
        $this->send_sftp_packet(NET_SFTP_OPEN, $packet);
2136
 
2377
 
2137
        $response = $this->get_sftp_packet();
2378
        $response = $this->get_sftp_packet();
2138
        switch ($this->packet_type) {
2379
        switch ($this->packet_type) {
2139
            case SFTPPacketType::HANDLE:
2380
            case NET_SFTP_HANDLE:
2140
                $handle = substr($response, 4);
2381
                $handle = substr($response, 4);
2141
                break;
2382
                break;
2142
            case SFTPPacketType::STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
2383
            case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
2143
                $this->logError($response);
2384
                $this->logError($response);
2144
                return false;
2385
                return false;
2145
            default:
2386
            default:
2146
                throw new \UnexpectedValueException('Expected PacketType::HANDLE or PacketType::STATUS. '
2387
                throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
2147
                                                  . 'Got packet type: ' . $this->packet_type);
2388
                                                  . 'Got packet type: ' . $this->packet_type);
2148
        }
2389
        }
2149
 
2390
 
2150
        if (is_resource($local_file)) {
2391
        if (is_resource($local_file)) {
2151
            $fp = $local_file;
2392
            $fp = $local_file;
Line 2168... Line 2409...
2168
        $start = $offset;
2409
        $start = $offset;
2169
        $read = 0;
2410
        $read = 0;
2170
        while (true) {
2411
        while (true) {
2171
            $i = 0;
2412
            $i = 0;
2172
 
2413
 
2173
            while ($i < $this->queueSize && ($length < 0 || $read < $length)) {
2414
            while ($i < NET_SFTP_QUEUE_SIZE && ($length < 0 || $read < $length)) {
2174
                $tempoffset = $start + $read;
2415
                $tempoffset = $start + $read;
2175
 
2416
 
2176
                $packet_size = $length > 0 ? min($this->max_sftp_packet, $length - $read) : $this->max_sftp_packet;
2417
                $packet_size = $length > 0 ? min($this->max_sftp_packet, $length - $read) : $this->max_sftp_packet;
2177
 
2418
 
2178
                $packet = Strings::packSSH2('sN3', $handle, $tempoffset / 4294967296, $tempoffset, $packet_size);
2419
                $packet = Strings::packSSH2('sN3', $handle, $tempoffset / 4294967296, $tempoffset, $packet_size);
2179
                try {
2420
                try {
2180
                    $this->send_sftp_packet(SFTPPacketType::READ, $packet, $i);
2421
                    $this->send_sftp_packet(NET_SFTP_READ, $packet, $i);
2181
                } catch (\Exception $e) {
2422
                } catch (\Exception $e) {
2182
                    if ($fclose_check) {
2423
                    if ($fclose_check) {
2183
                        fclose($fp);
2424
                        fclose($fp);
2184
                    }
2425
                    }
2185
                    throw $e;
2426
                    throw $e;
Line 2205... Line 2446...
2205
                } else {
2446
                } else {
2206
                    $response = $this->get_sftp_packet($packets_sent - $i);
2447
                    $response = $this->get_sftp_packet($packets_sent - $i);
2207
                }
2448
                }
2208
 
2449
 
2209
                switch ($this->packet_type) {
2450
                switch ($this->packet_type) {
2210
                    case SFTPPacketType::DATA:
2451
                    case NET_SFTP_DATA:
2211
                        $temp = substr($response, 4);
2452
                        $temp = substr($response, 4);
2212
                        $offset += strlen($temp);
2453
                        $offset += strlen($temp);
2213
                        if ($local_file === false) {
2454
                        if ($local_file === false) {
2214
                            $content .= $temp;
2455
                            $content .= $temp;
2215
                        } elseif (is_callable($local_file)) {
2456
                        } elseif (is_callable($local_file)) {
Line 2220... Line 2461...
2220
                        if (is_callable($progressCallback)) {
2461
                        if (is_callable($progressCallback)) {
2221
                            call_user_func($progressCallback, $offset);
2462
                            call_user_func($progressCallback, $offset);
2222
                        }
2463
                        }
2223
                        $temp = null;
2464
                        $temp = null;
2224
                        break;
2465
                        break;
2225
                    case SFTPPacketType::STATUS:
2466
                    case NET_SFTP_STATUS:
2226
                        // could, in theory, return false if !strlen($content) but we'll hold off for the time being
2467
                        // could, in theory, return false if !strlen($content) but we'll hold off for the time being
2227
                        $this->logError($response);
2468
                        $this->logError($response);
2228
                        $clear_responses = true; // don't break out of the loop yet, so we can read the remaining responses
2469
                        $clear_responses = true; // don't break out of the loop yet, so we can read the remaining responses
2229
                        break;
2470
                        break;
2230
                    default:
2471
                    default:
Line 2234... Line 2475...
2234
                        if ($this->channel_close) {
2475
                        if ($this->channel_close) {
2235
                            $this->partial_init = false;
2476
                            $this->partial_init = false;
2236
                            $this->init_sftp_connection();
2477
                            $this->init_sftp_connection();
2237
                            return false;
2478
                            return false;
2238
                        } else {
2479
                        } else {
2239
                            throw new \UnexpectedValueException('Expected PacketType::DATA or PacketType::STATUS. '
2480
                            throw new \UnexpectedValueException('Expected NET_SFTP_DATA or NET_SFTP_STATUS. '
2240
                                                              . 'Got packet type: ' . $this->packet_type);
2481
                                                              . 'Got packet type: ' . $this->packet_type);
2241
                        }
2482
                        }
2242
                }
2483
                }
2243
                $response = null;
2484
                $response = null;
2244
            }
2485
            }
Line 2278... Line 2519...
2278
     *
2519
     *
2279
     * @param string $path
2520
     * @param string $path
2280
     * @param bool $recursive
2521
     * @param bool $recursive
2281
     * @return bool
2522
     * @return bool
2282
     * @throws \UnexpectedValueException on receipt of unexpected packets
2523
     * @throws \UnexpectedValueException on receipt of unexpected packets
-
 
2524
     * @access public
2283
     */
2525
     */
2284
    public function delete($path, $recursive = true)
2526
    public function delete($path, $recursive = true)
2285
    {
2527
    {
2286
        if (!$this->precheck()) {
2528
        if (!$this->precheck()) {
2287
            return false;
2529
            return false;
Line 2300... Line 2542...
2300
        if ($path === false) {
2542
        if ($path === false) {
2301
            return false;
2543
            return false;
2302
        }
2544
        }
2303
 
2545
 
2304
        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
2546
        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
2305
        $this->send_sftp_packet(SFTPPacketType::REMOVE, pack('Na*', strlen($path), $path));
2547
        $this->send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($path), $path));
2306
 
2548
 
2307
        $response = $this->get_sftp_packet();
2549
        $response = $this->get_sftp_packet();
2308
        if ($this->packet_type != SFTPPacketType::STATUS) {
2550
        if ($this->packet_type != NET_SFTP_STATUS) {
2309
            throw new \UnexpectedValueException('Expected PacketType::STATUS. '
2551
            throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
2310
                                              . 'Got packet type: ' . $this->packet_type);
2552
                                              . 'Got packet type: ' . $this->packet_type);
2311
        }
2553
        }
2312
 
2554
 
2313
        // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
2555
        // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
2314
        list($status) = Strings::unpackSSH2('N', $response);
2556
        list($status) = Strings::unpackSSH2('N', $response);
2315
        if ($status != StatusCode::OK) {
2557
        if ($status != NET_SFTP_STATUS_OK) {
2316
            $this->logError($response, $status);
2558
            $this->logError($response, $status);
2317
            if (!$recursive) {
2559
            if (!$recursive) {
2318
                return false;
2560
                return false;
2319
            }
2561
            }
2320
 
2562
 
Line 2335... Line 2577...
2335
     * Minimizes directory lookups and SSH_FXP_STATUS requests for speed.
2577
     * Minimizes directory lookups and SSH_FXP_STATUS requests for speed.
2336
     *
2578
     *
2337
     * @param string $path
2579
     * @param string $path
2338
     * @param int $i
2580
     * @param int $i
2339
     * @return bool
2581
     * @return bool
-
 
2582
     * @access private
2340
     */
2583
     */
2341
    private function delete_recursive($path, &$i)
2584
    private function delete_recursive($path, &$i)
2342
    {
2585
    {
2343
        if (!$this->read_put_responses($i)) {
2586
        if (!$this->read_put_responses($i)) {
2344
            return false;
2587
            return false;
Line 2347... Line 2590...
2347
        $entries = $this->readlist($path, true);
2590
        $entries = $this->readlist($path, true);
2348
 
2591
 
2349
        // normally $entries would have at least . and .. but it might not if the directories
2592
        // normally $entries would have at least . and .. but it might not if the directories
2350
        // permissions didn't allow reading
2593
        // permissions didn't allow reading
2351
        if (empty($entries)) {
2594
        if (empty($entries)) {
2352
            $entries = [];
2595
            return false;
2353
        }
2596
        }
2354
 
2597
 
2355
        unset($entries['.'], $entries['..']);
2598
        unset($entries['.'], $entries['..']);
2356
        foreach ($entries as $filename => $props) {
2599
        foreach ($entries as $filename => $props) {
2357
            if (!isset($props['type'])) {
2600
            if (!isset($props['type'])) {
2358
                return false;
2601
                return false;
2359
            }
2602
            }
2360
 
2603
 
2361
            $temp = $path . '/' . $filename;
2604
            $temp = $path . '/' . $filename;
2362
            if ($props['type'] == FileType::DIRECTORY) {
2605
            if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
2363
                if (!$this->delete_recursive($temp, $i)) {
2606
                if (!$this->delete_recursive($temp, $i)) {
2364
                    return false;
2607
                    return false;
2365
                }
2608
                }
2366
            } else {
2609
            } else {
2367
                $this->send_sftp_packet(SFTPPacketType::REMOVE, Strings::packSSH2('s', $temp));
2610
                $this->send_sftp_packet(NET_SFTP_REMOVE, Strings::packSSH2('s', $temp));
2368
                $this->remove_from_stat_cache($temp);
2611
                $this->remove_from_stat_cache($temp);
2369
 
2612
 
2370
                $i++;
2613
                $i++;
2371
 
2614
 
2372
                if ($i >= $this->queueSize) {
2615
                if ($i >= NET_SFTP_QUEUE_SIZE) {
2373
                    if (!$this->read_put_responses($i)) {
2616
                    if (!$this->read_put_responses($i)) {
2374
                        return false;
2617
                        return false;
2375
                    }
2618
                    }
2376
                    $i = 0;
2619
                    $i = 0;
2377
                }
2620
                }
2378
            }
2621
            }
2379
        }
2622
        }
2380
 
2623
 
2381
        $this->send_sftp_packet(SFTPPacketType::RMDIR, Strings::packSSH2('s', $path));
2624
        $this->send_sftp_packet(NET_SFTP_RMDIR, Strings::packSSH2('s', $path));
2382
        $this->remove_from_stat_cache($path);
2625
        $this->remove_from_stat_cache($path);
2383
 
2626
 
2384
        $i++;
2627
        $i++;
2385
 
2628
 
2386
        if ($i >= $this->queueSize) {
2629
        if ($i >= NET_SFTP_QUEUE_SIZE) {
2387
            if (!$this->read_put_responses($i)) {
2630
            if (!$this->read_put_responses($i)) {
2388
                return false;
2631
                return false;
2389
            }
2632
            }
2390
            $i = 0;
2633
            $i = 0;
2391
        }
2634
        }
Line 2396... Line 2639...
2396
    /**
2639
    /**
2397
     * Checks whether a file or directory exists
2640
     * Checks whether a file or directory exists
2398
     *
2641
     *
2399
     * @param string $path
2642
     * @param string $path
2400
     * @return bool
2643
     * @return bool
-
 
2644
     * @access public
2401
     */
2645
     */
2402
    public function file_exists($path)
2646
    public function file_exists($path)
2403
    {
2647
    {
2404
        if ($this->use_stat_cache) {
2648
        if ($this->use_stat_cache) {
2405
            if (!$this->precheck()) {
2649
            if (!$this->precheck()) {
Line 2422... Line 2666...
2422
    /**
2666
    /**
2423
     * Tells whether the filename is a directory
2667
     * Tells whether the filename is a directory
2424
     *
2668
     *
2425
     * @param string $path
2669
     * @param string $path
2426
     * @return bool
2670
     * @return bool
-
 
2671
     * @access public
2427
     */
2672
     */
2428
    public function is_dir($path)
2673
    public function is_dir($path)
2429
    {
2674
    {
2430
        $result = $this->get_stat_cache_prop($path, 'type');
2675
        $result = $this->get_stat_cache_prop($path, 'type');
2431
        if ($result === false) {
2676
        if ($result === false) {
2432
            return false;
2677
            return false;
2433
        }
2678
        }
2434
        return $result === FileType::DIRECTORY;
2679
        return $result === NET_SFTP_TYPE_DIRECTORY;
2435
    }
2680
    }
2436
 
2681
 
2437
    /**
2682
    /**
2438
     * Tells whether the filename is a regular file
2683
     * Tells whether the filename is a regular file
2439
     *
2684
     *
2440
     * @param string $path
2685
     * @param string $path
2441
     * @return bool
2686
     * @return bool
-
 
2687
     * @access public
2442
     */
2688
     */
2443
    public function is_file($path)
2689
    public function is_file($path)
2444
    {
2690
    {
2445
        $result = $this->get_stat_cache_prop($path, 'type');
2691
        $result = $this->get_stat_cache_prop($path, 'type');
2446
        if ($result === false) {
2692
        if ($result === false) {
2447
            return false;
2693
            return false;
2448
        }
2694
        }
2449
        return $result === FileType::REGULAR;
2695
        return $result === NET_SFTP_TYPE_REGULAR;
2450
    }
2696
    }
2451
 
2697
 
2452
    /**
2698
    /**
2453
     * Tells whether the filename is a symbolic link
2699
     * Tells whether the filename is a symbolic link
2454
     *
2700
     *
2455
     * @param string $path
2701
     * @param string $path
2456
     * @return bool
2702
     * @return bool
-
 
2703
     * @access public
2457
     */
2704
     */
2458
    public function is_link($path)
2705
    public function is_link($path)
2459
    {
2706
    {
2460
        $result = $this->get_lstat_cache_prop($path, 'type');
2707
        $result = $this->get_lstat_cache_prop($path, 'type');
2461
        if ($result === false) {
2708
        if ($result === false) {
2462
            return false;
2709
            return false;
2463
        }
2710
        }
2464
        return $result === FileType::SYMLINK;
2711
        return $result === NET_SFTP_TYPE_SYMLINK;
2465
    }
2712
    }
2466
 
2713
 
2467
    /**
2714
    /**
2468
     * Tells whether a file exists and is readable
2715
     * Tells whether a file exists and is readable
2469
     *
2716
     *
2470
     * @param string $path
2717
     * @param string $path
2471
     * @return bool
2718
     * @return bool
-
 
2719
     * @access public
2472
     */
2720
     */
2473
    public function is_readable($path)
2721
    public function is_readable($path)
2474
    {
2722
    {
2475
        if (!$this->precheck()) {
2723
        if (!$this->precheck()) {
2476
            return false;
2724
            return false;
2477
        }
2725
        }
2478
 
2726
 
2479
        $packet = Strings::packSSH2('sNN', $this->realpath($path), OpenFlag::READ, 0);
2727
        $packet = Strings::packSSH2('sNN', $this->realpath($path), NET_SFTP_OPEN_READ, 0);
2480
        $this->send_sftp_packet(SFTPPacketType::OPEN, $packet);
2728
        $this->send_sftp_packet(NET_SFTP_OPEN, $packet);
2481
 
2729
 
2482
        $response = $this->get_sftp_packet();
2730
        $response = $this->get_sftp_packet();
2483
        switch ($this->packet_type) {
2731
        switch ($this->packet_type) {
2484
            case SFTPPacketType::HANDLE:
2732
            case NET_SFTP_HANDLE:
2485
                return true;
2733
                return true;
2486
            case SFTPPacketType::STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
2734
            case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
2487
                return false;
2735
                return false;
2488
            default:
2736
            default:
2489
                throw new \UnexpectedValueException('Expected PacketType::HANDLE or PacketType::STATUS. '
2737
                throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
2490
                                                  . 'Got packet type: ' . $this->packet_type);
2738
                                                  . 'Got packet type: ' . $this->packet_type);
2491
        }
2739
        }
2492
    }
2740
    }
2493
 
2741
 
2494
    /**
2742
    /**
2495
     * Tells whether the filename is writable
2743
     * Tells whether the filename is writable
2496
     *
2744
     *
2497
     * @param string $path
2745
     * @param string $path
2498
     * @return bool
2746
     * @return bool
-
 
2747
     * @access public
2499
     */
2748
     */
2500
    public function is_writable($path)
2749
    public function is_writable($path)
2501
    {
2750
    {
2502
        if (!$this->precheck()) {
2751
        if (!$this->precheck()) {
2503
            return false;
2752
            return false;
2504
        }
2753
        }
2505
 
2754
 
2506
        $packet = Strings::packSSH2('sNN', $this->realpath($path), OpenFlag::WRITE, 0);
2755
        $packet = Strings::packSSH2('sNN', $this->realpath($path), NET_SFTP_OPEN_WRITE, 0);
2507
        $this->send_sftp_packet(SFTPPacketType::OPEN, $packet);
2756
        $this->send_sftp_packet(NET_SFTP_OPEN, $packet);
2508
 
2757
 
2509
        $response = $this->get_sftp_packet();
2758
        $response = $this->get_sftp_packet();
2510
        switch ($this->packet_type) {
2759
        switch ($this->packet_type) {
2511
            case SFTPPacketType::HANDLE:
2760
            case NET_SFTP_HANDLE:
2512
                return true;
2761
                return true;
2513
            case SFTPPacketType::STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
2762
            case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
2514
                return false;
2763
                return false;
2515
            default:
2764
            default:
2516
                throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS. '
2765
                throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS. '
2517
                                                  . 'Got packet type: ' . $this->packet_type);
2766
                                                  . 'Got packet type: ' . $this->packet_type);
2518
        }
2767
        }
Line 2523... Line 2772...
2523
     *
2772
     *
2524
     * Alias of is_writable
2773
     * Alias of is_writable
2525
     *
2774
     *
2526
     * @param string $path
2775
     * @param string $path
2527
     * @return bool
2776
     * @return bool
-
 
2777
     * @access public
2528
     */
2778
     */
2529
    public function is_writeable($path)
2779
    public function is_writeable($path)
2530
    {
2780
    {
2531
        return $this->is_writable($path);
2781
        return $this->is_writable($path);
2532
    }
2782
    }
Line 2534... Line 2784...
2534
    /**
2784
    /**
2535
     * Gets last access time of file
2785
     * Gets last access time of file
2536
     *
2786
     *
2537
     * @param string $path
2787
     * @param string $path
2538
     * @return mixed
2788
     * @return mixed
-
 
2789
     * @access public
2539
     */
2790
     */
2540
    public function fileatime($path)
2791
    public function fileatime($path)
2541
    {
2792
    {
2542
        return $this->get_stat_cache_prop($path, 'atime');
2793
        return $this->get_stat_cache_prop($path, 'atime');
2543
    }
2794
    }
Line 2545... Line 2796...
2545
    /**
2796
    /**
2546
     * Gets file modification time
2797
     * Gets file modification time
2547
     *
2798
     *
2548
     * @param string $path
2799
     * @param string $path
2549
     * @return mixed
2800
     * @return mixed
-
 
2801
     * @access public
2550
     */
2802
     */
2551
    public function filemtime($path)
2803
    public function filemtime($path)
2552
    {
2804
    {
2553
        return $this->get_stat_cache_prop($path, 'mtime');
2805
        return $this->get_stat_cache_prop($path, 'mtime');
2554
    }
2806
    }
Line 2556... Line 2808...
2556
    /**
2808
    /**
2557
     * Gets file permissions
2809
     * Gets file permissions
2558
     *
2810
     *
2559
     * @param string $path
2811
     * @param string $path
2560
     * @return mixed
2812
     * @return mixed
-
 
2813
     * @access public
2561
     */
2814
     */
2562
    public function fileperms($path)
2815
    public function fileperms($path)
2563
    {
2816
    {
2564
        return $this->get_stat_cache_prop($path, 'mode');
2817
        return $this->get_stat_cache_prop($path, 'mode');
2565
    }
2818
    }
Line 2567... Line 2820...
2567
    /**
2820
    /**
2568
     * Gets file owner
2821
     * Gets file owner
2569
     *
2822
     *
2570
     * @param string $path
2823
     * @param string $path
2571
     * @return mixed
2824
     * @return mixed
-
 
2825
     * @access public
2572
     */
2826
     */
2573
    public function fileowner($path)
2827
    public function fileowner($path)
2574
    {
2828
    {
2575
        return $this->get_stat_cache_prop($path, 'uid');
2829
        return $this->get_stat_cache_prop($path, 'uid');
2576
    }
2830
    }
Line 2578... Line 2832...
2578
    /**
2832
    /**
2579
     * Gets file group
2833
     * Gets file group
2580
     *
2834
     *
2581
     * @param string $path
2835
     * @param string $path
2582
     * @return mixed
2836
     * @return mixed
-
 
2837
     * @access public
2583
     */
2838
     */
2584
    public function filegroup($path)
2839
    public function filegroup($path)
2585
    {
2840
    {
2586
        return $this->get_stat_cache_prop($path, 'gid');
2841
        return $this->get_stat_cache_prop($path, 'gid');
2587
    }
2842
    }
Line 2589... Line 2844...
2589
    /**
2844
    /**
2590
     * Gets file size
2845
     * Gets file size
2591
     *
2846
     *
2592
     * @param string $path
2847
     * @param string $path
2593
     * @return mixed
2848
     * @return mixed
-
 
2849
     * @access public
2594
     */
2850
     */
2595
    public function filesize($path)
2851
    public function filesize($path)
2596
    {
2852
    {
2597
        return $this->get_stat_cache_prop($path, 'size');
2853
        return $this->get_stat_cache_prop($path, 'size');
2598
    }
2854
    }
Line 2600... Line 2856...
2600
    /**
2856
    /**
2601
     * Gets file type
2857
     * Gets file type
2602
     *
2858
     *
2603
     * @param string $path
2859
     * @param string $path
2604
     * @return string|false
2860
     * @return string|false
-
 
2861
     * @access public
2605
     */
2862
     */
2606
    public function filetype($path)
2863
    public function filetype($path)
2607
    {
2864
    {
2608
        $type = $this->get_stat_cache_prop($path, 'type');
2865
        $type = $this->get_stat_cache_prop($path, 'type');
2609
        if ($type === false) {
2866
        if ($type === false) {
2610
            return false;
2867
            return false;
2611
        }
2868
        }
2612
 
2869
 
2613
        switch ($type) {
2870
        switch ($type) {
2614
            case FileType::BLOCK_DEVICE:
2871
            case NET_SFTP_TYPE_BLOCK_DEVICE:
2615
                return 'block';
2872
                return 'block';
2616
            case FileType::CHAR_DEVICE:
2873
            case NET_SFTP_TYPE_CHAR_DEVICE:
2617
                return 'char';
2874
                return 'char';
2618
            case FileType::DIRECTORY:
2875
            case NET_SFTP_TYPE_DIRECTORY:
2619
                return 'dir';
2876
                return 'dir';
2620
            case FileType::FIFO:
2877
            case NET_SFTP_TYPE_FIFO:
2621
                return 'fifo';
2878
                return 'fifo';
2622
            case FileType::REGULAR:
2879
            case NET_SFTP_TYPE_REGULAR:
2623
                return 'file';
2880
                return 'file';
2624
            case FileType::SYMLINK:
2881
            case NET_SFTP_TYPE_SYMLINK:
2625
                return 'link';
2882
                return 'link';
2626
            default:
2883
            default:
2627
                return false;
2884
                return false;
2628
        }
2885
        }
2629
    }
2886
    }
Line 2634... Line 2891...
2634
     * Uses cache if appropriate.
2891
     * Uses cache if appropriate.
2635
     *
2892
     *
2636
     * @param string $path
2893
     * @param string $path
2637
     * @param string $prop
2894
     * @param string $prop
2638
     * @return mixed
2895
     * @return mixed
-
 
2896
     * @access private
2639
     */
2897
     */
2640
    private function get_stat_cache_prop($path, $prop)
2898
    private function get_stat_cache_prop($path, $prop)
2641
    {
2899
    {
2642
        return $this->get_xstat_cache_prop($path, $prop, 'stat');
2900
        return $this->get_xstat_cache_prop($path, $prop, 'stat');
2643
    }
2901
    }
Line 2648... Line 2906...
2648
     * Uses cache if appropriate.
2906
     * Uses cache if appropriate.
2649
     *
2907
     *
2650
     * @param string $path
2908
     * @param string $path
2651
     * @param string $prop
2909
     * @param string $prop
2652
     * @return mixed
2910
     * @return mixed
-
 
2911
     * @access private
2653
     */
2912
     */
2654
    private function get_lstat_cache_prop($path, $prop)
2913
    private function get_lstat_cache_prop($path, $prop)
2655
    {
2914
    {
2656
        return $this->get_xstat_cache_prop($path, $prop, 'lstat');
2915
        return $this->get_xstat_cache_prop($path, $prop, 'lstat');
2657
    }
2916
    }
Line 2663... Line 2922...
2663
     *
2922
     *
2664
     * @param string $path
2923
     * @param string $path
2665
     * @param string $prop
2924
     * @param string $prop
2666
     * @param string $type
2925
     * @param string $type
2667
     * @return mixed
2926
     * @return mixed
-
 
2927
     * @access private
2668
     */
2928
     */
2669
    private function get_xstat_cache_prop($path, $prop, $type)
2929
    private function get_xstat_cache_prop($path, $prop, $type)
2670
    {
2930
    {
2671
        if (!$this->precheck()) {
2931
        if (!$this->precheck()) {
2672
            return false;
2932
            return false;
Line 2698... Line 2958...
2698
     *
2958
     *
2699
     * @param string $oldname
2959
     * @param string $oldname
2700
     * @param string $newname
2960
     * @param string $newname
2701
     * @return bool
2961
     * @return bool
2702
     * @throws \UnexpectedValueException on receipt of unexpected packets
2962
     * @throws \UnexpectedValueException on receipt of unexpected packets
-
 
2963
     * @access public
2703
     */
2964
     */
2704
    public function rename($oldname, $newname)
2965
    public function rename($oldname, $newname)
2705
    {
2966
    {
2706
        if (!$this->precheck()) {
2967
        if (!$this->precheck()) {
2707
            return false;
2968
            return false;
Line 2725... Line 2986...
2725
                   SSH_FXP_RENAME_NATIVE     0x00000004
2986
                   SSH_FXP_RENAME_NATIVE     0x00000004
2726
 
2987
 
2727
               (none of these are currently supported) */
2988
               (none of these are currently supported) */
2728
            $packet .= "\0\0\0\0";
2989
            $packet .= "\0\0\0\0";
2729
        }
2990
        }
2730
        $this->send_sftp_packet(SFTPPacketType::RENAME, $packet);
2991
        $this->send_sftp_packet(NET_SFTP_RENAME, $packet);
2731
 
2992
 
2732
        $response = $this->get_sftp_packet();
2993
        $response = $this->get_sftp_packet();
2733
        if ($this->packet_type != SFTPPacketType::STATUS) {
2994
        if ($this->packet_type != NET_SFTP_STATUS) {
2734
            throw new \UnexpectedValueException('Expected PacketType::STATUS. '
2995
            throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
2735
                                              . 'Got packet type: ' . $this->packet_type);
2996
                                              . 'Got packet type: ' . $this->packet_type);
2736
        }
2997
        }
2737
 
2998
 
2738
        // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
2999
        // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
2739
        list($status) = Strings::unpackSSH2('N', $response);
3000
        list($status) = Strings::unpackSSH2('N', $response);
2740
        if ($status != StatusCode::OK) {
3001
        if ($status != NET_SFTP_STATUS_OK) {
2741
            $this->logError($response, $status);
3002
            $this->logError($response, $status);
2742
            return false;
3003
            return false;
2743
        }
3004
        }
2744
 
3005
 
2745
        // don't move the stat cache entry over since this operation could very well change the
3006
        // don't move the stat cache entry over since this operation could very well change the
Line 2758... Line 3019...
2758
     *
3019
     *
2759
     * @param string $key
3020
     * @param string $key
2760
     * @param int $flags
3021
     * @param int $flags
2761
     * @param string $response
3022
     * @param string $response
2762
     * @return array
3023
     * @return array
-
 
3024
     * @access private
2763
     */
3025
     */
2764
    private function parseTime($key, $flags, &$response)
3026
    private function parseTime($key, $flags, &$response)
2765
    {
3027
    {
2766
        $attr = [];
3028
        $attr = [];
2767
        list($attr[$key]) = Strings::unpackSSH2('Q', $response);
3029
        list($attr[$key]) = Strings::unpackSSH2('Q', $response);
2768
        if ($flags & Attribute::SUBSECOND_TIMES) {
3030
        if ($flags & NET_SFTP_ATTR_SUBSECOND_TIMES) {
2769
            list($attr[$key . '-nseconds']) = Strings::unpackSSH2('N', $response);
3031
            list($attr[$key . '-nseconds']) = Strings::unpackSSH2('N', $response);
2770
        }
3032
        }
2771
        return $attr;
3033
        return $attr;
2772
    }
3034
    }
2773
 
3035
 
Line 2776... Line 3038...
2776
     *
3038
     *
2777
     * See '7.  File Attributes' of draft-ietf-secsh-filexfer-13 for more info.
3039
     * See '7.  File Attributes' of draft-ietf-secsh-filexfer-13 for more info.
2778
     *
3040
     *
2779
     * @param string $response
3041
     * @param string $response
2780
     * @return array
3042
     * @return array
-
 
3043
     * @access private
2781
     */
3044
     */
2782
    protected function parseAttributes(&$response)
3045
    protected function parseAttributes(&$response)
2783
    {
3046
    {
2784
        if ($this->version >= 4) {
3047
        if ($this->version >= 4) {
2785
            list($flags, $attr['type']) = Strings::unpackSSH2('NC', $response);
3048
            list($flags, $attr['type']) = Strings::unpackSSH2('NC', $response);
2786
        } else {
3049
        } else {
2787
            list($flags) = Strings::unpackSSH2('N', $response);
3050
            list($flags) = Strings::unpackSSH2('N', $response);
2788
        }
3051
        }
2789
 
3052
 
2790
        foreach (Attribute::getConstants() as $value => $key) {
3053
        foreach ($this->attributes as $key => $value) {
2791
            switch ($flags & $key) {
3054
            switch ($flags & $key) {
2792
                case Attribute::UIDGID:
3055
                case NET_SFTP_ATTR_UIDGID:
2793
                    if ($this->version > 3) {
3056
                    if ($this->version > 3) {
2794
                        continue 2;
3057
                        continue 2;
2795
                    }
3058
                    }
2796
                    break;
3059
                    break;
2797
                case Attribute::CREATETIME:
3060
                case NET_SFTP_ATTR_CREATETIME:
2798
                case Attribute::MODIFYTIME:
3061
                case NET_SFTP_ATTR_MODIFYTIME:
2799
                case Attribute::ACL:
3062
                case NET_SFTP_ATTR_ACL:
2800
                case Attribute::OWNERGROUP:
3063
                case NET_SFTP_ATTR_OWNERGROUP:
2801
                case Attribute::SUBSECOND_TIMES:
3064
                case NET_SFTP_ATTR_SUBSECOND_TIMES:
2802
                    if ($this->version < 4) {
3065
                    if ($this->version < 4) {
2803
                        continue 2;
3066
                        continue 2;
2804
                    }
3067
                    }
2805
                    break;
3068
                    break;
2806
                case Attribute::BITS:
3069
                case NET_SFTP_ATTR_BITS:
2807
                    if ($this->version < 5) {
3070
                    if ($this->version < 5) {
2808
                        continue 2;
3071
                        continue 2;
2809
                    }
3072
                    }
2810
                    break;
3073
                    break;
2811
                case Attribute::ALLOCATION_SIZE:
3074
                case NET_SFTP_ATTR_ALLOCATION_SIZE:
2812
                case Attribute::TEXT_HINT:
3075
                case NET_SFTP_ATTR_TEXT_HINT:
2813
                case Attribute::MIME_TYPE:
3076
                case NET_SFTP_ATTR_MIME_TYPE:
2814
                case Attribute::LINK_COUNT:
3077
                case NET_SFTP_ATTR_LINK_COUNT:
2815
                case Attribute::UNTRANSLATED_NAME:
3078
                case NET_SFTP_ATTR_UNTRANSLATED_NAME:
2816
                case Attribute::CTIME:
3079
                case NET_SFTP_ATTR_CTIME:
2817
                    if ($this->version < 6) {
3080
                    if ($this->version < 6) {
2818
                        continue 2;
3081
                        continue 2;
2819
                    }
3082
                    }
2820
            }
3083
            }
2821
            switch ($flags & $key) {
3084
            switch ($flags & $key) {
2822
                case Attribute::SIZE:             // 0x00000001
3085
                case NET_SFTP_ATTR_SIZE:             // 0x00000001
2823
                    // The size attribute is defined as an unsigned 64-bit integer.
3086
                    // The size attribute is defined as an unsigned 64-bit integer.
2824
                    // The following will use floats on 32-bit platforms, if necessary.
3087
                    // The following will use floats on 32-bit platforms, if necessary.
2825
                    // As can be seen in the BigInteger class, floats are generally
3088
                    // As can be seen in the BigInteger class, floats are generally
2826
                    // IEEE 754 binary64 "double precision" on such platforms and
3089
                    // IEEE 754 binary64 "double precision" on such platforms and
2827
                    // as such can represent integers of at least 2^50 without loss
3090
                    // as such can represent integers of at least 2^50 without loss
2828
                    // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB.
3091
                    // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB.
2829
                    list($attr['size']) = Strings::unpackSSH2('Q', $response);
3092
                    list($attr['size']) = Strings::unpackSSH2('Q', $response);
2830
                    break;
3093
                    break;
2831
                case Attribute::UIDGID: // 0x00000002 (SFTPv3 only)
3094
                case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only)
2832
                    list($attr['uid'], $attr['gid']) = Strings::unpackSSH2('NN', $response);
3095
                    list($attr['uid'], $attr['gid']) = Strings::unpackSSH2('NN', $response);
2833
                    break;
3096
                    break;
2834
                case Attribute::PERMISSIONS: // 0x00000004
3097
                case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
2835
                    list($attr['mode']) = Strings::unpackSSH2('N', $response);
3098
                    list($attr['mode']) = Strings::unpackSSH2('N', $response);
2836
                    $fileType = $this->parseMode($attr['mode']);
3099
                    $fileType = $this->parseMode($attr['mode']);
2837
                    if ($this->version < 4 && $fileType !== false) {
3100
                    if ($this->version < 4 && $fileType !== false) {
2838
                        $attr += ['type' => $fileType];
3101
                        $attr += ['type' => $fileType];
2839
                    }
3102
                    }
2840
                    break;
3103
                    break;
2841
                case Attribute::ACCESSTIME: // 0x00000008
3104
                case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
2842
                    if ($this->version >= 4) {
3105
                    if ($this->version >= 4) {
2843
                        $attr += $this->parseTime('atime', $flags, $response);
3106
                        $attr += $this->parseTime('atime', $flags, $response);
2844
                        break;
3107
                        break;
2845
                    }
3108
                    }
2846
                    list($attr['atime'], $attr['mtime']) = Strings::unpackSSH2('NN', $response);
3109
                    list($attr['atime'], $attr['mtime']) = Strings::unpackSSH2('NN', $response);
2847
                    break;
3110
                    break;
2848
                case Attribute::CREATETIME:       // 0x00000010 (SFTPv4+)
3111
                case NET_SFTP_ATTR_CREATETIME:       // 0x00000010 (SFTPv4+)
2849
                    $attr += $this->parseTime('createtime', $flags, $response);
3112
                    $attr += $this->parseTime('createtime', $flags, $response);
2850
                    break;
3113
                    break;
2851
                case Attribute::MODIFYTIME:       // 0x00000020
3114
                case NET_SFTP_ATTR_MODIFYTIME:       // 0x00000020
2852
                    $attr += $this->parseTime('mtime', $flags, $response);
3115
                    $attr += $this->parseTime('mtime', $flags, $response);
2853
                    break;
3116
                    break;
2854
                case Attribute::ACL:              // 0x00000040
3117
                case NET_SFTP_ATTR_ACL:              // 0x00000040
2855
                    // access control list
3118
                    // access control list
2856
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-04#section-5.7
3119
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-04#section-5.7
2857
                    // currently unsupported
3120
                    // currently unsupported
2858
                    list($count) = Strings::unpackSSH2('N', $response);
3121
                    list($count) = Strings::unpackSSH2('N', $response);
2859
                    for ($i = 0; $i < $count; $i++) {
3122
                    for ($i = 0; $i < $count; $i++) {
2860
                        list($type, $flag, $mask, $who) = Strings::unpackSSH2('N3s', $result);
3123
                        list($type, $flag, $mask, $who) = Strings::unpackSSH2('N3s', $result);
2861
                    }
3124
                    }
2862
                    break;
3125
                    break;
2863
                case Attribute::OWNERGROUP:       // 0x00000080
3126
                case NET_SFTP_ATTR_OWNERGROUP:       // 0x00000080
2864
                    list($attr['owner'], $attr['$group']) = Strings::unpackSSH2('ss', $response);
3127
                    list($attr['owner'], $attr['$group']) = Strings::unpackSSH2('ss', $response);
2865
                    break;
3128
                    break;
2866
                case Attribute::SUBSECOND_TIMES:  // 0x00000100
3129
                case NET_SFTP_ATTR_SUBSECOND_TIMES:  // 0x00000100
2867
                    break;
3130
                    break;
2868
                case Attribute::BITS:             // 0x00000200 (SFTPv5+)
3131
                case NET_SFTP_ATTR_BITS:             // 0x00000200 (SFTPv5+)
2869
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-5.8
3132
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-5.8
2870
                    // currently unsupported
3133
                    // currently unsupported
2871
                    // tells if you file is:
3134
                    // tells if you file is:
2872
                    // readonly, system, hidden, case inensitive, archive, encrypted, compressed, sparse
3135
                    // readonly, system, hidden, case inensitive, archive, encrypted, compressed, sparse
2873
                    // append only, immutable, sync
3136
                    // append only, immutable, sync
2874
                    list($attrib_bits, $attrib_bits_valid) = Strings::unpackSSH2('N2', $response);
3137
                    list($attrib_bits, $attrib_bits_valid) = Strings::unpackSSH2('N2', $response);
2875
                    // if we were actually gonna implement the above it ought to be
3138
                    // if we were actually gonna implement the above it ought to be
2876
                    // $attr['attrib-bits'] and $attr['attrib-bits-valid']
3139
                    // $attr['attrib-bits'] and $attr['attrib-bits-valid']
2877
                    // eg. - instead of _
3140
                    // eg. - instead of _
2878
                    break;
3141
                    break;
2879
                case Attribute::ALLOCATION_SIZE:  // 0x00000400 (SFTPv6+)
3142
                case NET_SFTP_ATTR_ALLOCATION_SIZE:  // 0x00000400 (SFTPv6+)
2880
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.4
3143
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.4
2881
                    // represents the number of bytes that the file consumes on the disk. will
3144
                    // represents the number of bytes that the file consumes on the disk. will
2882
                    // usually be larger than the 'size' field
3145
                    // usually be larger than the 'size' field
2883
                    list($attr['allocation-size']) = Strings::unpackSSH2('Q', $response);
3146
                    list($attr['allocation-size']) = Strings::unpackSSH2('Q', $response);
2884
                    break;
3147
                    break;
2885
                case Attribute::TEXT_HINT:        // 0x00000800
3148
                case NET_SFTP_ATTR_TEXT_HINT:        // 0x00000800
2886
                    // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.10
3149
                    // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.10
2887
                    // currently unsupported
3150
                    // currently unsupported
2888
                    // tells if file is "known text", "guessed text", "known binary", "guessed binary"
3151
                    // tells if file is "known text", "guessed text", "known binary", "guessed binary"
2889
                    list($text_hint) = Strings::unpackSSH2('C', $response);
3152
                    list($text_hint) = Strings::unpackSSH2('C', $response);
2890
                    // the above should be $attr['text-hint']
3153
                    // the above should be $attr['text-hint']
2891
                    break;
3154
                    break;
2892
                case Attribute::MIME_TYPE:        // 0x00001000
3155
                case NET_SFTP_ATTR_MIME_TYPE:        // 0x00001000
2893
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.11
3156
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.11
2894
                    list($attr['mime-type']) = Strings::unpackSSH2('s', $response);
3157
                    list($attr['mime-type']) = Strings::unpackSSH2('s', $response);
2895
                    break;
3158
                    break;
2896
                case Attribute::LINK_COUNT:       // 0x00002000
3159
                case NET_SFTP_ATTR_LINK_COUNT:       // 0x00002000
2897
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.12
3160
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.12
2898
                    list($attr['link-count']) = Strings::unpackSSH2('N', $response);
3161
                    list($attr['link-count']) = Strings::unpackSSH2('N', $response);
2899
                    break;
3162
                    break;
2900
                case Attribute::UNTRANSLATED_NAME:// 0x00004000
3163
                case NET_SFTP_ATTR_UNTRANSLATED_NAME:// 0x00004000
2901
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.13
3164
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.13
2902
                    list($attr['untranslated-name']) = Strings::unpackSSH2('s', $response);
3165
                    list($attr['untranslated-name']) = Strings::unpackSSH2('s', $response);
2903
                    break;
3166
                    break;
2904
                case Attribute::CTIME:            // 0x00008000
3167
                case NET_SFTP_ATTR_CTIME:            // 0x00008000
2905
                    // 'ctime' contains the last time the file attributes were changed.  The
3168
                    // 'ctime' contains the last time the file attributes were changed.  The
2906
                    // exact meaning of this field depends on the server.
3169
                    // exact meaning of this field depends on the server.
2907
                    $attr += $this->parseTime('ctime', $flags, $response);
3170
                    $attr += $this->parseTime('ctime', $flags, $response);
2908
                    break;
3171
                    break;
2909
                case Attribute::EXTENDED: // 0x80000000
3172
                case NET_SFTP_ATTR_EXTENDED: // 0x80000000
2910
                    list($count) = Strings::unpackSSH2('N', $response);
3173
                    list($count) = Strings::unpackSSH2('N', $response);
2911
                    for ($i = 0; $i < $count; $i++) {
3174
                    for ($i = 0; $i < $count; $i++) {
2912
                        list($key, $value) = Strings::unpackSSH2('ss', $response);
3175
                        list($key, $value) = Strings::unpackSSH2('ss', $response);
2913
                        $attr[$key] = $value;
3176
                        $attr[$key] = $value;
2914
                    }
3177
                    }
Line 2922... Line 3185...
2922
     *
3185
     *
2923
     * Quoting the SFTP RFC, "Implementations MUST NOT send bits that are not defined" but they seem to anyway
3186
     * Quoting the SFTP RFC, "Implementations MUST NOT send bits that are not defined" but they seem to anyway
2924
     *
3187
     *
2925
     * @param int $mode
3188
     * @param int $mode
2926
     * @return int
3189
     * @return int
-
 
3190
     * @access private
2927
     */
3191
     */
2928
    private function parseMode($mode)
3192
    private function parseMode($mode)
2929
    {
3193
    {
2930
        // values come from http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L12
3194
        // values come from http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L12
2931
        // see, also, http://linux.die.net/man/2/stat
3195
        // see, also, http://linux.die.net/man/2/stat
2932
        switch ($mode & 0170000) {// ie. 1111 0000 0000 0000
3196
        switch ($mode & 0170000) {// ie. 1111 0000 0000 0000
2933
            case 0000000: // no file type specified - figure out the file type using alternative means
3197
            case 0000000: // no file type specified - figure out the file type using alternative means
2934
                return false;
3198
                return false;
2935
            case 0040000:
3199
            case 0040000:
2936
                return FileType::DIRECTORY;
3200
                return NET_SFTP_TYPE_DIRECTORY;
2937
            case 0100000:
3201
            case 0100000:
2938
                return FileType::REGULAR;
3202
                return NET_SFTP_TYPE_REGULAR;
2939
            case 0120000:
3203
            case 0120000:
2940
                return FileType::SYMLINK;
3204
                return NET_SFTP_TYPE_SYMLINK;
2941
            // new types introduced in SFTPv5+
3205
            // new types introduced in SFTPv5+
2942
            // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
3206
            // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
2943
            case 0010000: // named pipe (fifo)
3207
            case 0010000: // named pipe (fifo)
2944
                return FileType::FIFO;
3208
                return NET_SFTP_TYPE_FIFO;
2945
            case 0020000: // character special
3209
            case 0020000: // character special
2946
                return FileType::CHAR_DEVICE;
3210
                return NET_SFTP_TYPE_CHAR_DEVICE;
2947
            case 0060000: // block special
3211
            case 0060000: // block special
2948
                return FileType::BLOCK_DEVICE;
3212
                return NET_SFTP_TYPE_BLOCK_DEVICE;
2949
            case 0140000: // socket
3213
            case 0140000: // socket
2950
                return FileType::SOCKET;
3214
                return NET_SFTP_TYPE_SOCKET;
2951
            case 0160000: // whiteout
3215
            case 0160000: // whiteout
2952
                // "SPECIAL should be used for files that are of
3216
                // "SPECIAL should be used for files that are of
2953
                //  a known type which cannot be expressed in the protocol"
3217
                //  a known type which cannot be expressed in the protocol"
2954
                return FileType::SPECIAL;
3218
                return NET_SFTP_TYPE_SPECIAL;
2955
            default:
3219
            default:
2956
                return FileType::UNKNOWN;
3220
                return NET_SFTP_TYPE_UNKNOWN;
2957
        }
3221
        }
2958
    }
3222
    }
2959
 
3223
 
2960
    /**
3224
    /**
2961
     * Parse Longname
3225
     * Parse Longname
Line 2968... Line 3232...
2968
     *
3232
     *
2969
     * If the longname is in an unrecognized format bool(false) is returned.
3233
     * If the longname is in an unrecognized format bool(false) is returned.
2970
     *
3234
     *
2971
     * @param string $longname
3235
     * @param string $longname
2972
     * @return mixed
3236
     * @return mixed
-
 
3237
     * @access private
2973
     */
3238
     */
2974
    private function parseLongname($longname)
3239
    private function parseLongname($longname)
2975
    {
3240
    {
2976
        // http://en.wikipedia.org/wiki/Unix_file_types
3241
        // http://en.wikipedia.org/wiki/Unix_file_types
2977
        // http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions
3242
        // http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions
2978
        if (preg_match('#^[^/]([r-][w-][xstST-]){3}#', $longname)) {
3243
        if (preg_match('#^[^/]([r-][w-][xstST-]){3}#', $longname)) {
2979
            switch ($longname[0]) {
3244
            switch ($longname[0]) {
2980
                case '-':
3245
                case '-':
2981
                    return FileType::REGULAR;
3246
                    return NET_SFTP_TYPE_REGULAR;
2982
                case 'd':
3247
                case 'd':
2983
                    return FileType::DIRECTORY;
3248
                    return NET_SFTP_TYPE_DIRECTORY;
2984
                case 'l':
3249
                case 'l':
2985
                    return FileType::SYMLINK;
3250
                    return NET_SFTP_TYPE_SYMLINK;
2986
                default:
3251
                default:
2987
                    return FileType::SPECIAL;
3252
                    return NET_SFTP_TYPE_SPECIAL;
2988
            }
3253
            }
2989
        }
3254
        }
2990
 
3255
 
2991
        return false;
3256
        return false;
2992
    }
3257
    }
Line 3000... Line 3265...
3000
     * @param string $data
3265
     * @param string $data
3001
     * @param int $request_id
3266
     * @param int $request_id
3002
     * @see self::_get_sftp_packet()
3267
     * @see self::_get_sftp_packet()
3003
     * @see self::send_channel_packet()
3268
     * @see self::send_channel_packet()
3004
     * @return void
3269
     * @return void
-
 
3270
     * @access private
3005
     */
3271
     */
3006
    private function send_sftp_packet($type, $data, $request_id = 1)
3272
    private function send_sftp_packet($type, $data, $request_id = 1)
3007
    {
3273
    {
3008
        // in SSH2.php the timeout is cumulative per function call. eg. exec() will
3274
        // in SSH2.php the timeout is cumulative per function call. eg. exec() will
3009
        // timeout after 10s. but for SFTP.php it's cumulative per packet
3275
        // timeout after 10s. but for SFTP.php it's cumulative per packet
Line 3016... Line 3282...
3016
        $start = microtime(true);
3282
        $start = microtime(true);
3017
        $this->send_channel_packet(self::CHANNEL, $packet);
3283
        $this->send_channel_packet(self::CHANNEL, $packet);
3018
        $stop = microtime(true);
3284
        $stop = microtime(true);
3019
 
3285
 
3020
        if (defined('NET_SFTP_LOGGING')) {
3286
        if (defined('NET_SFTP_LOGGING')) {
3021
            $packet_type = sprintf(
3287
            $packet_type = '-> ' . $this->packet_types[$type] .
3022
                '-> %s (%ss)',
-
 
3023
                SFTPPacketType::getConstantNameByValue($type),
-
 
3024
                round($stop - $start, 4)
3288
                           ' (' . round($stop - $start, 4) . 's)';
3025
            );
-
 
3026
            if (NET_SFTP_LOGGING == self::LOG_REALTIME) {
3289
            if (NET_SFTP_LOGGING == self::LOG_REALTIME) {
3027
                switch (PHP_SAPI) {
3290
                switch (PHP_SAPI) {
3028
                    case 'cli':
3291
                    case 'cli':
3029
                        $start = $stop = "\r\n";
3292
                        $start = $stop = "\r\n";
3030
                        break;
3293
                        break;
Line 3046... Line 3309...
3046
 
3309
 
3047
    /**
3310
    /**
3048
     * Resets a connection for re-use
3311
     * Resets a connection for re-use
3049
     *
3312
     *
3050
     * @param int $reason
3313
     * @param int $reason
-
 
3314
     * @access private
3051
     */
3315
     */
3052
    protected function reset_connection($reason)
3316
    protected function reset_connection($reason)
3053
    {
3317
    {
3054
        parent::reset_connection($reason);
3318
        parent::reset_connection($reason);
3055
        $this->use_request_id = false;
3319
        $this->use_request_id = false;
Line 3066... Line 3330...
3066
     * There can be one SSH_MSG_CHANNEL_DATA messages containing two SFTP packets or there can be two SSH_MSG_CHANNEL_DATA
3330
     * There can be one SSH_MSG_CHANNEL_DATA messages containing two SFTP packets or there can be two SSH_MSG_CHANNEL_DATA
3067
     * messages containing one SFTP packet.
3331
     * messages containing one SFTP packet.
3068
     *
3332
     *
3069
     * @see self::_send_sftp_packet()
3333
     * @see self::_send_sftp_packet()
3070
     * @return string
3334
     * @return string
-
 
3335
     * @access private
3071
     */
3336
     */
3072
    private function get_sftp_packet($request_id = null)
3337
    private function get_sftp_packet($request_id = null)
3073
    {
3338
    {
3074
        $this->channel_close = false;
3339
        $this->channel_close = false;
3075
 
3340
 
Line 3088... Line 3353...
3088
 
3353
 
3089
        // SFTP packet length
3354
        // SFTP packet length
3090
        while (strlen($this->packet_buffer) < 4) {
3355
        while (strlen($this->packet_buffer) < 4) {
3091
            $temp = $this->get_channel_packet(self::CHANNEL, true);
3356
            $temp = $this->get_channel_packet(self::CHANNEL, true);
3092
            if ($temp === true) {
3357
            if ($temp === true) {
3093
                if ($this->channel_status[self::CHANNEL] === SSH2MessageType::CHANNEL_CLOSE) {
3358
                if ($this->channel_status[self::CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) {
3094
                    $this->channel_close = true;
3359
                    $this->channel_close = true;
3095
                }
3360
                }
3096
                $this->packet_type = false;
3361
                $this->packet_type = false;
3097
                $this->packet_buffer = '';
3362
                $this->packet_buffer = '';
3098
                return false;
3363
                return false;
Line 3137... Line 3402...
3137
        }
3402
        }
3138
 
3403
 
3139
        $packet = Strings::shift($this->packet_buffer, $length);
3404
        $packet = Strings::shift($this->packet_buffer, $length);
3140
 
3405
 
3141
        if (defined('NET_SFTP_LOGGING')) {
3406
        if (defined('NET_SFTP_LOGGING')) {
3142
            $packet_type = sprintf(
-
 
3143
                '<- %s (%ss)',
-
 
3144
                SFTPPacketType::getConstantNameByValue($this->packet_type),
3407
            $packet_type = '<- ' . $this->packet_types[$this->packet_type] .
3145
                round($stop - $start, 4)
3408
                           ' (' . round($stop - $start, 4) . 's)';
3146
            );
-
 
3147
            if (NET_SFTP_LOGGING == self::LOG_REALTIME) {
3409
            if (NET_SFTP_LOGGING == self::LOG_REALTIME) {
3148
                switch (PHP_SAPI) {
3410
                switch (PHP_SAPI) {
3149
                    case 'cli':
3411
                    case 'cli':
3150
                        $start = $stop = "\r\n";
3412
                        $start = $stop = "\r\n";
3151
                        break;
3413
                        break;
Line 3176... Line 3438...
3176
    }
3438
    }
3177
 
3439
 
3178
    /**
3440
    /**
3179
     * Returns a log of the packets that have been sent and received.
3441
     * Returns a log of the packets that have been sent and received.
3180
     *
3442
     *
3181
     * Returns a string if PacketType::LOGGING == self::LOG_COMPLEX, an array if PacketType::LOGGING == self::LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING')
3443
     * Returns a string if NET_SFTP_LOGGING == self::LOG_COMPLEX, an array if NET_SFTP_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING')
3182
     *
3444
     *
-
 
3445
     * @access public
3183
     * @return array|string
3446
     * @return array|string
3184
     */
3447
     */
3185
    public function getSFTPLog()
3448
    public function getSFTPLog()
3186
    {
3449
    {
3187
        if (!defined('NET_SFTP_LOGGING')) {
3450
        if (!defined('NET_SFTP_LOGGING')) {
Line 3200... Line 3463...
3200
 
3463
 
3201
    /**
3464
    /**
3202
     * Returns all errors
3465
     * Returns all errors
3203
     *
3466
     *
3204
     * @return array
3467
     * @return array
-
 
3468
     * @access public
3205
     */
3469
     */
3206
    public function getSFTPErrors()
3470
    public function getSFTPErrors()
3207
    {
3471
    {
3208
        return $this->sftp_errors;
3472
        return $this->sftp_errors;
3209
    }
3473
    }
3210
 
3474
 
3211
    /**
3475
    /**
3212
     * Returns the last error
3476
     * Returns the last error
3213
     *
3477
     *
3214
     * @return string
3478
     * @return string
-
 
3479
     * @access public
3215
     */
3480
     */
3216
    public function getLastSFTPError()
3481
    public function getLastSFTPError()
3217
    {
3482
    {
3218
        return count($this->sftp_errors) ? $this->sftp_errors[count($this->sftp_errors) - 1] : '';
3483
        return count($this->sftp_errors) ? $this->sftp_errors[count($this->sftp_errors) - 1] : '';
3219
    }
3484
    }
3220
 
3485
 
3221
    /**
3486
    /**
3222
     * Get supported SFTP versions
3487
     * Get supported SFTP versions
3223
     *
3488
     *
3224
     * @return array
3489
     * @return array
-
 
3490
     * @access public
3225
     */
3491
     */
3226
    public function getSupportedVersions()
3492
    public function getSupportedVersions()
3227
    {
3493
    {
3228
        if (!($this->bitmap & SSH2::MASK_LOGIN)) {
3494
        if (!($this->bitmap & SSH2::MASK_LOGIN)) {
3229
            return false;
3495
            return false;
Line 3242... Line 3508...
3242
 
3508
 
3243
    /**
3509
    /**
3244
     * Get supported SFTP versions
3510
     * Get supported SFTP versions
3245
     *
3511
     *
3246
     * @return int|false
3512
     * @return int|false
-
 
3513
     * @access public
3247
     */
3514
     */
3248
    public function getNegotiatedVersion()
3515
    public function getNegotiatedVersion()
3249
    {
3516
    {
3250
        if (!$this->precheck()) {
3517
        if (!$this->precheck()) {
3251
            return false;
3518
            return false;
Line 3260... Line 3527...
3260
     * If you're preferred version isn't supported then the highest supported
3527
     * If you're preferred version isn't supported then the highest supported
3261
     * version of SFTP will be utilized. Set to null or false or int(0) to
3528
     * version of SFTP will be utilized. Set to null or false or int(0) to
3262
     * unset the preferred version
3529
     * unset the preferred version
3263
     *
3530
     *
3264
     * @param int $version
3531
     * @param int $version
-
 
3532
     * @access public
3265
     */
3533
     */
3266
    public function setPreferredVersion($version)
3534
    public function setPreferredVersion($version)
3267
    {
3535
    {
3268
        $this->preferredVersion = $version;
3536
        $this->preferredVersion = $version;
3269
    }
3537
    }
Line 3271... Line 3539...
3271
    /**
3539
    /**
3272
     * Disconnect
3540
     * Disconnect
3273
     *
3541
     *
3274
     * @param int $reason
3542
     * @param int $reason
3275
     * @return false
3543
     * @return false
-
 
3544
     * @access protected
3276
     */
3545
     */
3277
    protected function disconnect_helper($reason)
3546
    protected function disconnect_helper($reason)
3278
    {
3547
    {
3279
        $this->pwd = false;
3548
        $this->pwd = false;
3280
        return parent::disconnect_helper($reason);
3549
        return parent::disconnect_helper($reason);
3281
    }
3550
    }
3282
 
3551
 
3283
    /**
3552
    /**
3284
     * Enable Date Preservation
3553
     * Enable Date Preservation
3285
     *
3554
     *
-
 
3555
     * @access public
3286
     */
3556
     */
3287
    public function enableDatePreservation()
3557
    public function enableDatePreservation()
3288
    {
3558
    {
3289
        $this->preserveTime = true;
3559
        $this->preserveTime = true;
3290
    }
3560
    }
3291
 
3561
 
3292
    /**
3562
    /**
3293
     * Disable Date Preservation
3563
     * Disable Date Preservation
3294
     *
3564
     *
-
 
3565
     * @access public
3295
     */
3566
     */
3296
    public function disableDatePreservation()
3567
    public function disableDatePreservation()
3297
    {
3568
    {
3298
        $this->preserveTime = false;
3569
        $this->preserveTime = false;
3299
    }
3570
    }